@pareto4/pp-core 1.11.0 → 1.12.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 (256) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +31 -0
  3. package/agents/pp-code-fixer.md +1 -1
  4. package/agents/pp-debug-session-manager.md +1 -1
  5. package/agents/pp-debugger.md +1 -1
  6. package/agents/pp-executor.md +20 -10
  7. package/agents/pp-intel-updater.md +1 -1
  8. package/agents/pp-phase-researcher.md +1 -1
  9. package/agents/pp-plan-checker.md +5 -5
  10. package/agents/pp-planner.md +10 -24
  11. package/agents/pp-project-researcher.md +1 -1
  12. package/agents/pp-research-synthesizer.md +1 -1
  13. package/agents/pp-ui-researcher.md +1 -1
  14. package/agents/pp-verifier.md +8 -4
  15. package/bin/install.js +429 -82
  16. package/commands/pp-autonomous.md +8 -6
  17. package/commands/pp-discovery-to-build.md +1 -1
  18. package/commands/pp-discovery.md +3 -2
  19. package/commands/pp-discuss-phase.md +2 -2
  20. package/commands/pp-execute-phase.md +3 -1
  21. package/commands/pp-graphify.md +5 -5
  22. package/commands/pp-import.md +1 -1
  23. package/commands/pp-map-codebase.md +1 -1
  24. package/commands/pp-ns-project.md +2 -3
  25. package/commands/pp-ns-workflow.md +1 -1
  26. package/commands/pp-plan-phase.md +2 -1
  27. package/commands/pp-verify-work.md +2 -1
  28. package/gemini-extension.json +1 -1
  29. package/hooks/dist/pp-cursor-session-start.js +2 -2
  30. package/hooks/dist/pp-session-state.sh +1 -1
  31. package/hooks/dist/pp-statusline.js +21 -2
  32. package/hooks/pp-cursor-session-start.js +2 -2
  33. package/hooks/pp-session-state.sh +1 -1
  34. package/hooks/pp-statusline.js +21 -2
  35. package/package.json +18 -5
  36. package/pareto-skills/pp-project-setup/SKILL.md +5 -63
  37. package/pp-core/bin/detect-build-commands.sh +182 -0
  38. package/pp-core/bin/lib/capability-registry.cjs +65 -7
  39. package/pp-core/bin/lib/check-command-router.cjs +79 -38
  40. package/pp-core/bin/lib/clusters.cjs +2 -3
  41. package/pp-core/bin/lib/command-aliases.cjs +36 -0
  42. package/pp-core/bin/lib/command-arg-projection.cjs +99 -1
  43. package/pp-core/bin/lib/commands.cjs +21 -6
  44. package/pp-core/bin/lib/compose-bindings.cjs +128 -15
  45. package/pp-core/bin/lib/config-schema.cjs +16 -0
  46. package/pp-core/bin/lib/config.cjs +142 -27
  47. package/pp-core/bin/lib/core-utils.cjs +43 -7
  48. package/pp-core/bin/lib/discovery-input-check.cjs +218 -0
  49. package/pp-core/bin/lib/discovery-rebaseline.cjs +1372 -0
  50. package/pp-core/bin/lib/discovery-research-track.cjs +979 -0
  51. package/pp-core/bin/lib/discovery-rollup.cjs +476 -0
  52. package/pp-core/bin/lib/discovery-screens.cjs +1995 -0
  53. package/pp-core/bin/lib/discovery-sweep.cjs +390 -0
  54. package/pp-core/bin/lib/discovery-table.cjs +98 -0
  55. package/pp-core/bin/lib/dispatch-plan.cjs +472 -0
  56. package/pp-core/bin/lib/frontmatter.cjs +17 -2
  57. package/pp-core/bin/lib/init-command-router.cjs +8 -5
  58. package/pp-core/bin/lib/init-contract.cjs +213 -33
  59. package/pp-core/bin/lib/init-interactive.cjs +20 -1
  60. package/pp-core/bin/lib/init.cjs +124 -134
  61. package/pp-core/bin/lib/install-profiles.cjs +5 -0
  62. package/pp-core/bin/lib/install-run.cjs +536 -0
  63. package/pp-core/bin/lib/io.cjs +15 -0
  64. package/pp-core/bin/lib/loop-resolver.cjs +24 -0
  65. package/pp-core/bin/lib/milestone.cjs +345 -48
  66. package/pp-core/bin/lib/phase-command-router.cjs +23 -1
  67. package/pp-core/bin/lib/phase-locator.cjs +15 -6
  68. package/pp-core/bin/lib/phase-scope.cjs +91 -0
  69. package/pp-core/bin/lib/phase-tier.cjs +467 -0
  70. package/pp-core/bin/lib/phase.cjs +247 -21
  71. package/pp-core/bin/lib/phases-command-router.cjs +2 -0
  72. package/pp-core/bin/lib/plan-graph.cjs +368 -0
  73. package/pp-core/bin/lib/planning-workspace.cjs +53 -0
  74. package/pp-core/bin/lib/platform-bindings.cjs +32 -2
  75. package/pp-core/bin/lib/profile-output.cjs +11 -3
  76. package/pp-core/bin/lib/prohibition-enforcement.cjs +112 -0
  77. package/pp-core/bin/lib/roadmap-parser.cjs +366 -99
  78. package/pp-core/bin/lib/roadmap.cjs +141 -39
  79. package/pp-core/bin/lib/runtime-hooks-surface.cjs +1 -1
  80. package/pp-core/bin/lib/shell-command-projection.cjs +24 -0
  81. package/pp-core/bin/lib/state-command-router.cjs +82 -13
  82. package/pp-core/bin/lib/state-document.cjs +124 -7
  83. package/pp-core/bin/lib/state.cjs +784 -257
  84. package/pp-core/bin/lib/template.cjs +22 -6
  85. package/pp-core/bin/lib/vault-command-router.cjs +57 -6
  86. package/pp-core/bin/lib/vault-practices.cjs +6 -36
  87. package/pp-core/bin/lib/vault-write-back.cjs +20 -4
  88. package/pp-core/bin/lib/vault.cjs +555 -23
  89. package/pp-core/bin/lib/verify.cjs +53 -31
  90. package/pp-core/bin/lib/workstream.cjs +1 -1
  91. package/pp-core/bin/lib/worktree-safety.cjs +26 -15
  92. package/pp-core/bin/portable-timeout.sh +89 -0
  93. package/pp-core/bin/pp-tools.cjs +158 -8
  94. package/pp-core/bin/shared/config-defaults.manifest.json +7 -0
  95. package/pp-core/bin/shared/config-schema.manifest.json +3 -0
  96. package/pp-core/references/autonomous-best-effort.md +461 -0
  97. package/pp-core/references/autonomous-lifecycle.md +146 -0
  98. package/pp-core/references/autonomous-smart-discuss.md +16 -12
  99. package/pp-core/references/checkpoints.md +1 -1
  100. package/pp-core/references/discovery-id-conventions.md +202 -0
  101. package/pp-core/references/discovery-rebaseline.md +254 -0
  102. package/pp-core/references/discovery-research-track.md +283 -0
  103. package/pp-core/references/discovery-traceability.md +400 -0
  104. package/pp-core/references/loop-hook-dispatch.md +7 -0
  105. package/pp-core/references/mandatory-initial-read.md +1 -1
  106. package/pp-core/references/planner-gap-closure.md +1 -0
  107. package/pp-core/references/planner-human-verify-mode.md +1 -1
  108. package/pp-core/references/planner-load-graph-context.md +1 -1
  109. package/pp-core/references/planner-plan-graph.md +66 -0
  110. package/pp-core/references/planner-tdd.md +1 -0
  111. package/pp-core/references/planning-config.md +8 -0
  112. package/pp-core/references/sketch-tooling.md +4 -0
  113. package/pp-core/references/tdd.md +1 -0
  114. package/pp-core/templates/claude-md.md +1 -1
  115. package/pp-core/templates/phase-prompt.md +6 -2
  116. package/pp-core/templates/prototype-review/review.js +303 -0
  117. package/pp-core/templates/state.md +6 -0
  118. package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -1
  119. package/pp-core/workflows/add-backlog.md +1 -1
  120. package/pp-core/workflows/add-phase.md +1 -1
  121. package/pp-core/workflows/add-tests.md +1 -1
  122. package/pp-core/workflows/add-todo.md +1 -1
  123. package/pp-core/workflows/ai-integration-phase.md +2 -2
  124. package/pp-core/workflows/audit-fix.md +12 -16
  125. package/pp-core/workflows/audit-milestone.md +1 -1
  126. package/pp-core/workflows/audit-uat.md +1 -1
  127. package/pp-core/workflows/autonomous.md +68 -306
  128. package/pp-core/workflows/check-todos.md +1 -1
  129. package/pp-core/workflows/cleanup.md +1 -1
  130. package/pp-core/workflows/code-review-fix.md +1 -1
  131. package/pp-core/workflows/code-review.md +19 -7
  132. package/pp-core/workflows/complete-milestone.md +41 -60
  133. package/pp-core/workflows/debug.md +1 -1
  134. package/pp-core/workflows/diagnose-issues.md +1 -1
  135. package/pp-core/workflows/discovery-sprint/gap-analysis.md +140 -0
  136. package/pp-core/workflows/discovery-sprint/input-package.md +140 -0
  137. package/pp-core/workflows/discovery-sprint/prefill.md +92 -0
  138. package/pp-core/workflows/discovery-sprint/prototype.md +257 -0
  139. package/pp-core/workflows/discovery-sprint/rebaseline.md +276 -0
  140. package/pp-core/workflows/discovery-sprint/session-13-gates.md +24 -0
  141. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +92 -0
  142. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +98 -0
  143. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +215 -0
  144. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +146 -0
  145. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +98 -0
  146. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +107 -0
  147. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +98 -0
  148. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +85 -0
  149. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +109 -0
  150. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +119 -0
  151. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +92 -0
  152. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +98 -0
  153. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +205 -0
  154. package/pp-core/workflows/discovery-sprint/templates/context-template.md +266 -0
  155. package/pp-core/workflows/discovery-sprint.md +79 -1714
  156. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +5 -5
  157. package/pp-core/workflows/discovery-to-build/steps/screen-inventory-gate.md +70 -0
  158. package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +54 -0
  159. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +184 -0
  160. package/pp-core/workflows/discovery-to-build.md +21 -238
  161. package/pp-core/workflows/discuss-phase/modes/advisor.md +1 -1
  162. package/pp-core/workflows/discuss-phase/modes/auto.md +1 -1
  163. package/pp-core/workflows/discuss-phase/modes/chain.md +1 -1
  164. package/pp-core/workflows/discuss-phase-assumptions.md +1 -1
  165. package/pp-core/workflows/discuss-phase.md +9 -2
  166. package/pp-core/workflows/do.md +4 -4
  167. package/pp-core/workflows/docs-update.md +1 -1
  168. package/pp-core/workflows/edit-phase.md +1 -1
  169. package/pp-core/workflows/eval-review.md +1 -1
  170. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +45 -0
  171. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +1 -1
  172. package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  173. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +90 -0
  174. package/pp-core/workflows/execute-phase/steps/install-run.md +29 -0
  175. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +23 -0
  176. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +76 -77
  177. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +72 -0
  178. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +5 -5
  179. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +19 -112
  180. package/pp-core/workflows/execute-phase.md +102 -216
  181. package/pp-core/workflows/execute-plan.md +6 -2
  182. package/pp-core/workflows/explore.md +1 -1
  183. package/pp-core/workflows/extract-learnings.md +1 -1
  184. package/pp-core/workflows/forensics.md +4 -4
  185. package/pp-core/workflows/graduation.md +1 -1
  186. package/pp-core/workflows/health.md +1 -1
  187. package/pp-core/workflows/help/modes/brief.md +1 -0
  188. package/pp-core/workflows/help/modes/default.md +2 -1
  189. package/pp-core/workflows/help/modes/full.md +12 -5
  190. package/pp-core/workflows/import.md +1 -1
  191. package/pp-core/workflows/ingest-docs.md +1 -1
  192. package/pp-core/workflows/init.md +18 -2
  193. package/pp-core/workflows/insert-phase.md +1 -1
  194. package/pp-core/workflows/list-workspaces.md +1 -1
  195. package/pp-core/workflows/manager.md +3 -3
  196. package/pp-core/workflows/map-codebase.md +2 -2
  197. package/pp-core/workflows/milestone-summary.md +4 -4
  198. package/pp-core/workflows/mvp-phase.md +1 -1
  199. package/pp-core/workflows/new-milestone.md +7 -8
  200. package/pp-core/workflows/new-project.md +6 -1
  201. package/pp-core/workflows/new-workspace.md +1 -1
  202. package/pp-core/workflows/next.md +2 -2
  203. package/pp-core/workflows/note.md +2 -2
  204. package/pp-core/workflows/pause-work.md +1 -1
  205. package/pp-core/workflows/plan-milestone-gaps.md +1 -1
  206. package/pp-core/workflows/plan-phase/steps/chunked-planning.md +111 -0
  207. package/pp-core/workflows/plan-phase/steps/phase-tier.md +34 -0
  208. package/pp-core/workflows/plan-phase.md +43 -167
  209. package/pp-core/workflows/plan-review-convergence.md +2 -2
  210. package/pp-core/workflows/plant-seed.md +1 -1
  211. package/pp-core/workflows/pr-branch.md +1 -1
  212. package/pp-core/workflows/profile-user.md +1 -1
  213. package/pp-core/workflows/progress.md +3 -3
  214. package/pp-core/workflows/quick.md +3 -1
  215. package/pp-core/workflows/remove-phase.md +1 -1
  216. package/pp-core/workflows/remove-workspace.md +1 -1
  217. package/pp-core/workflows/resume-project.md +6 -12
  218. package/pp-core/workflows/review.md +1 -1
  219. package/pp-core/workflows/scan.md +1 -1
  220. package/pp-core/workflows/secure-phase.md +1 -1
  221. package/pp-core/workflows/settings-advanced.md +1 -1
  222. package/pp-core/workflows/settings-integrations.md +1 -1
  223. package/pp-core/workflows/settings.md +1 -1
  224. package/pp-core/workflows/ship.md +25 -8
  225. package/pp-core/workflows/sketch-wrap-up.md +1 -1
  226. package/pp-core/workflows/sketch.md +2 -1
  227. package/pp-core/workflows/spec-phase.md +1 -1
  228. package/pp-core/workflows/spike-wrap-up.md +1 -1
  229. package/pp-core/workflows/spike.md +1 -1
  230. package/pp-core/workflows/stats.md +2 -2
  231. package/pp-core/workflows/thread.md +1 -1
  232. package/pp-core/workflows/transition.md +5 -9
  233. package/pp-core/workflows/ui-phase.md +23 -2
  234. package/pp-core/workflows/ui-review.md +1 -1
  235. package/pp-core/workflows/ultraplan-phase.md +2 -2
  236. package/pp-core/workflows/validate-phase.md +1 -1
  237. package/pp-core/workflows/vault-write-back.md +1 -1
  238. package/pp-core/workflows/verify-phase.md +29 -26
  239. package/pp-core/workflows/verify-work.md +14 -10
  240. package/scripts/base64-scan.sh +7 -41
  241. package/scripts/changeset/cli.cjs +25 -5
  242. package/scripts/dev-env.cjs +462 -0
  243. package/scripts/lib/allowlist-ratchet.cjs +103 -1
  244. package/scripts/lib/size-budget.cjs +311 -0
  245. package/scripts/lint-neutrality-names.cjs +694 -0
  246. package/scripts/lint-org-coordinates.allowlist.json +35 -0
  247. package/scripts/lint-org-coordinates.cjs +426 -0
  248. package/scripts/lint-skill-deps.cjs +48 -9
  249. package/scripts/lint-test-file-count.cjs +17 -2
  250. package/scripts/prompt-injection-scan.sh +41 -20
  251. package/scripts/sync-manifest-versions.cjs +41 -7
  252. package/scripts/sync-runtime-launcher.cjs +106 -1
  253. package/scripts/update-size-baseline.cjs +274 -30
  254. package/scripts/workflow-size.cjs +61 -9
  255. package/commands/pp-build-handoff.md +0 -46
  256. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +0 -94
