@blxzer/cursor-trellis 0.2.9 → 0.3.2

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 (234) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +85 -19
  3. package/README.zh-CN.md +60 -21
  4. package/bin/smart-search.js +1 -1
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +46 -18
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/commands/capability-smoke.d.ts +24 -0
  9. package/dist/commands/capability-smoke.d.ts.map +1 -0
  10. package/dist/commands/capability-smoke.js +83 -0
  11. package/dist/commands/capability-smoke.js.map +1 -0
  12. package/dist/commands/channel/agent-loader.d.ts +1 -1
  13. package/dist/commands/channel/agent-loader.js +4 -4
  14. package/dist/commands/channel/agent-loader.js.map +1 -1
  15. package/dist/commands/channel/guard.d.ts +2 -2
  16. package/dist/commands/channel/guard.js +3 -3
  17. package/dist/commands/channel/guard.js.map +1 -1
  18. package/dist/commands/channel/index.js +3 -3
  19. package/dist/commands/channel/index.js.map +1 -1
  20. package/dist/commands/channel/list.d.ts +1 -1
  21. package/dist/commands/channel/list.js +1 -1
  22. package/dist/commands/channel/rm.d.ts +1 -1
  23. package/dist/commands/channel/rm.js +1 -1
  24. package/dist/commands/channel/store/lock.d.ts +1 -1
  25. package/dist/commands/channel/store/lock.js +1 -1
  26. package/dist/commands/channel/store/paths.js +1 -1
  27. package/dist/commands/channel/store/paths.js.map +1 -1
  28. package/dist/commands/init.d.ts.map +1 -1
  29. package/dist/commands/init.js +197 -91
  30. package/dist/commands/init.js.map +1 -1
  31. package/dist/commands/uninstall.d.ts +4 -4
  32. package/dist/commands/uninstall.js +12 -12
  33. package/dist/commands/uninstall.js.map +1 -1
  34. package/dist/commands/update.d.ts +7 -2
  35. package/dist/commands/update.d.ts.map +1 -1
  36. package/dist/commands/update.js +161 -61
  37. package/dist/commands/update.js.map +1 -1
  38. package/dist/commands/upgrade.js +2 -2
  39. package/dist/commands/upgrade.js.map +1 -1
  40. package/dist/commands/workflow.d.ts +4 -4
  41. package/dist/commands/workflow.d.ts.map +1 -1
  42. package/dist/commands/workflow.js +10 -9
  43. package/dist/commands/workflow.js.map +1 -1
  44. package/dist/configurators/cursor.d.ts +2 -2
  45. package/dist/configurators/cursor.js +4 -4
  46. package/dist/configurators/cursor.js.map +1 -1
  47. package/dist/configurators/index.d.ts.map +1 -1
  48. package/dist/configurators/index.js +4 -4
  49. package/dist/configurators/index.js.map +1 -1
  50. package/dist/configurators/shared.d.ts +10 -10
  51. package/dist/configurators/shared.d.ts.map +1 -1
  52. package/dist/configurators/shared.js +32 -32
  53. package/dist/configurators/shared.js.map +1 -1
  54. package/dist/configurators/workflow.d.ts +6 -6
  55. package/dist/configurators/workflow.js +1 -1
  56. package/dist/constants/paths.d.ts +24 -24
  57. package/dist/constants/paths.d.ts.map +1 -1
  58. package/dist/constants/paths.js +16 -16
  59. package/dist/constants/paths.js.map +1 -1
  60. package/dist/migrations/manifests/0.2.10.json +374 -0
  61. package/dist/migrations/manifests/0.3.0.json +89 -0
  62. package/dist/migrations/manifests/0.3.1.json +32 -0
  63. package/dist/templates/common/bundled-skills/{trellis-cursor2plus-setup → cstl-cursor2plus-setup}/SKILL.md +15 -14
  64. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/SKILL.md +75 -75
  65. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/add-project-local-conventions.md +19 -19
  66. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-agents.md +8 -8
  67. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-context-loading.md +26 -26
  68. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-hooks.md +9 -9
  69. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-skills-or-commands.md +8 -8
  70. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-spec-structure.md +17 -17
  71. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-task-lifecycle.md +46 -46
  72. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-workflow.md +18 -18
  73. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/overview.md +55 -55
  74. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/context-injection.md +20 -20
  75. package/dist/templates/common/bundled-skills/cstl-meta/references/local-architecture/generated-files.md +83 -0
  76. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/overview.md +15 -15
  77. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/spec-system.md +12 -12
  78. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/task-system.md +34 -34
  79. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/workflow.md +8 -8
  80. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/workspace-memory.md +19 -19
  81. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/agents.md +12 -12
  82. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/hooks-and-settings.md +7 -7
  83. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/overview.md +9 -9
  84. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/platform-map.md +7 -7
  85. package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/skills-and-commands.md +19 -19
  86. package/dist/templates/common/bundled-skills/{trellis-micro-grill → cstl-micro-grill}/SKILL.md +62 -47
  87. package/dist/templates/common/bundled-skills/{trellis-skill-creator → cstl-skill-creator}/SKILL.md +44 -44
  88. package/dist/templates/common/bundled-skills/{trellis-skill-creator → cstl-skill-creator}/references/authoring-rules.md +97 -97
  89. package/dist/templates/common/bundled-skills/{trellis-skill-creator/references/trellis-skill-locations.md → cstl-skill-creator/references/cstl-skill-locations.md} +52 -52
  90. package/dist/templates/common/bundled-skills/{trellis-skill-creator → cstl-skill-creator}/references/review-checklist.md +47 -47
  91. package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/SKILL.md +6 -6
  92. package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/repository-analysis.md +1 -1
  93. package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/spec-task-planning.md +3 -3
  94. package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/spec-writing.md +1 -1
  95. package/dist/templates/common/commands/continue.md +24 -24
  96. package/dist/templates/common/commands/finish-work.md +5 -5
  97. package/dist/templates/common/commands/start.md +20 -20
  98. package/dist/templates/common/index.d.ts +1 -1
  99. package/dist/templates/common/index.d.ts.map +1 -1
  100. package/dist/templates/common/skills/before-dev.md +4 -4
  101. package/dist/templates/common/skills/brainstorm.md +188 -164
  102. package/dist/templates/common/skills/break-loop.md +3 -3
  103. package/dist/templates/common/skills/check.md +5 -5
  104. package/dist/templates/common/skills/update-spec.md +4 -4
  105. package/dist/templates/cursor/agents/{trellis-check.md → cstl-check.md} +14 -14
  106. package/dist/templates/cursor/agents/{trellis-implement.md → cstl-implement.md} +14 -14
  107. package/dist/templates/cursor/agents/{trellis-research.md → cstl-research.md} +11 -11
  108. package/dist/templates/cursor/commands/cursor2plus-setup.md +15 -15
  109. package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
  110. package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
  111. package/dist/templates/cursor/fixtures/expected-rules.js +2 -2
  112. package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
  113. package/dist/templates/cursor/rules/{trellis-subagent-dispatch.mdc → cstl-subagent-dispatch.mdc} +4 -4
  114. package/dist/templates/cursor/rules/{trellis-triage.mdc → cstl-triage.mdc} +3 -3
  115. package/dist/templates/cursor/rules/retrieval-routing.mdc +4 -4
  116. package/dist/templates/extract.d.ts +1 -1
  117. package/dist/templates/extract.js +1 -1
  118. package/dist/templates/markdown/agents.md +13 -13
  119. package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +1 -1
  120. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +634 -634
  121. package/dist/templates/markdown/spec/guides/cursor-context-injection-guide.md.txt +5 -5
  122. package/dist/templates/markdown/spec/guides/cursor-semantic-compliance.md.txt +7 -1
  123. package/dist/templates/markdown/spec/guides/cursor-subagent-policy.md.txt +393 -391
  124. package/dist/templates/markdown/spec/guides/durable-learning-decision-guide.md.txt +14 -14
  125. package/dist/templates/markdown/spec/guides/execution-strategy.md.txt +5 -5
  126. package/dist/templates/markdown/spec/guides/index.md.txt +3 -2
  127. package/dist/templates/markdown/spec/guides/retrieval-daily-guide.md.txt +10 -10
  128. package/dist/templates/markdown/workspace-index.md +3 -3
  129. package/dist/templates/markdown/worktree.yaml.txt +1 -1
  130. package/dist/templates/shared-hooks/index.d.ts +2 -2
  131. package/dist/templates/shared-hooks/index.js +2 -2
  132. package/dist/templates/shared-hooks/inject-retrieval-plan.py +3 -3
  133. package/dist/templates/shared-hooks/inject-shell-session-context.py +7 -7
  134. package/dist/templates/shared-hooks/inject-subagent-context.py +4 -4
  135. package/dist/templates/shared-hooks/inject-workflow-state.py +37 -37
  136. package/dist/templates/shared-hooks/research-end-retrieval-pack.py +2 -2
  137. package/dist/templates/shared-hooks/session-start.py +16 -16
  138. package/dist/templates/trellis/config/execution-strategy-rules.json +1 -1
  139. package/dist/templates/trellis/config.yaml +5 -25
  140. package/dist/templates/trellis/index.d.ts +2 -2
  141. package/dist/templates/trellis/index.js +2 -2
  142. package/dist/templates/trellis/local/README.md +15 -13
  143. package/dist/templates/trellis/local/patch_wpelc8.py +7 -7
  144. package/dist/templates/trellis/local/subagent-models.json.example +1 -1
  145. package/dist/templates/trellis/local/trellis-task-models.json.example +3 -3
  146. package/dist/templates/trellis/local/trellis-task-models.json5.example +5 -5
  147. package/dist/templates/trellis/local/trellis_task_models_config.py +6 -6
  148. package/dist/templates/trellis/scripts/add_session.py +20 -20
  149. package/dist/templates/trellis/scripts/common/active_task.py +2 -2
  150. package/dist/templates/trellis/scripts/common/artifact_search.py +587 -587
  151. package/dist/templates/trellis/scripts/common/cli_adapter.py +3 -3
  152. package/dist/templates/trellis/scripts/common/codebase_retrieval_router.py +6 -6
  153. package/dist/templates/trellis/scripts/common/config.py +3 -3
  154. package/dist/templates/trellis/scripts/common/developer.py +2 -2
  155. package/dist/templates/trellis/scripts/common/execution_strategy.py +1 -1
  156. package/dist/templates/trellis/scripts/common/packages_context.py +23 -23
  157. package/dist/templates/trellis/scripts/common/parent_orchestration.py +702 -702
  158. package/dist/templates/trellis/scripts/common/paths.py +107 -107
  159. package/dist/templates/trellis/scripts/common/project_file_stats.py +1 -1
  160. package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +1 -1
  161. package/dist/templates/trellis/scripts/common/safe_commit.py +31 -31
  162. package/dist/templates/trellis/scripts/common/session_context.py +1159 -1159
  163. package/dist/templates/trellis/scripts/common/session_memory.py +360 -360
  164. package/dist/templates/trellis/scripts/common/smart_search_evidence.py +514 -514
  165. package/dist/templates/trellis/scripts/common/smart_search_resolve.py +1 -1
  166. package/dist/templates/trellis/scripts/common/subagent_dispatch.py +7 -7
  167. package/dist/templates/trellis/scripts/common/task_context.py +1 -1
  168. package/dist/templates/trellis/scripts/common/task_dashboard.py +140 -140
  169. package/dist/templates/trellis/scripts/common/task_gates.py +2056 -2034
  170. package/dist/templates/trellis/scripts/common/task_store.py +1594 -1594
  171. package/dist/templates/trellis/scripts/common/task_utils.py +3 -3
  172. package/dist/templates/trellis/scripts/common/tasks.py +1 -1
  173. package/dist/templates/trellis/scripts/common/trellis_config.py +3 -3
  174. package/dist/templates/trellis/scripts/common/workflow_phase.py +1 -1
  175. package/dist/templates/trellis/scripts/cursor_retrieval_probe.py +3 -3
  176. package/dist/templates/trellis/scripts/cursor_retrieval_probe_prompt.md +11 -11
  177. package/dist/templates/trellis/scripts/hooks/linear_sync.py +8 -8
  178. package/dist/templates/trellis/scripts/init_developer.py +2 -2
  179. package/dist/templates/trellis/scripts/retrieval_probe_matrix_template.json +4 -4
  180. package/dist/templates/trellis/scripts/route_codebase_retrieval.py +1 -1
  181. package/dist/templates/trellis/scripts/task.py +918 -918
  182. package/dist/templates/trellis/tasks/templates/release-readiness/implement.md +2 -2
  183. package/dist/templates/trellis/workflow.md +805 -805
  184. package/dist/types/ai-tools.d.ts +1 -1
  185. package/dist/types/ai-tools.d.ts.map +1 -1
  186. package/dist/types/ai-tools.js +1 -1
  187. package/dist/types/ai-tools.js.map +1 -1
  188. package/dist/types/migration.d.ts +1 -1
  189. package/dist/types/migration.d.ts.map +1 -1
  190. package/dist/utils/agents-md.d.ts +37 -0
  191. package/dist/utils/agents-md.d.ts.map +1 -0
  192. package/dist/utils/agents-md.js +82 -0
  193. package/dist/utils/agents-md.js.map +1 -0
  194. package/dist/utils/codebase-retrieval-router.js +5 -5
  195. package/dist/utils/codebase-retrieval-router.js.map +1 -1
  196. package/dist/utils/manifest-prune.d.ts +2 -2
  197. package/dist/utils/manifest-prune.js +4 -4
  198. package/dist/utils/manifest-prune.js.map +1 -1
  199. package/dist/utils/mirror-check.d.ts +3 -1
  200. package/dist/utils/mirror-check.d.ts.map +1 -1
  201. package/dist/utils/mirror-check.js +22 -11
  202. package/dist/utils/mirror-check.js.map +1 -1
  203. package/dist/utils/project-capabilities.d.ts +14 -3
  204. package/dist/utils/project-capabilities.d.ts.map +1 -1
  205. package/dist/utils/project-capabilities.js +127 -17
  206. package/dist/utils/project-capabilities.js.map +1 -1
  207. package/dist/utils/readiness.d.ts +10 -0
  208. package/dist/utils/readiness.d.ts.map +1 -1
  209. package/dist/utils/readiness.js +6 -3
  210. package/dist/utils/readiness.js.map +1 -1
  211. package/dist/utils/retrieval-agent-instructions.js +1 -1
  212. package/dist/utils/retrieval-agent-instructions.js.map +1 -1
  213. package/dist/utils/retrieval-execution-telemetry.js +1 -1
  214. package/dist/utils/retrieval-execution-telemetry.js.map +1 -1
  215. package/dist/utils/template-fetcher.js +1 -1
  216. package/dist/utils/template-fetcher.js.map +1 -1
  217. package/dist/utils/template-hash.d.ts +2 -2
  218. package/dist/utils/template-hash.d.ts.map +1 -1
  219. package/dist/utils/template-hash.js +12 -10
  220. package/dist/utils/template-hash.js.map +1 -1
  221. package/dist/utils/workflow-dir.d.ts +13 -0
  222. package/dist/utils/workflow-dir.d.ts.map +1 -0
  223. package/dist/utils/workflow-dir.js +35 -0
  224. package/dist/utils/workflow-dir.js.map +1 -0
  225. package/dist/utils/workflow-ownership.d.ts +36 -0
  226. package/dist/utils/workflow-ownership.d.ts.map +1 -0
  227. package/dist/utils/workflow-ownership.js +137 -0
  228. package/dist/utils/workflow-ownership.js.map +1 -0
  229. package/dist/utils/workflow-resolver.d.ts +1 -1
  230. package/dist/utils/workflow-resolver.js +1 -1
  231. package/package.json +3 -4
  232. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +0 -83
  233. /package/bin/{trellis.js → cstl.js} +0 -0
  234. /package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/mcp-setup.md +0 -0
