@jokerized/getresearchdone 0.4.1

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 (711) hide show
  1. package/.claude-plugin/plugin.json +103 -0
  2. package/README.md +211 -0
  3. package/agents/grd-baseline-assessor.md +684 -0
  4. package/agents/grd-code-reviewer.md +300 -0
  5. package/agents/grd-codebase-mapper.md +355 -0
  6. package/agents/grd-critique-agent.md +119 -0
  7. package/agents/grd-debugger.md +519 -0
  8. package/agents/grd-deep-diver.md +737 -0
  9. package/agents/grd-eval-planner.md +913 -0
  10. package/agents/grd-eval-reporter.md +717 -0
  11. package/agents/grd-executor.md +683 -0
  12. package/agents/grd-feasibility-analyst.md +624 -0
  13. package/agents/grd-integration-checker.md +367 -0
  14. package/agents/grd-knowledge-miner.md +81 -0
  15. package/agents/grd-migrator.md +88 -0
  16. package/agents/grd-phase-researcher.md +697 -0
  17. package/agents/grd-plan-checker.md +443 -0
  18. package/agents/grd-planner.md +1532 -0
  19. package/agents/grd-product-owner.md +562 -0
  20. package/agents/grd-project-researcher.md +513 -0
  21. package/agents/grd-research-synthesizer.md +273 -0
  22. package/agents/grd-roadmapper.md +798 -0
  23. package/agents/grd-surveyor.md +566 -0
  24. package/agents/grd-verifier.md +893 -0
  25. package/bin/gd.js +4 -0
  26. package/bin/gd.ts +227 -0
  27. package/bin/grd-manifest.js +4 -0
  28. package/bin/grd-manifest.ts +286 -0
  29. package/bin/grd-mcp-server.js +4 -0
  30. package/bin/grd-mcp-server.ts +124 -0
  31. package/bin/grd-tools.js +4 -0
  32. package/bin/grd-tools.ts +2471 -0
  33. package/bin/postinstall.js +4 -0
  34. package/bin/postinstall.ts +80 -0
  35. package/commands/add-phase.md +123 -0
  36. package/commands/add-todo.md +87 -0
  37. package/commands/assess-baseline.md +289 -0
  38. package/commands/autopilot.md +100 -0
  39. package/commands/autoplan.md +55 -0
  40. package/commands/check-todos.md +87 -0
  41. package/commands/compare-methods.md +262 -0
  42. package/commands/complete-milestone.md +225 -0
  43. package/commands/debug.md +372 -0
  44. package/commands/deep-dive.md +288 -0
  45. package/commands/discover.md +281 -0
  46. package/commands/discuss-phase.md +188 -0
  47. package/commands/discuss.md +55 -0
  48. package/commands/eval-report.md +310 -0
  49. package/commands/evolve.md +79 -0
  50. package/commands/execute-phase.md +1017 -0
  51. package/commands/feasibility.md +292 -0
  52. package/commands/help.md +407 -0
  53. package/commands/init.md +1508 -0
  54. package/commands/insert-phase.md +113 -0
  55. package/commands/iterate.md +327 -0
  56. package/commands/list-phase-assumptions.md +217 -0
  57. package/commands/long-term-roadmap.md +202 -0
  58. package/commands/map-codebase.md +111 -0
  59. package/commands/migrate.md +159 -0
  60. package/commands/new-milestone.md +169 -0
  61. package/commands/pause-work.md +83 -0
  62. package/commands/plan-milestone-gaps.md +373 -0
  63. package/commands/plan-phase.md +655 -0
  64. package/commands/principles.md +328 -0
  65. package/commands/product-plan.md +319 -0
  66. package/commands/progress.md +481 -0
  67. package/commands/quick.md +167 -0
  68. package/commands/reapply-patches.md +154 -0
  69. package/commands/remove-phase.md +97 -0
  70. package/commands/requirement.md +96 -0
  71. package/commands/resume-project.md +113 -0
  72. package/commands/settings.md +1144 -0
  73. package/commands/survey.md +242 -0
  74. package/commands/sync.md +246 -0
  75. package/commands/tracker-setup.md +322 -0
  76. package/commands/update.md +202 -0
  77. package/commands/verify-phase.md +335 -0
  78. package/commands/verify-work.md +701 -0
  79. package/commands/wireup.md +29 -0
  80. package/dist/bin/gd.d.ts +3 -0
  81. package/dist/bin/gd.d.ts.map +1 -0
  82. package/dist/bin/gd.js +178 -0
  83. package/dist/bin/gd.js.map +1 -0
  84. package/dist/bin/grd-manifest.d.ts +3 -0
  85. package/dist/bin/grd-manifest.d.ts.map +1 -0
  86. package/dist/bin/grd-manifest.js +202 -0
  87. package/dist/bin/grd-manifest.js.map +1 -0
  88. package/dist/bin/grd-mcp-server.d.ts +3 -0
  89. package/dist/bin/grd-mcp-server.d.ts.map +1 -0
  90. package/dist/bin/grd-mcp-server.js +71 -0
  91. package/dist/bin/grd-mcp-server.js.map +1 -0
  92. package/dist/bin/grd-tools.d.ts +3 -0
  93. package/dist/bin/grd-tools.d.ts.map +1 -0
  94. package/dist/bin/grd-tools.js +1680 -0
  95. package/dist/bin/grd-tools.js.map +1 -0
  96. package/dist/bin/postinstall.d.ts +3 -0
  97. package/dist/bin/postinstall.d.ts.map +1 -0
  98. package/dist/bin/postinstall.js +61 -0
  99. package/dist/bin/postinstall.js.map +1 -0
  100. package/dist/lib/autopilot-milestone.d.ts +2 -0
  101. package/dist/lib/autopilot-milestone.d.ts.map +1 -0
  102. package/dist/lib/autopilot-milestone.js +94 -0
  103. package/dist/lib/autopilot-milestone.js.map +1 -0
  104. package/dist/lib/autopilot-pipeline.d.ts +2 -0
  105. package/dist/lib/autopilot-pipeline.d.ts.map +1 -0
  106. package/dist/lib/autopilot-pipeline.js +830 -0
  107. package/dist/lib/autopilot-pipeline.js.map +1 -0
  108. package/dist/lib/autopilot-waves.d.ts +2 -0
  109. package/dist/lib/autopilot-waves.d.ts.map +1 -0
  110. package/dist/lib/autopilot-waves.js +266 -0
  111. package/dist/lib/autopilot-waves.js.map +1 -0
  112. package/dist/lib/autopilot.d.ts +2 -0
  113. package/dist/lib/autopilot.d.ts.map +1 -0
  114. package/dist/lib/autopilot.js +1314 -0
  115. package/dist/lib/autopilot.js.map +1 -0
  116. package/dist/lib/autoplan.d.ts +2 -0
  117. package/dist/lib/autoplan.d.ts.map +1 -0
  118. package/dist/lib/autoplan.js +198 -0
  119. package/dist/lib/autoplan.js.map +1 -0
  120. package/dist/lib/autoresearch.d.ts +2 -0
  121. package/dist/lib/autoresearch.d.ts.map +1 -0
  122. package/dist/lib/autoresearch.js +626 -0
  123. package/dist/lib/autoresearch.js.map +1 -0
  124. package/dist/lib/backend.d.ts +2 -0
  125. package/dist/lib/backend.d.ts.map +1 -0
  126. package/dist/lib/backend.js +1036 -0
  127. package/dist/lib/backend.js.map +1 -0
  128. package/dist/lib/benchmark.d.ts +99 -0
  129. package/dist/lib/benchmark.d.ts.map +1 -0
  130. package/dist/lib/benchmark.js +278 -0
  131. package/dist/lib/benchmark.js.map +1 -0
  132. package/dist/lib/citations.d.ts +2 -0
  133. package/dist/lib/citations.d.ts.map +1 -0
  134. package/dist/lib/citations.js +642 -0
  135. package/dist/lib/citations.js.map +1 -0
  136. package/dist/lib/cleanup.d.ts +2 -0
  137. package/dist/lib/cleanup.d.ts.map +1 -0
  138. package/dist/lib/cleanup.js +1222 -0
  139. package/dist/lib/cleanup.js.map +1 -0
  140. package/dist/lib/cli/adapters.d.ts +10 -0
  141. package/dist/lib/cli/adapters.d.ts.map +1 -0
  142. package/dist/lib/cli/adapters.js +27 -0
  143. package/dist/lib/cli/adapters.js.map +1 -0
  144. package/dist/lib/cli/agent.d.ts +17 -0
  145. package/dist/lib/cli/agent.d.ts.map +1 -0
  146. package/dist/lib/cli/agent.js +53 -0
  147. package/dist/lib/cli/agent.js.map +1 -0
  148. package/dist/lib/cli/index.d.ts +21 -0
  149. package/dist/lib/cli/index.d.ts.map +1 -0
  150. package/dist/lib/cli/index.js +264 -0
  151. package/dist/lib/cli/index.js.map +1 -0
  152. package/dist/lib/cli/output.d.ts +20 -0
  153. package/dist/lib/cli/output.d.ts.map +1 -0
  154. package/dist/lib/cli/output.js +22 -0
  155. package/dist/lib/cli/output.js.map +1 -0
  156. package/dist/lib/cli/scan-dispatch.d.ts +9 -0
  157. package/dist/lib/cli/scan-dispatch.d.ts.map +1 -0
  158. package/dist/lib/cli/scan-dispatch.js +107 -0
  159. package/dist/lib/cli/scan-dispatch.js.map +1 -0
  160. package/dist/lib/cli/tools.d.ts +16 -0
  161. package/dist/lib/cli/tools.d.ts.map +1 -0
  162. package/dist/lib/cli/tools.js +168 -0
  163. package/dist/lib/cli/tools.js.map +1 -0
  164. package/dist/lib/commands/_dashboard-parsers.d.ts +2 -0
  165. package/dist/lib/commands/_dashboard-parsers.d.ts.map +1 -0
  166. package/dist/lib/commands/_dashboard-parsers.js +192 -0
  167. package/dist/lib/commands/_dashboard-parsers.js.map +1 -0
  168. package/dist/lib/commands/analysis.d.ts +2 -0
  169. package/dist/lib/commands/analysis.d.ts.map +1 -0
  170. package/dist/lib/commands/analysis.js +1418 -0
  171. package/dist/lib/commands/analysis.js.map +1 -0
  172. package/dist/lib/commands/assumptions.d.ts +2 -0
  173. package/dist/lib/commands/assumptions.d.ts.map +1 -0
  174. package/dist/lib/commands/assumptions.js +166 -0
  175. package/dist/lib/commands/assumptions.js.map +1 -0
  176. package/dist/lib/commands/blame.d.ts +2 -0
  177. package/dist/lib/commands/blame.d.ts.map +1 -0
  178. package/dist/lib/commands/blame.js +133 -0
  179. package/dist/lib/commands/blame.js.map +1 -0
  180. package/dist/lib/commands/budget.d.ts +2 -0
  181. package/dist/lib/commands/budget.d.ts.map +1 -0
  182. package/dist/lib/commands/budget.js +100 -0
  183. package/dist/lib/commands/budget.js.map +1 -0
  184. package/dist/lib/commands/check-plans.d.ts +2 -0
  185. package/dist/lib/commands/check-plans.d.ts.map +1 -0
  186. package/dist/lib/commands/check-plans.js +190 -0
  187. package/dist/lib/commands/check-plans.js.map +1 -0
  188. package/dist/lib/commands/config.d.ts +2 -0
  189. package/dist/lib/commands/config.d.ts.map +1 -0
  190. package/dist/lib/commands/config.js +188 -0
  191. package/dist/lib/commands/config.js.map +1 -0
  192. package/dist/lib/commands/dashboard.d.ts +2 -0
  193. package/dist/lib/commands/dashboard.d.ts.map +1 -0
  194. package/dist/lib/commands/dashboard.js +466 -0
  195. package/dist/lib/commands/dashboard.js.map +1 -0
  196. package/dist/lib/commands/estimate.d.ts +2 -0
  197. package/dist/lib/commands/estimate.d.ts.map +1 -0
  198. package/dist/lib/commands/estimate.js +148 -0
  199. package/dist/lib/commands/estimate.js.map +1 -0
  200. package/dist/lib/commands/eval-diff.d.ts +2 -0
  201. package/dist/lib/commands/eval-diff.d.ts.map +1 -0
  202. package/dist/lib/commands/eval-diff.js +213 -0
  203. package/dist/lib/commands/eval-diff.js.map +1 -0
  204. package/dist/lib/commands/freshness.d.ts +2 -0
  205. package/dist/lib/commands/freshness.d.ts.map +1 -0
  206. package/dist/lib/commands/freshness.js +163 -0
  207. package/dist/lib/commands/freshness.js.map +1 -0
  208. package/dist/lib/commands/health.d.ts +2 -0
  209. package/dist/lib/commands/health.d.ts.map +1 -0
  210. package/dist/lib/commands/health.js +435 -0
  211. package/dist/lib/commands/health.js.map +1 -0
  212. package/dist/lib/commands/index.d.ts +2 -0
  213. package/dist/lib/commands/index.d.ts.map +1 -0
  214. package/dist/lib/commands/index.js +128 -0
  215. package/dist/lib/commands/index.js.map +1 -0
  216. package/dist/lib/commands/install.d.ts +56 -0
  217. package/dist/lib/commands/install.d.ts.map +1 -0
  218. package/dist/lib/commands/install.js +214 -0
  219. package/dist/lib/commands/install.js.map +1 -0
  220. package/dist/lib/commands/knowhow-aggregator.d.ts +2 -0
  221. package/dist/lib/commands/knowhow-aggregator.d.ts.map +1 -0
  222. package/dist/lib/commands/knowhow-aggregator.js +279 -0
  223. package/dist/lib/commands/knowhow-aggregator.js.map +1 -0
  224. package/dist/lib/commands/knowledge-search.d.ts +2 -0
  225. package/dist/lib/commands/knowledge-search.d.ts.map +1 -0
  226. package/dist/lib/commands/knowledge-search.js +113 -0
  227. package/dist/lib/commands/knowledge-search.js.map +1 -0
  228. package/dist/lib/commands/long-term-roadmap.d.ts +2 -0
  229. package/dist/lib/commands/long-term-roadmap.d.ts.map +1 -0
  230. package/dist/lib/commands/long-term-roadmap.js +272 -0
  231. package/dist/lib/commands/long-term-roadmap.js.map +1 -0
  232. package/dist/lib/commands/patterns.d.ts +91 -0
  233. package/dist/lib/commands/patterns.d.ts.map +1 -0
  234. package/dist/lib/commands/patterns.js +391 -0
  235. package/dist/lib/commands/patterns.js.map +1 -0
  236. package/dist/lib/commands/phase-info.d.ts +2 -0
  237. package/dist/lib/commands/phase-info.d.ts.map +1 -0
  238. package/dist/lib/commands/phase-info.js +509 -0
  239. package/dist/lib/commands/phase-info.js.map +1 -0
  240. package/dist/lib/commands/plan-lint.d.ts +56 -0
  241. package/dist/lib/commands/plan-lint.d.ts.map +1 -0
  242. package/dist/lib/commands/plan-lint.js +481 -0
  243. package/dist/lib/commands/plan-lint.js.map +1 -0
  244. package/dist/lib/commands/plan-phase.d.ts +53 -0
  245. package/dist/lib/commands/plan-phase.d.ts.map +1 -0
  246. package/dist/lib/commands/plan-phase.js +288 -0
  247. package/dist/lib/commands/plan-phase.js.map +1 -0
  248. package/dist/lib/commands/progress.d.ts +2 -0
  249. package/dist/lib/commands/progress.d.ts.map +1 -0
  250. package/dist/lib/commands/progress.js +266 -0
  251. package/dist/lib/commands/progress.js.map +1 -0
  252. package/dist/lib/commands/quality.d.ts +2 -0
  253. package/dist/lib/commands/quality.d.ts.map +1 -0
  254. package/dist/lib/commands/quality.js +80 -0
  255. package/dist/lib/commands/quality.js.map +1 -0
  256. package/dist/lib/commands/rollback.d.ts +2 -0
  257. package/dist/lib/commands/rollback.d.ts.map +1 -0
  258. package/dist/lib/commands/rollback.js +145 -0
  259. package/dist/lib/commands/rollback.js.map +1 -0
  260. package/dist/lib/commands/scan.d.ts +25 -0
  261. package/dist/lib/commands/scan.d.ts.map +1 -0
  262. package/dist/lib/commands/scan.js +28 -0
  263. package/dist/lib/commands/scan.js.map +1 -0
  264. package/dist/lib/commands/search.d.ts +2 -0
  265. package/dist/lib/commands/search.d.ts.map +1 -0
  266. package/dist/lib/commands/search.js +212 -0
  267. package/dist/lib/commands/search.js.map +1 -0
  268. package/dist/lib/commands/select-candidate.d.ts +128 -0
  269. package/dist/lib/commands/select-candidate.d.ts.map +1 -0
  270. package/dist/lib/commands/select-candidate.js +518 -0
  271. package/dist/lib/commands/select-candidate.js.map +1 -0
  272. package/dist/lib/commands/singularity.d.ts +2 -0
  273. package/dist/lib/commands/singularity.d.ts.map +1 -0
  274. package/dist/lib/commands/singularity.js +185 -0
  275. package/dist/lib/commands/singularity.js.map +1 -0
  276. package/dist/lib/commands/slug-timestamp.d.ts +2 -0
  277. package/dist/lib/commands/slug-timestamp.d.ts.map +1 -0
  278. package/dist/lib/commands/slug-timestamp.js +54 -0
  279. package/dist/lib/commands/slug-timestamp.js.map +1 -0
  280. package/dist/lib/commands/tail.d.ts +2 -0
  281. package/dist/lib/commands/tail.d.ts.map +1 -0
  282. package/dist/lib/commands/tail.js +100 -0
  283. package/dist/lib/commands/tail.js.map +1 -0
  284. package/dist/lib/commands/todo.d.ts +2 -0
  285. package/dist/lib/commands/todo.d.ts.map +1 -0
  286. package/dist/lib/commands/todo.js +200 -0
  287. package/dist/lib/commands/todo.js.map +1 -0
  288. package/dist/lib/commands/watch.d.ts +2 -0
  289. package/dist/lib/commands/watch.d.ts.map +1 -0
  290. package/dist/lib/commands/watch.js +72 -0
  291. package/dist/lib/commands/watch.js.map +1 -0
  292. package/dist/lib/complexity.d.ts +55 -0
  293. package/dist/lib/complexity.d.ts.map +1 -0
  294. package/dist/lib/complexity.js +80 -0
  295. package/dist/lib/complexity.js.map +1 -0
  296. package/dist/lib/context/agents.d.ts +2 -0
  297. package/dist/lib/context/agents.d.ts.map +1 -0
  298. package/dist/lib/context/agents.js +344 -0
  299. package/dist/lib/context/agents.js.map +1 -0
  300. package/dist/lib/context/base.d.ts +2 -0
  301. package/dist/lib/context/base.d.ts.map +1 -0
  302. package/dist/lib/context/base.js +81 -0
  303. package/dist/lib/context/base.js.map +1 -0
  304. package/dist/lib/context/execute.d.ts +2 -0
  305. package/dist/lib/context/execute.d.ts.map +1 -0
  306. package/dist/lib/context/execute.js +753 -0
  307. package/dist/lib/context/execute.js.map +1 -0
  308. package/dist/lib/context/index.d.ts +2 -0
  309. package/dist/lib/context/index.d.ts.map +1 -0
  310. package/dist/lib/context/index.js +88 -0
  311. package/dist/lib/context/index.js.map +1 -0
  312. package/dist/lib/context/progress.d.ts +2 -0
  313. package/dist/lib/context/progress.d.ts.map +1 -0
  314. package/dist/lib/context/progress.js +178 -0
  315. package/dist/lib/context/progress.js.map +1 -0
  316. package/dist/lib/context/project.d.ts +2 -0
  317. package/dist/lib/context/project.d.ts.map +1 -0
  318. package/dist/lib/context/project.js +413 -0
  319. package/dist/lib/context/project.js.map +1 -0
  320. package/dist/lib/context/research.d.ts +2 -0
  321. package/dist/lib/context/research.d.ts.map +1 -0
  322. package/dist/lib/context/research.js +466 -0
  323. package/dist/lib/context/research.js.map +1 -0
  324. package/dist/lib/dead-ends.d.ts +28 -0
  325. package/dist/lib/dead-ends.d.ts.map +1 -0
  326. package/dist/lib/dead-ends.js +451 -0
  327. package/dist/lib/dead-ends.js.map +1 -0
  328. package/dist/lib/deps.d.ts +2 -0
  329. package/dist/lib/deps.d.ts.map +1 -0
  330. package/dist/lib/deps.js +630 -0
  331. package/dist/lib/deps.js.map +1 -0
  332. package/dist/lib/discussion.d.ts +2 -0
  333. package/dist/lib/discussion.d.ts.map +1 -0
  334. package/dist/lib/discussion.js +1041 -0
  335. package/dist/lib/discussion.js.map +1 -0
  336. package/dist/lib/drift.d.ts +36 -0
  337. package/dist/lib/drift.d.ts.map +1 -0
  338. package/dist/lib/drift.js +481 -0
  339. package/dist/lib/drift.js.map +1 -0
  340. package/dist/lib/evolve/_dimensions-features.d.ts +2 -0
  341. package/dist/lib/evolve/_dimensions-features.d.ts.map +1 -0
  342. package/dist/lib/evolve/_dimensions-features.js +369 -0
  343. package/dist/lib/evolve/_dimensions-features.js.map +1 -0
  344. package/dist/lib/evolve/_dimensions.d.ts +2 -0
  345. package/dist/lib/evolve/_dimensions.d.ts.map +1 -0
  346. package/dist/lib/evolve/_dimensions.js +358 -0
  347. package/dist/lib/evolve/_dimensions.js.map +1 -0
  348. package/dist/lib/evolve/_product-ideation.d.ts +2 -0
  349. package/dist/lib/evolve/_product-ideation.d.ts.map +1 -0
  350. package/dist/lib/evolve/_product-ideation.js +281 -0
  351. package/dist/lib/evolve/_product-ideation.js.map +1 -0
  352. package/dist/lib/evolve/_prompts.d.ts +2 -0
  353. package/dist/lib/evolve/_prompts.d.ts.map +1 -0
  354. package/dist/lib/evolve/_prompts.js +153 -0
  355. package/dist/lib/evolve/_prompts.js.map +1 -0
  356. package/dist/lib/evolve/cli.d.ts +2 -0
  357. package/dist/lib/evolve/cli.d.ts.map +1 -0
  358. package/dist/lib/evolve/cli.js +224 -0
  359. package/dist/lib/evolve/cli.js.map +1 -0
  360. package/dist/lib/evolve/discovery.d.ts +2 -0
  361. package/dist/lib/evolve/discovery.d.ts.map +1 -0
  362. package/dist/lib/evolve/discovery.js +391 -0
  363. package/dist/lib/evolve/discovery.js.map +1 -0
  364. package/dist/lib/evolve/index.d.ts +2 -0
  365. package/dist/lib/evolve/index.d.ts.map +1 -0
  366. package/dist/lib/evolve/index.js +88 -0
  367. package/dist/lib/evolve/index.js.map +1 -0
  368. package/dist/lib/evolve/orchestrator.d.ts +2 -0
  369. package/dist/lib/evolve/orchestrator.d.ts.map +1 -0
  370. package/dist/lib/evolve/orchestrator.js +851 -0
  371. package/dist/lib/evolve/orchestrator.js.map +1 -0
  372. package/dist/lib/evolve/scoring.d.ts +2 -0
  373. package/dist/lib/evolve/scoring.d.ts.map +1 -0
  374. package/dist/lib/evolve/scoring.js +118 -0
  375. package/dist/lib/evolve/scoring.js.map +1 -0
  376. package/dist/lib/evolve/state.d.ts +2 -0
  377. package/dist/lib/evolve/state.d.ts.map +1 -0
  378. package/dist/lib/evolve/state.js +264 -0
  379. package/dist/lib/evolve/state.js.map +1 -0
  380. package/dist/lib/evolve/types.d.ts +249 -0
  381. package/dist/lib/evolve/types.d.ts.map +1 -0
  382. package/dist/lib/evolve/types.js +3 -0
  383. package/dist/lib/evolve/types.js.map +1 -0
  384. package/dist/lib/frontmatter.d.ts +2 -0
  385. package/dist/lib/frontmatter.d.ts.map +1 -0
  386. package/dist/lib/frontmatter.js +513 -0
  387. package/dist/lib/frontmatter.js.map +1 -0
  388. package/dist/lib/gates.d.ts +2 -0
  389. package/dist/lib/gates.d.ts.map +1 -0
  390. package/dist/lib/gates.js +578 -0
  391. package/dist/lib/gates.js.map +1 -0
  392. package/dist/lib/genome.d.ts +10 -0
  393. package/dist/lib/genome.d.ts.map +1 -0
  394. package/dist/lib/genome.js +368 -0
  395. package/dist/lib/genome.js.map +1 -0
  396. package/dist/lib/got.d.ts +2 -0
  397. package/dist/lib/got.d.ts.map +1 -0
  398. package/dist/lib/got.js +280 -0
  399. package/dist/lib/got.js.map +1 -0
  400. package/dist/lib/invariants.d.ts +2 -0
  401. package/dist/lib/invariants.d.ts.map +1 -0
  402. package/dist/lib/invariants.js +298 -0
  403. package/dist/lib/invariants.js.map +1 -0
  404. package/dist/lib/knowledge.d.ts +2 -0
  405. package/dist/lib/knowledge.d.ts.map +1 -0
  406. package/dist/lib/knowledge.js +658 -0
  407. package/dist/lib/knowledge.js.map +1 -0
  408. package/dist/lib/long-term-roadmap.d.ts +2 -0
  409. package/dist/lib/long-term-roadmap.d.ts.map +1 -0
  410. package/dist/lib/long-term-roadmap.js +602 -0
  411. package/dist/lib/long-term-roadmap.js.map +1 -0
  412. package/dist/lib/markdown-split.d.ts +2 -0
  413. package/dist/lib/markdown-split.d.ts.map +1 -0
  414. package/dist/lib/markdown-split.js +199 -0
  415. package/dist/lib/markdown-split.js.map +1 -0
  416. package/dist/lib/mcp-server.d.ts +2 -0
  417. package/dist/lib/mcp-server.d.ts.map +1 -0
  418. package/dist/lib/mcp-server.js +2424 -0
  419. package/dist/lib/mcp-server.js.map +1 -0
  420. package/dist/lib/metrics.d.ts +16 -0
  421. package/dist/lib/metrics.d.ts.map +1 -0
  422. package/dist/lib/metrics.js +48 -0
  423. package/dist/lib/metrics.js.map +1 -0
  424. package/dist/lib/overstory.d.ts +2 -0
  425. package/dist/lib/overstory.d.ts.map +1 -0
  426. package/dist/lib/overstory.js +211 -0
  427. package/dist/lib/overstory.js.map +1 -0
  428. package/dist/lib/parallel.d.ts +2 -0
  429. package/dist/lib/parallel.d.ts.map +1 -0
  430. package/dist/lib/parallel.js +349 -0
  431. package/dist/lib/parallel.js.map +1 -0
  432. package/dist/lib/paths.d.ts +2 -0
  433. package/dist/lib/paths.d.ts.map +1 -0
  434. package/dist/lib/paths.js +254 -0
  435. package/dist/lib/paths.js.map +1 -0
  436. package/dist/lib/phase-complete-llm.d.ts +22 -0
  437. package/dist/lib/phase-complete-llm.d.ts.map +1 -0
  438. package/dist/lib/phase-complete-llm.js +331 -0
  439. package/dist/lib/phase-complete-llm.js.map +1 -0
  440. package/dist/lib/phase-complete.d.ts +46 -0
  441. package/dist/lib/phase-complete.d.ts.map +1 -0
  442. package/dist/lib/phase-complete.js +278 -0
  443. package/dist/lib/phase-complete.js.map +1 -0
  444. package/dist/lib/phase-io.d.ts +2 -0
  445. package/dist/lib/phase-io.d.ts.map +1 -0
  446. package/dist/lib/phase-io.js +126 -0
  447. package/dist/lib/phase-io.js.map +1 -0
  448. package/dist/lib/phase.d.ts +2 -0
  449. package/dist/lib/phase.d.ts.map +1 -0
  450. package/dist/lib/phase.js +1344 -0
  451. package/dist/lib/phase.js.map +1 -0
  452. package/dist/lib/plan-tournament.d.ts +63 -0
  453. package/dist/lib/plan-tournament.d.ts.map +1 -0
  454. package/dist/lib/plan-tournament.js +353 -0
  455. package/dist/lib/plan-tournament.js.map +1 -0
  456. package/dist/lib/refinement.d.ts +74 -0
  457. package/dist/lib/refinement.d.ts.map +1 -0
  458. package/dist/lib/refinement.js +283 -0
  459. package/dist/lib/refinement.js.map +1 -0
  460. package/dist/lib/requirements.d.ts +2 -0
  461. package/dist/lib/requirements.d.ts.map +1 -0
  462. package/dist/lib/requirements.js +355 -0
  463. package/dist/lib/requirements.js.map +1 -0
  464. package/dist/lib/research-bundle.d.ts +2 -0
  465. package/dist/lib/research-bundle.d.ts.map +1 -0
  466. package/dist/lib/research-bundle.js +246 -0
  467. package/dist/lib/research-bundle.js.map +1 -0
  468. package/dist/lib/roadmap.d.ts +2 -0
  469. package/dist/lib/roadmap.d.ts.map +1 -0
  470. package/dist/lib/roadmap.js +541 -0
  471. package/dist/lib/roadmap.js.map +1 -0
  472. package/dist/lib/sample.d.ts +16 -0
  473. package/dist/lib/sample.d.ts.map +1 -0
  474. package/dist/lib/sample.js +20 -0
  475. package/dist/lib/sample.js.map +1 -0
  476. package/dist/lib/scaffold.d.ts +2 -0
  477. package/dist/lib/scaffold.d.ts.map +1 -0
  478. package/dist/lib/scaffold.js +355 -0
  479. package/dist/lib/scaffold.js.map +1 -0
  480. package/dist/lib/scan/_utils.d.ts +11 -0
  481. package/dist/lib/scan/_utils.d.ts.map +1 -0
  482. package/dist/lib/scan/_utils.js +36 -0
  483. package/dist/lib/scan/_utils.js.map +1 -0
  484. package/dist/lib/scan/base64.d.ts +15 -0
  485. package/dist/lib/scan/base64.d.ts.map +1 -0
  486. package/dist/lib/scan/base64.js +66 -0
  487. package/dist/lib/scan/base64.js.map +1 -0
  488. package/dist/lib/scan/ignorefile.d.ts +30 -0
  489. package/dist/lib/scan/ignorefile.d.ts.map +1 -0
  490. package/dist/lib/scan/ignorefile.js +101 -0
  491. package/dist/lib/scan/ignorefile.js.map +1 -0
  492. package/dist/lib/scan/injection.d.ts +14 -0
  493. package/dist/lib/scan/injection.d.ts.map +1 -0
  494. package/dist/lib/scan/injection.js +39 -0
  495. package/dist/lib/scan/injection.js.map +1 -0
  496. package/dist/lib/scan/patterns.d.ts +17 -0
  497. package/dist/lib/scan/patterns.d.ts.map +1 -0
  498. package/dist/lib/scan/patterns.js +123 -0
  499. package/dist/lib/scan/patterns.js.map +1 -0
  500. package/dist/lib/scan/strip-markdown.d.ts +7 -0
  501. package/dist/lib/scan/strip-markdown.d.ts.map +1 -0
  502. package/dist/lib/scan/strip-markdown.js +38 -0
  503. package/dist/lib/scan/strip-markdown.js.map +1 -0
  504. package/dist/lib/scan/types.d.ts +23 -0
  505. package/dist/lib/scan/types.d.ts.map +1 -0
  506. package/dist/lib/scan/types.js +3 -0
  507. package/dist/lib/scan/types.js.map +1 -0
  508. package/dist/lib/scheduler-wait.d.ts +2 -0
  509. package/dist/lib/scheduler-wait.d.ts.map +1 -0
  510. package/dist/lib/scheduler-wait.js +59 -0
  511. package/dist/lib/scheduler-wait.js.map +1 -0
  512. package/dist/lib/scheduler.d.ts +254 -0
  513. package/dist/lib/scheduler.d.ts.map +1 -0
  514. package/dist/lib/scheduler.js +1147 -0
  515. package/dist/lib/scheduler.js.map +1 -0
  516. package/dist/lib/state.d.ts +2 -0
  517. package/dist/lib/state.d.ts.map +1 -0
  518. package/dist/lib/state.js +744 -0
  519. package/dist/lib/state.js.map +1 -0
  520. package/dist/lib/think.d.ts +18 -0
  521. package/dist/lib/think.d.ts.map +1 -0
  522. package/dist/lib/think.js +317 -0
  523. package/dist/lib/think.js.map +1 -0
  524. package/dist/lib/tracker.d.ts +2 -0
  525. package/dist/lib/tracker.d.ts.map +1 -0
  526. package/dist/lib/tracker.js +1121 -0
  527. package/dist/lib/tracker.js.map +1 -0
  528. package/dist/lib/types.d.ts +1514 -0
  529. package/dist/lib/types.d.ts.map +1 -0
  530. package/dist/lib/types.js +4 -0
  531. package/dist/lib/types.js.map +1 -0
  532. package/dist/lib/utils.d.ts +2 -0
  533. package/dist/lib/utils.d.ts.map +1 -0
  534. package/dist/lib/utils.js +1363 -0
  535. package/dist/lib/utils.js.map +1 -0
  536. package/dist/lib/verify.d.ts +2 -0
  537. package/dist/lib/verify.d.ts.map +1 -0
  538. package/dist/lib/verify.js +1153 -0
  539. package/dist/lib/verify.js.map +1 -0
  540. package/dist/lib/wireup/autofix.d.ts +2 -0
  541. package/dist/lib/wireup/autofix.d.ts.map +1 -0
  542. package/dist/lib/wireup/autofix.js +188 -0
  543. package/dist/lib/wireup/autofix.js.map +1 -0
  544. package/dist/lib/wireup/cli.d.ts +2 -0
  545. package/dist/lib/wireup/cli.d.ts.map +1 -0
  546. package/dist/lib/wireup/cli.js +194 -0
  547. package/dist/lib/wireup/cli.js.map +1 -0
  548. package/dist/lib/wireup/detection.d.ts +47 -0
  549. package/dist/lib/wireup/detection.d.ts.map +1 -0
  550. package/dist/lib/wireup/detection.js +410 -0
  551. package/dist/lib/wireup/detection.js.map +1 -0
  552. package/dist/lib/wireup/discovery.d.ts +2 -0
  553. package/dist/lib/wireup/discovery.d.ts.map +1 -0
  554. package/dist/lib/wireup/discovery.js +934 -0
  555. package/dist/lib/wireup/discovery.js.map +1 -0
  556. package/dist/lib/wireup/execution.d.ts +2 -0
  557. package/dist/lib/wireup/execution.d.ts.map +1 -0
  558. package/dist/lib/wireup/execution.js +573 -0
  559. package/dist/lib/wireup/execution.js.map +1 -0
  560. package/dist/lib/wireup/index.d.ts +2 -0
  561. package/dist/lib/wireup/index.d.ts.map +1 -0
  562. package/dist/lib/wireup/index.js +85 -0
  563. package/dist/lib/wireup/index.js.map +1 -0
  564. package/dist/lib/wireup/orchestrator.d.ts +2 -0
  565. package/dist/lib/wireup/orchestrator.d.ts.map +1 -0
  566. package/dist/lib/wireup/orchestrator.js +366 -0
  567. package/dist/lib/wireup/orchestrator.js.map +1 -0
  568. package/dist/lib/wireup/report.d.ts +47 -0
  569. package/dist/lib/wireup/report.d.ts.map +1 -0
  570. package/dist/lib/wireup/report.js +201 -0
  571. package/dist/lib/wireup/report.js.map +1 -0
  572. package/dist/lib/wireup/scenarios.d.ts +2 -0
  573. package/dist/lib/wireup/scenarios.d.ts.map +1 -0
  574. package/dist/lib/wireup/scenarios.js +516 -0
  575. package/dist/lib/wireup/scenarios.js.map +1 -0
  576. package/dist/lib/wireup/state.d.ts +2 -0
  577. package/dist/lib/wireup/state.d.ts.map +1 -0
  578. package/dist/lib/wireup/state.js +102 -0
  579. package/dist/lib/wireup/state.js.map +1 -0
  580. package/dist/lib/wireup/types.d.ts +376 -0
  581. package/dist/lib/wireup/types.d.ts.map +1 -0
  582. package/dist/lib/wireup/types.js +3 -0
  583. package/dist/lib/wireup/types.js.map +1 -0
  584. package/dist/lib/worktree.d.ts +2 -0
  585. package/dist/lib/worktree.d.ts.map +1 -0
  586. package/dist/lib/worktree.js +999 -0
  587. package/dist/lib/worktree.js.map +1 -0
  588. package/lib/autopilot-milestone.ts +136 -0
  589. package/lib/autopilot-pipeline.ts +1179 -0
  590. package/lib/autopilot-waves.ts +361 -0
  591. package/lib/autopilot.ts +1874 -0
  592. package/lib/autoplan.ts +280 -0
  593. package/lib/autoresearch.js +4 -0
  594. package/lib/autoresearch.ts +886 -0
  595. package/lib/backend.ts +1252 -0
  596. package/lib/benchmark.ts +341 -0
  597. package/lib/citations.ts +760 -0
  598. package/lib/cleanup.ts +1588 -0
  599. package/lib/cli/adapters.ts +41 -0
  600. package/lib/cli/agent.ts +83 -0
  601. package/lib/cli/index.ts +273 -0
  602. package/lib/cli/output.ts +33 -0
  603. package/lib/cli/scan-dispatch.ts +130 -0
  604. package/lib/cli/tools.ts +198 -0
  605. package/lib/commands/_dashboard-parsers.ts +275 -0
  606. package/lib/commands/analysis.ts +1851 -0
  607. package/lib/commands/assumptions.ts +232 -0
  608. package/lib/commands/blame.ts +174 -0
  609. package/lib/commands/budget.ts +148 -0
  610. package/lib/commands/check-plans.ts +233 -0
  611. package/lib/commands/config.ts +287 -0
  612. package/lib/commands/dashboard.ts +680 -0
  613. package/lib/commands/estimate.ts +204 -0
  614. package/lib/commands/eval-diff.ts +252 -0
  615. package/lib/commands/freshness.ts +213 -0
  616. package/lib/commands/health.ts +607 -0
  617. package/lib/commands/index.ts +266 -0
  618. package/lib/commands/install.ts +307 -0
  619. package/lib/commands/knowhow-aggregator.ts +345 -0
  620. package/lib/commands/knowledge-search.ts +153 -0
  621. package/lib/commands/long-term-roadmap.ts +390 -0
  622. package/lib/commands/patterns.ts +465 -0
  623. package/lib/commands/phase-info.ts +698 -0
  624. package/lib/commands/plan-lint.ts +546 -0
  625. package/lib/commands/plan-phase.ts +375 -0
  626. package/lib/commands/progress.ts +319 -0
  627. package/lib/commands/quality.ts +138 -0
  628. package/lib/commands/rollback.ts +195 -0
  629. package/lib/commands/scan.ts +72 -0
  630. package/lib/commands/search.ts +300 -0
  631. package/lib/commands/select-candidate.ts +687 -0
  632. package/lib/commands/singularity.ts +222 -0
  633. package/lib/commands/slug-timestamp.ts +74 -0
  634. package/lib/commands/tail.ts +129 -0
  635. package/lib/commands/todo.ts +273 -0
  636. package/lib/commands/watch.ts +80 -0
  637. package/lib/complexity.ts +117 -0
  638. package/lib/context/agents.ts +505 -0
  639. package/lib/context/base.ts +123 -0
  640. package/lib/context/execute.ts +977 -0
  641. package/lib/context/index.ts +110 -0
  642. package/lib/context/progress.ts +278 -0
  643. package/lib/context/project.ts +531 -0
  644. package/lib/context/research.ts +646 -0
  645. package/lib/dead-ends.ts +506 -0
  646. package/lib/deps.ts +773 -0
  647. package/lib/discussion.ts +1275 -0
  648. package/lib/drift.ts +519 -0
  649. package/lib/evolve/_dimensions-features.ts +525 -0
  650. package/lib/evolve/_dimensions.ts +511 -0
  651. package/lib/evolve/_product-ideation.ts +405 -0
  652. package/lib/evolve/_prompts.ts +178 -0
  653. package/lib/evolve/cli.ts +330 -0
  654. package/lib/evolve/discovery.ts +571 -0
  655. package/lib/evolve/index.ts +105 -0
  656. package/lib/evolve/orchestrator.ts +1139 -0
  657. package/lib/evolve/scoring.ts +167 -0
  658. package/lib/evolve/state.ts +330 -0
  659. package/lib/evolve/types.ts +290 -0
  660. package/lib/frontmatter.ts +615 -0
  661. package/lib/gates.ts +695 -0
  662. package/lib/genome.ts +402 -0
  663. package/lib/got.js +4 -0
  664. package/lib/got.ts +361 -0
  665. package/lib/invariants.ts +378 -0
  666. package/lib/knowledge.ts +768 -0
  667. package/lib/long-term-roadmap.ts +806 -0
  668. package/lib/markdown-split.ts +273 -0
  669. package/lib/mcp-server.ts +3292 -0
  670. package/lib/metrics.ts +49 -0
  671. package/lib/overstory.ts +270 -0
  672. package/lib/parallel.ts +570 -0
  673. package/lib/paths.ts +293 -0
  674. package/lib/phase-complete-llm.ts +376 -0
  675. package/lib/phase-complete.ts +366 -0
  676. package/lib/phase-io.ts +101 -0
  677. package/lib/phase.ts +1981 -0
  678. package/lib/plan-tournament.ts +426 -0
  679. package/lib/refinement.ts +349 -0
  680. package/lib/requirements.ts +469 -0
  681. package/lib/research-bundle.ts +300 -0
  682. package/lib/roadmap.ts +775 -0
  683. package/lib/scaffold.ts +480 -0
  684. package/lib/scan/_utils.ts +37 -0
  685. package/lib/scan/base64.ts +90 -0
  686. package/lib/scan/ignorefile.ts +109 -0
  687. package/lib/scan/injection.ts +67 -0
  688. package/lib/scan/patterns.ts +139 -0
  689. package/lib/scan/strip-markdown.ts +39 -0
  690. package/lib/scan/types.ts +28 -0
  691. package/lib/scheduler-wait.ts +58 -0
  692. package/lib/scheduler.ts +1370 -0
  693. package/lib/state.ts +1000 -0
  694. package/lib/think.ts +365 -0
  695. package/lib/tracker.ts +1591 -0
  696. package/lib/types.ts +1663 -0
  697. package/lib/utils.ts +1479 -0
  698. package/lib/verify.ts +1434 -0
  699. package/lib/wireup/autofix.ts +241 -0
  700. package/lib/wireup/cli.ts +278 -0
  701. package/lib/wireup/detection.ts +542 -0
  702. package/lib/wireup/discovery.ts +1063 -0
  703. package/lib/wireup/execution.ts +686 -0
  704. package/lib/wireup/index.ts +117 -0
  705. package/lib/wireup/orchestrator.ts +519 -0
  706. package/lib/wireup/report.ts +286 -0
  707. package/lib/wireup/scenarios.ts +616 -0
  708. package/lib/wireup/state.ts +139 -0
  709. package/lib/wireup/types.ts +436 -0
  710. package/lib/worktree.ts +1309 -0
  711. package/package.json +67 -0