@@ -11,7 +11,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
11
11
  };
12
12
  const node_fs_1 = __importDefault(require("node:fs"));
13
13
  const node_path_1 = __importDefault(require("node:path"));
14
- const clock_cjs_1 = require("./clock.cjs");
15
14
  // eslint-disable-next-line @typescript-eslint/no-require-imports
16
15
  const ioMod = require("./io.cjs");
17
16
  const { output, error } = ioMod;
@@ -19,11 +18,11 @@ const { output, error } = ioMod;
19
18
  const phaseIdMod = require("./phase-id.cjs");
20
19
  const { escapeRegex, normalizePhaseName, phaseMarkdownRegexSource, phaseMarkdownRegexSourceExact, phaseTokenMatches } = phaseIdMod;
21
20
  // eslint-disable-next-line @typescript-eslint/no-require-imports
22
- const phaseLocatorMod = require("./phase-locator.cjs");
23
- const { findPhaseInternal } = phaseLocatorMod;
21
+ const phaseScopeMod = require("./phase-scope.cjs");
22
+ const { resolvePhaseForRead } = phaseScopeMod;
24
23
  // eslint-disable-next-line @typescript-eslint/no-require-imports
25
24
  const roadmapParserModule = require("./roadmap-parser.cjs");
26
- const { stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone } = roadmapParserModule;
25
+ const { stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, buildPlansSummaryLinePattern, PHASE_SECTION_SCAN } = roadmapParserModule;
27
26
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
28
27
  // eslint-disable-next-line @typescript-eslint/no-require-imports
