cowork-flow 0.0.4 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +44 -1
  2. package/package.json +8 -5
  3. package/src/lib/copy-template.js +9 -9
  4. package/src/lib/package-info.js +8 -2
  5. package/template/.agent/skills/agent-team-execution/SKILL.md +26 -0
  6. package/template/{.superpowers → .agent/skills}/brainstorming/scripts/start-server.sh +0 -0
  7. package/template/{.superpowers → .agent/skills}/brainstorming/scripts/stop-server.sh +0 -0
  8. package/template/.agent/skills/start/SKILL.md +1 -0
  9. package/template/.cowork-flow/.version +1 -1
  10. package/template/.cowork-flow/agent-team/adapters.yaml +15 -0
  11. package/template/.cowork-flow/agent-team/agents.yaml +142 -0
  12. package/template/.cowork-flow/agent-team/policy.yaml +23 -0
  13. package/template/.cowork-flow/config.yaml +9 -0
  14. package/template/.cowork-flow/run +10 -93
  15. package/template/.cowork-flow/run.cmd +12 -115
  16. package/template/.cowork-flow/scripts/agent_team.py +584 -0
  17. package/template/.cowork-flow/scripts/change.py +68 -15
  18. package/template/.cowork-flow/scripts/common/agent_team.py +467 -0
  19. package/template/.cowork-flow/scripts/common/archive_utils.py +72 -0
  20. package/template/.cowork-flow/scripts/common/config.py +18 -0
  21. package/template/.cowork-flow/scripts/common/git_context.py +17 -0
  22. package/template/.cowork-flow/scripts/common/task_utils.py +5 -18
  23. package/template/.cowork-flow/scripts/run.py +92 -0
  24. package/template/.cowork-flow/scripts/task.py +1 -1
  25. package/template/.cowork-flow/workflow.md +65 -158
  26. package/template/.superpowers/executing-plans/SKILL.md +2 -0
  27. package/template/AGENTS.md +3 -2
  28. /package/template/{.superpowers → .agent/skills}/brainstorming/SKILL.md +0 -0
  29. /package/template/{.superpowers → .agent/skills}/brainstorming/scripts/frame-template.html +0 -0
  30. /package/template/{.superpowers → .agent/skills}/brainstorming/scripts/helper.js +0 -0
  31. /package/template/{.superpowers → .agent/skills}/brainstorming/scripts/server.cjs +0 -0
  32. /package/template/{.superpowers → .agent/skills}/brainstorming/spec-document-reviewer-prompt.md +0 -0
  33. /package/template/{.superpowers → .agent/skills}/brainstorming/visual-companion.md +0 -0
  34. /package/template/{.superpowers → .agent/skills}/requesting-code-review/SKILL.md +0 -0
  35. /package/template/{.superpowers → .agent/skills}/requesting-code-review/code-reviewer.md +0 -0
  36. /package/template/{.superpowers → .agent/skills}/subagent-driven-development/SKILL.md +0 -0
  37. /package/template/{.superpowers → .agent/skills}/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
  38. /package/template/{.superpowers → .agent/skills}/subagent-driven-development/implementer-prompt.md +0 -0
  39. /package/template/{.superpowers → .agent/skills}/subagent-driven-development/spec-reviewer-prompt.md +0 -0
  40. /package/template/{.superpowers → .agent/skills}/writing-plans/SKILL.md +0 -0
  41. /package/template/{.superpowers → .agent/skills}/writing-plans/plan-document-reviewer-prompt.md +0 -0