@@ -0,0 +1,1344 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { normalizePhaseName, generateSlugInternal, stripShippedSections, execGit, loadConfig, getMilestoneInfo: getMilestoneInfoUtil, output, error, } = require('./utils');
6
+ const { extractFrontmatter, } = require('./frontmatter');
7
+ const { runPreflightGates, checkOrphanedPhases, } = require('./gates');
8
+ const { phasesDir: getPhasesDirPath, phaseDir: getPhaseDirPath, milestonesDir: getMilestonesDirPath, archivedPhasesDir: getArchivedPhasesDir, } = require('./paths');
9
+ const { _phaseCompleteCore } = require('./phase-complete');
10
+ const { readRoadmapFile, writeRoadmapFile, readStateFile, writeStateFile } = require('./phase-io');
11
+ // ─── Phases List ──────────────────────────────────────────────────────────────
12
+ /**
13
+ * CLI command: List phase directories with optional filtering by type or phase number.
14
+ * @param cwd - Project working directory
15
+ * @param options - List options
16
+ * @param raw - Output raw text (newline-separated) instead of JSON
17
+ * @returns void — writes JSON or raw text to stdout and exits on error
18
+ */
19
+ function cmdPhasesList(cwd, options, raw) {
20
+ const phasesDir = getPhasesDirPath(cwd);
21
+ const { type, phase } = options;
22
+ // If no phases directory, return empty
23
+ if (!fs.existsSync(phasesDir)) {
24
+ if (type) {
25
+ output({ files: [], count: 0 }, raw, '');
26
+ }
27
+ else {
28
+ output({ directories: [], count: 0 }, raw, '');
29
+ }
30
+ return;
31
+ }
32
+ try {
33
+ // Get all phase directories
34
+ const entries = fs.readdirSync(phasesDir, {
35
+ withFileTypes: true,
36
+ });
37
+ let dirs = entries
38
+ .filter((e) => e.isDirectory())
39
+ .map((e) => e.name);
40
+ // Sort numerically (handles decimals: 01, 02, 02.1, 02.2, 03)
41
+ dirs.sort((a, b) => {
42
+ const aNum = parseFloat(a.match(/^(\d+(?:\.\d+)?)/)?.[1] || '0');
43
+ const bNum = parseFloat(b.match(/^(\d+(?:\.\d+)?)/)?.[1] || '0');
44
+ return aNum - bNum;
45
+ });
46
+ // If filtering by phase number
47
+ if (phase) {
48
+ const normalized = normalizePhaseName(phase);
49
+ const match = dirs.find((d) => d.startsWith(normalized + '-') || d === normalized);
50
+ if (!match) {
51
+ output({ files: [], count: 0, phase_dir: null, error: 'Phase not found' }, raw, '');
52
+ return;
53
+ }
54
+ dirs = [match];
55
+ }
56
+ // If listing files of a specific type
57
+ if (type) {
58
+ const files = [];
59
+ for (const dir of dirs) {
60
+ const dirPath = path.join(phasesDir, dir);
61
+ const dirFiles = fs.readdirSync(dirPath);
62
+ let filtered;
63
+ if (type === 'plans') {
64
+ filtered = dirFiles.filter((f) => f.endsWith('-PLAN.md') || f === 'PLAN.md');
65
+ }
66
+ else if (type === 'summaries') {
67
+ filtered = dirFiles.filter((f) => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
68
+ }
69
+ else {
70
+ filtered = dirFiles;
71
+ }
72
+ files.push(...filtered.sort());
73
+ }
74
+ const result = {
75
+ files,
76
+ count: files.length,
77
+ phase_dir: phase ? dirs[0].replace(/^\d+(?:\.\d+)?-?/, '') : null,
78
+ };
79
+ output(result, raw, files.join('\n'));
80
+ return;
81
+ }
82
+ // Default: list directories
83
+ output({ directories: dirs, count: dirs.length }, raw, dirs.join('\n'));
84
+ }
85
+ catch (e) {
86
+ error('Failed to list phases: ' + e.message);
87
+ }
88
+ }
89
+ // ─── Phase Add ────────────────────────────────────────────────────────────────
90
+ /**
91
+ * CLI command: Add a new phase to the end of the roadmap and create its directory.
92
+ * @param cwd - Project working directory
93
+ * @param description - Human-readable phase description for the roadmap heading
94
+ * @param raw - Output raw padded number instead of JSON
95
+ * @param context - Optional context text for CONTEXT.md
96
+ * @returns void — writes JSON or raw text to stdout and exits on error
97
+ */
98
+ function cmdPhaseAdd(cwd, description, raw, context) {
99
+ if (!description) {
100
+ error('description required for phase add');
101
+ }
102
+ if (description.length > 60) {
103
+ error(`description too long (${description.length} chars): must not exceed 60 characters. Shorten your description to fewer than the maximum characters, e.g.: phase add 'Short name'`);
104
+ }
105
+ // Pre-flight gate checks
106
+ const gates = runPreflightGates(cwd, 'phase-add');
107
+ if (!gates.passed) {
108
+ output({
109
+ gate_failed: true,
110
+ gate_errors: gates.errors,
111
+ gate_warnings: gates.warnings,
112
+ }, raw);
113
+ return;
114
+ }
115
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
116
+ let content;
117
+ try {
118
+ content = readRoadmapFile(roadmapPath);
119
+ }
120
+ catch {
121
+ error('ROADMAP.md not found');
122
+ return;
123
+ }
124
+ const slug = generateSlugInternal(description);
125
+ // Find highest integer phase number across full content (including shipped sections)
126
+ const phasePattern = /#{2,3}\s*Phase\s+(\d+)(?:\.\d+)?:/gi;
127
+ let maxPhase = 0;
128
+ const existingPhaseNums = [];
129
+ let m;
130
+ while ((m = phasePattern.exec(content)) !== null) {
131
+ const num = parseInt(m[1], 10);
132
+ if (!existingPhaseNums.includes(num))
133
+ existingPhaseNums.push(num);
134
+ if (num > maxPhase)
135
+ maxPhase = num;
136
+ }
137
+ // Detect numbering gaps in existing phases
138
+ const addWarnings = [];
139
+ existingPhaseNums.sort((a, b) => a - b);
140
+ for (let i = 1; i < existingPhaseNums.length; i++) {
141
+ if (existingPhaseNums[i] !== existingPhaseNums[i - 1] + 1) {
142
+ addWarnings.push(`Gap in phase sequence: ${existingPhaseNums[i - 1]} to ${existingPhaseNums[i]} (missing ${existingPhaseNums[i - 1] + 1})`);
143
+ }
144
+ }
145
+ const newPhaseNum = maxPhase + 1;
146
+ const paddedNum = String(newPhaseNum).padStart(2, '0');
147
+ const dirName = `${paddedNum}-${slug}`;
148
+ const dirPath = getPhaseDirPath(cwd, null, dirName);
149
+ // Create directory
150
+ fs.mkdirSync(dirPath, { recursive: true });
151
+ // Write CONTEXT.md if context provided
152
+ if (context) {
153
+ const today = new Date().toISOString().slice(0, 10);
154
+ const contextContent = `---\nphase: "${paddedNum}"\nname: "${description}"\ncreated: ${today}\n---\n\n# Phase ${newPhaseNum}: ${description} -- Context\n\n${context}\n`;
155
+ fs.writeFileSync(path.join(dirPath, `${paddedNum}-CONTEXT.md`), contextContent, 'utf-8');
156
+ }
157
+ // Detect heading level used in existing ROADMAP (## or ###)
158
+ const headingLevel = /^## Phase \d+:/m.test(content) ? '##' : '###';
159
+ // Build phase entry (includes Duration for schedule computation)
160
+ const phaseEntry = `\n${headingLevel} Phase ${newPhaseNum}: ${description}\n\n**Goal:** ${description}\n**Depends on:** Phase ${maxPhase}\n**Duration:** 7d\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /grd:plan-phase ${newPhaseNum} to break down)\n`;
161
+ // Find insertion point: before last "---" or at end
162
+ let updatedContent;
163
+ const lastSeparator = content.lastIndexOf('\n---');
164
+ if (lastSeparator > 0) {
165
+ updatedContent = content.slice(0, lastSeparator) + phaseEntry + content.slice(lastSeparator);
166
+ }
167
+ else {
168
+ updatedContent = content + phaseEntry;
169
+ }
170
+ writeRoadmapFile(roadmapPath, updatedContent);
171
+ const result = {
172
+ phase_number: newPhaseNum,
173
+ padded: paddedNum,
174
+ name: description,
175
+ slug,
176
+ directory: path.relative(cwd, dirPath),
177
+ schedule_affected: true,
178
+ ...(addWarnings.length > 0 ? { warnings: addWarnings } : {}),
179
+ };
180
+ output(result, raw, paddedNum);
181
+ }
182
+ // ─── Phase Insert (Decimal) ──────────────────────────────────────────────────
183
+ /**
184
+ * CLI command: Insert a decimal phase after a specified phase in the roadmap.
185
+ * @param cwd - Project working directory
186
+ * @param afterPhase - Phase number to insert after (e.g., '06')
187
+ * @param description - Human-readable phase description
188
+ * @param raw - Output raw decimal phase number instead of JSON
189
+ * @returns void — writes JSON or raw text to stdout and exits on error
190
+ */
191
+ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
192
+ if (!afterPhase || !description) {
193
+ error('after-phase and description required for phase insert. Usage: phase insert <after-phase-number> <description>. Provide both arguments, e.g.: phase insert 2 "New phase description"');
194
+ }
195
+ // Pre-flight gate checks
196
+ const gates = runPreflightGates(cwd, 'phase-insert');
197
+ if (!gates.passed) {
198
+ output({
199
+ gate_failed: true,
200
+ gate_errors: gates.errors,
201
+ gate_warnings: gates.warnings,
202
+ }, raw);
203
+ return;
204
+ }
205
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
206
+ let content;
207
+ try {
208
+ content = readRoadmapFile(roadmapPath);
209
+ }
210
+ catch {
211
+ error('ROADMAP.md not found');
212
+ return;
213
+ }
214
+ const activeContent = stripShippedSections(content);
215
+ const slug = generateSlugInternal(description);
216
+ // Verify target phase exists (in active section only)
217
+ const afterPhaseEscaped = afterPhase.replace(/\./g, '\\.');
218
+ const targetPattern = new RegExp(`#{2,}\\s*Phase\\s+${afterPhaseEscaped}:`, 'i');
219
+ if (!targetPattern.test(activeContent)) {
220
+ error(`Phase ${afterPhase} not found in ROADMAP.md. Run "roadmap get-phase ${afterPhase}" to verify the phase exists, or check .planning/ROADMAP.md`);
221
+ }
222
+ // Calculate next decimal using existing logic
223
+ const phasesDir = getPhasesDirPath(cwd);
224
+ const normalizedBase = normalizePhaseName(afterPhase);
225
+ const existingDecimals = [];
226
+ try {
227
+ const entries = fs.readdirSync(phasesDir, {
228
+ withFileTypes: true,
229
+ });
230
+ const dirs = entries
231
+ .filter((e) => e.isDirectory())
232
+ .map((e) => e.name);
233
+ const decimalPattern = new RegExp(`^${normalizedBase}\\.(\\d+)`);
234
+ for (const dir of dirs) {
235
+ const dm = dir.match(decimalPattern);
236
+ if (dm)
237
+ existingDecimals.push(parseInt(dm[1], 10));
238
+ }
239
+ }
240
+ catch {
241
+ // Phases directory may not exist yet; start decimal numbering from 1
242
+ }
243
+ const nextDecimal = existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1;
244
+ const decimalPhase = `${normalizedBase}.${nextDecimal}`;
245
+ const dirName = `${decimalPhase}-${slug}`;
246
+ const dirPath = path.join(phasesDir, dirName);
247
+ // Create directory
248
+ fs.mkdirSync(dirPath, { recursive: true });
249
+ // Detect heading level used in existing ROADMAP (## or ###)
250
+ const headingLevel = /^## Phase \d+:/m.test(content) ? '##' : '###';
251
+ // Build phase entry (includes Duration for schedule computation)
252
+ const phaseEntry = `\n${headingLevel} Phase ${decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Depends on:** Phase ${afterPhase}\n**Duration:** 3d\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /grd:plan-phase ${decimalPhase} to break down)\n`;
253
+ // Insert after the target phase section
254
+ const headerPattern = new RegExp(`(#{2,}\\s*Phase\\s+${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
255
+ const headerMatch = content.match(headerPattern);
256
+ if (!headerMatch) {
257
+ error(`Could not find Phase ${afterPhase} header in ROADMAP.md. Ensure the phase heading matches the format "## Phase ${afterPhase}: <description>"`);
258
+ }
259
+ const headerIdx = content.indexOf(headerMatch[0]);
260
+ const afterHeader = content.slice(headerIdx + headerMatch[0].length);
261
+ const nextPhaseMatch = afterHeader.match(/\n#{2,}\s+Phase\s+\d/i);
262
+ let insertIdx;
263
+ if (nextPhaseMatch && nextPhaseMatch.index !== undefined) {
264
+ insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
265
+ }
266
+ else {
267
+ insertIdx = content.length;
268
+ }
269
+ const updatedContent = content.slice(0, insertIdx) + phaseEntry + content.slice(insertIdx);
270
+ writeRoadmapFile(roadmapPath, updatedContent);
271
+ const result = {
272
+ phase_number: decimalPhase,
273
+ after_phase: afterPhase,
274
+ name: description,
275
+ slug,
276
+ directory: path.relative(cwd, dirPath),
277
+ schedule_affected: true,
278
+ };
279
+ output(result, raw, decimalPhase);
280
+ }
281
+ // ─── Phase Remove ─────────────────────────────────────────────────────────────
282
+ /**
283
+ * Validate the targetPhase argument for phase remove.
284
+ * Calls error() and returns false if invalid; returns true if valid.
285
+ * @param targetPhase - Phase number to validate
286
+ */
287
+ function _validateRemoveArgs(targetPhase) {
288
+ if (!targetPhase) {
289
+ error("phase number required for phase remove. Usage: phase remove <N>. Provide the phase number to remove, e.g.: phase remove 3. Run 'phase list' to see available phases.");
290
+ return false;
291
+ }
292
+ return true;
293
+ }
294
+ /**
295
+ * Renumber integer phase directories after removing an integer phase.
296
+ * All directories with an integer part greater than removedInt are shifted down by 1.
297
+ * Mutates renamedDirs and renamedFiles arrays in place.
298
+ * @param phasesDir - Absolute path to the phases directory
299
+ * @param removedInt - The integer phase number that was removed
300
+ * @param renamedDirs - Accumulator for renamed directories
301
+ * @param renamedFiles - Accumulator for renamed files
302
+ */
303
+ function _renumberIntegerPhases(phasesDir, removedInt, renamedDirs, renamedFiles) {
304
+ try {
305
+ const entries = fs.readdirSync(phasesDir, {
306
+ withFileTypes: true,
307
+ });
308
+ const dirs = entries
309
+ .filter((e) => e.isDirectory())
310
+ .map((e) => e.name)
311
+ .sort();
312
+ // Collect directories that need renumbering (integer phases > removed, and their decimals)
313
+ const toRename = [];
314
+ for (const dir of dirs) {
315
+ const dm = dir.match(/^(\d+)(?:\.(\d+))?-(.+)$/);
316
+ if (!dm)
317
+ continue;
318
+ const dirInt = parseInt(dm[1], 10);
319
+ if (dirInt > removedInt) {
320
+ toRename.push({
321
+ dir,
322
+ oldInt: dirInt,
323
+ decimal: dm[2] ? parseInt(dm[2], 10) : null,
324
+ slug: dm[3],
325
+ });
326
+ }
327
+ }
328
+ // Sort descending to avoid conflicts
329
+ toRename.sort((a, b) => {
330
+ if (a.oldInt !== b.oldInt)
331
+ return b.oldInt - a.oldInt;
332
+ return (b.decimal || 0) - (a.decimal || 0);
333
+ });
334
+ for (const item of toRename) {
335
+ const newInt = item.oldInt - 1;
336
+ const newPadded = String(newInt).padStart(2, '0');
337
+ const oldPadded = String(item.oldInt).padStart(2, '0');
338
+ const decimalSuffix = item.decimal !== null ? `.${item.decimal}` : '';
339
+ const oldPrefix = `${oldPadded}${decimalSuffix}`;
340
+ const newPrefix = `${newPadded}${decimalSuffix}`;
341
+ const newDirName = `${newPrefix}-${item.slug}`;
342
+ // Rename directory
343
+ fs.renameSync(path.join(phasesDir, item.dir), path.join(phasesDir, newDirName));
344
+ renamedDirs.push({ from: item.dir, to: newDirName });
345
+ // Rename files inside
346
+ let dirFiles;
347
+ try {
348
+ dirFiles = fs.readdirSync(path.join(phasesDir, newDirName));
349
+ }
350
+ catch (readDirErr) {
351
+ const typedErr = readDirErr;
352
+ if (typedErr.code && typedErr.code !== 'ENOENT') {
353
+ process.stderr.write(`[phase] renumber read error (${typedErr.code}): ${typedErr.message}\n`);
354
+ }
355
+ continue;
356
+ }
357
+ for (const f of dirFiles) {
358
+ if (f.startsWith(oldPrefix)) {
359
+ const newFileName = newPrefix + f.slice(oldPrefix.length);
360
+ fs.renameSync(path.join(phasesDir, newDirName, f), path.join(phasesDir, newDirName, newFileName));
361
+ renamedFiles.push({ from: f, to: newFileName });
362
+ }
363
+ }
364
+ }
365
+ }
366
+ catch (renumberErr) {
367
+ const typedErr = renumberErr;
368
+ if (typedErr.code && typedErr.code !== 'ENOENT') {
369
+ process.stderr.write(`[phase] renumber error (${typedErr.code}): ${typedErr.message}\n`);
370
+ }
371
+ }
372
+ }
373
+ /**
374
+ * Renumber decimal phase directories after removing a decimal phase.
375
+ * Sibling decimals with a higher decimal part than removedDecimal are shifted down by 1.
376
+ * Mutates renamedDirs and renamedFiles arrays in place.
377
+ * @param phasesDir - Absolute path to the phases directory
378
+ * @param baseInt - The integer part of the removed decimal phase (e.g. "06")
379
+ * @param removedDecimal - The decimal part that was removed (e.g. 2 for "06.2")
380
+ * @param renamedDirs - Accumulator for renamed directories
381
+ * @param renamedFiles - Accumulator for renamed files
382
+ */
383
+ function _renumberDecimalPhases(phasesDir, baseInt, removedDecimal, renamedDirs, renamedFiles) {
384
+ try {
385
+ const entries = fs.readdirSync(phasesDir, {
386
+ withFileTypes: true,
387
+ });
388
+ const dirs = entries
389
+ .filter((e) => e.isDirectory())
390
+ .map((e) => e.name)
391
+ .sort();
392
+ // Find sibling decimals with higher numbers
393
+ const decPattern = new RegExp(`^${baseInt}\\.(\\d+)-(.+)$`);
394
+ const toRename = [];
395
+ for (const dir of dirs) {
396
+ const dm = dir.match(decPattern);
397
+ if (dm && parseInt(dm[1], 10) > removedDecimal) {
398
+ toRename.push({
399
+ dir,
400
+ oldDecimal: parseInt(dm[1], 10),
401
+ slug: dm[2],
402
+ });
403
+ }
404
+ }
405
+ // Sort descending to avoid conflicts
406
+ toRename.sort((a, b) => b.oldDecimal - a.oldDecimal);
407
+ for (const item of toRename) {
408
+ const newDecimal = item.oldDecimal - 1;
409
+ const oldPhaseId = `${baseInt}.${item.oldDecimal}`;
410
+ const newPhaseId = `${baseInt}.${newDecimal}`;
411
+ const newDirName = `${baseInt}.${newDecimal}-${item.slug}`;
412
+ // Rename directory
413
+ fs.renameSync(path.join(phasesDir, item.dir), path.join(phasesDir, newDirName));
414
+ renamedDirs.push({ from: item.dir, to: newDirName });
415
+ // Rename files inside
416
+ const dirFiles = fs.readdirSync(path.join(phasesDir, newDirName));
417
+ for (const f of dirFiles) {
418
+ // Files may have phase prefix like "06.2-01-PLAN.md"
419
+ if (f.includes(oldPhaseId)) {
420
+ const newFileName = f.replace(oldPhaseId, newPhaseId);
421
+ fs.renameSync(path.join(phasesDir, newDirName, f), path.join(phasesDir, newDirName, newFileName));
422
+ renamedFiles.push({ from: f, to: newFileName });
423
+ }
424
+ }
425
+ }
426
+ }
427
+ catch {
428
+ // Phases directory may not exist; no decimal phases to rename
429
+ }
430
+ }
431
+ /**
432
+ * Rename phase directories and files after a phase removal.
433
+ * Dispatches to _renumberIntegerPhases or _renumberDecimalPhases based on isDecimal.
434
+ * @param phasesDir - Absolute path to the phases directory
435
+ * @param normalized - Normalized phase number string (e.g. "06" or "06.2")
436
+ * @param isDecimal - Whether the removed phase was a decimal phase
437
+ */
438
+ function _reorderDirectories(phasesDir, normalized, isDecimal) {
439
+ const renamedDirs = [];
440
+ const renamedFiles = [];
441
+ if (isDecimal) {
442
+ const baseParts = normalized.split('.');
443
+ const baseInt = baseParts[0];
444
+ const removedDecimal = parseInt(baseParts[1], 10);
445
+ _renumberDecimalPhases(phasesDir, baseInt, removedDecimal, renamedDirs, renamedFiles);
446
+ }
447
+ else {
448
+ const removedInt = parseInt(normalized, 10);
449
+ _renumberIntegerPhases(phasesDir, removedInt, renamedDirs, renamedFiles);
450
+ }
451
+ return { renamedDirs, renamedFiles };
452
+ }
453
+ /**
454
+ * Update ROADMAP.md text after a phase removal: remove the target section and renumber
455
+ * all subsequent phase references (headings, checkboxes, plan refs, table rows, depends-on).
456
+ * @param roadmapContent - Current ROADMAP.md content
457
+ * @param targetPhase - The phase number that was removed (e.g. "06" or "06.2")
458
+ * @param normalized - Normalized phase number string
459
+ * @param isDecimal - Whether the removed phase was a decimal phase
460
+ */
461
+ function _reorderRoadmapEntries(roadmapContent, targetPhase, normalized, isDecimal) {
462
+ const targetEscaped = targetPhase.replace(/\./g, '\\.');
463
+ // Remove the target phase section
464
+ const sectionPattern = new RegExp(`\\n?#{2,}\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,}\\s+Phase\\s+\\d|$)`, 'i');
465
+ roadmapContent = roadmapContent.replace(sectionPattern, '');
466
+ // Remove from phase list (checkbox)
467
+ const checkboxPattern = new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${targetEscaped}[:\\s][^\\n]*`, 'gi');
468
+ roadmapContent = roadmapContent.replace(checkboxPattern, '');
469
+ // Remove from progress table
470
+ const tableRowPattern = new RegExp(`\\n?\\|\\s*${targetEscaped}\\.?\\s[^|]*\\|[^\\n]*`, 'gi');
471
+ roadmapContent = roadmapContent.replace(tableRowPattern, '');
472
+ // Renumber references in ROADMAP for subsequent integer phases
473
+ if (!isDecimal) {
474
+ const removedInt = parseInt(normalized, 10);
475
+ // Collect all integer phases > removedInt
476
+ const maxPhase = 99; // reasonable upper bound
477
+ for (let oldNum = maxPhase; oldNum > removedInt; oldNum--) {
478
+ const newNum = oldNum - 1;
479
+ const oldStr = String(oldNum);
480
+ const newStr = String(newNum);
481
+ const oldPad = oldStr.padStart(2, '0');
482
+ const newPad = newStr.padStart(2, '0');
483
+ // Phase headings: ### Phase 18: -> ### Phase 17: (or ## Phase)
484
+ roadmapContent = roadmapContent.replace(new RegExp(`(#{2,}\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'), `$1${newStr}$2`);
485
+ // Checkbox items: - [ ] **Phase 18:** -> - [ ] **Phase 17:**
486
+ roadmapContent = roadmapContent.replace(new RegExp(`(Phase\\s+)${oldStr}([:\\s])`, 'g'), `$1${newStr}$2`);
487
+ // Plan references: 18-01 -> 17-01
488
+ roadmapContent = roadmapContent.replace(new RegExp(`${oldPad}-(\\d{2})`, 'g'), `${newPad}-$1`);
489
+ // Table rows: | 18. -> | 17.
490
+ roadmapContent = roadmapContent.replace(new RegExp(`(\\|\\s*)${oldStr}\\.\\s`, 'g'), `$1${newStr}. `);
491
+ // Depends on references
492
+ roadmapContent = roadmapContent.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${oldStr}\\b`, 'gi'), `$1${newStr}`);
493
+ }
494
+ }
495
+ return roadmapContent;
496
+ }
497
+ /**
498
+ * Patch frontmatter phase references inside plan/summary files when phases are renumbered.
499
+ * Currently a no-op placeholder -- frontmatter phase refs are not yet tracked in this workflow.
500
+ * @param _phasesDir - Absolute path to the phases directory
501
+ * @param _oldNum - Old phase number string
502
+ * @param _newNum - New phase number string
503
+ */
504
+ function _patchFrontmatterRefs(_phasesDir, _oldNum, _newNum) {
505
+ // No frontmatter phase references are currently maintained in plan/summary files.
506
+ // This is a reserved hook for future frontmatter ref tracking.
507
+ }
508
+ /**
509
+ * CLI command: Remove a phase from the roadmap, delete its directory, and renumber subsequent phases.
510
+ * @param cwd - Project working directory
511
+ * @param targetPhase - Phase number to remove (integer or decimal)
512
+ * @param options - Remove options
513
+ * @param raw - Output raw text instead of JSON
514
+ * @returns void — writes JSON or raw text to stdout and exits on error
515
+ */
516
+ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
517
+ if (!_validateRemoveArgs(targetPhase))
518
+ return;
519
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
520
+ const phasesDir = getPhasesDirPath(cwd);
521
+ const force = options.force || false;
522
+ const dryRun = options.dryRun || false;
523
+ // Read ROADMAP.md FIRST (before any mutations) to detect unreadable state early
524
+ let roadmapContent;
525
+ try {
526
+ roadmapContent = readRoadmapFile(roadmapPath);
527
+ }
528
+ catch (readErr) {
529
+ error(`Cannot read ROADMAP.md: ${readErr.message}. Ensure .planning/ROADMAP.md exists and is readable, then retry.`);
530
+ return;
531
+ }
532
+ // Normalize the target
533
+ const normalized = normalizePhaseName(targetPhase);
534
+ const isDecimal = targetPhase.includes('.');
535
+ // Find and validate target directory
536
+ let targetDir = null;
537
+ try {
538
+ const entries = fs.readdirSync(phasesDir, {
539
+ withFileTypes: true,
540
+ });
541
+ const dirs = entries
542
+ .filter((e) => e.isDirectory())
543
+ .map((e) => e.name)
544
+ .sort();
545
+ targetDir =
546
+ dirs.find((d) => d.startsWith(normalized + '-') || d === normalized) || null;
547
+ }
548
+ catch {
549
+ // Phases directory may not exist; targetDir stays null
550
+ }
551
+ // Check for executed work (SUMMARY.md files) -- skip when dry-run
552
+ if (targetDir && !force && !dryRun) {
553
+ const targetPath = path.join(phasesDir, targetDir);
554
+ const files = fs.readdirSync(targetPath);
555
+ const summaries = files.filter((f) => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
556
+ if (summaries.length > 0) {
557
+ error(`Phase ${targetPhase} has ${summaries.length} executed plan(s). Use --force to remove anyway.`);
558
+ }
559
+ }
560
+ // Dry-run: collect what would happen and return early
561
+ if (dryRun) {
562
+ // Predict which phases would be renumbered
563
+ const wouldRenumber = [];
564
+ try {
565
+ const entries = fs.readdirSync(phasesDir, {
566
+ withFileTypes: true,
567
+ });
568
+ const dirs = entries
569
+ .filter((e) => e.isDirectory())
570
+ .map((e) => e.name)
571
+ .sort();
572
+ const removedInt = parseInt(normalized, 10);
573
+ for (const dir of dirs) {
574
+ const dm = dir.match(/^(\d+)(?:\.(\d+))?-(.+)$/);
575
+ if (!dm)
576
+ continue;
577
+ const dirInt = parseInt(dm[1], 10);
578
+ if (dirInt > removedInt) {
579
+ wouldRenumber.push(dir);
580
+ }
581
+ }
582
+ }
583
+ catch {
584
+ // Phases directory may not exist; dry-run preview will show empty renumber list
585
+ }
586
+ const result = {
587
+ dry_run: true,
588
+ would_remove: targetDir || normalized,
589
+ would_renumber: wouldRenumber,
590
+ removed: targetPhase,
591
+ directory_deleted: null,
592
+ renamed_directories: [],
593
+ renamed_files: [],
594
+ roadmap_updated: false,
595
+ state_updated: false,
596
+ };
597
+ output(result, raw, `dry-run: would remove phase ${targetPhase}`);
598
+ return;
599
+ }
600
+ // Delete target directory
601
+ if (targetDir) {
602
+ fs.rmSync(path.join(phasesDir, targetDir), {
603
+ recursive: true,
604
+ force: true,
605
+ });
606
+ }
607
+ // Clean up matching .worktrees/ directories
608
+ const cleanedWorktrees = [];
609
+ const worktreesDir = path.join(cwd, '.worktrees');
610
+ if (fs.existsSync(worktreesDir)) {
611
+ try {
612
+ const wtEntries = fs.readdirSync(worktreesDir, {
613
+ withFileTypes: true,
614
+ });
615
+ const wtPattern = new RegExp(`-${normalized}(?:-|$)`);
616
+ for (const entry of wtEntries) {
617
+ if (entry.isDirectory() && wtPattern.test(entry.name)) {
618
+ const wtPath = path.join(worktreesDir, entry.name);
619
+ fs.rmSync(wtPath, { recursive: true, force: true });
620
+ cleanedWorktrees.push(entry.name);
621
+ }
622
+ }
623
+ }
624
+ catch {
625
+ // Non-fatal
626
+ }
627
+ }
628
+ // Renumber subsequent phases (directories + files)
629
+ const { renamedDirs, renamedFiles } = _reorderDirectories(phasesDir, normalized, isDecimal);
630
+ // Patch frontmatter refs for all renumbered phases
631
+ _patchFrontmatterRefs(phasesDir, normalized, isDecimal ? normalized : normalized);
632
+ // Update ROADMAP.md (already read above)
633
+ roadmapContent = _reorderRoadmapEntries(roadmapContent, targetPhase, normalized, isDecimal);
634
+ writeRoadmapFile(roadmapPath, roadmapContent);
635
+ // Update STATE.md phase count
636
+ const statePath = path.join(cwd, '.planning', 'STATE.md');
637
+ if (fs.existsSync(statePath)) {
638
+ let stateContent = readStateFile(statePath);
639
+ // Update "Total Phases" field
640
+ const totalPattern = /(\*\*Total Phases:\*\*\s*)(\d+)/;
641
+ const totalMatch = stateContent.match(totalPattern);
642
+ if (totalMatch) {
643
+ const oldTotal = parseInt(totalMatch[2], 10);
644
+ stateContent = stateContent.replace(totalPattern, `$1${oldTotal - 1}`);
645
+ }
646
+ // Update "Phase: X of Y" pattern
647
+ const ofPattern = /(\bof\s+)(\d+)(\s*(?:\(|phases?))/i;
648
+ const ofMatch = stateContent.match(ofPattern);
649
+ if (ofMatch) {
650
+ const oldTotal = parseInt(ofMatch[2], 10);
651
+ stateContent = stateContent.replace(ofPattern, `$1${oldTotal - 1}$3`);
652
+ }
653
+ writeStateFile(statePath, stateContent);
654
+ }
655
+ const result = {
656
+ removed: targetPhase,
657
+ directory_deleted: targetDir || null,
658
+ renamed_directories: renamedDirs,
659
+ renamed_files: renamedFiles,
660
+ roadmap_updated: true,
661
+ state_updated: fs.existsSync(statePath),
662
+ ...(cleanedWorktrees.length > 0 ? { cleaned_worktrees: cleanedWorktrees } : {}),
663
+ };
664
+ output(result, raw, `Removed phase ${result.removed}`);
665
+ }
666
+ // ─── Phase Complete (Transition) ──────────────────────────────────────────────
667
+ // _phaseCompleteCore moved to lib/phase-complete.ts in Spec 3.
668
+ // cmdPhaseComplete and cmdPhaseBatchComplete below import it from there.
669
+ /**
670
+ * CLI command: Mark a phase as complete, update STATE.md, ROADMAP.md, and run quality analysis.
671
+ * @param cwd - Project working directory
672
+ * @param phaseNum - Phase number to complete (e.g., '02' or '2')
673
+ * @param raw - Output raw text instead of JSON
674
+ * @param options - Options (e.g., dryRun, force)
675
+ * @returns void — writes JSON or raw text to stdout and exits on error
676
+ */
677
+ async function cmdPhaseComplete(cwd, phaseNum, raw, options) {
678
+ if (!phaseNum) {
679
+ error("phase number required for phase complete. Usage: phase complete <N>. Provide the phase number to mark complete, e.g.: phase complete 3. Run 'phase list' to see available phases.");
680
+ }
681
+ let result;
682
+ try {
683
+ result = _phaseCompleteCore(cwd, phaseNum, options);
684
+ }
685
+ catch (e) {
686
+ const config = loadConfig(cwd);
687
+ if (config.phase_complete_llm_fallback === true) {
688
+ process.stderr.write(`[phase-complete-llm] mechanical path failed, attempting fallback\n`);
689
+ const { createScheduler } = require('./scheduler');
690
+ const { attemptLlmFallbackCompletion } = require('./phase-complete-llm');
691
+ const scheduler = createScheduler(config.scheduler, config.superpowers);
692
+ const fallbackResult = await attemptLlmFallbackCompletion(cwd, phaseNum, scheduler, e);
693
+ if (fallbackResult) {
694
+ result = fallbackResult;
695
+ }
696
+ else {
697
+ error(e.message);
698
+ return; // unreachable after error() but helps TS narrowing
699
+ }
700
+ }
701
+ else {
702
+ error(e.message);
703
+ return; // unreachable after error() but helps TS narrowing
704
+ }
705
+ }
706
+ // If the mechanical path returned gate_failed (not thrown), also try the LLM fallback.
707
+ if (result.gate_failed) {
708
+ const config = loadConfig(cwd);
709
+ if (config.phase_complete_llm_fallback === true) {
710
+ process.stderr.write(`[phase-complete-llm] gates failed, attempting fallback\n`);
711
+ const { createScheduler } = require('./scheduler');
712
+ const { attemptLlmFallbackCompletion } = require('./phase-complete-llm');
713
+ const scheduler = createScheduler(config.scheduler, config.superpowers);
714
+ const fallbackResult = await attemptLlmFallbackCompletion(cwd, phaseNum, scheduler, { gate_errors: result.gate_errors });
715
+ if (fallbackResult) {
716
+ result = fallbackResult;
717
+ }
718
+ // else: leave result as gate_failed — existing output logic renders it
719
+ }
720
+ }
721
+ let rawOutput = '';
722
+ if (raw) {
723
+ if (result.dry_run) {
724
+ rawOutput = `dry-run: would complete phase ${phaseNum}`;
725
+ }
726
+ else if (result.gate_failed) {
727
+ rawOutput = '';
728
+ }
729
+ else {
730
+ rawOutput = `Phase ${phaseNum} complete. ${result.plans_executed} plans executed.`;
731
+ if (result.next_phase) {
732
+ rawOutput += ` Next: Phase ${result.next_phase}`;
733
+ }
734
+ if (result.quality_report &&
735
+ result.quality_report.summary &&
736
+ result.quality_report.summary.total_issues > 0) {
737
+ rawOutput += ` | Quality: ${result.quality_report.summary.total_issues} issue(s) found`;
738
+ }
739
+ if (result.cleanup_plan_generated) {
740
+ rawOutput += ` | Cleanup plan generated: ${result.cleanup_plan_generated.path}`;
741
+ }
742
+ }
743
+ }
744
+ output(result, raw, rawOutput);
745
+ }
746
+ // ─── Milestone Complete ───────────────────────────────────────────────────────
747
+ /**
748
+ * Archive phase directories and supporting files (.planning/ROADMAP.md,
749
+ * REQUIREMENTS.md, audit) for a completed milestone.
750
+ * @param cwd - Project working directory
751
+ * @param _version - Milestone version string (unused but kept for API consistency)
752
+ * @param _sourceDir - Directory containing the phase subdirectories (unused but kept for API consistency)
753
+ * @param archiveDir - Destination directory for archived files
754
+ * @param ctx - Additional context for archival
755
+ */
756
+ function _archiveMilestone(cwd, version, _sourceDir, archiveDir, ctx) {
757
+ const { roadmapPath, reqPath, milestoneName, today, phasesDir, phaseCount, totalPlans, totalTasks, accomplishments, phasesAlreadyInPlace, } = ctx;
758
+ // Archive phase directories to .planning/milestones/{version}-phases/
759
+ const phasesArchiveDir = getArchivedPhasesDir(cwd, version);
760
+ let archivedPhaseCount = 0;
761
+ if (phasesAlreadyInPlace) {
762
+ // Phases already live under milestones/{version}/phases/ -- skip redundant copy
763
+ archivedPhaseCount = phaseCount;
764
+ }
765
+ else {
766
+ // Old-style layout -- copy phases to archive, then delete originals
767
+ try {
768
+ const phaseEntries = fs.readdirSync(phasesDir, {
769
+ withFileTypes: true,
770
+ });
771
+ const phaseDirs = phaseEntries
772
+ .filter((e) => e.isDirectory())
773
+ .map((e) => e.name);
774
+ if (phaseDirs.length > 0) {
775
+ fs.mkdirSync(phasesArchiveDir, { recursive: true });
776
+ for (let _pi = 0; _pi < phaseDirs.length; _pi++) {
777
+ const dir = phaseDirs[_pi];
778
+ process.stderr.write(` Archiving phase ${_pi + 1}/${phaseDirs.length}: ${dir}\n`);
779
+ fs.cpSync(path.join(phasesDir, dir), path.join(phasesArchiveDir, dir), {
780
+ recursive: true,
781
+ });
782
+ fs.rmSync(path.join(phasesDir, dir), {
783
+ recursive: true,
784
+ force: true,
785
+ });
786
+ archivedPhaseCount++;
787
+ }
788
+ }
789
+ }
790
+ catch {
791
+ // Phase archival is non-blocking
792
+ }
793
+ }
794
+ // Archive ROADMAP.md
795
+ if (fs.existsSync(roadmapPath)) {
796
+ const roadmapContent = readRoadmapFile(roadmapPath);
797
+ fs.writeFileSync(path.join(archiveDir, `${version}-ROADMAP.md`), roadmapContent, 'utf-8');
798
+ }
799
+ // Archive REQUIREMENTS.md
800
+ if (fs.existsSync(reqPath)) {
801
+ const reqContent = fs.readFileSync(reqPath, 'utf-8');
802
+ const archiveHeader = `# Requirements Archive: ${version} ${milestoneName}\n\n**Archived:** ${today}\n**Status:** SHIPPED\n\nFor current requirements, see \`.planning/REQUIREMENTS.md\`.\n\n---\n\n`;
803
+ fs.writeFileSync(path.join(archiveDir, `${version}-REQUIREMENTS.md`), archiveHeader + reqContent, 'utf-8');
804
+ }
805
+ // Archive audit file if exists
806
+ const auditFile = path.join(cwd, '.planning', `${version}-MILESTONE-AUDIT.md`);
807
+ if (fs.existsSync(auditFile)) {
808
+ fs.renameSync(auditFile, path.join(archiveDir, `${version}-MILESTONE-AUDIT.md`));
809
+ }
810
+ // Write archived.json metadata marker (REQ-60)
811
+ const milestoneVersionDir = path.join(cwd, '.planning', 'milestones', version);
812
+ fs.mkdirSync(milestoneVersionDir, { recursive: true });
813
+ const markerPath = path.join(milestoneVersionDir, 'archived.json');
814
+ const marker = {
815
+ version,
816
+ name: milestoneName,
817
+ archived_date: today,
818
+ phases: phaseCount,
819
+ plans: totalPlans,
820
+ tasks: totalTasks,
821
+ accomplishments,
822
+ };
823
+ fs.writeFileSync(markerPath, JSON.stringify(marker, null, 2) + '\n', 'utf-8');
824
+ return { archivedPhaseCount };
825
+ }
826
+ /**
827
+ * Patch STATE.md after a milestone is marked complete.
828
+ * Updates Status, Last Activity, and Last Activity Description fields.
829
+ * @param cwd - Project working directory
830
+ * @param version - Milestone version string (e.g. 'v1.0')
831
+ * @param today - ISO date string for today (YYYY-MM-DD)
832
+ */
833
+ function _updateStateAfterComplete(cwd, version, today) {
834
+ const statePath = path.join(cwd, '.planning', 'STATE.md');
835
+ if (!fs.existsSync(statePath))
836
+ return false;
837
+ let stateContent = readStateFile(statePath);
838
+ stateContent = stateContent.replace(/(\*\*Status:\*\*\s*).*/, `$1${version} milestone complete`);
839
+ stateContent = stateContent.replace(/(\*\*Last Activity:\*\*\s*).*/, `$1${today}`);
840
+ stateContent = stateContent.replace(/(\*\*Last Activity Description:\*\*\s*).*/, `$1${version} milestone completed and archived`);
841
+ writeStateFile(statePath, stateContent);
842
+ return true;
843
+ }
844
+ /**
845
+ * Rewrite MILESTONES.md after a milestone is complete, appending the new entry.
846
+ * @param milestonesPath - Absolute path to MILESTONES.md
847
+ * @param version - Milestone version string (e.g. 'v1.0')
848
+ * @param milestoneName - Display name for the milestone
849
+ * @param today - ISO date string for today (YYYY-MM-DD)
850
+ * @param phaseCount - Number of phases completed
851
+ * @param totalPlans - Total plans executed
852
+ * @param totalTasks - Total tasks executed
853
+ * @param accomplishments - List of one-liner accomplishment strings
854
+ */
855
+ function _rewriteRoadmapAfterComplete(milestonesPath, version, milestoneName, today, phaseCount, totalPlans, totalTasks, accomplishments) {
856
+ const accomplishmentsList = accomplishments.map((a) => `- ${a}`).join('\n');
857
+ const milestoneEntry = `## ${version} ${milestoneName} (Shipped: ${today})\n\n**Phases completed:** ${phaseCount} phases, ${totalPlans} plans, ${totalTasks} tasks\n\n**Key accomplishments:**\n${accomplishmentsList || '- (none recorded)'}\n\n---\n\n`;
858
+ if (fs.existsSync(milestonesPath)) {
859
+ const existing = fs.readFileSync(milestonesPath, 'utf-8');
860
+ fs.writeFileSync(milestonesPath, existing + '\n' + milestoneEntry, 'utf-8');
861
+ }
862
+ else {
863
+ fs.writeFileSync(milestonesPath, `# Milestones\n\n${milestoneEntry}`, 'utf-8');
864
+ }
865
+ }
866
+ /**
867
+ * CLI command: Archive a completed milestone, gather stats, and update MILESTONES.md and STATE.md.
868
+ * @param cwd - Project working directory
869
+ * @param version - Milestone version to complete (e.g., 'v1.0')
870
+ * @param options - Milestone options
871
+ * @param raw - Output raw text instead of JSON
872
+ * @returns void — writes JSON or raw text to stdout and exits on error
873
+ */
874
+ function cmdMilestoneComplete(cwd, version, options, raw) {
875
+ if (!version) {
876
+ error('version required for milestone complete (e.g., v1.0). Usage: milestone complete <version>. Provide the milestone version, e.g.: milestone complete v1.2.0. Check .planning/ROADMAP.md for the current milestone version.');
877
+ }
878
+ const dryRun = (options && options.dryRun) || false;
879
+ // Dry-run: return preview without modifying anything
880
+ if (dryRun) {
881
+ const result = {
882
+ dry_run: true,
883
+ would_archive_version: version,
884
+ };
885
+ output(result, raw, `dry-run: would archive milestone ${version}`);
886
+ return;
887
+ }
888
+ // Pre-flight gate checks
889
+ const gates = runPreflightGates(cwd, 'milestone-complete');
890
+ if (!gates.passed) {
891
+ output({
892
+ gate_failed: true,
893
+ gate_errors: gates.errors,
894
+ gate_warnings: gates.warnings,
895
+ }, raw);
896
+ return;
897
+ }
898
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
899
+ const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
900
+ const statePath = path.join(cwd, '.planning', 'STATE.md');
901
+ const milestonesPath = path.join(cwd, '.planning', 'MILESTONES.md');
902
+ const archiveDir = getMilestonesDirPath(cwd);
903
+ const phasesDir = getPhasesDirPath(cwd);
904
+ const today = new Date().toISOString().split('T')[0];
905
+ const milestoneName = options.name || version;
906
+ // Ensure archive directory exists
907
+ fs.mkdirSync(archiveDir, { recursive: true });
908
+ // Check if phases are already under the milestone directory (new-style layout)
909
+ const milestonePhaseDir = path.join(cwd, '.planning', 'milestones', version, 'phases');
910
+ let phasesAlreadyInPlace = false;
911
+ try {
912
+ phasesAlreadyInPlace =
913
+ fs.existsSync(milestonePhaseDir) &&
914
+ fs.readdirSync(milestonePhaseDir, {
915
+ withFileTypes: true,
916
+ }).some((e) => e.isDirectory());
917
+ }
918
+ catch {
919
+ // Milestone phases directory may not exist; assume old-style layout
920
+ }
921
+ // Determine the source directory for stat gathering
922
+ const statsSourceDir = phasesAlreadyInPlace ? milestonePhaseDir : phasesDir;
923
+ // Gather stats from phases
924
+ let phaseCount = 0;
925
+ let totalPlans = 0;
926
+ let totalTasks = 0;
927
+ const accomplishments = [];
928
+ try {
929
+ const entries = fs.readdirSync(statsSourceDir, {
930
+ withFileTypes: true,
931
+ });
932
+ const dirs = entries
933
+ .filter((e) => e.isDirectory())
934
+ .map((e) => e.name)
935
+ .sort();
936
+ for (const dir of dirs) {
937
+ phaseCount++;
938
+ const phaseFiles = fs.readdirSync(path.join(statsSourceDir, dir));
939
+ const plans = phaseFiles.filter((f) => f.endsWith('-PLAN.md') || f === 'PLAN.md');
940
+ const summaries = phaseFiles.filter((f) => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
941
+ totalPlans += plans.length;
942
+ // Extract one-liners from summaries
943
+ for (let _si = 0; _si < summaries.length; _si++) {
944
+ const s = summaries[_si];
945
+ process.stderr.write(` Reading summary ${_si + 1}/${summaries.length}: ${s}\n`);
946
+ try {
947
+ const content = fs.readFileSync(path.join(statsSourceDir, dir, s), 'utf-8');
948
+ const fm = extractFrontmatter(content);
949
+ if (fm['one-liner']) {
950
+ accomplishments.push(fm['one-liner']);
951
+ }
952
+ // Count tasks
953
+ const taskMatches = content.match(/##\s*Task\s*\d+/gi);
954
+ totalTasks += taskMatches ? taskMatches.length : 0;
955
+ }
956
+ catch {
957
+ // Summary file unreadable; skip one-liner and task count for this file
958
+ }
959
+ }
960
+ }
961
+ }
962
+ catch {
963
+ // Stats source directory may not exist; use zero stats
964
+ }
965
+ // Archive phases, documents, and write metadata marker
966
+ const archiveCtx = {
967
+ roadmapPath,
968
+ reqPath,
969
+ milestoneName,
970
+ today,
971
+ phasesDir,
972
+ phaseCount,
973
+ totalPlans,
974
+ totalTasks,
975
+ accomplishments,
976
+ phasesAlreadyInPlace,
977
+ };
978
+ const { archivedPhaseCount } = _archiveMilestone(cwd, version, statsSourceDir, archiveDir, archiveCtx);
979
+ // Append entry to MILESTONES.md
980
+ _rewriteRoadmapAfterComplete(milestonesPath, version, milestoneName, today, phaseCount, totalPlans, totalTasks, accomplishments);
981
+ // Update STATE.md
982
+ _updateStateAfterComplete(cwd, version, today);
983
+ // Merge milestone branch into base branch (if branching strategy is active)
984
+ let gitMerge = null;
985
+ try {
986
+ const config = loadConfig(cwd);
987
+ if (config.branching_strategy && config.branching_strategy !== 'none') {
988
+ const template = config.milestone_branch_template || 'grd/{milestone}-{slug}';
989
+ let msName = milestoneName;
990
+ try {
991
+ const msInfo = getMilestoneInfoUtil(cwd);
992
+ msName = msInfo.name || milestoneName;
993
+ }
994
+ catch {
995
+ // Use milestoneName from options
996
+ }
997
+ const msSlug = generateSlugInternal(msName) || 'milestone';
998
+ const msBranch = template.replace('{milestone}', version).replace('{slug}', msSlug);
999
+ const baseBranch = config.base_branch || 'main';
1000
+ // Check if milestone branch exists
1001
+ const msCheck = execGit(cwd, ['rev-parse', '--verify', msBranch]);
1002
+ if (msCheck.exitCode !== 0) {
1003
+ gitMerge = {
1004
+ skipped: true,
1005
+ reason: `Milestone branch '${msBranch}' not found`,
1006
+ };
1007
+ }
1008
+ else {
1009
+ // Record current branch
1010
+ const headResult = execGit(cwd, ['rev-parse', '--abbrev-ref', 'HEAD']);
1011
+ const originalBranch = headResult.exitCode === 0 ? headResult.stdout.trim() : baseBranch;
1012
+ // Checkout base branch
1013
+ const coResult = execGit(cwd, ['checkout', baseBranch]);
1014
+ if (coResult.exitCode !== 0) {
1015
+ gitMerge = {
1016
+ skipped: true,
1017
+ reason: `Failed to checkout '${baseBranch}'`,
1018
+ };
1019
+ }
1020
+ else {
1021
+ // Merge milestone branch
1022
+ const mergeResult = execGit(cwd, [
1023
+ 'merge',
1024
+ '--no-ff',
1025
+ msBranch,
1026
+ '-m',
1027
+ `Merge milestone ${version}: ${milestoneName}`,
1028
+ ]);
1029
+ if (mergeResult.exitCode !== 0) {
1030
+ // Conflict -- abort and restore
1031
+ execGit(cwd, ['merge', '--abort']);
1032
+ execGit(cwd, ['checkout', originalBranch]);
1033
+ gitMerge = {
1034
+ error: 'Merge conflict',
1035
+ milestone_branch: msBranch,
1036
+ base_branch: baseBranch,
1037
+ };
1038
+ }
1039
+ else {
1040
+ // Delete milestone branch after successful merge
1041
+ execGit(cwd, ['branch', '-d', msBranch]);
1042
+ // Restore original branch (stay on base after milestone merge)
1043
+ if (originalBranch !== baseBranch) {
1044
+ execGit(cwd, ['checkout', originalBranch]);
1045
+ }
1046
+ gitMerge = {
1047
+ merged: true,
1048
+ milestone_branch: msBranch,
1049
+ base_branch: baseBranch,
1050
+ branch_deleted: true,
1051
+ };
1052
+ }
1053
+ }
1054
+ }
1055
+ }
1056
+ }
1057
+ catch {
1058
+ // Git merge is non-blocking
1059
+ }
1060
+ const result = {
1061
+ version,
1062
+ name: milestoneName,
1063
+ date: today,
1064
+ phases: phaseCount,
1065
+ plans: totalPlans,
1066
+ tasks: totalTasks,
1067
+ accomplishments,
1068
+ phases_already_in_place: phasesAlreadyInPlace,
1069
+ archived: {
1070
+ roadmap: fs.existsSync(path.join(archiveDir, `${version}-ROADMAP.md`)),
1071
+ requirements: fs.existsSync(path.join(archiveDir, `${version}-REQUIREMENTS.md`)),
1072
+ audit: fs.existsSync(path.join(archiveDir, `${version}-MILESTONE-AUDIT.md`)),
1073
+ phases: archivedPhaseCount > 0,
1074
+ phase_count: archivedPhaseCount,
1075
+ marker: true,
1076
+ },
1077
+ milestones_updated: true,
1078
+ state_updated: fs.existsSync(statePath),
1079
+ ...(gitMerge ? { git_merge: gitMerge } : {}),
1080
+ };
1081
+ output(result, raw, `Milestone ${result.version} complete: ${result.phases} phases, ${result.plans} plans`);
1082
+ }
1083
+ // ─── Validate Consistency ─────────────────────────────────────────────────────
1084
+ /**
1085
+ * CLI command: Validate phase numbering consistency between ROADMAP.md and disk directories.
1086
+ * @param cwd - Project working directory
1087
+ * @param raw - Output raw 'passed'/'failed' instead of JSON
1088
+ * @param options - Validation options (e.g., fix)
1089
+ * @returns void — writes JSON or raw text to stdout and exits on error
1090
+ */
1091
+ function cmdValidateConsistency(cwd, raw, options) {
1092
+ const fix = (options && options.fix) || false;
1093
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
1094
+ const phasesDir = getPhasesDirPath(cwd);
1095
+ const errors_list = [];
1096
+ const warnings = [];
1097
+ const fixed = [];
1098
+ // Check for ROADMAP
1099
+ let roadmapContent;
1100
+ try {
1101
+ roadmapContent = readRoadmapFile(roadmapPath);
1102
+ }
1103
+ catch {
1104
+ errors_list.push('ROADMAP.md not found');
1105
+ output({ passed: false, errors: errors_list, warnings }, raw, 'failed');
1106
+ return;
1107
+ }
1108
+ const activeContent = stripShippedSections(roadmapContent);
1109
+ // Extract phases from ROADMAP (active section only)
1110
+ const roadmapPhases = new Set();
1111
+ const phasePattern = /#{2,3}\s*Phase\s+(\d+(?:\.\d+)?)\s*:/gi;
1112
+ let m;
1113
+ while ((m = phasePattern.exec(activeContent)) !== null) {
1114
+ roadmapPhases.add(m[1]);
1115
+ }
1116
+ // Get phases on disk
1117
+ const diskPhases = new Set();
1118
+ try {
1119
+ const entries = fs.readdirSync(phasesDir, {
1120
+ withFileTypes: true,
1121
+ });
1122
+ const dirs = entries
1123
+ .filter((e) => e.isDirectory())
1124
+ .map((e) => e.name);
1125
+ for (const dir of dirs) {
1126
+ const dm = dir.match(/^(\d+(?:\.\d+)?)/);
1127
+ if (dm)
1128
+ diskPhases.add(dm[1]);
1129
+ }
1130
+ }
1131
+ catch {
1132
+ // Phases directory may not exist; diskPhases stays empty
1133
+ }
1134
+ // Check: phases in ROADMAP but not on disk
1135
+ for (const p of roadmapPhases) {
1136
+ if (!diskPhases.has(p) && !diskPhases.has(normalizePhaseName(p))) {
1137
+ warnings.push(`Phase ${p} in ROADMAP.md but no directory on disk`);
1138
+ }
1139
+ }
1140
+ // Check: orphaned phases on disk but not in ROADMAP (errors, not warnings)
1141
+ const orphanViolations = checkOrphanedPhases(cwd);
1142
+ for (const v of orphanViolations) {
1143
+ errors_list.push(v.message);
1144
+ }
1145
+ // Check: sequential phase numbers (integers only)
1146
+ const integerPhases = [...diskPhases]
1147
+ .filter((p) => !p.includes('.'))
1148
+ .map((p) => parseInt(p, 10))
1149
+ .sort((a, b) => a - b);
1150
+ for (let i = 1; i < integerPhases.length; i++) {
1151
+ if (integerPhases[i] !== integerPhases[i - 1] + 1) {
1152
+ warnings.push(`Gap in phase numbering: ${integerPhases[i - 1]} \u2192 ${integerPhases[i]}`);
1153
+ }
1154
+ }
1155
+ // Check: plan numbering within phases
1156
+ try {
1157
+ const entries = fs.readdirSync(phasesDir, {
1158
+ withFileTypes: true,
1159
+ });
1160
+ const dirs = entries
1161
+ .filter((e) => e.isDirectory())
1162
+ .map((e) => e.name)
1163
+ .sort();
1164
+ for (const dir of dirs) {
1165
+ const phaseFiles = fs.readdirSync(path.join(phasesDir, dir));
1166
+ const plans = phaseFiles.filter((f) => f.endsWith('-PLAN.md')).sort();
1167
+ // Extract plan numbers
1168
+ const planNums = plans
1169
+ .map((p) => {
1170
+ const pm = p.match(/-(\d{2})-PLAN\.md$/);
1171
+ return pm ? parseInt(pm[1], 10) : null;
1172
+ })
1173
+ .filter((n) => n !== null);
1174
+ for (let i = 1; i < planNums.length; i++) {
1175
+ if (planNums[i] !== planNums[i - 1] + 1) {
1176
+ warnings.push(`Gap in plan numbering in ${dir}: plan ${planNums[i - 1]} \u2192 ${planNums[i]}`);
1177
+ }
1178
+ }
1179
+ // Check: plans without summaries (completed plans)
1180
+ const summaries = phaseFiles.filter((f) => f.endsWith('-SUMMARY.md'));
1181
+ const planIds = new Set(plans.map((p) => p.replace('-PLAN.md', '')));
1182
+ const summaryIds = new Set(summaries.map((s) => s.replace('-SUMMARY.md', '')));
1183
+ // Summary without matching plan is suspicious (orphaned)
1184
+ for (const sid of summaryIds) {
1185
+ if (!planIds.has(sid)) {
1186
+ const orphanFile = `${sid}-SUMMARY.md`;
1187
+ const orphanPath = path.join(phasesDir, dir, orphanFile);
1188
+ if (fix) {
1189
+ try {
1190
+ fs.unlinkSync(orphanPath);
1191
+ fixed.push(orphanPath);
1192
+ }
1193
+ catch {
1194
+ warnings.push(`Failed to remove orphaned summary: ${orphanFile} in ${dir}`);
1195
+ }
1196
+ }
1197
+ else {
1198
+ warnings.push(`Orphaned summary ${orphanFile} in ${dir} has no matching PLAN.md`);
1199
+ }
1200
+ }
1201
+ }
1202
+ }
1203
+ }
1204
+ catch {
1205
+ // Phases directory may not exist; skip plan-numbering and orphan checks
1206
+ }
1207
+ // Check: frontmatter in plans has required fields
1208
+ try {
1209
+ const entries = fs.readdirSync(phasesDir, {
1210
+ withFileTypes: true,
1211
+ });
1212
+ const dirs = entries
1213
+ .filter((e) => e.isDirectory())
1214
+ .map((e) => e.name);
1215
+ for (const dir of dirs) {
1216
+ const phaseFiles = fs.readdirSync(path.join(phasesDir, dir));
1217
+ const plans = phaseFiles.filter((f) => f.endsWith('-PLAN.md'));
1218
+ for (const plan of plans) {
1219
+ const content = fs.readFileSync(path.join(phasesDir, dir, plan), 'utf-8');
1220
+ const fm = extractFrontmatter(content);
1221
+ if (!fm.wave) {
1222
+ warnings.push(`${dir}/${plan}: missing 'wave' in frontmatter`);
1223
+ }
1224
+ }
1225
+ }
1226
+ }
1227
+ catch {
1228
+ // Phases directory may not exist; skip frontmatter validation
1229
+ }
1230
+ const passed = errors_list.length === 0;
1231
+ output({
1232
+ passed,
1233
+ errors: errors_list,
1234
+ warnings,
1235
+ warning_count: warnings.length,
1236
+ ...(fix ? { fixed } : {}),
1237
+ }, raw, passed ? 'passed' : 'failed');
1238
+ }
1239
+ // ─── Version Bump ─────────────────────────────────────────────────────────────
1240
+ /**
1241
+ * CLI command: Bump version in plugin.json, VERSION, and package.json.
1242
+ * @param cwd - Project working directory
1243
+ * @param version - Version string (with or without 'v' prefix)
1244
+ * @param raw - Output raw text instead of JSON
1245
+ * @returns void — writes JSON or raw text to stdout and exits on error
1246
+ */
1247
+ function cmdVersionBump(cwd, version, raw) {
1248
+ if (!version) {
1249
+ error('version required for version bump (e.g., v1.0.0). Usage: milestone version-bump <version>. Provide the new version, e.g.: milestone version-bump v1.3.0. Current version can be found in .planning/config.json. To check current version: cat .planning/config.json | grep version');
1250
+ }
1251
+ // Strip leading 'v' prefix
1252
+ const semver = version.replace(/^v/, '');
1253
+ const files = {
1254
+ VERSION: path.join(cwd, 'VERSION'),
1255
+ 'package.json': path.join(cwd, 'package.json'),
1256
+ '.claude-plugin/plugin.json': path.join(cwd, '.claude-plugin', 'plugin.json'),
1257
+ };
1258
+ const updated = [];
1259
+ // Update VERSION file
1260
+ if (fs.existsSync(files.VERSION)) {
1261
+ fs.writeFileSync(files.VERSION, semver + '\n', 'utf-8');
1262
+ updated.push('VERSION');
1263
+ }
1264
+ // Update package.json
1265
+ if (fs.existsSync(files['package.json'])) {
1266
+ const pkg = JSON.parse(fs.readFileSync(files['package.json'], 'utf-8'));
1267
+ pkg.version = semver;
1268
+ fs.writeFileSync(files['package.json'], JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
1269
+ updated.push('package.json');
1270
+ }
1271
+ // Update .claude-plugin/plugin.json
1272
+ if (fs.existsSync(files['.claude-plugin/plugin.json'])) {
1273
+ const plugin = JSON.parse(fs.readFileSync(files['.claude-plugin/plugin.json'], 'utf-8'));
1274
+ plugin.version = semver;
1275
+ fs.writeFileSync(files['.claude-plugin/plugin.json'], JSON.stringify(plugin, null, 2) + '\n', 'utf-8');
1276
+ updated.push('.claude-plugin/plugin.json');
1277
+ }
1278
+ const result = {
1279
+ version: semver,
1280
+ files_updated: updated,
1281
+ count: updated.length,
1282
+ };
1283
+ output(result, raw, `Bumped ${updated.length} files to ${semver}`);
1284
+ }
1285
+ // ─── Batch Phase Complete ─────────────────────────────────────────────────────
1286
+ /**
1287
+ * CLI command: Complete multiple phases in a single call.
1288
+ * @param cwd - Project working directory
1289
+ * @param phases - Array of phase numbers to complete
1290
+ * @param options - Options passed to each cmdPhaseComplete call
1291
+ * @param raw - Output raw text instead of JSON
1292
+ * @returns void — writes JSON or raw text to stdout and exits on error
1293
+ */
1294
+ function cmdPhaseBatchComplete(cwd, phases, options, raw) {
1295
+ if (!phases || phases.length === 0) {
1296
+ output({ error: 'phases list is required and must not be empty' }, raw, 'error');
1297
+ return;
1298
+ }
1299
+ const results = [];
1300
+ let completedCount = 0;
1301
+ for (const phase of phases) {
1302
+ try {
1303
+ const phaseResult = _phaseCompleteCore(cwd, phase, options);
1304
+ completedCount++;
1305
+ results.push({ phase, result: phaseResult });
1306
+ }
1307
+ catch (e) {
1308
+ results.push({ phase, error: e.message });
1309
+ }
1310
+ }
1311
+ output({
1312
+ results,
1313
+ total_phases: phases.length,
1314
+ completed_count: completedCount,
1315
+ }, raw, `Completed ${completedCount}/${phases.length} phases`);
1316
+ }
1317
+ // ─── Atomic Write ─────────────────────────────────────────────────────────────
1318
+ /**
1319
+ * Write content to a file atomically using a .tmp intermediate file.
1320
+ * On success, the .tmp file is renamed to the target path.
1321
+ * On failure, the original file is left untouched.
1322
+ * @param filePath - Absolute path to the target file
1323
+ * @param content - Content to write
1324
+ * @returns void — throws on write or rename failure
1325
+ */
1326
+ function atomicWriteFile(filePath, content) {
1327
+ const tmpPath = filePath + '.tmp';
1328
+ fs.writeFileSync(tmpPath, content, 'utf-8');
1329
+ fs.renameSync(tmpPath, filePath);
1330
+ }
1331
+ // ─── Exports ──────────────────────────────────────────────────────────────────
1332
+ module.exports = {
1333
+ cmdPhasesList,
1334
+ cmdPhaseAdd,
1335
+ cmdPhaseInsert,
1336
+ cmdPhaseRemove,
1337
+ cmdPhaseComplete,
1338
+ cmdMilestoneComplete,
1339
+ cmdValidateConsistency,
1340
+ cmdVersionBump,
1341
+ cmdPhaseBatchComplete,
1342
+ atomicWriteFile,
1343
+ };
1344
+ //# sourceMappingURL=phase.js.map