@@ -1,918 +1,918 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Task Management Script.
5
-
6
- Usage:
7
- python task.py create "<title>" [--slug <name>] [--assignee <dev>] [--priority P0|P1|P2|P3] [--parent <dir>] [--package <pkg>]
8
- python task.py add-context <dir> <file> <path> [reason] # Add jsonl entry
9
- python task.py validate <dir> # Validate jsonl files
10
- python task.py list-context <dir> # List jsonl entries
11
- python task.py dashboard # Show Task Dashboard
12
- python task.py select <dir> # Select task for this live session
13
- python task.py selected [--source] # Show selected task
14
- python task.py start-execution <dir> --approved # Start approved execution
15
- python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
16
- python task.py exit # Clear selected task
17
- python task.py set-branch <dir> <branch> # Set git branch
18
- python task.py set-base-branch <dir> <branch> # Set PR target branch
19
- python task.py set-scope <dir> <scope> # Set scope for PR title
20
- python task.py archive <task-dir> [--check] [--archive-integrated-children] # Check or archive completed task
21
- python task.py prepare-archive-evidence <task-dir> [--dry-run] # Draft missing verify.md archive evidence
22
- python task.py prepare-learning-scaffold <task-dir> [--trigger <text>] # Print spec-capture checklist (stdout only)
23
- python task.py list # List active tasks
24
- python task.py list-archive [month] # List archived tasks
25
- python task.py add-subtask <parent-dir> <child-dir> # Link child to parent
26
- python task.py remove-subtask <parent-dir> <child-dir> # Unlink child from parent
27
- python task.py prepare-child-worktree <parent-dir> <child-dir> --branch <branch>
28
- python task.py set-child-state <parent-dir> <child-dir> <state> --evidence <ref>
29
- python task.py integrate-child <parent-dir> <child-dir> <state> --evidence <ref>
30
- python task.py generate-child-prompt <parent-dir> <child-dir> [--mode inline|subagent]
31
- python task.py generate-dispatch-prompt <task-dir> <role> [--scope TEXT] [--finish] [--max-chars N]
32
- python task.py parent-status <parent-dir>
33
- python task.py review-child <parent-dir> <child-dir> [--check] [--decision accept|changes|cancel|integrate-through]
34
- """
35
-
36
- from __future__ import annotations
37
-
38
- import argparse
39
- import sys
40
- from datetime import datetime, timezone
41
- from pathlib import Path
42
-
43
- from common.log import Colors, colored
44
- from common.paths import (
45
- DIR_WORKFLOW,
46
- DIR_TASKS,
47
- FILE_TASK_JSON,
48
- get_repo_root,
49
- get_developer,
50
- get_tasks_dir,
51
- )
52
- from common.active_task import (
53
- clear_selected_task,
54
- resolve_context_key,
55
- resolve_selected_task,
56
- set_selected_task,
57
- )
58
- from common.io import read_json, write_json
59
- from common.task_dashboard import render_task_dashboard
60
- from common.cli_environment import optional_capability_note
61
- from common.task_gates import (
62
- BASELINE_GATE,
63
- build_reviewer_gate_record,
64
- read_strategy_contract,
65
- start_execution_repair_hints,
66
- validate_start_execution,
67
- validate_start_execution_check,
68
- write_gate_record,
69
- )
70
- from common.task_utils import resolve_task_dir, run_task_hooks
71
- from common.tasks import (
72
- children_progress,
73
- format_child_task_display,
74
- iter_active_tasks,
75
- load_parent_child_integration_states,
76
- )
77
- from common.task_map import get_child_state
78
-
79
- # Import command handlers from split modules (also re-exports for plan.py compatibility)
80
- from common.task_store import (
81
- cmd_create,
82
- cmd_archive,
83
- cmd_prepare_archive_evidence,
84
- cmd_prepare_learning_scaffold,
85
- cmd_set_branch,
86
- cmd_set_base_branch,
87
- cmd_set_scope,
88
- cmd_add_subtask,
89
- cmd_remove_subtask,
90
- cmd_prepare_child_worktree,
91
- cmd_set_child_state,
92
- cmd_integrate_child,
93
- cmd_generate_child_prompt,
94
- cmd_generate_dispatch_prompt,
95
- cmd_suggest_execution_strategy,
96
- cmd_parent_status,
97
- cmd_review_child,
98
- )
99
- from common.task_context import (
100
- cmd_add_context,
101
- cmd_validate,
102
- cmd_list_context,
103
- )
104
-
105
-
106
- # =============================================================================
107
- # Command: dashboard / select / selected / start-execution / record-gate / exit
108
- # =============================================================================
109
-
110
- def _repo_relative(path, repo_root) -> str:
111
- try:
112
- return path.relative_to(repo_root).as_posix()
113
- except ValueError:
114
- return str(path)
115
-
116
-
117
- def _print_select_dispatch_fallback_tip() -> None:
118
- print(
119
- "Tip: if no session identity, use "
120
- "'python ./.trellis/scripts/generate_dispatch_prompt.py --task <path>' "
121
- "to dispatch directly.",
122
- file=sys.stderr,
123
- )
124
-
125
-
126
- def _resolve_existing_task(task_input: str, repo_root):
127
- full_path = resolve_task_dir(task_input, repo_root)
128
- if not full_path.is_dir():
129
- print(colored(f"Error: Task not found: {task_input}", Colors.RED), file=sys.stderr)
130
- print("Hint: Use task name (e.g., 'my-task') or full path (e.g., '.trellis/tasks/01-31-my-task')", file=sys.stderr)
131
- return None
132
- return full_path
133
-
134
-
135
- def cmd_dashboard(args: argparse.Namespace) -> int:
136
- """Show Task Dashboard without mutating selection or status."""
137
- _ = args
138
- print(render_task_dashboard(get_repo_root()))
139
- return 0
140
-
141
-
142
- def cmd_select(args: argparse.Namespace) -> int:
143
- """Select a task for this live session without changing task.status."""
144
- repo_root = get_repo_root()
145
- task_input = args.dir
146
- full_path = _resolve_existing_task(task_input, repo_root)
147
- if full_path is None:
148
- _print_select_dispatch_fallback_tip()
149
- return 1
150
-
151
- if not resolve_context_key():
152
- print(
153
- colored("Error: session identity not available; selected_task was not persisted.", Colors.RED),
154
- file=sys.stderr,
155
- )
156
- print(
157
- "Hint: run inside an AI session that exposes session identity, or set TRELLIS_CONTEXT_ID before running task.py select.",
158
- file=sys.stderr,
159
- )
160
- _print_select_dispatch_fallback_tip()
161
- return 1
162
-
163
- selected = set_selected_task(_repo_relative(full_path, repo_root), repo_root)
164
- if not selected:
165
- print(colored("Error: failed to select task", Colors.RED), file=sys.stderr)
166
- _print_select_dispatch_fallback_tip()
167
- return 1
168
-
169
- print(colored(f"✓ Selected task: {selected.task_path}", Colors.GREEN))
170
- print(f"Source: {selected.source}")
171
- print("Task status unchanged.")
172
- return 0
173
-
174
-
175
- def _print_no_selected_task_guidance() -> None:
176
- """Explain why no task is selected and what to run next."""
177
- print(colored("No task selected for this live session.", Colors.YELLOW), file=sys.stderr)
178
- print("Next actions:", file=sys.stderr)
179
- print(
180
- " - Route work: python ./.trellis/scripts/task.py dashboard",
181
- file=sys.stderr,
182
- )
183
- print(
184
- " - Select a task: python ./.trellis/scripts/task.py select <task-dir>",
185
- file=sys.stderr,
186
- )
187
- print(
188
- " - List active tasks: python ./.trellis/scripts/task.py list",
189
- file=sys.stderr,
190
- )
191
- print(
192
- " - Persist selection in shells: set TRELLIS_CONTEXT_ID (or use your platform session hook)",
193
- file=sys.stderr,
194
- )
195
-
196
-
197
- def cmd_selected(args: argparse.Namespace) -> int:
198
- """Show selected task."""
199
- repo_root = get_repo_root()
200
- selected = resolve_selected_task(repo_root)
201
-
202
- if args.source:
203
- print(f"Selected task: {selected.task_path or '(none)'}")
204
- print(f"Source: {selected.source}")
205
- if selected.stale:
206
- print("State: stale")
207
- if not selected.task_path:
208
- _print_no_selected_task_guidance()
209
- return 0 if selected.task_path else 1
210
-
211
- if selected.task_path:
212
- print(selected.task_path)
213
- return 0
214
-
215
- _print_no_selected_task_guidance()
216
- return 1
217
-
218
-
219
- def cmd_exit(args: argparse.Namespace) -> int:
220
- """Clear selected task for this live session without changing task.status."""
221
- _ = args
222
- repo_root = get_repo_root()
223
- selected = clear_selected_task(repo_root)
224
- if not selected.task_path:
225
- print(colored("No selected task set", Colors.YELLOW))
226
- return 0
227
-
228
- print(colored(f"✓ Cleared selected task (was: {selected.task_path})", Colors.GREEN))
229
- print(f"Source: {selected.source}")
230
- print("Task status unchanged.")
231
- return 0
232
-
233
-
234
- def _print_guard_errors(items: list[str], stream=None) -> None:
235
- if stream is None:
236
- stream = sys.stdout
237
- for item in items:
238
- print(f" - {item}", file=stream)
239
-
240
-
241
- def cmd_start_execution(args: argparse.Namespace) -> int:
242
- """Start approved task execution after a non-mutating readiness check."""
243
- repo_root = get_repo_root()
244
- task_dir = _resolve_existing_task(args.dir, repo_root)
245
- if task_dir is None:
246
- return 1
247
-
248
- task_json_path = task_dir / FILE_TASK_JSON
249
- task_data = read_json(task_json_path) if task_json_path.is_file() else None
250
-
251
- if args.check:
252
- guard = validate_start_execution_check(task_dir, task_data)
253
- if not guard.ok:
254
- print(colored("Start-execution check: FAIL", Colors.RED))
255
- _print_guard_errors(guard.errors)
256
- for hint in start_execution_repair_hints(guard.errors, task_dir):
257
- print(f" Hint: {hint}")
258
- return 1
259
- print(colored("Start-execution check: PASS", Colors.GREEN))
260
- print(f"Contract fingerprint: {guard.contract_fingerprint}")
261
- baseline_fingerprint = guard.artifact_fingerprints.get(BASELINE_GATE)
262
- if baseline_fingerprint:
263
- print(f"Artifact fingerprint: {baseline_fingerprint}")
264
- if guard.required_gates:
265
- print(f"Required reviewer gates: {', '.join(guard.required_gates)}")
266
- if task_dir is not None:
267
- contract, _ = read_strategy_contract(task_dir)
268
- cap_note = optional_capability_note(contract.get("optional_capabilities"))
269
- if cap_note:
270
- print(colored(f"Note: {cap_note}", Colors.YELLOW))
271
- from common.execution_strategy import (
272
- contract_drift_warnings,
273
- validate_strategy_pair,
274
- )
275
-
276
- if contract:
277
- for item in contract_drift_warnings(
278
- repo_root, task_dir, task_data or {}, contract
279
- ):
280
- print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
281
- mode = contract.get("execution_mode")
282
- iso = contract.get("isolation")
283
- if isinstance(mode, str) and isinstance(iso, str):
284
- for item in validate_strategy_pair(mode, iso):
285
- print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
286
- print("Artifact gates are ready. Ask the user for explicit execution approval before running `task.py start-execution <task> --approved`.")
287
- return 0
288
-
289
- if not args.approved:
290
- print(colored("Error: no action selected for start-execution.", Colors.RED), file=sys.stderr)
291
- print("Run with --check for non-mutating preflight or --approved after explicit user approval.", file=sys.stderr)
292
- return 1
293
-
294
- guard = validate_start_execution(task_dir, task_data, approved=True)
295
- if not guard.ok:
296
- print(colored("Error: cannot start execution; readiness check failed.", Colors.RED), file=sys.stderr)
297
- _print_guard_errors(guard.errors, stream=sys.stderr)
298
- return 1
299
-
300
- assert task_data is not None
301
- if guard.baseline_record:
302
- write_gate_record(task_data, "start-execution", BASELINE_GATE, guard.baseline_record)
303
- for gate, record in guard.auto_gate_records.items():
304
- write_gate_record(task_data, "start-execution", gate, record)
305
- task_data["execution_approval"] = {
306
- "schema_version": 1,
307
- "transition": "start-execution",
308
- "approved_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
309
- "approved_by": "user",
310
- "approval_source": "task.py start-execution --approved",
311
- "contract_fingerprint": guard.contract_fingerprint,
312
- "artifact_fingerprint": guard.artifact_fingerprints.get(BASELINE_GATE),
313
- }
314
- if task_data.get("status") == "planning":
315
- task_data["status"] = "in_progress"
316
- if not write_json(task_json_path, task_data):
317
- print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
318
- return 1
319
-
320
- print(colored(f"✓ Execution approved for: {_repo_relative(task_dir, repo_root)}", Colors.GREEN))
321
- print(f"Status: {task_data.get('status')}")
322
- run_task_hooks("after_start", task_json_path, repo_root)
323
- return 0
324
-
325
-
326
- def cmd_record_gate(args: argparse.Namespace) -> int:
327
- """Record a non-baseline reviewer gate result."""
328
- repo_root = get_repo_root()
329
- task_dir = _resolve_existing_task(args.dir, repo_root)
330
- if task_dir is None:
331
- return 1
332
-
333
- task_json_path = task_dir / FILE_TASK_JSON
334
- task_data = read_json(task_json_path) if task_json_path.is_file() else None
335
- if task_data is None:
336
- print(colored("Error: task.json not found or invalid", Colors.RED), file=sys.stderr)
337
- return 1
338
-
339
- record, errors, warnings = build_reviewer_gate_record(
340
- task_dir=task_dir,
341
- task_data=task_data,
342
- transition=args.transition,
343
- gate=args.gate,
344
- result=args.result,
345
- reviewer=args.reviewer,
346
- evidence=args.evidence,
347
- issue_fingerprint=args.issue_fingerprint,
348
- issue_summary=args.issue_summary,
349
- root_cause=args.root_cause,
350
- skip_approved_by=args.skip_approved_by,
351
- skip_reason=args.skip_reason,
352
- contract_fingerprint=args.contract_fingerprint,
353
- artifact_fingerprint=args.artifact_fingerprint,
354
- )
355
- if errors:
356
- print(colored("Error: cannot record quality gate.", Colors.RED), file=sys.stderr)
357
- _print_guard_errors(errors, stream=sys.stderr)
358
- return 1
359
-
360
- assert record is not None
361
- write_gate_record(task_data, args.transition, args.gate, record)
362
- if not write_json(task_json_path, task_data):
363
- print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
364
- return 1
365
-
366
- print(colored(f"✓ Recorded gate: {args.transition}/{args.gate} = {record['result']}", Colors.GREEN))
367
- print(f"Evidence: {record['evidence']}")
368
- print(f"Contract fingerprint: {record['contract_fingerprint']}")
369
- print(f"Artifact fingerprint: {record['artifact_fingerprint']}")
370
- if record.get("route"):
371
- print(f"Route: {record['route']}")
372
- for warning in warnings:
373
- print(colored(f"Warning: {warning}", Colors.YELLOW), file=sys.stderr)
374
- return 0
375
-
376
-
377
- # =============================================================================
378
- # Command: list
379
- # =============================================================================
380
-
381
- def cmd_list(args: argparse.Namespace) -> int:
382
- """List active tasks."""
383
- repo_root = get_repo_root()
384
- tasks_dir = get_tasks_dir(repo_root)
385
- selected_task = resolve_selected_task(repo_root).task_path
386
- developer = get_developer(repo_root)
387
- filter_mine = args.mine
388
- filter_status = args.status
389
-
390
- if filter_mine:
391
- if not developer:
392
- print(colored("Error: No developer set. Run init_developer.py first", Colors.RED), file=sys.stderr)
393
- return 1
394
- print(colored(f"My tasks (assignee: {developer}):", Colors.BLUE))
395
- else:
396
- print(colored("All active tasks:", Colors.BLUE))
397
- print()
398
-
399
- # Single pass: collect all tasks via shared iterator
400
- all_tasks = {t.dir_name: t for t in iter_active_tasks(tasks_dir)}
401
- all_statuses = {name: t.status for name, t in all_tasks.items()}
402
-
403
- # Display tasks hierarchically
404
- count = 0
405
-
406
- def _print_task(
407
- dir_name: str,
408
- indent: int = 0,
409
- parent_dir: Path | None = None,
410
- ) -> None:
411
- nonlocal count
412
- t = all_tasks[dir_name]
413
-
414
- # Apply --mine filter
415
- if filter_mine and (t.assignee or "-") != developer:
416
- return
417
-
418
- # Apply --status filter
419
- if filter_status and t.status != filter_status:
420
- return
421
-
422
- relative_path = f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
423
- marker = ""
424
- if relative_path == selected_task:
425
- marker = f" {colored('<- selected', Colors.GREEN)}"
426
-
427
- integration_states = None
428
- if t.children:
429
- integration_states = load_parent_child_integration_states(
430
- t.directory, t.children
431
- )
432
- progress = children_progress(
433
- t.children, all_statuses, integration_states
434
- )
435
-
436
- integration_state = None
437
- if parent_dir is not None:
438
- integration_state = get_child_state(parent_dir, dir_name)
439
- status_display = format_child_task_display(t.status, integration_state)
440
-
441
- # Package tag
442
- pkg_tag = f" @{t.package}" if t.package else ""
443
-
444
- prefix = " " * indent + " - "
445
-
446
- if filter_mine:
447
- print(
448
- f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress}{marker}"
449
- )
450
- else:
451
- print(
452
- f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress} "
453
- f"[{colored(t.assignee or '-', Colors.CYAN)}]{marker}"
454
- )
455
- count += 1
456
-
457
- # Print children indented
458
- for child_name in t.children:
459
- if child_name in all_tasks:
460
- _print_task(child_name, indent + 1, parent_dir=t.directory)
461
-
462
- # Display only top-level tasks (those without a parent)
463
- for dir_name in sorted(all_tasks.keys()):
464
- if not all_tasks[dir_name].parent:
465
- _print_task(dir_name)
466
-
467
- if count == 0:
468
- if filter_mine:
469
- print(" (no tasks assigned to you)")
470
- else:
471
- print(" (no active tasks)")
472
-
473
- print()
474
- print(f"Total: {count} task(s)")
475
- return 0
476
-
477
-
478
- # =============================================================================
479
- # Command: list-archive
480
- # =============================================================================
481
-
482
- def cmd_list_archive(args: argparse.Namespace) -> int:
483
- """List archived tasks."""
484
- repo_root = get_repo_root()
485
- tasks_dir = get_tasks_dir(repo_root)
486
- archive_dir = tasks_dir / "archive"
487
- month = args.month
488
-
489
- print(colored("Archived tasks:", Colors.BLUE))
490
- print()
491
-
492
- if month:
493
- month_dir = archive_dir / month
494
- if month_dir.is_dir():
495
- print(f"[{month}]")
496
- for d in sorted(month_dir.iterdir()):
497
- if d.is_dir():
498
- print(f" - {d.name}/")
499
- else:
500
- print(f" No archives for {month}")
501
- else:
502
- if archive_dir.is_dir():
503
- for month_dir in sorted(archive_dir.iterdir()):
504
- if month_dir.is_dir():
505
- month_name = month_dir.name
506
- count = sum(1 for d in month_dir.iterdir() if d.is_dir())
507
- print(f"[{month_name}] - {count} task(s)")
508
-
509
- return 0
510
-
511
-
512
- # =============================================================================
513
- # Help
514
- # =============================================================================
515
-
516
- def show_usage() -> None:
517
- """Show usage help."""
518
- print("""Task Management Script
519
-
520
- Usage:
521
- python task.py create <title> Create new task directory
522
- python task.py create <title> --package <pkg> Create task for a specific package
523
- python task.py create <title> --parent <dir> Create task as child of parent
524
- python task.py add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
525
- python task.py validate <dir> Validate jsonl files
526
- python task.py list-context <dir> List jsonl entries
527
- python task.py dashboard Show Task Dashboard
528
- python task.py select <dir> Select task for this live session
529
- python task.py selected [--source] Show selected task
530
- python task.py start-execution <dir> --check Check execution readiness
531
- python task.py start-execution <dir> --approved Start approved execution
532
- python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
533
- python task.py exit Clear selected task
534
- python task.py set-branch <dir> <branch> Set git branch
535
- python task.py set-base-branch <dir> <branch> Set PR target branch
536
- python task.py set-scope <dir> <scope> Set scope for PR title
537
- python task.py archive <task-dir> [--check] Check or archive completed task
538
- python task.py add-subtask <parent> <child> Link child task to parent
539
- python task.py remove-subtask <parent> <child> Unlink child from parent
540
- python task.py prepare-child-worktree <parent> <child> --branch <branch>
541
- python task.py set-child-state <parent> <child> <state> --evidence <ref>
542
- python task.py integrate-child <parent> <child> <state> --evidence <ref>
543
- python task.py list [--mine] [--status <status>] List tasks
544
- python task.py list-archive [YYYY-MM] List archived tasks
545
-
546
- Monorepo options:
547
- --package <pkg> Package name (validated against config.yaml packages)
548
-
549
- List options:
550
- --mine, -m Show only tasks assigned to current developer
551
- --status, -s <s> Filter by status (planning, in_progress, review, completed)
552
-
553
- Examples:
554
- python task.py create "Add login feature" --slug add-login
555
- python task.py create "Add login feature" --slug add-login --package cli
556
- python task.py create "Child task" --slug child --parent .trellis/tasks/01-21-parent
557
- python task.py add-context <dir> implement .trellis/spec/cli/backend/auth.md "Auth guidelines"
558
- python task.py set-branch <dir> task/add-login
559
- python task.py dashboard
560
- python task.py select .trellis/tasks/01-21-add-login
561
- python task.py selected --source
562
- python task.py start-execution .trellis/tasks/01-21-add-login --check
563
- python task.py start-execution .trellis/tasks/01-21-add-login --approved
564
- python task.py record-gate .trellis/tasks/01-21-add-login --transition full-task-complete --gate code-review --result FAIL --reviewer codex --evidence verify.md --issue-fingerprint auth-branch-1 --root-cause implementation-defect
565
- python task.py exit
566
- python task.py archive add-login --check
567
- python task.py archive add-login
568
- python task.py add-subtask parent-task child-task # Link existing tasks
569
- python task.py remove-subtask parent-task child-task
570
- python task.py prepare-child-worktree parent-task child-task --branch child-task
571
- python task.py set-child-state parent-task child-task review --evidence verify.md
572
- python task.py integrate-child parent-task child-task accepted --evidence handoff.md --ref child-branch
573
- python task.py integrate-child parent-task child-task integrated --evidence task-map.md --ref child-branch --execute-merge
574
- python task.py generate-child-prompt parent-task child-task --mode inline
575
- python task.py parent-status parent-task
576
- python task.py review-child parent-task child-task --check
577
- python task.py review-child parent-task child-task --decision accept --ref child-branch
578
- python task.py list # List all active tasks
579
- python task.py list --mine # List my tasks only
580
- python task.py list --mine --status in_progress # List my in-progress tasks
581
- """)
582
-
583
-
584
- # =============================================================================
585
- # Main Entry
586
- # =============================================================================
587
-
588
- def main() -> int:
589
- """CLI entry point."""
590
- # Deprecation guard: `init-context` was removed in v0.5.0-beta.12.
591
- # Detect early so argparse doesn't mask the real reason with a generic
592
- # "invalid choice" error.
593
- if len(sys.argv) >= 2 and sys.argv[1] == "init-context":
594
- print(
595
- colored(
596
- "Error: `task.py init-context` was removed in v0.5.0-beta.12.",
597
- Colors.RED,
598
- ),
599
- file=sys.stderr,
600
- )
601
- print(
602
- "implement.jsonl / check.jsonl are now seeded on `task.py create` for",
603
- file=sys.stderr,
604
- )
605
- print(
606
- "sub-agent-capable platforms and curated by the AI during planning when needed.",
607
- file=sys.stderr,
608
- )
609
- print("See .trellis/workflow.md planning artifact guidance or run:", file=sys.stderr)
610
- print(
611
- " python ./.trellis/scripts/get_context.py --mode phase --step 1",
612
- file=sys.stderr,
613
- )
614
- print(
615
- "Use `task.py add-context <dir> implement|check <path> <reason>` to append entries.",
616
- file=sys.stderr,
617
- )
618
- return 2
619
-
620
- parser = argparse.ArgumentParser(
621
- description="Task Management Script",
622
- formatter_class=argparse.RawDescriptionHelpFormatter,
623
- )
624
- subparsers = parser.add_subparsers(dest="command", help="Commands")
625
-
626
- # create
627
- p_create = subparsers.add_parser("create", help="Create new task")
628
- p_create.add_argument("title", help="Task title")
629
- p_create.add_argument("--slug", "-s", help="Task slug")
630
- p_create.add_argument("--assignee", "-a", help="Assignee developer")
631
- p_create.add_argument("--priority", "-p", default="P2", help="Priority (P0-P3)")
632
- p_create.add_argument("--description", "-d", help="Task description")
633
- p_create.add_argument("--parent", help="Parent task directory (establishes subtask link)")
634
- p_create.add_argument("--package", help="Package name for monorepo projects")
635
-
636
- # add-context
637
- p_add = subparsers.add_parser("add-context", help="Add context entry")
638
- p_add.add_argument("dir", help="Task directory")
639
- p_add.add_argument("file", help="JSONL file (implement|check)")
640
- p_add.add_argument("path", help="File path to add")
641
- p_add.add_argument("reason", nargs="?", help="Reason for adding")
642
-
643
- # validate
644
- p_validate = subparsers.add_parser("validate", help="Validate context files")
645
- p_validate.add_argument("dir", help="Task directory")
646
-
647
- # list-context
648
- p_listctx = subparsers.add_parser("list-context", help="List context entries")
649
- p_listctx.add_argument("dir", help="Task directory")
650
-
651
- # dashboard
652
- subparsers.add_parser("dashboard", help="Show Task Dashboard")
653
-
654
- # select
655
- p_select = subparsers.add_parser("select", help="Select task for this live session")
656
- p_select.add_argument("dir", help="Task directory")
657
-
658
- # selected
659
- p_selected = subparsers.add_parser("selected", help="Show selected task")
660
- p_selected.add_argument("--source", action="store_true",
661
- help="Show selected task source")
662
-
663
- # start-execution
664
- p_start_execution = subparsers.add_parser("start-execution", help="Start approved task execution")
665
- p_start_execution.add_argument("dir", help="Task directory")
666
- p_start_execution.add_argument("--check", action="store_true",
667
- help="Run non-mutating execution readiness check")
668
- p_start_execution.add_argument("--approved", action="store_true",
669
- help="Record explicit approval and start execution")
670
-
671
- # record-gate
672
- p_record_gate = subparsers.add_parser("record-gate", help="Record reviewer quality gate")
673
- p_record_gate.add_argument("dir", help="Task directory")
674
- p_record_gate.add_argument("--transition", required=True, help="Transition key")
675
- p_record_gate.add_argument("--gate", required=True, help="Gate name")
676
- p_record_gate.add_argument("--result", required=True, help="PASS, FAIL, or SKIPPED")
677
- p_record_gate.add_argument("--reviewer", required=True, help="Reviewer identifier")
678
- p_record_gate.add_argument("--evidence", required=True, help="Short evidence reference")
679
- p_record_gate.add_argument("--issue-fingerprint", help="Required for FAIL")
680
- p_record_gate.add_argument("--issue-summary", help="Optional short issue summary for FAIL")
681
- p_record_gate.add_argument("--root-cause",
682
- help="Required for FAIL: implementation-defect, contract-changing-defect, or validation-environment-blocker")
683
- p_record_gate.add_argument("--skip-approved-by", help="Must be 'user' for SKIPPED")
684
- p_record_gate.add_argument("--skip-reason", help="Required reason for SKIPPED")
685
- p_record_gate.add_argument("--contract-fingerprint", help="Optional current contract fingerprint assertion")
686
- p_record_gate.add_argument("--artifact-fingerprint", help="Optional current artifact fingerprint assertion")
687
-
688
- # exit
689
- subparsers.add_parser("exit", help="Clear selected task")
690
-
691
- # set-branch
692
- p_branch = subparsers.add_parser("set-branch", help="Set git branch")
693
- p_branch.add_argument("dir", help="Task directory")
694
- p_branch.add_argument("branch", help="Branch name")
695
-
696
- # set-base-branch
697
- p_base = subparsers.add_parser("set-base-branch", help="Set PR target branch")
698
- p_base.add_argument("dir", help="Task directory")
699
- p_base.add_argument("base_branch", help="Base branch name (PR target)")
700
-
701
- # set-scope
702
- p_scope = subparsers.add_parser("set-scope", help="Set scope")
703
- p_scope.add_argument("dir", help="Task directory")
704
- p_scope.add_argument("scope", help="Scope name")
705
-
706
- # archive
707
- p_archive = subparsers.add_parser("archive", help="Archive task")
708
- p_archive.add_argument("name", help="Task directory or name")
709
- p_archive.add_argument("--check", action="store_true", help="Run non-mutating archive readiness check")
710
- p_archive.add_argument("--no-commit", action="store_true", help="Skip auto git commit after archive")
711
- p_archive.add_argument(
712
- "--archive-integrated-children",
713
- action="store_true",
714
- help="When archiving a parent, also archive integrated children that pass archive --check",
715
- )
716
-
717
- # prepare-archive-evidence
718
- p_prepare_archive = subparsers.add_parser(
719
- "prepare-archive-evidence",
720
- help="Append missing archive evidence sections to verify.md",
721
- )
722
- p_prepare_archive.add_argument("name", help="Task directory or name")
723
- p_prepare_archive.add_argument(
724
- "--dry-run",
725
- action="store_true",
726
- help="Show what would be appended without writing verify.md",
727
- )
728
-
729
- # prepare-learning-scaffold
730
- p_learning_scaffold = subparsers.add_parser(
731
- "prepare-learning-scaffold",
732
- help="Print durable-learning / spec-update checklist (does not edit specs)",
733
- )
734
- p_learning_scaffold.add_argument("name", help="Task directory or name")
735
- p_learning_scaffold.add_argument(
736
- "--trigger",
737
- help="Optional reason (e.g. parent review changes, repeated workflow bug)",
738
- )
739
-
740
- # list
741
- p_list = subparsers.add_parser("list", help="List tasks")
742
- p_list.add_argument("--mine", "-m", action="store_true", help="My tasks only")
743
- p_list.add_argument("--status", "-s", help="Filter by status")
744
-
745
- # add-subtask
746
- p_addsub = subparsers.add_parser("add-subtask", help="Link child task to parent")
747
- p_addsub.add_argument("parent_dir", help="Parent task directory")
748
- p_addsub.add_argument("child_dir", help="Child task directory")
749
-
750
- # remove-subtask
751
- p_rmsub = subparsers.add_parser("remove-subtask", help="Unlink child task from parent")
752
- p_rmsub.add_argument("parent_dir", help="Parent task directory")
753
- p_rmsub.add_argument("child_dir", help="Child task directory")
754
-
755
- # prepare-child-worktree
756
- p_prepare_worktree = subparsers.add_parser("prepare-child-worktree", help="Create/register a Child git worktree")
757
- p_prepare_worktree.add_argument("parent_dir", help="Parent task directory")
758
- p_prepare_worktree.add_argument("child_dir", help="Child task directory")
759
- p_prepare_worktree.add_argument("--branch", required=True, help="Child git branch to create or checkout")
760
- p_prepare_worktree.add_argument("--base", help="Base ref for a new Child branch")
761
- p_prepare_worktree.add_argument("--path", help="Worktree path under .trellis/worktrees/")
762
- p_prepare_worktree.add_argument("--check", action="store_true", help="Run non-mutating worktree readiness check")
763
-
764
- # set-child-state
765
- p_child_state = subparsers.add_parser("set-child-state", help="Set Child Worker state in Parent task-map.md")
766
- p_child_state.add_argument("parent_dir", help="Parent task directory")
767
- p_child_state.add_argument("child_dir", help="Child task directory")
768
- p_child_state.add_argument("state", help="Child state")
769
- p_child_state.add_argument("--evidence", required=True, help="Short evidence reference")
770
- p_child_state.add_argument("--reason", help="Optional short reason")
771
-
772
- # integrate-child
773
- p_integrate_child = subparsers.add_parser("integrate-child", help="Set Parent-controlled Child integration state")
774
- p_integrate_child.add_argument("parent_dir", help="Parent task directory")
775
- p_integrate_child.add_argument("child_dir", help="Child task directory")
776
- p_integrate_child.add_argument("state", help="Parent-controlled Child state")
777
- p_integrate_child.add_argument("--evidence", required=True, help="Short evidence reference")
778
- p_integrate_child.add_argument("--ref", help="Child git ref or reviewed diff reference")
779
- p_integrate_child.add_argument("--reason", help="Optional short reason")
780
- p_integrate_child.add_argument("--execute-merge", action="store_true", help="Execute git merge --no-ff --no-commit for an integrated Child")
781
- p_integrate_child.add_argument("--check", action="store_true", help="Run non-mutating integration readiness check")
782
-
783
- # suggest-execution-strategy
784
- p_suggest_strategy = subparsers.add_parser(
785
- "suggest-execution-strategy",
786
- help="Suggest execution_mode and isolation for Development Strategy Contract",
787
- )
788
- p_suggest_strategy.add_argument("task_dir", help="Task directory")
789
- p_suggest_strategy.add_argument(
790
- "--json",
791
- action="store_true",
792
- help="Emit machine-readable JSON",
793
- )
794
-
795
- # generate-dispatch-prompt
796
- p_dispatch_prompt = subparsers.add_parser(
797
- "generate-dispatch-prompt",
798
- help="Build full Task dispatch prompt (Agent-facing)",
799
- )
800
- p_dispatch_prompt.add_argument("task_dir", help="Task directory")
801
- p_dispatch_prompt.add_argument(
802
- "role",
803
- choices=["implement", "check", "research"],
804
- help="Subagent role",
805
- )
806
- p_dispatch_prompt.add_argument("--scope", help="One-line task instruction for subagent")
807
- p_dispatch_prompt.add_argument(
808
- "--finish",
809
- action="store_true",
810
- help="Use finish check context (role=check only)",
811
- )
812
- p_dispatch_prompt.add_argument(
813
- "--max-chars",
814
- type=int,
815
- help="Hard truncate embedded context block",
816
- )
817
-
818
- # generate-child-prompt
819
- p_gen_prompt = subparsers.add_parser(
820
- "generate-child-prompt",
821
- help="Generate child implementation prompt for parent orchestration",
822
- )
823
- p_gen_prompt.add_argument("parent_dir", help="Parent task directory")
824
- p_gen_prompt.add_argument("child_dir", help="Child task directory")
825
- p_gen_prompt.add_argument(
826
- "--mode",
827
- choices=["inline", "subagent"],
828
- default="inline",
829
- help="Delivery mode hint (inline manual handoff vs optional subagent)",
830
- )
831
- p_gen_prompt.add_argument(
832
- "--include-artifacts",
833
- action="store_true",
834
- help="Embed child artifact bodies in the generated prompt",
835
- )
836
- p_gen_prompt.add_argument("--output", "-o", help="Write prompt to file instead of stdout")
837
-
838
- # parent-status
839
- p_parent_status = subparsers.add_parser("parent-status", help="Show parent task-map orchestration status")
840
- p_parent_status.add_argument("parent_dir", help="Parent task directory")
841
-
842
- # review-child
843
- p_review_child = subparsers.add_parser(
844
- "review-child",
845
- help="Review child handoff and optionally advance integration states",
846
- )
847
- p_review_child.add_argument("parent_dir", help="Parent task directory")
848
- p_review_child.add_argument("child_dir", help="Child task directory")
849
- p_review_child.add_argument("--check", action="store_true", help="Non-mutating review readiness check")
850
- p_review_child.add_argument(
851
- "--decision",
852
- choices=["accept", "changes", "cancel", "integrate-through"],
853
- help="Parent review decision (runs integrate-child steps when valid)",
854
- )
855
- p_review_child.add_argument("--ref", help="Child git ref for accept / integrate-through")
856
- p_review_child.add_argument("--reason", help="Required for changes or cancel decisions")
857
- p_review_child.add_argument("--notes", help="Short parent review notes included in the report")
858
- p_review_child.add_argument(
859
- "--write-artifact",
860
- action="store_true",
861
- help="Also write review-<child>.md under the parent task directory",
862
- )
863
- p_review_child.add_argument(
864
- "--no-append-parent-verify",
865
- action="store_true",
866
- help="Do not append review notes to parent verify.md",
867
- )
868
-
869
- # list-archive
870
- p_listarch = subparsers.add_parser("list-archive", help="List archived tasks")
871
- p_listarch.add_argument("month", nargs="?", help="Month (YYYY-MM)")
872
-
873
- args = parser.parse_args()
874
-
875
- if not args.command:
876
- show_usage()
877
- return 1
878
-
879
- commands = {
880
- "create": cmd_create,
881
- "add-context": cmd_add_context,
882
- "validate": cmd_validate,
883
- "list-context": cmd_list_context,
884
- "dashboard": cmd_dashboard,
885
- "select": cmd_select,
886
- "selected": cmd_selected,
887
- "start-execution": cmd_start_execution,
888
- "record-gate": cmd_record_gate,
889
- "exit": cmd_exit,
890
- "set-branch": cmd_set_branch,
891
- "set-base-branch": cmd_set_base_branch,
892
- "set-scope": cmd_set_scope,
893
- "archive": cmd_archive,
894
- "prepare-archive-evidence": cmd_prepare_archive_evidence,
895
- "prepare-learning-scaffold": cmd_prepare_learning_scaffold,
896
- "add-subtask": cmd_add_subtask,
897
- "remove-subtask": cmd_remove_subtask,
898
- "prepare-child-worktree": cmd_prepare_child_worktree,
899
- "set-child-state": cmd_set_child_state,
900
- "integrate-child": cmd_integrate_child,
901
- "generate-child-prompt": cmd_generate_child_prompt,
902
- "generate-dispatch-prompt": cmd_generate_dispatch_prompt,
903
- "suggest-execution-strategy": cmd_suggest_execution_strategy,
904
- "parent-status": cmd_parent_status,
905
- "review-child": cmd_review_child,
906
- "list": cmd_list,
907
- "list-archive": cmd_list_archive,
908
- }
909
-
910
- if args.command in commands:
911
- return commands[args.command](args)
912
- else:
913
- show_usage()
914
- return 1
915
-
916
-
917
- if __name__ == "__main__":
918
- sys.exit(main())
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Task Management Script.
5
+
6
+ Usage:
7
+ python task.py create "<title>" [--slug <name>] [--assignee <dev>] [--priority P0|P1|P2|P3] [--parent <dir>] [--package <pkg>]
8
+ python task.py add-context <dir> <file> <path> [reason] # Add jsonl entry
9
+ python task.py validate <dir> # Validate jsonl files
10
+ python task.py list-context <dir> # List jsonl entries
11
+ python task.py dashboard # Show Task Dashboard
12
+ python task.py select <dir> # Select task for this live session
13
+ python task.py selected [--source] # Show selected task
14
+ python task.py start-execution <dir> --approved # Start approved execution
15
+ python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
16
+ python task.py exit # Clear selected task
17
+ python task.py set-branch <dir> <branch> # Set git branch
18
+ python task.py set-base-branch <dir> <branch> # Set PR target branch
19
+ python task.py set-scope <dir> <scope> # Set scope for PR title
20
+ python task.py archive <task-dir> [--check] [--archive-integrated-children] # Check or archive completed task
21
+ python task.py prepare-archive-evidence <task-dir> [--dry-run] # Draft missing verify.md archive evidence
22
+ python task.py prepare-learning-scaffold <task-dir> [--trigger <text>] # Print spec-capture checklist (stdout only)
23
+ python task.py list # List active tasks
24
+ python task.py list-archive [month] # List archived tasks
25
+ python task.py add-subtask <parent-dir> <child-dir> # Link child to parent
26
+ python task.py remove-subtask <parent-dir> <child-dir> # Unlink child from parent
27
+ python task.py prepare-child-worktree <parent-dir> <child-dir> --branch <branch>
28
+ python task.py set-child-state <parent-dir> <child-dir> <state> --evidence <ref>
29
+ python task.py integrate-child <parent-dir> <child-dir> <state> --evidence <ref>
30
+ python task.py generate-child-prompt <parent-dir> <child-dir> [--mode inline|subagent]
31
+ python task.py generate-dispatch-prompt <task-dir> <role> [--scope TEXT] [--finish] [--max-chars N]
32
+ python task.py parent-status <parent-dir>
33
+ python task.py review-child <parent-dir> <child-dir> [--check] [--decision accept|changes|cancel|integrate-through]
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import argparse
39
+ import sys
40
+ from datetime import datetime, timezone
41
+ from pathlib import Path
42
+
43
+ from common.log import Colors, colored
44
+ from common.paths import (
45
+ DIR_WORKFLOW,
46
+ DIR_TASKS,
47
+ FILE_TASK_JSON,
48
+ get_repo_root,
49
+ get_developer,
50
+ get_tasks_dir,
51
+ )
52
+ from common.active_task import (
53
+ clear_selected_task,
54
+ resolve_context_key,
55
+ resolve_selected_task,
56
+ set_selected_task,
57
+ )
58
+ from common.io import read_json, write_json
59
+ from common.task_dashboard import render_task_dashboard
60
+ from common.cli_environment import optional_capability_note
61
+ from common.task_gates import (
62
+ BASELINE_GATE,
63
+ build_reviewer_gate_record,
64
+ read_strategy_contract,
65
+ start_execution_repair_hints,
66
+ validate_start_execution,
67
+ validate_start_execution_check,
68
+ write_gate_record,
69
+ )
70
+ from common.task_utils import resolve_task_dir, run_task_hooks
71
+ from common.tasks import (
72
+ children_progress,
73
+ format_child_task_display,
74
+ iter_active_tasks,
75
+ load_parent_child_integration_states,
76
+ )
77
+ from common.task_map import get_child_state
78
+
79
+ # Import command handlers from split modules (also re-exports for plan.py compatibility)
80
+ from common.task_store import (
81
+ cmd_create,
82
+ cmd_archive,
83
+ cmd_prepare_archive_evidence,
84
+ cmd_prepare_learning_scaffold,
85
+ cmd_set_branch,
86
+ cmd_set_base_branch,
87
+ cmd_set_scope,
88
+ cmd_add_subtask,
89
+ cmd_remove_subtask,
90
+ cmd_prepare_child_worktree,
91
+ cmd_set_child_state,
92
+ cmd_integrate_child,
93
+ cmd_generate_child_prompt,
94
+ cmd_generate_dispatch_prompt,
95
+ cmd_suggest_execution_strategy,
96
+ cmd_parent_status,
97
+ cmd_review_child,
98
+ )
99
+ from common.task_context import (
100
+ cmd_add_context,
101
+ cmd_validate,
102
+ cmd_list_context,
103
+ )
104
+
105
+
106
+ # =============================================================================
107
+ # Command: dashboard / select / selected / start-execution / record-gate / exit
108
+ # =============================================================================
109
+
110
+ def _repo_relative(path, repo_root) -> str:
111
+ try:
112
+ return path.relative_to(repo_root).as_posix()
113
+ except ValueError:
114
+ return str(path)
115
+
116
+
117
+ def _print_select_dispatch_fallback_tip() -> None:
118
+ print(
119
+ "Tip: if no session identity, use "
120
+ "'python ./.cstl/scripts/generate_dispatch_prompt.py --task <path>' "
121
+ "to dispatch directly.",
122
+ file=sys.stderr,
123
+ )
124
+
125
+
126
+ def _resolve_existing_task(task_input: str, repo_root):
127
+ full_path = resolve_task_dir(task_input, repo_root)
128
+ if not full_path.is_dir():
129
+ print(colored(f"Error: Task not found: {task_input}", Colors.RED), file=sys.stderr)
130
+ print("Hint: Use task name (e.g., 'my-task') or full path (e.g., '.cstl/tasks/01-31-my-task')", file=sys.stderr)
131
+ return None
132
+ return full_path
133
+
134
+
135
+ def cmd_dashboard(args: argparse.Namespace) -> int:
136
+ """Show Task Dashboard without mutating selection or status."""
137
+ _ = args
138
+ print(render_task_dashboard(get_repo_root()))
139
+ return 0
140
+
141
+
142
+ def cmd_select(args: argparse.Namespace) -> int:
143
+ """Select a task for this live session without changing task.status."""
144
+ repo_root = get_repo_root()
145
+ task_input = args.dir
146
+ full_path = _resolve_existing_task(task_input, repo_root)
147
+ if full_path is None:
148
+ _print_select_dispatch_fallback_tip()
149
+ return 1
150
+
151
+ if not resolve_context_key():
152
+ print(
153
+ colored("Error: session identity not available; selected_task was not persisted.", Colors.RED),
154
+ file=sys.stderr,
155
+ )
156
+ print(
157
+ "Hint: run inside an AI session that exposes session identity, or set TRELLIS_CONTEXT_ID before running task.py select.",
158
+ file=sys.stderr,
159
+ )
160
+ _print_select_dispatch_fallback_tip()
161
+ return 1
162
+
163
+ selected = set_selected_task(_repo_relative(full_path, repo_root), repo_root)
164
+ if not selected:
165
+ print(colored("Error: failed to select task", Colors.RED), file=sys.stderr)
166
+ _print_select_dispatch_fallback_tip()
167
+ return 1
168
+
169
+ print(colored(f"✓ Selected task: {selected.task_path}", Colors.GREEN))
170
+ print(f"Source: {selected.source}")
171
+ print("Task status unchanged.")
172
+ return 0
173
+
174
+
175
+ def _print_no_selected_task_guidance() -> None:
176
+ """Explain why no task is selected and what to run next."""
177
+ print(colored("No task selected for this live session.", Colors.YELLOW), file=sys.stderr)
178
+ print("Next actions:", file=sys.stderr)
179
+ print(
180
+ " - Route work: python ./.cstl/scripts/task.py dashboard",
181
+ file=sys.stderr,
182
+ )
183
+ print(
184
+ " - Select a task: python ./.cstl/scripts/task.py select <task-dir>",
185
+ file=sys.stderr,
186
+ )
187
+ print(
188
+ " - List active tasks: python ./.cstl/scripts/task.py list",
189
+ file=sys.stderr,
190
+ )
191
+ print(
192
+ " - Persist selection in shells: set TRELLIS_CONTEXT_ID (or use your platform session hook)",
193
+ file=sys.stderr,
194
+ )
195
+
196
+
197
+ def cmd_selected(args: argparse.Namespace) -> int:
198
+ """Show selected task."""
199
+ repo_root = get_repo_root()
200
+ selected = resolve_selected_task(repo_root)
201
+
202
+ if args.source:
203
+ print(f"Selected task: {selected.task_path or '(none)'}")
204
+ print(f"Source: {selected.source}")
205
+ if selected.stale:
206
+ print("State: stale")
207
+ if not selected.task_path:
208
+ _print_no_selected_task_guidance()
209
+ return 0 if selected.task_path else 1
210
+
211
+ if selected.task_path:
212
+ print(selected.task_path)
213
+ return 0
214
+
215
+ _print_no_selected_task_guidance()
216
+ return 1
217
+
218
+
219
+ def cmd_exit(args: argparse.Namespace) -> int:
220
+ """Clear selected task for this live session without changing task.status."""
221
+ _ = args
222
+ repo_root = get_repo_root()
223
+ selected = clear_selected_task(repo_root)
224
+ if not selected.task_path:
225
+ print(colored("No selected task set", Colors.YELLOW))
226
+ return 0
227
+
228
+ print(colored(f"✓ Cleared selected task (was: {selected.task_path})", Colors.GREEN))
229
+ print(f"Source: {selected.source}")
230
+ print("Task status unchanged.")
231
+ return 0
232
+
233
+
234
+ def _print_guard_errors(items: list[str], stream=None) -> None:
235
+ if stream is None:
236
+ stream = sys.stdout
237
+ for item in items:
238
+ print(f" - {item}", file=stream)
239
+
240
+
241
+ def cmd_start_execution(args: argparse.Namespace) -> int:
242
+ """Start approved task execution after a non-mutating readiness check."""
243
+ repo_root = get_repo_root()
244
+ task_dir = _resolve_existing_task(args.dir, repo_root)
245
+ if task_dir is None:
246
+ return 1
247
+
248
+ task_json_path = task_dir / FILE_TASK_JSON
249
+ task_data = read_json(task_json_path) if task_json_path.is_file() else None
250
+
251
+ if args.check:
252
+ guard = validate_start_execution_check(task_dir, task_data)
253
+ if not guard.ok:
254
+ print(colored("Start-execution check: FAIL", Colors.RED))
255
+ _print_guard_errors(guard.errors)
256
+ for hint in start_execution_repair_hints(guard.errors, task_dir):
257
+ print(f" Hint: {hint}")
258
+ return 1
259
+ print(colored("Start-execution check: PASS", Colors.GREEN))
260
+ print(f"Contract fingerprint: {guard.contract_fingerprint}")
261
+ baseline_fingerprint = guard.artifact_fingerprints.get(BASELINE_GATE)
262
+ if baseline_fingerprint:
263
+ print(f"Artifact fingerprint: {baseline_fingerprint}")
264
+ if guard.required_gates:
265
+ print(f"Required reviewer gates: {', '.join(guard.required_gates)}")
266
+ if task_dir is not None:
267
+ contract, _ = read_strategy_contract(task_dir)
268
+ cap_note = optional_capability_note(contract.get("optional_capabilities"))
269
+ if cap_note:
270
+ print(colored(f"Note: {cap_note}", Colors.YELLOW))
271
+ from common.execution_strategy import (
272
+ contract_drift_warnings,
273
+ validate_strategy_pair,
274
+ )
275
+
276
+ if contract:
277
+ for item in contract_drift_warnings(
278
+ repo_root, task_dir, task_data or {}, contract
279
+ ):
280
+ print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
281
+ mode = contract.get("execution_mode")
282
+ iso = contract.get("isolation")
283
+ if isinstance(mode, str) and isinstance(iso, str):
284
+ for item in validate_strategy_pair(mode, iso):
285
+ print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
286
+ print("Artifact gates are ready. Ask the user for explicit execution approval before running `task.py start-execution <task> --approved`.")
287
+ return 0
288
+
289
+ if not args.approved:
290
+ print(colored("Error: no action selected for start-execution.", Colors.RED), file=sys.stderr)
291
+ print("Run with --check for non-mutating preflight or --approved after explicit user approval.", file=sys.stderr)
292
+ return 1
293
+
294
+ guard = validate_start_execution(task_dir, task_data, approved=True)
295
+ if not guard.ok:
296
+ print(colored("Error: cannot start execution; readiness check failed.", Colors.RED), file=sys.stderr)
297
+ _print_guard_errors(guard.errors, stream=sys.stderr)
298
+ return 1
299
+
300
+ assert task_data is not None
301
+ if guard.baseline_record:
302
+ write_gate_record(task_data, "start-execution", BASELINE_GATE, guard.baseline_record)
303
+ for gate, record in guard.auto_gate_records.items():
304
+ write_gate_record(task_data, "start-execution", gate, record)
305
+ task_data["execution_approval"] = {
306
+ "schema_version": 1,
307
+ "transition": "start-execution",
308
+ "approved_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
309
+ "approved_by": "user",
310
+ "approval_source": "task.py start-execution --approved",
311
+ "contract_fingerprint": guard.contract_fingerprint,
312
+ "artifact_fingerprint": guard.artifact_fingerprints.get(BASELINE_GATE),
313
+ }
314
+ if task_data.get("status") == "planning":
315
+ task_data["status"] = "in_progress"
316
+ if not write_json(task_json_path, task_data):
317
+ print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
318
+ return 1
319
+
320
+ print(colored(f"✓ Execution approved for: {_repo_relative(task_dir, repo_root)}", Colors.GREEN))
321
+ print(f"Status: {task_data.get('status')}")
322
+ run_task_hooks("after_start", task_json_path, repo_root)
323
+ return 0
324
+
325
+
326
+ def cmd_record_gate(args: argparse.Namespace) -> int:
327
+ """Record a non-baseline reviewer gate result."""
328
+ repo_root = get_repo_root()
329
+ task_dir = _resolve_existing_task(args.dir, repo_root)
330
+ if task_dir is None:
331
+ return 1
332
+
333
+ task_json_path = task_dir / FILE_TASK_JSON
334
+ task_data = read_json(task_json_path) if task_json_path.is_file() else None
335
+ if task_data is None:
336
+ print(colored("Error: task.json not found or invalid", Colors.RED), file=sys.stderr)
337
+ return 1
338
+
339
+ record, errors, warnings = build_reviewer_gate_record(
340
+ task_dir=task_dir,
341
+ task_data=task_data,
342
+ transition=args.transition,
343
+ gate=args.gate,
344
+ result=args.result,
345
+ reviewer=args.reviewer,
346
+ evidence=args.evidence,
347
+ issue_fingerprint=args.issue_fingerprint,
348
+ issue_summary=args.issue_summary,
349
+ root_cause=args.root_cause,
350
+ skip_approved_by=args.skip_approved_by,
351
+ skip_reason=args.skip_reason,
352
+ contract_fingerprint=args.contract_fingerprint,
353
+ artifact_fingerprint=args.artifact_fingerprint,
354
+ )
355
+ if errors:
356
+ print(colored("Error: cannot record quality gate.", Colors.RED), file=sys.stderr)
357
+ _print_guard_errors(errors, stream=sys.stderr)
358
+ return 1
359
+
360
+ assert record is not None
361
+ write_gate_record(task_data, args.transition, args.gate, record)
362
+ if not write_json(task_json_path, task_data):
363
+ print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
364
+ return 1
365
+
366
+ print(colored(f"✓ Recorded gate: {args.transition}/{args.gate} = {record['result']}", Colors.GREEN))
367
+ print(f"Evidence: {record['evidence']}")
368
+ print(f"Contract fingerprint: {record['contract_fingerprint']}")
369
+ print(f"Artifact fingerprint: {record['artifact_fingerprint']}")
370
+ if record.get("route"):
371
+ print(f"Route: {record['route']}")
372
+ for warning in warnings:
373
+ print(colored(f"Warning: {warning}", Colors.YELLOW), file=sys.stderr)
374
+ return 0
375
+
376
+
377
+ # =============================================================================
378
+ # Command: list
379
+ # =============================================================================
380
+
381
+ def cmd_list(args: argparse.Namespace) -> int:
382
+ """List active tasks."""
383
+ repo_root = get_repo_root()
384
+ tasks_dir = get_tasks_dir(repo_root)
385
+ selected_task = resolve_selected_task(repo_root).task_path
386
+ developer = get_developer(repo_root)
387
+ filter_mine = args.mine
388
+ filter_status = args.status
389
+
390
+ if filter_mine:
391
+ if not developer:
392
+ print(colored("Error: No developer set. Run init_developer.py first", Colors.RED), file=sys.stderr)
393
+ return 1
394
+ print(colored(f"My tasks (assignee: {developer}):", Colors.BLUE))
395
+ else:
396
+ print(colored("All active tasks:", Colors.BLUE))
397
+ print()
398
+
399
+ # Single pass: collect all tasks via shared iterator
400
+ all_tasks = {t.dir_name: t for t in iter_active_tasks(tasks_dir)}
401
+ all_statuses = {name: t.status for name, t in all_tasks.items()}
402
+
403
+ # Display tasks hierarchically
404
+ count = 0
405
+
406
+ def _print_task(
407
+ dir_name: str,
408
+ indent: int = 0,
409
+ parent_dir: Path | None = None,
410
+ ) -> None:
411
+ nonlocal count
412
+ t = all_tasks[dir_name]
413
+
414
+ # Apply --mine filter
415
+ if filter_mine and (t.assignee or "-") != developer:
416
+ return
417
+
418
+ # Apply --status filter
419
+ if filter_status and t.status != filter_status:
420
+ return
421
+
422
+ relative_path = f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
423
+ marker = ""
424
+ if relative_path == selected_task:
425
+ marker = f" {colored('<- selected', Colors.GREEN)}"
426
+
427
+ integration_states = None
428
+ if t.children:
429
+ integration_states = load_parent_child_integration_states(
430
+ t.directory, t.children
431
+ )
432
+ progress = children_progress(
433
+ t.children, all_statuses, integration_states
434
+ )
435
+
436
+ integration_state = None
437
+ if parent_dir is not None:
438
+ integration_state = get_child_state(parent_dir, dir_name)
439
+ status_display = format_child_task_display(t.status, integration_state)
440
+
441
+ # Package tag
442
+ pkg_tag = f" @{t.package}" if t.package else ""
443
+
444
+ prefix = " " * indent + " - "
445
+
446
+ if filter_mine:
447
+ print(
448
+ f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress}{marker}"
449
+ )
450
+ else:
451
+ print(
452
+ f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress} "
453
+ f"[{colored(t.assignee or '-', Colors.CYAN)}]{marker}"
454
+ )
455
+ count += 1
456
+
457
+ # Print children indented
458
+ for child_name in t.children:
459
+ if child_name in all_tasks:
460
+ _print_task(child_name, indent + 1, parent_dir=t.directory)
461
+
462
+ # Display only top-level tasks (those without a parent)
463
+ for dir_name in sorted(all_tasks.keys()):
464
+ if not all_tasks[dir_name].parent:
465
+ _print_task(dir_name)
466
+
467
+ if count == 0:
468
+ if filter_mine:
469
+ print(" (no tasks assigned to you)")
470
+ else:
471
+ print(" (no active tasks)")
472
+
473
+ print()
474
+ print(f"Total: {count} task(s)")
475
+ return 0
476
+
477
+
478
+ # =============================================================================
479
+ # Command: list-archive
480
+ # =============================================================================
481
+
482
+ def cmd_list_archive(args: argparse.Namespace) -> int:
483
+ """List archived tasks."""
484
+ repo_root = get_repo_root()
485
+ tasks_dir = get_tasks_dir(repo_root)
486
+ archive_dir = tasks_dir / "archive"
487
+ month = args.month
488
+
489
+ print(colored("Archived tasks:", Colors.BLUE))
490
+ print()
491
+
492
+ if month:
493
+ month_dir = archive_dir / month
494
+ if month_dir.is_dir():
495
+ print(f"[{month}]")
496
+ for d in sorted(month_dir.iterdir()):
497
+ if d.is_dir():
498
+ print(f" - {d.name}/")
499
+ else:
500
+ print(f" No archives for {month}")
501
+ else:
502
+ if archive_dir.is_dir():
503
+ for month_dir in sorted(archive_dir.iterdir()):
504
+ if month_dir.is_dir():
505
+ month_name = month_dir.name
506
+ count = sum(1 for d in month_dir.iterdir() if d.is_dir())
507
+ print(f"[{month_name}] - {count} task(s)")
508
+
509
+ return 0
510
+
511
+
512
+ # =============================================================================
513
+ # Help
514
+ # =============================================================================
515
+
516
+ def show_usage() -> None:
517
+ """Show usage help."""
518
+ print("""Task Management Script
519
+
520
+ Usage:
521
+ python task.py create <title> Create new task directory
522
+ python task.py create <title> --package <pkg> Create task for a specific package
523
+ python task.py create <title> --parent <dir> Create task as child of parent
524
+ python task.py add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
525
+ python task.py validate <dir> Validate jsonl files
526
+ python task.py list-context <dir> List jsonl entries
527
+ python task.py dashboard Show Task Dashboard
528
+ python task.py select <dir> Select task for this live session
529
+ python task.py selected [--source] Show selected task
530
+ python task.py start-execution <dir> --check Check execution readiness
531
+ python task.py start-execution <dir> --approved Start approved execution
532
+ python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
533
+ python task.py exit Clear selected task
534
+ python task.py set-branch <dir> <branch> Set git branch
535
+ python task.py set-base-branch <dir> <branch> Set PR target branch
536
+ python task.py set-scope <dir> <scope> Set scope for PR title
537
+ python task.py archive <task-dir> [--check] Check or archive completed task
538
+ python task.py add-subtask <parent> <child> Link child task to parent
539
+ python task.py remove-subtask <parent> <child> Unlink child from parent
540
+ python task.py prepare-child-worktree <parent> <child> --branch <branch>
541
+ python task.py set-child-state <parent> <child> <state> --evidence <ref>
542
+ python task.py integrate-child <parent> <child> <state> --evidence <ref>
543
+ python task.py list [--mine] [--status <status>] List tasks
544
+ python task.py list-archive [YYYY-MM] List archived tasks
545
+
546
+ Monorepo options:
547
+ --package <pkg> Package name (validated against config.yaml packages)
548
+
549
+ List options:
550
+ --mine, -m Show only tasks assigned to current developer
551
+ --status, -s <s> Filter by status (planning, in_progress, review, completed)
552
+
553
+ Examples:
554
+ python task.py create "Add login feature" --slug add-login
555
+ python task.py create "Add login feature" --slug add-login --package cli
556
+ python task.py create "Child task" --slug child --parent .cstl/tasks/01-21-parent
557
+ python task.py add-context <dir> implement .cstl/spec/cli/backend/auth.md "Auth guidelines"
558
+ python task.py set-branch <dir> task/add-login
559
+ python task.py dashboard
560
+ python task.py select .cstl/tasks/01-21-add-login
561
+ python task.py selected --source
562
+ python task.py start-execution .cstl/tasks/01-21-add-login --check
563
+ python task.py start-execution .cstl/tasks/01-21-add-login --approved
564
+ python task.py record-gate .cstl/tasks/01-21-add-login --transition full-task-complete --gate code-review --result FAIL --reviewer codex --evidence verify.md --issue-fingerprint auth-branch-1 --root-cause implementation-defect
565
+ python task.py exit
566
+ python task.py archive add-login --check
567
+ python task.py archive add-login
568
+ python task.py add-subtask parent-task child-task # Link existing tasks
569
+ python task.py remove-subtask parent-task child-task
570
+ python task.py prepare-child-worktree parent-task child-task --branch child-task
571
+ python task.py set-child-state parent-task child-task review --evidence verify.md
572
+ python task.py integrate-child parent-task child-task accepted --evidence handoff.md --ref child-branch
573
+ python task.py integrate-child parent-task child-task integrated --evidence task-map.md --ref child-branch --execute-merge
574
+ python task.py generate-child-prompt parent-task child-task --mode inline
575
+ python task.py parent-status parent-task
576
+ python task.py review-child parent-task child-task --check
577
+ python task.py review-child parent-task child-task --decision accept --ref child-branch
578
+ python task.py list # List all active tasks
579
+ python task.py list --mine # List my tasks only
580
+ python task.py list --mine --status in_progress # List my in-progress tasks
581
+ """)
582
+
583
+
584
+ # =============================================================================
585
+ # Main Entry
586
+ # =============================================================================
587
+
588
+ def main() -> int:
589
+ """CLI entry point."""
590
+ # Deprecation guard: `init-context` was removed in v0.5.0-beta.12.
591
+ # Detect early so argparse doesn't mask the real reason with a generic
592
+ # "invalid choice" error.
593
+ if len(sys.argv) >= 2 and sys.argv[1] == "init-context":
594
+ print(
595
+ colored(
596
+ "Error: `task.py init-context` was removed in v0.5.0-beta.12.",
597
+ Colors.RED,
598
+ ),
599
+ file=sys.stderr,
600
+ )
601
+ print(
602
+ "implement.jsonl / check.jsonl are now seeded on `task.py create` for",
603
+ file=sys.stderr,
604
+ )
605
+ print(
606
+ "sub-agent-capable platforms and curated by the AI during planning when needed.",
607
+ file=sys.stderr,
608
+ )
609
+ print("See .cstl/workflow.md planning artifact guidance or run:", file=sys.stderr)
610
+ print(
611
+ " python ./.cstl/scripts/get_context.py --mode phase --step 1",
612
+ file=sys.stderr,
613
+ )
614
+ print(
615
+ "Use `task.py add-context <dir> implement|check <path> <reason>` to append entries.",
616
+ file=sys.stderr,
617
+ )
618
+ return 2
619
+
620
+ parser = argparse.ArgumentParser(
621
+ description="Task Management Script",
622
+ formatter_class=argparse.RawDescriptionHelpFormatter,
623
+ )
624
+ subparsers = parser.add_subparsers(dest="command", help="Commands")
625
+
626
+ # create
627
+ p_create = subparsers.add_parser("create", help="Create new task")
628
+ p_create.add_argument("title", help="Task title")
629
+ p_create.add_argument("--slug", "-s", help="Task slug")
630
+ p_create.add_argument("--assignee", "-a", help="Assignee developer")
631
+ p_create.add_argument("--priority", "-p", default="P2", help="Priority (P0-P3)")
632
+ p_create.add_argument("--description", "-d", help="Task description")
633
+ p_create.add_argument("--parent", help="Parent task directory (establishes subtask link)")
634
+ p_create.add_argument("--package", help="Package name for monorepo projects")
635
+
636
+ # add-context
637
+ p_add = subparsers.add_parser("add-context", help="Add context entry")
638
+ p_add.add_argument("dir", help="Task directory")
639
+ p_add.add_argument("file", help="JSONL file (implement|check)")
640
+ p_add.add_argument("path", help="File path to add")
641
+ p_add.add_argument("reason", nargs="?", help="Reason for adding")
642
+
643
+ # validate
644
+ p_validate = subparsers.add_parser("validate", help="Validate context files")
645
+ p_validate.add_argument("dir", help="Task directory")
646
+
647
+ # list-context
648
+ p_listctx = subparsers.add_parser("list-context", help="List context entries")
649
+ p_listctx.add_argument("dir", help="Task directory")
650
+
651
+ # dashboard
652
+ subparsers.add_parser("dashboard", help="Show Task Dashboard")
653
+
654
+ # select
655
+ p_select = subparsers.add_parser("select", help="Select task for this live session")
656
+ p_select.add_argument("dir", help="Task directory")
657
+
658
+ # selected
659
+ p_selected = subparsers.add_parser("selected", help="Show selected task")
660
+ p_selected.add_argument("--source", action="store_true",
661
+ help="Show selected task source")
662
+
663
+ # start-execution
664
+ p_start_execution = subparsers.add_parser("start-execution", help="Start approved task execution")
665
+ p_start_execution.add_argument("dir", help="Task directory")
666
+ p_start_execution.add_argument("--check", action="store_true",
667
+ help="Run non-mutating execution readiness check")
668
+ p_start_execution.add_argument("--approved", action="store_true",
669
+ help="Record explicit approval and start execution")
670
+
671
+ # record-gate
672
+ p_record_gate = subparsers.add_parser("record-gate", help="Record reviewer quality gate")
673
+ p_record_gate.add_argument("dir", help="Task directory")
674
+ p_record_gate.add_argument("--transition", required=True, help="Transition key")
675
+ p_record_gate.add_argument("--gate", required=True, help="Gate name")
676
+ p_record_gate.add_argument("--result", required=True, help="PASS, FAIL, or SKIPPED")
677
+ p_record_gate.add_argument("--reviewer", required=True, help="Reviewer identifier")
678
+ p_record_gate.add_argument("--evidence", required=True, help="Short evidence reference")
679
+ p_record_gate.add_argument("--issue-fingerprint", help="Required for FAIL")
680
+ p_record_gate.add_argument("--issue-summary", help="Optional short issue summary for FAIL")
681
+ p_record_gate.add_argument("--root-cause",
682
+ help="Required for FAIL: implementation-defect, contract-changing-defect, or validation-environment-blocker")
683
+ p_record_gate.add_argument("--skip-approved-by", help="Must be 'user' for SKIPPED")
684
+ p_record_gate.add_argument("--skip-reason", help="Required reason for SKIPPED")
685
+ p_record_gate.add_argument("--contract-fingerprint", help="Optional current contract fingerprint assertion")
686
+ p_record_gate.add_argument("--artifact-fingerprint", help="Optional current artifact fingerprint assertion")
687
+
688
+ # exit
689
+ subparsers.add_parser("exit", help="Clear selected task")
690
+
691
+ # set-branch
692
+ p_branch = subparsers.add_parser("set-branch", help="Set git branch")
693
+ p_branch.add_argument("dir", help="Task directory")
694
+ p_branch.add_argument("branch", help="Branch name")
695
+
696
+ # set-base-branch
697
+ p_base = subparsers.add_parser("set-base-branch", help="Set PR target branch")
698
+ p_base.add_argument("dir", help="Task directory")
699
+ p_base.add_argument("base_branch", help="Base branch name (PR target)")
700
+
701
+ # set-scope
702
+ p_scope = subparsers.add_parser("set-scope", help="Set scope")
703
+ p_scope.add_argument("dir", help="Task directory")
704
+ p_scope.add_argument("scope", help="Scope name")
705
+
706
+ # archive
707
+ p_archive = subparsers.add_parser("archive", help="Archive task")
708
+ p_archive.add_argument("name", help="Task directory or name")
709
+ p_archive.add_argument("--check", action="store_true", help="Run non-mutating archive readiness check")
710
+ p_archive.add_argument("--no-commit", action="store_true", help="Skip auto git commit after archive")
711
+ p_archive.add_argument(
712
+ "--archive-integrated-children",
713
+ action="store_true",
714
+ help="When archiving a parent, also archive integrated children that pass archive --check",
715
+ )
716
+
717
+ # prepare-archive-evidence
718
+ p_prepare_archive = subparsers.add_parser(
719
+ "prepare-archive-evidence",
720
+ help="Append missing archive evidence sections to verify.md",
721
+ )
722
+ p_prepare_archive.add_argument("name", help="Task directory or name")
723
+ p_prepare_archive.add_argument(
724
+ "--dry-run",
725
+ action="store_true",
726
+ help="Show what would be appended without writing verify.md",
727
+ )
728
+
729
+ # prepare-learning-scaffold
730
+ p_learning_scaffold = subparsers.add_parser(
731
+ "prepare-learning-scaffold",
732
+ help="Print durable-learning / spec-update checklist (does not edit specs)",
733
+ )
734
+ p_learning_scaffold.add_argument("name", help="Task directory or name")
735
+ p_learning_scaffold.add_argument(
736
+ "--trigger",
737
+ help="Optional reason (e.g. parent review changes, repeated workflow bug)",
738
+ )
739
+
740
+ # list
741
+ p_list = subparsers.add_parser("list", help="List tasks")
742
+ p_list.add_argument("--mine", "-m", action="store_true", help="My tasks only")
743
+ p_list.add_argument("--status", "-s", help="Filter by status")
744
+
745
+ # add-subtask
746
+ p_addsub = subparsers.add_parser("add-subtask", help="Link child task to parent")
747
+ p_addsub.add_argument("parent_dir", help="Parent task directory")
748
+ p_addsub.add_argument("child_dir", help="Child task directory")
749
+
750
+ # remove-subtask
751
+ p_rmsub = subparsers.add_parser("remove-subtask", help="Unlink child task from parent")
752
+ p_rmsub.add_argument("parent_dir", help="Parent task directory")
753
+ p_rmsub.add_argument("child_dir", help="Child task directory")
754
+
755
+ # prepare-child-worktree
756
+ p_prepare_worktree = subparsers.add_parser("prepare-child-worktree", help="Create/register a Child git worktree")
757
+ p_prepare_worktree.add_argument("parent_dir", help="Parent task directory")
758
+ p_prepare_worktree.add_argument("child_dir", help="Child task directory")
759
+ p_prepare_worktree.add_argument("--branch", required=True, help="Child git branch to create or checkout")
760
+ p_prepare_worktree.add_argument("--base", help="Base ref for a new Child branch")
761
+ p_prepare_worktree.add_argument("--path", help="Worktree path under .cstl/worktrees/")
762
+ p_prepare_worktree.add_argument("--check", action="store_true", help="Run non-mutating worktree readiness check")
763
+
764
+ # set-child-state
765
+ p_child_state = subparsers.add_parser("set-child-state", help="Set Child Worker state in Parent task-map.md")
766
+ p_child_state.add_argument("parent_dir", help="Parent task directory")
767
+ p_child_state.add_argument("child_dir", help="Child task directory")
768
+ p_child_state.add_argument("state", help="Child state")
769
+ p_child_state.add_argument("--evidence", required=True, help="Short evidence reference")
770
+ p_child_state.add_argument("--reason", help="Optional short reason")
771
+
772
+ # integrate-child
773
+ p_integrate_child = subparsers.add_parser("integrate-child", help="Set Parent-controlled Child integration state")
774
+ p_integrate_child.add_argument("parent_dir", help="Parent task directory")
775
+ p_integrate_child.add_argument("child_dir", help="Child task directory")
776
+ p_integrate_child.add_argument("state", help="Parent-controlled Child state")
777
+ p_integrate_child.add_argument("--evidence", required=True, help="Short evidence reference")
778
+ p_integrate_child.add_argument("--ref", help="Child git ref or reviewed diff reference")
779
+ p_integrate_child.add_argument("--reason", help="Optional short reason")
780
+ p_integrate_child.add_argument("--execute-merge", action="store_true", help="Execute git merge --no-ff --no-commit for an integrated Child")
781
+ p_integrate_child.add_argument("--check", action="store_true", help="Run non-mutating integration readiness check")
782
+
783
+ # suggest-execution-strategy
784
+ p_suggest_strategy = subparsers.add_parser(
785
+ "suggest-execution-strategy",
786
+ help="Suggest execution_mode and isolation for Development Strategy Contract",
787
+ )
788
+ p_suggest_strategy.add_argument("task_dir", help="Task directory")
789
+ p_suggest_strategy.add_argument(
790
+ "--json",
791
+ action="store_true",
792
+ help="Emit machine-readable JSON",
793
+ )
794
+
795
+ # generate-dispatch-prompt
796
+ p_dispatch_prompt = subparsers.add_parser(
797
+ "generate-dispatch-prompt",
798
+ help="Build full Task dispatch prompt (Agent-facing)",
799
+ )
800
+ p_dispatch_prompt.add_argument("task_dir", help="Task directory")
801
+ p_dispatch_prompt.add_argument(
802
+ "role",
803
+ choices=["implement", "check", "research"],
804
+ help="Subagent role",
805
+ )
806
+ p_dispatch_prompt.add_argument("--scope", help="One-line task instruction for subagent")
807
+ p_dispatch_prompt.add_argument(
808
+ "--finish",
809
+ action="store_true",
810
+ help="Use finish check context (role=check only)",
811
+ )
812
+ p_dispatch_prompt.add_argument(
813
+ "--max-chars",
814
+ type=int,
815
+ help="Hard truncate embedded context block",
816
+ )
817
+
818
+ # generate-child-prompt
819
+ p_gen_prompt = subparsers.add_parser(
820
+ "generate-child-prompt",
821
+ help="Generate child implementation prompt for parent orchestration",
822
+ )
823
+ p_gen_prompt.add_argument("parent_dir", help="Parent task directory")
824
+ p_gen_prompt.add_argument("child_dir", help="Child task directory")
825
+ p_gen_prompt.add_argument(
826
+ "--mode",
827
+ choices=["inline", "subagent"],
828
+ default="inline",
829
+ help="Delivery mode hint (inline manual handoff vs optional subagent)",
830
+ )
831
+ p_gen_prompt.add_argument(
832
+ "--include-artifacts",
833
+ action="store_true",
834
+ help="Embed child artifact bodies in the generated prompt",
835
+ )
836
+ p_gen_prompt.add_argument("--output", "-o", help="Write prompt to file instead of stdout")
837
+
838
+ # parent-status
839
+ p_parent_status = subparsers.add_parser("parent-status", help="Show parent task-map orchestration status")
840
+ p_parent_status.add_argument("parent_dir", help="Parent task directory")
841
+
842
+ # review-child
843
+ p_review_child = subparsers.add_parser(
844
+ "review-child",
845
+ help="Review child handoff and optionally advance integration states",
846
+ )
847
+ p_review_child.add_argument("parent_dir", help="Parent task directory")
848
+ p_review_child.add_argument("child_dir", help="Child task directory")
849
+ p_review_child.add_argument("--check", action="store_true", help="Non-mutating review readiness check")
850
+ p_review_child.add_argument(
851
+ "--decision",
852
+ choices=["accept", "changes", "cancel", "integrate-through"],
853
+ help="Parent review decision (runs integrate-child steps when valid)",
854
+ )
855
+ p_review_child.add_argument("--ref", help="Child git ref for accept / integrate-through")
856
+ p_review_child.add_argument("--reason", help="Required for changes or cancel decisions")
857
+ p_review_child.add_argument("--notes", help="Short parent review notes included in the report")
858
+ p_review_child.add_argument(
859
+ "--write-artifact",
860
+ action="store_true",
861
+ help="Also write review-<child>.md under the parent task directory",
862
+ )
863
+ p_review_child.add_argument(
864
+ "--no-append-parent-verify",
865
+ action="store_true",
866
+ help="Do not append review notes to parent verify.md",
867
+ )
868
+
869
+ # list-archive
870
+ p_listarch = subparsers.add_parser("list-archive", help="List archived tasks")
871
+ p_listarch.add_argument("month", nargs="?", help="Month (YYYY-MM)")
872
+
873
+ args = parser.parse_args()
874
+
875
+ if not args.command:
876
+ show_usage()
877
+ return 1
878
+
879
+ commands = {
880
+ "create": cmd_create,
881
+ "add-context": cmd_add_context,
882
+ "validate": cmd_validate,
883
+ "list-context": cmd_list_context,
884
+ "dashboard": cmd_dashboard,
885
+ "select": cmd_select,
886
+ "selected": cmd_selected,
887
+ "start-execution": cmd_start_execution,
888
+ "record-gate": cmd_record_gate,
889
+ "exit": cmd_exit,
890
+ "set-branch": cmd_set_branch,
891
+ "set-base-branch": cmd_set_base_branch,
892
+ "set-scope": cmd_set_scope,
893
+ "archive": cmd_archive,
894
+ "prepare-archive-evidence": cmd_prepare_archive_evidence,
895
+ "prepare-learning-scaffold": cmd_prepare_learning_scaffold,
896
+ "add-subtask": cmd_add_subtask,
897
+ "remove-subtask": cmd_remove_subtask,
898
+ "prepare-child-worktree": cmd_prepare_child_worktree,
899
+ "set-child-state": cmd_set_child_state,
900
+ "integrate-child": cmd_integrate_child,
901
+ "generate-child-prompt": cmd_generate_child_prompt,
902
+ "generate-dispatch-prompt": cmd_generate_dispatch_prompt,
903
+ "suggest-execution-strategy": cmd_suggest_execution_strategy,
904
+ "parent-status": cmd_parent_status,
905
+ "review-child": cmd_review_child,
906
+ "list": cmd_list,
907
+ "list-archive": cmd_list_archive,
908
+ }
909
+
910
+ if args.command in commands:
911
+ return commands[args.command](args)
912
+ else:
913
+ show_usage()
914
+ return 1
915
+
916
+
917
+ if __name__ == "__main__":
918
+ sys.exit(main())