cap-pro 1.0.0

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 (275) hide show
  1. package/.claude-plugin/README.md +26 -0
  2. package/.claude-plugin/marketplace.json +24 -0
  3. package/.claude-plugin/plugin.json +24 -0
  4. package/LICENSE +21 -0
  5. package/README.ja-JP.md +834 -0
  6. package/README.ko-KR.md +823 -0
  7. package/README.md +806 -0
  8. package/README.pt-BR.md +452 -0
  9. package/README.zh-CN.md +800 -0
  10. package/agents/cap-architect.md +269 -0
  11. package/agents/cap-brainstormer.md +207 -0
  12. package/agents/cap-curator.md +276 -0
  13. package/agents/cap-debugger.md +365 -0
  14. package/agents/cap-designer.md +246 -0
  15. package/agents/cap-historian.md +464 -0
  16. package/agents/cap-migrator.md +291 -0
  17. package/agents/cap-prototyper.md +197 -0
  18. package/agents/cap-validator.md +308 -0
  19. package/bin/install.js +5433 -0
  20. package/cap/bin/cap-tools.cjs +853 -0
  21. package/cap/bin/lib/arc-scanner.cjs +344 -0
  22. package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
  23. package/cap/bin/lib/cap-anchor.cjs +228 -0
  24. package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
  25. package/cap/bin/lib/cap-checkpoint.cjs +434 -0
  26. package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
  27. package/cap/bin/lib/cap-cluster-display.cjs +52 -0
  28. package/cap/bin/lib/cap-cluster-format.cjs +245 -0
  29. package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
  30. package/cap/bin/lib/cap-cluster-io.cjs +212 -0
  31. package/cap/bin/lib/cap-completeness.cjs +540 -0
  32. package/cap/bin/lib/cap-deps.cjs +583 -0
  33. package/cap/bin/lib/cap-design-families.cjs +332 -0
  34. package/cap/bin/lib/cap-design.cjs +966 -0
  35. package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
  36. package/cap/bin/lib/cap-doctor.cjs +752 -0
  37. package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
  38. package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
  39. package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
  40. package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
  41. package/cap/bin/lib/cap-feature-map.cjs +1943 -0
  42. package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
  43. package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
  44. package/cap/bin/lib/cap-learn-review.cjs +1072 -0
  45. package/cap/bin/lib/cap-learning-signals.cjs +627 -0
  46. package/cap/bin/lib/cap-loader.cjs +227 -0
  47. package/cap/bin/lib/cap-logger.cjs +57 -0
  48. package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
  49. package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
  50. package/cap/bin/lib/cap-memory-dir.cjs +987 -0
  51. package/cap/bin/lib/cap-memory-engine.cjs +698 -0
  52. package/cap/bin/lib/cap-memory-extends.cjs +398 -0
  53. package/cap/bin/lib/cap-memory-graph.cjs +790 -0
  54. package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
  55. package/cap/bin/lib/cap-memory-pin.cjs +183 -0
  56. package/cap/bin/lib/cap-memory-platform.cjs +490 -0
  57. package/cap/bin/lib/cap-memory-prune.cjs +707 -0
  58. package/cap/bin/lib/cap-memory-schema.cjs +812 -0
  59. package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
  60. package/cap/bin/lib/cap-migrate.cjs +540 -0
  61. package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
  62. package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
  63. package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
  64. package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
  65. package/cap/bin/lib/cap-reconcile.cjs +570 -0
  66. package/cap/bin/lib/cap-research-gate.cjs +218 -0
  67. package/cap/bin/lib/cap-scope-filter.cjs +402 -0
  68. package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
  69. package/cap/bin/lib/cap-session-extract.cjs +987 -0
  70. package/cap/bin/lib/cap-session.cjs +445 -0
  71. package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
  72. package/cap/bin/lib/cap-stack-docs.cjs +646 -0
  73. package/cap/bin/lib/cap-tag-observer.cjs +371 -0
  74. package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
  75. package/cap/bin/lib/cap-telemetry.cjs +466 -0
  76. package/cap/bin/lib/cap-test-audit.cjs +1438 -0
  77. package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
  78. package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
  79. package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
  80. package/cap/bin/lib/cap-trace.cjs +399 -0
  81. package/cap/bin/lib/cap-trust-mode.cjs +336 -0
  82. package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
  83. package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
  84. package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
  85. package/cap/bin/lib/cap-ui.cjs +1245 -0
  86. package/cap/bin/lib/cap-upgrade.cjs +1028 -0
  87. package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
  88. package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
  89. package/cap/bin/lib/cli/init-router.cjs +68 -0
  90. package/cap/bin/lib/cli/phase-router.cjs +102 -0
  91. package/cap/bin/lib/cli/state-router.cjs +61 -0
  92. package/cap/bin/lib/cli/template-router.cjs +37 -0
  93. package/cap/bin/lib/cli/uat-router.cjs +29 -0
  94. package/cap/bin/lib/cli/validation-router.cjs +26 -0
  95. package/cap/bin/lib/cli/verification-router.cjs +31 -0
  96. package/cap/bin/lib/cli/workstream-router.cjs +39 -0
  97. package/cap/bin/lib/commands.cjs +961 -0
  98. package/cap/bin/lib/config.cjs +467 -0
  99. package/cap/bin/lib/convention-reader.cjs +258 -0
  100. package/cap/bin/lib/core.cjs +1241 -0
  101. package/cap/bin/lib/feature-aggregator.cjs +423 -0
  102. package/cap/bin/lib/frontmatter.cjs +337 -0
  103. package/cap/bin/lib/init.cjs +1443 -0
  104. package/cap/bin/lib/manifest-generator.cjs +383 -0
  105. package/cap/bin/lib/milestone.cjs +253 -0
  106. package/cap/bin/lib/model-profiles.cjs +69 -0
  107. package/cap/bin/lib/monorepo-context.cjs +226 -0
  108. package/cap/bin/lib/monorepo-migrator.cjs +509 -0
  109. package/cap/bin/lib/phase.cjs +889 -0
  110. package/cap/bin/lib/profile-output.cjs +989 -0
  111. package/cap/bin/lib/profile-pipeline.cjs +540 -0
  112. package/cap/bin/lib/roadmap.cjs +330 -0
  113. package/cap/bin/lib/security.cjs +394 -0
  114. package/cap/bin/lib/session-manager.cjs +292 -0
  115. package/cap/bin/lib/skeleton-generator.cjs +179 -0
  116. package/cap/bin/lib/state.cjs +1032 -0
  117. package/cap/bin/lib/template.cjs +231 -0
  118. package/cap/bin/lib/test-detector.cjs +62 -0
  119. package/cap/bin/lib/uat.cjs +283 -0
  120. package/cap/bin/lib/verify.cjs +889 -0
  121. package/cap/bin/lib/workspace-detector.cjs +371 -0
  122. package/cap/bin/lib/workstream.cjs +492 -0
  123. package/cap/commands/gsd/workstreams.md +63 -0
  124. package/cap/references/arc-standard.md +315 -0
  125. package/cap/references/cap-agent-architecture.md +101 -0
  126. package/cap/references/cap-gitignore-template +9 -0
  127. package/cap/references/cap-zero-deps.md +158 -0
  128. package/cap/references/checkpoints.md +778 -0
  129. package/cap/references/continuation-format.md +249 -0
  130. package/cap/references/contract-test-templates.md +312 -0
  131. package/cap/references/feature-map-template.md +25 -0
  132. package/cap/references/git-integration.md +295 -0
  133. package/cap/references/git-planning-commit.md +38 -0
  134. package/cap/references/model-profiles.md +174 -0
  135. package/cap/references/phase-numbering.md +126 -0
  136. package/cap/references/planning-config.md +202 -0
  137. package/cap/references/property-test-templates.md +316 -0
  138. package/cap/references/security-test-templates.md +347 -0
  139. package/cap/references/session-template.json +8 -0
  140. package/cap/references/tdd.md +263 -0
  141. package/cap/references/user-profiling.md +681 -0
  142. package/cap/references/verification-patterns.md +612 -0
  143. package/cap/templates/UAT.md +265 -0
  144. package/cap/templates/claude-md.md +175 -0
  145. package/cap/templates/codebase/architecture.md +255 -0
  146. package/cap/templates/codebase/concerns.md +310 -0
  147. package/cap/templates/codebase/conventions.md +307 -0
  148. package/cap/templates/codebase/integrations.md +280 -0
  149. package/cap/templates/codebase/stack.md +186 -0
  150. package/cap/templates/codebase/structure.md +285 -0
  151. package/cap/templates/codebase/testing.md +480 -0
  152. package/cap/templates/config.json +44 -0
  153. package/cap/templates/context.md +352 -0
  154. package/cap/templates/continue-here.md +78 -0
  155. package/cap/templates/copilot-instructions.md +7 -0
  156. package/cap/templates/debug-subagent-prompt.md +91 -0
  157. package/cap/templates/discussion-log.md +63 -0
  158. package/cap/templates/milestone-archive.md +123 -0
  159. package/cap/templates/milestone.md +115 -0
  160. package/cap/templates/phase-prompt.md +610 -0
  161. package/cap/templates/planner-subagent-prompt.md +117 -0
  162. package/cap/templates/project.md +186 -0
  163. package/cap/templates/requirements.md +231 -0
  164. package/cap/templates/research-project/ARCHITECTURE.md +204 -0
  165. package/cap/templates/research-project/FEATURES.md +147 -0
  166. package/cap/templates/research-project/PITFALLS.md +200 -0
  167. package/cap/templates/research-project/STACK.md +120 -0
  168. package/cap/templates/research-project/SUMMARY.md +170 -0
  169. package/cap/templates/research.md +552 -0
  170. package/cap/templates/roadmap.md +202 -0
  171. package/cap/templates/state.md +176 -0
  172. package/cap/templates/summary.md +364 -0
  173. package/cap/templates/user-preferences.md +498 -0
  174. package/cap/templates/verification-report.md +322 -0
  175. package/cap/workflows/add-phase.md +112 -0
  176. package/cap/workflows/add-tests.md +351 -0
  177. package/cap/workflows/add-todo.md +158 -0
  178. package/cap/workflows/audit-milestone.md +340 -0
  179. package/cap/workflows/audit-uat.md +109 -0
  180. package/cap/workflows/autonomous.md +891 -0
  181. package/cap/workflows/check-todos.md +177 -0
  182. package/cap/workflows/cleanup.md +152 -0
  183. package/cap/workflows/complete-milestone.md +767 -0
  184. package/cap/workflows/diagnose-issues.md +231 -0
  185. package/cap/workflows/discovery-phase.md +289 -0
  186. package/cap/workflows/discuss-phase-assumptions.md +653 -0
  187. package/cap/workflows/discuss-phase.md +1049 -0
  188. package/cap/workflows/do.md +104 -0
  189. package/cap/workflows/execute-phase.md +846 -0
  190. package/cap/workflows/execute-plan.md +514 -0
  191. package/cap/workflows/fast.md +105 -0
  192. package/cap/workflows/forensics.md +265 -0
  193. package/cap/workflows/health.md +181 -0
  194. package/cap/workflows/help.md +660 -0
  195. package/cap/workflows/insert-phase.md +130 -0
  196. package/cap/workflows/list-phase-assumptions.md +178 -0
  197. package/cap/workflows/list-workspaces.md +56 -0
  198. package/cap/workflows/manager.md +362 -0
  199. package/cap/workflows/map-codebase.md +377 -0
  200. package/cap/workflows/milestone-summary.md +223 -0
  201. package/cap/workflows/new-milestone.md +486 -0
  202. package/cap/workflows/new-project.md +1250 -0
  203. package/cap/workflows/new-workspace.md +237 -0
  204. package/cap/workflows/next.md +97 -0
  205. package/cap/workflows/node-repair.md +92 -0
  206. package/cap/workflows/note.md +156 -0
  207. package/cap/workflows/pause-work.md +176 -0
  208. package/cap/workflows/plan-milestone-gaps.md +273 -0
  209. package/cap/workflows/plan-phase.md +857 -0
  210. package/cap/workflows/plant-seed.md +169 -0
  211. package/cap/workflows/pr-branch.md +129 -0
  212. package/cap/workflows/profile-user.md +449 -0
  213. package/cap/workflows/progress.md +507 -0
  214. package/cap/workflows/quick.md +757 -0
  215. package/cap/workflows/remove-phase.md +155 -0
  216. package/cap/workflows/remove-workspace.md +90 -0
  217. package/cap/workflows/research-phase.md +82 -0
  218. package/cap/workflows/resume-project.md +326 -0
  219. package/cap/workflows/review.md +228 -0
  220. package/cap/workflows/session-report.md +146 -0
  221. package/cap/workflows/settings.md +283 -0
  222. package/cap/workflows/ship.md +228 -0
  223. package/cap/workflows/stats.md +60 -0
  224. package/cap/workflows/transition.md +671 -0
  225. package/cap/workflows/ui-phase.md +298 -0
  226. package/cap/workflows/ui-review.md +161 -0
  227. package/cap/workflows/update.md +323 -0
  228. package/cap/workflows/validate-phase.md +170 -0
  229. package/cap/workflows/verify-phase.md +254 -0
  230. package/cap/workflows/verify-work.md +637 -0
  231. package/commands/cap/annotate.md +165 -0
  232. package/commands/cap/brainstorm.md +393 -0
  233. package/commands/cap/checkpoint.md +106 -0
  234. package/commands/cap/completeness.md +94 -0
  235. package/commands/cap/continue.md +72 -0
  236. package/commands/cap/debug.md +588 -0
  237. package/commands/cap/deps.md +169 -0
  238. package/commands/cap/design.md +479 -0
  239. package/commands/cap/init.md +354 -0
  240. package/commands/cap/iterate.md +249 -0
  241. package/commands/cap/learn.md +459 -0
  242. package/commands/cap/memory.md +275 -0
  243. package/commands/cap/migrate-feature-map.md +91 -0
  244. package/commands/cap/migrate-memory.md +108 -0
  245. package/commands/cap/migrate-tags.md +91 -0
  246. package/commands/cap/migrate.md +131 -0
  247. package/commands/cap/prototype.md +510 -0
  248. package/commands/cap/reconcile.md +121 -0
  249. package/commands/cap/review.md +360 -0
  250. package/commands/cap/save.md +72 -0
  251. package/commands/cap/scan.md +404 -0
  252. package/commands/cap/start.md +356 -0
  253. package/commands/cap/status.md +118 -0
  254. package/commands/cap/test-audit.md +262 -0
  255. package/commands/cap/test.md +394 -0
  256. package/commands/cap/trace.md +133 -0
  257. package/commands/cap/ui.md +167 -0
  258. package/hooks/dist/cap-check-update.js +115 -0
  259. package/hooks/dist/cap-context-monitor.js +185 -0
  260. package/hooks/dist/cap-learn-review-hook.js +114 -0
  261. package/hooks/dist/cap-learning-hook.js +192 -0
  262. package/hooks/dist/cap-memory.js +299 -0
  263. package/hooks/dist/cap-prompt-guard.js +97 -0
  264. package/hooks/dist/cap-statusline.js +157 -0
  265. package/hooks/dist/cap-tag-observer.js +115 -0
  266. package/hooks/dist/cap-version-check.js +112 -0
  267. package/hooks/dist/cap-workflow-guard.js +175 -0
  268. package/hooks/hooks.json +55 -0
  269. package/package.json +58 -0
  270. package/scripts/base64-scan.sh +262 -0
  271. package/scripts/build-hooks.js +93 -0
  272. package/scripts/cap-removal-checklist.md +202 -0
  273. package/scripts/prompt-injection-scan.sh +199 -0
  274. package/scripts/run-tests.cjs +181 -0
  275. package/scripts/secret-scan.sh +227 -0