29
28
  const planningWorkspace = require("./planning-workspace.cjs");
@@ -375,12 +374,69 @@ function cmdRoadmapAnalyze(cwd, raw) {
375
374
  output(result, raw, undefined);
376
375
  }
377
376
  // ─── cmdRoadmapUpdatePlanProgress ─────────────────────────────────────────────
377
+ /**
378
+ * The status a phase carries between its last executed wave and the
379
+ * `execute:post` governance gate (D-08-15).
380
+ *
381
+ * Every plan has a SUMMARY, and nothing has yet recorded the phase as governed.
382
+ * It is deliberately a third literal, distinct from both neighbours:
383
+ * - `In Progress` — plans remain unexecuted;
384
+ * - `Built` — all plans executed, governance not yet granted;
385
+ * - `Complete` — written ONLY by `phase.complete`, after the execute:post
386
+ * gate returned without halting (D-08-13).
387
+ *
388
+ * A reader (or an auditor) can therefore tell "the code exists" from "the code
389
+ * was allowed to ship" by looking at one cell.
390
+ */
391
+ const ROADMAP_BUILT_STATUS = 'Built';
392
+ /**
393
+ * Refresh a phase's ROADMAP.md plan rows, executed count and status from disk.
394
+ *
395
+ * D-08-12: this handler can NOT write phase-level completion. It never ticks the
396
+ * phase checkbox, never writes a `(completed <date>)` stamp, never writes the
397
+ * `Complete` status cell and never writes `plans complete`. Those four surfaces
398
+ * belong to `phase.complete` (`src/phase.cts` `cmdPhaseComplete`), which the
399
+ * execute-phase workflow reaches only AFTER the `execute:post` gate dispatch
400
+ * returned without halting (D-08-13).
401
+ *
402
+ * Why it was narrowed — VA-R2-F2, measured: completion used to be INFERRED here
403
+ * from a file count (`summaryCount >= planCount`), and both call sites run per
404
+ * wave, several waves before governance runs. The round-2 fixture caught the
405
+ * consequence: ROADMAP.md read `Complete 2/2` for a phase whose blocking
406
+ * `vault.trace` gate had halted, while STATE.md still read `status: executing`.
407
+ * A file count is evidence that plans were built; it is not evidence that the
408
+ * phase was governed, and this verb is no longer allowed to confuse the two.
409
+ *
410
+ * D-08-32 — AND IT MAY NOT UNWRITE ONE EITHER. D-08-12 closed the forward
411
+ * direction and left the reverse open: on the live UAT fixture, phase 1 read
412
+ * `| 2/2 | Complete | 2026-09-16 |` after a governed completion, and a single
413
+ * `roadmap update-plan-progress 1` demoted it to `Built` and erased the date.
414
+ * A completion date is the one stored fact that distinguishes "the code exists"
415
+ * from "the code was allowed to ship", and a counter that can silently delete it
416
+ * makes the record unusable as a record.
417
+ *
418
+ * So: a phase is GOVERNED when its progress row's Status cell reads `Complete`
419
+ * (case-insensitive, trimmed) OR its Plans summary line already says
420
+ * `plans complete`. Both are read from the UNMODIFIED content before any write.
421
+ * On a governed phase this verb refreshes the executed-count cell and nothing
422
+ * else — Status, Completed and the summary line are left exactly as
423
+ * `phase.complete` wrote them. Un-governing a phase (a plan added after
424
+ * completion) is a `phase`-family action, not this counter's.
425
+ *
426
+ * And on EVERY path, governed or not, the Completed cell is never assigned. It
427
+ * is `phase.complete`'s cell (D-08-12); a `-` placeholder is the template's own
428
+ * not-complete marker, and blanking it is still a write to a cell this verb does
429
+ * not own.
430
+ *
431
+ * `status` and `built` in the JSON output stay count-derived, so this verb still
432
+ * never REPORTS `Complete`; `governed` says whether it preserved a row.
433
+ */
378
434
  function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
