@luanpdd/kit-mcp 1.30.2 → 1.31.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 (347) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +168 -168
  3. package/gates/agent-no-recursive-dispatch.md +84 -82
  4. package/kit/COMANDOS.md +138 -138
  5. package/kit/README.md +76 -76
  6. package/kit/agents/advisor-researcher.md +107 -106
  7. package/kit/agents/ai-mutation-tester.md +1 -0
  8. package/kit/agents/assumptions-analyzer.md +108 -107
  9. package/kit/agents/audit-log-implementer.md +314 -313
  10. package/kit/agents/auditor-consistencia-isolamento.md +414 -413
  11. package/kit/agents/b2b-saas-architect.md +157 -156
  12. package/kit/agents/burn-rate-forecaster.md +1 -0
  13. package/kit/agents/cascading-failures-auditor.md +299 -298
  14. package/kit/agents/codebase-mapper.md +769 -768
  15. package/kit/agents/crm-pipeline-implementer.md +257 -256
  16. package/kit/agents/debugger.md +814 -813
  17. package/kit/agents/detector-tenant-quente.md +338 -337
  18. package/kit/agents/evolution-go-integrator.md +201 -200
  19. package/kit/agents/example-reviewer.md +22 -21
  20. package/kit/agents/executor.md +565 -564
  21. package/kit/agents/golden-signals-instrumenter.md +1 -0
  22. package/kit/agents/incident-investigator.md +1 -0
  23. package/kit/agents/integration-checker.md +201 -200
  24. package/kit/agents/invite-flow-implementer.md +190 -189
  25. package/kit/agents/legacy-characterizer.md +369 -368
  26. package/kit/agents/lgpd-compliance-auditor.md +296 -295
  27. package/kit/agents/load-shedding-instrumenter.md +1 -0
  28. package/kit/agents/multi-tenant-isolation-auditor.md +254 -253
  29. package/kit/agents/multi-tenant-rls-writer.md +341 -340
  30. package/kit/agents/nyquist-auditor.md +179 -178
  31. package/kit/agents/observability-coverage-auditor.md +316 -315
  32. package/kit/agents/observability-instrumenter.md +1 -0
  33. package/kit/agents/omm-auditor.md +1 -0
  34. package/kit/agents/org-onboarding-implementer.md +224 -223
  35. package/kit/agents/payload-capture-instrumenter.md +274 -273
  36. package/kit/agents/phase-researcher.md +697 -696
  37. package/kit/agents/plan-checker.md +273 -272
  38. package/kit/agents/planner.md +923 -922
  39. package/kit/agents/postmortem-writer.md +1 -0
  40. package/kit/agents/project-researcher.md +653 -652
  41. package/kit/agents/prr-conductor.md +1 -0
  42. package/kit/agents/refactor-safety-auditor.md +405 -404
  43. package/kit/agents/release-pipeline-auditor.md +1 -0
  44. package/kit/agents/research-synthesizer.md +246 -245
  45. package/kit/agents/roadmapper.md +678 -677
  46. package/kit/agents/schema-checker.md +1 -0
  47. package/kit/agents/seam-finder.md +360 -359
  48. package/kit/agents/shotgun-surgery-detector.md +350 -349
  49. package/kit/agents/slo-engineer.md +1 -0
  50. package/kit/agents/storytelling-analyst.md +1 -0
  51. package/kit/agents/supabase-architect.md +1 -0
  52. package/kit/agents/supabase-auth-bootstrapper.md +1 -0
  53. package/kit/agents/supabase-branching-architect.md +563 -562
  54. package/kit/agents/supabase-cicd-pipeline-implementer.md +778 -777
  55. package/kit/agents/supabase-column-privileges-writer.md +400 -399
  56. package/kit/agents/supabase-edge-fn-tester.md +2 -1
  57. package/kit/agents/supabase-edge-fn-writer.md +2 -1
  58. package/kit/agents/supabase-migration-writer.md +386 -385
  59. package/kit/agents/supabase-rbac-implementer.md +393 -392
  60. package/kit/agents/supabase-realtime-implementer.md +364 -363
  61. package/kit/agents/supabase-rls-hardener.md +522 -521
  62. package/kit/agents/supabase-rls-writer.md +324 -323
  63. package/kit/agents/supabase-roles-implementer.md +356 -355
  64. package/kit/agents/supabase-storage-implementer.md +1 -0
  65. package/kit/agents/super-admin-implementer.md +282 -281
  66. package/kit/agents/toil-auditor.md +1 -0
  67. package/kit/agents/ui-auditor.md +438 -437
  68. package/kit/agents/ui-checker.md +303 -302
  69. package/kit/agents/ui-researcher.md +356 -355
  70. package/kit/agents/user-profiler.md +176 -175
  71. package/kit/agents/validador-evolucao-schema.md +336 -335
  72. package/kit/agents/verifier.md +729 -728
  73. package/kit/commands/adicionar-backlog.md +75 -75
  74. package/kit/commands/adicionar-fase.md +42 -42
  75. package/kit/commands/adicionar-tarefa.md +45 -45
  76. package/kit/commands/adicionar-testes.md +41 -41
  77. package/kit/commands/ajuda.md +21 -21
  78. package/kit/commands/atualizar.md +37 -37
  79. package/kit/commands/auditar-cascading.md +111 -111
  80. package/kit/commands/auditar-marco.md +179 -179
  81. package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
  82. package/kit/commands/auditar-refactor.md +219 -219
  83. package/kit/commands/auditar-release.md +109 -109
  84. package/kit/commands/auditar-uat.md +23 -23
  85. package/kit/commands/autonomo.md +40 -40
  86. package/kit/commands/branch-pr.md +24 -24
  87. package/kit/commands/burn-rate-status.md +408 -408
  88. package/kit/commands/capturar-payloads.md +193 -193
  89. package/kit/commands/caracterizar.md +212 -212
  90. package/kit/commands/concluir-marco.md +247 -247
  91. package/kit/commands/configuracoes.md +36 -36
  92. package/kit/commands/dados-distribuidos.md +188 -188
  93. package/kit/commands/definir-perfil.md +10 -10
  94. package/kit/commands/depurar.md +190 -190
  95. package/kit/commands/detectar-duplicacao.md +197 -197
  96. package/kit/commands/discutir-fase.md +131 -131
  97. package/kit/commands/encontrar-seams.md +136 -136
  98. package/kit/commands/entrar-discord.md +17 -17
  99. package/kit/commands/estatisticas.md +18 -18
  100. package/kit/commands/example-greeting.md +33 -33
  101. package/kit/commands/executar-fase.md +58 -58
  102. package/kit/commands/expresso.md +56 -56
  103. package/kit/commands/fase-ui.md +34 -34
  104. package/kit/commands/fazer.md +57 -57
  105. package/kit/commands/fio.md +125 -125
  106. package/kit/commands/fluxos-trabalho.md +64 -64
  107. package/kit/commands/forense.md +176 -176
  108. package/kit/commands/gerenciador.md +38 -38
  109. package/kit/commands/inserir-fase.md +31 -31
  110. package/kit/commands/legacy.md +263 -263
  111. package/kit/commands/limpeza.md +17 -17
  112. package/kit/commands/listar-hipoteses-fase.md +45 -45
  113. package/kit/commands/listar-workspaces.md +18 -18
  114. package/kit/commands/load-shedding.md +117 -117
  115. package/kit/commands/mapear-codebase.md +70 -70
  116. package/kit/commands/multi-tenant.md +163 -163
  117. package/kit/commands/nota.md +33 -33
  118. package/kit/commands/novo-marco.md +43 -43
  119. package/kit/commands/novo-projeto.md +41 -41
  120. package/kit/commands/novo-workspace.md +43 -43
  121. package/kit/commands/pausar-trabalho.md +37 -37
  122. package/kit/commands/perfil-usuario.md +45 -45
  123. package/kit/commands/pesquisar-fase.md +195 -195
  124. package/kit/commands/planejar-fase.md +67 -67
  125. package/kit/commands/planejar-lacunas.md +33 -33
  126. package/kit/commands/plantar-ideia.md +25 -25
  127. package/kit/commands/progresso.md +24 -24
  128. package/kit/commands/proximo.md +30 -30
  129. package/kit/commands/publicar.md +490 -490
  130. package/kit/commands/rapido.md +35 -35
  131. package/kit/commands/reaplicar-patches.md +124 -124
  132. package/kit/commands/refactor-seguro.md +321 -321
  133. package/kit/commands/relatorio-sessao.md +19 -19
  134. package/kit/commands/remover-fase.md +31 -31
  135. package/kit/commands/remover-workspace.md +26 -26
  136. package/kit/commands/resumo-marco.md +50 -50
  137. package/kit/commands/retomar-trabalho.md +40 -40
  138. package/kit/commands/revisar-backlog.md +60 -60
  139. package/kit/commands/revisar-ui.md +32 -32
  140. package/kit/commands/revisar.md +37 -37
  141. package/kit/commands/saude.md +21 -21
  142. package/kit/commands/setup-notion.md +93 -93
  143. package/kit/commands/storytelling.md +179 -179
  144. package/kit/commands/sync-main.md +68 -68
  145. package/kit/commands/validar-fase.md +35 -35
  146. package/kit/commands/verificar-tarefas.md +44 -44
  147. package/kit/commands/verificar-trabalho.md +64 -64
  148. package/kit/file-manifest.json +82 -81
  149. package/kit/framework/bin/lib/commands.cjs +959 -959
  150. package/kit/framework/bin/lib/config.cjs +442 -442
  151. package/kit/framework/bin/lib/core.cjs +1230 -1230
  152. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  153. package/kit/framework/bin/lib/init.cjs +1442 -1442
  154. package/kit/framework/bin/lib/milestone.cjs +252 -252
  155. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  156. package/kit/framework/bin/lib/phase.cjs +888 -888
  157. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  158. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  159. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  160. package/kit/framework/bin/lib/security.cjs +382 -382
  161. package/kit/framework/bin/lib/state.cjs +1031 -1031
  162. package/kit/framework/bin/lib/template.cjs +222 -222
  163. package/kit/framework/bin/lib/uat.cjs +282 -282
  164. package/kit/framework/bin/lib/verify.cjs +888 -888
  165. package/kit/framework/bin/lib/workstream.cjs +491 -491
  166. package/kit/framework/bin/tools.cjs +918 -918
  167. package/kit/framework/commands/workstreams.md +63 -63
  168. package/kit/framework/references/checkpoints.md +778 -778
  169. package/kit/framework/references/continuation-format.md +249 -249
  170. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  171. package/kit/framework/references/git-integration.md +295 -295
  172. package/kit/framework/references/git-planning-commit.md +38 -38
  173. package/kit/framework/references/model-profile-resolution.md +36 -36
  174. package/kit/framework/references/model-profiles.md +139 -139
  175. package/kit/framework/references/phase-argument-parsing.md +61 -61
  176. package/kit/framework/references/planning-config.md +202 -202
  177. package/kit/framework/references/questioning.md +162 -162
  178. package/kit/framework/references/tdd.md +263 -263
  179. package/kit/framework/references/ui-brand.md +160 -160
  180. package/kit/framework/references/user-profiling.md +657 -657
  181. package/kit/framework/references/verification-patterns.md +612 -612
  182. package/kit/framework/references/workstream-flag.md +58 -58
  183. package/kit/framework/templates/DEBUG.md +164 -164
  184. package/kit/framework/templates/UAT.md +265 -265
  185. package/kit/framework/templates/UI-SPEC.md +100 -100
  186. package/kit/framework/templates/VALIDATION.md +76 -76
  187. package/kit/framework/templates/claude-md.md +122 -122
  188. package/kit/framework/templates/codebase/architecture.md +185 -185
  189. package/kit/framework/templates/codebase/concerns.md +205 -205
  190. package/kit/framework/templates/codebase/conventions.md +204 -204
  191. package/kit/framework/templates/codebase/integrations.md +192 -192
  192. package/kit/framework/templates/codebase/stack.md +158 -158
  193. package/kit/framework/templates/codebase/structure.md +199 -199
  194. package/kit/framework/templates/codebase/testing.md +301 -301
  195. package/kit/framework/templates/config.json +44 -44
  196. package/kit/framework/templates/context.md +352 -352
  197. package/kit/framework/templates/continue-here.md +78 -78
  198. package/kit/framework/templates/copilot-instructions.md +7 -7
  199. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  200. package/kit/framework/templates/dev-preferences.md +20 -20
  201. package/kit/framework/templates/discovery.md +146 -146
  202. package/kit/framework/templates/discussion-log.md +63 -63
  203. package/kit/framework/templates/milestone-archive.md +123 -123
  204. package/kit/framework/templates/milestone.md +115 -115
  205. package/kit/framework/templates/phase-prompt.md +610 -610
  206. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  207. package/kit/framework/templates/project.md +186 -186
  208. package/kit/framework/templates/requirements.md +231 -231
  209. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  210. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  211. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  212. package/kit/framework/templates/research-project/STACK.md +120 -120
  213. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  214. package/kit/framework/templates/research.md +419 -419
  215. package/kit/framework/templates/retrospective.md +54 -54
  216. package/kit/framework/templates/roadmap.md +202 -202
  217. package/kit/framework/templates/state.md +176 -176
  218. package/kit/framework/templates/summary-complex.md +59 -59
  219. package/kit/framework/templates/summary-minimal.md +41 -41
  220. package/kit/framework/templates/summary-standard.md +48 -48
  221. package/kit/framework/templates/summary.md +209 -209
  222. package/kit/framework/templates/user-profile.md +146 -146
  223. package/kit/framework/templates/user-setup.md +256 -256
  224. package/kit/framework/templates/verification-report.md +258 -258
  225. package/kit/framework/workflows/add-phase.md +112 -112
  226. package/kit/framework/workflows/add-tests.md +351 -351
  227. package/kit/framework/workflows/add-todo.md +158 -158
  228. package/kit/framework/workflows/audit-milestone.md +340 -340
  229. package/kit/framework/workflows/audit-uat.md +109 -109
  230. package/kit/framework/workflows/autonomous.md +891 -891
  231. package/kit/framework/workflows/check-todos.md +177 -177
  232. package/kit/framework/workflows/cleanup.md +152 -152
  233. package/kit/framework/workflows/complete-milestone.md +696 -696
  234. package/kit/framework/workflows/diagnose-issues.md +231 -231
  235. package/kit/framework/workflows/discovery-phase.md +289 -289
  236. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  237. package/kit/framework/workflows/discuss-phase.md +784 -784
  238. package/kit/framework/workflows/do.md +104 -104
  239. package/kit/framework/workflows/execute-phase.md +838 -838
  240. package/kit/framework/workflows/execute-plan.md +510 -510
  241. package/kit/framework/workflows/fast.md +102 -102
  242. package/kit/framework/workflows/forensics.md +265 -265
  243. package/kit/framework/workflows/health.md +181 -181
  244. package/kit/framework/workflows/help.md +619 -619
  245. package/kit/framework/workflows/insert-phase.md +130 -130
  246. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  247. package/kit/framework/workflows/list-workspaces.md +56 -56
  248. package/kit/framework/workflows/manager.md +362 -362
  249. package/kit/framework/workflows/map-codebase.md +377 -377
  250. package/kit/framework/workflows/milestone-summary.md +223 -223
  251. package/kit/framework/workflows/new-milestone.md +486 -486
  252. package/kit/framework/workflows/new-project.md +1159 -1159
  253. package/kit/framework/workflows/new-workspace.md +237 -237
  254. package/kit/framework/workflows/next.md +97 -97
  255. package/kit/framework/workflows/node-repair.md +92 -92
  256. package/kit/framework/workflows/note.md +156 -156
  257. package/kit/framework/workflows/pause-work.md +176 -176
  258. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  259. package/kit/framework/workflows/plan-phase.md +765 -765
  260. package/kit/framework/workflows/plant-seed.md +169 -169
  261. package/kit/framework/workflows/pr-branch.md +129 -129
  262. package/kit/framework/workflows/profile-user.md +450 -450
  263. package/kit/framework/workflows/progress.md +507 -507
  264. package/kit/framework/workflows/quick.md +757 -757
  265. package/kit/framework/workflows/remove-phase.md +155 -155
  266. package/kit/framework/workflows/remove-workspace.md +90 -90
  267. package/kit/framework/workflows/research-phase.md +82 -82
  268. package/kit/framework/workflows/resume-project.md +326 -326
  269. package/kit/framework/workflows/review.md +228 -228
  270. package/kit/framework/workflows/session-report.md +146 -146
  271. package/kit/framework/workflows/settings.md +283 -283
  272. package/kit/framework/workflows/ship.md +228 -228
  273. package/kit/framework/workflows/stats.md +60 -60
  274. package/kit/framework/workflows/transition.md +671 -671
  275. package/kit/framework/workflows/ui-phase.md +302 -302
  276. package/kit/framework/workflows/ui-review.md +165 -165
  277. package/kit/framework/workflows/update.md +323 -323
  278. package/kit/framework/workflows/validate-phase.md +174 -174
  279. package/kit/framework/workflows/verify-phase.md +252 -252
  280. package/kit/framework/workflows/verify-work.md +637 -637
  281. package/kit/hooks/check-update.js +118 -118
  282. package/kit/hooks/context-monitor.js +163 -163
  283. package/kit/hooks/kit-attribution-reminder.cjs +29 -50
  284. package/kit/hooks/kit-router.cjs +137 -0
  285. package/kit/hooks/prompt-guard.js +103 -103
  286. package/kit/hooks/statusline.js +125 -125
  287. package/kit/hooks/workflow-guard.js +101 -101
  288. package/kit/settings.json +45 -45
  289. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
  290. package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
  291. package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
  292. package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
  293. package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
  294. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
  295. package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
  296. package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
  297. package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
  298. package/kit/skills/example-skill/SKILL.md +42 -42
  299. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
  300. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
  301. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
  302. package/kit/skills/legacy-extract-class/SKILL.md +203 -203
  303. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
  304. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
  305. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
  306. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
  307. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
  308. package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
  309. package/kit/skills/member-invite-flow/SKILL.md +305 -305
  310. package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
  311. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
  312. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
  313. package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
  314. package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
  315. package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
  316. package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
  317. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
  318. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
  319. package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
  320. package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
  321. package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
  322. package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
  323. package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
  324. package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
  325. package/kit/skills/supabase-edge-functions/SKILL.md +1 -1
  326. package/kit/skills/supabase-edge-functions-auth/SKILL.md +1 -1
  327. package/kit/skills/supabase-edge-functions-limits/SKILL.md +1 -1
  328. package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +1 -1
  329. package/kit/skills/supabase-edge-functions-testing/SKILL.md +1 -1
  330. package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +1 -1
  331. package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
  332. package/kit/skills/supabase-migrations/SKILL.md +297 -297
  333. package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
  334. package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
  335. package/kit/skills/supabase-realtime/SKILL.md +460 -460
  336. package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
  337. package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
  338. package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
  339. package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
  340. package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
  341. package/package.json +1 -1
  342. package/src/core/kit.js +216 -216
  343. package/src/core/reflect.js +247 -247
  344. package/src/core/reverse-sync.js +372 -372
  345. package/src/core/sync.js +437 -418
  346. package/src/core/watch.js +121 -121
  347. package/src/mcp-server/index.js +794 -746