@@ -0,0 +1,853 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CAP Tools — CLI utility for CAP workflow operations
5
+ *
6
+ * Centralizes: config parsing, model resolution, phase lookup, git commits, summary verification.
7
+ *
8
+ * Usage: node cap-tools.cjs <command> [args] [--raw] [--pick <field>]
9
+ *
10
+ * Atomic Commands:
11
+ * state load Load project config + state
12
+ * state json Output STATE.md frontmatter as JSON
13
+ * state update <field> <value> Update a STATE.md field
14
+ * state get [section] Get STATE.md content or section
15
+ * state patch --field val ... Batch update STATE.md fields
16
+ * state begin-phase --phase N --name S --plans C Update STATE.md for new phase start
17
+ * state signal-waiting --type T --question Q --options "A|B" --phase P Write WAITING.json signal
18
+ * state signal-resume Remove WAITING.json signal
19
+ * resolve-model <agent-type> Get model for agent based on profile
20
+ * find-phase <phase> Find phase directory by number
21
+ * commit <message> [--files f1 f2] [--no-verify] Commit planning docs
22
+ * commit-to-subrepo <msg> --files f1 f2 Route commits to sub-repos
23
+ * verify-summary <path> Verify a SUMMARY.md file
24
+ * generate-slug <text> Convert text to URL-safe slug
25
+ * current-timestamp [format] Get timestamp (full|date|filename)
26
+ * list-todos [area] Count and enumerate pending todos
27
+ * verify-path-exists <path> Check file/directory existence
28
+ * config-ensure-section Initialize .planning/config.json
29
+ * history-digest Aggregate all SUMMARY.md data
30
+ * summary-extract <path> [--fields] Extract structured data from SUMMARY.md
31
+ * state-snapshot Structured parse of STATE.md
32
+ * phase-plan-index <phase> Index plans with waves and status
33
+ * websearch <query> Search web via Brave API (if configured)
34
+ * [--limit N] [--freshness day|week|month]
35
+ *
36
+ * Phase Operations:
37
+ * phase next-decimal <phase> Calculate next decimal phase number
38
+ * phase add <description> [--id ID] Append new phase to roadmap + create dir
39
+ * phase insert <after> <description> Insert decimal phase after existing
40
+ * phase remove <phase> [--force] Remove phase, renumber all subsequent
41
+ * phase complete <phase> Mark phase done, update state + roadmap
42
+ *
43
+ * Roadmap Operations:
44
+ * roadmap get-phase <phase> Extract phase section from ROADMAP.md
45
+ * roadmap analyze Full roadmap parse with disk status
46
+ * roadmap update-plan-progress <N> Update progress table row from disk (PLAN vs SUMMARY counts)
47
+ *
48
+ * Requirements Operations:
49
+ * requirements mark-complete <ids> Mark requirement IDs as complete in REQUIREMENTS.md
50
+ * Accepts: REQ-01,REQ-02 or REQ-01 REQ-02 or [REQ-01, REQ-02]
51
+ *
52
+ * Milestone Operations:
53
+ * milestone complete <version> Archive milestone, create MILESTONES.md
54
+ * [--name <name>]
55
+ * [--archive-phases] Move phase dirs to milestones/vX.Y-phases/
56
+ *
57
+ * Validation:
58
+ * validate consistency Check phase numbering, disk/roadmap sync
59
+ * validate health [--repair] Check .planning/ integrity, optionally repair
60
+ * validate agents Check GSD agent installation status
61
+ *
62
+ * Progress:
63
+ * progress [json|table|bar] Render progress in various formats
64
+ *
65
+ * Todos:
66
+ * todo complete <filename> Move todo from pending to completed
67
+ *
68
+ * UAT Audit:
69
+ * audit-uat Scan all phases for unresolved UAT/verification items
70
+ * uat render-checkpoint --file <path> Render the current UAT checkpoint block
71
+ *
72
+ * Scaffolding:
73
+ * scaffold context --phase <N> Create CONTEXT.md template
74
+ * scaffold uat --phase <N> Create UAT.md template
75
+ * scaffold verification --phase <N> Create VERIFICATION.md template
76
+ * scaffold phase-dir --phase <N> Create phase directory
77
+ * --name <name>
78
+ *
79
+ * Frontmatter CRUD:
80
+ * frontmatter get <file> [--field k] Extract frontmatter as JSON
81
+ * frontmatter set <file> --field k Update single frontmatter field
82
+ * --value jsonVal
83
+ * frontmatter merge <file> Merge JSON into frontmatter
84
+ * --data '{json}'
85
+ * frontmatter validate <file> Validate required fields
86
+ * --schema plan|summary|verification
87
+ *
88
+ * Verification Suite:
89
+ * verify plan-structure <file> Check PLAN.md structure + tasks
90
+ * verify phase-completeness <phase> Check all plans have summaries
91
+ * verify references <file> Check @-refs + paths resolve
92
+ * verify commits <h1> [h2] ... Batch verify commit hashes
93
+ * verify artifacts <plan-file> Check must_haves.artifacts
94
+ * verify key-links <plan-file> Check must_haves.key_links
95
+ *
96
+ * Template Fill:
97
+ * template fill summary --phase N Create pre-filled SUMMARY.md
98
+ * [--plan M] [--name "..."]
99
+ * [--fields '{json}']
100
+ * template fill plan --phase N Create pre-filled PLAN.md
101
+ * [--plan M] [--type execute|tdd]
102
+ * [--wave N] [--fields '{json}']
103
+ * template fill verification Create pre-filled VERIFICATION.md
104
+ * --phase N [--fields '{json}']
105
+ *
106
+ * State Progression:
107
+ * state advance-plan Increment plan counter
108
+ * state record-metric --phase N Record execution metrics
109
+ * --plan M --duration Xmin
110
+ * [--tasks N] [--files N]
111
+ * state update-progress Recalculate progress bar
112
+ * state add-decision --summary "..." Add decision to STATE.md
113
+ * [--phase N] [--rationale "..."]
114
+ * [--summary-file path] [--rationale-file path]
115
+ * state add-blocker --text "..." Add blocker
116
+ * [--text-file path]
117
+ * state resolve-blocker --text "..." Remove blocker
118
+ * state record-session Update session continuity
119
+ * --stopped-at "..."
120
+ * [--resume-file path]
121
+ *
122
+ * Compound Commands (workflow-specific initialization):
123
+ * init execute-phase <phase> All context for execute-phase workflow
124
+ * init plan-phase <phase> All context for plan-phase workflow
125
+ * init new-project All context for new-project workflow
126
+ * init new-milestone All context for new-milestone workflow
127
+ * init quick <description> All context for quick workflow
128
+ * init resume All context for resume-project workflow
129
+ * init verify-work <phase> All context for verify-work workflow
130
+ * init phase-op <phase> Generic phase operation context
131
+ * init todos [area] All context for todo workflows
132
+ * init milestone-op All context for milestone operations
133
+ * init map-codebase All context for map-codebase workflow
134
+ * init progress All context for progress workflow
135
+ *
136
+ * Test Detection:
137
+ * detect-test-framework [dir] Detect test framework from package.json
138
+ * Outputs: { framework, testCommand, filePattern }
139
+ * Defaults to cwd if dir not provided
140
+ *
141
+ * Feature Aggregation:
142
+ * aggregate-features [--output path] [--inventory path] Generate .planning/FEATURES.md from PRDs and CODE-INVENTORY.md
143
+ *
144
+ * Session & Migration Operations:
145
+ * monorepo-migrate [--output file] Audit monorepo apps for existing .planning/ dirs
146
+ * session-get Print current SESSION.json or "No session"
147
+ * session-set --app <path> Set active app in SESSION.json
148
+ * session-set --global Clear active app (root/global scope)
149
+ */
150
+
151
+ const fs = require('fs');
152
+ const path = require('path');
153
+ const core = require('./lib/core.cjs');
154
+ const { error, findProjectRoot, getActiveWorkstream } = core;
155
+ const config = require('./lib/config.cjs');
156
+ const commands = require('./lib/commands.cjs');
157
+ const phase = require('./lib/phase.cjs');
158
+ const init = require('./lib/init.cjs');
159
+ const profilePipeline = require('./lib/profile-pipeline.cjs');
160
+ const profileOutput = require('./lib/profile-output.cjs');
161
+ const arcScanner = require('./lib/arc-scanner.cjs');
162
+ const verify = require('./lib/verify.cjs');
163
+ const state = require('./lib/state.cjs');
164
+
165
+ // Routers — extracted from the formerly-monolithic switch statement.
166
+ // Each router exports a `dispatch(args, cwd, raw)` (or named variant) and
167
+ // owns the sub-switch for its command group. Routers are pure delegation —
168
+ // no business logic moved with them, only the wiring.
169
+ const stateRouter = require('./lib/cli/state-router.cjs');
170
+ const templateRouter = require('./lib/cli/template-router.cjs');
171
+ const frontmatterRouter = require('./lib/cli/frontmatter-router.cjs');
172
+ const verificationRouter = require('./lib/cli/verification-router.cjs');
173
+ const validationRouter = require('./lib/cli/validation-router.cjs');
174
+ const phaseRouter = require('./lib/cli/phase-router.cjs');
175
+ const initRouter = require('./lib/cli/init-router.cjs');
176
+ const workstreamRouter = require('./lib/cli/workstream-router.cjs');
177
+ const uatRouter = require('./lib/cli/uat-router.cjs');
178
+ const { parseNamedArgs, parseMultiwordArg } = require('./lib/cli/arg-helpers.cjs');
179
+
180
+ // ─── CLI Router ───────────────────────────────────────────────────────────────
181
+
182
+ async function main() {
183
+ const args = process.argv.slice(2);
184
+
185
+ // Optional cwd override for sandboxed subagents running outside project root.
186
+ let cwd = process.cwd();
187
+ const cwdEqArg = args.find(arg => arg.startsWith('--cwd='));
188
+ const cwdIdx = args.indexOf('--cwd');
189
+ if (cwdEqArg) {
190
+ const value = cwdEqArg.slice('--cwd='.length).trim();
191
+ if (!value) error('Missing value for --cwd');
192
+ args.splice(args.indexOf(cwdEqArg), 1);
193
+ cwd = path.resolve(value);
194
+ } else if (cwdIdx !== -1) {
195
+ const value = args[cwdIdx + 1];
196
+ if (!value || value.startsWith('--')) error('Missing value for --cwd');
197
+ args.splice(cwdIdx, 2);
198
+ cwd = path.resolve(value);
199
+ }
200
+
201
+ if (!fs.existsSync(cwd) || !fs.statSync(cwd).isDirectory()) {
202
+ error(`Invalid --cwd: ${cwd}`);
203
+ }
204
+
205
+ // Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
206
+ // However, in monorepo worktrees where the subdirectory itself owns .planning/,
207
+ // skip worktree resolution — the CWD is already the correct project root.
208
+ const { resolveWorktreeRoot } = require('./lib/core.cjs');
209
+ if (!fs.existsSync(path.join(cwd, '.planning'))) {
210
+ const worktreeRoot = resolveWorktreeRoot(cwd);
211
+ if (worktreeRoot !== cwd) {
212
+ cwd = worktreeRoot;
213
+ }
214
+ }
215
+
216
+ // Optional workstream override for parallel milestone work.
217
+ // Priority: --ws flag > GSD_WORKSTREAM env var > active-workstream file > null (flat mode)
218
+ const wsEqArg = args.find(arg => arg.startsWith('--ws='));
219
+ const wsIdx = args.indexOf('--ws');
220
+ let ws = null;
221
+ if (wsEqArg) {
222
+ ws = wsEqArg.slice('--ws='.length).trim();
223
+ if (!ws) error('Missing value for --ws');
224
+ args.splice(args.indexOf(wsEqArg), 1);
225
+ } else if (wsIdx !== -1) {
226
+ ws = args[wsIdx + 1];
227
+ if (!ws || ws.startsWith('--')) error('Missing value for --ws');
228
+ args.splice(wsIdx, 2);
229
+ } else if (process.env.GSD_WORKSTREAM) {
230
+ ws = process.env.GSD_WORKSTREAM.trim();
231
+ } else {
232
+ ws = getActiveWorkstream(cwd);
233
+ }
234
+ // Validate workstream name to prevent path traversal attacks.
235
+ if (ws && !/^[a-zA-Z0-9_-]+$/.test(ws)) {
236
+ error('Invalid workstream name: must be alphanumeric, hyphens, and underscores only');
237
+ }
238
+ // Set env var so all modules (planningDir, planningPaths) auto-resolve workstream paths
239
+ if (ws) {
240
+ process.env.GSD_WORKSTREAM = ws;
241
+ }
242
+
243
+ const rawIndex = args.indexOf('--raw');
244
+ const raw = rawIndex !== -1;
245
+ if (rawIndex !== -1) args.splice(rawIndex, 1);
246
+
247
+ // --pick <name>: extract a single field from JSON output (replaces jq dependency).
248
+ // Supports dot-notation (e.g., --pick workflow.research) and bracket notation
249
+ // for arrays (e.g., --pick directories[-1]).
250
+ const pickIdx = args.indexOf('--pick');
251
+ let pickField = null;
252
+ if (pickIdx !== -1) {
253
+ pickField = args[pickIdx + 1];
254
+ if (!pickField || pickField.startsWith('--')) error('Missing value for --pick');
255
+ args.splice(pickIdx, 2);
256
+ }
257
+
258
+ const command = args[0];
259
+
260
+ if (!command) {
261
+ error('Usage: cap-tools <command> [args] [--raw] [--pick <field>] [--cwd <path>] [--ws <name>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-new-project, init, workstream');
262
+ }
263
+
264
+ // Multi-repo guard: resolve project root for commands that read/write .planning/.
265
+ // Skip for pure-utility commands that don't touch .planning/ to avoid unnecessary
266
+ // filesystem traversal on every invocation.
267
+ const SKIP_ROOT_RESOLUTION = new Set([
268
+ 'generate-slug', 'current-timestamp', 'verify-path-exists',
269
+ 'verify-summary', 'template', 'frontmatter',
270
+ ]);
271
+ if (!SKIP_ROOT_RESOLUTION.has(command)) {
272
+ cwd = findProjectRoot(cwd);
273
+ }
274
+
275
+ // When --pick is active, intercept stdout to extract the requested field.
276
+ if (pickField) {
277
+ const origWriteSync = fs.writeSync;
278
+ const chunks = [];
279
+ fs.writeSync = function (fd, data, ...rest) {
280
+ if (fd === 1) { chunks.push(String(data)); return; }
281
+ return origWriteSync.call(fs, fd, data, ...rest);
282
+ };
283
+ const cleanup = () => {
284
+ fs.writeSync = origWriteSync;
285
+ const captured = chunks.join('');
286
+ let jsonStr = captured;
287
+ if (jsonStr.startsWith('@file:')) {
288
+ jsonStr = fs.readFileSync(jsonStr.slice(6), 'utf-8');
289
+ }
290
+ try {
291
+ const obj = JSON.parse(jsonStr);
292
+ const value = extractField(obj, pickField);
293
+ const result = value === null || value === undefined ? '' : String(value);
294
+ origWriteSync.call(fs, 1, result);
295
+ } catch {
296
+ origWriteSync.call(fs, 1, captured);
297
+ }
298
+ };
299
+ try {
300
+ await runCommand(command, args, cwd, raw);
301
+ cleanup();
302
+ } catch (e) {
303
+ fs.writeSync = origWriteSync;
304
+ throw e;
305
+ }
306
+ return;
307
+ }
308
+
309
+ await runCommand(command, args, cwd, raw);
310
+ }
311
+
312
+ /**
313
+ * Extract a field from an object using dot-notation and bracket syntax.
314
+ * Supports: 'field', 'parent.child', 'arr[-1]', 'arr[0]'
315
+ */
316
+ function extractField(obj, fieldPath) {
317
+ const parts = fieldPath.split('.');
318
+ let current = obj;
319
+ for (const part of parts) {
320
+ if (current === null || current === undefined) return undefined;
321
+ const bracketMatch = part.match(/^(.+?)\[(-?\d+)]$/);
322
+ if (bracketMatch) {
323
+ const key = bracketMatch[1];
324
+ const index = parseInt(bracketMatch[2], 10);
325
+ current = current[key];
326
+ if (!Array.isArray(current)) return undefined;
327
+ current = index < 0 ? current[current.length + index] : current[index];
328
+ } else {
329
+ current = current[part];
330
+ }
331
+ }
332
+ return current;
333
+ }
334
+
335
+ async function runCommand(command, args, cwd, raw) {
336
+ switch (command) {
337
+ case 'state':
338
+ stateRouter.dispatch(args, cwd, raw);
339
+ break;
340
+
341
+ case 'resolve-model': {
342
+ commands.cmdResolveModel(cwd, args[1], raw);
343
+ break;
344
+ }
345
+
346
+ case 'find-phase': {
347
+ phase.cmdFindPhase(cwd, args[1], raw);
348
+ break;
349
+ }
350
+
351
+ case 'commit': {
352
+ const amend = args.includes('--amend');
353
+ const noVerify = args.includes('--no-verify');
354
+ const filesIndex = args.indexOf('--files');
355
+ // Collect all positional args between command name and first flag,
356
+ // then join them — handles both quoted ("multi word msg") and
357
+ // unquoted (multi word msg) invocations from different shells
358
+ const endIndex = filesIndex !== -1 ? filesIndex : args.length;
359
+ const messageArgs = args.slice(1, endIndex).filter(a => !a.startsWith('--'));
360
+ const message = messageArgs.join(' ') || undefined;
361
+ const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
362
+ commands.cmdCommit(cwd, message, files, raw, amend, noVerify);
363
+ break;
364
+ }
365
+
366
+ case 'commit-to-subrepo': {
367
+ const message = args[1];
368
+ const filesIndex = args.indexOf('--files');
369
+ const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
370
+ commands.cmdCommitToSubrepo(cwd, message, files, raw);
371
+ break;
372
+ }
373
+
374
+ case 'verify-summary': {
375
+ const summaryPath = args[1];
376
+ const countIndex = args.indexOf('--check-count');
377
+ const checkCount = countIndex !== -1 ? parseInt(args[countIndex + 1], 10) : 2;
378
+ verify.cmdVerifySummary(cwd, summaryPath, checkCount, raw);
379
+ break;
380
+ }
381
+
382
+ case 'template':
383
+ templateRouter.dispatch(args, cwd, raw);
384
+ break;
385
+
386
+ case 'frontmatter':
387
+ frontmatterRouter.dispatch(args, cwd, raw);
388
+ break;
389
+
390
+ case 'verify':
391
+ verificationRouter.dispatch(args, cwd, raw);
392
+ break;
393
+
394
+ case 'generate-slug': {
395
+ commands.cmdGenerateSlug(args[1], raw);
396
+ break;
397
+ }
398
+
399
+ case 'current-timestamp': {
400
+ commands.cmdCurrentTimestamp(args[1] || 'full', raw);
401
+ break;
402
+ }
403
+
404
+ case 'list-todos': {
405
+ commands.cmdListTodos(cwd, args[1], raw);
406
+ break;
407
+ }
408
+
409
+ case 'verify-path-exists': {
410
+ commands.cmdVerifyPathExists(cwd, args[1], raw);
411
+ break;
412
+ }
413
+
414
+ case 'config-ensure-section': {
415
+ config.cmdConfigEnsureSection(cwd, raw);
416
+ break;
417
+ }
418
+
419
+ case 'config-set': {
420
+ config.cmdConfigSet(cwd, args[1], args[2], raw);
421
+ break;
422
+ }
423
+
424
+ case "config-set-model-profile": {
425
+ config.cmdConfigSetModelProfile(cwd, args[1], raw);
426
+ break;
427
+ }
428
+
429
+ case 'config-get': {
430
+ config.cmdConfigGet(cwd, args[1], raw);
431
+ break;
432
+ }
433
+
434
+ case 'config-new-project': {
435
+ config.cmdConfigNewProject(cwd, args[1], raw);
436
+ break;
437
+ }
438
+
439
+ case 'agent-skills': {
440
+ init.cmdAgentSkills(cwd, args[1], raw);
441
+ break;
442
+ }
443
+
444
+ case 'history-digest': {
445
+ commands.cmdHistoryDigest(cwd, raw);
446
+ break;
447
+ }
448
+
449
+ case 'phases':
450
+ phaseRouter.dispatchPhases(args, cwd, raw);
451
+ break;
452
+
453
+ case 'roadmap':
454
+ phaseRouter.dispatchRoadmap(args, cwd, raw);
455
+ break;
456
+
457
+ case 'requirements':
458
+ phaseRouter.dispatchRequirements(args, cwd, raw);
459
+ break;
460
+
461
+ case 'phase':
462
+ phaseRouter.dispatchPhase(args, cwd, raw);
463
+ break;
464
+
465
+ case 'milestone':
466
+ phaseRouter.dispatchMilestone(args, cwd, raw);
467
+ break;
468
+
469
+ case 'validate':
470
+ validationRouter.dispatch(args, cwd, raw);
471
+ break;
472
+
473
+ case 'progress': {
474
+ const subcommand = args[1] || 'json';
475
+ commands.cmdProgressRender(cwd, subcommand, raw);
476
+ break;
477
+ }
478
+
479
+ case 'audit-uat':
480
+ uatRouter.dispatchAuditUat(args, cwd, raw);
481
+ break;
482
+
483
+ case 'uat':
484
+ uatRouter.dispatchUat(args, cwd, raw);
485
+ break;
486
+
487
+ case 'stats': {
488
+ const subcommand = args[1] || 'json';
489
+ commands.cmdStats(cwd, subcommand, raw);
490
+ break;
491
+ }
492
+
493
+ case 'todo': {
494
+ const subcommand = args[1];
495
+ if (subcommand === 'complete') {
496
+ commands.cmdTodoComplete(cwd, args[2], raw);
497
+ } else if (subcommand === 'match-phase') {
498
+ commands.cmdTodoMatchPhase(cwd, args[2], raw);
499
+ } else {
500
+ error('Unknown todo subcommand. Available: complete, match-phase');
501
+ }
502
+ break;
503
+ }
504
+
505
+ case 'scaffold': {
506
+ const scaffoldType = args[1];
507
+ const scaffoldOptions = {
508
+ phase: parseNamedArgs(args, ['phase']).phase,
509
+ name: parseMultiwordArg(args, 'name'),
510
+ };
511
+ commands.cmdScaffold(cwd, scaffoldType, scaffoldOptions, raw);
512
+ break;
513
+ }
514
+
515
+ case 'init':
516
+ initRouter.dispatch(args, cwd, raw);
517
+ break;
518
+
519
+ case 'phase-plan-index': {
520
+ phase.cmdPhasePlanIndex(cwd, args[1], raw);
521
+ break;
522
+ }
523
+
524
+ case 'state-snapshot': {
525
+ state.cmdStateSnapshot(cwd, raw);
526
+ break;
527
+ }
528
+
529
+ case 'summary-extract': {
530
+ const summaryPath = args[1];
531
+ const fieldsIndex = args.indexOf('--fields');
532
+ const fields = fieldsIndex !== -1 ? args[fieldsIndex + 1].split(',') : null;
533
+ commands.cmdSummaryExtract(cwd, summaryPath, fields, raw);
534
+ break;
535
+ }
536
+
537
+ case 'websearch': {
538
+ const query = args[1];
539
+ const limitIdx = args.indexOf('--limit');
540
+ const freshnessIdx = args.indexOf('--freshness');
541
+ await commands.cmdWebsearch(query, {
542
+ limit: limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 10,
543
+ freshness: freshnessIdx !== -1 ? args[freshnessIdx + 1] : null,
544
+ }, raw);
545
+ break;
546
+ }
547
+
548
+ // ─── Profiling Pipeline ────────────────────────────────────────────────
549
+
550
+ case 'scan-sessions': {
551
+ const pathIdx = args.indexOf('--path');
552
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
553
+ const verboseFlag = args.includes('--verbose');
554
+ const jsonFlag = args.includes('--json');
555
+ await profilePipeline.cmdScanSessions(sessionsPath, { verbose: verboseFlag, json: jsonFlag }, raw);
556
+ break;
557
+ }
558
+
559
+ case 'extract-messages': {
560
+ const sessionIdx = args.indexOf('--session');
561
+ const sessionId = sessionIdx !== -1 ? args[sessionIdx + 1] : null;
562
+ const limitIdx = args.indexOf('--limit');
563
+ const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : null;
564
+ const pathIdx = args.indexOf('--path');
565
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
566
+ const projectArg = args[1];
567
+ if (!projectArg || projectArg.startsWith('--')) {
568
+ error('Usage: cap-tools extract-messages <project> [--session <id>] [--limit N] [--path <dir>]\nRun scan-sessions first to see available projects.');
569
+ }
570
+ await profilePipeline.cmdExtractMessages(projectArg, { sessionId, limit }, raw, sessionsPath);
571
+ break;
572
+ }
573
+
574
+ case 'profile-sample': {
575
+ const pathIdx = args.indexOf('--path');
576
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
577
+ const limitIdx = args.indexOf('--limit');
578
+ const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 150;
579
+ const maxPerIdx = args.indexOf('--max-per-project');
580
+ const maxPerProject = maxPerIdx !== -1 ? parseInt(args[maxPerIdx + 1], 10) : null;
581
+ const maxCharsIdx = args.indexOf('--max-chars');
582
+ const maxChars = maxCharsIdx !== -1 ? parseInt(args[maxCharsIdx + 1], 10) : 500;
583
+ await profilePipeline.cmdProfileSample(sessionsPath, { limit, maxPerProject, maxChars }, raw);
584
+ break;
585
+ }
586
+
587
+ // ─── Profile Output ──────────────────────────────────────────────────
588
+
589
+ case 'write-profile': {
590
+ const inputIdx = args.indexOf('--input');
591
+ const inputPath = inputIdx !== -1 ? args[inputIdx + 1] : null;
592
+ if (!inputPath) error('--input <analysis-json-path> is required');
593
+ const outputIdx = args.indexOf('--output');
594
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
595
+ profileOutput.cmdWriteProfile(cwd, { input: inputPath, output: outputPath }, raw);
596
+ break;
597
+ }
598
+
599
+ case 'profile-questionnaire': {
600
+ const answersIdx = args.indexOf('--answers');
601
+ const answers = answersIdx !== -1 ? args[answersIdx + 1] : null;
602
+ profileOutput.cmdProfileQuestionnaire({ answers }, raw);
603
+ break;
604
+ }
605
+
606
+ case 'generate-dev-preferences': {
607
+ const analysisIdx = args.indexOf('--analysis');
608
+ const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
609
+ const outputIdx = args.indexOf('--output');
610
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
611
+ const stackIdx = args.indexOf('--stack');
612
+ const stack = stackIdx !== -1 ? args[stackIdx + 1] : null;
613
+ profileOutput.cmdGenerateDevPreferences(cwd, { analysis: analysisPath, output: outputPath, stack }, raw);
614
+ break;
615
+ }
616
+
617
+ case 'generate-claude-profile': {
618
+ const analysisIdx = args.indexOf('--analysis');
619
+ const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
620
+ const outputIdx = args.indexOf('--output');
621
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
622
+ const globalFlag = args.includes('--global');
623
+ profileOutput.cmdGenerateClaudeProfile(cwd, { analysis: analysisPath, output: outputPath, global: globalFlag }, raw);
624
+ break;
625
+ }
626
+
627
+ case 'generate-claude-md': {
628
+ const outputIdx = args.indexOf('--output');
629
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
630
+ const autoFlag = args.includes('--auto');
631
+ const forceFlag = args.includes('--force');
632
+ profileOutput.cmdGenerateClaudeMd(cwd, { output: outputPath, auto: autoFlag, force: forceFlag }, raw);
633
+ break;
634
+ }
635
+
636
+ case 'workstream':
637
+ workstreamRouter.dispatch(args, cwd, raw);
638
+ break;
639
+
640
+ case 'extract-tags': {
641
+ const allArgs = args.slice(1);
642
+ const namedFlags = ['phase', 'type', 'format', 'output', 'app'];
643
+ const { phase: phaseFilter, type: typeFilter, format, output: outputFile, app } = parseNamedArgs(allArgs, namedFlags);
644
+ // Filter out --flag tokens and their values so positional arg detection works
645
+ const consumed = new Set();
646
+ for (const f of namedFlags) { const i = allArgs.indexOf(`--${f}`); if (i !== -1) { consumed.add(i); if (allArgs[i + 1] && !allArgs[i + 1].startsWith('--')) consumed.add(i + 1); } }
647
+ const targetPath = allArgs.filter((_, i) => !consumed.has(i) && !allArgs[i].startsWith('--'))[0] || cwd;
648
+
649
+ if (app) {
650
+ const workspaceDetector = require('./lib/workspace-detector.cjs');
651
+ const monorepoContext = require('./lib/monorepo-context.cjs');
652
+ const workspace = workspaceDetector.detectWorkspace(cwd);
653
+ const validation = workspaceDetector.validateAppPath(workspace, app);
654
+ if (!validation.valid) { error(validation.error); }
655
+ const scoped = monorepoContext.scopeExtractTags(cwd, validation.resolved.path, {
656
+ phaseFilter, typeFilter, format: format || 'json', outputFile,
657
+ });
658
+ arcScanner.cmdExtractTags(cwd, scoped.targetPath, { ...scoped });
659
+
660
+ if (scoped.outputFile) {
661
+ try {
662
+ const featureAggregator = require('./lib/feature-aggregator.cjs');
663
+ featureAggregator.cmdAggregateFeatures(cwd, {
664
+ inventoryFile: scoped.outputFile,
665
+ });
666
+ } catch (e) {
667
+ process.stderr.write(`feature-aggregator: auto-chain skipped — ${e.message}\n`);
668
+ }
669
+ }
670
+ break;
671
+ }
672
+
673
+ // Session fallback: auto-scope to SESSION.json current_app when no --app flag
674
+ {
675
+ const sessionManager = require('./lib/session-manager.cjs');
676
+ const sessionApp = sessionManager.resolveCurrentApp(cwd, null);
677
+ if (sessionApp) {
678
+ const workspaceDetector = require('./lib/workspace-detector.cjs');
679
+ const monorepoContext = require('./lib/monorepo-context.cjs');
680
+ const workspace = workspaceDetector.detectWorkspace(cwd);
681
+ const validation = workspace ? workspaceDetector.validateAppPath(workspace, sessionApp) : { valid: false };
682
+ if (validation.valid) {
683
+ const scoped = monorepoContext.scopeExtractTags(cwd, validation.resolved.path, {
684
+ phaseFilter, typeFilter, format: format || 'json', outputFile,
685
+ });
686
+ arcScanner.cmdExtractTags(cwd, scoped.targetPath, { ...scoped });
687
+
688
+ if (scoped.outputFile) {
689
+ try {
690
+ const featureAggregator = require('./lib/feature-aggregator.cjs');
691
+ featureAggregator.cmdAggregateFeatures(cwd, {
692
+ inventoryFile: scoped.outputFile,
693
+ });
694
+ } catch (e) {
695
+ process.stderr.write(`feature-aggregator: auto-chain skipped — ${e.message}\n`);
696
+ }
697
+ }
698
+ break;
699
+ }
700
+ }
701
+ }
702
+
703
+ arcScanner.cmdExtractTags(cwd, targetPath, {
704
+ phaseFilter,
705
+ typeFilter,
706
+ format: format || 'json',
707
+ outputFile,
708
+ });
709
+
710
+ // Auto-chain: regenerate FEATURES.md after extract-tags completes
711
+ // Only run when outputFile is set (markdown mode) — skip for JSON stdout mode
712
+ if (outputFile) {
713
+ try {
714
+ const featureAggregator = require('./lib/feature-aggregator.cjs');
715
+ featureAggregator.cmdAggregateFeatures(cwd, {
716
+ inventoryFile: outputFile,
717
+ });
718
+ } catch (e) {
719
+ // Non-fatal: feature aggregation failure should not break extract-tags
720
+ process.stderr.write(`feature-aggregator: auto-chain skipped — ${e.message}\n`);
721
+ }
722
+ }
723
+
724
+ break;
725
+ }
726
+
727
+ case 'set-mode': {
728
+ const allArgs = args.slice(1);
729
+ const named = parseNamedArgs(allArgs, ['phase']);
730
+ const modeValue = allArgs.find(a => !a.startsWith('--'));
731
+ const VALID_MODES = new Set(['code-first', 'plan-first', 'hybrid']);
732
+ if (!modeValue || !VALID_MODES.has(modeValue)) {
733
+ error('Usage: set-mode <code-first|plan-first|hybrid> [--phase N]');
734
+ }
735
+ const keyPath = named.phase ? `phase_modes.${named.phase}` : 'default_phase_mode';
736
+ const result = config.setConfigValue(cwd, keyPath, modeValue);
737
+ core.output(result, raw, `Mode set: ${keyPath} = ${modeValue}`);
738
+ break;
739
+ }
740
+
741
+ case 'detect-test-framework': {
742
+ const targetDir = args[1] || cwd;
743
+ const testDetector = require('./lib/test-detector.cjs');
744
+ const result = testDetector.detectTestFramework(targetDir);
745
+ core.output(result, raw, `${result.framework}: ${result.testCommand}`);
746
+ break;
747
+ }
748
+
749
+ case 'aggregate-features': {
750
+ const allArgs = args.slice(1);
751
+ const named = parseNamedArgs(allArgs, ['output', 'inventory']);
752
+ const featureAggregator = require('./lib/feature-aggregator.cjs');
753
+ featureAggregator.cmdAggregateFeatures(cwd, {
754
+ outputFile: named.output,
755
+ inventoryFile: named.inventory,
756
+ });
757
+ break;
758
+ }
759
+
760
+ case 'detect-workspace': {
761
+ const workspaceDetector = require('./lib/workspace-detector.cjs');
762
+ workspaceDetector.cmdDetectWorkspace(cwd, raw);
763
+ break;
764
+ }
765
+
766
+ case 'generate-manifest': {
767
+ const manifestGenerator = require('./lib/manifest-generator.cjs');
768
+ manifestGenerator.cmdGenerateManifest(cwd, args[1], raw);
769
+ break;
770
+ }
771
+
772
+ case 'generate-manifests': {
773
+ const workspaceDetector = require('./lib/workspace-detector.cjs');
774
+ const manifestGenerator = require('./lib/manifest-generator.cjs');
775
+ const workspace = workspaceDetector.detectWorkspace(cwd);
776
+ if (!workspace) {
777
+ process.stderr.write('No workspace detected.\n');
778
+ process.exitCode = 1;
779
+ break;
780
+ }
781
+ const written = manifestGenerator.generateAllManifests(cwd, workspace.packages, {});
782
+ for (const f of written) process.stdout.write(f + '\n');
783
+ break;
784
+ }
785
+
786
+ case 'monorepo-init-app': {
787
+ const monorepoContext = require('./lib/monorepo-context.cjs');
788
+ const appPath = args[1];
789
+ if (!appPath) { process.stderr.write('Usage: monorepo-init-app <app-relative-path>\n'); process.exitCode = 1; break; }
790
+ const appName = parseNamedArgs(args.slice(2), ['name']).name || path.basename(appPath);
791
+ const created = monorepoContext.initAppPlanning(cwd, appPath, { appName });
792
+ process.stdout.write(`Initialized: ${created}\n`);
793
+ break;
794
+ }
795
+
796
+ case 'monorepo-migrate': {
797
+ const workspaceDetector = require('./lib/workspace-detector.cjs');
798
+ const monorepoMigrator = require('./lib/monorepo-migrator.cjs');
799
+ const workspace = workspaceDetector.detectWorkspace(cwd);
800
+ if (!workspace) {
801
+ process.stderr.write('No workspace detected. Not a monorepo or missing workspace config.\n');
802
+ process.exitCode = 1;
803
+ break;
804
+ }
805
+ const audit = monorepoMigrator.auditAppPlanning(cwd, workspace.apps);
806
+ const report = monorepoMigrator.formatAuditReport(audit);
807
+ process.stdout.write(report + '\n');
808
+ const allArgs = args.slice(1);
809
+ const { output: outputFile } = parseNamedArgs(allArgs, ['output']);
810
+ if (outputFile) {
811
+ const outDir = path.dirname(outputFile);
812
+ fs.mkdirSync(outDir, { recursive: true });
813
+ fs.writeFileSync(outputFile, report + '\n', 'utf-8');
814
+ }
815
+ break;
816
+ }
817
+
818
+ case 'session-get': {
819
+ const sessionManager = require('./lib/session-manager.cjs');
820
+ const session = sessionManager.getSession(cwd);
821
+ core.output(session, raw, session ? JSON.stringify(session, null, 2) : 'No session');
822
+ break;
823
+ }
824
+
825
+ case 'session-set': {
826
+ const sessionManager = require('./lib/session-manager.cjs');
827
+ const allArgs = args.slice(1);
828
+ const { app: appPath } = parseNamedArgs(allArgs, ['app']);
829
+ const isGlobal = allArgs.includes('--global');
830
+ if (!appPath && !isGlobal) {
831
+ error('Usage: session-set --app <path> | --global');
832
+ }
833
+ const updated = isGlobal
834
+ ? sessionManager.setCurrentApp(cwd, null, [])
835
+ : sessionManager.setCurrentApp(cwd, appPath, []);
836
+ core.output(updated, raw, JSON.stringify(updated, null, 2));
837
+ break;
838
+ }
839
+
840
+ case 'session-set-default': {
841
+ const sessionManager = require('./lib/session-manager.cjs');
842
+ const appPath = args[1] || null;
843
+ const updated = sessionManager.setDefaultApp(cwd, appPath);
844
+ core.output(updated, raw, JSON.stringify(updated, null, 2));
845
+ break;
846
+ }
847
+
848
+ default:
849
+ error(`Unknown command: ${command}`);
850
+ }
851
+ }
852
+
853
+ main();