379
435
  if (!phaseNum) {
380
436
  error('phase number required for roadmap update-plan-progress');
381
437
  }
382
438
  const roadmapPath = planningPaths(cwd).roadmap;
383
- const phaseInfo = findPhaseInternal(cwd, phaseNum);
439
+ const phaseInfo = resolvePhaseForRead(cwd, phaseNum);
384
440
  if (!phaseInfo) {
385
441
  error(`Phase ${phaseNum} not found`);
386
442
  }
@@ -390,59 +446,91 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
390
446
  output({ updated: false, reason: 'No plans found', plan_count: 0, summary_count: 0 }, raw, 'no plans');
391
447
  return;
392
448
  }
393
- const isComplete = summaryCount >= planCount;
394
- const status = isComplete ? 'Complete' : summaryCount > 0 ? 'In Progress' : 'Planned';
395
- const today = clock_cjs_1.realClock.today();
449
+ const built = summaryCount >= planCount;
450
+ const status = built ? ROADMAP_BUILT_STATUS : summaryCount > 0 ? 'In Progress' : 'Planned';
396
451
  if (!node_fs_1.default.existsSync(roadmapPath)) {
397
452
  output({ updated: false, reason: 'ROADMAP.md not found', plan_count: planCount, summary_count: summaryCount }, raw, 'no roadmap');
398
453
  return;
399
454
  }
455
+ // Hoisted out of the locked section so the JSON output can report it
456
+ // (D-08-32); assigned exactly once, inside the lock, from the content read
457
+ // there.
458
+ let governedPhase = false;
400
459
  // Wrap entire read-modify-write in lock to prevent concurrent corruption
