test-mcp / scripts /tracker_cli.py
mikeboone's picture
Claude Fable 5.1
fix(pipeline): fold in commits pushed straight to the Space; a token without repos never stops the server
02321f4
Raw History Blame Contribute Delete
3.45 kB
#!/usr/bin/env python
"""The tracker from a shell: what the agent worker (scripts/agent_worker.sh)
and a person at a terminal need, over the same store the MCP tools use.
.venv/bin/python scripts/tracker_cli.py next --owner demoprep.agent
.venv/bin/python scripts/tracker_cli.py list --owner jack
.venv/bin/python scripts/tracker_cli.py get T-42
.venv/bin/python scripts/tracker_cli.py update T-42 --by mike.boone --status in_progress
.venv/bin/python scripts/tracker_cli.py update T-42 --by demoprep.agent --status review \\
--pr-url https://github.com/boone10/DemoPrep/pull/57 --note "PR opened"
.venv/bin/python scripts/tracker_cli.py note T-42 --by demoprep.agent --note "no change made: ..."
Output is JSON on stdout. Exit 1 with the refusal on stderr when the store
refuses (unknown person, bad status, no such task).
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from dotenv import load_dotenv # noqa: E402
load_dotenv(REPO_ROOT / ".env")
load_dotenv() # or the nearest .env up the tree, when run from a worktree
from demoprep_app.tracker import store as S # noqa: E402
from demoprep_app.tracker.store import TrackerError # noqa: E402
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description="DemoPrep team tracker")
sub = ap.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("next", help="the top open task for an owner, or nothing")
p.add_argument("--owner", required=True)
p = sub.add_parser("list")
p.add_argument("--owner", default="")
p.add_argument("--status", default="")
p.add_argument("--include-done", action="store_true")
p = sub.add_parser("get")
p.add_argument("task")
p = sub.add_parser("update")
p.add_argument("task")
p.add_argument("--by", required=True)
for f in ("title", "body", "status", "priority", "label", "owner", "due-date", "pr-url",
"run-id", "liveboard-url", "note"):
p.add_argument(f"--{f}", default="")
p = sub.add_parser("note")
p.add_argument("task")
p.add_argument("--by", required=True)
p.add_argument("--note", required=True)
a = ap.parse_args(argv)
try:
if a.cmd == "next":
tasks = [t for t in S.list_tasks(owner=a.owner, limit=200) if t["status"] in ("open", "in_progress")]
out = tasks[0] if tasks else None
elif a.cmd == "list":
out = S.list_tasks(owner=a.owner, status=a.status, include_done=a.include_done, limit=200)
elif a.cmd == "get":
out = S.get_task(a.task)
elif a.cmd == "update":
fields = {k.replace("-", "_"): getattr(a, k.replace("-", "_"))
for k in ("title", "body", "status", "priority", "label", "owner", "due-date",
"pr-url", "run-id", "liveboard-url")}
changes = {k: v for k, v in fields.items() if v}
out = S.update_task(a.task, changes, by=a.by, note=a.note)
else:
out = {"note_id": S.add_note(a.task, a.note, by=a.by)}
except TrackerError as e:
print(str(e), file=sys.stderr)
return 1
print(json.dumps(out, indent=2, default=str))
return 0
if __name__ == "__main__":
sys.exit(main())