Files
rdb-discovery/src/rdb_discovery/cli.py
T

142 lines
4.5 KiB
Python

from __future__ import annotations
from datetime import datetime
from pathlib import Path
import typer
from rich.console import Console
from rich.table import Table
from .discovery import append_discovery_answer, core_questions
from .handoff import build_handoff
from .status import project_stage, task_counts
from .tasks import generate_agent_prompt, get_next_task, update_task_status
from .templates import CONTEXT_FILES, write_file_if_missing
app = typer.Typer(help="RDB discovery and delivery workflow CLI.")
console = Console()
def root_path() -> Path:
return Path.cwd()
def append_run_log(root: Path, event: str, task_id: str = "", notes: str = "") -> None:
path = root / "RUN_LOG.md"
if not path.exists():
path.write_text("# RUN_LOG.md\n\n| Date | Event | Task | Notes |\n|---|---|---|---|\n", encoding="utf-8")
line = f"| {datetime.now().isoformat(timespec='seconds')} | {event} | {task_id} | {notes} |\n"
with path.open("a", encoding="utf-8") as handle:
handle.write(line)
@app.command()
def init() -> None:
"""Create the initial markdown project structure."""
root = root_path()
created: list[str] = []
skipped: list[str] = []
for relative_path, content in CONTEXT_FILES.items():
if write_file_if_missing(root, relative_path, content):
created.append(relative_path)
else:
skipped.append(relative_path)
console.print("[bold green]Initialised rdb-discovery files.[/bold green]")
if created:
console.print("\nCreated:")
for item in created:
console.print(f"- {item}")
if skipped:
console.print("\nSkipped existing files:")
for item in skipped:
console.print(f"- {item}")
@app.command()
def discover() -> None:
"""Ask the core discovery questions and append answers to the discovery ledger."""
root = root_path()
write_file_if_missing(root, "context/discovery-log.md", CONTEXT_FILES["context/discovery-log.md"])
for index, question in enumerate(core_questions(), start=1):
qid = f"Q-{index:03d}"
answer = typer.prompt(question)
confidence = typer.prompt("Confidence?", default="Medium")
follow_up = typer.prompt("Follow-up needed?", default="No")
append_discovery_answer(root, qid, question, answer, confidence, follow_up)
append_run_log(root, "Discovery completed", notes="Core questions answered")
console.print("[bold green]Discovery complete.[/bold green]")
@app.command()
def status() -> None:
"""Show current project stage and task counts."""
root = root_path()
console.print(f"[bold]Project stage:[/bold] {project_stage(root)}")
counts = task_counts(root)
table = Table(title="Task Counts")
table.add_column("Status")
table.add_column("Count")
if counts:
for status_name, count in counts.items():
table.add_row(status_name, str(count))
else:
table.add_row("None", "0")
console.print(table)
@app.command(name="next")
def next_task() -> None:
"""Show the next Todo task."""
task = get_next_task(root_path())
if not task:
console.print("[yellow]No Todo task found.[/yellow]")
raise typer.Exit(code=0)
console.print(f"[bold]{task.task_id}[/bold] - {task.title}")
console.print(f"Status: {task.status}")
console.print(task.body)
@app.command()
def start(task_id: str) -> None:
"""Mark a task as In Progress."""
root = root_path()
if not update_task_status(root, task_id, "In Progress"):
console.print(f"[red]Task not found:[/red] {task_id}")
raise typer.Exit(code=1)
append_run_log(root, "Task started", task_id=task_id)
console.print(f"[green]Started {task_id}.[/green]")
@app.command()
def complete(task_id: str) -> None:
"""Mark a task as Done and record validation notes."""
root = root_path()
notes = typer.prompt("Validation notes", default="Not tested")
if not update_task_status(root, task_id, "Done"):
console.print(f"[red]Task not found:[/red] {task_id}")
raise typer.Exit(code=1)
append_run_log(root, "Task completed", task_id=task_id, notes=notes)
console.print(f"[green]Completed {task_id}.[/green]")
@app.command()
def handoff() -> None:
"""Print an AI-agent handoff summary."""
console.print(build_handoff(root_path()))
@app.command()
def prompt() -> None:
"""Generate a ready-to-paste implementation prompt for the next Todo task."""
root = root_path()
result = generate_agent_prompt(root)
console.print(result)