401
460
  withPlanningLock(cwd, () => {
402
461
  let roadmapContent = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
403
462
  const phasePattern = phaseMarkdownRegexSource(phaseNum);
404
463
  // Progress table row: update Plans/Status/Date columns (handles 4 or 5 column tables)
405
464
  const tableRowPattern = new RegExp(`^(\\|\\s*${phasePattern}\\.?\\s[^|]*(?:\\|[^\\n]*))$`, 'im');
465
+ // Update plan count in phase detail section. The three recognised spellings,
466
+ // the phase-number boundary, the phase-section bound and the same-line tail
467
+ // all live in `buildPlansSummaryLinePattern` (src/roadmap-parser.cjs, D-08-33)
468
+ // — the same builder `cmdPhaseComplete` uses. Two writers, one reading of
469
+ // what "this phase's Plans line" means.
470
+ const planCountPattern = buildPlansSummaryLinePattern(phaseNum);
471
+ // ── D-08-32: is this phase already GOVERNED? ─────────────────────────────
472
+ // BOTH reads run against the UNMODIFIED content, before any write, so the
473
+ // answer cannot be contaminated by this call's own edits.
474
+ const governedByRow = (() => {
475
+ const rowMatch = roadmapContent.match(tableRowPattern);
476
+ if (!rowMatch)
477
+ return false;
478
+ const cells = rowMatch[0].split('|').slice(1, -1);
479
+ // Same column arithmetic as the write below: Status is index 3 on a
480
+ // 5-column table and index 2 on a 4-column one.
481
+ const statusCell = cells.length === 5 ? cells[3] : cells.length === 4 ? cells[2] : undefined;
482
+ return statusCell !== undefined && statusCell.trim().toLowerCase() === 'complete';
483
+ })();
484
+ const governedByLine = (() => {
485
+ // Scoped exactly as the WRITE is (`replaceInCurrentMilestone`): a
486
+ // `plans complete` line inside an archived <details> milestone says
487
+ // nothing about the phase in the active one.
488
+ const lastClose = roadmapContent.lastIndexOf('</details>');
489
+ const scoped = lastClose === -1 ? roadmapContent : roadmapContent.slice(lastClose + '</details>'.length);
490
+ const lineMatch = scoped.match(planCountPattern);
491
+ if (!lineMatch)
492
+ return false;
493
+ const value = lineMatch[0].slice(lineMatch[1].length);
494
+ return /plans\s+complete/i.test(value);
495
+ })();
496
+ const governed = governedByRow || governedByLine;
497
+ governedPhase = governed;
406
498
  roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
407
499
  const cells = fullRow.split('|').slice(1, -1); // drop leading/trailing empty from split
408
- const dateShape = /^\d{4}-\d{2}-\d{2}$/;
500
+ // The Completed cell is `phase.complete`'s to fill (D-08-12) and is NEVER
501
+ // assigned here, on any branch (D-08-32) — not even to blank it. A `-`
502
+ // placeholder is the template's own not-complete marker and survives.
503
+ // The Status cell is written only when the phase is NOT governed; on a
504
+ // governed row only the executed count is refreshed.
505
+ // Both padded cells carry a TRAILING space as well as a leading one: the
506
+ // cells are re-joined on a bare `|`, so ` ${status.padEnd(11)}` rendered
507
+ // an 11-character status as `| In Progress|` — a malformed table cell
508
+ // observed in three consecutive live runs of this verb.
409
509
  if (cells.length === 5) {
410
510
  // 5-col: Phase | Milestone | Plans | Status | Completed
411
511
  cells[2] = ` ${summaryCount}/${planCount} `;
412
- cells[3] = ` ${status.padEnd(11)}`;
413
- // Preserve only a valid ISO date (#1161: idempotent; self-heal garbage)
414
- const existingDate5 = cells[4].trim();
415
- cells[4] = isComplete
416
- ? (dateShape.test(existingDate5) ? cells[4] : ` ${today} `)
417
- : ' ';
512
+ if (!governed)
513
+ cells[3] = ` ${status.padEnd(11)} `;
418
514
  }
419
515
  else if (cells.length === 4) {
420
516
  // 4-col: Phase | Plans | Status | Completed
421
517
  cells[1] = ` ${summaryCount}/${planCount} `;
422
- cells[2] = ` ${status.padEnd(11)}`;
423
- // Preserve only a valid ISO date (#1161: idempotent; self-heal garbage)
424
- const existingDate4 = cells[3].trim();
425
- cells[3] = isComplete
426
- ? (dateShape.test(existingDate4) ? cells[3] : ` ${today} `)
427
- : ' ';
518
+ if (!governed)
519
+ cells[2] = ` ${status.padEnd(11)} `;
428
520
  }
429
521
  return '|' + cells.join('|') + '|';
430
522
  });
431
- // Update plan count in phase detail section.
432
- // Three recognised forms (all tolerated; canonical template uses the first):
433
- // `**Plans**: N plans` — bold word + outer colon (gsd-core/templates/roadmap.md)
434
- // `**Plans:** N plans` — bold "Plans:" (colon inside bold)
435
- // `Plans: N plans` — plain text header
436
- const planCountPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phasePattern}(?=[:\\s])[\\s\\S]*?(?:\\*\\*Plans\\*\\*:|\\*\\*Plans:\\*\\*|(?:^|\\n)Plans:)\\s*)[^\\n]+`, 'i');
437
- const planCountText = isComplete
438
- ? `${summaryCount}/${planCount} plans complete`
439
- : `${summaryCount}/${planCount} plans executed`;
440
- roadmapContent = replaceInCurrentMilestone(roadmapContent, planCountPattern, `$1${planCountText}`);
441
- // If complete: check checkbox
442
- if (isComplete) {
443
- const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phasePattern}[:\\s][^\\n]*)`, 'i');
444
- roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
523
+ // Always "executed", never "complete" (D-08-12): this is a count of the
524
+ // SUMMARY files on disk, not a statement that the phase was governed.
525
+ // And on a GOVERNED phase the line is not rewritten at all (D-08-32) —
526
+ // demoting `2/2 plans complete` to `2/2 plans executed` would delete the
527
+ // very distinction D-08-12 introduced the two wordings to preserve.
528
+ if (!governed) {
529
+ const planCountText = `${summaryCount}/${planCount} plans executed`;
530
+ roadmapContent = replaceInCurrentMilestone(roadmapContent, planCountPattern, `$1${planCountText}`);
445
531
  }
532
+ // The phase checkbox is NOT ticked here under any count (D-08-12).
533
+ // `phase.complete` ticks it, after the execute:post gate (D-08-13).
446
534
  // Mark completed plan checkboxes (e.g. "- [ ] 50-01-PLAN.md", "- [ ] 50-01:", or "- [ ] **50-01**")