@@ -1,918 +1,918 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * framework Tools — CLI utility for framework workflow operations
5
- *
6
- * Replaces repetitive inline bash patterns across ~50 framework command/workflow/agent files.
7
- * Centralizes: config parsing, model resolution, phase lookup, git commits, summary verification.
8
- *
9
- * Usage: node tools.cjs <command> [args] [--raw] [--pick <field>]
10
- *
11
- * Atomic Commands:
12
- * state load Load project config + state
13
- * state json Output STATE.md frontmatter as JSON
14
- * state update <field> <value> Update a STATE.md field
15
- * state get [section] Get STATE.md content or section
16
- * state patch --field val ... Batch update STATE.md fields
17
- * state begin-phase --phase N --name S --plans C Update STATE.md for new phase start
18
- * state signal-waiting --type T --question Q --options "A|B" --phase P Write WAITING.json signal
19
- * state signal-resume Remove WAITING.json signal
20
- * resolve-model <agent-type> Get model for agent based on profile
21
- * find-phase <phase> Find phase directory by number
22
- * commit <message> [--files f1 f2] [--no-verify] Commit planning docs
23
- * commit-to-subrepo <msg> --files f1 f2 Route commits to sub-repos
24
- * verify-summary <path> Verify a SUMMARY.md file
25
- * generate-slug <text> Convert text to URL-safe slug
26
- * current-timestamp [format] Get timestamp (full|date|filename)
27
- * list-todos [area] Count and enumerate pending todos
28
- * verify-path-exists <path> Check file/directory existence
29
- * config-ensure-section Initialize .planning/config.json
30
- * history-digest Aggregate all SUMMARY.md data
31
- * summary-extract <path> [--fields] Extract structured data from SUMMARY.md
32
- * state-snapshot Structured parse of STATE.md
33
- * phase-plan-index <phase> Index plans with waves and status
34
- * websearch <query> Search web via Brave API (if configured)
35
- * [--limit N] [--freshness day|week|month]
36
- *
37
- * Phase Operations:
38
- * phase next-decimal <phase> Calculate next decimal phase number
39
- * phase add <description> [--id ID] Append new phase to roadmap + create dir
40
- * phase insert <after> <description> Insert decimal phase after existing
41
- * phase remove <phase> [--force] Remove phase, renumber all subsequent
42
- * phase complete <phase> Mark phase done, update state + roadmap
43
- *
44
- * Roadmap Operations:
45
- * roadmap get-phase <phase> Extract phase section from ROADMAP.md
46
- * roadmap analyze Full roadmap parse with disk status
47
- * roadmap update-plan-progress <N> Update progress table row from disk (PLAN vs SUMMARY counts)
48
- *
49
- * Requirements Operations:
50
- * requirements mark-complete <ids> Mark requirement IDs as complete in REQUIREMENTS.md
51
- * Accepts: REQ-01,REQ-02 or REQ-01 REQ-02 or [REQ-01, REQ-02]
52
- *
53
- * Milestone Operations:
54
- * milestone complete <version> Archive milestone, create MILESTONES.md
55
- * [--name <name>]
56
- * [--archive-phases] Move phase dirs to milestones/vX.Y-phases/
57
- *
58
- * Validation:
59
- * validate consistency Check phase numbering, disk/roadmap sync
60
- * validate health [--repair] Check .planning/ integrity, optionally repair
61
- * validate agents Check framework agent installation status
62
- *
63
- * Progress:
64
- * progress [json|table|bar] Render progress in various formats
65
- *
66
- * Todos:
67
- * todo complete <filename> Move todo from pending to completed
68
- *
69
- * UAT Audit:
70
- * audit-uat Scan all phases for unresolved UAT/verification items
71
- * uat render-checkpoint --file <path> Render the current UAT checkpoint block
72
- *
73
- * Scaffolding:
74
- * scaffold context --phase <N> Create CONTEXT.md template
75
- * scaffold uat --phase <N> Create UAT.md template
76
- * scaffold verification --phase <N> Create VERIFICATION.md template
77
- * scaffold phase-dir --phase <N> Create phase directory
78
- * --name <name>
79
- *
80
- * Frontmatter CRUD:
81
- * frontmatter get <file> [--field k] Extract frontmatter as JSON
82
- * frontmatter set <file> --field k Update single frontmatter field
83
- * --value jsonVal
84
- * frontmatter merge <file> Merge JSON into frontmatter
85
- * --data '{json}'
86
- * frontmatter validate <file> Validate required fields
87
- * --schema plan|summary|verification
88
- *
89
- * Verification Suite:
90
- * verify plan-structure <file> Check PLAN.md structure + tasks
91
- * verify phase-completeness <phase> Check all plans have summaries
92
- * verify references <file> Check @-refs + paths resolve
93
- * verify commits <h1> [h2] ... Batch verify commit hashes
94
- * verify artifacts <plan-file> Check must_haves.artifacts
95
- * verify key-links <plan-file> Check must_haves.key_links
96
- *
97
- * Template Fill:
98
- * template fill summary --phase N Create pre-filled SUMMARY.md
99
- * [--plan M] [--name "..."]
100
- * [--fields '{json}']
101
- * template fill plan --phase N Create pre-filled PLAN.md
102
- * [--plan M] [--type execute|tdd]
103
- * [--wave N] [--fields '{json}']
104
- * template fill verification Create pre-filled VERIFICATION.md
105
- * --phase N [--fields '{json}']
106
- *
107
- * State Progression:
108
- * state advance-plan Increment plan counter
109
- * state record-metric --phase N Record execution metrics
110
- * --plan M --duration Xmin
111
- * [--tasks N] [--files N]
112
- * state update-progress Recalculate progress bar
113
- * state add-decision --summary "..." Add decision to STATE.md
114
- * [--phase N] [--rationale "..."]
115
- * [--summary-file path] [--rationale-file path]
116
- * state add-blocker --text "..." Add blocker
117
- * [--text-file path]
118
- * state resolve-blocker --text "..." Remove blocker
119
- * state record-session Update session continuity
120
- * --stopped-at "..."
121
- * [--resume-file path]
122
- *
123
- * Compound Commands (workflow-specific initialization):
124
- * init execute-phase <phase> All context for execute-phase workflow
125
- * init plan-phase <phase> All context for plan-phase workflow
126
- * init new-project All context for new-project workflow
127
- * init new-milestone All context for new-milestone workflow
128
- * init quick <description> All context for quick workflow
129
- * init resume All context for resume-project workflow
130
- * init verify-work <phase> All context for verify-work workflow
131
- * init phase-op <phase> Generic phase operation context
132
- * init todos [area] All context for todo workflows
133
- * init milestone-op All context for milestone operations
134
- * init map-codebase All context for map-codebase workflow
135
- * init progress All context for progress workflow
136
- */
137
-
138
- const fs = require('fs');
139
- const path = require('path');
140
- const core = require('./lib/core.cjs');
141
- const { error, findProjectRoot, getActiveWorkstream } = core;
142
- const state = require('./lib/state.cjs');
143
- const phase = require('./lib/phase.cjs');
144
- const roadmap = require('./lib/roadmap.cjs');
145
- const verify = require('./lib/verify.cjs');
146
- const config = require('./lib/config.cjs');
147
- const template = require('./lib/template.cjs');
148
- const milestone = require('./lib/milestone.cjs');
149
- const commands = require('./lib/commands.cjs');
150
- const init = require('./lib/init.cjs');
151
- const frontmatter = require('./lib/frontmatter.cjs');
152
- const profilePipeline = require('./lib/profile-pipeline.cjs');
153
- const profileOutput = require('./lib/profile-output.cjs');
154
- const workstream = require('./lib/workstream.cjs');
155
-
156
- // ─── Arg parsing helpers ──────────────────────────────────────────────────────
157
-
158
- /**
159
- * Extract named --flag <value> pairs from an args array.
160
- * Returns an object mapping flag names to their values (null if absent).
161
- * Flags listed in `booleanFlags` are treated as boolean (no value consumed).
162
- *
163
- * parseNamedArgs(args, 'phase', 'plan') → { phase: '3', plan: '1' }
164
- * parseNamedArgs(args, [], ['amend', 'force']) → { amend: true, force: false }
165
- */
166
- function parseNamedArgs(args, valueFlags = [], booleanFlags = []) {
167
- const result = {};
168
- for (const flag of valueFlags) {
169
- const idx = args.indexOf(`--${flag}`);
170
- result[flag] = idx !== -1 && args[idx + 1] !== undefined && !args[idx + 1].startsWith('--')
171
- ? args[idx + 1]
172
- : null;
173
- }
174
- for (const flag of booleanFlags) {
175
- result[flag] = args.includes(`--${flag}`);
176
- }
177
- return result;
178
- }
179
-
180
- /**
181
- * Collect all tokens after --flag until the next --flag or end of args.
182
- * Handles multi-word values like --name Foo Bar Version 1.
183
- * Returns null if the flag is absent.
184
- */
185
- function parseMultiwordArg(args, flag) {
186
- const idx = args.indexOf(`--${flag}`);
187
- if (idx === -1) return null;
188
- const tokens = [];
189
- for (let i = idx + 1; i < args.length; i++) {
190
- if (args[i].startsWith('--')) break;
191
- tokens.push(args[i]);
192
- }
193
- return tokens.length > 0 ? tokens.join(' ') : null;
194
- }
195
-
196
- // ─── CLI Router ───────────────────────────────────────────────────────────────
197
-
198
- async function main() {
199
- const args = process.argv.slice(2);
200
-
201
- // Optional cwd override for sandboxed subagents running outside project root.
202
- let cwd = process.cwd();
203
- const cwdEqArg = args.find(arg => arg.startsWith('--cwd='));
204
- const cwdIdx = args.indexOf('--cwd');
205
- if (cwdEqArg) {
206
- const value = cwdEqArg.slice('--cwd='.length).trim();
207
- if (!value) error('Missing value for --cwd');
208
- args.splice(args.indexOf(cwdEqArg), 1);
209
- cwd = path.resolve(value);
210
- } else if (cwdIdx !== -1) {
211
- const value = args[cwdIdx + 1];
212
- if (!value || value.startsWith('--')) error('Missing value for --cwd');
213
- args.splice(cwdIdx, 2);
214
- cwd = path.resolve(value);
215
- }
216
-
217
- if (!fs.existsSync(cwd) || !fs.statSync(cwd).isDirectory()) {
218
- error(`Invalid --cwd: ${cwd}`);
219
- }
220
-
221
- // Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
222
- // However, in monorepo worktrees where the subdirectory itself owns .planning/,
223
- // skip worktree resolution — the CWD is already the correct project root.
224
- const { resolveWorktreeRoot } = require('./lib/core.cjs');
225
- if (!fs.existsSync(path.join(cwd, '.planning'))) {
226
- const worktreeRoot = resolveWorktreeRoot(cwd);
227
- if (worktreeRoot !== cwd) {
228
- cwd = worktreeRoot;
229
- }
230
- }
231
-
232
- // Optional workstream override for parallel milestone work.
233
- // Priority: --ws flag > WORKSTREAM env var > active-workstream file > null (flat mode)
234
- const wsEqArg = args.find(arg => arg.startsWith('--ws='));
235
- const wsIdx = args.indexOf('--ws');
236
- let ws = null;
237
- if (wsEqArg) {
238
- ws = wsEqArg.slice('--ws='.length).trim();
239
- if (!ws) error('Missing value for --ws');
240
- args.splice(args.indexOf(wsEqArg), 1);
241
- } else if (wsIdx !== -1) {
242
- ws = args[wsIdx + 1];
243
- if (!ws || ws.startsWith('--')) error('Missing value for --ws');
244
- args.splice(wsIdx, 2);
245
- } else if (process.env.WORKSTREAM) {
246
- ws = process.env.WORKSTREAM.trim();
247
- } else {
248
- ws = getActiveWorkstream(cwd);
249
- }
250
- // Validate workstream name to prevent path traversal attacks.
251
- if (ws && !/^[a-zA-Z0-9_-]+$/.test(ws)) {
252
- error('Invalid workstream name: must be alphanumeric, hyphens, and underscores only');
253
- }
254
- // Set env var so all modules (planningDir, planningPaths) auto-resolve workstream paths
255
- if (ws) {
256
- process.env.WORKSTREAM = ws;
257
- }
258
-
259
- const rawIndex = args.indexOf('--raw');
260
- const raw = rawIndex !== -1;
261
- if (rawIndex !== -1) args.splice(rawIndex, 1);
262
-
263
- // --pick <name>: extract a single field from JSON output (replaces jq dependency).
264
- // Supports dot-notation (e.g., --pick workflow.research) and bracket notation
265
- // for arrays (e.g., --pick directories[-1]).
266
- const pickIdx = args.indexOf('--pick');
267
- let pickField = null;
268
- if (pickIdx !== -1) {
269
- pickField = args[pickIdx + 1];
270
- if (!pickField || pickField.startsWith('--')) error('Missing value for --pick');
271
- args.splice(pickIdx, 2);
272
- }
273
-
274
- const command = args[0];
275
-
276
- if (!command) {
277
- error('Usage: 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');
278
- }
279
-
280
- // Multi-repo guard: resolve project root for commands that read/write .planning/.
281
- // Skip for pure-utility commands that don't touch .planning/ to avoid unnecessary
282
- // filesystem traversal on every invocation.
283
- const SKIP_ROOT_RESOLUTION = new Set([
284
- 'generate-slug', 'current-timestamp', 'verify-path-exists',
285
- 'verify-summary', 'template', 'frontmatter',
286
- ]);
287
- if (!SKIP_ROOT_RESOLUTION.has(command)) {
288
- cwd = findProjectRoot(cwd);
289
- }
290
-
291
- // When --pick is active, intercept stdout to extract the requested field.
292
- if (pickField) {
293
- const origWriteSync = fs.writeSync;
294
- const chunks = [];
295
- fs.writeSync = function (fd, data, ...rest) {
296
- if (fd === 1) { chunks.push(String(data)); return; }
297
- return origWriteSync.call(fs, fd, data, ...rest);
298
- };
299
- const cleanup = () => {
300
- fs.writeSync = origWriteSync;
301
- const captured = chunks.join('');
302
- let jsonStr = captured;
303
- if (jsonStr.startsWith('@file:')) {
304
- jsonStr = fs.readFileSync(jsonStr.slice(6), 'utf-8');
305
- }
306
- try {
307
- const obj = JSON.parse(jsonStr);
308
- const value = extractField(obj, pickField);
309
- const result = value === null || value === undefined ? '' : String(value);
310
- origWriteSync.call(fs, 1, result);
311
- } catch {
312
- origWriteSync.call(fs, 1, captured);
313
- }
314
- };
315
- try {
316
- await runCommand(command, args, cwd, raw);
317
- cleanup();
318
- } catch (e) {
319
- fs.writeSync = origWriteSync;
320
- throw e;
321
- }
322
- return;
323
- }
324
-
325
- await runCommand(command, args, cwd, raw);
326
- }
327
-
328
- /**
329
- * Extract a field from an object using dot-notation and bracket syntax.
330
- * Supports: 'field', 'parent.child', 'arr[-1]', 'arr[0]'
331
- */
332
- function extractField(obj, fieldPath) {
333
- const parts = fieldPath.split('.');
334
- let current = obj;
335
- for (const part of parts) {
336
- if (current === null || current === undefined) return undefined;
337
- const bracketMatch = part.match(/^(.+?)\[(-?\d+)]$/);
338
- if (bracketMatch) {
339
- const key = bracketMatch[1];
340
- const index = parseInt(bracketMatch[2], 10);
341
- current = current[key];
342
- if (!Array.isArray(current)) return undefined;
343
- current = index < 0 ? current[current.length + index] : current[index];
344
- } else {
345
- current = current[part];
346
- }
347
- }
348
- return current;
349
- }
350
-
351
- async function runCommand(command, args, cwd, raw) {
352
- switch (command) {
353
- case 'state': {
354
- const subcommand = args[1];
355
- if (subcommand === 'json') {
356
- state.cmdStateJson(cwd, raw);
357
- } else if (subcommand === 'update') {
358
- state.cmdStateUpdate(cwd, args[2], args[3]);
359
- } else if (subcommand === 'get') {
360
- state.cmdStateGet(cwd, args[2], raw);
361
- } else if (subcommand === 'patch') {
362
- const patches = {};
363
- for (let i = 2; i < args.length; i += 2) {
364
- const key = args[i].replace(/^--/, '');
365
- const value = args[i + 1];
366
- if (key && value !== undefined) {
367
- patches[key] = value;
368
- }
369
- }
370
- state.cmdStatePatch(cwd, patches, raw);
371
- } else if (subcommand === 'advance-plan') {
372
- state.cmdStateAdvancePlan(cwd, raw);
373
- } else if (subcommand === 'record-metric') {
374
- const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
375
- state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
376
- } else if (subcommand === 'update-progress') {
377
- state.cmdStateUpdateProgress(cwd, raw);
378
- } else if (subcommand === 'add-decision') {
379
- const { phase: p, summary, 'summary-file': summary_file, rationale, 'rationale-file': rationale_file } = parseNamedArgs(args, ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file']);
380
- state.cmdStateAddDecision(cwd, { phase: p, summary, summary_file, rationale: rationale || '', rationale_file }, raw);
381
- } else if (subcommand === 'add-blocker') {
382
- const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
383
- state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
384
- } else if (subcommand === 'resolve-blocker') {
385
- state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw);
386
- } else if (subcommand === 'record-session') {
387
- const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
388
- state.cmdStateRecordSession(cwd, { stopped_at, resume_file: resume_file || 'None' }, raw);
389
- } else if (subcommand === 'begin-phase') {
390
- const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
391
- state.cmdStateBeginPhase(cwd, p, name, plans !== null ? parseInt(plans, 10) : null, raw);
392
- } else if (subcommand === 'signal-waiting') {
393
- const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
394
- state.cmdSignalWaiting(cwd, type, question, options, p, raw);
395
- } else if (subcommand === 'signal-resume') {
396
- state.cmdSignalResume(cwd, raw);
397
- } else {
398
- state.cmdStateLoad(cwd, raw);
399
- }
400
- break;
401
- }
402
-
403
- case 'resolve-model': {
404
- commands.cmdResolveModel(cwd, args[1], raw);
405
- break;
406
- }
407
-
408
- case 'find-phase': {
409
- phase.cmdFindPhase(cwd, args[1], raw);
410
- break;
411
- }
412
-
413
- case 'commit': {
414
- const amend = args.includes('--amend');
415
- const noVerify = args.includes('--no-verify');
416
- const filesIndex = args.indexOf('--files');
417
- // Collect all positional args between command name and first flag,
418
- // then join them — handles both quoted ("multi word msg") and
419
- // unquoted (multi word msg) invocations from different shells
420
- const endIndex = filesIndex !== -1 ? filesIndex : args.length;
421
- const messageArgs = args.slice(1, endIndex).filter(a => !a.startsWith('--'));
422
- const message = messageArgs.join(' ') || undefined;
423
- const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
424
- commands.cmdCommit(cwd, message, files, raw, amend, noVerify);
425
- break;
426
- }
427
-
428
- case 'commit-to-subrepo': {
429
- const message = args[1];
430
- const filesIndex = args.indexOf('--files');
431
- const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
432
- commands.cmdCommitToSubrepo(cwd, message, files, raw);
433
- break;
434
- }
435
-
436
- case 'verify-summary': {
437
- const summaryPath = args[1];
438
- const countIndex = args.indexOf('--check-count');
439
- const checkCount = countIndex !== -1 ? parseInt(args[countIndex + 1], 10) : 2;
440
- verify.cmdVerifySummary(cwd, summaryPath, checkCount, raw);
441
- break;
442
- }
443
-
444
- case 'template': {
445
- const subcommand = args[1];
446
- if (subcommand === 'select') {
447
- template.cmdTemplateSelect(cwd, args[2], raw);
448
- } else if (subcommand === 'fill') {
449
- const templateType = args[2];
450
- const { phase, plan, name, type, wave, fields: fieldsRaw } = parseNamedArgs(args, ['phase', 'plan', 'name', 'type', 'wave', 'fields']);
451
- let fields = {};
452
- if (fieldsRaw) {
453
- const { safeJsonParse } = require('./lib/security.cjs');
454
- const result = safeJsonParse(fieldsRaw, { label: '--fields' });
455
- if (!result.ok) error(result.error);
456
- fields = result.value;
457
- }
458
- template.cmdTemplateFill(cwd, templateType, {
459
- phase, plan, name, fields,
460
- type: type || 'execute',
461
- wave: wave || '1',
462
- }, raw);
463
- } else {
464
- error('Unknown template subcommand. Available: select, fill');
465
- }
466
- break;
467
- }
468
-
469
- case 'frontmatter': {
470
- const subcommand = args[1];
471
- const file = args[2];
472
- if (subcommand === 'get') {
473
- frontmatter.cmdFrontmatterGet(cwd, file, parseNamedArgs(args, ['field']).field, raw);
474
- } else if (subcommand === 'set') {
475
- const { field, value } = parseNamedArgs(args, ['field', 'value']);
476
- frontmatter.cmdFrontmatterSet(cwd, file, field, value !== null ? value : undefined, raw);
477
- } else if (subcommand === 'merge') {
478
- frontmatter.cmdFrontmatterMerge(cwd, file, parseNamedArgs(args, ['data']).data, raw);
479
- } else if (subcommand === 'validate') {
480
- frontmatter.cmdFrontmatterValidate(cwd, file, parseNamedArgs(args, ['schema']).schema, raw);
481
- } else {
482
- error('Unknown frontmatter subcommand. Available: get, set, merge, validate');
483
- }
484
- break;
485
- }
486
-
487
- case 'verify': {
488
- const subcommand = args[1];
489
- if (subcommand === 'plan-structure') {
490
- verify.cmdVerifyPlanStructure(cwd, args[2], raw);
491
- } else if (subcommand === 'phase-completeness') {
492
- verify.cmdVerifyPhaseCompleteness(cwd, args[2], raw);
493
- } else if (subcommand === 'references') {
494
- verify.cmdVerifyReferences(cwd, args[2], raw);
495
- } else if (subcommand === 'commits') {
496
- verify.cmdVerifyCommits(cwd, args.slice(2), raw);
497
- } else if (subcommand === 'artifacts') {
498
- verify.cmdVerifyArtifacts(cwd, args[2], raw);
499
- } else if (subcommand === 'key-links') {
500
- verify.cmdVerifyKeyLinks(cwd, args[2], raw);
501
- } else {
502
- error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links');
503
- }
504
- break;
505
- }
506
-
507
- case 'generate-slug': {
508
- commands.cmdGenerateSlug(args[1], raw);
509
- break;
510
- }
511
-
512
- case 'current-timestamp': {
513
- commands.cmdCurrentTimestamp(args[1] || 'full', raw);
514
- break;
515
- }
516
-
517
- case 'list-todos': {
518
- commands.cmdListTodos(cwd, args[1], raw);
519
- break;
520
- }
521
-
522
- case 'verify-path-exists': {
523
- commands.cmdVerifyPathExists(cwd, args[1], raw);
524
- break;
525
- }
526
-
527
- case 'config-ensure-section': {
528
- config.cmdConfigEnsureSection(cwd, raw);
529
- break;
530
- }
531
-
532
- case 'config-set': {
533
- config.cmdConfigSet(cwd, args[1], args[2], raw);
534
- break;
535
- }
536
-
537
- case "config-set-model-profile": {
538
- config.cmdConfigSetModelProfile(cwd, args[1], raw);
539
- break;
540
- }
541
-
542
- case 'config-get': {
543
- config.cmdConfigGet(cwd, args[1], raw);
544
- break;
545
- }
546
-
547
- case 'config-new-project': {
548
- config.cmdConfigNewProject(cwd, args[1], raw);
549
- break;
550
- }
551
-
552
- case 'agent-skills': {
553
- init.cmdAgentSkills(cwd, args[1], raw);
554
- break;
555
- }
556
-
557
- case 'history-digest': {
558
- commands.cmdHistoryDigest(cwd, raw);
559
- break;
560
- }
561
-
562
- case 'phases': {
563
- const subcommand = args[1];
564
- if (subcommand === 'list') {
565
- const typeIndex = args.indexOf('--type');
566
- const phaseIndex = args.indexOf('--phase');
567
- const options = {
568
- type: typeIndex !== -1 ? args[typeIndex + 1] : null,
569
- phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
570
- includeArchived: args.includes('--include-archived'),
571
- };
572
- phase.cmdPhasesList(cwd, options, raw);
573
- } else {
574
- error('Unknown phases subcommand. Available: list');
575
- }
576
- break;
577
- }
578
-
579
- case 'roadmap': {
580
- const subcommand = args[1];
581
- if (subcommand === 'get-phase') {
582
- roadmap.cmdRoadmapGetPhase(cwd, args[2], raw);
583
- } else if (subcommand === 'analyze') {
584
- roadmap.cmdRoadmapAnalyze(cwd, raw);
585
- } else if (subcommand === 'update-plan-progress') {
586
- roadmap.cmdRoadmapUpdatePlanProgress(cwd, args[2], raw);
587
- } else {
588
- error('Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress');
589
- }
590
- break;
591
- }
592
-
593
- case 'requirements': {
594
- const subcommand = args[1];
595
- if (subcommand === 'mark-complete') {
596
- milestone.cmdRequirementsMarkComplete(cwd, args.slice(2), raw);
597
- } else {
598
- error('Unknown requirements subcommand. Available: mark-complete');
599
- }
600
- break;
601
- }
602
-
603
- case 'phase': {
604
- const subcommand = args[1];
605
- if (subcommand === 'next-decimal') {
606
- phase.cmdPhaseNextDecimal(cwd, args[2], raw);
607
- } else if (subcommand === 'add') {
608
- const idIdx = args.indexOf('--id');
609
- let customId = null;
610
- const descArgs = [];
611
- for (let i = 2; i < args.length; i++) {
612
- if (args[i] === '--id' && i + 1 < args.length) {
613
- customId = args[i + 1];
614
- i++; // skip value
615
- } else {
616
- descArgs.push(args[i]);
617
- }
618
- }
619
- phase.cmdPhaseAdd(cwd, descArgs.join(' '), raw, customId);
620
- } else if (subcommand === 'insert') {
621
- phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
622
- } else if (subcommand === 'remove') {
623
- const forceFlag = args.includes('--force');
624
- phase.cmdPhaseRemove(cwd, args[2], { force: forceFlag }, raw);
625
- } else if (subcommand === 'complete') {
626
- phase.cmdPhaseComplete(cwd, args[2], raw);
627
- } else {
628
- error('Unknown phase subcommand. Available: next-decimal, add, insert, remove, complete');
629
- }
630
- break;
631
- }
632
-
633
- case 'milestone': {
634
- const subcommand = args[1];
635
- if (subcommand === 'complete') {
636
- const milestoneName = parseMultiwordArg(args, 'name');
637
- const archivePhases = args.includes('--archive-phases');
638
- milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases }, raw);
639
- } else {
640
- error('Unknown milestone subcommand. Available: complete');
641
- }
642
- break;
643
- }
644
-
645
- case 'validate': {
646
- const subcommand = args[1];
647
- if (subcommand === 'consistency') {
648
- verify.cmdValidateConsistency(cwd, raw);
649
- } else if (subcommand === 'health') {
650
- const repairFlag = args.includes('--repair');
651
- verify.cmdValidateHealth(cwd, { repair: repairFlag }, raw);
652
- } else if (subcommand === 'agents') {
653
- verify.cmdValidateAgents(cwd, raw);
654
- } else {
655
- error('Unknown validate subcommand. Available: consistency, health, agents');
656
- }
657
- break;
658
- }
659
-
660
- case 'progress': {
661
- const subcommand = args[1] || 'json';
662
- commands.cmdProgressRender(cwd, subcommand, raw);
663
- break;
664
- }
665
-
666
- case 'audit-uat': {
667
- const uat = require('./lib/uat.cjs');
668
- uat.cmdAuditUat(cwd, raw);
669
- break;
670
- }
671
-
672
- case 'uat': {
673
- const subcommand = args[1];
674
- const uat = require('./lib/uat.cjs');
675
- if (subcommand === 'render-checkpoint') {
676
- const options = parseNamedArgs(args, ['file']);
677
- uat.cmdRenderCheckpoint(cwd, options, raw);
678
- } else {
679
- error('Unknown uat subcommand. Available: render-checkpoint');
680
- }
681
- break;
682
- }
683
-
684
- case 'stats': {
685
- const subcommand = args[1] || 'json';
686
- commands.cmdStats(cwd, subcommand, raw);
687
- break;
688
- }
689
-
690
- case 'todo': {
691
- const subcommand = args[1];
692
- if (subcommand === 'complete') {
693
- commands.cmdTodoComplete(cwd, args[2], raw);
694
- } else if (subcommand === 'match-phase') {
695
- commands.cmdTodoMatchPhase(cwd, args[2], raw);
696
- } else {
697
- error('Unknown todo subcommand. Available: complete, match-phase');
698
- }
699
- break;
700
- }
701
-
702
- case 'scaffold': {
703
- const scaffoldType = args[1];
704
- const scaffoldOptions = {
705
- phase: parseNamedArgs(args, ['phase']).phase,
706
- name: parseMultiwordArg(args, 'name'),
707
- };
708
- commands.cmdScaffold(cwd, scaffoldType, scaffoldOptions, raw);
709
- break;
710
- }
711
-
712
- case 'init': {
713
- const workflow = args[1];
714
- switch (workflow) {
715
- case 'execute-phase':
716
- init.cmdInitExecutePhase(cwd, args[2], raw);
717
- break;
718
- case 'plan-phase':
719
- init.cmdInitPlanPhase(cwd, args[2], raw);
720
- break;
721
- case 'new-project':
722
- init.cmdInitNewProject(cwd, raw);
723
- break;
724
- case 'new-milestone':
725
- init.cmdInitNewMilestone(cwd, raw);
726
- break;
727
- case 'quick':
728
- init.cmdInitQuick(cwd, args.slice(2).join(' '), raw);
729
- break;
730
- case 'resume':
731
- init.cmdInitResume(cwd, raw);
732
- break;
733
- case 'verify-work':
734
- init.cmdInitVerifyWork(cwd, args[2], raw);
735
- break;
736
- case 'phase-op':
737
- init.cmdInitPhaseOp(cwd, args[2], raw);
738
- break;
739
- case 'todos':
740
- init.cmdInitTodos(cwd, args[2], raw);
741
- break;
742
- case 'milestone-op':
743
- init.cmdInitMilestoneOp(cwd, raw);
744
- break;
745
- case 'map-codebase':
746
- init.cmdInitMapCodebase(cwd, raw);
747
- break;
748
- case 'progress':
749
- init.cmdInitProgress(cwd, raw);
750
- break;
751
- case 'manager':
752
- init.cmdInitManager(cwd, raw);
753
- break;
754
- case 'new-workspace':
755
- init.cmdInitNewWorkspace(cwd, raw);
756
- break;
757
- case 'list-workspaces':
758
- init.cmdInitListWorkspaces(cwd, raw);
759
- break;
760
- case 'remove-workspace':
761
- init.cmdInitRemoveWorkspace(cwd, args[2], raw);
762
- break;
763
- default:
764
- error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, resume, verify-work, phase-op, todos, milestone-op, map-codebase, progress, manager, new-workspace, list-workspaces, remove-workspace`);
765
- }
766
- break;
767
- }
768
-
769
- case 'phase-plan-index': {
770
- phase.cmdPhasePlanIndex(cwd, args[1], raw);
771
- break;
772
- }
773
-
774
- case 'state-snapshot': {
775
- state.cmdStateSnapshot(cwd, raw);
776
- break;
777
- }
778
-
779
- case 'summary-extract': {
780
- const summaryPath = args[1];
781
- const fieldsIndex = args.indexOf('--fields');
782
- const fields = fieldsIndex !== -1 ? args[fieldsIndex + 1].split(',') : null;
783
- commands.cmdSummaryExtract(cwd, summaryPath, fields, raw);
784
- break;
785
- }
786
-
787
- case 'websearch': {
788
- const query = args[1];
789
- const limitIdx = args.indexOf('--limit');
790
- const freshnessIdx = args.indexOf('--freshness');
791
- await commands.cmdWebsearch(query, {
792
- limit: limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 10,
793
- freshness: freshnessIdx !== -1 ? args[freshnessIdx + 1] : null,
794
- }, raw);
795
- break;
796
- }
797
-
798
- // ─── Profiling Pipeline ────────────────────────────────────────────────
799
-
800
- case 'scan-sessions': {
801
- const pathIdx = args.indexOf('--path');
802
- const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
803
- const verboseFlag = args.includes('--verbose');
804
- const jsonFlag = args.includes('--json');
805
- await profilePipeline.cmdScanSessions(sessionsPath, { verbose: verboseFlag, json: jsonFlag }, raw);
806
- break;
807
- }
808
-
809
- case 'extract-messages': {
810
- const sessionIdx = args.indexOf('--session');
811
- const sessionId = sessionIdx !== -1 ? args[sessionIdx + 1] : null;
812
- const limitIdx = args.indexOf('--limit');
813
- const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : null;
814
- const pathIdx = args.indexOf('--path');
815
- const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
816
- const projectArg = args[1];
817
- if (!projectArg || projectArg.startsWith('--')) {
818
- error('Usage: tools extract-messages <project> [--session <id>] [--limit N] [--path <dir>]\nRun scan-sessions first to see available projects.');
819
- }
820
- await profilePipeline.cmdExtractMessages(projectArg, { sessionId, limit }, raw, sessionsPath);
821
- break;
822
- }
823
-
824
- case 'profile-sample': {
825
- const pathIdx = args.indexOf('--path');
826
- const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
827
- const limitIdx = args.indexOf('--limit');
828
- const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 150;
829
- const maxPerIdx = args.indexOf('--max-per-project');
830
- const maxPerProject = maxPerIdx !== -1 ? parseInt(args[maxPerIdx + 1], 10) : null;
831
- const maxCharsIdx = args.indexOf('--max-chars');
832
- const maxChars = maxCharsIdx !== -1 ? parseInt(args[maxCharsIdx + 1], 10) : 500;
833
- await profilePipeline.cmdProfileSample(sessionsPath, { limit, maxPerProject, maxChars }, raw);
834
- break;
835
- }
836
-
837
- // ─── Profile Output ──────────────────────────────────────────────────
838
-
839
- case 'write-profile': {
840
- const inputIdx = args.indexOf('--input');
841
- const inputPath = inputIdx !== -1 ? args[inputIdx + 1] : null;
842
- if (!inputPath) error('--input <analysis-json-path> is required');
843
- const outputIdx = args.indexOf('--output');
844
- const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
845
- profileOutput.cmdWriteProfile(cwd, { input: inputPath, output: outputPath }, raw);
846
- break;
847
- }
848
-
849
- case 'profile-questionnaire': {
850
- const answersIdx = args.indexOf('--answers');
851
- const answers = answersIdx !== -1 ? args[answersIdx + 1] : null;
852
- profileOutput.cmdProfileQuestionnaire({ answers }, raw);
853
- break;
854
- }
855
-
856
- case 'generate-dev-preferences': {
857
- const analysisIdx = args.indexOf('--analysis');
858
- const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
859
- const outputIdx = args.indexOf('--output');
860
- const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
861
- const stackIdx = args.indexOf('--stack');
862
- const stack = stackIdx !== -1 ? args[stackIdx + 1] : null;
863
- profileOutput.cmdGenerateDevPreferences(cwd, { analysis: analysisPath, output: outputPath, stack }, raw);
864
- break;
865
- }
866
-
867
- case 'generate-claude-profile': {
868
- const analysisIdx = args.indexOf('--analysis');
869
- const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
870
- const outputIdx = args.indexOf('--output');
871
- const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
872
- const globalFlag = args.includes('--global');
873
- profileOutput.cmdGenerateClaudeProfile(cwd, { analysis: analysisPath, output: outputPath, global: globalFlag }, raw);
874
- break;
875
- }
876
-
877
- case 'generate-claude-md': {
878
- const outputIdx = args.indexOf('--output');
879
- const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
880
- const autoFlag = args.includes('--auto');
881
- const forceFlag = args.includes('--force');
882
- profileOutput.cmdGenerateClaudeMd(cwd, { output: outputPath, auto: autoFlag, force: forceFlag }, raw);
883
- break;
884
- }
885
-
886
- case 'workstream': {
887
- const subcommand = args[1];
888
- if (subcommand === 'create') {
889
- const migrateNameIdx = args.indexOf('--migrate-name');
890
- const noMigrate = args.includes('--no-migrate');
891
- workstream.cmdWorkstreamCreate(cwd, args[2], {
892
- migrate: !noMigrate,
893
- migrateName: migrateNameIdx !== -1 ? args[migrateNameIdx + 1] : null,
894
- }, raw);
895
- } else if (subcommand === 'list') {
896
- workstream.cmdWorkstreamList(cwd, raw);
897
- } else if (subcommand === 'status') {
898
- workstream.cmdWorkstreamStatus(cwd, args[2], raw);
899
- } else if (subcommand === 'complete') {
900
- workstream.cmdWorkstreamComplete(cwd, args[2], {}, raw);
901
- } else if (subcommand === 'set') {
902
- workstream.cmdWorkstreamSet(cwd, args[2], raw);
903
- } else if (subcommand === 'get') {
904
- workstream.cmdWorkstreamGet(cwd, raw);
905
- } else if (subcommand === 'progress') {
906
- workstream.cmdWorkstreamProgress(cwd, raw);
907
- } else {
908
- error('Unknown workstream subcommand. Available: create, list, status, complete, set, get, progress');
909
- }
910
- break;
911
- }
912
-
913
- default:
914
- error(`Unknown command: ${command}`);
915
- }
916
- }
917
-
918
- main();
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * framework Tools — CLI utility for framework workflow operations
5
+ *
6
+ * Replaces repetitive inline bash patterns across ~50 framework command/workflow/agent files.
7
+ * Centralizes: config parsing, model resolution, phase lookup, git commits, summary verification.
8
+ *
9
+ * Usage: node tools.cjs <command> [args] [--raw] [--pick <field>]
10
+ *
11
+ * Atomic Commands:
12
+ * state load Load project config + state
13
+ * state json Output STATE.md frontmatter as JSON
14
+ * state update <field> <value> Update a STATE.md field
15
+ * state get [section] Get STATE.md content or section
16
+ * state patch --field val ... Batch update STATE.md fields
17
+ * state begin-phase --phase N --name S --plans C Update STATE.md for new phase start
18
+ * state signal-waiting --type T --question Q --options "A|B" --phase P Write WAITING.json signal
19
+ * state signal-resume Remove WAITING.json signal
20
+ * resolve-model <agent-type> Get model for agent based on profile
21
+ * find-phase <phase> Find phase directory by number
22
+ * commit <message> [--files f1 f2] [--no-verify] Commit planning docs
23
+ * commit-to-subrepo <msg> --files f1 f2 Route commits to sub-repos
24
+ * verify-summary <path> Verify a SUMMARY.md file
25
+ * generate-slug <text> Convert text to URL-safe slug
26
+ * current-timestamp [format] Get timestamp (full|date|filename)
27
+ * list-todos [area] Count and enumerate pending todos
28
+ * verify-path-exists <path> Check file/directory existence
29
+ * config-ensure-section Initialize .planning/config.json
30
+ * history-digest Aggregate all SUMMARY.md data
31
+ * summary-extract <path> [--fields] Extract structured data from SUMMARY.md
32
+ * state-snapshot Structured parse of STATE.md
33
+ * phase-plan-index <phase> Index plans with waves and status
34
+ * websearch <query> Search web via Brave API (if configured)
35
+ * [--limit N] [--freshness day|week|month]
36
+ *
37
+ * Phase Operations:
38
+ * phase next-decimal <phase> Calculate next decimal phase number
39
+ * phase add <description> [--id ID] Append new phase to roadmap + create dir
40
+ * phase insert <after> <description> Insert decimal phase after existing
41
+ * phase remove <phase> [--force] Remove phase, renumber all subsequent
42
+ * phase complete <phase> Mark phase done, update state + roadmap
43
+ *
44
+ * Roadmap Operations:
45
+ * roadmap get-phase <phase> Extract phase section from ROADMAP.md
46
+ * roadmap analyze Full roadmap parse with disk status
47
+ * roadmap update-plan-progress <N> Update progress table row from disk (PLAN vs SUMMARY counts)
48
+ *
49
+ * Requirements Operations:
50
+ * requirements mark-complete <ids> Mark requirement IDs as complete in REQUIREMENTS.md
51
+ * Accepts: REQ-01,REQ-02 or REQ-01 REQ-02 or [REQ-01, REQ-02]
52
+ *
53
+ * Milestone Operations:
54
+ * milestone complete <version> Archive milestone, create MILESTONES.md
55
+ * [--name <name>]
56
+ * [--archive-phases] Move phase dirs to milestones/vX.Y-phases/
57
+ *
58
+ * Validation:
59
+ * validate consistency Check phase numbering, disk/roadmap sync
60
+ * validate health [--repair] Check .planning/ integrity, optionally repair
61
+ * validate agents Check framework agent installation status
62
+ *
63
+ * Progress:
64
+ * progress [json|table|bar] Render progress in various formats
65
+ *
66
+ * Todos:
67
+ * todo complete <filename> Move todo from pending to completed
68
+ *
69
+ * UAT Audit:
70
+ * audit-uat Scan all phases for unresolved UAT/verification items
71
+ * uat render-checkpoint --file <path> Render the current UAT checkpoint block
72
+ *
73
+ * Scaffolding:
74
+ * scaffold context --phase <N> Create CONTEXT.md template
75
+ * scaffold uat --phase <N> Create UAT.md template
76
+ * scaffold verification --phase <N> Create VERIFICATION.md template
77
+ * scaffold phase-dir --phase <N> Create phase directory
78
+ * --name <name>
79
+ *
80
+ * Frontmatter CRUD:
81
+ * frontmatter get <file> [--field k] Extract frontmatter as JSON
82
+ * frontmatter set <file> --field k Update single frontmatter field
83
+ * --value jsonVal
84
+ * frontmatter merge <file> Merge JSON into frontmatter
85
+ * --data '{json}'
86
+ * frontmatter validate <file> Validate required fields
87
+ * --schema plan|summary|verification
88
+ *
89
+ * Verification Suite:
90
+ * verify plan-structure <file> Check PLAN.md structure + tasks
91
+ * verify phase-completeness <phase> Check all plans have summaries
92
+ * verify references <file> Check @-refs + paths resolve
93
+ * verify commits <h1> [h2] ... Batch verify commit hashes
94
+ * verify artifacts <plan-file> Check must_haves.artifacts
95
+ * verify key-links <plan-file> Check must_haves.key_links
96
+ *
97
+ * Template Fill:
98
+ * template fill summary --phase N Create pre-filled SUMMARY.md
99
+ * [--plan M] [--name "..."]
100
+ * [--fields '{json}']
101
+ * template fill plan --phase N Create pre-filled PLAN.md
102
+ * [--plan M] [--type execute|tdd]
103
+ * [--wave N] [--fields '{json}']
104
+ * template fill verification Create pre-filled VERIFICATION.md
105
+ * --phase N [--fields '{json}']
106
+ *
107
+ * State Progression:
108
+ * state advance-plan Increment plan counter
109
+ * state record-metric --phase N Record execution metrics
110
+ * --plan M --duration Xmin
111
+ * [--tasks N] [--files N]
112
+ * state update-progress Recalculate progress bar
113
+ * state add-decision --summary "..." Add decision to STATE.md
114
+ * [--phase N] [--rationale "..."]
115
+ * [--summary-file path] [--rationale-file path]
116
+ * state add-blocker --text "..." Add blocker
117
+ * [--text-file path]
118
+ * state resolve-blocker --text "..." Remove blocker
119
+ * state record-session Update session continuity
120
+ * --stopped-at "..."
121
+ * [--resume-file path]
122
+ *
123
+ * Compound Commands (workflow-specific initialization):
124
+ * init execute-phase <phase> All context for execute-phase workflow
125
+ * init plan-phase <phase> All context for plan-phase workflow
126
+ * init new-project All context for new-project workflow
127
+ * init new-milestone All context for new-milestone workflow
128
+ * init quick <description> All context for quick workflow
129
+ * init resume All context for resume-project workflow
130
+ * init verify-work <phase> All context for verify-work workflow
131
+ * init phase-op <phase> Generic phase operation context
132
+ * init todos [area] All context for todo workflows
133
+ * init milestone-op All context for milestone operations
134
+ * init map-codebase All context for map-codebase workflow
135
+ * init progress All context for progress workflow
136
+ */
137
+
138
+ const fs = require('fs');
139
+ const path = require('path');
140
+ const core = require('./lib/core.cjs');
141
+ const { error, findProjectRoot, getActiveWorkstream } = core;
142
+ const state = require('./lib/state.cjs');
143
+ const phase = require('./lib/phase.cjs');
144
+ const roadmap = require('./lib/roadmap.cjs');
145
+ const verify = require('./lib/verify.cjs');
146
+ const config = require('./lib/config.cjs');
147
+ const template = require('./lib/template.cjs');
148
+ const milestone = require('./lib/milestone.cjs');
149
+ const commands = require('./lib/commands.cjs');
150
+ const init = require('./lib/init.cjs');
151
+ const frontmatter = require('./lib/frontmatter.cjs');
152
+ const profilePipeline = require('./lib/profile-pipeline.cjs');
153
+ const profileOutput = require('./lib/profile-output.cjs');
154
+ const workstream = require('./lib/workstream.cjs');
155
+
156
+ // ─── Arg parsing helpers ──────────────────────────────────────────────────────
157
+
158
+ /**
159
+ * Extract named --flag <value> pairs from an args array.
160
+ * Returns an object mapping flag names to their values (null if absent).
161
+ * Flags listed in `booleanFlags` are treated as boolean (no value consumed).
162
+ *
163
+ * parseNamedArgs(args, 'phase', 'plan') → { phase: '3', plan: '1' }
164
+ * parseNamedArgs(args, [], ['amend', 'force']) → { amend: true, force: false }
165
+ */
166
+ function parseNamedArgs(args, valueFlags = [], booleanFlags = []) {
167
+ const result = {};
168
+ for (const flag of valueFlags) {
169
+ const idx = args.indexOf(`--${flag}`);
170
+ result[flag] = idx !== -1 && args[idx + 1] !== undefined && !args[idx + 1].startsWith('--')
171
+ ? args[idx + 1]
172
+ : null;
173
+ }
174
+ for (const flag of booleanFlags) {
175
+ result[flag] = args.includes(`--${flag}`);
176
+ }
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Collect all tokens after --flag until the next --flag or end of args.
182
+ * Handles multi-word values like --name Foo Bar Version 1.
183
+ * Returns null if the flag is absent.
184
+ */
185
+ function parseMultiwordArg(args, flag) {
186
+ const idx = args.indexOf(`--${flag}`);
187
+ if (idx === -1) return null;
188
+ const tokens = [];
189
+ for (let i = idx + 1; i < args.length; i++) {
190
+ if (args[i].startsWith('--')) break;
191
+ tokens.push(args[i]);
192
+ }
193
+ return tokens.length > 0 ? tokens.join(' ') : null;
194
+ }
195
+
196
+ // ─── CLI Router ───────────────────────────────────────────────────────────────
197
+
198
+ async function main() {
199
+ const args = process.argv.slice(2);
200
+
201
+ // Optional cwd override for sandboxed subagents running outside project root.
202
+ let cwd = process.cwd();
203
+ const cwdEqArg = args.find(arg => arg.startsWith('--cwd='));
204
+ const cwdIdx = args.indexOf('--cwd');
205
+ if (cwdEqArg) {
206
+ const value = cwdEqArg.slice('--cwd='.length).trim();
207
+ if (!value) error('Missing value for --cwd');
208
+ args.splice(args.indexOf(cwdEqArg), 1);
209
+ cwd = path.resolve(value);
210
+ } else if (cwdIdx !== -1) {
211
+ const value = args[cwdIdx + 1];
212
+ if (!value || value.startsWith('--')) error('Missing value for --cwd');
213
+ args.splice(cwdIdx, 2);
214
+ cwd = path.resolve(value);
215
+ }
216
+
217
+ if (!fs.existsSync(cwd) || !fs.statSync(cwd).isDirectory()) {
218
+ error(`Invalid --cwd: ${cwd}`);
219
+ }
220
+
221
+ // Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
222
+ // However, in monorepo worktrees where the subdirectory itself owns .planning/,
223
+ // skip worktree resolution — the CWD is already the correct project root.
224
+ const { resolveWorktreeRoot } = require('./lib/core.cjs');
225
+ if (!fs.existsSync(path.join(cwd, '.planning'))) {
226
+ const worktreeRoot = resolveWorktreeRoot(cwd);
227
+ if (worktreeRoot !== cwd) {
228
+ cwd = worktreeRoot;
229
+ }
230
+ }
231
+
232
+ // Optional workstream override for parallel milestone work.
233
+ // Priority: --ws flag > WORKSTREAM env var > active-workstream file > null (flat mode)
234
+ const wsEqArg = args.find(arg => arg.startsWith('--ws='));
235
+ const wsIdx = args.indexOf('--ws');
236
+ let ws = null;
237
+ if (wsEqArg) {
238
+ ws = wsEqArg.slice('--ws='.length).trim();
239
+ if (!ws) error('Missing value for --ws');
240
+ args.splice(args.indexOf(wsEqArg), 1);
241
+ } else if (wsIdx !== -1) {
242
+ ws = args[wsIdx + 1];
243
+ if (!ws || ws.startsWith('--')) error('Missing value for --ws');
244
+ args.splice(wsIdx, 2);
245
+ } else if (process.env.WORKSTREAM) {
246
+ ws = process.env.WORKSTREAM.trim();
247
+ } else {
248
+ ws = getActiveWorkstream(cwd);
249
+ }
250
+ // Validate workstream name to prevent path traversal attacks.
251
+ if (ws && !/^[a-zA-Z0-9_-]+$/.test(ws)) {
252
+ error('Invalid workstream name: must be alphanumeric, hyphens, and underscores only');
253
+ }
254
+ // Set env var so all modules (planningDir, planningPaths) auto-resolve workstream paths
255
+ if (ws) {
256
+ process.env.WORKSTREAM = ws;
257
+ }
258
+
259
+ const rawIndex = args.indexOf('--raw');
260
+ const raw = rawIndex !== -1;
261
+ if (rawIndex !== -1) args.splice(rawIndex, 1);
262
+
263
+ // --pick <name>: extract a single field from JSON output (replaces jq dependency).
264
+ // Supports dot-notation (e.g., --pick workflow.research) and bracket notation
265
+ // for arrays (e.g., --pick directories[-1]).
266
+ const pickIdx = args.indexOf('--pick');
267
+ let pickField = null;
268
+ if (pickIdx !== -1) {
269
+ pickField = args[pickIdx + 1];
270
+ if (!pickField || pickField.startsWith('--')) error('Missing value for --pick');
271
+ args.splice(pickIdx, 2);
272
+ }
273
+
274
+ const command = args[0];
275
+
276
+ if (!command) {
277
+ error('Usage: 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');
278
+ }
279
+
280
+ // Multi-repo guard: resolve project root for commands that read/write .planning/.
281
+ // Skip for pure-utility commands that don't touch .planning/ to avoid unnecessary
282
+ // filesystem traversal on every invocation.
283
+ const SKIP_ROOT_RESOLUTION = new Set([
284
+ 'generate-slug', 'current-timestamp', 'verify-path-exists',
285
+ 'verify-summary', 'template', 'frontmatter',
286
+ ]);
287
+ if (!SKIP_ROOT_RESOLUTION.has(command)) {
288
+ cwd = findProjectRoot(cwd);
289
+ }
290
+
291
+ // When --pick is active, intercept stdout to extract the requested field.
292
+ if (pickField) {
293
+ const origWriteSync = fs.writeSync;
294
+ const chunks = [];
295
+ fs.writeSync = function (fd, data, ...rest) {
296
+ if (fd === 1) { chunks.push(String(data)); return; }
297
+ return origWriteSync.call(fs, fd, data, ...rest);
298
+ };
299
+ const cleanup = () => {
300
+ fs.writeSync = origWriteSync;
301
+ const captured = chunks.join('');
302
+ let jsonStr = captured;
303
+ if (jsonStr.startsWith('@file:')) {
304
+ jsonStr = fs.readFileSync(jsonStr.slice(6), 'utf-8');
305
+ }
306
+ try {
307
+ const obj = JSON.parse(jsonStr);
308
+ const value = extractField(obj, pickField);
309
+ const result = value === null || value === undefined ? '' : String(value);
310
+ origWriteSync.call(fs, 1, result);
311
+ } catch {
312
+ origWriteSync.call(fs, 1, captured);
313
+ }
314
+ };
315
+ try {
316
+ await runCommand(command, args, cwd, raw);
317
+ cleanup();
318
+ } catch (e) {
319
+ fs.writeSync = origWriteSync;
320
+ throw e;
321
+ }
322
+ return;
323
+ }
324
+
325
+ await runCommand(command, args, cwd, raw);
326
+ }
327
+
328
+ /**
329
+ * Extract a field from an object using dot-notation and bracket syntax.
330
+ * Supports: 'field', 'parent.child', 'arr[-1]', 'arr[0]'
331
+ */
332
+ function extractField(obj, fieldPath) {
333
+ const parts = fieldPath.split('.');
334
+ let current = obj;
335
+ for (const part of parts) {
336
+ if (current === null || current === undefined) return undefined;
337
+ const bracketMatch = part.match(/^(.+?)\[(-?\d+)]$/);
338
+ if (bracketMatch) {
339
+ const key = bracketMatch[1];
340
+ const index = parseInt(bracketMatch[2], 10);
341
+ current = current[key];
342
+ if (!Array.isArray(current)) return undefined;
343
+ current = index < 0 ? current[current.length + index] : current[index];
344
+ } else {
345
+ current = current[part];
346
+ }
347
+ }
348
+ return current;
349
+ }
350
+
351
+ async function runCommand(command, args, cwd, raw) {
352
+ switch (command) {
353
+ case 'state': {
354
+ const subcommand = args[1];
355
+ if (subcommand === 'json') {
356
+ state.cmdStateJson(cwd, raw);
357
+ } else if (subcommand === 'update') {
358
+ state.cmdStateUpdate(cwd, args[2], args[3]);
359
+ } else if (subcommand === 'get') {
360
+ state.cmdStateGet(cwd, args[2], raw);
361
+ } else if (subcommand === 'patch') {
362
+ const patches = {};
363
+ for (let i = 2; i < args.length; i += 2) {
364
+ const key = args[i].replace(/^--/, '');
365
+ const value = args[i + 1];
366
+ if (key && value !== undefined) {
367
+ patches[key] = value;
368
+ }
369
+ }
370
+ state.cmdStatePatch(cwd, patches, raw);
371
+ } else if (subcommand === 'advance-plan') {
372
+ state.cmdStateAdvancePlan(cwd, raw);
373
+ } else if (subcommand === 'record-metric') {
374
+ const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
375
+ state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
376
+ } else if (subcommand === 'update-progress') {
377
+ state.cmdStateUpdateProgress(cwd, raw);
378
+ } else if (subcommand === 'add-decision') {
379
+ const { phase: p, summary, 'summary-file': summary_file, rationale, 'rationale-file': rationale_file } = parseNamedArgs(args, ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file']);
380
+ state.cmdStateAddDecision(cwd, { phase: p, summary, summary_file, rationale: rationale || '', rationale_file }, raw);
381
+ } else if (subcommand === 'add-blocker') {
382
+ const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
383
+ state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
384
+ } else if (subcommand === 'resolve-blocker') {
385
+ state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw);
386
+ } else if (subcommand === 'record-session') {
387
+ const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
388
+ state.cmdStateRecordSession(cwd, { stopped_at, resume_file: resume_file || 'None' }, raw);
389
+ } else if (subcommand === 'begin-phase') {
390
+ const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
391
+ state.cmdStateBeginPhase(cwd, p, name, plans !== null ? parseInt(plans, 10) : null, raw);
392
+ } else if (subcommand === 'signal-waiting') {
393
+ const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
394
+ state.cmdSignalWaiting(cwd, type, question, options, p, raw);
395
+ } else if (subcommand === 'signal-resume') {
396
+ state.cmdSignalResume(cwd, raw);
397
+ } else {
398
+ state.cmdStateLoad(cwd, raw);
399
+ }
400
+ break;
401
+ }
402
+
403
+ case 'resolve-model': {
404
+ commands.cmdResolveModel(cwd, args[1], raw);
405
+ break;
406
+ }
407
+
408
+ case 'find-phase': {
409
+ phase.cmdFindPhase(cwd, args[1], raw);
410
+ break;
411
+ }
412
+
413
+ case 'commit': {
414
+ const amend = args.includes('--amend');
415
+ const noVerify = args.includes('--no-verify');
416
+ const filesIndex = args.indexOf('--files');
417
+ // Collect all positional args between command name and first flag,
418
+ // then join them — handles both quoted ("multi word msg") and
419
+ // unquoted (multi word msg) invocations from different shells
420
+ const endIndex = filesIndex !== -1 ? filesIndex : args.length;
421
+ const messageArgs = args.slice(1, endIndex).filter(a => !a.startsWith('--'));
422
+ const message = messageArgs.join(' ') || undefined;
423
+ const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
424
+ commands.cmdCommit(cwd, message, files, raw, amend, noVerify);
425
+ break;
426
+ }
427
+
428
+ case 'commit-to-subrepo': {
429
+ const message = args[1];
430
+ const filesIndex = args.indexOf('--files');
431
+ const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
432
+ commands.cmdCommitToSubrepo(cwd, message, files, raw);
433
+ break;
434
+ }
435
+
436
+ case 'verify-summary': {
437
+ const summaryPath = args[1];
438
+ const countIndex = args.indexOf('--check-count');
439
+ const checkCount = countIndex !== -1 ? parseInt(args[countIndex + 1], 10) : 2;
440
+ verify.cmdVerifySummary(cwd, summaryPath, checkCount, raw);
441
+ break;
442
+ }
443
+
444
+ case 'template': {
445
+ const subcommand = args[1];
446
+ if (subcommand === 'select') {
447
+ template.cmdTemplateSelect(cwd, args[2], raw);
448
+ } else if (subcommand === 'fill') {
449
+ const templateType = args[2];
450
+ const { phase, plan, name, type, wave, fields: fieldsRaw } = parseNamedArgs(args, ['phase', 'plan', 'name', 'type', 'wave', 'fields']);
451
+ let fields = {};
452
+ if (fieldsRaw) {
453
+ const { safeJsonParse } = require('./lib/security.cjs');
454
+ const result = safeJsonParse(fieldsRaw, { label: '--fields' });
455
+ if (!result.ok) error(result.error);
456
+ fields = result.value;
457
+ }
458
+ template.cmdTemplateFill(cwd, templateType, {
459
+ phase, plan, name, fields,
460
+ type: type || 'execute',
461
+ wave: wave || '1',
462
+ }, raw);
463
+ } else {
464
+ error('Unknown template subcommand. Available: select, fill');
465
+ }
466
+ break;
467
+ }
468
+
469
+ case 'frontmatter': {
470
+ const subcommand = args[1];
471
+ const file = args[2];
472
+ if (subcommand === 'get') {
473
+ frontmatter.cmdFrontmatterGet(cwd, file, parseNamedArgs(args, ['field']).field, raw);
474
+ } else if (subcommand === 'set') {
475
+ const { field, value } = parseNamedArgs(args, ['field', 'value']);
476
+ frontmatter.cmdFrontmatterSet(cwd, file, field, value !== null ? value : undefined, raw);
477
+ } else if (subcommand === 'merge') {
478
+ frontmatter.cmdFrontmatterMerge(cwd, file, parseNamedArgs(args, ['data']).data, raw);
479
+ } else if (subcommand === 'validate') {
480
+ frontmatter.cmdFrontmatterValidate(cwd, file, parseNamedArgs(args, ['schema']).schema, raw);
481
+ } else {
482
+ error('Unknown frontmatter subcommand. Available: get, set, merge, validate');
483
+ }
484
+ break;
485
+ }
486
+
487
+ case 'verify': {
488
+ const subcommand = args[1];
489
+ if (subcommand === 'plan-structure') {
490
+ verify.cmdVerifyPlanStructure(cwd, args[2], raw);
491
+ } else if (subcommand === 'phase-completeness') {
492
+ verify.cmdVerifyPhaseCompleteness(cwd, args[2], raw);
493
+ } else if (subcommand === 'references') {
494
+ verify.cmdVerifyReferences(cwd, args[2], raw);
495
+ } else if (subcommand === 'commits') {
496
+ verify.cmdVerifyCommits(cwd, args.slice(2), raw);
497
+ } else if (subcommand === 'artifacts') {
498
+ verify.cmdVerifyArtifacts(cwd, args[2], raw);
499
+ } else if (subcommand === 'key-links') {
500
+ verify.cmdVerifyKeyLinks(cwd, args[2], raw);
501
+ } else {
502
+ error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links');
503
+ }
504
+ break;
505
+ }
506
+
507
+ case 'generate-slug': {
508
+ commands.cmdGenerateSlug(args[1], raw);
509
+ break;
510
+ }
511
+
512
+ case 'current-timestamp': {
513
+ commands.cmdCurrentTimestamp(args[1] || 'full', raw);
514
+ break;
515
+ }
516
+
517
+ case 'list-todos': {
518
+ commands.cmdListTodos(cwd, args[1], raw);
519
+ break;
520
+ }
521
+
522
+ case 'verify-path-exists': {
523
+ commands.cmdVerifyPathExists(cwd, args[1], raw);
524
+ break;
525
+ }
526
+
527
+ case 'config-ensure-section': {
528
+ config.cmdConfigEnsureSection(cwd, raw);
529
+ break;
530
+ }
531
+
532
+ case 'config-set': {
533
+ config.cmdConfigSet(cwd, args[1], args[2], raw);
534
+ break;
535
+ }
536
+
537
+ case "config-set-model-profile": {
538
+ config.cmdConfigSetModelProfile(cwd, args[1], raw);
539
+ break;
540
+ }
541
+
542
+ case 'config-get': {
543
+ config.cmdConfigGet(cwd, args[1], raw);
544
+ break;
545
+ }
546
+
547
+ case 'config-new-project': {
548
+ config.cmdConfigNewProject(cwd, args[1], raw);
549
+ break;
550
+ }
551
+
552
+ case 'agent-skills': {
553
+ init.cmdAgentSkills(cwd, args[1], raw);
554
+ break;
555
+ }
556
+
557
+ case 'history-digest': {
558
+ commands.cmdHistoryDigest(cwd, raw);
559
+ break;
560
+ }
561
+
562
+ case 'phases': {
563
+ const subcommand = args[1];
564
+ if (subcommand === 'list') {
565
+ const typeIndex = args.indexOf('--type');
566
+ const phaseIndex = args.indexOf('--phase');
567
+ const options = {
568
+ type: typeIndex !== -1 ? args[typeIndex + 1] : null,
569
+ phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
570
+ includeArchived: args.includes('--include-archived'),
571
+ };
572
+ phase.cmdPhasesList(cwd, options, raw);
573
+ } else {
574
+ error('Unknown phases subcommand. Available: list');
575
+ }
576
+ break;
577
+ }
578
+
579
+ case 'roadmap': {
580
+ const subcommand = args[1];
581
+ if (subcommand === 'get-phase') {
582
+ roadmap.cmdRoadmapGetPhase(cwd, args[2], raw);
583
+ } else if (subcommand === 'analyze') {
584
+ roadmap.cmdRoadmapAnalyze(cwd, raw);
585
+ } else if (subcommand === 'update-plan-progress') {
586
+ roadmap.cmdRoadmapUpdatePlanProgress(cwd, args[2], raw);
587
+ } else {
588
+ error('Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress');
589
+ }
590
+ break;
591
+ }
592
+
593
+ case 'requirements': {
594
+ const subcommand = args[1];
595
+ if (subcommand === 'mark-complete') {
596
+ milestone.cmdRequirementsMarkComplete(cwd, args.slice(2), raw);
597
+ } else {
598
+ error('Unknown requirements subcommand. Available: mark-complete');
599
+ }
600
+ break;
601
+ }
602
+
603
+ case 'phase': {
604
+ const subcommand = args[1];
605
+ if (subcommand === 'next-decimal') {
606
+ phase.cmdPhaseNextDecimal(cwd, args[2], raw);
607
+ } else if (subcommand === 'add') {
608
+ const idIdx = args.indexOf('--id');
609
+ let customId = null;
610
+ const descArgs = [];
611
+ for (let i = 2; i < args.length; i++) {
612
+ if (args[i] === '--id' && i + 1 < args.length) {
613
+ customId = args[i + 1];
614
+ i++; // skip value
615
+ } else {
616
+ descArgs.push(args[i]);
617
+ }
618
+ }
619
+ phase.cmdPhaseAdd(cwd, descArgs.join(' '), raw, customId);
620
+ } else if (subcommand === 'insert') {
621
+ phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
622
+ } else if (subcommand === 'remove') {
623
+ const forceFlag = args.includes('--force');
624
+ phase.cmdPhaseRemove(cwd, args[2], { force: forceFlag }, raw);
625
+ } else if (subcommand === 'complete') {
626
+ phase.cmdPhaseComplete(cwd, args[2], raw);
627
+ } else {
628
+ error('Unknown phase subcommand. Available: next-decimal, add, insert, remove, complete');
629
+ }
630
+ break;
631
+ }
632
+
633
+ case 'milestone': {
634
+ const subcommand = args[1];
635
+ if (subcommand === 'complete') {
636
+ const milestoneName = parseMultiwordArg(args, 'name');
637
+ const archivePhases = args.includes('--archive-phases');
638
+ milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases }, raw);
639
+ } else {
640
+ error('Unknown milestone subcommand. Available: complete');
641
+ }
642
+ break;
643
+ }
644
+
645
+ case 'validate': {
646
+ const subcommand = args[1];
647
+ if (subcommand === 'consistency') {
648
+ verify.cmdValidateConsistency(cwd, raw);
649
+ } else if (subcommand === 'health') {
650
+ const repairFlag = args.includes('--repair');
651
+ verify.cmdValidateHealth(cwd, { repair: repairFlag }, raw);
652
+ } else if (subcommand === 'agents') {
653
+ verify.cmdValidateAgents(cwd, raw);
654
+ } else {
655
+ error('Unknown validate subcommand. Available: consistency, health, agents');
656
+ }
657
+ break;
658
+ }
659
+
660
+ case 'progress': {
661
+ const subcommand = args[1] || 'json';
662
+ commands.cmdProgressRender(cwd, subcommand, raw);
663
+ break;
664
+ }
665
+
666
+ case 'audit-uat': {
667
+ const uat = require('./lib/uat.cjs');
668
+ uat.cmdAuditUat(cwd, raw);
669
+ break;
670
+ }
671
+
672
+ case 'uat': {
673
+ const subcommand = args[1];
674
+ const uat = require('./lib/uat.cjs');
675
+ if (subcommand === 'render-checkpoint') {
676
+ const options = parseNamedArgs(args, ['file']);
677
+ uat.cmdRenderCheckpoint(cwd, options, raw);
678
+ } else {
679
+ error('Unknown uat subcommand. Available: render-checkpoint');
680
+ }
681
+ break;
682
+ }
683
+
684
+ case 'stats': {
685
+ const subcommand = args[1] || 'json';
686
+ commands.cmdStats(cwd, subcommand, raw);
687
+ break;
688
+ }
689
+
690
+ case 'todo': {
691
+ const subcommand = args[1];
692
+ if (subcommand === 'complete') {
693
+ commands.cmdTodoComplete(cwd, args[2], raw);
694
+ } else if (subcommand === 'match-phase') {
695
+ commands.cmdTodoMatchPhase(cwd, args[2], raw);
696
+ } else {
697
+ error('Unknown todo subcommand. Available: complete, match-phase');
698
+ }
699
+ break;
700
+ }
701
+
702
+ case 'scaffold': {
703
+ const scaffoldType = args[1];
704
+ const scaffoldOptions = {
705
+ phase: parseNamedArgs(args, ['phase']).phase,
706
+ name: parseMultiwordArg(args, 'name'),
707
+ };
708
+ commands.cmdScaffold(cwd, scaffoldType, scaffoldOptions, raw);
709
+ break;
710
+ }
711
+
712
+ case 'init': {
713
+ const workflow = args[1];
714
+ switch (workflow) {
715
+ case 'execute-phase':
716
+ init.cmdInitExecutePhase(cwd, args[2], raw);
717
+ break;
718
+ case 'plan-phase':
719
+ init.cmdInitPlanPhase(cwd, args[2], raw);
720
+ break;
721
+ case 'new-project':
722
+ init.cmdInitNewProject(cwd, raw);
723
+ break;
724
+ case 'new-milestone':
725
+ init.cmdInitNewMilestone(cwd, raw);
726
+ break;
727
+ case 'quick':
728
+ init.cmdInitQuick(cwd, args.slice(2).join(' '), raw);
729
+ break;
730
+ case 'resume':
731
+ init.cmdInitResume(cwd, raw);
732
+ break;
733
+ case 'verify-work':
734
+ init.cmdInitVerifyWork(cwd, args[2], raw);
735
+ break;
736
+ case 'phase-op':
737
+ init.cmdInitPhaseOp(cwd, args[2], raw);
738
+ break;
739
+ case 'todos':
740
+ init.cmdInitTodos(cwd, args[2], raw);
741
+ break;
742
+ case 'milestone-op':
743
+ init.cmdInitMilestoneOp(cwd, raw);
744
+ break;
745
+ case 'map-codebase':
746
+ init.cmdInitMapCodebase(cwd, raw);
747
+ break;
748
+ case 'progress':
749
+ init.cmdInitProgress(cwd, raw);
750
+ break;
751
+ case 'manager':
752
+ init.cmdInitManager(cwd, raw);
753
+ break;
754
+ case 'new-workspace':
755
+ init.cmdInitNewWorkspace(cwd, raw);
756
+ break;
757
+ case 'list-workspaces':
758
+ init.cmdInitListWorkspaces(cwd, raw);
759
+ break;
760
+ case 'remove-workspace':
761
+ init.cmdInitRemoveWorkspace(cwd, args[2], raw);
762
+ break;
763
+ default:
764
+ error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, resume, verify-work, phase-op, todos, milestone-op, map-codebase, progress, manager, new-workspace, list-workspaces, remove-workspace`);
765
+ }
766
+ break;
767
+ }
768
+
769
+ case 'phase-plan-index': {
770
+ phase.cmdPhasePlanIndex(cwd, args[1], raw);
771
+ break;
772
+ }
773
+
774
+ case 'state-snapshot': {
775
+ state.cmdStateSnapshot(cwd, raw);
776
+ break;
777
+ }
778
+
779
+ case 'summary-extract': {
780
+ const summaryPath = args[1];
781
+ const fieldsIndex = args.indexOf('--fields');
782
+ const fields = fieldsIndex !== -1 ? args[fieldsIndex + 1].split(',') : null;
783
+ commands.cmdSummaryExtract(cwd, summaryPath, fields, raw);
784
+ break;
785
+ }
786
+
787
+ case 'websearch': {
788
+ const query = args[1];
789
+ const limitIdx = args.indexOf('--limit');
790
+ const freshnessIdx = args.indexOf('--freshness');
791
+ await commands.cmdWebsearch(query, {
792
+ limit: limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 10,
793
+ freshness: freshnessIdx !== -1 ? args[freshnessIdx + 1] : null,
794
+ }, raw);
795
+ break;
796
+ }
797
+
798
+ // ─── Profiling Pipeline ────────────────────────────────────────────────
799
+
800
+ case 'scan-sessions': {
801
+ const pathIdx = args.indexOf('--path');
802
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
803
+ const verboseFlag = args.includes('--verbose');
804
+ const jsonFlag = args.includes('--json');
805
+ await profilePipeline.cmdScanSessions(sessionsPath, { verbose: verboseFlag, json: jsonFlag }, raw);
806
+ break;
807
+ }
808
+
809
+ case 'extract-messages': {
810
+ const sessionIdx = args.indexOf('--session');
811
+ const sessionId = sessionIdx !== -1 ? args[sessionIdx + 1] : null;
812
+ const limitIdx = args.indexOf('--limit');
813
+ const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : null;
814
+ const pathIdx = args.indexOf('--path');
815
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
816
+ const projectArg = args[1];
817
+ if (!projectArg || projectArg.startsWith('--')) {
818
+ error('Usage: tools extract-messages <project> [--session <id>] [--limit N] [--path <dir>]\nRun scan-sessions first to see available projects.');
819
+ }
820
+ await profilePipeline.cmdExtractMessages(projectArg, { sessionId, limit }, raw, sessionsPath);
821
+ break;
822
+ }
823
+
824
+ case 'profile-sample': {
825
+ const pathIdx = args.indexOf('--path');
826
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
827
+ const limitIdx = args.indexOf('--limit');
828
+ const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 150;
829
+ const maxPerIdx = args.indexOf('--max-per-project');
830
+ const maxPerProject = maxPerIdx !== -1 ? parseInt(args[maxPerIdx + 1], 10) : null;
831
+ const maxCharsIdx = args.indexOf('--max-chars');
832
+ const maxChars = maxCharsIdx !== -1 ? parseInt(args[maxCharsIdx + 1], 10) : 500;
833
+ await profilePipeline.cmdProfileSample(sessionsPath, { limit, maxPerProject, maxChars }, raw);
834
+ break;
835
+ }
836
+
837
+ // ─── Profile Output ──────────────────────────────────────────────────
838
+
839
+ case 'write-profile': {
840
+ const inputIdx = args.indexOf('--input');
841
+ const inputPath = inputIdx !== -1 ? args[inputIdx + 1] : null;
842
+ if (!inputPath) error('--input <analysis-json-path> is required');
843
+ const outputIdx = args.indexOf('--output');
844
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
845
+ profileOutput.cmdWriteProfile(cwd, { input: inputPath, output: outputPath }, raw);
846
+ break;
847
+ }
848
+
849
+ case 'profile-questionnaire': {
850
+ const answersIdx = args.indexOf('--answers');
851
+ const answers = answersIdx !== -1 ? args[answersIdx + 1] : null;
852
+ profileOutput.cmdProfileQuestionnaire({ answers }, raw);
853
+ break;
854
+ }
855
+
856
+ case 'generate-dev-preferences': {
857
+ const analysisIdx = args.indexOf('--analysis');
858
+ const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
859
+ const outputIdx = args.indexOf('--output');
860
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
861
+ const stackIdx = args.indexOf('--stack');
862
+ const stack = stackIdx !== -1 ? args[stackIdx + 1] : null;
863
+ profileOutput.cmdGenerateDevPreferences(cwd, { analysis: analysisPath, output: outputPath, stack }, raw);
864
+ break;
865
+ }
866
+
867
+ case 'generate-claude-profile': {
868
+ const analysisIdx = args.indexOf('--analysis');
869
+ const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
870
+ const outputIdx = args.indexOf('--output');
871
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
872
+ const globalFlag = args.includes('--global');
873
+ profileOutput.cmdGenerateClaudeProfile(cwd, { analysis: analysisPath, output: outputPath, global: globalFlag }, raw);
874
+ break;
875
+ }
876
+
877
+ case 'generate-claude-md': {
878
+ const outputIdx = args.indexOf('--output');
879
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
880
+ const autoFlag = args.includes('--auto');
881
+ const forceFlag = args.includes('--force');
882
+ profileOutput.cmdGenerateClaudeMd(cwd, { output: outputPath, auto: autoFlag, force: forceFlag }, raw);
883
+ break;
884
+ }
885
+
886
+ case 'workstream': {
887
+ const subcommand = args[1];
888
+ if (subcommand === 'create') {
889
+ const migrateNameIdx = args.indexOf('--migrate-name');
890
+ const noMigrate = args.includes('--no-migrate');
891
+ workstream.cmdWorkstreamCreate(cwd, args[2], {
892
+ migrate: !noMigrate,
893
+ migrateName: migrateNameIdx !== -1 ? args[migrateNameIdx + 1] : null,
894
+ }, raw);
895
+ } else if (subcommand === 'list') {
896
+ workstream.cmdWorkstreamList(cwd, raw);
897
+ } else if (subcommand === 'status') {
898
+ workstream.cmdWorkstreamStatus(cwd, args[2], raw);
899
+ } else if (subcommand === 'complete') {
900
+ workstream.cmdWorkstreamComplete(cwd, args[2], {}, raw);
901
+ } else if (subcommand === 'set') {
902
+ workstream.cmdWorkstreamSet(cwd, args[2], raw);
903
+ } else if (subcommand === 'get') {
904
+ workstream.cmdWorkstreamGet(cwd, raw);
905
+ } else if (subcommand === 'progress') {
906
+ workstream.cmdWorkstreamProgress(cwd, raw);
907
+ } else {
908
+ error('Unknown workstream subcommand. Available: create, list, status, complete, set, get, progress');
909
+ }
910
+ break;
911
+ }
912
+
913
+ default:
914
+ error(`Unknown command: ${command}`);
915
+ }
916
+ }
917
+
918
+ main();