Add backlog audit and task update command
- agents/prompts/backlog_audit.md: QA analyst prompt for checking
which pending tasks are already implemented in the codebase
- agents/runner.py: run_audit() — project-level agent that reads
all pending tasks, inspects code, returns classification
- cli/main.py: kin audit <project_id> — runs audit, offers to mark
done tasks; kin task update <id> --status --priority
- web/api.py: POST /api/projects/{id}/audit (runs audit inline),
POST /api/projects/{id}/audit/apply (batch mark as done)
- Frontend: "Audit backlog" button on ProjectView with results
modal showing already_done/still_pending/unclear categories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e755a19633
commit
96509dcafc
9 changed files with 548 additions and 2 deletions
85
cli/main.py
85
cli/main.py
|
|
@ -220,6 +220,32 @@ def task_show(ctx, id):
|
|||
click.echo(f" Updated: {t['updated_at']}")
|
||||
|
||||
|
||||
@task.command("update")
|
||||
@click.argument("task_id")
|
||||
@click.option("--status", type=click.Choice(
|
||||
["pending", "in_progress", "review", "done", "blocked", "decomposed"]),
|
||||
default=None, help="New status")
|
||||
@click.option("--priority", type=int, default=None, help="New priority (1-10)")
|
||||
@click.pass_context
|
||||
def task_update(ctx, task_id, status, priority):
|
||||
"""Update a task's status or priority."""
|
||||
conn = ctx.obj["conn"]
|
||||
t = models.get_task(conn, task_id)
|
||||
if not t:
|
||||
click.echo(f"Task '{task_id}' not found.", err=True)
|
||||
raise SystemExit(1)
|
||||
fields = {}
|
||||
if status is not None:
|
||||
fields["status"] = status
|
||||
if priority is not None:
|
||||
fields["priority"] = priority
|
||||
if not fields:
|
||||
click.echo("Nothing to update. Use --status or --priority.", err=True)
|
||||
raise SystemExit(1)
|
||||
updated = models.update_task(conn, task_id, **fields)
|
||||
click.echo(f"Updated {updated['id']}: status={updated['status']}, priority={updated['priority']}")
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# decision
|
||||
# ===========================================================================
|
||||
|
|
@ -564,6 +590,65 @@ def run_task(ctx, task_id, dry_run, allow_write):
|
|||
click.echo(f"Duration: {result['total_duration_seconds']}s")
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# audit
|
||||
# ===========================================================================
|
||||
|
||||
@cli.command("audit")
|
||||
@click.argument("project_id")
|
||||
@click.pass_context
|
||||
def audit_backlog(ctx, project_id):
|
||||
"""Audit pending tasks — check which are already implemented in the code."""
|
||||
from agents.runner import run_audit
|
||||
|
||||
conn = ctx.obj["conn"]
|
||||
p = models.get_project(conn, project_id)
|
||||
if not p:
|
||||
click.echo(f"Project '{project_id}' not found.", err=True)
|
||||
raise SystemExit(1)
|
||||
|
||||
pending = models.list_tasks(conn, project_id=project_id, status="pending")
|
||||
if not pending:
|
||||
click.echo("No pending tasks to audit.")
|
||||
return
|
||||
|
||||
click.echo(f"Auditing {len(pending)} pending tasks for {project_id}...")
|
||||
result = run_audit(conn, project_id)
|
||||
|
||||
if not result["success"]:
|
||||
click.echo(f"Audit failed: {result.get('error', 'unknown')}", err=True)
|
||||
raise SystemExit(1)
|
||||
|
||||
done = result.get("already_done", [])
|
||||
still = result.get("still_pending", [])
|
||||
unclear = result.get("unclear", [])
|
||||
|
||||
if done:
|
||||
click.echo(f"\nAlready done ({len(done)}):")
|
||||
for item in done:
|
||||
click.echo(f" {item['id']}: {item.get('reason', '')}")
|
||||
|
||||
if still:
|
||||
click.echo(f"\nStill pending ({len(still)}):")
|
||||
for item in still:
|
||||
click.echo(f" {item['id']}: {item.get('reason', '')}")
|
||||
|
||||
if unclear:
|
||||
click.echo(f"\nUnclear ({len(unclear)}):")
|
||||
for item in unclear:
|
||||
click.echo(f" {item['id']}: {item.get('reason', '')}")
|
||||
|
||||
if result.get("cost_usd"):
|
||||
click.echo(f"\nCost: ${result['cost_usd']:.4f}")
|
||||
if result.get("duration_seconds"):
|
||||
click.echo(f"Duration: {result['duration_seconds']}s")
|
||||
|
||||
if done and click.confirm(f"\nMark {len(done)} tasks as done?"):
|
||||
for item in done:
|
||||
models.update_task(conn, item["id"], status="done")
|
||||
click.echo(f"Marked {len(done)} tasks as done.")
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# bootstrap
|
||||
# ===========================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue