@pareto4/pp-core 1.5.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 (618) hide show
  1. package/.claude-plugin/plugin.json +23 -0
  2. package/GEMINI.md +53 -0
  3. package/LICENSE +22 -0
  4. package/README.md +100 -0
  5. package/agents/pp-advisor-researcher.md +108 -0
  6. package/agents/pp-ai-researcher.md +114 -0
  7. package/agents/pp-assumptions-analyzer.md +105 -0
  8. package/agents/pp-code-fixer.md +669 -0
  9. package/agents/pp-code-reviewer.md +387 -0
  10. package/agents/pp-codebase-mapper.md +853 -0
  11. package/agents/pp-debug-session-manager.md +315 -0
  12. package/agents/pp-debugger.md +1453 -0
  13. package/agents/pp-doc-classifier.md +168 -0
  14. package/agents/pp-doc-synthesizer.md +204 -0
  15. package/agents/pp-doc-verifier.md +217 -0
  16. package/agents/pp-doc-writer.md +616 -0
  17. package/agents/pp-domain-researcher.md +147 -0
  18. package/agents/pp-eval-auditor.md +191 -0
  19. package/agents/pp-eval-planner.md +154 -0
  20. package/agents/pp-executor.md +808 -0
  21. package/agents/pp-framework-selector.md +160 -0
  22. package/agents/pp-integration-checker.md +470 -0
  23. package/agents/pp-intel-updater.md +339 -0
  24. package/agents/pp-mempalace-curator.md +47 -0
  25. package/agents/pp-nyquist-auditor.md +204 -0
  26. package/agents/pp-pattern-mapper.md +335 -0
  27. package/agents/pp-phase-researcher.md +867 -0
  28. package/agents/pp-plan-checker.md +988 -0
  29. package/agents/pp-planner.md +1052 -0
  30. package/agents/pp-project-researcher.md +612 -0
  31. package/agents/pp-research-synthesizer.md +260 -0
  32. package/agents/pp-roadmapper.md +741 -0
  33. package/agents/pp-security-auditor.md +156 -0
  34. package/agents/pp-ui-auditor.md +495 -0
  35. package/agents/pp-ui-checker.md +309 -0
  36. package/agents/pp-ui-researcher.md +375 -0
  37. package/agents/pp-user-profiler.md +171 -0
  38. package/agents/pp-verifier.md +967 -0
  39. package/assets/gsd-logo-2000-transparent.png +0 -0
  40. package/assets/gsd-logo-2000-transparent.svg +17 -0
  41. package/assets/gsd-logo-2000.png +0 -0
  42. package/assets/gsd-logo-2000.svg +21 -0
  43. package/assets/terminal.svg +68 -0
  44. package/bin/install.js +12370 -0
  45. package/bin/lib/ui-safety-gate.cjs +107 -0
  46. package/commands/pp-add-tests.md +42 -0
  47. package/commands/pp-ai-integration-phase.md +37 -0
  48. package/commands/pp-audit-fix.md +34 -0
  49. package/commands/pp-audit-milestone.md +37 -0
  50. package/commands/pp-audit-uat.md +24 -0
  51. package/commands/pp-autonomous.md +51 -0
  52. package/commands/pp-capture.md +62 -0
  53. package/commands/pp-cleanup.md +24 -0
  54. package/commands/pp-code-review.md +59 -0
  55. package/commands/pp-complete-milestone.md +143 -0
  56. package/commands/pp-config.md +56 -0
  57. package/commands/pp-debug.md +52 -0
  58. package/commands/pp-discovery.md +34 -0
  59. package/commands/pp-discuss-phase.md +77 -0
  60. package/commands/pp-docs-update.md +49 -0
  61. package/commands/pp-eval-review.md +33 -0
  62. package/commands/pp-execute-phase.md +65 -0
  63. package/commands/pp-explore.md +27 -0
  64. package/commands/pp-extract-learnings.md +23 -0
  65. package/commands/pp-fast.md +31 -0
  66. package/commands/pp-forensics.md +57 -0
  67. package/commands/pp-graphify.md +204 -0
  68. package/commands/pp-health.md +31 -0
  69. package/commands/pp-help.md +28 -0
  70. package/commands/pp-import.md +45 -0
  71. package/commands/pp-inbox.md +39 -0
  72. package/commands/pp-ingest-docs.md +42 -0
  73. package/commands/pp-manager.md +45 -0
  74. package/commands/pp-map-codebase.md +83 -0
  75. package/commands/pp-mempalace-capture.md +71 -0
  76. package/commands/pp-mempalace-recall.md +102 -0
  77. package/commands/pp-milestone-summary.md +51 -0
  78. package/commands/pp-mvp-phase.md +45 -0
  79. package/commands/pp-new-milestone.md +45 -0
  80. package/commands/pp-new-project.md +47 -0
  81. package/commands/pp-ns-context.md +25 -0
  82. package/commands/pp-ns-ideate.md +24 -0
  83. package/commands/pp-ns-manage.md +36 -0
  84. package/commands/pp-ns-project.md +27 -0
  85. package/commands/pp-ns-review.md +29 -0
  86. package/commands/pp-ns-workflow.md +35 -0
  87. package/commands/pp-pause-work.md +43 -0
  88. package/commands/pp-phase.md +56 -0
  89. package/commands/pp-plan-phase.md +63 -0
  90. package/commands/pp-plan-review-convergence.md +60 -0
  91. package/commands/pp-pr-branch.md +26 -0
  92. package/commands/pp-profile-user.md +46 -0
  93. package/commands/pp-progress.md +49 -0
  94. package/commands/pp-quick.md +174 -0
  95. package/commands/pp-resume-work.md +30 -0
  96. package/commands/pp-review-backlog.md +63 -0
  97. package/commands/pp-review.md +42 -0
  98. package/commands/pp-secure-phase.md +36 -0
  99. package/commands/pp-settings.md +29 -0
  100. package/commands/pp-ship.md +24 -0
  101. package/commands/pp-sketch.md +60 -0
  102. package/commands/pp-spec-phase.md +63 -0
  103. package/commands/pp-spike.md +57 -0
  104. package/commands/pp-stats.md +20 -0
  105. package/commands/pp-surface.md +162 -0
  106. package/commands/pp-thread.md +24 -0
  107. package/commands/pp-ui-phase.md +35 -0
  108. package/commands/pp-ui-review.md +33 -0
  109. package/commands/pp-ultraplan-phase.md +34 -0
  110. package/commands/pp-undo.md +35 -0
  111. package/commands/pp-update.md +49 -0
  112. package/commands/pp-validate-phase.md +36 -0
  113. package/commands/pp-verify-work.md +39 -0
  114. package/commands/pp-workspace.md +52 -0
  115. package/commands/pp-workstreams.md +70 -0
  116. package/gemini-extension.json +6 -0
  117. package/hooks/dist/lib/git-cmd.js +150 -0
  118. package/hooks/dist/lib/pp-graphify-rebuild.sh +65 -0
  119. package/hooks/dist/managed-hooks-registry.cjs +39 -0
  120. package/hooks/dist/pp-check-update-worker.js +108 -0
  121. package/hooks/dist/pp-check-update.js +66 -0
  122. package/hooks/dist/pp-config-reload.js +133 -0
  123. package/hooks/dist/pp-context-monitor.js +196 -0
  124. package/hooks/dist/pp-cursor-post-tool.js +75 -0
  125. package/hooks/dist/pp-cursor-session-start.js +52 -0
  126. package/hooks/dist/pp-ensure-canonical-path.js +305 -0
  127. package/hooks/dist/pp-graphify-update.sh +158 -0
  128. package/hooks/dist/pp-phase-boundary.sh +47 -0
  129. package/hooks/dist/pp-prompt-guard.js +97 -0
  130. package/hooks/dist/pp-read-guard.js +101 -0
  131. package/hooks/dist/pp-read-injection-scanner.js +203 -0
  132. package/hooks/dist/pp-session-state.sh +59 -0
  133. package/hooks/dist/pp-statusline.js +566 -0
  134. package/hooks/dist/pp-update-banner.js +138 -0
  135. package/hooks/dist/pp-validate-commit.sh +57 -0
  136. package/hooks/dist/pp-workflow-guard.js +167 -0
  137. package/hooks/dist/pp-worktree-path-guard.js +185 -0
  138. package/hooks/hooks.json +70 -0
  139. package/hooks/lib/git-cmd.js +150 -0
  140. package/hooks/lib/pp-graphify-rebuild.sh +65 -0
  141. package/hooks/managed-hooks-registry.cjs +39 -0
  142. package/hooks/pp-check-update-worker.js +108 -0
  143. package/hooks/pp-check-update.js +66 -0
  144. package/hooks/pp-config-reload.js +133 -0
  145. package/hooks/pp-context-monitor.js +196 -0
  146. package/hooks/pp-cursor-post-tool.js +75 -0
  147. package/hooks/pp-cursor-session-start.js +52 -0
  148. package/hooks/pp-ensure-canonical-path.js +305 -0
  149. package/hooks/pp-graphify-update.sh +158 -0
  150. package/hooks/pp-phase-boundary.sh +47 -0
  151. package/hooks/pp-prompt-guard.js +97 -0
  152. package/hooks/pp-read-guard.js +101 -0
  153. package/hooks/pp-read-injection-scanner.js +203 -0
  154. package/hooks/pp-session-state.sh +59 -0
  155. package/hooks/pp-statusline.js +566 -0
  156. package/hooks/pp-update-banner.js +138 -0
  157. package/hooks/pp-validate-commit.sh +57 -0
  158. package/hooks/pp-workflow-guard.js +167 -0
  159. package/hooks/pp-worktree-path-guard.js +185 -0
  160. package/package.json +125 -0
  161. package/pp-core/bin/check-latest-version.cjs +161 -0
  162. package/pp-core/bin/lib/active-workstream-store.cjs +297 -0
  163. package/pp-core/bin/lib/adr-parser.cjs +399 -0
  164. package/pp-core/bin/lib/agent-command-router.cjs +68 -0
  165. package/pp-core/bin/lib/agent-install-check.cjs +143 -0
  166. package/pp-core/bin/lib/artifacts.cjs +51 -0
  167. package/pp-core/bin/lib/audit-command-router.cjs +61 -0
  168. package/pp-core/bin/lib/audit.cjs +743 -0
  169. package/pp-core/bin/lib/capability-activation.cjs +113 -0
  170. package/pp-core/bin/lib/capability-registry.cjs +3727 -0
  171. package/pp-core/bin/lib/capability-state.cjs +497 -0
  172. package/pp-core/bin/lib/capability-writer.cjs +355 -0
  173. package/pp-core/bin/lib/check-command-router.cjs +800 -0
  174. package/pp-core/bin/lib/cjs-command-router-adapter.cjs +81 -0
  175. package/pp-core/bin/lib/cli-exit.cjs +61 -0
  176. package/pp-core/bin/lib/clock.cjs +95 -0
  177. package/pp-core/bin/lib/clusters.cjs +131 -0
  178. package/pp-core/bin/lib/code-review-flags.cjs +59 -0
  179. package/pp-core/bin/lib/command-aliases.cjs +809 -0
  180. package/pp-core/bin/lib/command-arg-projection.cjs +55 -0
  181. package/pp-core/bin/lib/command-roster.cjs +19 -0
  182. package/pp-core/bin/lib/command-routing-hub.cjs +300 -0
  183. package/pp-core/bin/lib/commands.cjs +1244 -0
  184. package/pp-core/bin/lib/config-loader.cjs +714 -0
  185. package/pp-core/bin/lib/config-schema.cjs +58 -0
  186. package/pp-core/bin/lib/config-types.cjs +20 -0
  187. package/pp-core/bin/lib/config.cjs +796 -0
  188. package/pp-core/bin/lib/configuration.cjs +209 -0
  189. package/pp-core/bin/lib/context-utilization.cjs +48 -0
  190. package/pp-core/bin/lib/core-utils.cjs +198 -0
  191. package/pp-core/bin/lib/decisions.cjs +136 -0
  192. package/pp-core/bin/lib/docs.cjs +264 -0
  193. package/pp-core/bin/lib/drift.cjs +364 -0
  194. package/pp-core/bin/lib/edge-probe.cjs +196 -0
  195. package/pp-core/bin/lib/fallow-runner.cjs +153 -0
  196. package/pp-core/bin/lib/federated-config.cjs +182 -0
  197. package/pp-core/bin/lib/frontmatter.cjs +494 -0
  198. package/pp-core/bin/lib/gap-checker.cjs +260 -0
  199. package/pp-core/bin/lib/git-base-branch.cjs +220 -0
  200. package/pp-core/bin/lib/graphify-command-router.cjs +72 -0
  201. package/pp-core/bin/lib/graphify.cjs +472 -0
  202. package/pp-core/bin/lib/gsd2-import.cjs +456 -0
  203. package/pp-core/bin/lib/init-command-router.cjs +62 -0
  204. package/pp-core/bin/lib/init.cjs +1912 -0
  205. package/pp-core/bin/lib/install-profiles.cjs +814 -0
  206. package/pp-core/bin/lib/installer-migration-authoring.cjs +122 -0
  207. package/pp-core/bin/lib/installer-migration-report.cjs +351 -0
  208. package/pp-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +225 -0
  209. package/pp-core/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +48 -0
  210. package/pp-core/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +94 -0
  211. package/pp-core/bin/lib/installer-migrations/003-rename-get-shit-done-to-gsd-core.cjs +108 -0
  212. package/pp-core/bin/lib/installer-migrations/004-prune-stale-pristine-snapshots.cjs +123 -0
  213. package/pp-core/bin/lib/installer-migrations/005-rename-gsd-core-to-pp-core.cjs +109 -0
  214. package/pp-core/bin/lib/installer-migrations/006-rename-gsd-command-namespace.cjs +135 -0
  215. package/pp-core/bin/lib/installer-migrations.cjs +823 -0
  216. package/pp-core/bin/lib/intel-command-router.cjs +119 -0
  217. package/pp-core/bin/lib/intel.cjs +584 -0
  218. package/pp-core/bin/lib/io.cjs +220 -0
  219. package/pp-core/bin/lib/learnings.cjs +270 -0
  220. package/pp-core/bin/lib/legacy-cleanup.cjs +253 -0
  221. package/pp-core/bin/lib/loop-host-contract.cjs +105 -0
  222. package/pp-core/bin/lib/loop-resolver.cjs +479 -0
  223. package/pp-core/bin/lib/milestone.cjs +381 -0
  224. package/pp-core/bin/lib/model-catalog.cjs +154 -0
  225. package/pp-core/bin/lib/model-profiles.cjs +24 -0
  226. package/pp-core/bin/lib/model-resolver.cjs +467 -0
  227. package/pp-core/bin/lib/observability/event.cjs +51 -0
  228. package/pp-core/bin/lib/observability/logger.cjs +146 -0
  229. package/pp-core/bin/lib/observability/redaction.cjs +48 -0
  230. package/pp-core/bin/lib/package-identity.cjs +35 -0
  231. package/pp-core/bin/lib/package-legitimacy.cjs +368 -0
  232. package/pp-core/bin/lib/phase-command-router.cjs +209 -0
  233. package/pp-core/bin/lib/phase-id.cjs +213 -0
  234. package/pp-core/bin/lib/phase-lifecycle.cjs +74 -0
  235. package/pp-core/bin/lib/phase-locator.cjs +145 -0
  236. package/pp-core/bin/lib/phase.cjs +1446 -0
  237. package/pp-core/bin/lib/phases-command-router.cjs +43 -0
  238. package/pp-core/bin/lib/plan-drift-guard.cjs +117 -0
  239. package/pp-core/bin/lib/plan-scan.cjs +91 -0
  240. package/pp-core/bin/lib/planning-workspace.cjs +245 -0
  241. package/pp-core/bin/lib/probe-core.cjs +402 -0
  242. package/pp-core/bin/lib/profile-output.cjs +1164 -0
  243. package/pp-core/bin/lib/profile-pipeline-command-router.cjs +138 -0
  244. package/pp-core/bin/lib/profile-pipeline.cjs +519 -0
  245. package/pp-core/bin/lib/prohibition-enforcement.cjs +660 -0
  246. package/pp-core/bin/lib/project-root.cjs +119 -0
  247. package/pp-core/bin/lib/prompt-budget.cjs +305 -0
  248. package/pp-core/bin/lib/research-provider.cjs +137 -0
  249. package/pp-core/bin/lib/research-store.cjs +167 -0
  250. package/pp-core/bin/lib/review-reviewer-selection.cjs +121 -0
  251. package/pp-core/bin/lib/roadmap-command-router.cjs +166 -0
  252. package/pp-core/bin/lib/roadmap-parser.cjs +456 -0
  253. package/pp-core/bin/lib/roadmap-upgrade.cjs +476 -0
  254. package/pp-core/bin/lib/roadmap.cjs +738 -0
  255. package/pp-core/bin/lib/runtime-artifact-conversion.cjs +1966 -0
  256. package/pp-core/bin/lib/runtime-artifact-layout.cjs +342 -0
  257. package/pp-core/bin/lib/runtime-config-adapter-registry.cjs +103 -0
  258. package/pp-core/bin/lib/runtime-homes.cjs +272 -0
  259. package/pp-core/bin/lib/runtime-hooks-surface.cjs +1460 -0
  260. package/pp-core/bin/lib/runtime-name-policy.cjs +97 -0
  261. package/pp-core/bin/lib/runtime-slash.cjs +137 -0
  262. package/pp-core/bin/lib/schema-detect.cjs +159 -0
  263. package/pp-core/bin/lib/secrets.cjs +34 -0
  264. package/pp-core/bin/lib/security.cjs +480 -0
  265. package/pp-core/bin/lib/semver-compare.cjs +42 -0
  266. package/pp-core/bin/lib/shell-command-projection.cjs +550 -0
  267. package/pp-core/bin/lib/state-command-router.cjs +172 -0
  268. package/pp-core/bin/lib/state-document.cjs +312 -0
  269. package/pp-core/bin/lib/state.cjs +2587 -0
  270. package/pp-core/bin/lib/surface.cjs +505 -0
  271. package/pp-core/bin/lib/task-command-router.cjs +85 -0
  272. package/pp-core/bin/lib/teams-status.cjs +74 -0
  273. package/pp-core/bin/lib/template.cjs +246 -0
  274. package/pp-core/bin/lib/uat-predicate.cjs +329 -0
  275. package/pp-core/bin/lib/uat.cjs +359 -0
  276. package/pp-core/bin/lib/ui-safety-gate.cjs +98 -0
  277. package/pp-core/bin/lib/update-context.cjs +221 -0
  278. package/pp-core/bin/lib/validate-command-router.cjs +91 -0
  279. package/pp-core/bin/lib/validate.cjs +122 -0
  280. package/pp-core/bin/lib/verification-command-router.cjs +31 -0
  281. package/pp-core/bin/lib/verification.cjs +196 -0
  282. package/pp-core/bin/lib/verify-command-router.cjs +44 -0
  283. package/pp-core/bin/lib/verify.cjs +1932 -0
  284. package/pp-core/bin/lib/workstream-inventory-builder.cjs +81 -0
  285. package/pp-core/bin/lib/workstream-inventory.cjs +147 -0
  286. package/pp-core/bin/lib/workstream-name-policy.cjs +91 -0
  287. package/pp-core/bin/lib/workstream.cjs +386 -0
  288. package/pp-core/bin/lib/worktree-base-ref.cjs +350 -0
  289. package/pp-core/bin/lib/worktree-safety.cjs +985 -0
  290. package/pp-core/bin/pp-tools.cjs +2239 -0
  291. package/pp-core/bin/pp_run +20 -0
  292. package/pp-core/bin/shared/config-defaults.manifest.json +97 -0
  293. package/pp-core/bin/shared/config-schema.manifest.json +176 -0
  294. package/pp-core/bin/shared/model-catalog.json +160 -0
  295. package/pp-core/bin/shared/runtime-aliases.manifest.json +79 -0
  296. package/pp-core/bin/verify-reapply-patches.cjs +400 -0
  297. package/pp-core/contexts/dev.md +21 -0
  298. package/pp-core/contexts/research.md +22 -0
  299. package/pp-core/contexts/review.md +23 -0
  300. package/pp-core/references/agent-contracts.md +79 -0
  301. package/pp-core/references/ai-evals.md +156 -0
  302. package/pp-core/references/ai-frameworks.md +186 -0
  303. package/pp-core/references/artifact-types.md +131 -0
  304. package/pp-core/references/autonomous-smart-discuss.md +277 -0
  305. package/pp-core/references/checkpoints.md +814 -0
  306. package/pp-core/references/common-bug-patterns.md +114 -0
  307. package/pp-core/references/context-budget.md +85 -0
  308. package/pp-core/references/continuation-format.md +253 -0
  309. package/pp-core/references/debugger-philosophy.md +76 -0
  310. package/pp-core/references/decimal-phase-calculation.md +64 -0
  311. package/pp-core/references/doc-conflict-engine.md +91 -0
  312. package/pp-core/references/domain-probes.md +125 -0
  313. package/pp-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +7 -0
  314. package/pp-core/references/edge-probe-fixtures/01-round-half-even/requirements.json +1 -0
  315. package/pp-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +8 -0
  316. package/pp-core/references/edge-probe-fixtures/02-merge-intervals/requirements.json +1 -0
  317. package/pp-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +7 -0
  318. package/pp-core/references/edge-probe-fixtures/03-truncate-graphemes/requirements.json +1 -0
  319. package/pp-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +7 -0
  320. package/pp-core/references/edge-probe-fixtures/04-money-rounding/requirements.json +1 -0
  321. package/pp-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +8 -0
  322. package/pp-core/references/edge-probe-fixtures/05-list-dedupe/requirements.json +1 -0
  323. package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +8 -0
  324. package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/requirements.json +1 -0
  325. package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/resolutions.json +4 -0
  326. package/pp-core/references/edge-probe.md +272 -0
  327. package/pp-core/references/execute-mvp-tdd.md +81 -0
  328. package/pp-core/references/executor-examples.md +110 -0
  329. package/pp-core/references/few-shot-examples/plan-checker.md +73 -0
  330. package/pp-core/references/few-shot-examples/verifier.md +109 -0
  331. package/pp-core/references/gate-prompts.md +100 -0
  332. package/pp-core/references/gates.md +70 -0
  333. package/pp-core/references/git-integration.md +298 -0
  334. package/pp-core/references/git-planning-commit.md +40 -0
  335. package/pp-core/references/ios-scaffold.md +123 -0
  336. package/pp-core/references/loop-hook-dispatch.md +61 -0
  337. package/pp-core/references/mandatory-initial-read.md +2 -0
  338. package/pp-core/references/model-profile-resolution.md +38 -0
  339. package/pp-core/references/model-profiles.md +245 -0
  340. package/pp-core/references/mvp-concepts.md +49 -0
  341. package/pp-core/references/phase-argument-parsing.md +61 -0
  342. package/pp-core/references/planner-antipatterns.md +176 -0
  343. package/pp-core/references/planner-chunked.md +49 -0
  344. package/pp-core/references/planner-gap-closure.md +62 -0
  345. package/pp-core/references/planner-graphify-auto-update.md +67 -0
  346. package/pp-core/references/planner-guidance.md +186 -0
  347. package/pp-core/references/planner-human-verify-mode.md +57 -0
  348. package/pp-core/references/planner-interface-context.md +62 -0
  349. package/pp-core/references/planner-load-graph-context.md +36 -0
  350. package/pp-core/references/planner-mvp-mode.md +53 -0
  351. package/pp-core/references/planner-reviews.md +42 -0
  352. package/pp-core/references/planner-revision.md +87 -0
  353. package/pp-core/references/planner-source-audit.md +73 -0
  354. package/pp-core/references/planning-config.md +477 -0
  355. package/pp-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json +14 -0
  356. package/pp-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json +4 -0
  357. package/pp-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json +32 -0
  358. package/pp-core/references/prohibition-probe.md +326 -0
  359. package/pp-core/references/project-skills-discovery.md +19 -0
  360. package/pp-core/references/questioning.md +162 -0
  361. package/pp-core/references/research-documentation-lookup.md +29 -0
  362. package/pp-core/references/research-philosophy.md +29 -0
  363. package/pp-core/references/research-verification-protocol.md +27 -0
  364. package/pp-core/references/revision-loop.md +97 -0
  365. package/pp-core/references/scout-codebase.md +51 -0
  366. package/pp-core/references/skeleton-template.md +48 -0
  367. package/pp-core/references/sketch-interactivity.md +41 -0
  368. package/pp-core/references/sketch-theme-system.md +94 -0
  369. package/pp-core/references/sketch-tooling.md +45 -0
  370. package/pp-core/references/sketch-variant-patterns.md +81 -0
  371. package/pp-core/references/spidr-splitting.md +69 -0
  372. package/pp-core/references/tdd.md +330 -0
  373. package/pp-core/references/thinking-models-debug.md +44 -0
  374. package/pp-core/references/thinking-models-execution.md +50 -0
  375. package/pp-core/references/thinking-models-planning.md +62 -0
  376. package/pp-core/references/thinking-models-research.md +50 -0
  377. package/pp-core/references/thinking-models-verification.md +55 -0
  378. package/pp-core/references/thinking-partner.md +96 -0
  379. package/pp-core/references/ui-brand.md +162 -0
  380. package/pp-core/references/universal-anti-patterns.md +63 -0
  381. package/pp-core/references/user-profiling.md +681 -0
  382. package/pp-core/references/user-story-template.md +58 -0
  383. package/pp-core/references/verification-overrides.md +227 -0
  384. package/pp-core/references/verification-patterns.md +612 -0
  385. package/pp-core/references/verify-mvp-mode.md +85 -0
  386. package/pp-core/references/workstream-flag.md +111 -0
  387. package/pp-core/references/worktree-branch-check.md +44 -0
  388. package/pp-core/references/worktree-path-safety.md +67 -0
  389. package/pp-core/templates/AI-SPEC.md +246 -0
  390. package/pp-core/templates/DEBUG.md +169 -0
  391. package/pp-core/templates/README.md +77 -0
  392. package/pp-core/templates/SECURITY.md +61 -0
  393. package/pp-core/templates/UAT.md +265 -0
  394. package/pp-core/templates/UI-SPEC.md +100 -0
  395. package/pp-core/templates/VALIDATION.md +76 -0
  396. package/pp-core/templates/claude-md.md +145 -0
  397. package/pp-core/templates/codebase/architecture.md +255 -0
  398. package/pp-core/templates/codebase/concerns.md +310 -0
  399. package/pp-core/templates/codebase/conventions.md +307 -0
  400. package/pp-core/templates/codebase/integrations.md +280 -0
  401. package/pp-core/templates/codebase/stack.md +186 -0
  402. package/pp-core/templates/codebase/structure.md +285 -0
  403. package/pp-core/templates/codebase/testing.md +480 -0
  404. package/pp-core/templates/config.json +62 -0
  405. package/pp-core/templates/context.md +352 -0
  406. package/pp-core/templates/continue-here.md +78 -0
  407. package/pp-core/templates/copilot-instructions.md +7 -0
  408. package/pp-core/templates/debug-subagent-prompt.md +91 -0
  409. package/pp-core/templates/dev-preferences.md +21 -0
  410. package/pp-core/templates/discovery.md +146 -0
  411. package/pp-core/templates/discussion-log.md +63 -0
  412. package/pp-core/templates/milestone-archive.md +123 -0
  413. package/pp-core/templates/milestone.md +115 -0
  414. package/pp-core/templates/phase-prompt.md +610 -0
  415. package/pp-core/templates/planner-subagent-prompt.md +117 -0
  416. package/pp-core/templates/project.md +203 -0
  417. package/pp-core/templates/requirements.md +231 -0
  418. package/pp-core/templates/research-project/ARCHITECTURE.md +204 -0
  419. package/pp-core/templates/research-project/FEATURES.md +147 -0
  420. package/pp-core/templates/research-project/PITFALLS.md +200 -0
  421. package/pp-core/templates/research-project/STACK.md +120 -0
  422. package/pp-core/templates/research-project/SUMMARY.md +170 -0
  423. package/pp-core/templates/research.md +592 -0
  424. package/pp-core/templates/retrospective.md +54 -0
  425. package/pp-core/templates/roadmap.md +202 -0
  426. package/pp-core/templates/spec.md +333 -0
  427. package/pp-core/templates/state.md +195 -0
  428. package/pp-core/templates/summary-complex.md +60 -0
  429. package/pp-core/templates/summary-minimal.md +42 -0
  430. package/pp-core/templates/summary-standard.md +49 -0
  431. package/pp-core/templates/summary.md +249 -0
  432. package/pp-core/templates/user-profile.md +146 -0
  433. package/pp-core/templates/user-setup.md +311 -0
  434. package/pp-core/templates/verification-report.md +335 -0
  435. package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -0
  436. package/pp-core/workflows/add-backlog.md +91 -0
  437. package/pp-core/workflows/add-phase.md +113 -0
  438. package/pp-core/workflows/add-tests.md +355 -0
  439. package/pp-core/workflows/add-todo.md +161 -0
  440. package/pp-core/workflows/ai-integration-phase.md +295 -0
  441. package/pp-core/workflows/analyze-dependencies.md +96 -0
  442. package/pp-core/workflows/audit-fix.md +178 -0
  443. package/pp-core/workflows/audit-milestone.md +362 -0
  444. package/pp-core/workflows/audit-uat.md +110 -0
  445. package/pp-core/workflows/autonomous.md +886 -0
  446. package/pp-core/workflows/check-todos.md +180 -0
  447. package/pp-core/workflows/cleanup.md +195 -0
  448. package/pp-core/workflows/code-review-fix.md +506 -0
  449. package/pp-core/workflows/code-review.md +696 -0
  450. package/pp-core/workflows/complete-milestone.md +857 -0
  451. package/pp-core/workflows/debug.md +237 -0
  452. package/pp-core/workflows/diagnose-issues.md +245 -0
  453. package/pp-core/workflows/discovery-phase.md +291 -0
  454. package/pp-core/workflows/discovery-sprint.md +1546 -0
  455. package/pp-core/workflows/discuss-phase/modes/advisor.md +176 -0
  456. package/pp-core/workflows/discuss-phase/modes/all.md +28 -0
  457. package/pp-core/workflows/discuss-phase/modes/analyze.md +44 -0
  458. package/pp-core/workflows/discuss-phase/modes/auto.md +57 -0
  459. package/pp-core/workflows/discuss-phase/modes/batch.md +52 -0
  460. package/pp-core/workflows/discuss-phase/modes/chain.md +98 -0
  461. package/pp-core/workflows/discuss-phase/modes/default.md +141 -0
  462. package/pp-core/workflows/discuss-phase/modes/power.md +44 -0
  463. package/pp-core/workflows/discuss-phase/modes/text.md +55 -0
  464. package/pp-core/workflows/discuss-phase/templates/checkpoint.json +18 -0
  465. package/pp-core/workflows/discuss-phase/templates/context.md +136 -0
  466. package/pp-core/workflows/discuss-phase/templates/discussion-log.md +50 -0
  467. package/pp-core/workflows/discuss-phase-assumptions.md +675 -0
  468. package/pp-core/workflows/discuss-phase-power.md +291 -0
  469. package/pp-core/workflows/discuss-phase.md +520 -0
  470. package/pp-core/workflows/do.md +111 -0
  471. package/pp-core/workflows/docs-update.md +1168 -0
  472. package/pp-core/workflows/edit-phase.md +295 -0
  473. package/pp-core/workflows/eval-review.md +159 -0
  474. package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +95 -0
  475. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  476. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +117 -0
  477. package/pp-core/workflows/execute-phase/steps/worktree-recovery-policy.md +9 -0
  478. package/pp-core/workflows/execute-phase.md +1707 -0
  479. package/pp-core/workflows/execute-plan.md +541 -0
  480. package/pp-core/workflows/explore.md +146 -0
  481. package/pp-core/workflows/extract-learnings.md +243 -0
  482. package/pp-core/workflows/fast.md +124 -0
  483. package/pp-core/workflows/forensics.md +279 -0
  484. package/pp-core/workflows/graduation.md +196 -0
  485. package/pp-core/workflows/health.md +224 -0
  486. package/pp-core/workflows/help/modes/brief.md +22 -0
  487. package/pp-core/workflows/help/modes/default.md +50 -0
  488. package/pp-core/workflows/help/modes/full.md +791 -0
  489. package/pp-core/workflows/help/modes/topic.md +74 -0
  490. package/pp-core/workflows/help.md +24 -0
  491. package/pp-core/workflows/import.md +256 -0
  492. package/pp-core/workflows/inbox.md +387 -0
  493. package/pp-core/workflows/ingest-docs.md +340 -0
  494. package/pp-core/workflows/insert-phase.md +152 -0
  495. package/pp-core/workflows/list-phase-assumptions.md +178 -0
  496. package/pp-core/workflows/list-workspaces.md +57 -0
  497. package/pp-core/workflows/manager.md +435 -0
  498. package/pp-core/workflows/map-codebase.md +444 -0
  499. package/pp-core/workflows/milestone-summary.md +224 -0
  500. package/pp-core/workflows/mvp-phase.md +222 -0
  501. package/pp-core/workflows/new-milestone.md +643 -0
  502. package/pp-core/workflows/new-project.md +1563 -0
  503. package/pp-core/workflows/new-workspace.md +240 -0
  504. package/pp-core/workflows/next.md +347 -0
  505. package/pp-core/workflows/node-repair.md +92 -0
  506. package/pp-core/workflows/note.md +158 -0
  507. package/pp-core/workflows/pause-work.md +250 -0
  508. package/pp-core/workflows/plan-milestone-gaps.md +281 -0
  509. package/pp-core/workflows/plan-phase.md +1755 -0
  510. package/pp-core/workflows/plan-review-convergence.md +369 -0
  511. package/pp-core/workflows/plant-seed.md +230 -0
  512. package/pp-core/workflows/pr-branch.md +159 -0
  513. package/pp-core/workflows/profile-user.md +455 -0
  514. package/pp-core/workflows/progress.md +756 -0
  515. package/pp-core/workflows/quick.md +1045 -0
  516. package/pp-core/workflows/reapply-patches.md +443 -0
  517. package/pp-core/workflows/remove-phase.md +156 -0
  518. package/pp-core/workflows/remove-workspace.md +108 -0
  519. package/pp-core/workflows/resume-project.md +348 -0
  520. package/pp-core/workflows/review.md +765 -0
  521. package/pp-core/workflows/scan.md +107 -0
  522. package/pp-core/workflows/secure-phase.md +184 -0
  523. package/pp-core/workflows/session-report.md +146 -0
  524. package/pp-core/workflows/settings-advanced.md +816 -0
  525. package/pp-core/workflows/settings-integrations.md +312 -0
  526. package/pp-core/workflows/settings.md +592 -0
  527. package/pp-core/workflows/ship.md +465 -0
  528. package/pp-core/workflows/sketch-wrap-up.md +286 -0
  529. package/pp-core/workflows/sketch.md +361 -0
  530. package/pp-core/workflows/spec-phase.md +495 -0
  531. package/pp-core/workflows/spike-wrap-up.md +307 -0
  532. package/pp-core/workflows/spike.md +453 -0
  533. package/pp-core/workflows/stats.md +80 -0
  534. package/pp-core/workflows/sync-skills.md +182 -0
  535. package/pp-core/workflows/thread.md +222 -0
  536. package/pp-core/workflows/transition.md +694 -0
  537. package/pp-core/workflows/ui-phase.md +328 -0
  538. package/pp-core/workflows/ui-review.md +193 -0
  539. package/pp-core/workflows/ultraplan-phase.md +199 -0
  540. package/pp-core/workflows/undo.md +314 -0
  541. package/pp-core/workflows/update.md +501 -0
  542. package/pp-core/workflows/validate-phase.md +183 -0
  543. package/pp-core/workflows/verify-phase.md +569 -0
  544. package/pp-core/workflows/verify-work.md +804 -0
  545. package/scripts/affected-tests-lib.cjs +554 -0
  546. package/scripts/audit-workflow-script-paths.cjs +73 -0
  547. package/scripts/base64-scan.sh +351 -0
  548. package/scripts/build-hooks.js +254 -0
  549. package/scripts/changeset/README.md +129 -0
  550. package/scripts/changeset/cli.cjs +597 -0
  551. package/scripts/changeset/github-release-notes.cjs +199 -0
  552. package/scripts/changeset/lint.cjs +147 -0
  553. package/scripts/changeset/new.cjs +151 -0
  554. package/scripts/changeset/parse.cjs +114 -0
  555. package/scripts/changeset/render.cjs +34 -0
  556. package/scripts/changeset/serialize.cjs +130 -0
  557. package/scripts/check-alias-drift.cjs +114 -0
  558. package/scripts/check-env.cjs +312 -0
  559. package/scripts/check-npm-integrity.cjs +215 -0
  560. package/scripts/ci-guard-runner.cjs +22 -0
  561. package/scripts/ci-prepare-test-scope.cjs +93 -0
  562. package/scripts/ci-rebase-check.cjs +86 -0
  563. package/scripts/ci-test-scope.cjs +441 -0
  564. package/scripts/command-contract-helpers.cjs +64 -0
  565. package/scripts/diff-touches-shipped-paths.cjs +149 -0
  566. package/scripts/fix-slash-commands.cjs +161 -0
  567. package/scripts/gen-capability-registry.cjs +2646 -0
  568. package/scripts/gen-commands-reference.cjs +139 -0
  569. package/scripts/gen-inventory-manifest.cjs +114 -0
  570. package/scripts/gen-loop-host-contract.cjs +526 -0
  571. package/scripts/gen-research-agents.cjs +276 -0
  572. package/scripts/generate-package-identity.cjs +134 -0
  573. package/scripts/issue-dedupe.cjs +278 -0
  574. package/scripts/issue-version-gate.cjs +140 -0
  575. package/scripts/lib/allowlist-ratchet.cjs +236 -0
  576. package/scripts/lib/cli-exit.cjs +56 -0
  577. package/scripts/lint-allow-test-rule-refs.allowlist.json +328 -0
  578. package/scripts/lint-allow-test-rule-refs.cjs +162 -0
  579. package/scripts/lint-command-contract.cjs +118 -0
  580. package/scripts/lint-descriptions.cjs +87 -0
  581. package/scripts/lint-docs-required.cjs +222 -0
  582. package/scripts/lint-legacy-dir-name.cjs +166 -0
  583. package/scripts/lint-package-identity-drift.cjs +141 -0
  584. package/scripts/lint-pr-check-project-dir.cjs +99 -0
  585. package/scripts/lint-regression-test-names.allowlist.json +269 -0
  586. package/scripts/lint-regression-test-names.cjs +117 -0
  587. package/scripts/lint-shell-command-projection-drift.cjs +62 -0
  588. package/scripts/lint-skill-deps.cjs +185 -0
  589. package/scripts/lint-test-file-count.allowlist.json +181 -0
  590. package/scripts/lint-test-file-count.cjs +246 -0
  591. package/scripts/lint-windows-test-portability.cjs +178 -0
  592. package/scripts/mutation-matrix.cjs +323 -0
  593. package/scripts/pr-target-policy.cjs +63 -0
  594. package/scripts/pr-template-policy.cjs +268 -0
  595. package/scripts/prompt-injection-scan.sh +207 -0
  596. package/scripts/registry-smoke.cjs +337 -0
  597. package/scripts/release-notes/discord-release-summary.cjs +373 -0
  598. package/scripts/release-notes/format-github-release-notes.cjs +261 -0
  599. package/scripts/release-tarball-smoke.cjs +763 -0
  600. package/scripts/research-profiles.cjs +149 -0
  601. package/scripts/run-affected-tests.cjs +7 -0
  602. package/scripts/run-tests.cjs +589 -0
  603. package/scripts/secret-scan-lint.sh +231 -0
  604. package/scripts/secret-scan.sh +358 -0
  605. package/scripts/setup-branch-protection.sh +234 -0
  606. package/scripts/strip-prose-atrefs.cjs +106 -0
  607. package/scripts/sync-manifest-versions.cjs +119 -0
  608. package/scripts/sync-next-version.cjs +133 -0
  609. package/scripts/sync-rulesets.sh +34 -0
  610. package/scripts/sync-runtime-launcher.cjs +425 -0
  611. package/scripts/test-failure-reasons.cjs +34 -0
  612. package/scripts/update-size-baseline.cjs +68 -0
  613. package/scripts/verify-npm-publish.cjs +240 -0
  614. package/scripts/verify-opengsd-sweep.cjs +288 -0
  615. package/scripts/verify-phase-02-rename.sh +258 -0
  616. package/scripts/verify-phase-03-rename.sh +376 -0
  617. package/scripts/workflow-policy.cjs +483 -0
  618. package/scripts/workflow-size.cjs +90 -0
@@ -0,0 +1,1912 @@
1
+ "use strict";
2
+ /**
3
+ * Init — Compound init commands for workflow bootstrapping
4
+ *
5
+ * ADR-457 build-at-publish: the hand-written bin/lib/init.cjs collapsed to
6
+ * a TypeScript source of truth, compiled by tsc to a gitignored .cjs at the
7
+ * same require() path. Behaviour preserved byte-for-behaviour; only types are added.
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ const node_fs_1 = __importDefault(require("node:fs"));
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const node_os_1 = __importDefault(require("node:os"));
15
+ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
16
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- io.cjs is an export= CommonJS module
17
+ const io = require("./io.cjs");
18
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module
19
+ const configLoader = require("./config-loader.cjs");
20
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- model-resolver.cjs is an export= CommonJS module
21
+ const modelResolver = require("./model-resolver.cjs");
22
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
23
+ const phaseLocator = require("./phase-locator.cjs");
24
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- roadmap-parser.cjs is an export= CommonJS module
25
+ const roadmapParser = require("./roadmap-parser.cjs");
26
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- core-utils.cjs is an export= CommonJS module
27
+ const coreUtils = require("./core-utils.cjs");
28
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
29
+ const phaseId = require("./phase-id.cjs");
30
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- worktree-safety.cjs is an export= CommonJS module
31
+ const worktreeSafety = require("./worktree-safety.cjs");
32
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
33
+ const planningWorkspace = require("./planning-workspace.cjs");
34
+ const secrets_cjs_1 = require("./secrets.cjs");
35
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-scan.cjs is an export= CommonJS module
36
+ const scanPhasePlans = require("./plan-scan.cjs");
37
+ const state_document_cjs_1 = require("./state-document.cjs");
38
+ const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
39
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- commands.cjs is an export= CommonJS module
40
+ const commandsMod = require("./commands.cjs");
41
+ const security_cjs_1 = require("./security.cjs");
42
+ const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
43
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
44
+ const frontmatterMod = require("./frontmatter.cjs");
45
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- agent-install-check.cjs is an export= CommonJS module
46
+ const agentInstallCheck = require("./agent-install-check.cjs");
47
+ const { checkAgentsInstalled } = agentInstallCheck;
48
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- git-base-branch.cjs is an export= CommonJS module
49
+ const gitBaseBranch = require("./git-base-branch.cjs");
50
+ const { gitWorktreeInfoInternal } = gitBaseBranch;
51
+ const { output, error } = io;
52
+ const { loadConfig } = configLoader;
53
+ const { resolveModelInternal, resolveGranularityInternal, assertValidGranularityOverride } = modelResolver;
54
+ const { findPhaseInternal } = phaseLocator;
55
+ const { getRoadmapPhaseInternal, getMilestoneInfo, getMilestonePhaseFilter, stripShippedMilestones, extractCurrentMilestone, } = roadmapParser;
56
+ const { pathExistsInternal, generateSlugInternal, toPosixPath } = coreUtils;
57
+ const { normalizePhaseName, phaseTokenMatches } = phaseId;
58
+ const { pruneOrphanedWorktrees } = worktreeSafety;
59
+ const { planningPaths, planningDir, planningRoot, findContextMdIn, } = planningWorkspace;
60
+ const { determinePhaseStatus } = commandsMod;
61
+ const { extractFrontmatter } = frontmatterMod;
62
+ // Unused but imported for structural parity
63
+ void stripShippedMilestones;
64
+ // Accept all bold/colon variants of the Requirements header (#2769)
65
+ const REQUIREMENTS_HEADER_RE = /^\*\*Requirements:?\*\*[^\S\n]*:?[^\S\n]*([^\n]*)$/m;
66
+ function listPhaseSummaryFiles(phaseDir) {
67
+ return scanPhasePlans(phaseDir)['summaryFiles'];
68
+ }
69
+ function listPhasePlanFiles(phaseDir) {
70
+ return scanPhasePlans(phaseDir)['planFiles'];
71
+ }
72
+ function getLatestCompletedMilestone(cwd) {
73
+ const milestonesPath = node_path_1.default.join(planningRoot(cwd), 'MILESTONES.md');
74
+ const content = (0, shell_command_projection_cjs_1.platformReadSync)(milestonesPath);
75
+ if (content === null)
76
+ return null;
77
+ const match = content.match(/^##\s+(v[\d.]+)\s+(.+?)\s+\(Shipped:/m);
78
+ if (!match)
79
+ return null;
80
+ return {
81
+ version: match[1],
82
+ name: match[2].trim(),
83
+ };
84
+ }
85
+ function withProjectRoot(cwd, result) {
86
+ result['project_root'] = cwd;
87
+ const activeRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
88
+ const agentStatus = checkAgentsInstalled(activeRuntime);
89
+ result['agents_installed'] = agentStatus.agents_installed;
90
+ result['missing_agents'] = agentStatus.missing_agents;
91
+ result['agents_dir'] = agentStatus.agents_dir;
92
+ result['agent_runtime'] = agentStatus.agent_runtime;
93
+ const config = loadConfig(cwd);
94
+ if (config.response_language) {
95
+ result['response_language'] = config.response_language;
96
+ }
97
+ if (config.project_code) {
98
+ result['project_code'] = config.project_code;
99
+ }
100
+ const projectMdPath = node_path_1.default.join(planningDir(cwd), 'PROJECT.md');
101
+ const content = (0, shell_command_projection_cjs_1.platformReadSync)(projectMdPath);
102
+ if (content) {
103
+ const h1Match = content.match(/^#\s+(.+)$/m);
104
+ if (h1Match) {
105
+ result['project_title'] = h1Match[1].trim();
106
+ }
107
+ }
108
+ return result;
109
+ }
110
+ function getInitGitState(cwd) {
111
+ const info = gitWorktreeInfoInternal(cwd);
112
+ const worktreeRoot = info['worktreeRoot'];
113
+ const normalizeForCompare = (p) => {
114
+ if (typeof p !== 'string' || p.length === 0)
115
+ return null;
116
+ let resolved;
117
+ try {
118
+ resolved = node_fs_1.default.realpathSync.native(p);
119
+ }
120
+ catch {
121
+ resolved = node_path_1.default.resolve(p);
122
+ }
123
+ resolved = node_path_1.default.resolve(resolved);
124
+ if (process.platform === 'win32') {
125
+ return resolved.replace(/\//g, '\\').toLowerCase();
126
+ }
127
+ return resolved;
128
+ };
129
+ let inNestedSubdir = false;
130
+ if (info['inside']) {
131
+ let resolvedByGitPrefix = false;
132
+ try {
133
+ const prefixResult = (0, shell_command_projection_cjs_1.execGit)(['rev-parse', '--show-prefix'], { cwd, timeout: 5000 });
134
+ if (prefixResult['exitCode'] === 0) {
135
+ const prefix = (typeof prefixResult['stdout'] === 'string' ? prefixResult['stdout'] : '').trim().replace(/\\/g, '/');
136
+ inNestedSubdir = prefix.length > 0 && prefix !== '.' && prefix !== './';
137
+ resolvedByGitPrefix = true;
138
+ }
139
+ }
140
+ catch {
141
+ /* intentionally empty */
142
+ }
143
+ if (!resolvedByGitPrefix) {
144
+ const rootNorm = normalizeForCompare(worktreeRoot);
145
+ const cwdNorm = normalizeForCompare(cwd);
146
+ if (rootNorm && cwdNorm) {
147
+ if (rootNorm === cwdNorm) {
148
+ inNestedSubdir = false;
149
+ }
150
+ else {
151
+ const rel = node_path_1.default.relative(rootNorm, cwdNorm);
152
+ const relNorm = process.platform === 'win32' ? rel.replace(/\//g, '\\') : rel;
153
+ inNestedSubdir =
154
+ relNorm !== '' &&
155
+ relNorm !== '.' &&
156
+ !relNorm.startsWith('..') &&
157
+ !node_path_1.default.isAbsolute(relNorm);
158
+ }
159
+ }
160
+ else {
161
+ inNestedSubdir = worktreeRoot !== null;
162
+ }
163
+ }
164
+ }
165
+ if (inNestedSubdir && typeof worktreeRoot === 'string') {
166
+ const toComparableRaw = (p) => p.replace(/\\/g, '/').replace(/\/+$/g, '').toLowerCase();
167
+ if (toComparableRaw(worktreeRoot) === toComparableRaw(String(cwd))) {
168
+ inNestedSubdir = false;
169
+ }
170
+ }
171
+ return {
172
+ has_git: info['inside'],
173
+ git_worktree_root: worktreeRoot,
174
+ in_nested_subdir: inNestedSubdir,
175
+ };
176
+ }
177
+ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
178
+ if (!phase) {
179
+ error('phase required for init execute-phase');
180
+ }
181
+ const config = loadConfig(cwd);
182
+ let phaseInfo = findPhaseInternal(cwd, phase);
183
+ const milestone = getMilestoneInfo(cwd);
184
+ const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
185
+ if (phaseInfo?.['archived'] && roadmapPhase?.['found']) {
186
+ phaseInfo = null;
187
+ }
188
+ if (!phaseInfo && roadmapPhase?.['found']) {
189
+ const phaseName = roadmapPhase['phase_name'];
190
+ phaseInfo = {
191
+ found: true,
192
+ directory: null,
193
+ phase_number: roadmapPhase['phase_number'],
194
+ phase_name: phaseName,
195
+ phase_slug: phaseName
196
+ ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
197
+ : null,
198
+ plans: [],
199
+ summaries: [],
200
+ incomplete_plans: [],
201
+ has_research: false,
202
+ has_context: false,
203
+ has_verification: false,
204
+ has_reviews: false,
205
+ };
206
+ }
207
+ const reqMatch = roadmapPhase?.['section']?.match(REQUIREMENTS_HEADER_RE);
208
+ const reqExtracted = reqMatch
209
+ ? reqMatch[1].replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
210
+ : null;
211
+ const phase_req_ids = reqExtracted && reqExtracted !== 'TBD' ? reqExtracted : null;
212
+ const wf = (config.workflow ?? {});
213
+ const result = {
214
+ executor_model: resolveModelInternal(cwd, 'pp-executor'),
215
+ verifier_model: resolveModelInternal(cwd, 'pp-verifier'),
216
+ tdd_mode: options['tdd'] || Boolean(wf['tdd_mode']) || false,
217
+ commit_docs: config.commit_docs,
218
+ sub_repos: config.sub_repos,
219
+ parallelization: config.parallelization,
220
+ context_window: config.context_window,
221
+ branching_strategy: config.branching_strategy,
222
+ phase_branch_template: config.phase_branch_template,
223
+ milestone_branch_template: config.milestone_branch_template,
224
+ verifier_enabled: config.verifier,
225
+ phase_found: !!phaseInfo,
226
+ phase_dir: phaseInfo?.['directory'] || null,
227
+ phase_number: phaseInfo?.['phase_number'] || null,
228
+ phase_name: phaseInfo?.['phase_name'] || null,
229
+ phase_slug: phaseInfo?.['phase_slug'] || null,
230
+ phase_req_ids,
231
+ plans: phaseInfo?.['plans'] || [],
232
+ summaries: phaseInfo?.['summaries'] || [],
233
+ incomplete_plans: phaseInfo?.['incomplete_plans'] || [],
234
+ plan_count: phaseInfo?.['plans']?.length || 0,
235
+ incomplete_count: phaseInfo?.['incomplete_plans']?.length || 0,
236
+ branch_name: config.branching_strategy === 'phase' && phaseInfo
237
+ ? config.phase_branch_template
238
+ .replace('{project}', config.project_code || '')
239
+ .replace('{phase}', normalizePhaseName(phaseInfo['phase_number']))
240
+ .replace('{slug}', phaseInfo['phase_slug'] || 'phase')
241
+ : config.branching_strategy === 'milestone'
242
+ ? config.milestone_branch_template
243
+ .replace('{milestone}', milestone['version'])
244
+ .replace('{slug}', generateSlugInternal(milestone['name']) || 'milestone')
245
+ : null,
246
+ milestone_version: milestone['version'],
247
+ milestone_name: milestone['name'],
248
+ milestone_slug: generateSlugInternal(milestone['name']),
249
+ state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
250
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
251
+ config_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'config.json')),
252
+ state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
253
+ roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
254
+ config_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'config.json'))),
255
+ };
256
+ if (options['validate']) {
257
+ try {
258
+ const statePath = node_path_1.default.join(planningDir(cwd), 'STATE.md');
259
+ const stateContent = (0, shell_command_projection_cjs_1.platformReadSync)(statePath);
260
+ if (stateContent !== null) {
261
+ result['state_validation_ran'] = true;
262
+ const stateWarnings = [];
263
+ if (phaseInfo?.['directory'] && node_fs_1.default.existsSync(node_path_1.default.join(cwd, phaseInfo['directory']))) {
264
+ const diskPlans = listPhasePlanFiles(node_path_1.default.join(cwd, phaseInfo['directory'])).length;
265
+ const totalPlansRaw = (0, state_document_cjs_1.stateExtractField)(stateContent, 'Total Plans in Phase');
266
+ const totalPlansInPhase = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
267
+ if (totalPlansInPhase !== null && diskPlans !== totalPlansInPhase) {
268
+ stateWarnings.push(`Plan count mismatch: STATE.md says ${totalPlansInPhase}, disk has ${diskPlans}`);
269
+ }
270
+ }
271
+ result['state_warnings'] = stateWarnings;
272
+ }
273
+ }
274
+ catch {
275
+ /* intentionally empty */
276
+ }
277
+ }
278
+ output(withProjectRoot(cwd, result), raw);
279
+ }
280
+ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
281
+ if (!phase) {
282
+ error('phase required for init plan-phase');
283
+ }
284
+ const config = loadConfig(cwd);
285
+ let phaseInfo = findPhaseInternal(cwd, phase);
286
+ const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
287
+ if (phaseInfo?.['archived'] && roadmapPhase?.['found']) {
288
+ phaseInfo = null;
289
+ }
290
+ if (!phaseInfo && roadmapPhase?.['found']) {
291
+ const phaseName = roadmapPhase['phase_name'];
292
+ phaseInfo = {
293
+ found: true,
294
+ directory: null,
295
+ phase_number: roadmapPhase['phase_number'],
296
+ phase_name: phaseName,
297
+ phase_slug: phaseName
298
+ ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
299
+ : null,
300
+ plans: [],
301
+ summaries: [],
302
+ incomplete_plans: [],
303
+ has_research: false,
304
+ has_context: false,
305
+ has_verification: false,
306
+ has_reviews: false,
307
+ };
308
+ }
309
+ const reqMatch = roadmapPhase?.['section']?.match(REQUIREMENTS_HEADER_RE);
310
+ const reqExtracted = reqMatch
311
+ ? reqMatch[1].replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
312
+ : null;
313
+ const phase_req_ids = reqExtracted && reqExtracted !== 'TBD' ? reqExtracted : null;
314
+ const phaseDirPlan = phaseInfo?.['directory'] || null;
315
+ const phaseNumberPlan = phaseInfo?.['phase_number'] || null;
316
+ const phaseNamePlan = phaseInfo?.['phase_name'] || null;
317
+ const rawProjectCodePlan = config.project_code || '';
318
+ let expectedPhaseDirPlan = null;
319
+ if (!phaseDirPlan && phaseNumberPlan && phaseNamePlan) {
320
+ const paddedNum = normalizePhaseName(phaseNumberPlan);
321
+ const slug = (generateSlugInternal(phaseNamePlan) || '').substring(0, 60);
322
+ if (slug) {
323
+ const prefix = rawProjectCodePlan ? `${rawProjectCodePlan}-` : '';
324
+ const dirName = `${prefix}${paddedNum}-${slug}`;
325
+ expectedPhaseDirPlan = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningPaths(cwd).phases, dirName)));
326
+ }
327
+ }
328
+ const granularityOverride = options['granularity'];
329
+ assertValidGranularityOverride(granularityOverride, error);
330
+ const granularity = resolveGranularityInternal(cwd, 'planning', granularityOverride || undefined);
331
+ const wf = (config.workflow ?? {});
332
+ const result = {
333
+ researcher_model: resolveModelInternal(cwd, 'pp-phase-researcher'),
334
+ planner_model: resolveModelInternal(cwd, 'pp-planner'),
335
+ checker_model: resolveModelInternal(cwd, 'pp-plan-checker'),
336
+ tdd_mode: options['tdd'] || Boolean(wf['tdd_mode']) || false,
337
+ granularity,
338
+ research_enabled: wf['research'],
339
+ plan_checker_enabled: config.plan_checker,
340
+ nyquist_validation_enabled: wf['nyquist_validation'],
341
+ commit_docs: config.commit_docs,
342
+ text_mode: config.text_mode,
343
+ auto_advance: !!(config.auto_advance),
344
+ auto_chain_active: !!(config._auto_chain_active),
345
+ mode: config.mode || 'interactive',
346
+ phase_found: !!phaseInfo,
347
+ phase_dir: phaseDirPlan,
348
+ expected_phase_dir: expectedPhaseDirPlan,
349
+ phase_number: phaseNumberPlan,
350
+ phase_name: phaseNamePlan,
351
+ phase_slug: phaseInfo?.['phase_slug'] || null,
352
+ padded_phase: phaseNumberPlan ? normalizePhaseName(phaseNumberPlan) : null,
353
+ phase_req_ids,
354
+ phase_status: phaseDirPlan
355
+ ? determinePhaseStatus(phaseInfo?.['plans']?.length || 0, phaseInfo?.['summaries']?.length || 0, node_path_1.default.join(cwd, phaseDirPlan), 'Pending')
356
+ : 'Pending',
357
+ has_research: phaseInfo?.['has_research'] || false,
358
+ has_context: phaseInfo?.['has_context'] || false,
359
+ has_reviews: phaseInfo?.['has_reviews'] || false,
360
+ has_plans: (phaseInfo?.['plans']?.length || 0) > 0,
361
+ plan_count: phaseInfo?.['plans']?.length || 0,
362
+ planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
363
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
364
+ state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
365
+ roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
366
+ requirements_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md'))),
367
+ patterns_path: null,
368
+ };
369
+ if (phaseInfo?.['directory']) {
370
+ const phaseDirFull = node_path_1.default.join(cwd, phaseInfo['directory']);
371
+ try {
372
+ const files = node_fs_1.default.readdirSync(phaseDirFull);
373
+ const contextFile = findContextMdIn(phaseDirFull);
374
+ if (contextFile) {
375
+ result['context_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], contextFile));
376
+ }
377
+ const researchFile = files.find((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
378
+ if (researchFile) {
379
+ result['research_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], researchFile));
380
+ }
381
+ const verificationFile = files.find((f) => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
382
+ if (verificationFile) {
383
+ result['verification_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], verificationFile));
384
+ }
385
+ const uatFile = files.find((f) => f.endsWith('-UAT.md') || f === 'UAT.md');
386
+ if (uatFile) {
387
+ result['uat_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], uatFile));
388
+ }
389
+ const reviewsFile = files.find((f) => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md');
390
+ if (reviewsFile) {
391
+ result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], reviewsFile));
392
+ }
393
+ const patternsFile = files.find((f) => f.endsWith('-PATTERNS.md') || f === 'PATTERNS.md');
394
+ if (patternsFile) {
395
+ result['patterns_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], patternsFile));
396
+ }
397
+ }
398
+ catch {
399
+ /* intentionally empty */
400
+ }
401
+ }
402
+ if (options['validate']) {
403
+ try {
404
+ const statePath = node_path_1.default.join(planningDir(cwd), 'STATE.md');
405
+ const stateContent = (0, shell_command_projection_cjs_1.platformReadSync)(statePath);
406
+ if (stateContent !== null) {
407
+ const stateWarnings = [];
408
+ result['state_validation_ran'] = true;
409
+ const totalPlansRaw = (0, state_document_cjs_1.stateExtractField)(stateContent, 'Total Plans in Phase');
410
+ const totalPlansInPhase = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
411
+ if (totalPlansInPhase !== null &&
412
+ phaseInfo &&
413
+ totalPlansInPhase !==
414
+ (phaseInfo['plans']?.length || 0)) {
415
+ stateWarnings.push(`Plan count mismatch: STATE.md says ${totalPlansInPhase}, disk has ${phaseInfo['plans']?.length || 0}`);
416
+ }
417
+ result['state_warnings'] = stateWarnings;
418
+ }
419
+ }
420
+ catch {
421
+ /* intentionally empty */
422
+ }
423
+ }
424
+ output(withProjectRoot(cwd, result), raw);
425
+ }
426
+ function cmdInitNewProject(cwd, raw) {
427
+ const config = loadConfig(cwd);
428
+ const homedir = node_os_1.default.homedir();
429
+ const braveKeyFile = node_path_1.default.join(homedir, '.gsd', 'brave_api_key');
430
+ const hasBraveSearch = !!(process.env['BRAVE_API_KEY'] || node_fs_1.default.existsSync(braveKeyFile));
431
+ const firecrawlKeyFile = node_path_1.default.join(homedir, '.gsd', 'firecrawl_api_key');
432
+ const hasFirecrawl = !!(process.env['FIRECRAWL_API_KEY'] || node_fs_1.default.existsSync(firecrawlKeyFile));
433
+ const exaKeyFile = node_path_1.default.join(homedir, '.gsd', 'exa_api_key');
434
+ const hasExaSearch = !!(process.env['EXA_API_KEY'] || node_fs_1.default.existsSync(exaKeyFile));
435
+ let hasCode = false;
436
+ let hasPackageFile = false;
437
+ try {
438
+ const codeExtensions = new Set([
439
+ '.ts', '.js', '.py', '.go', '.rs', '.swift', '.java',
440
+ '.kt', '.kts',
441
+ '.c', '.cpp', '.h',
442
+ '.cs',
443
+ '.rb',
444
+ '.php',
445
+ '.dart',
446
+ '.m', '.mm',
447
+ '.scala',
448
+ '.groovy',
449
+ '.lua',
450
+ '.r', '.R',
451
+ '.zig',
452
+ '.ex', '.exs',
453
+ '.clj',
454
+ ]);
455
+ const skipDirs = new Set([
456
+ 'node_modules', '.git', '.planning', '.claude', '.codex',
457
+ '__pycache__', 'target', 'dist', 'build',
458
+ ]);
459
+ function findCodeFiles(dir, depth) {
460
+ if (depth > 3)
461
+ return false;
462
+ let entries;
463
+ try {
464
+ entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
465
+ }
466
+ catch {
467
+ return false;
468
+ }
469
+ for (const entry of entries) {
470
+ if (entry.isFile() && codeExtensions.has(node_path_1.default.extname(entry.name)))
471
+ return true;
472
+ if (entry.isDirectory() && !skipDirs.has(entry.name)) {
473
+ if (findCodeFiles(node_path_1.default.join(dir, entry.name), depth + 1))
474
+ return true;
475
+ }
476
+ }
477
+ return false;
478
+ }
479
+ hasCode = findCodeFiles(cwd, 0);
480
+ }
481
+ catch {
482
+ /* intentionally empty — best-effort detection */
483
+ }
484
+ hasPackageFile =
485
+ pathExistsInternal(cwd, 'package.json') ||
486
+ pathExistsInternal(cwd, 'requirements.txt') ||
487
+ pathExistsInternal(cwd, 'Cargo.toml') ||
488
+ pathExistsInternal(cwd, 'go.mod') ||
489
+ pathExistsInternal(cwd, 'Package.swift') ||
490
+ pathExistsInternal(cwd, 'build.gradle') ||
491
+ pathExistsInternal(cwd, 'build.gradle.kts') ||
492
+ pathExistsInternal(cwd, 'pom.xml') ||
493
+ pathExistsInternal(cwd, 'Gemfile') ||
494
+ pathExistsInternal(cwd, 'composer.json') ||
495
+ pathExistsInternal(cwd, 'pubspec.yaml') ||
496
+ pathExistsInternal(cwd, 'CMakeLists.txt') ||
497
+ pathExistsInternal(cwd, 'Makefile') ||
498
+ pathExistsInternal(cwd, 'build.zig') ||
499
+ pathExistsInternal(cwd, 'mix.exs') ||
500
+ pathExistsInternal(cwd, 'project.clj');
501
+ const result = {
502
+ researcher_model: resolveModelInternal(cwd, 'pp-project-researcher'),
503
+ synthesizer_model: resolveModelInternal(cwd, 'pp-research-synthesizer'),
504
+ roadmapper_model: resolveModelInternal(cwd, 'pp-roadmapper'),
505
+ commit_docs: config.commit_docs,
506
+ project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
507
+ has_codebase_map: pathExistsInternal(cwd, '.planning/codebase'),
508
+ planning_exists: pathExistsInternal(cwd, '.planning'),
509
+ has_existing_code: hasCode,
510
+ has_package_file: hasPackageFile,
511
+ is_brownfield: hasCode || hasPackageFile,
512
+ needs_codebase_map: (hasCode || hasPackageFile) && !pathExistsInternal(cwd, '.planning/codebase'),
513
+ ...getInitGitState(cwd),
514
+ brave_search_available: hasBraveSearch,
515
+ firecrawl_available: hasFirecrawl,
516
+ exa_search_available: hasExaSearch,
517
+ project_path: '.planning/PROJECT.md',
518
+ };
519
+ output(withProjectRoot(cwd, result), raw);
520
+ }
521
+ function cmdInitNewMilestone(cwd, raw) {
522
+ const config = loadConfig(cwd);
523
+ const milestone = getMilestoneInfo(cwd);
524
+ const latestCompleted = getLatestCompletedMilestone(cwd);
525
+ const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
526
+ let phaseDirCount = 0;
527
+ try {
528
+ if (node_fs_1.default.existsSync(phasesDir)) {
529
+ const isDirInMilestone = getMilestonePhaseFilter(cwd);
530
+ phaseDirCount = node_fs_1.default
531
+ .readdirSync(phasesDir, { withFileTypes: true })
532
+ .filter((entry) => entry.isDirectory() && isDirInMilestone(entry.name))
533
+ .length;
534
+ }
535
+ }
536
+ catch {
537
+ /* intentionally empty */
538
+ }
539
+ const wf = (config.workflow ?? {});
540
+ const result = {
541
+ researcher_model: resolveModelInternal(cwd, 'pp-project-researcher'),
542
+ synthesizer_model: resolveModelInternal(cwd, 'pp-research-synthesizer'),
543
+ roadmapper_model: resolveModelInternal(cwd, 'pp-roadmapper'),
544
+ commit_docs: config.commit_docs,
545
+ research_enabled: wf['research'],
546
+ current_milestone: milestone['version'],
547
+ current_milestone_name: milestone['name'],
548
+ latest_completed_milestone: latestCompleted?.version || null,
549
+ latest_completed_milestone_name: latestCompleted?.name || null,
550
+ phase_dir_count: phaseDirCount,
551
+ phase_archive_path: latestCompleted
552
+ ? toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningRoot(cwd), 'milestones', `${latestCompleted.version}-phases`)))
553
+ : null,
554
+ project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
555
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
556
+ state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
557
+ project_path: '.planning/PROJECT.md',
558
+ roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
559
+ state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
560
+ };
561
+ output(withProjectRoot(cwd, result), raw);
562
+ }
563
+ function cmdInitQuick(cwd, description, raw) {
564
+ const config = loadConfig(cwd);
565
+ const now = new Date();
566
+ const slug = description ? generateSlugInternal(description)?.substring(0, 40) : null;
567
+ const yy = String(now.getFullYear()).slice(-2);
568
+ const mm = String(now.getMonth() + 1).padStart(2, '0');
569
+ const dd = String(now.getDate()).padStart(2, '0');
570
+ const dateStr = yy + mm + dd;
571
+ const secondsSinceMidnight = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds();
572
+ const timeBlocks = Math.floor(secondsSinceMidnight / 2);
573
+ const timeEncoded = timeBlocks.toString(36).padStart(3, '0');
574
+ const quickId = dateStr + '-' + timeEncoded;
575
+ const branchSlug = slug || 'quick';
576
+ const quickBranchName = config.quick_branch_template
577
+ ? config.quick_branch_template
578
+ .replace('{num}', quickId)
579
+ .replace('{quick}', quickId)
580
+ .replace('{slug}', branchSlug)
581
+ : null;
582
+ const result = {
583
+ planner_model: resolveModelInternal(cwd, 'pp-planner'),
584
+ executor_model: resolveModelInternal(cwd, 'pp-executor'),
585
+ checker_model: resolveModelInternal(cwd, 'pp-plan-checker'),
586
+ verifier_model: resolveModelInternal(cwd, 'pp-verifier'),
587
+ commit_docs: config.commit_docs,
588
+ branch_name: quickBranchName,
589
+ quick_id: quickId,
590
+ slug: slug,
591
+ description: description || null,
592
+ date: now.toISOString().split('T')[0],
593
+ timestamp: now.toISOString(),
594
+ quick_dir: '.planning/quick',
595
+ task_dir: slug ? `.planning/quick/${quickId}-${slug}` : null,
596
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
597
+ planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
598
+ };
599
+ output(withProjectRoot(cwd, result), raw);
600
+ }
601
+ function cmdInitIngestDocs(cwd, raw) {
602
+ const config = loadConfig(cwd);
603
+ const result = {
604
+ project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
605
+ planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
606
+ ...getInitGitState(cwd),
607
+ project_path: '.planning/PROJECT.md',
608
+ commit_docs: config.commit_docs,
609
+ };
610
+ output(withProjectRoot(cwd, result), raw);
611
+ }
612
+ function cmdInitResume(cwd, raw) {
613
+ const config = loadConfig(cwd);
614
+ let interruptedAgentId = null;
615
+ const agentIdRaw = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(planningRoot(cwd), 'current-agent-id.txt'));
616
+ if (agentIdRaw !== null)
617
+ interruptedAgentId = agentIdRaw.trim();
618
+ const result = {
619
+ state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
620
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
621
+ project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
622
+ planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
623
+ state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
624
+ roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
625
+ project_path: '.planning/PROJECT.md',
626
+ has_interrupted_agent: !!interruptedAgentId,
627
+ interrupted_agent_id: interruptedAgentId,
628
+ commit_docs: config.commit_docs,
629
+ };
630
+ output(withProjectRoot(cwd, result), raw);
631
+ }
632
+ function cmdInitVerifyWork(cwd, phase, raw) {
633
+ if (!phase) {
634
+ error('phase required for init verify-work');
635
+ }
636
+ const config = loadConfig(cwd);
637
+ let phaseInfo = findPhaseInternal(cwd, phase);
638
+ if (phaseInfo?.['archived']) {
639
+ const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
640
+ if (roadmapPhase?.['found']) {
641
+ phaseInfo = null;
642
+ }
643
+ }
644
+ if (!phaseInfo) {
645
+ const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
646
+ if (roadmapPhase?.['found']) {
647
+ const phaseName = roadmapPhase['phase_name'];
648
+ phaseInfo = {
649
+ found: true,
650
+ directory: null,
651
+ phase_number: roadmapPhase['phase_number'],
652
+ phase_name: phaseName,
653
+ phase_slug: phaseName
654
+ ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
655
+ : null,
656
+ plans: [],
657
+ summaries: [],
658
+ incomplete_plans: [],
659
+ has_research: false,
660
+ has_context: false,
661
+ has_verification: false,
662
+ };
663
+ }
664
+ }
665
+ const result = {
666
+ planner_model: resolveModelInternal(cwd, 'pp-planner'),
667
+ checker_model: resolveModelInternal(cwd, 'pp-plan-checker'),
668
+ commit_docs: config.commit_docs,
669
+ phase_found: !!phaseInfo,
670
+ phase_dir: phaseInfo?.['directory'] || null,
671
+ phase_number: phaseInfo?.['phase_number'] || null,
672
+ phase_name: phaseInfo?.['phase_name'] || null,
673
+ has_verification: phaseInfo?.['has_verification'] || false,
674
+ };
675
+ output(withProjectRoot(cwd, result), raw);
676
+ }
677
+ function cmdInitPhaseOp(cwd, phase, raw) {
678
+ const config = loadConfig(cwd);
679
+ let phaseInfo = findPhaseInternal(cwd, phase);
680
+ if (phaseInfo?.['archived']) {
681
+ const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
682
+ if (roadmapPhase?.['found']) {
683
+ const phaseName = roadmapPhase['phase_name'];
684
+ phaseInfo = {
685
+ found: true,
686
+ directory: null,
687
+ phase_number: roadmapPhase['phase_number'],
688
+ phase_name: phaseName,
689
+ phase_slug: phaseName
690
+ ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
691
+ : null,
692
+ plans: [],
693
+ summaries: [],
694
+ incomplete_plans: [],
695
+ has_research: false,
696
+ has_context: false,
697
+ has_verification: false,
698
+ };
699
+ }
700
+ }
701
+ if (!phaseInfo) {
702
+ const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
703
+ if (roadmapPhase?.['found']) {
704
+ const phaseName = roadmapPhase['phase_name'];
705
+ phaseInfo = {
706
+ found: true,
707
+ directory: null,
708
+ phase_number: roadmapPhase['phase_number'],
709
+ phase_name: phaseName,
710
+ phase_slug: phaseName
711
+ ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
712
+ : null,
713
+ plans: [],
714
+ summaries: [],
715
+ incomplete_plans: [],
716
+ has_research: false,
717
+ has_context: false,
718
+ has_verification: false,
719
+ };
720
+ }
721
+ }
722
+ const phaseDir = phaseInfo?.['directory'] || null;
723
+ const phaseNumber = phaseInfo?.['phase_number'] || null;
724
+ const phaseName = phaseInfo?.['phase_name'] || null;
725
+ const rawProjectCode = config.project_code || '';
726
+ let expectedPhaseDir = null;
727
+ if (!phaseDir && phaseNumber && phaseName) {
728
+ const paddedNum = normalizePhaseName(phaseNumber);
729
+ const slug = (generateSlugInternal(phaseName) || '').substring(0, 60);
730
+ if (slug) {
731
+ const prefix = rawProjectCode ? `${rawProjectCode}-` : '';
732
+ const dirName = `${prefix}${paddedNum}-${slug}`;
733
+ expectedPhaseDir = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningPaths(cwd).phases, dirName)));
734
+ }
735
+ }
736
+ const result = {
737
+ commit_docs: config.commit_docs,
738
+ brave_search: typeof config.brave_search === 'string'
739
+ ? (0, secrets_cjs_1.maskIfSecret)('brave_search', config.brave_search)
740
+ : config.brave_search,
741
+ firecrawl: typeof config.firecrawl === 'string'
742
+ ? (0, secrets_cjs_1.maskIfSecret)('firecrawl', config.firecrawl)
743
+ : config.firecrawl,
744
+ exa_search: typeof config.exa_search === 'string'
745
+ ? (0, secrets_cjs_1.maskIfSecret)('exa_search', config.exa_search)
746
+ : config.exa_search,
747
+ phase_found: !!phaseInfo,
748
+ phase_dir: phaseDir,
749
+ expected_phase_dir: expectedPhaseDir,
750
+ phase_number: phaseNumber,
751
+ phase_name: phaseName,
752
+ phase_slug: phaseInfo?.['phase_slug'] || null,
753
+ padded_phase: phaseNumber ? normalizePhaseName(phaseNumber) : null,
754
+ has_research: phaseInfo?.['has_research'] || false,
755
+ has_context: phaseInfo?.['has_context'] || false,
756
+ has_plans: (phaseInfo?.['plans']?.length || 0) > 0,
757
+ has_verification: phaseInfo?.['has_verification'] || false,
758
+ has_reviews: phaseInfo?.['has_reviews'] || false,
759
+ plan_count: phaseInfo?.['plans']?.length || 0,
760
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
761
+ planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
762
+ state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
763
+ roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
764
+ requirements_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md'))),
765
+ };
766
+ if (phaseInfo?.['directory']) {
767
+ const phaseDirFull = node_path_1.default.join(cwd, phaseInfo['directory']);
768
+ try {
769
+ const files = node_fs_1.default.readdirSync(phaseDirFull);
770
+ const contextFile = findContextMdIn(phaseDirFull);
771
+ if (contextFile) {
772
+ result['context_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], contextFile));
773
+ }
774
+ const researchFile = files.find((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
775
+ if (researchFile) {
776
+ result['research_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], researchFile));
777
+ }
778
+ const verificationFile = files.find((f) => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
779
+ if (verificationFile) {
780
+ result['verification_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], verificationFile));
781
+ }
782
+ const uatFile = files.find((f) => f.endsWith('-UAT.md') || f === 'UAT.md');
783
+ if (uatFile) {
784
+ result['uat_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], uatFile));
785
+ }
786
+ const reviewsFile = files.find((f) => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md');
787
+ if (reviewsFile) {
788
+ result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], reviewsFile));
789
+ }
790
+ }
791
+ catch {
792
+ /* intentionally empty */
793
+ }
794
+ }
795
+ output(withProjectRoot(cwd, result), raw);
796
+ }
797
+ function cmdInitTodos(cwd, area, raw) {
798
+ const config = loadConfig(cwd);
799
+ const now = new Date();
800
+ const pendingDir = node_path_1.default.join(planningDir(cwd), 'todos', 'pending');
801
+ let count = 0;
802
+ const todos = [];
803
+ try {
804
+ const files = node_fs_1.default.readdirSync(pendingDir).filter((f) => f.endsWith('.md'));
805
+ for (const file of files) {
806
+ const content = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(pendingDir, file));
807
+ if (content === null)
808
+ continue;
809
+ try {
810
+ const createdMatch = content.match(/^created:\s*(.+)$/m);
811
+ const titleMatch = content.match(/^title:\s*(.+)$/m);
812
+ const areaMatch = content.match(/^area:\s*(.+)$/m);
813
+ const todoArea = areaMatch ? areaMatch[1].trim() : 'general';
814
+ if (area && todoArea !== area)
815
+ continue;
816
+ count++;
817
+ todos.push({
818
+ file,
819
+ created: createdMatch ? createdMatch[1].trim() : 'unknown',
820
+ title: titleMatch ? titleMatch[1].trim() : 'Untitled',
821
+ area: todoArea,
822
+ path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'todos', 'pending', file))),
823
+ });
824
+ }
825
+ catch {
826
+ /* intentionally empty */
827
+ }
828
+ }
829
+ }
830
+ catch {
831
+ /* intentionally empty */
832
+ }
833
+ const result = {
834
+ commit_docs: config.commit_docs,
835
+ date: now.toISOString().split('T')[0],
836
+ timestamp: now.toISOString(),
837
+ todo_count: count,
838
+ todos,
839
+ area_filter: area || null,
840
+ pending_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'todos', 'pending'))),
841
+ completed_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'todos', 'completed'))),
842
+ planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
843
+ todos_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'todos')),
844
+ pending_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'todos', 'pending')),
845
+ };
846
+ output(withProjectRoot(cwd, result), raw);
847
+ }
848
+ function cmdInitMilestoneOp(cwd, raw) {
849
+ const config = loadConfig(cwd);
850
+ const milestone = getMilestoneInfo(cwd);
851
+ let phaseCount = 0;
852
+ let completedPhases = 0;
853
+ const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
854
+ const roadmapPhaseNumbers = [];
855
+ try {
856
+ const roadmapPath = node_path_1.default.join(planningDir(cwd), 'ROADMAP.md');
857
+ const roadmapRaw = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
858
+ const currentSection = extractCurrentMilestone(roadmapRaw, cwd);
859
+ const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:/gi;
860
+ let m;
861
+ while ((m = phasePattern.exec(currentSection)) !== null) {
862
+ roadmapPhaseNumbers.push(m[1]);
863
+ }
864
+ }
865
+ catch {
866
+ /* intentionally empty */
867
+ }
868
+ const canonicalizePhase = (tok) => {
869
+ const m = tok.match(/^(\d+)([A-Z]?(?:\.\d+)*)$/);
870
+ return m ? String(parseInt(m[1], 10)) + m[2] : tok;
871
+ };
872
+ const diskPhaseDirs = new Map();
873
+ try {
874
+ const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
875
+ for (const e of entries) {
876
+ if (!e.isDirectory())
877
+ continue;
878
+ const m = e.name.match(/^(\d+[A-Z]?(?:\.\d+)*)/);
879
+ if (!m)
880
+ continue;
881
+ diskPhaseDirs.set(canonicalizePhase(m[1]), e.name);
882
+ }
883
+ }
884
+ catch {
885
+ /* intentionally empty */
886
+ }
887
+ if (roadmapPhaseNumbers.length > 0) {
888
+ phaseCount = roadmapPhaseNumbers.length;
889
+ for (const num of roadmapPhaseNumbers) {
890
+ const dirName = diskPhaseDirs.get(canonicalizePhase(num));
891
+ if (!dirName)
892
+ continue;
893
+ try {
894
+ const hasSummary = listPhaseSummaryFiles(node_path_1.default.join(phasesDir, dirName)).length > 0;
895
+ if (hasSummary)
896
+ completedPhases++;
897
+ }
898
+ catch {
899
+ /* intentionally empty */
900
+ }
901
+ }
902
+ }
903
+ else {
904
+ try {
905
+ const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
906
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
907
+ phaseCount = dirs.length;
908
+ for (const dir of dirs) {
909
+ try {
910
+ const hasSummary = listPhaseSummaryFiles(node_path_1.default.join(phasesDir, dir)).length > 0;
911
+ if (hasSummary)
912
+ completedPhases++;
913
+ }
914
+ catch {
915
+ /* intentionally empty */
916
+ }
917
+ }
918
+ }
919
+ catch {
920
+ /* intentionally empty */
921
+ }
922
+ }
923
+ const archiveDir = node_path_1.default.join(planningRoot(cwd), 'archive');
924
+ let archivedMilestones = [];
925
+ try {
926
+ archivedMilestones = node_fs_1.default
927
+ .readdirSync(archiveDir, { withFileTypes: true })
928
+ .filter((e) => e.isDirectory())
929
+ .map((e) => e.name);
930
+ }
931
+ catch {
932
+ /* intentionally empty */
933
+ }
934
+ const result = {
935
+ commit_docs: config.commit_docs,
936
+ milestone_version: milestone['version'],
937
+ milestone_name: milestone['name'],
938
+ milestone_slug: generateSlugInternal(milestone['name']),
939
+ phase_count: phaseCount,
940
+ completed_phases: completedPhases,
941
+ all_phases_complete: phaseCount > 0 && phaseCount === completedPhases,
942
+ archived_milestones: archivedMilestones,
943
+ archive_count: archivedMilestones.length,
944
+ project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
945
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
946
+ state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
947
+ archive_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningRoot(cwd), 'archive')),
948
+ phases_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'phases')),
949
+ };
950
+ output(withProjectRoot(cwd, result), raw);
951
+ }
952
+ function cmdInitMapCodebase(cwd, raw) {
953
+ const config = loadConfig(cwd);
954
+ const now = new Date();
955
+ const codebaseDir = node_path_1.default.join(planningRoot(cwd), 'codebase');
956
+ let existingMaps = [];
957
+ try {
958
+ existingMaps = node_fs_1.default.readdirSync(codebaseDir).filter((f) => f.endsWith('.md'));
959
+ }
960
+ catch {
961
+ /* intentionally empty */
962
+ }
963
+ const result = {
964
+ mapper_model: resolveModelInternal(cwd, 'pp-codebase-mapper'),
965
+ commit_docs: config.commit_docs,
966
+ search_gitignored: config.search_gitignored,
967
+ parallelization: config.parallelization,
968
+ subagent_timeout: config.subagent_timeout,
969
+ date: now.toISOString().split('T')[0],
970
+ timestamp: now.toISOString(),
971
+ codebase_dir: '.planning/codebase',
972
+ existing_maps: existingMaps,
973
+ has_maps: existingMaps.length > 0,
974
+ planning_exists: pathExistsInternal(cwd, '.planning'),
975
+ codebase_dir_exists: pathExistsInternal(cwd, '.planning/codebase'),
976
+ };
977
+ output(withProjectRoot(cwd, result), raw);
978
+ }
979
+ function cmdInitManager(cwd, raw) {
980
+ const config = loadConfig(cwd);
981
+ const milestone = getMilestoneInfo(cwd);
982
+ const _slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
983
+ const paths = planningPaths(cwd);
984
+ if (!node_fs_1.default.existsSync(paths.roadmap)) {
985
+ error(`No ROADMAP.md found. Run ${(0, runtime_slash_cjs_1.formatGsdSlash)('new-milestone', _slashRuntime)} first.`);
986
+ }
987
+ if (!node_fs_1.default.existsSync(paths.state)) {
988
+ error(`No STATE.md found. Run ${(0, runtime_slash_cjs_1.formatGsdSlash)('new-milestone', _slashRuntime)} first.`);
989
+ }
990
+ const rawContent = node_fs_1.default.readFileSync(paths.roadmap, 'utf-8');
991
+ const content = extractCurrentMilestone(rawContent, cwd);
992
+ const phasesDir = paths.phases;
993
+ const isDirInMilestone = getMilestonePhaseFilter(cwd);
994
+ const _phaseDirEntries = (() => {
995
+ try {
996
+ return node_fs_1.default
997
+ .readdirSync(phasesDir, { withFileTypes: true })
998
+ .filter((e) => e.isDirectory())
999
+ .map((e) => e.name);
1000
+ }
1001
+ catch {
1002
+ return [];
1003
+ }
1004
+ })();
1005
+ const _checkboxStates = new Map();
1006
+ const _cbPattern = /-\s*\[(x| )\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)[:\s]/gi;
1007
+ let _cbMatch;
1008
+ while ((_cbMatch = _cbPattern.exec(content)) !== null) {
1009
+ _checkboxStates.set(_cbMatch[2], _cbMatch[1].toLowerCase() === 'x');
1010
+ }
1011
+ const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
1012
+ const phases = [];
1013
+ let match;
1014
+ while ((match = phasePattern.exec(content)) !== null) {
1015
+ const phaseNum = match[1];
1016
+ const phaseName = match[2].replace(/\(INSERTED\)/i, '').trim();
1017
+ const sectionStart = match.index;
1018
+ const restOfContent = content.slice(sectionStart);
1019
+ const nextHeader = restOfContent.match(/\n#{2,4}\s+Phase\s+\d[\d.]*/i);
1020
+ const sectionEnd = nextHeader
1021
+ ? sectionStart + nextHeader.index
1022
+ : content.length;
1023
+ const section = content.slice(sectionStart, sectionEnd);
1024
+ const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
1025
+ const goal = goalMatch ? goalMatch[1].trim() : null;
1026
+ const dependsMatch = section.match(/\*\*Depends on(?::\*\*|\*\*:)\s*([^\n]+)/i);
1027
+ const depends_on = dependsMatch ? dependsMatch[1].trim() : null;
1028
+ const normalized = normalizePhaseName(phaseNum);
1029
+ let diskStatus = 'no_directory';
1030
+ let planCount = 0;
1031
+ let summaryCount = 0;
1032
+ let hasContext = false;
1033
+ let hasResearch = false;
1034
+ let lastActivity = null;
1035
+ let isActive = false;
1036
+ try {
1037
+ const dirs = _phaseDirEntries.filter(isDirInMilestone);
1038
+ const dirMatch = dirs.find((d) => phaseTokenMatches(d, normalized));
1039
+ if (dirMatch) {
1040
+ const fullDir = node_path_1.default.join(phasesDir, dirMatch);
1041
+ const phaseFiles = node_fs_1.default.readdirSync(fullDir);
1042
+ planCount = listPhasePlanFiles(fullDir).length;
1043
+ summaryCount = listPhaseSummaryFiles(fullDir).length;
1044
+ hasContext = findContextMdIn(fullDir) !== null;
1045
+ hasResearch = phaseFiles.some((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
1046
+ if (summaryCount >= planCount && planCount > 0)
1047
+ diskStatus = 'complete';
1048
+ else if (summaryCount > 0)
1049
+ diskStatus = 'partial';
1050
+ else if (planCount > 0)
1051
+ diskStatus = 'planned';
1052
+ else if (hasResearch)
1053
+ diskStatus = 'researched';
1054
+ else if (hasContext)
1055
+ diskStatus = 'discussed';
1056
+ else
1057
+ diskStatus = 'empty';
1058
+ const nowMs = Date.now();
1059
+ let newestMtime = 0;
1060
+ for (const f of phaseFiles) {
1061
+ try {
1062
+ const stat = node_fs_1.default.statSync(node_path_1.default.join(fullDir, f));
1063
+ if (stat.mtimeMs > newestMtime)
1064
+ newestMtime = stat.mtimeMs;
1065
+ }
1066
+ catch {
1067
+ /* intentionally empty */
1068
+ }
1069
+ }
1070
+ if (newestMtime > 0) {
1071
+ lastActivity = new Date(newestMtime).toISOString();
1072
+ isActive = nowMs - newestMtime < 300000;
1073
+ }
1074
+ }
1075
+ }
1076
+ catch {
1077
+ /* intentionally empty */
1078
+ }
1079
+ const roadmapComplete = _checkboxStates.get(phaseNum) || false;
1080
+ if (roadmapComplete && diskStatus !== 'complete') {
1081
+ diskStatus = 'complete';
1082
+ }
1083
+ phases.push({
1084
+ number: phaseNum,
1085
+ name: phaseName,
1086
+ goal,
1087
+ depends_on,
1088
+ disk_status: diskStatus,
1089
+ has_context: hasContext,
1090
+ has_research: hasResearch,
1091
+ plan_count: planCount,
1092
+ summary_count: summaryCount,
1093
+ roadmap_complete: roadmapComplete,
1094
+ last_activity: lastActivity,
1095
+ is_active: isActive,
1096
+ });
1097
+ }
1098
+ const MAX_NAME_WIDTH = 20;
1099
+ for (const phase of phases) {
1100
+ const name = phase['name'];
1101
+ if (name.length > MAX_NAME_WIDTH) {
1102
+ phase['display_name'] = name.slice(0, MAX_NAME_WIDTH - 1) + '…';
1103
+ }
1104
+ else {
1105
+ phase['display_name'] = name;
1106
+ }
1107
+ }
1108
+ const completedNums = new Set(phases.filter((p) => p['disk_status'] === 'complete').map((p) => p['number']));
1109
+ const _allCompletedPattern = /-\s*\[x\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)[:\s]/gi;
1110
+ let _allMatch;
1111
+ while ((_allMatch = _allCompletedPattern.exec(rawContent)) !== null) {
1112
+ completedNums.add(_allMatch[1]);
1113
+ }
1114
+ const phaseMap = new Map(phases.map((p) => [p['number'], p]));
1115
+ function reaches(from, to, visited = new Set()) {
1116
+ if (visited.has(from))
1117
+ return false;
1118
+ visited.add(from);
1119
+ const p = phaseMap.get(from);
1120
+ if (!p || !p['dep_phases'] || p['dep_phases'].length === 0)
1121
+ return false;
1122
+ if (p['dep_phases'].includes(to))
1123
+ return true;
1124
+ return p['dep_phases'].some((dep) => reaches(dep, to, visited));
1125
+ }
1126
+ function hasDepRelationship(numA, numB) {
1127
+ return reaches(numA, numB) || reaches(numB, numA);
1128
+ }
1129
+ for (const phase of phases) {
1130
+ if (!phase['depends_on'] ||
1131
+ /^none$/i.test(phase['depends_on'].trim())) {
1132
+ phase['deps_satisfied'] = true;
1133
+ }
1134
+ else {
1135
+ const depNums = phase['depends_on'].match(/\d+(?:\.\d+)*/g) || [];
1136
+ phase['deps_satisfied'] = depNums.every((n) => completedNums.has(n));
1137
+ phase['dep_phases'] = depNums;
1138
+ }
1139
+ }
1140
+ for (const phase of phases) {
1141
+ phase['deps_display'] =
1142
+ phase['dep_phases'] && phase['dep_phases'].length > 0
1143
+ ? phase['dep_phases'].join(',')
1144
+ : '—';
1145
+ }
1146
+ for (const phase of phases) {
1147
+ phase['is_next_to_discuss'] =
1148
+ (phase['disk_status'] === 'empty' || phase['disk_status'] === 'no_directory') &&
1149
+ phase['deps_satisfied'];
1150
+ }
1151
+ let waitingSignal = null;
1152
+ try {
1153
+ const waitingPath = node_path_1.default.join(cwd, '.planning', 'WAITING.json');
1154
+ const waitingRaw = (0, shell_command_projection_cjs_1.platformReadSync)(waitingPath);
1155
+ if (waitingRaw !== null) {
1156
+ waitingSignal = JSON.parse(waitingRaw);
1157
+ }
1158
+ }
1159
+ catch {
1160
+ /* intentionally empty */
1161
+ }
1162
+ const recommendedActions = [];
1163
+ for (const phase of phases) {
1164
+ if (phase['disk_status'] === 'complete')
1165
+ continue;
1166
+ if (/^999(?:\.|$)/.test(phase['number']))
1167
+ continue;
1168
+ if (phase['disk_status'] === 'planned' && phase['deps_satisfied']) {
1169
+ recommendedActions.push({
1170
+ phase: phase['number'],
1171
+ phase_name: phase['name'],
1172
+ action: 'execute',
1173
+ reason: `${phase['plan_count']} plans ready, dependencies met`,
1174
+ command: `${(0, runtime_slash_cjs_1.formatGsdSlash)('execute-phase', _slashRuntime)} ${phase['number']}`,
1175
+ });
1176
+ }
1177
+ else if (phase['disk_status'] === 'discussed' ||
1178
+ phase['disk_status'] === 'researched') {
1179
+ recommendedActions.push({
1180
+ phase: phase['number'],
1181
+ phase_name: phase['name'],
1182
+ action: 'plan',
1183
+ reason: 'Context gathered, ready for planning',
1184
+ command: `${(0, runtime_slash_cjs_1.formatGsdSlash)('plan-phase', _slashRuntime)} ${phase['number']}`,
1185
+ });
1186
+ }
1187
+ else if ((phase['disk_status'] === 'empty' || phase['disk_status'] === 'no_directory') &&
1188
+ phase['is_next_to_discuss']) {
1189
+ recommendedActions.push({
1190
+ phase: phase['number'],
1191
+ phase_name: phase['name'],
1192
+ action: 'discuss',
1193
+ reason: 'Unblocked, ready to gather context',
1194
+ command: `${(0, runtime_slash_cjs_1.formatGsdSlash)('discuss-phase', _slashRuntime)} ${phase['number']}`,
1195
+ });
1196
+ }
1197
+ }
1198
+ const activeExecuting = phases.filter((p) => p['disk_status'] === 'partial' ||
1199
+ (p['disk_status'] === 'planned' && p['is_active']));
1200
+ const activePlanning = phases.filter((p) => p['is_active'] &&
1201
+ (p['disk_status'] === 'discussed' || p['disk_status'] === 'researched'));
1202
+ const filteredActions = recommendedActions.filter((action) => {
1203
+ if (action['action'] === 'execute' && activeExecuting.length > 0) {
1204
+ return activeExecuting.every((active) => !hasDepRelationship(action['phase'], active['number']));
1205
+ }
1206
+ if (action['action'] === 'plan' && activePlanning.length > 0) {
1207
+ return activePlanning.every((active) => !hasDepRelationship(action['phase'], active['number']));
1208
+ }
1209
+ return true;
1210
+ });
1211
+ const nonBacklogPhases = phases.filter((p) => !/^999(?:\.|$)/.test(p['number']));
1212
+ const completedCount = nonBacklogPhases.filter((p) => p['disk_status'] === 'complete').length;
1213
+ const sanitizeFlags = (rawVal) => {
1214
+ const val = typeof rawVal === 'string' ? rawVal : '';
1215
+ if (!val)
1216
+ return '';
1217
+ const tokens = val.split(/\s+/).filter(Boolean);
1218
+ const safe = tokens.every((t) => /^--[a-zA-Z0-9][-a-zA-Z0-9]*$/.test(t) ||
1219
+ /^[a-zA-Z0-9][-a-zA-Z0-9_.]*$/.test(t));
1220
+ if (!safe) {
1221
+ process.stderr.write(`gsd-tools: warning: manager.flags contains invalid tokens, ignoring: ${val}\n`);
1222
+ return '';
1223
+ }
1224
+ return val;
1225
+ };
1226
+ const mgr = config.manager;
1227
+ const mgrFlags = mgr?.['flags'];
1228
+ const managerFlags = {
1229
+ discuss: sanitizeFlags(mgrFlags?.['discuss']),
1230
+ plan: sanitizeFlags(mgrFlags?.['plan']),
1231
+ execute: sanitizeFlags(mgrFlags?.['execute']),
1232
+ };
1233
+ const result = {
1234
+ milestone_version: milestone['version'],
1235
+ milestone_name: milestone['name'],
1236
+ phases,
1237
+ phase_count: phases.length,
1238
+ completed_count: completedCount,
1239
+ in_progress_count: phases.filter((p) => ['partial', 'planned', 'discussed', 'researched'].includes(p['disk_status'])).length,
1240
+ recommended_actions: filteredActions,
1241
+ waiting_signal: waitingSignal,
1242
+ all_complete: completedCount === nonBacklogPhases.length && nonBacklogPhases.length > 0,
1243
+ project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
1244
+ roadmap_exists: true,
1245
+ state_exists: true,
1246
+ manager_flags: managerFlags,
1247
+ };
1248
+ output(withProjectRoot(cwd, result), raw);
1249
+ }
1250
+ function cmdInitProgress(cwd, raw) {
1251
+ try {
1252
+ pruneOrphanedWorktrees(cwd);
1253
+ }
1254
+ catch {
1255
+ /* intentionally empty */
1256
+ }
1257
+ const config = loadConfig(cwd);
1258
+ const milestone = getMilestoneInfo(cwd);
1259
+ const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
1260
+ const phases = [];
1261
+ let currentPhase = null;
1262
+ let nextPhase = null;
1263
+ const roadmapPhaseNums = new Set();
1264
+ const roadmapPhaseNames = new Map();
1265
+ const roadmapCheckboxStates = new Map();
1266
+ try {
1267
+ const roadmapContent = extractCurrentMilestone(node_fs_1.default.readFileSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'), 'utf-8'), cwd);
1268
+ const headingPattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
1269
+ let hm;
1270
+ while ((hm = headingPattern.exec(roadmapContent)) !== null) {
1271
+ roadmapPhaseNums.add(hm[1]);
1272
+ roadmapPhaseNames.set(hm[1], hm[2].replace(/\(INSERTED\)/i, '').trim());
1273
+ }
1274
+ const cbPattern = /-\s*\[(x| )\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)[:\s]/gi;
1275
+ let cbm;
1276
+ while ((cbm = cbPattern.exec(roadmapContent)) !== null) {
1277
+ roadmapCheckboxStates.set(cbm[2], cbm[1].toLowerCase() === 'x');
1278
+ }
1279
+ }
1280
+ catch {
1281
+ /* intentionally empty */
1282
+ }
1283
+ const isDirInMilestone = getMilestonePhaseFilter(cwd);
1284
+ const seenPhaseNums = new Set();
1285
+ try {
1286
+ const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
1287
+ const dirs = entries
1288
+ .filter((e) => e.isDirectory())
1289
+ .map((e) => e.name)
1290
+ .filter(isDirInMilestone)
1291
+ .sort((a, b) => {
1292
+ const pa = a.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
1293
+ const pb = b.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
1294
+ if (!pa || !pb)
1295
+ return a.localeCompare(b);
1296
+ return parseInt(pa[1], 10) - parseInt(pb[1], 10);
1297
+ });
1298
+ for (const dir of dirs) {
1299
+ const dirMatch = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i);
1300
+ const phaseNumber = dirMatch ? dirMatch[1] : dir;
1301
+ const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
1302
+ seenPhaseNums.add(phaseNumber.replace(/^0+/, '') || '0');
1303
+ const phasePath = node_path_1.default.join(phasesDir, dir);
1304
+ const phaseFiles = node_fs_1.default.readdirSync(phasePath);
1305
+ const plans = listPhasePlanFiles(phasePath);
1306
+ const summaries = listPhaseSummaryFiles(phasePath);
1307
+ const hasResearch = phaseFiles.some((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
1308
+ const status = summaries.length >= plans.length && plans.length > 0
1309
+ ? 'complete'
1310
+ : plans.length > 0
1311
+ ? 'in_progress'
1312
+ : hasResearch
1313
+ ? 'researched'
1314
+ : 'pending';
1315
+ const phaseInfo = {
1316
+ number: phaseNumber,
1317
+ name: phaseName,
1318
+ directory: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'phases', dir))),
1319
+ status,
1320
+ plan_count: plans.length,
1321
+ summary_count: summaries.length,
1322
+ has_research: hasResearch,
1323
+ };
1324
+ phases.push(phaseInfo);
1325
+ if (!currentPhase && (status === 'in_progress' || status === 'researched')) {
1326
+ currentPhase = phaseInfo;
1327
+ }
1328
+ if (!nextPhase && status === 'pending') {
1329
+ nextPhase = phaseInfo;
1330
+ }
1331
+ }
1332
+ }
1333
+ catch {
1334
+ /* intentionally empty */
1335
+ }
1336
+ for (const [num, name] of roadmapPhaseNames) {
1337
+ const stripped = num.replace(/^0+/, '') || '0';
1338
+ if (!seenPhaseNums.has(stripped)) {
1339
+ const checkboxComplete = roadmapCheckboxStates.get(num) === true ||
1340
+ roadmapCheckboxStates.get(stripped) === true;
1341
+ const status = checkboxComplete ? 'complete' : 'not_started';
1342
+ const phaseInfo = {
1343
+ number: num,
1344
+ name: name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''),
1345
+ directory: null,
1346
+ status,
1347
+ plan_count: 0,
1348
+ summary_count: 0,
1349
+ has_research: false,
1350
+ };
1351
+ phases.push(phaseInfo);
1352
+ if (!nextPhase && !currentPhase && status !== 'complete') {
1353
+ nextPhase = phaseInfo;
1354
+ }
1355
+ }
1356
+ }
1357
+ phases.sort((a, b) => parseInt(a['number'], 10) - parseInt(b['number'], 10));
1358
+ let pausedAt = null;
1359
+ const state = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(planningDir(cwd), 'STATE.md'));
1360
+ if (state !== null) {
1361
+ const pauseMatch = state.match(/\*\*Paused At:\*\*\s*(.+)/);
1362
+ if (pauseMatch)
1363
+ pausedAt = pauseMatch[1].trim();
1364
+ }
1365
+ const result = {
1366
+ executor_model: resolveModelInternal(cwd, 'pp-executor'),
1367
+ planner_model: resolveModelInternal(cwd, 'pp-planner'),
1368
+ commit_docs: config.commit_docs,
1369
+ milestone_version: milestone['version'],
1370
+ milestone_name: milestone['name'],
1371
+ phases,
1372
+ phase_count: phases.length,
1373
+ completed_count: phases.filter((p) => p['status'] === 'complete').length,
1374
+ in_progress_count: phases.filter((p) => p['status'] === 'in_progress').length,
1375
+ current_phase: currentPhase,
1376
+ next_phase: nextPhase,
1377
+ paused_at: pausedAt,
1378
+ has_work_in_progress: !!currentPhase,
1379
+ project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
1380
+ roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
1381
+ state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
1382
+ state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
1383
+ roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
1384
+ project_path: '.planning/PROJECT.md',
1385
+ config_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'config.json'))),
1386
+ };
1387
+ output(withProjectRoot(cwd, result), raw);
1388
+ }
1389
+ function detectChildRepos(dir) {
1390
+ const repos = [];
1391
+ let entries;
1392
+ try {
1393
+ entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
1394
+ }
1395
+ catch {
1396
+ return repos;
1397
+ }
1398
+ for (const entry of entries) {
1399
+ if (!entry.isDirectory())
1400
+ continue;
1401
+ if (entry.name.startsWith('.'))
1402
+ continue;
1403
+ const fullPath = node_path_1.default.join(dir, entry.name);
1404
+ const gitDir = node_path_1.default.join(fullPath, '.git');
1405
+ if (node_fs_1.default.existsSync(gitDir)) {
1406
+ const statusResult = (0, shell_command_projection_cjs_1.execGit)(['status', '--porcelain'], {
1407
+ cwd: fullPath,
1408
+ timeout: 5000,
1409
+ });
1410
+ const hasUncommitted = statusResult['exitCode'] === 0 &&
1411
+ statusResult['stdout'].length > 0;
1412
+ repos.push({ name: entry.name, path: fullPath, has_uncommitted: hasUncommitted });
1413
+ }
1414
+ }
1415
+ return repos;
1416
+ }
1417
+ function cmdInitNewWorkspace(cwd, raw) {
1418
+ const homedir = process.env['HOME'] || node_os_1.default.homedir();
1419
+ const defaultBase = node_path_1.default.join(homedir, 'gsd-workspaces');
1420
+ const childRepos = detectChildRepos(cwd);
1421
+ const gitVersion = (0, shell_command_projection_cjs_1.execGit)(['--version'], { timeout: 5000 });
1422
+ const worktreeAvailable = gitVersion['exitCode'] === 0;
1423
+ const result = {
1424
+ default_workspace_base: defaultBase,
1425
+ child_repos: childRepos,
1426
+ child_repo_count: childRepos.length,
1427
+ worktree_available: worktreeAvailable,
1428
+ is_git_repo: pathExistsInternal(cwd, '.git'),
1429
+ cwd_repo_name: node_path_1.default.basename(cwd),
1430
+ };
1431
+ output(withProjectRoot(cwd, result), raw);
1432
+ }
1433
+ function cmdInitListWorkspaces(cwd, raw) {
1434
+ const homedir = process.env['HOME'] || node_os_1.default.homedir();
1435
+ const defaultBase = node_path_1.default.join(homedir, 'gsd-workspaces');
1436
+ const workspaces = [];
1437
+ if (node_fs_1.default.existsSync(defaultBase)) {
1438
+ let entries;
1439
+ try {
1440
+ entries = node_fs_1.default.readdirSync(defaultBase, { withFileTypes: true });
1441
+ }
1442
+ catch {
1443
+ entries = [];
1444
+ }
1445
+ for (const entry of entries) {
1446
+ if (!entry.isDirectory())
1447
+ continue;
1448
+ const wsPath = node_path_1.default.join(defaultBase, entry.name);
1449
+ const manifestPath = node_path_1.default.join(wsPath, 'WORKSPACE.md');
1450
+ if (!node_fs_1.default.existsSync(manifestPath))
1451
+ continue;
1452
+ let repoCount = 0;
1453
+ let hasProject = false;
1454
+ let strategy = 'unknown';
1455
+ const manifest = (0, shell_command_projection_cjs_1.platformReadSync)(manifestPath);
1456
+ if (manifest !== null) {
1457
+ const strategyMatch = manifest.match(/^Strategy:\s*(.+)$/m);
1458
+ if (strategyMatch)
1459
+ strategy = strategyMatch[1].trim();
1460
+ const tableRows = manifest
1461
+ .split('\n')
1462
+ .filter((l) => l.match(/^\|\s*\w/) && !l.includes('Repo') && !l.includes('---'));
1463
+ repoCount = tableRows.length;
1464
+ }
1465
+ hasProject = node_fs_1.default.existsSync(node_path_1.default.join(wsPath, '.planning', 'PROJECT.md'));
1466
+ workspaces.push({
1467
+ name: entry.name,
1468
+ path: wsPath,
1469
+ repo_count: repoCount,
1470
+ strategy,
1471
+ has_project: hasProject,
1472
+ });
1473
+ }
1474
+ }
1475
+ const result = {
1476
+ workspace_base: defaultBase,
1477
+ workspaces,
1478
+ workspace_count: workspaces.length,
1479
+ };
1480
+ output(result, raw);
1481
+ }
1482
+ function cmdInitRemoveWorkspace(cwd, name, raw) {
1483
+ const homedir = process.env['HOME'] || node_os_1.default.homedir();
1484
+ const defaultBase = node_path_1.default.join(homedir, 'gsd-workspaces');
1485
+ if (!name) {
1486
+ error('workspace name required for init remove-workspace');
1487
+ }
1488
+ const wsPath = node_path_1.default.join(defaultBase, name);
1489
+ const manifestPath = node_path_1.default.join(wsPath, 'WORKSPACE.md');
1490
+ if (!node_fs_1.default.existsSync(wsPath)) {
1491
+ error(`Workspace not found: ${wsPath}`);
1492
+ }
1493
+ const repos = [];
1494
+ let strategy = 'unknown';
1495
+ const manifestContent = (0, shell_command_projection_cjs_1.platformReadSync)(manifestPath);
1496
+ if (manifestContent !== null) {
1497
+ try {
1498
+ const manifest = manifestContent;
1499
+ const strategyMatch = manifest.match(/^Strategy:\s*(.+)$/m);
1500
+ if (strategyMatch)
1501
+ strategy = strategyMatch[1].trim();
1502
+ const lines = manifest.split('\n');
1503
+ for (const line of lines) {
1504
+ const lineMatch = line.match(/^\|\s*(\S+)\s*\|\s*(\S+)\s*\|\s*(\S+)\s*\|\s*(\S+)\s*\|$/);
1505
+ if (lineMatch && lineMatch[1] !== 'Repo' && !lineMatch[1].includes('---')) {
1506
+ repos.push({
1507
+ name: lineMatch[1],
1508
+ source: lineMatch[2],
1509
+ branch: lineMatch[3],
1510
+ strategy: lineMatch[4],
1511
+ });
1512
+ }
1513
+ }
1514
+ }
1515
+ catch {
1516
+ /* best-effort */
1517
+ }
1518
+ }
1519
+ const dirtyRepos = [];
1520
+ for (const repo of repos) {
1521
+ const repoPath = node_path_1.default.join(wsPath, repo.name);
1522
+ if (!node_fs_1.default.existsSync(repoPath))
1523
+ continue;
1524
+ const statusResult = (0, shell_command_projection_cjs_1.execGit)(['status', '--porcelain'], {
1525
+ cwd: repoPath,
1526
+ timeout: 5000,
1527
+ });
1528
+ if (statusResult['exitCode'] === 0 &&
1529
+ statusResult['stdout'].length > 0) {
1530
+ dirtyRepos.push(repo.name);
1531
+ }
1532
+ }
1533
+ const result = {
1534
+ workspace_name: name,
1535
+ workspace_path: wsPath,
1536
+ has_manifest: node_fs_1.default.existsSync(manifestPath),
1537
+ strategy,
1538
+ repos,
1539
+ repo_count: repos.length,
1540
+ dirty_repos: dirtyRepos,
1541
+ has_dirty_repos: dirtyRepos.length > 0,
1542
+ };
1543
+ output(result, raw);
1544
+ }
1545
+ function buildAgentSkillsBlock(config, agentType, projectRoot) {
1546
+ const runtime = (config && config['runtime']) || 'claude';
1547
+ const globalSkillsBase = (0, runtime_homes_cjs_1.getGlobalSkillsBase)(runtime);
1548
+ if (!config || !config['agent_skills'] || !agentType)
1549
+ return '';
1550
+ let skillPaths = config['agent_skills'][agentType];
1551
+ if (!skillPaths)
1552
+ return '';
1553
+ if (typeof skillPaths === 'string')
1554
+ skillPaths = [skillPaths];
1555
+ if (!Array.isArray(skillPaths) || skillPaths.length === 0)
1556
+ return '';
1557
+ // Hoist trusted roots computation before the loop: loadTrustedGlobalRoots does
1558
+ // realpathSync I/O and should run at most once per call, not once per failing skill.
1559
+ // It returns [] cheaply when no roots are configured, so the realpath cost only
1560
+ // occurs when the caller has actually set trusted_global_roots.
1561
+ const trustedGlobalRoots = (0, security_cjs_1.loadTrustedGlobalRoots)(config);
1562
+ // Each entry is either a filesystem include ({ kind: 'include', ref, display }) or a
1563
+ // Skill-tool directive ({ kind: 'directive', name }) for plugin-provided namespaced skills.
1564
+ const validEntries = [];
1565
+ for (const skillPath of skillPaths) {
1566
+ if (typeof skillPath !== 'string')
1567
+ continue;
1568
+ if (skillPath.startsWith('global:')) {
1569
+ const skillName = skillPath.slice(7);
1570
+ if (!skillName) {
1571
+ process.stderr.write(`[agent-skills] WARNING: "global:" prefix with empty skill name — skipping\n`);
1572
+ continue;
1573
+ }
1574
+ // Accept: one or more [A-Za-z0-9_-]+ segments joined by single colons.
1575
+ // Rejects: empty segments (::), leading/trailing colon, dots, slashes, backslashes.
1576
+ if (!/^[A-Za-z0-9_-]+(:[A-Za-z0-9_-]+)*$/.test(skillName)) {
1577
+ process.stderr.write(`[agent-skills] WARNING: Invalid global skill name "${skillName}" — skipping\n`);
1578
+ continue;
1579
+ }
1580
+ const isNamespaced = skillName.includes(':');
1581
+ if (isNamespaced) {
1582
+ // Plugin-provided namespaced skill: no filesystem path exists locally.
1583
+ if (runtime === 'claude') {
1584
+ // Emit a natural-language Skill-tool directive (not a @-include).
1585
+ validEntries.push({ kind: 'directive', name: skillName });
1586
+ }
1587
+ else {
1588
+ process.stderr.write(`[agent-skills] WARNING: Plugin-namespaced skill "global:${skillName}" requires a Skill-tool-capable runtime (claude) — skipping on runtime "${runtime}"\n`);
1589
+ }
1590
+ continue;
1591
+ }
1592
+ // Non-namespaced bare name: attempt filesystem resolution as before.
1593
+ if (globalSkillsBase === null) {
1594
+ process.stderr.write(`[agent-skills] WARNING: Runtime "${runtime}" does not use a skills directory — "global:${skillName}" is not supported on this runtime\n`);
1595
+ continue;
1596
+ }
1597
+ const globalSkillDir = (0, runtime_homes_cjs_1.getGlobalSkillDir)(runtime, skillName);
1598
+ const globalSkillMd = node_path_1.default.join(globalSkillDir, 'SKILL.md');
1599
+ const displayPath = (0, runtime_homes_cjs_1.getGlobalSkillDisplayPath)(runtime, skillName);
1600
+ if (!node_fs_1.default.existsSync(globalSkillMd)) {
1601
+ process.stderr.write(`[agent-skills] WARNING: Global skill not found at "${displayPath}/SKILL.md" — skipping\n`);
1602
+ continue;
1603
+ }
1604
+ const pathCheck = (0, security_cjs_1.validatePath)(globalSkillMd, globalSkillsBase, { allowAbsolute: true });
1605
+ if (!pathCheck['safe']) {
1606
+ const acceptedViaTrustedRoot = trustedGlobalRoots.some((root) => {
1607
+ const rootCheck = (0, security_cjs_1.validatePath)(globalSkillMd, root, { allowAbsolute: true });
1608
+ return Boolean(rootCheck['safe']);
1609
+ });
1610
+ if (!acceptedViaTrustedRoot) {
1611
+ process.stderr.write(`[agent-skills] WARNING: Global skill "${skillName}" failed path check (symlink escape?) — skipping\n`);
1612
+ continue;
1613
+ }
1614
+ process.stderr.write(`[agent-skills] NOTE: Global skill "${skillName}" accepted via trusted_global_roots (resolves outside the default skills dir)\n`);
1615
+ }
1616
+ validEntries.push({ kind: 'include', ref: `${globalSkillDir}/SKILL.md`, display: displayPath });
1617
+ continue;
1618
+ }
1619
+ const pathCheck = (0, security_cjs_1.validatePath)(skillPath, projectRoot);
1620
+ if (!pathCheck['safe']) {
1621
+ process.stderr.write(`[agent-skills] WARNING: Skipping unsafe path "${skillPath}": ${pathCheck['error']}\n`);
1622
+ continue;
1623
+ }
1624
+ const skillMdPath = node_path_1.default.join(projectRoot, skillPath, 'SKILL.md');
1625
+ if (!node_fs_1.default.existsSync(skillMdPath)) {
1626
+ process.stderr.write(`[agent-skills] WARNING: Skill not found at "${skillPath}/SKILL.md" — skipping\n`);
1627
+ continue;
1628
+ }
1629
+ validEntries.push({ kind: 'include', ref: `${skillPath}/SKILL.md`, display: skillPath });
1630
+ }
1631
+ if (validEntries.length === 0)
1632
+ return '';
1633
+ const lines = validEntries.map((entry) => {
1634
+ if (entry.kind === 'directive') {
1635
+ return `- Load the \`${entry.name}\` skill via the Skill tool before proceeding (plugin-provided).`;
1636
+ }
1637
+ return `- @${entry.ref}`;
1638
+ }).join('\n');
1639
+ return `<agent_skills>\nRead these user-configured skills:\n${lines}\n</agent_skills>`;
1640
+ }
1641
+ function cmdAgentSkills(cwd, agentType, raw, jsonMode) {
1642
+ if (!agentType) {
1643
+ output('', raw, '');
1644
+ return;
1645
+ }
1646
+ const config = loadConfig(cwd);
1647
+ const block = buildAgentSkillsBlock(config, agentType, cwd);
1648
+ if (jsonMode) {
1649
+ const skillPaths = (config && config.agent_skills && config.agent_skills[agentType]) || [];
1650
+ const normalizedPaths = Array.isArray(skillPaths)
1651
+ ? skillPaths
1652
+ : skillPaths
1653
+ ? [skillPaths]
1654
+ : [];
1655
+ output({ agent_type: agentType, block: block || '', skills_count: normalizedPaths.length }, raw);
1656
+ return;
1657
+ }
1658
+ if (block) {
1659
+ process.stdout.write(block);
1660
+ }
1661
+ process.exit(0);
1662
+ }
1663
+ function buildSkillManifest(cwd, skillsDir = null) {
1664
+ const canonicalRoots = skillsDir
1665
+ ? [
1666
+ {
1667
+ root: node_path_1.default.resolve(skillsDir),
1668
+ path: node_path_1.default.resolve(skillsDir),
1669
+ scope: 'custom',
1670
+ present: node_fs_1.default.existsSync(skillsDir),
1671
+ kind: 'skills',
1672
+ },
1673
+ ]
1674
+ : [
1675
+ {
1676
+ root: '.claude/skills',
1677
+ path: node_path_1.default.join(cwd, '.claude', 'skills'),
1678
+ scope: 'project',
1679
+ kind: 'skills',
1680
+ },
1681
+ {
1682
+ root: '.agents/skills',
1683
+ path: node_path_1.default.join(cwd, '.agents', 'skills'),
1684
+ scope: 'project',
1685
+ kind: 'skills',
1686
+ },
1687
+ {
1688
+ root: '.cursor/skills',
1689
+ path: node_path_1.default.join(cwd, '.cursor', 'skills'),
1690
+ scope: 'project',
1691
+ kind: 'skills',
1692
+ },
1693
+ {
1694
+ root: '.github/skills',
1695
+ path: node_path_1.default.join(cwd, '.github', 'skills'),
1696
+ scope: 'project',
1697
+ kind: 'skills',
1698
+ },
1699
+ {
1700
+ root: '.codex/skills',
1701
+ path: node_path_1.default.join(cwd, '.codex', 'skills'),
1702
+ scope: 'project',
1703
+ kind: 'skills',
1704
+ },
1705
+ {
1706
+ root: '~/.claude/skills',
1707
+ path: (0, runtime_homes_cjs_1.getGlobalSkillsBase)('claude'),
1708
+ scope: 'global',
1709
+ kind: 'skills',
1710
+ },
1711
+ {
1712
+ root: '~/.codex/skills',
1713
+ path: (0, runtime_homes_cjs_1.getGlobalSkillsBase)('codex'),
1714
+ scope: 'global',
1715
+ kind: 'skills',
1716
+ },
1717
+ {
1718
+ root: '.claude/pp-core/skills',
1719
+ path: node_path_1.default.join(node_os_1.default.homedir(), '.claude', 'pp-core', 'skills'),
1720
+ scope: 'import-only',
1721
+ kind: 'skills',
1722
+ deprecated: true,
1723
+ },
1724
+ {
1725
+ root: '.claude/commands/gsd',
1726
+ path: node_path_1.default.join(node_os_1.default.homedir(), '.claude', 'commands', 'gsd'),
1727
+ scope: 'legacy-commands',
1728
+ kind: 'commands',
1729
+ deprecated: true,
1730
+ },
1731
+ ];
1732
+ const skills = [];
1733
+ const roots = [];
1734
+ let legacyClaudeCommandsInstalled = false;
1735
+ for (const rootInfo of canonicalRoots) {
1736
+ const rootPath = rootInfo.path;
1737
+ const rootSummary = {
1738
+ root: rootInfo.root,
1739
+ path: rootPath,
1740
+ scope: rootInfo.scope,
1741
+ present: node_fs_1.default.existsSync(rootPath),
1742
+ deprecated: !!rootInfo.deprecated,
1743
+ };
1744
+ if (!rootSummary.present) {
1745
+ roots.push(rootSummary);
1746
+ continue;
1747
+ }
1748
+ if (rootInfo.kind === 'commands') {
1749
+ let entries = [];
1750
+ try {
1751
+ entries = node_fs_1.default.readdirSync(rootPath, { withFileTypes: true });
1752
+ }
1753
+ catch {
1754
+ roots.push(rootSummary);
1755
+ continue;
1756
+ }
1757
+ const commandFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith('.md'));
1758
+ rootSummary.command_count = commandFiles.length;
1759
+ if (rootSummary.command_count > 0)
1760
+ legacyClaudeCommandsInstalled = true;
1761
+ roots.push(rootSummary);
1762
+ continue;
1763
+ }
1764
+ let entries;
1765
+ try {
1766
+ entries = node_fs_1.default.readdirSync(rootPath, { withFileTypes: true });
1767
+ }
1768
+ catch {
1769
+ roots.push(rootSummary);
1770
+ continue;
1771
+ }
1772
+ // Track skill names seen within this root to deduplicate dual-routed concretes
1773
+ // (e.g. spec-phase nested under both gsd-ns-workflow and gsd-ns-manage).
1774
+ const seenNamesInRoot = new Set();
1775
+ function pushSkillEntry(
1776
+ // relPath must use forward slashes on all platforms (manifest paths are
1777
+ // posix-style for cross-platform stability; flat entries use template
1778
+ // literals that always produce '/'; nested entries are joined below
1779
+ // with explicit '/' separators rather than path.join).
1780
+ relPath, content) {
1781
+ const frontmatter = extractFrontmatter(content);
1782
+ const dirPart = relPath.replace(/\/SKILL\.md$/, '');
1783
+ const stem = dirPart.includes('/') ? dirPart.split('/').pop() : dirPart;
1784
+ const name = frontmatter['name'] || stem;
1785
+ if (seenNamesInRoot.has(name))
1786
+ return false; // dedupe dual-routed concretes
1787
+ seenNamesInRoot.add(name);
1788
+ const description = frontmatter['description'] || '';
1789
+ const triggers = [];
1790
+ const bodyMatch = content.match(/^---[\s\S]*?---\s*\n([\s\S]*)$/);
1791
+ if (bodyMatch) {
1792
+ const body = bodyMatch[1];
1793
+ const triggerLines = body.match(/^TRIGGER\s+when:\s*(.+)$/gmi);
1794
+ if (triggerLines) {
1795
+ for (const line of triggerLines) {
1796
+ const m = line.match(/^TRIGGER\s+when:\s*(.+)$/i);
1797
+ if (m)
1798
+ triggers.push(m[1].trim());
1799
+ }
1800
+ }
1801
+ }
1802
+ skills.push({
1803
+ name,
1804
+ description,
1805
+ triggers,
1806
+ path: dirPart,
1807
+ file_path: relPath,
1808
+ root: rootInfo.root,
1809
+ scope: rootInfo.scope,
1810
+ installed: rootInfo.scope !== 'import-only',
1811
+ deprecated: !!rootInfo.deprecated,
1812
+ });
1813
+ return true;
1814
+ }
1815
+ let skillCount = 0;
1816
+ for (const entry of entries) {
1817
+ if (!entry.isDirectory())
1818
+ continue;
1819
+ const skillMdPath = node_path_1.default.join(rootPath, entry.name, 'SKILL.md');
1820
+ const content = (0, shell_command_projection_cjs_1.platformReadSync)(skillMdPath);
1821
+ if (content !== null) {
1822
+ if (pushSkillEntry(`${entry.name}/SKILL.md`, content))
1823
+ skillCount++;
1824
+ }
1825
+ // Nested layout: <entry>/skills/<stem>/SKILL.md
1826
+ // Used by cline, qwen, hermes, augment, trae, antigravity (#69 nested=true).
1827
+ // Descend exactly one level into <entry>/skills/ — no deeper recursion.
1828
+ // Scope to gsd-ns-* routers only: never vacuum up an unrelated user skill
1829
+ // that happens to have its own `skills/` subdirectory.
1830
+ if (!entry.name.startsWith('gsd-ns-'))
1831
+ continue;
1832
+ const nestedSkillsDir = node_path_1.default.join(rootPath, entry.name, 'skills');
1833
+ let nestedEntries = [];
1834
+ try {
1835
+ nestedEntries = node_fs_1.default.readdirSync(nestedSkillsDir, { withFileTypes: true });
1836
+ }
1837
+ catch {
1838
+ // No skills/ subdir — flat layout or unreadable; nothing to do.
1839
+ nestedEntries = [];
1840
+ }
1841
+ for (const nested of nestedEntries) {
1842
+ if (!nested.isDirectory())
1843
+ continue;
1844
+ const nestedSkillMd = node_path_1.default.join(nestedSkillsDir, nested.name, 'SKILL.md');
1845
+ const nestedContent = (0, shell_command_projection_cjs_1.platformReadSync)(nestedSkillMd);
1846
+ if (nestedContent === null)
1847
+ continue;
1848
+ // Use forward-slash separator explicitly so manifest paths are posix-style
1849
+ // on all platforms, matching the flat-layout behaviour above.
1850
+ const relPath = `${entry.name}/skills/${nested.name}/SKILL.md`;
1851
+ if (pushSkillEntry(relPath, nestedContent))
1852
+ skillCount++;
1853
+ }
1854
+ }
1855
+ rootSummary.skill_count = skillCount;
1856
+ roots.push(rootSummary);
1857
+ }
1858
+ skills.sort((a, b) => {
1859
+ const rootCmp = a.root.localeCompare(b.root);
1860
+ return rootCmp !== 0 ? rootCmp : a.name.localeCompare(b.name);
1861
+ });
1862
+ const gsdSkillsInstalled = skills.some((skill) => skill.name.startsWith('gsd-'));
1863
+ return {
1864
+ skills,
1865
+ roots,
1866
+ installation: {
1867
+ gsd_skills_installed: gsdSkillsInstalled,
1868
+ legacy_claude_commands_installed: legacyClaudeCommandsInstalled,
1869
+ },
1870
+ counts: {
1871
+ skills: skills.length,
1872
+ roots: roots.length,
1873
+ },
1874
+ };
1875
+ }
1876
+ function cmdSkillManifest(cwd, args, raw) {
1877
+ const skillsDirIdx = args.indexOf('--skills-dir');
1878
+ const skillsDir = skillsDirIdx >= 0 && args[skillsDirIdx + 1] ? args[skillsDirIdx + 1] : null;
1879
+ const manifest = buildSkillManifest(cwd, skillsDir);
1880
+ if (args.includes('--write')) {
1881
+ const planDir = node_path_1.default.join(cwd, '.planning');
1882
+ if (node_fs_1.default.existsSync(planDir)) {
1883
+ const manifestPath = node_path_1.default.join(planDir, 'skill-manifest.json');
1884
+ (0, shell_command_projection_cjs_1.platformWriteSync)(manifestPath, JSON.stringify(manifest, null, 2));
1885
+ }
1886
+ }
1887
+ output(manifest, raw);
1888
+ }
1889
+ module.exports = {
1890
+ cmdInitExecutePhase,
1891
+ cmdInitPlanPhase,
1892
+ cmdInitNewProject,
1893
+ cmdInitNewMilestone,
1894
+ cmdInitQuick,
1895
+ cmdInitIngestDocs,
1896
+ cmdInitResume,
1897
+ cmdInitVerifyWork,
1898
+ cmdInitPhaseOp,
1899
+ cmdInitTodos,
1900
+ cmdInitMilestoneOp,
1901
+ cmdInitMapCodebase,
1902
+ cmdInitProgress,
1903
+ cmdInitManager,
1904
+ cmdInitNewWorkspace,
1905
+ cmdInitListWorkspaces,
1906
+ cmdInitRemoveWorkspace,
1907
+ detectChildRepos,
1908
+ buildAgentSkillsBlock,
1909
+ cmdAgentSkills,
1910
+ buildSkillManifest,
1911
+ cmdSkillManifest,
1912
+ };