feat(P12.1): CLI — add propose subcommands, remove task_type=task, add milestone status filter, transition comment support
This commit is contained in:
92
cli.py
92
cli.py
@@ -23,7 +23,6 @@ STATUS_ICON = {
|
||||
}
|
||||
TYPE_ICON = {
|
||||
"resolution": "⚖️",
|
||||
"task": "📋",
|
||||
"story": "📖",
|
||||
"test": "🧪",
|
||||
"issue": "📌",
|
||||
@@ -151,10 +150,56 @@ def cmd_search(args):
|
||||
|
||||
|
||||
def cmd_transition(args):
|
||||
result = _request("POST", f"/tasks/{args.task_id}/transition?new_status={args.status}")
|
||||
body = {}
|
||||
if args.comment:
|
||||
body["comment"] = args.comment
|
||||
result = _request("POST", f"/tasks/{args.task_id}/transition?new_status={args.status}", body or None)
|
||||
print(f"Task #{result['id']} transitioned to: {result['status']}")
|
||||
|
||||
|
||||
# ── Propose commands ──────────────────────────────────────────────
|
||||
|
||||
def cmd_proposes(args):
|
||||
if not args.project:
|
||||
print("Error: --project is required for proposes", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
result = _request("GET", f"/projects/{args.project}/proposes")
|
||||
items = result if isinstance(result, list) else result.get("items", [])
|
||||
if not items:
|
||||
print(" No proposes found.")
|
||||
return
|
||||
for p in items:
|
||||
status_icon = STATUS_ICON.get(p["status"], "❓")
|
||||
feat = f" → task {p['feat_task_id']}" if p.get("feat_task_id") else ""
|
||||
print(f" {status_icon} 💡 {p['propose_code']} {p['title']}{feat}")
|
||||
|
||||
|
||||
def cmd_propose_create(args):
|
||||
data = {"title": args.title}
|
||||
if args.description:
|
||||
data["description"] = args.description
|
||||
result = _request("POST", f"/projects/{args.project}/proposes", data)
|
||||
print(f"Created propose {result['propose_code']}: {result['title']}")
|
||||
|
||||
|
||||
def cmd_propose_accept(args):
|
||||
result = _request("POST", f"/projects/{args.project}/proposes/{args.propose_id}/accept?milestone_id={args.milestone}")
|
||||
print(f"Propose #{args.propose_id} accepted → task {result.get('feat_task_id', '?')}")
|
||||
|
||||
|
||||
def cmd_propose_reject(args):
|
||||
data = {}
|
||||
if args.reason:
|
||||
data["reason"] = args.reason
|
||||
result = _request("POST", f"/projects/{args.project}/proposes/{args.propose_id}/reject", data or None)
|
||||
print(f"Propose #{args.propose_id} rejected")
|
||||
|
||||
|
||||
def cmd_propose_reopen(args):
|
||||
result = _request("POST", f"/projects/{args.project}/proposes/{args.propose_id}/reopen")
|
||||
print(f"Propose #{args.propose_id} reopened")
|
||||
|
||||
|
||||
def cmd_stats(args):
|
||||
params = f"?project_id={args.project}" if args.project else ""
|
||||
stats = _request("GET", f"/dashboard/stats{params}")
|
||||
@@ -170,8 +215,13 @@ def cmd_stats(args):
|
||||
|
||||
|
||||
def cmd_milestones(args):
|
||||
params = f"?project_id={args.project}" if args.project else ""
|
||||
milestones = _request("GET", f"/milestones{params}")
|
||||
params = []
|
||||
if args.project:
|
||||
params.append(f"project_id={args.project}")
|
||||
if args.status:
|
||||
params.append(f"status={args.status}")
|
||||
qs = f"?{'&'.join(params)}" if params else ""
|
||||
milestones = _request("GET", f"/milestones{qs}")
|
||||
if not milestones:
|
||||
print(" No milestones found.")
|
||||
return
|
||||
@@ -242,7 +292,7 @@ def main():
|
||||
|
||||
p_tasks = sub.add_parser("tasks", aliases=["issues"], help="List tasks")
|
||||
p_tasks.add_argument("--project", "-p", type=int)
|
||||
p_tasks.add_argument("--type", "-t", choices=["task", "story", "test", "resolution", "issue", "maintenance", "research", "review"])
|
||||
p_tasks.add_argument("--type", "-t", choices=["story", "test", "resolution", "issue", "maintenance", "research", "review"])
|
||||
p_tasks.add_argument("--status", "-s", choices=["open", "pending", "undergoing", "completed", "closed"])
|
||||
|
||||
p_create = sub.add_parser("create-task", aliases=["create-issue"], help="Create a task")
|
||||
@@ -250,7 +300,7 @@ def main():
|
||||
p_create.add_argument("--project", "-p", type=int, required=True)
|
||||
p_create.add_argument("--milestone", "-m", type=int, required=True)
|
||||
p_create.add_argument("--reporter", "-r", type=int, required=True)
|
||||
p_create.add_argument("--type", "-t", default="task", choices=["task", "story", "test", "resolution", "issue", "maintenance", "research", "review"])
|
||||
p_create.add_argument("--type", "-t", default="issue", choices=["story", "test", "resolution", "issue", "maintenance", "research", "review"])
|
||||
p_create.add_argument("--subtype")
|
||||
p_create.add_argument("--priority", choices=["low", "medium", "high", "critical"])
|
||||
p_create.add_argument("--description", "-d")
|
||||
@@ -271,12 +321,14 @@ def main():
|
||||
p_trans = sub.add_parser("transition", help="Transition task status")
|
||||
p_trans.add_argument("task_id", type=int)
|
||||
p_trans.add_argument("status", choices=["open", "pending", "undergoing", "completed", "closed"])
|
||||
p_trans.add_argument("--comment", "-c", help="Comment (required for undergoing→completed)")
|
||||
|
||||
p_stats = sub.add_parser("stats", help="Dashboard stats")
|
||||
p_stats.add_argument("--project", "-p", type=int)
|
||||
|
||||
p_ms = sub.add_parser("milestones", help="List milestones")
|
||||
p_ms.add_argument("--project", "-p", type=int)
|
||||
p_ms.add_argument("--status", "-s", choices=["open", "freeze", "undergoing", "completed", "closed"])
|
||||
|
||||
p_msp = sub.add_parser("milestone-progress", help="Show milestone progress")
|
||||
p_msp.add_argument("milestone_id", type=int)
|
||||
@@ -296,6 +348,29 @@ def main():
|
||||
p_worklogs = sub.add_parser("worklogs", help="List work logs for a task")
|
||||
p_worklogs.add_argument("task_id", type=int)
|
||||
|
||||
# ── Propose subcommands ──
|
||||
p_proposes = sub.add_parser("proposes", help="List proposes for a project")
|
||||
p_proposes.add_argument("--project", "-p", type=int, required=True)
|
||||
|
||||
p_pc = sub.add_parser("propose-create", help="Create a propose")
|
||||
p_pc.add_argument("title")
|
||||
p_pc.add_argument("--project", "-p", type=int, required=True)
|
||||
p_pc.add_argument("--description", "-d")
|
||||
|
||||
p_pa = sub.add_parser("propose-accept", help="Accept a propose into a milestone")
|
||||
p_pa.add_argument("propose_id", type=int)
|
||||
p_pa.add_argument("--project", "-p", type=int, required=True)
|
||||
p_pa.add_argument("--milestone", "-m", type=int, required=True)
|
||||
|
||||
p_pr = sub.add_parser("propose-reject", help="Reject a propose")
|
||||
p_pr.add_argument("propose_id", type=int)
|
||||
p_pr.add_argument("--project", "-p", type=int, required=True)
|
||||
p_pr.add_argument("--reason", "-r")
|
||||
|
||||
p_pro = sub.add_parser("propose-reopen", help="Reopen a rejected propose")
|
||||
p_pro.add_argument("propose_id", type=int)
|
||||
p_pro.add_argument("--project", "-p", type=int, required=True)
|
||||
|
||||
args = parser.parse_args()
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
@@ -320,6 +395,11 @@ def main():
|
||||
"overdue": cmd_overdue,
|
||||
"log-time": cmd_log_time,
|
||||
"worklogs": cmd_worklogs,
|
||||
"proposes": cmd_proposes,
|
||||
"propose-create": cmd_propose_create,
|
||||
"propose-accept": cmd_propose_accept,
|
||||
"propose-reject": cmd_propose_reject,
|
||||
"propose-reopen": cmd_propose_reopen,
|
||||
}
|
||||
cmds[args.command](args)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user