447
535
  for (const summaryFile of phaseInfo.summaries) {
448
536
  const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
@@ -490,8 +578,17 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
490
578
  //
491
579
  // Pattern A: anchor to bare `Plans:` header (preferred).
492
580
  // Pattern B: fallback to bold summary when no bare header exists.
493
- const insertRowsPatternA = new RegExp(`(#{2,4}\\s*Phase\\s+${phasePattern}(?=[:\\s])[\\s\\S]*?(?:^|\\n)(?:Plans:)[^\\n]*)`, 'i');
494
- const insertRowsPatternB = new RegExp(`(#{2,4}\\s*Phase\\s+${phasePattern}(?=[:\\s])[\\s\\S]*?(?:\\*\\*Plans\\*\\*:|\\*\\*Plans:\\*\\*)[^\\n]*)`, 'i');
581
+ //
582
+ // BOTH anchors carry the same phase-section bound as the summary-line
583
+ // builder (`PHASE_SECTION_SCAN`, D-08-33). They did not, and the omission
584
+ // was the F2 defect wearing a second hat: given a phase whose section has
585
+ // NO Plans anchor of either shape, the unbounded lazy scan found the next
586
+ // phase's anchor and inserted THIS phase's plan rows into THAT phase's
587
+ // checklist — a plan row asserting membership of a phase it has nothing to
588
+ // do with. Bounded, a phase with no anchor simply gets no rows, which is
589
+ // the honest outcome.
590
+ const insertRowsPatternA = new RegExp(`(#{2,4}\\s*Phase\\s+${phasePattern}(?=[:\\s])${PHASE_SECTION_SCAN}(?:^|\\n)(?:Plans:)[^\\n]*)`, 'i');
591
+ const insertRowsPatternB = new RegExp(`(#{2,4}\\s*Phase\\s+${phasePattern}(?=[:\\s])${PHASE_SECTION_SCAN}(?:\\*\\*Plans\\*\\*:|\\*\\*Plans:\\*\\*)[^\\n]*)`, 'i');
495
592
  const sortedMissing = [...missingPlans].sort();
496
593
  const newRows = sortedMissing.map(p => `- [ ] ${p}`).join('\n');
497
594
  const inserter = (match) => `${match}\n${newRows}`;
@@ -534,7 +631,11 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
534
631
  plan_count: planCount,
535
632
  summary_count: summaryCount,
536
633
  status,
537
- complete: isComplete,
634
+ built,
635
+ // D-08-32: true when the phase was already governed and this call therefore
636
+ // refreshed the executed count only. `status`/`built` stay count-derived, so
637
+ // this verb still never REPORTS completion — it only says it preserved one.
638
+ governed: governedPhase,
538
639
  }, raw, `${summaryCount}/${planCount} ${status}`);
539
640
  }
540
641
  // ─── cmdRoadmapAnnotateDependencies ───────────────────────────────────────────
@@ -560,7 +661,7 @@ function cmdRoadmapAnnotateDependencies(cwd, phaseNum, raw) {
560
661
  output({ updated: false, reason: 'ROADMAP.md not found' }, raw, 'no roadmap');
561
662
  return;
562
663
  }
563
- const phaseInfo = findPhaseInternal(cwd, phaseNum);
664
+ const phaseInfo = resolvePhaseForRead(cwd, phaseNum);
564
665
  if (!phaseInfo || phaseInfo.plans.length === 0) {
565
666
  output({ updated: false, reason: 'no plans found for phase', phase: phaseNum }, raw, 'no plans');
566
667
  return;
@@ -730,6 +831,7 @@ function cmdRoadmapAnnotateDependencies(cwd, phaseNum, raw) {
730
831
  }, raw, updated ? `annotated ${waves.length} wave(s), ${crossCuttingTruths.length} constraint(s)` : 'skipped (already annotated or no plan list)');
731
832
  }
732
833
  module.exports = {
834
+ ROADMAP_BUILT_STATUS,
733
835
  cmdRoadmapGetPhase,
734
836
  getRoadmapPhaseWithFallback,
735
837
  cmdRoadmapAnalyze,
@@ -45,7 +45,7 @@ const reset = '\x1b[0m';
45
45
  // ---------------------------------------------------------------------------
46
46
  const GSD_COPILOT_HOOK_FILE = 'pp-session.json';
47
47
  const GSD_COPILOT_SESSION_MSG_PRESENT = 'pp-core: .planning/STATE.md present - review the current phase and any blockers before acting.';
48
- const GSD_COPILOT_SESSION_MSG_ABSENT = 'pp-core: no .planning/ workflow found - run /pp-new-project to start a tracked workflow.';
48
+ const GSD_COPILOT_SESSION_MSG_ABSENT = 'pp-core: no .planning/ workflow found - run /pp-init to start a tracked workflow.';
49
49
  const GSD_COPILOT_SESSION_HOOK_BASH = 'if [ -f .planning/STATE.md ]; then ' +
50
50
  `printf '%s' '{"additionalContext":"${GSD_COPILOT_SESSION_MSG_PRESENT}"}'; else ` +
51
51
  `printf '%s' '{"additionalContext":"${GSD_COPILOT_SESSION_MSG_ABSENT}"}'; fi`;
@@ -166,6 +166,21 @@ const MANAGED_HOOK_BASENAMES_BY_SURFACE = {
166
166
  'pp-check-update.js',
167
167
  ]),
168
168
  };