@@ -0,0 +1,584 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Agent team runtime commands for cowork-flow."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import argparse
8
+ import json
9
+ import shutil
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ from common.agent_team import (
14
+ build_dispatch_plan,
15
+ build_initial_metrics,
16
+ build_initial_status,
17
+ parse_plan,
18
+ load_agent_registry,
19
+ render_assignment_prompt,
20
+ render_dispatch_plan,
21
+ write_json,
22
+ )
23
+ from common.config import get_agent_team_enabled
24
+ from common.paths import DIR_WORKFLOW, get_repo_root
25
+
26
+
27
+ DEFAULT_CONFIGS = {
28
+ "agents.yaml": """# Agent Team Registry
29
+ # Project teams may customize this file after initialization.
30
+
31
+ default_adapter: codex
32
+
33
+ agents:
34
+ implementer:
35
+ agent_type: worker
36
+ capabilities:
37
+ - implementation
38
+ - test-writing
39
+ preferred_task_types:
40
+ - code
41
+ - test
42
+ file_patterns:
43
+ - "src/**"
44
+ - "template/**"
45
+ - "tests/**"
46
+ - "test/**"
47
+ risk_limits:
48
+ max_parallel_write_conflicts: 0
49
+ prompt: |
50
+ Implement the smallest change that satisfies the approved plan and tests.
51
+ Write or update focused regression tests first, keep edits scoped, and report exact verification commands.
52
+
53
+ tester:
54
+ agent_type: worker
55
+ capabilities:
56
+ - test-writing
57
+ - verification
58
+ preferred_task_types:
59
+ - test
60
+ - verification
61
+ file_patterns:
62
+ - "tests/**"
63
+ - "test/**"
64
+ - "src/**"
65
+ - "template/**"
66
+ risk_limits:
67
+ max_parallel_write_conflicts: 0
68
+ prompt: |
69
+ Design tests that fail for the broken behavior and pass for the intended behavior.
70
+ Prefer clear behavior assertions over implementation details, and include the command that proves the result.
71
+
72
+ debugger:
73
+ agent_type: worker
74
+ capabilities:
75
+ - debugging
76
+ - implementation
77
+ - verification
78
+ preferred_task_types:
79
+ - bugfix
80
+ - code
81
+ file_patterns:
82
+ - "src/**"
83
+ - "template/**"
84
+ - "tests/**"
85
+ - "test/**"
86
+ risk_limits:
87
+ max_parallel_write_conflicts: 0
88
+ prompt: |
89
+ Find the root cause before changing code. Reproduce the failure, compare with nearby working patterns,
90
+ then make the smallest fix and verify the original symptom.
91
+
92
+ spec-reviewer:
93
+ agent_type: default
94
+ capabilities:
95
+ - spec-review
96
+ - acceptance-check
97
+ preferred_task_types:
98
+ - review
99
+ - documentation
100
+ file_patterns:
101
+ - ".cowork-flow/changes/**"
102
+ - ".cowork-flow/plans/**"
103
+ - "README.md"
104
+ - "template/**"
105
+ risk_limits:
106
+ max_parallel_write_conflicts: 0
107
+ prompt: |
108
+ Review the proposal, spec, plan, and task PRD for contradictions, missing acceptance criteria,
109
+ unclear scope, and behavior that is not covered by verification.
110
+
111
+ quality-reviewer:
112
+ agent_type: default
113
+ capabilities:
114
+ - code-quality-review
115
+ - test-review
116
+ - verification
117
+ preferred_task_types:
118
+ - review
119
+ - verification
120
+ file_patterns:
121
+ - "src/**"
122
+ - "template/**"
123
+ - "tests/**"
124
+ - "test/**"
125
+ risk_limits:
126
+ max_parallel_write_conflicts: 0
127
+ prompt: |
128
+ Review the diff for correctness, maintainability, focused scope, and meaningful tests.
129
+ Check that verification evidence matches the behavior being claimed.
130
+
131
+ docs-agent:
132
+ agent_type: worker
133
+ capabilities:
134
+ - documentation
135
+ - workflow-writing
136
+ preferred_task_types:
137
+ - docs
138
+ - documentation
139
+ file_patterns:
140
+ - "README.md"
141
+ - "AGENTS.md"
142
+ - ".cowork-flow/**/*.md"
143
+ - ".agent/skills/**"
144
+ risk_limits:
145
+ max_parallel_write_conflicts: 0
146
+ prompt: |
147
+ Write concise project documentation that reflects actual commands, files, and workflow behavior.
148
+ Avoid aspirational text that is not backed by the implementation.
149
+
150
+ release-reviewer:
151
+ agent_type: default
152
+ capabilities:
153
+ - release-review
154
+ - acceptance-check
155
+ - verification
156
+ preferred_task_types:
157
+ - release
158
+ - review
159
+ file_patterns:
160
+ - "package.json"
161
+ - "package-lock.json"
162
+ - "scripts/**"
163
+ - "README.md"
164
+ - ".cowork-flow/changes/**"
165
+ risk_limits:
166
+ max_parallel_write_conflicts: 0
167
+ prompt: |
168
+ Review release-facing changes for versioning, packaging, documentation, and command safety.
169
+ Confirm that generated artifacts and publish steps are intentional.
170
+ """,
171
+ "adapters.yaml": """# Agent Team Adapters
172
+
173
+ default: codex
174
+ fallback: manual
175
+
176
+ adapters:
177
+ codex:
178
+ mode: coordinator-dispatched
179
+ description: 主 agent 使用 Codex 子 agent 工具执行脚本生成的 assignments。
180
+ output_file: adapters/codex.json
181
+
182
+ manual:
183
+ mode: prompt-only
184
+ description: 生成可复制的 assignment prompt,由人或其他宿主执行并回写结果。
185
+ output_file: adapters/manual.json
186
+ """,
187
+ "policy.yaml": """# Agent Team Policy
188
+
189
+ parallel:
190
+ allow_parallel_batches: true
191
+ disallow_file_write_conflicts: true
192
+ require_coordinator_review: true
193
+
194
+ reviews:
195
+ require_spec_review: true
196
+ require_quality_review: true
197
+ chain:
198
+ - implementer
199
+ - spec-reviewer
200
+ - quality-reviewer
201
+
202
+ retry:
203
+ max_attempts: 3
204
+ retry_on:
205
+ - needs_context
206
+ - failed_verification
207
+ - review_rejected
208
+ - adapter_failed
209
+ escalation: needs-coordinator-decision
210
+ """,
211
+ }
212
+
213
+ TERMINAL_STATUSES = {"done", "approved"}
214
+ MAX_ATTEMPTS = 3
215
+ GATED_COMMANDS = {
216
+ "prepare",
217
+ "status",
218
+ "next",
219
+ "record-result",
220
+ "record-review",
221
+ "retry",
222
+ "complete",
223
+ }
224
+
225
+
226
+ def _agent_team_config_dir(repo_root: Path) -> Path:
227
+ return repo_root / DIR_WORKFLOW / "agent-team"
228
+
229
+
230
+ def _write_default_if_missing(path: Path, content: str) -> str:
231
+ if path.exists():
232
+ return "preserved"
233
+
234
+ path.parent.mkdir(parents=True, exist_ok=True)
235
+ path.write_text(content, encoding="utf-8")
236
+ return "created"
237
+
238
+
239
+ def _resolve_path(repo_root: Path, value: str) -> Path:
240
+ path = Path(value)
241
+ if path.is_absolute():
242
+ return path
243
+ return repo_root / path
244
+
245
+
246
+ def _task_ready(task_dir: Path) -> list[str]:
247
+ missing = []
248
+ for name in ("prd.md", "implement.jsonl", "check.jsonl", "debug.jsonl"):
249
+ path = task_dir / name
250
+ if not path.is_file() or not path.read_text(encoding="utf-8").strip():
251
+ missing.append(name)
252
+ return missing
253
+
254
+
255
+ def _runtime_dir(task_dir: Path) -> Path:
256
+ return task_dir / "agent-team"
257
+
258
+
259
+ def _load_json(path: Path) -> dict[str, object]:
260
+ return json.loads(path.read_text(encoding="utf-8"))
261
+
262
+
263
+ def _save_json(path: Path, data: dict[str, object]) -> None:
264
+ path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
265
+
266
+
267
+ def _load_status(task_dir: Path) -> dict[str, object]:
268
+ status_path = _runtime_dir(task_dir) / "status.json"
269
+ if not status_path.is_file():
270
+ raise FileNotFoundError(f"agent-team status not found: {status_path}")
271
+ return _load_json(status_path)
272
+
273
+
274
+ def _load_metrics(task_dir: Path) -> dict[str, object]:
275
+ metrics_path = _runtime_dir(task_dir) / "metrics.json"
276
+ if not metrics_path.is_file():
277
+ raise FileNotFoundError(f"agent-team metrics not found: {metrics_path}")
278
+ return _load_json(metrics_path)
279
+
280
+
281
+ def _unlock_ready_assignments(status_data: dict[str, object]) -> None:
282
+ assignments = status_data["assignments"]
283
+ for assignment in assignments.values():
284
+ if assignment.get("status") != "pending":
285
+ continue
286
+ depends_on = assignment.get("depends_on", [])
287
+ if all(assignments[dependency]["status"] in TERMINAL_STATUSES for dependency in depends_on):
288
+ assignment["status"] = "ready"
289
+
290
+
291
+ def _copy_payload(source: str | None, destination_dir: Path, assignment_id: str, attempt: int) -> None:
292
+ if not source:
293
+ return
294
+ source_path = Path(source)
295
+ if not source_path.is_file():
296
+ raise FileNotFoundError(f"payload file not found: {source}")
297
+ destination_dir.mkdir(parents=True, exist_ok=True)
298
+ shutil.copyfile(source_path, destination_dir / f"{assignment_id}-attempt-{attempt}.json")
299
+
300
+
301
+ def _record_assignment(
302
+ task_dir: Path,
303
+ assignment_id: str,
304
+ status: str,
305
+ payload_file: str | None,
306
+ *,
307
+ review: bool,
308
+ ) -> int:
309
+ status_data = _load_status(task_dir)
310
+ metrics = _load_metrics(task_dir)
311
+ assignments = status_data["assignments"]
312
+ if assignment_id not in assignments:
313
+ print(f"Error: assignment not found: {assignment_id}", file=sys.stderr)
314
+ return 1
315
+
316
+ assignment = assignments[assignment_id]
317
+ assignment["attempts"] = int(assignment.get("attempts", 0)) + 1
318
+ assignment["status"] = status
319
+ attempt = int(assignment["attempts"])
320
+
321
+ destination = _runtime_dir(task_dir) / ("reviews" if review else "results")
322
+ _copy_payload(payload_file, destination, assignment_id, attempt)
323
+
324
+ metrics["attempts"] = int(metrics.get("attempts", 0)) + 1
325
+ if status in TERMINAL_STATUSES:
326
+ metrics["successfulAssignments"] = int(metrics.get("successfulAssignments", 0)) + 1
327
+ else:
328
+ metrics["failedAssignments"] = int(metrics.get("failedAssignments", 0)) + 1
329
+ if review:
330
+ metrics["reviewReworks"] = int(metrics.get("reviewReworks", 0)) + 1
331
+
332
+ _unlock_ready_assignments(status_data)
333
+ _save_json(_runtime_dir(task_dir) / "status.json", status_data)
334
+ _save_json(_runtime_dir(task_dir) / "metrics.json", metrics)
335
+ print(f"recorded {assignment_id} status={status} attempt={attempt}")
336
+ return 0
337
+
338
+
339
+ def cmd_init(args: argparse.Namespace) -> int:
340
+ repo_root = get_repo_root()
341
+ config_dir = _agent_team_config_dir(repo_root)
342
+ outcomes = {
343
+ name: _write_default_if_missing(config_dir / name, content)
344
+ for name, content in DEFAULT_CONFIGS.items()
345
+ }
346
+ created = sum(1 for outcome in outcomes.values() if outcome == "created")
347
+ preserved = sum(1 for outcome in outcomes.values() if outcome == "preserved")
348
+ print(f"initialized agent-team config created={created} preserved={preserved}")
349
+ return 0
350
+
351
+
352
+ def cmd_prepare(args: argparse.Namespace) -> int:
353
+ repo_root = get_repo_root()
354
+ task_dir = _resolve_path(repo_root, args.task_dir)
355
+ plan_file = _resolve_path(repo_root, args.plan)
356
+
357
+ if not task_dir.is_dir():
358
+ print(f"Error: task directory not found: {task_dir}", file=sys.stderr)
359
+ return 1
360
+ if not plan_file.is_file():
361
+ print(f"Error: plan file not found: {plan_file}", file=sys.stderr)
362
+ return 1
363
+
364
+ missing = _task_ready(task_dir)
365
+ if missing:
366
+ print(f"Error: task context is incomplete: {', '.join(missing)}", file=sys.stderr)
367
+ return 1
368
+
369
+ tasks = parse_plan(plan_file.read_text(encoding="utf-8"))
370
+ if not tasks:
371
+ print(f"Error: unable to parse plan tasks from: {plan_file}", file=sys.stderr)
372
+ return 1
373
+
374
+ runtime_dir = task_dir / "agent-team"
375
+ assignments_dir = runtime_dir / "assignments"
376
+ adapters_dir = runtime_dir / "adapters"
377
+ for directory in (
378
+ runtime_dir,
379
+ assignments_dir,
380
+ runtime_dir / "results",
381
+ runtime_dir / "reviews",
382
+ runtime_dir / "blockers",
383
+ adapters_dir,
384
+ ):
385
+ directory.mkdir(parents=True, exist_ok=True)
386
+
387
+ registry = load_agent_registry(_agent_team_config_dir(repo_root) / "agents.yaml")
388
+ dispatch_plan = build_dispatch_plan(tasks, registry)
389
+ (runtime_dir / "dispatch-plan.yaml").write_text(
390
+ render_dispatch_plan(dispatch_plan),
391
+ encoding="utf-8",
392
+ )
393
+ write_json(runtime_dir / "status.json", build_initial_status(dispatch_plan))
394
+ write_json(runtime_dir / "metrics.json", build_initial_metrics(dispatch_plan))
395
+ write_json(
396
+ adapters_dir / f"{dispatch_plan['adapter']}.json",
397
+ {
398
+ "adapter": dispatch_plan["adapter"],
399
+ "mode": "coordinator-dispatched",
400
+ "assignmentCount": len(dispatch_plan["assignments"]),
401
+ },
402
+ )
403
+
404
+ for assignment in dispatch_plan["assignments"]:
405
+ prompt_path = assignments_dir / f"{assignment['id']}.md"
406
+ prompt_path.write_text(render_assignment_prompt(assignment), encoding="utf-8")
407
+
408
+ print(f"prepared agent-team runtime assignments={len(dispatch_plan['assignments'])}")
409
+ return 0
410
+
411
+
412
+ def cmd_status(args: argparse.Namespace) -> int:
413
+ task_dir = _resolve_path(get_repo_root(), args.task_dir)
414
+ try:
415
+ status_data = _load_status(task_dir)
416
+ except FileNotFoundError as error:
417
+ print(f"Error: {error}", file=sys.stderr)
418
+ return 1
419
+
420
+ counts: dict[str, int] = {}
421
+ for assignment in status_data["assignments"].values():
422
+ assignment_status = assignment.get("status", "unknown")
423
+ counts[assignment_status] = counts.get(assignment_status, 0) + 1
424
+
425
+ for status_name in sorted(counts):
426
+ print(f"{status_name}: {counts[status_name]}")
427
+ return 0
428
+
429
+
430
+ def cmd_next(args: argparse.Namespace) -> int:
431
+ task_dir = _resolve_path(get_repo_root(), args.task_dir)
432
+ try:
433
+ status_data = _load_status(task_dir)
434
+ except FileNotFoundError as error:
435
+ print(f"Error: {error}", file=sys.stderr)
436
+ return 1
437
+
438
+ ready = [
439
+ (assignment_id, assignment)
440
+ for assignment_id, assignment in status_data["assignments"].items()
441
+ if assignment.get("status") == "ready"
442
+ ]
443
+ if not ready:
444
+ print("No ready assignments")
445
+ return 0
446
+
447
+ for assignment_id, assignment in ready:
448
+ print(
449
+ f"{assignment_id}\trole={assignment['role']}\t"
450
+ f"agent={assignment['recommended_agent']}\tagent_type={assignment['agent_type']}"
451
+ )
452
+ return 0
453
+
454
+
455
+ def cmd_record_result(args: argparse.Namespace) -> int:
456
+ task_dir = _resolve_path(get_repo_root(), args.task_dir)
457
+ try:
458
+ return _record_assignment(task_dir, args.assignment, args.status, args.file, review=False)
459
+ except FileNotFoundError as error:
460
+ print(f"Error: {error}", file=sys.stderr)
461
+ return 1
462
+
463
+
464
+ def cmd_record_review(args: argparse.Namespace) -> int:
465
+ task_dir = _resolve_path(get_repo_root(), args.task_dir)
466
+ try:
467
+ return _record_assignment(task_dir, args.assignment, args.status, args.file, review=True)
468
+ except FileNotFoundError as error:
469
+ print(f"Error: {error}", file=sys.stderr)
470
+ return 1
471
+
472
+
473
+ def cmd_retry(args: argparse.Namespace) -> int:
474
+ task_dir = _resolve_path(get_repo_root(), args.task_dir)
475
+ try:
476
+ status_data = _load_status(task_dir)
477
+ metrics = _load_metrics(task_dir)
478
+ except FileNotFoundError as error:
479
+ print(f"Error: {error}", file=sys.stderr)
480
+ return 1
481
+
482
+ assignments = status_data["assignments"]
483
+ if args.assignment not in assignments:
484
+ print(f"Error: assignment not found: {args.assignment}", file=sys.stderr)
485
+ return 1
486
+
487
+ assignment = assignments[args.assignment]
488
+ assignment["attempts"] = int(assignment.get("attempts", 0)) + 1
489
+ assignment["last_retry_reason"] = args.reason
490
+ if int(assignment["attempts"]) >= MAX_ATTEMPTS:
491
+ assignment["status"] = "needs-coordinator-decision"
492
+ else:
493
+ assignment["status"] = "ready"
494
+
495
+ metrics["attempts"] = int(metrics.get("attempts", 0)) + 1
496
+ metrics["failedAssignments"] = int(metrics.get("failedAssignments", 0)) + 1
497
+ _save_json(_runtime_dir(task_dir) / "status.json", status_data)
498
+ _save_json(_runtime_dir(task_dir) / "metrics.json", metrics)
499
+ print(f"retry recorded {args.assignment} attempt={assignment['attempts']}")
500
+ return 0
501
+
502
+
503
+ def cmd_complete(args: argparse.Namespace) -> int:
504
+ task_dir = _resolve_path(get_repo_root(), args.task_dir)
505
+ try:
506
+ status_data = _load_status(task_dir)
507
+ except FileNotFoundError as error:
508
+ print(f"Error: {error}", file=sys.stderr)
509
+ return 1
510
+
511
+ pending = [
512
+ assignment_id
513
+ for assignment_id, assignment in status_data["assignments"].items()
514
+ if assignment.get("status") not in TERMINAL_STATUSES
515
+ ]
516
+ if pending:
517
+ print(f"Error: pending assignments: {', '.join(pending)}", file=sys.stderr)
518
+ return 1
519
+
520
+ print("agent team complete")
521
+ return 0
522
+
523
+
524
+ def build_parser() -> argparse.ArgumentParser:
525
+ parser = argparse.ArgumentParser(description="Manage Cowork Flow agent team runtime")
526
+ subparsers = parser.add_subparsers(dest="command", required=True)
527
+
528
+ init_parser = subparsers.add_parser("init", help="Initialize agent team configuration")
529
+ init_parser.set_defaults(func=cmd_init)
530
+
531
+ prepare_parser = subparsers.add_parser("prepare", help="Prepare agent team runtime from a plan")
532
+ prepare_parser.add_argument("task_dir", help="Task directory")
533
+ prepare_parser.add_argument("--plan", required=True, help="Implementation plan file")
534
+ prepare_parser.set_defaults(func=cmd_prepare)
535
+
536
+ status_parser = subparsers.add_parser("status", help="Show agent team status")
537
+ status_parser.add_argument("task_dir", help="Task directory")
538
+ status_parser.set_defaults(func=cmd_status)
539
+
540
+ next_parser = subparsers.add_parser("next", help="Show ready assignments")
541
+ next_parser.add_argument("task_dir", help="Task directory")
542
+ next_parser.set_defaults(func=cmd_next)
543
+
544
+ record_result_parser = subparsers.add_parser("record-result", help="Record assignment result")
545
+ record_result_parser.add_argument("task_dir", help="Task directory")
546
+ record_result_parser.add_argument("--assignment", required=True, help="Assignment id")
547
+ record_result_parser.add_argument("--status", required=True, help="Result status")
548
+ record_result_parser.add_argument("--file", help="JSON payload file")
549
+ record_result_parser.set_defaults(func=cmd_record_result)
550
+
551
+ record_review_parser = subparsers.add_parser("record-review", help="Record assignment review")
552
+ record_review_parser.add_argument("task_dir", help="Task directory")
553
+ record_review_parser.add_argument("--assignment", required=True, help="Assignment id")
554
+ record_review_parser.add_argument("--status", required=True, help="Review status")
555
+ record_review_parser.add_argument("--file", help="JSON payload file")
556
+ record_review_parser.set_defaults(func=cmd_record_review)
557
+
558
+ retry_parser = subparsers.add_parser("retry", help="Record assignment retry")
559
+ retry_parser.add_argument("task_dir", help="Task directory")
560
+ retry_parser.add_argument("--assignment", required=True, help="Assignment id")
561
+ retry_parser.add_argument("--reason", required=True, help="Retry reason")
562
+ retry_parser.set_defaults(func=cmd_retry)
563
+
564
+ complete_parser = subparsers.add_parser("complete", help="Check whether agent team work is complete")
565
+ complete_parser.add_argument("task_dir", help="Task directory")
566
+ complete_parser.set_defaults(func=cmd_complete)
567
+
568
+ return parser
569
+
570
+
571
+ def main() -> int:
572
+ parser = build_parser()
573
+ args = parser.parse_args()
574
+ if args.command in GATED_COMMANDS and not get_agent_team_enabled(get_repo_root()):
575
+ print(
576
+ "Error: agent-team is disabled. Set agent_team.enabled: true in .cowork-flow/config.yaml.",
577
+ file=sys.stderr,
578
+ )
579
+ return 1
580
+ return args.func(args)
581
+
582
+
583
+ if __name__ == "__main__":
584
+ sys.exit(main())