169
+ // This set must be a SUPERSET of every `MANAGED_HOOKS` name
170
+ // (hooks/managed-hooks-registry.cjs, the authoritative list) that the
171
+ // settings-json surface can register. `isManagedHookCommand` reads it, and the
172
+ // uninstall filter plus the #338 migration read both go through that — so a
173
+ // registry hook missing here is classified as a *user* hook and survives an
174
+ // uninstall. Measured in a phase-9 fixture: two names were missing, leaving one
175
+ // pp-core hook still wired to a script that persists (a live PreToolUse hook in
176
+ // a project the operator believes is uninstalled, D-09-08) and one wired to a
177
+ // script the same uninstall had just deleted.
178
+ //
179
+ // It cannot require() the registry: this module is a leaf compiled into
180
+ // pp-core/bin/lib/, and Codex/Copilot/Cline installs ship no hooks/ dir beside
181
+ // it. The pin that keeps the two in step therefore lives in
182
+ // tests/shell-command-projection-dispatch.test.cjs, which drives the check from
183
+ // MANAGED_HOOKS minus a named, reasoned exclusion table.
169
184
  const MANAGED_HOOK_COMMAND_BASENAMES_BY_SURFACE = {
170
185
  'settings-json': new Set([
171
186
  'pp-check-update.js',
@@ -180,6 +195,15 @@ const MANAGED_HOOK_COMMAND_BASENAMES_BY_SURFACE = {
180
195
  'pp-session-state.sh',
181
196
  'pp-validate-commit.sh',
182
197
  'pp-phase-boundary.sh',
198
+ // Registered by the Claude installer into the settings file (PreToolUse /
199
+ // PostToolUse / SessionStart) but absent from this set until phase 9.
200
+ 'pp-worktree-path-guard.js',
201
+ 'pp-graphify-update.sh',
202
+ // Registered on a settings-json-shaped surface by the marketplace plugin
203
+ // manifest (hooks/hooks.json, SessionStart); a classic install never wires
204
+ // it, so recognising it removes an entry that is there and changes nothing
205
+ // when it is not.
206
+ 'pp-ensure-canonical-path.js',
183
207
  ]),
184
208
  'codex-toml': new Set([
185
209
  'pp-check-update.js',
@@ -13,12 +13,19 @@
13
13
  * ADR-457 build-at-publish: the hand-written bin/lib/state-command-router.cjs
14
14
  * collapsed to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
15
15
  * from the prior hand-written .cjs; only types are added.
16
+ *
17
+ * 261008-mwk: every subcommand declares the arguments it reads in STATE_ARG_SPECS,
18
+ * and routeStateCommand refuses anything else (reason `usage`) BEFORE dispatch, so
19
+ * a refused call writes nothing. The same entry feeds each handler's parseNamedArgs
20
+ * call, so the validator and the parser read one owner (D-1, D-6).
16
21
  */
17
22
  const command_aliases_cjs_1 = require("./command-aliases.cjs");
18
23
  // eslint-disable-next-line @typescript-eslint/no-require-imports
19
24
  const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
20
25
  const { routeHubCommandFamily } = cjsCommandRouterAdapter;
21
26
  const command_arg_projection_cjs_1 = require("./command-arg-projection.cjs");
27
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
28
+ const io = require("./io.cjs");
22
29
  // ─── Types ────────────────────────────────────────────────────────────────────
23
30
  // Helper: extract string-only named arg value (value flags never return boolean).
24
31
  function strArg(opts, key) {
@@ -27,8 +34,69 @@ function strArg(opts, key) {
27
34
  return undefined;
28
35
  return v;
29
36
  }
37
+ // ─── Argument specs (261008-mwk D-6) ──────────────────────────────────────────
38
+ /**
39
+ * The arguments each state subcommand reads — one entry per registered subcommand
40
+ * (STATE_SUBCOMMANDS plus complete-phase). routeStateCommand validates argv against
41
+ * the entry before dispatch, and every handler's named-arg parse takes its flag
42
+ * lists from the same entry. A subcommand added without an entry reds the coverage
43
+ * pin in tests/state.test.cjs.
44
+ *
45
+ * planned-phase reads --phase and --plans only: cmdStatePlannedPhase has no name
46
+ * parameter, so a name flag would be parsed and dropped (D-4).
47
+ */
48
+ const STATE_ARG_SPECS = Object.freeze({
49
+ load: {},
50
+ json: {},
51
+ 'advance-plan': {},
52
+ 'update-progress': {},
53
+ 'signal-resume': {},
54
+ validate: {},
55
+ get: { maxPositionals: 1, verbatimPositionals: true, positionalLabel: 'at most one positional <section>' },
56
+ update: { maxPositionals: 2, verbatimPositionals: true, positionalLabel: 'at most two positionals <field> <value>' },
57
+ patch: { free: true },
58
+ 'complete-phase': {
59
+ valueFlags: ['phase'],
60
+ maxPositionals: 1,
61
+ positionalExcludedBy: 'phase',
62
+ positionalLabel: '--phase <value> or one positional <phase>',
63
+ },
64
+ 'begin-phase': { valueFlags: ['phase', 'name', 'plans'] },
65
+ 'record-metric': { valueFlags: ['phase', 'plan', 'duration', 'tasks', 'files'] },
66
+ 'add-decision': { valueFlags: ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file'] },
67
+ 'add-blocker': { valueFlags: ['text', 'text-file'] },
68
+ 'resolve-blocker': { valueFlags: ['text'] },
69
+ 'record-session': { valueFlags: ['stopped-at', 'resume-file'] },
70
+ 'signal-waiting': { valueFlags: ['type', 'question', 'options', 'phase'] },
71
+ 'planned-phase': { valueFlags: ['phase', 'plans'] },
72
+ sync: { booleanFlags: ['verify'] },
73
+ prune: { valueFlags: ['keep-recent'], booleanFlags: ['dry-run'] },
74
+ 'milestone-switch': { valueFlags: ['milestone', 'name'] },
75
+ 'add-roadmap-evolution': { valueFlags: ['phase', 'action', 'after', 'note', 'note-file'], booleanFlags: ['urgent'] },
76
+ });
77
+ // Own-property lookup only: a subcommand named `constructor` or `__proto__` must
78
+ // reach the hub's unknown-subcommand path, not an Object.prototype member.
79
+ function specFor(sub) {
80
+ return Object.prototype.hasOwnProperty.call(STATE_ARG_SPECS, sub) ? STATE_ARG_SPECS[sub] : undefined;
81
+ }
30
82
  // ─── Implementation ───────────────────────────────────────────────────────────
31
83
  function routeStateCommand({ state, args, cwd, raw, error }) {
84
+ // 261008-mwk D-1: refuse before dispatch, so a refused call runs no handler and
85
+ // writes nothing. Unknown subcommands fall through to the hub's own message.
86
+ const sub = args[1] || 'load';
87
+ const spec = specFor(sub);
88
+ if (spec) {
89
+ const verdict = (0, command_arg_projection_cjs_1.validateVerbArgs)(args.slice(2), spec, `state ${sub}`);
90
+ if (!verdict.ok) {
91
+ error(verdict.reason, io.ERROR_REASON.USAGE);
92
+ return;
93
+ }
94
+ }
95
+ // Every handler's named-arg parse reads its flag lists from the verb's spec (D-6).
96
+ const named = (verb) => {
97
+ const s = specFor(verb) ?? {};
98
+ return (0, command_arg_projection_cjs_1.parseNamedArgs)(args, s.valueFlags ?? [], s.booleanFlags ?? []);
99
+ };
32
100
  const parsePlans = (plans) => {
33
101
  const parsedPlans = plans == null ? null : Number.parseInt(plans, 10);
34
102
  if (plans != null && Number.isNaN(parsedPlans)) {
@@ -90,7 +158,7 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
90
158
  },
91
159
  'advance-plan': () => state.cmdStateAdvancePlan(cwd, raw),
92
160
  'record-metric': () => {
93
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
161
+ const a = named('record-metric');
94
162
  state.cmdStateRecordMetric(cwd, {
95
163
  phase: strArg(a, 'phase'),
96
164
  plan: strArg(a, 'plan'),
@@ -101,7 +169,7 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
101
169
  },
102
170
  'update-progress': () => state.cmdStateUpdateProgress(cwd, raw),
103
171
  'add-decision': () => {
104
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file']);
172
+ const a = named('add-decision');
105
173
  state.cmdStateAddDecision(cwd, {
106
174
  phase: strArg(a, 'phase'),
107
175
  summary: strArg(a, 'summary'),
@@ -111,11 +179,11 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
111
179
  }, raw);
112
180
  },
113
181
  'add-blocker': () => {
114
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['text', 'text-file']);
182
+ const a = named('add-blocker');
115
183
  state.cmdStateAddBlocker(cwd, { text: strArg(a, 'text'), text_file: strArg(a, 'text-file') }, raw);
116
184
  },
117
185
  'add-roadmap-evolution': () => {
118
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['phase', 'action', 'after', 'note', 'note-file'], ['urgent']);
186
+ const a = named('add-roadmap-evolution');
119
187
  state.cmdStateAddRoadmapEvolution(cwd, {
120
188
  phase: strArg(a, 'phase'),
121
189
  action: strArg(a, 'action'),
@@ -125,43 +193,43 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
125
193
  urgent: a['urgent'] === true,
126
194
  }, raw);
127
195
  },
128
- 'resolve-blocker': () => state.cmdStateResolveBlocker(cwd, strArg((0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['text']), 'text'), raw),
196
+ 'resolve-blocker': () => state.cmdStateResolveBlocker(cwd, strArg(named('resolve-blocker'), 'text'), raw),
129
197
  'record-session': () => {
130
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['stopped-at', 'resume-file']);
198
+ const a = named('record-session');
131
199
  // Pass resume_file as-is (undefined when --resume-file was not provided) so
132
200
  // cmdStateRecordSession can distinguish "caller explicitly passed a value" from
133
201
  // "option was not supplied" and apply the template-default-only replacement guard.
134
202
  state.cmdStateRecordSession(cwd, { stopped_at: strArg(a, 'stopped-at'), resume_file: strArg(a, 'resume-file') }, raw);
135
203
  },
136
204
  'begin-phase': () => {
137
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['phase', 'name', 'plans']);
205
+ const a = named('begin-phase');
138
206
  state.cmdStateBeginPhase(cwd, strArg(a, 'phase'), strArg(a, 'name'), parsePlans(strArg(a, 'plans')), raw);
139
207
  },
140
208
  'signal-waiting': () => {
141
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['type', 'question', 'options', 'phase']);
209
+ const a = named('signal-waiting');
142
210
  state.cmdSignalWaiting(cwd, strArg(a, 'type'), strArg(a, 'question'), strArg(a, 'options'), strArg(a, 'phase'), raw);
143
211
  },
144
212
  'signal-resume': () => state.cmdSignalResume(cwd, raw),
145
213
  'planned-phase': () => {
146
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['phase', 'name', 'plans']);
214
+ const a = named('planned-phase');
147
215
  state.cmdStatePlannedPhase(cwd, strArg(a, 'phase'), parsePlans(strArg(a, 'plans')), raw);
148
216
  },
149
217
  validate: () => state.cmdStateValidate(cwd, raw),
150
218
  sync: () => {
151
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, [], ['verify']);
219
+ const a = named('sync');
152
220
  state.cmdStateSync(cwd, { verify: a['verify'] }, raw);
153
221
  },
154
222
  prune: () => {
155
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['keep-recent'], ['dry-run']);
223
+ const a = named('prune');
156
224
  state.cmdStatePrune(cwd, { keepRecent: strArg(a, 'keep-recent') || '3', dryRun: a['dry-run'] === true }, raw);
157
225
  },
158
226
  // complete-phase: CJS-only — no SDK counterpart.
159
227
  'complete-phase': () => {
160
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['phase']);
228
+ const a = named('complete-phase');
161
229
  state.cmdStateCompletePhase(cwd, raw, strArg(a, 'phase') || args[2]);
162
230
  },
163
231
  'milestone-switch': () => {
164
- const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['milestone', 'name']);
232
+ const a = named('milestone-switch');
165
233
  state.cmdStateMilestoneSwitch(cwd, strArg(a, 'milestone'), strArg(a, 'name'), raw);
166
234
  },
167
235
  },
@@ -169,4 +237,5 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
169
237
  }
170
238
  module.exports = {
171
239
  routeStateCommand,
240
+ STATE_ARG_SPECS,
172
241
  };