@opengsd/gsd-core 1.7.0 → 1.9.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 (261) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +45 -1
  4. package/README.md +2 -0
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-codebase-mapper.md +1 -1
  7. package/agents/gsd-debug-session-manager.md +78 -4
  8. package/agents/gsd-debugger.md +87 -29
  9. package/agents/gsd-executor.md +49 -9
  10. package/agents/gsd-intel-updater.md +3 -3
  11. package/agents/gsd-phase-researcher.md +4 -2
  12. package/agents/gsd-plan-checker.md +20 -0
  13. package/agents/gsd-planner.md +44 -59
  14. package/agents/gsd-project-researcher.md +2 -2
  15. package/agents/gsd-ui-auditor.md +0 -40
  16. package/agents/gsd-verifier.md +2 -2
  17. package/bin/install.js +1338 -135
  18. package/commands/gsd/ai-integration-phase.md +1 -1
  19. package/commands/gsd/mempalace-capture.md +9 -5
  20. package/commands/gsd/new-milestone.md +1 -1
  21. package/commands/gsd/plan-phase.md +5 -3
  22. package/commands/gsd/plan-review-convergence.md +7 -2
  23. package/gsd-core/bin/gsd-tools.cjs +2690 -2472
  24. package/gsd-core/bin/lib/adapter-imperative.cjs +8 -1
  25. package/gsd-core/bin/lib/agent-command-router.cjs +20 -5
  26. package/gsd-core/bin/lib/api-coverage.cjs +360 -53
  27. package/gsd-core/bin/lib/audit.cjs +8 -8
  28. package/gsd-core/bin/lib/broken-windows.cjs +716 -0
  29. package/gsd-core/bin/lib/capability-command-router.cjs +733 -0
  30. package/gsd-core/bin/lib/capability-consent.cjs +40 -1
  31. package/gsd-core/bin/lib/capability-lifecycle.cjs +58 -0
  32. package/gsd-core/bin/lib/capability-loader.cjs +23 -1
  33. package/gsd-core/bin/lib/capability-registry.cjs +1450 -160
  34. package/gsd-core/bin/lib/capability-trust.cjs +468 -33
  35. package/gsd-core/bin/lib/capability-validator.cjs +882 -6
  36. package/gsd-core/bin/lib/capability-writer.cjs +6 -1
  37. package/gsd-core/bin/lib/check-command-router.cjs +140 -27
  38. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +15 -0
  39. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +209 -31
  40. package/gsd-core/bin/lib/claude-orchestration.cjs +203 -25
  41. package/gsd-core/bin/lib/command-aliases.cjs +14 -0
  42. package/gsd-core/bin/lib/commands.cjs +326 -21
  43. package/gsd-core/bin/lib/config-loader.cjs +214 -30
  44. package/gsd-core/bin/lib/config.cjs +158 -22
  45. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  46. package/gsd-core/bin/lib/decisions.cjs +32 -8
  47. package/gsd-core/bin/lib/docs.cjs +6 -0
  48. package/gsd-core/bin/lib/estimate-cli.cjs +336 -0
  49. package/gsd-core/bin/lib/external-descriptor-trust.cjs +14 -2
  50. package/gsd-core/bin/lib/frontmatter.cjs +125 -15
  51. package/gsd-core/bin/lib/gap-checker.cjs +17 -2
  52. package/gsd-core/bin/lib/host-integration.cjs +215 -8
  53. package/gsd-core/bin/lib/init.cjs +155 -66
  54. package/gsd-core/bin/lib/install-engine.cjs +299 -23
  55. package/gsd-core/bin/lib/install-profiles.cjs +239 -1
  56. package/gsd-core/bin/lib/installer-migrations/005-opencode-baseline-commands-dir.cjs +146 -0
  57. package/gsd-core/bin/lib/installer-migrations/006-pi-extension-cjs-to-js.cjs +91 -0
  58. package/gsd-core/bin/lib/installer-migrations.cjs +44 -5
  59. package/gsd-core/bin/lib/markdown-sectionizer.cjs +107 -0
  60. package/gsd-core/bin/lib/milestone.cjs +248 -14
  61. package/gsd-core/bin/lib/model-catalog.cjs +69 -4
  62. package/gsd-core/bin/lib/model-resolver.cjs +189 -7
  63. package/gsd-core/bin/lib/observability/logger.cjs +7 -2
  64. package/gsd-core/bin/lib/onboard-projection.cjs +11 -8
  65. package/gsd-core/bin/lib/phase-command-router.cjs +10 -1
  66. package/gsd-core/bin/lib/phase-estimation.cjs +398 -0
  67. package/gsd-core/bin/lib/phase-id.cjs +304 -9
  68. package/gsd-core/bin/lib/phase.cjs +258 -17
  69. package/gsd-core/bin/lib/plan-drift-guard.cjs +1 -1
  70. package/gsd-core/bin/lib/plan-scan.cjs +70 -2
  71. package/gsd-core/bin/lib/planning-workspace.cjs +9 -2
  72. package/gsd-core/bin/lib/profile-output.cjs +34 -8
  73. package/gsd-core/bin/lib/review-lane-descriptor.cjs +927 -0
  74. package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
  75. package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
  76. package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
  77. package/gsd-core/bin/lib/roadmap-parser.cjs +61 -10
  78. package/gsd-core/bin/lib/roadmap.cjs +23 -7
  79. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +38 -5
  80. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +23 -9
  81. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +156 -0
  82. package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
  83. package/gsd-core/bin/lib/smart-entry.cjs +70 -5
  84. package/gsd-core/bin/lib/state-document.cjs +171 -24
  85. package/gsd-core/bin/lib/state-transition.cjs +50 -11
  86. package/gsd-core/bin/lib/state.cjs +206 -32
  87. package/gsd-core/bin/lib/surface.cjs +51 -9
  88. package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
  89. package/gsd-core/bin/lib/uat.cjs +428 -11
  90. package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
  91. package/gsd-core/bin/lib/unusable-input.cjs +216 -0
  92. package/gsd-core/bin/lib/validate.cjs +44 -8
  93. package/gsd-core/bin/lib/verification.cjs +163 -31
  94. package/gsd-core/bin/lib/verify.cjs +348 -42
  95. package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
  96. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  97. package/gsd-core/bin/shared/config-schema.manifest.json +4 -15
  98. package/gsd-core/bin/shared/model-catalog.json +5 -0
  99. package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
  100. package/gsd-core/references/api-coverage.md +37 -7
  101. package/gsd-core/references/checkpoints.md +1 -1
  102. package/gsd-core/references/common-bug-patterns.md +13 -0
  103. package/gsd-core/references/context-budget.md +40 -0
  104. package/gsd-core/references/debugger-bug-taxonomy.md +111 -0
  105. package/gsd-core/references/debugger-fix-acceptance.md +157 -0
  106. package/gsd-core/references/debugger-philosophy.md +1 -0
  107. package/gsd-core/references/debugger-prevention.md +98 -0
  108. package/gsd-core/references/debugger-rca-branching.md +98 -0
  109. package/gsd-core/references/debugger-repro-hardening.md +130 -0
  110. package/gsd-core/references/debugger-sbfl.md +110 -0
  111. package/gsd-core/references/debugger-semantic-recall.md +81 -0
  112. package/gsd-core/references/execute-phase-quota-recovery.md +55 -0
  113. package/gsd-core/references/execute-phase-requirement-revert.md +8 -0
  114. package/gsd-core/references/execute-phase-response-language.md +7 -0
  115. package/gsd-core/references/gate-prompts.md +6 -3
  116. package/gsd-core/references/model-profile-resolution.md +64 -13
  117. package/gsd-core/references/offer-next.md +88 -0
  118. package/gsd-core/references/planner-antipatterns.md +6 -0
  119. package/gsd-core/references/planner-mvp-mode.md +12 -13
  120. package/gsd-core/references/planner-preconditions.md +156 -0
  121. package/gsd-core/references/planner-reversibility.md +132 -0
  122. package/gsd-core/references/planning-config.md +2 -1
  123. package/gsd-core/references/reviewer-instances.md +28 -19
  124. package/gsd-core/references/runtime-aware-dispatch.md +42 -0
  125. package/gsd-core/references/skeleton-template.md +1 -1
  126. package/gsd-core/references/thinking-models-planning.md +3 -1
  127. package/gsd-core/references/ui-consideration-probe.md +2 -2
  128. package/gsd-core/references/worktree-branch-check.md +4 -4
  129. package/gsd-core/templates/DEBUG.md +5 -3
  130. package/gsd-core/templates/summary-minimal.md +4 -0
  131. package/gsd-core/templates/summary-standard.md +4 -0
  132. package/gsd-core/templates/summary.md +7 -0
  133. package/gsd-core/workflows/add-phase.md +2 -0
  134. package/gsd-core/workflows/add-tests.md +3 -1
  135. package/gsd-core/workflows/add-todo.md +32 -1
  136. package/gsd-core/workflows/ai-integration-phase.md +8 -6
  137. package/gsd-core/workflows/audit-fix.md +6 -2
  138. package/gsd-core/workflows/audit-milestone.md +8 -0
  139. package/gsd-core/workflows/autonomous.md +19 -15
  140. package/gsd-core/workflows/check-todos.md +5 -3
  141. package/gsd-core/workflows/cleanup.md +7 -1
  142. package/gsd-core/workflows/code-review-fix.md +14 -6
  143. package/gsd-core/workflows/code-review.md +93 -24
  144. package/gsd-core/workflows/complete-milestone.md +3 -0
  145. package/gsd-core/workflows/debug.md +35 -7
  146. package/gsd-core/workflows/diagnose-issues.md +5 -1
  147. package/gsd-core/workflows/discovery-phase.md +7 -0
  148. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
  149. package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
  150. package/gsd-core/workflows/discuss-phase/templates/context.md +16 -2
  151. package/gsd-core/workflows/discuss-phase-assumptions.md +18 -9
  152. package/gsd-core/workflows/discuss-phase.md +2 -2
  153. package/gsd-core/workflows/do.md +7 -1
  154. package/gsd-core/workflows/docs-update.md +9 -0
  155. package/gsd-core/workflows/eval-review.md +4 -1
  156. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
  157. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
  158. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +4 -4
  159. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -2
  160. package/gsd-core/workflows/execute-phase.md +110 -149
  161. package/gsd-core/workflows/execute-plan.md +20 -8
  162. package/gsd-core/workflows/explore.md +4 -0
  163. package/gsd-core/workflows/extract-learnings.md +21 -0
  164. package/gsd-core/workflows/graduation.md +3 -0
  165. package/gsd-core/workflows/health.md +7 -1
  166. package/gsd-core/workflows/help/modes/full.md +9 -5
  167. package/gsd-core/workflows/import.md +11 -2
  168. package/gsd-core/workflows/inbox.md +7 -0
  169. package/gsd-core/workflows/ingest-docs.md +19 -10
  170. package/gsd-core/workflows/manager.md +3 -1
  171. package/gsd-core/workflows/map-codebase.md +17 -10
  172. package/gsd-core/workflows/mvp-phase.md +3 -0
  173. package/gsd-core/workflows/new-milestone.md +79 -23
  174. package/gsd-core/workflows/new-project.md +28 -19
  175. package/gsd-core/workflows/new-workspace.md +3 -1
  176. package/gsd-core/workflows/next.md +5 -2
  177. package/gsd-core/workflows/onboard.md +3 -0
  178. package/gsd-core/workflows/plan-phase.md +56 -51
  179. package/gsd-core/workflows/plan-review-convergence.md +61 -12
  180. package/gsd-core/workflows/plant-seed.md +3 -0
  181. package/gsd-core/workflows/profile-user.md +7 -1
  182. package/gsd-core/workflows/progress.md +31 -3
  183. package/gsd-core/workflows/quick.md +33 -10
  184. package/gsd-core/workflows/remove-workspace.md +3 -0
  185. package/gsd-core/workflows/review.md +172 -585
  186. package/gsd-core/workflows/scan.md +10 -2
  187. package/gsd-core/workflows/secure-phase.md +13 -2
  188. package/gsd-core/workflows/settings-integrations.md +3 -0
  189. package/gsd-core/workflows/settings.md +3 -0
  190. package/gsd-core/workflows/ship.md +88 -11
  191. package/gsd-core/workflows/sketch.md +3 -0
  192. package/gsd-core/workflows/smart-entry.md +4 -1
  193. package/gsd-core/workflows/spike.md +7 -1
  194. package/gsd-core/workflows/ui-phase.md +11 -2
  195. package/gsd-core/workflows/ui-review.md +11 -1
  196. package/gsd-core/workflows/undo.md +7 -0
  197. package/gsd-core/workflows/update.md +106 -5
  198. package/gsd-core/workflows/validate-phase.md +13 -2
  199. package/gsd-core/workflows/verify-phase.md +2 -2
  200. package/gsd-core/workflows/verify-work.md +15 -4
  201. package/hooks/dist/gsd-context-monitor.js +27 -9
  202. package/hooks/dist/gsd-cursor-session-start.js +6 -2
  203. package/hooks/dist/gsd-cursor-stop.js +6 -2
  204. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  205. package/hooks/dist/gsd-graphify-update.sh +9 -0
  206. package/hooks/dist/gsd-phase-boundary.sh +14 -2
  207. package/hooks/dist/gsd-prompt-guard.js +101 -2
  208. package/hooks/dist/gsd-read-guard.js +100 -2
  209. package/hooks/dist/gsd-read-injection-scanner.js +109 -2
  210. package/hooks/dist/gsd-statusline.js +97 -9
  211. package/hooks/dist/gsd-workflow-guard.js +110 -6
  212. package/hooks/dist/gsd-worktree-path-guard.js +132 -8
  213. package/hooks/dist/lib/cursor-workspace.js +74 -0
  214. package/hooks/gsd-context-monitor.js +27 -9
  215. package/hooks/gsd-cursor-session-start.js +6 -2
  216. package/hooks/gsd-cursor-stop.js +6 -2
  217. package/hooks/gsd-cursor-subagent-start.js +6 -2
  218. package/hooks/gsd-graphify-update.sh +9 -0
  219. package/hooks/gsd-phase-boundary.sh +14 -2
  220. package/hooks/gsd-prompt-guard.js +101 -2
  221. package/hooks/gsd-read-guard.js +100 -2
  222. package/hooks/gsd-read-injection-scanner.js +109 -2
  223. package/hooks/gsd-statusline.js +97 -9
  224. package/hooks/gsd-workflow-guard.js +110 -6
  225. package/hooks/gsd-worktree-path-guard.js +132 -8
  226. package/hooks/lib/cursor-workspace.js +74 -0
  227. package/package.json +10 -8
  228. package/pi/gsd.cjs +34 -3
  229. package/scripts/changeset/lint.cjs +1 -0
  230. package/scripts/changeset/parse.cjs +26 -0
  231. package/scripts/check-coverage-gate.cjs +51 -0
  232. package/scripts/check-glossary-refs.cjs +244 -0
  233. package/scripts/ci-rebase-check.cjs +48 -4
  234. package/scripts/ci-test-scope.cjs +67 -17
  235. package/scripts/gen-adr-index.cjs +528 -0
  236. package/scripts/gen-capability-matrix.cjs +26 -2
  237. package/scripts/gen-capability-registry.cjs +132 -34
  238. package/scripts/gen-emitted-baseline.cjs +145 -0
  239. package/scripts/gen-test-timings.cjs +201 -0
  240. package/scripts/lint-compiled-artifact-sync.cjs +146 -0
  241. package/scripts/lint-emitted-drift-ack.cjs +149 -0
  242. package/scripts/lint-fix-has-regression-test.cjs +131 -0
  243. package/scripts/lint-portable-timeout.cjs +140 -0
  244. package/scripts/lint-resolution-provenance.cjs +9 -0
  245. package/scripts/lint-test-file-count.allowlist.json +1 -0
  246. package/scripts/mutation-matrix.cjs +4 -0
  247. package/scripts/prompt-injection-scan.sh +6 -0
  248. package/scripts/registry-schema.cjs +57 -8
  249. package/scripts/release-notes/conventional-title.cjs +19 -1
  250. package/scripts/release-notes/format-github-release-notes.cjs +7 -3
  251. package/scripts/release-tarball-smoke.cjs +18 -11
  252. package/scripts/run-tests.cjs +420 -58
  253. package/scripts/workflow-size.cjs +16 -8
  254. package/skills/gsd-ai-integration-phase/SKILL.md +1 -1
  255. package/skills/gsd-mempalace-capture/SKILL.md +9 -5
  256. package/skills/gsd-new-milestone/SKILL.md +1 -1
  257. package/skills/gsd-plan-phase/SKILL.md +5 -3
  258. package/skills/gsd-plan-review-convergence/SKILL.md +7 -2
  259. package/vscode/package.json +1 -1
  260. package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
  261. package/scripts/update-size-baseline.cjs +0 -68
@@ -112,18 +112,6 @@ function listPhaseSummaryFiles(phaseDir) {
112
112
  function listPhasePlanFiles(phaseDir) {
113
113
  return scanPhasePlans(phaseDir)['planFiles'];
114
114
  }
115
- function verificationNextCommand(status, phaseNumber, slashRuntime) {
116
- if (status === 'gaps_found') {
117
- return `${(0, runtime_slash_cjs_1.formatGsdSlash)('plan-phase', slashRuntime)} ${phaseNumber} --gaps`;
118
- }
119
- if (status === 'human_needed' || status === 'stale') {
120
- return `${(0, runtime_slash_cjs_1.formatGsdSlash)('verify-work', slashRuntime)} ${phaseNumber}`;
121
- }
122
- if (status === 'missing' || status === 'unknown') {
123
- return `${(0, runtime_slash_cjs_1.formatGsdSlash)('execute-phase', slashRuntime)} ${phaseNumber}`;
124
- }
125
- return '';
126
- }
127
115
  function projectCompletionStatus(implementationComplete, verificationPassed) {
128
116
  if (implementationComplete && verificationPassed)
129
117
  return 'complete';
@@ -134,8 +122,14 @@ function projectCompletionStatus(implementationComplete, verificationPassed) {
134
122
  function buildPhaseCompletionProjection(cwd, phaseNumber, phaseDir, planCount, summaryCount, slashRuntime) {
135
123
  const implementationComplete = planCount > 0 && summaryCount >= planCount;
136
124
  const phaseFullDir = phaseDir ? node_path_1.default.join(cwd, phaseDir) : '';
125
+ // #2617: ONE verification-routing seam. init used to re-derive next_command
126
+ // from the status with its own projector, which had drifted from the router's
127
+ // table — it appended the phase number and answered `human_needed`; the table
128
+ // did neither. The router now owns both the content and the runtime
129
+ // projection, and init passes the phase number it already knows (its phaseDir
130
+ // is unresolved in some branches, where the router could not derive one).
137
131
  const verificationStatus = implementationComplete
138
- ? readVerificationStatus(phaseFullDir)
132
+ ? readVerificationStatus(phaseFullDir, { runtime: slashRuntime, phaseNumber })
139
133
  : { status: 'not_required', next_action: '', next_command: '' };
140
134
  const projectedVerificationStatus = verificationStatus.status;
141
135
  const projectedVerificationAction = verificationStatus.next_action;
@@ -148,7 +142,7 @@ function buildPhaseCompletionProjection(cwd, phaseNumber, phaseDir, planCount, s
148
142
  phase_complete: phaseComplete,
149
143
  completion_status: projectCompletionStatus(implementationComplete, verificationPassed),
150
144
  verification_next_action: projectedVerificationAction,
151
- verification_next_command: verificationNextCommand(projectedVerificationStatus, phaseNumber, slashRuntime),
145
+ verification_next_command: verificationStatus.next_command,
152
146
  };
153
147
  }
154
148
  function getLatestCompletedMilestone(cwd) {
@@ -305,7 +299,11 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
305
299
  milestone_branch_template: config.milestone_branch_template,
306
300
  verifier_enabled: config.verifier,
307
301
  phase_found: !!phaseInfo,
308
- phase_dir: phaseInfo?.['directory'] || null,
302
+ // #2376: absolute (anchored on cwd/project_root), not orchestrator-cwd-relative —
303
+ // a spawned subagent's own cwd may differ from the orchestrator's.
304
+ phase_dir: phaseInfo?.['directory']
305
+ ? toPosixPath(node_path_1.default.join(cwd, phaseInfo['directory']))
306
+ : null,
309
307
  phase_number: phaseInfo?.['phase_number'] || null,
310
308
  phase_name: phaseInfo?.['phase_name'] || null,
311
309
  phase_slug: phaseInfo?.['phase_slug'] || null,
@@ -331,9 +329,13 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
331
329
  state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
332
330
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
333
331
  config_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'config.json')),
334
- state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
335
- roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
336
- config_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'config.json'))),
332
+ // #2376: emit absolute paths — see comment above on phase_dir.
333
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
334
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
335
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
336
+ // #2376: execute-phase.md's verify_phase_goal step reads this instead of
337
+ // hardcoding '.planning/REQUIREMENTS.md' into the gsd-verifier spawn prompt.
338
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
337
339
  };
338
340
  if (options['validate']) {
339
341
  try {
@@ -405,7 +407,8 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
405
407
  if (slug) {
406
408
  const prefix = rawProjectCodePlan ? `${rawProjectCodePlan}-` : '';
407
409
  const dirName = `${prefix}${paddedNum}-${slug}`;
408
- expectedPhaseDirPlan = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningPaths(cwd).phases, dirName)));
410
+ // #2376: absolute — see comment on phase_dir below.
411
+ expectedPhaseDirPlan = toPosixPath(node_path_1.default.join(planningPaths(cwd).phases, dirName));
409
412
  }
410
413
  }
411
414
  const granularityOverride = options['granularity'];
@@ -427,7 +430,10 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
427
430
  auto_chain_active: !!(config._auto_chain_active),
428
431
  mode: config.mode || 'interactive',
429
432
  phase_found: !!phaseInfo,
430
- phase_dir: phaseDirPlan,
433
+ // #2376: absolute (anchored on cwd/project_root) — path.join(cwd, phaseDirPlan)
434
+ // handed to a spawned subagent must resolve regardless of that subagent's own cwd.
435
+ // phaseDirPlan itself stays relative — phase_status below still joins it against cwd.
436
+ phase_dir: phaseDirPlan ? toPosixPath(node_path_1.default.join(cwd, phaseDirPlan)) : null,
431
437
  expected_phase_dir: expectedPhaseDirPlan,
432
438
  phase_number: phaseNumberPlan,
433
439
  phase_name: phaseNamePlan,
@@ -444,9 +450,10 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
444
450
  plan_count: phaseInfo?.['plans']?.length || 0,
445
451
  planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
446
452
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
447
- state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
448
- roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
449
- requirements_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md'))),
453
+ // #2376: absolute — see comment on phase_dir above.
454
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
455
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
456
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
450
457
  patterns_path: null,
451
458
  };
452
459
  if (phaseInfo?.['directory']) {
@@ -455,27 +462,27 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
455
462
  const files = node_fs_1.default.readdirSync(phaseDirFull);
456
463
  const contextFile = findContextMdIn(phaseDirFull);
457
464
  if (contextFile) {
458
- result['context_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], contextFile));
465
+ result['context_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, contextFile));
459
466
  }
460
467
  const researchFile = files.find((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
461
468
  if (researchFile) {
462
- result['research_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], researchFile));
469
+ result['research_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, researchFile));
463
470
  }
464
471
  const verificationFile = files.find((f) => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
465
472
  if (verificationFile) {
466
- result['verification_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], verificationFile));
473
+ result['verification_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, verificationFile));
467
474
  }
468
475
  const uatFile = files.find((f) => f.endsWith('-UAT.md') || f === 'UAT.md');
469
476
  if (uatFile) {
470
- result['uat_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], uatFile));
477
+ result['uat_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, uatFile));
471
478
  }
472
479
  const reviewsFile = files.find((f) => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md');
473
480
  if (reviewsFile) {
474
- result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], reviewsFile));
481
+ result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, reviewsFile));
475
482
  }
476
483
  const patternsFile = files.find((f) => f.endsWith('-PATTERNS.md') || f === 'PATTERNS.md');
477
484
  if (patternsFile) {
478
- result['patterns_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], patternsFile));
485
+ result['patterns_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, patternsFile));
479
486
  }
480
487
  }
481
488
  catch {
@@ -536,7 +543,14 @@ function cmdInitNewProject(cwd, raw) {
536
543
  brave_search_available: hasBraveSearch,
537
544
  firecrawl_available: hasFirecrawl,
538
545
  exa_search_available: hasExaSearch,
539
- project_path: '.planning/PROJECT.md',
546
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
547
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
548
+ // #2376: new-project.md's research-synthesizer/roadmapper spawn prompts
549
+ // read these instead of hardcoding '.planning/...' literals.
550
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
551
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
552
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
553
+ research_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'research')),
540
554
  };
541
555
  output(withProjectRoot(cwd, result), raw);
542
556
  }
@@ -570,15 +584,22 @@ function cmdInitNewMilestone(cwd, raw) {
570
584
  latest_completed_milestone: latestCompleted?.version || null,
571
585
  latest_completed_milestone_name: latestCompleted?.name || null,
572
586
  phase_dir_count: phaseDirCount,
587
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
573
588
  phase_archive_path: latestCompleted
574
- ? toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningRoot(cwd), 'milestones', `${latestCompleted.version}-phases`)))
589
+ ? toPosixPath(node_path_1.default.join(planningRoot(cwd), 'milestones', `${latestCompleted.version}-phases`))
575
590
  : null,
576
591
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
577
592
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
578
593
  state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
579
- project_path: '.planning/PROJECT.md',
580
- roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
581
- state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
594
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
595
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
596
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
597
+ // #2376: new-milestone.md's research-synthesizer/roadmapper spawn prompts
598
+ // read these instead of hardcoding '.planning/...' literals.
599
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
600
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
601
+ research_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'research')),
602
+ milestones_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'MILESTONES.md')),
582
603
  };
583
604
  output(withProjectRoot(cwd, result), raw);
584
605
  }
@@ -616,8 +637,11 @@ function cmdInitQuick(cwd, description, raw) {
616
637
  description: description || null,
617
638
  date: clock_cjs_1.realClock.localToday(),
618
639
  timestamp: clock_cjs_1.realClock.nowIso(),
619
- quick_dir: '.planning/quick',
620
- task_dir: slug ? `.planning/quick/${quickId}-${slug}` : null,
640
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
641
+ quick_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'quick')),
642
+ task_dir: slug
643
+ ? toPosixPath(node_path_1.default.join(planningDir(cwd), 'quick', `${quickId}-${slug}`))
644
+ : null,
621
645
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
622
646
  planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
623
647
  };
@@ -629,7 +653,18 @@ function cmdInitIngestDocs(cwd, raw) {
629
653
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
630
654
  planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
631
655
  ...getInitGitState(cwd),
632
- project_path: '.planning/PROJECT.md',
656
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase. The
657
+ // classify_parallel/synthesize/route_new_mode spawns in ingest-docs.md
658
+ // (gsd-doc-classifier, gsd-doc-synthesizer, gsd-roadmapper) previously
659
+ // hardcoded bare '.planning/intel/...', '.planning/PROJECT.md', etc.
660
+ // literals into their Agent(prompt=...) blocks; those now interpolate
661
+ // these fields instead.
662
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
663
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
664
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
665
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
666
+ intel_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'intel')),
667
+ conflicts_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'INGEST-CONFLICTS.md')),
633
668
  commit_docs: config.commit_docs,
634
669
  };
635
670
  output(withProjectRoot(cwd, result), raw);
@@ -658,9 +693,10 @@ function cmdInitResume(cwd, raw) {
658
693
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
659
694
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
660
695
  planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
661
- state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
662
- roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
663
- project_path: '.planning/PROJECT.md',
696
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
697
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
698
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
699
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
664
700
  has_interrupted_agent: !!interruptedAgentId,
665
701
  interrupted_agent_id: interruptedAgentId,
666
702
  commit_docs: config.commit_docs,
@@ -715,9 +751,15 @@ function cmdInitVerifyWork(cwd, phase, raw) {
715
751
  checker_model: resolveModelInternal(cwd, 'gsd-plan-checker'),
716
752
  commit_docs: config.commit_docs,
717
753
  phase_found: !!phaseInfo,
718
- phase_dir: phaseDir,
754
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase. phaseDir
755
+ // itself stays relative — evaluateUatPassed above still joins it against cwd.
756
+ phase_dir: phaseDir ? toPosixPath(node_path_1.default.join(cwd, phaseDir)) : null,
719
757
  phase_number: phaseInfo?.['phase_number'] || null,
720
758
  phase_name: phaseInfo?.['phase_name'] || null,
759
+ // #2376: verify-work.md's plan_gap_closure step reads these instead of
760
+ // hardcoding '.planning/STATE.md' / '.planning/ROADMAP.md' literals.
761
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
762
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
721
763
  has_verification: phaseInfo?.['has_verification'] || false,
722
764
  phase_completion: {
723
765
  ...completion,
@@ -799,7 +841,8 @@ function cmdInitPhaseOp(cwd, phase, raw) {
799
841
  if (slug) {
800
842
  const prefix = rawProjectCode ? `${rawProjectCode}-` : '';
801
843
  const dirName = `${prefix}${paddedNum}-${slug}`;
802
- expectedPhaseDir = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningPaths(cwd).phases, dirName)));
844
+ // #2376: absolute — see comment on phase_dir below.
845
+ expectedPhaseDir = toPosixPath(node_path_1.default.join(planningPaths(cwd).phases, dirName));
803
846
  }
804
847
  }
805
848
  const result = {
@@ -814,7 +857,8 @@ function cmdInitPhaseOp(cwd, phase, raw) {
814
857
  ? (0, secrets_cjs_1.maskIfSecret)('exa_search', config.exa_search)
815
858
  : config.exa_search,
816
859
  phase_found: !!phaseInfo,
817
- phase_dir: phaseDir,
860
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
861
+ phase_dir: phaseDir ? toPosixPath(node_path_1.default.join(cwd, phaseDir)) : null,
818
862
  expected_phase_dir: expectedPhaseDir,
819
863
  phase_number: phaseNumber,
820
864
  phase_name: phaseName,
@@ -828,9 +872,10 @@ function cmdInitPhaseOp(cwd, phase, raw) {
828
872
  plan_count: phaseInfo?.['plans']?.length || 0,
829
873
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
830
874
  planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
831
- state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
832
- roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
833
- requirements_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md'))),
875
+ // #2376: absolute — see comment on phase_dir above.
876
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
877
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
878
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
834
879
  };
835
880
  if (phaseInfo?.['directory']) {
836
881
  const phaseDirFull = node_path_1.default.join(cwd, phaseInfo['directory']);
@@ -838,23 +883,23 @@ function cmdInitPhaseOp(cwd, phase, raw) {
838
883
  const files = node_fs_1.default.readdirSync(phaseDirFull);
839
884
  const contextFile = findContextMdIn(phaseDirFull);
840
885
  if (contextFile) {
841
- result['context_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], contextFile));
886
+ result['context_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, contextFile));
842
887
  }
843
888
  const researchFile = files.find((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
844
889
  if (researchFile) {
845
- result['research_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], researchFile));
890
+ result['research_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, researchFile));
846
891
  }
847
892
  const verificationFile = files.find((f) => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
848
893
  if (verificationFile) {
849
- result['verification_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], verificationFile));
894
+ result['verification_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, verificationFile));
850
895
  }
851
896
  const uatFile = files.find((f) => f.endsWith('-UAT.md') || f === 'UAT.md');
852
897
  if (uatFile) {
853
- result['uat_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], uatFile));
898
+ result['uat_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, uatFile));
854
899
  }
855
900
  const reviewsFile = files.find((f) => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md');
856
901
  if (reviewsFile) {
857
- result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], reviewsFile));
902
+ result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, reviewsFile));
858
903
  }
859
904
  }
860
905
  catch {
@@ -878,6 +923,9 @@ function cmdInitTodos(cwd, area, raw) {
878
923
  const createdMatch = content.match(/^created:\s*(.+)$/m);
879
924
  const titleMatch = content.match(/^title:\s*(.+)$/m);
880
925
  const areaMatch = content.match(/^area:\s*(.+)$/m);
926
+ // #2337: kept in parity with cmdListTodos — surface severity when
927
+ // present, omit the key entirely for todos with no severity line.
928
+ const severityMatch = content.match(/^severity:\s*(.+)$/m);
881
929
  const todoArea = areaMatch ? areaMatch[1].trim() : 'general';
882
930
  if (area && todoArea !== area)
883
931
  continue;
@@ -887,7 +935,9 @@ function cmdInitTodos(cwd, area, raw) {
887
935
  created: createdMatch ? createdMatch[1].trim() : 'unknown',
888
936
  title: titleMatch ? titleMatch[1].trim() : 'Untitled',
889
937
  area: todoArea,
890
- path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'todos', 'pending', file))),
938
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
939
+ path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'todos', 'pending', file)),
940
+ ...(severityMatch ? { severity: severityMatch[1].trim() } : {}),
891
941
  });
892
942
  }
893
943
  catch {
@@ -905,8 +955,9 @@ function cmdInitTodos(cwd, area, raw) {
905
955
  todo_count: count,
906
956
  todos,
907
957
  area_filter: area || null,
908
- pending_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'todos', 'pending'))),
909
- completed_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'todos', 'completed'))),
958
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
959
+ pending_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'todos', 'pending')),
960
+ completed_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'todos', 'completed')),
910
961
  planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
911
962
  todos_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'todos')),
912
963
  pending_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'todos', 'pending')),
@@ -1038,7 +1089,8 @@ function cmdInitMapCodebase(cwd, raw) {
1038
1089
  subagent_timeout: config.subagent_timeout,
1039
1090
  date: clock_cjs_1.realClock.localToday(),
1040
1091
  timestamp: clock_cjs_1.realClock.nowIso(),
1041
- codebase_dir: '.planning/codebase',
1092
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
1093
+ codebase_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'codebase')),
1042
1094
  existing_maps: existingMaps,
1043
1095
  has_maps: existingMaps.length > 0,
1044
1096
  planning_exists: pathExistsInternal(cwd, '.planning'),
@@ -1439,7 +1491,10 @@ function cmdInitProgress(cwd, raw) {
1439
1491
  const phaseInfo = {
1440
1492
  number: phaseNumber,
1441
1493
  name: phaseName,
1442
- directory: phaseDirRel,
1494
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
1495
+ // phaseDirRel itself stays relative — buildPhaseCompletionProjection
1496
+ // above still joins it against cwd.
1497
+ directory: toPosixPath(node_path_1.default.join(cwd, phaseDirRel)),
1443
1498
  status,
1444
1499
  plan_count: plans.length,
1445
1500
  summary_count: summaries.length,
@@ -1507,10 +1562,11 @@ function cmdInitProgress(cwd, raw) {
1507
1562
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
1508
1563
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
1509
1564
  state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
1510
- state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
1511
- roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
1512
- project_path: '.planning/PROJECT.md',
1513
- config_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'config.json'))),
1565
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
1566
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
1567
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
1568
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
1569
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
1514
1570
  };
1515
1571
  output(withProjectRoot(cwd, result), raw);
1516
1572
  }
@@ -1668,7 +1724,9 @@ function cmdInitRemoveWorkspace(cwd, name, raw) {
1668
1724
  dirty_repos: dirtyRepos,
1669
1725
  has_dirty_repos: dirtyRepos.length > 0,
1670
1726
  };
1671
- output(result, raw);
1727
+ // #2402: sibling init commands route through withProjectRoot so response_language
1728
+ // (and project_root/agents_installed) reach the workflow; this one didn't.
1729
+ output(withProjectRoot(cwd, result), raw);
1672
1730
  }
1673
1731
  function buildAgentSkillsBlock(config, agentType, projectRoot, diagnostics) {
1674
1732
  const warn = (message) => {
@@ -1790,7 +1848,38 @@ function cmdAgentSkills(cwd, agentType, raw, jsonMode) {
1790
1848
  const projectRoot = (0, project_root_cjs_1.findProjectRoot)(cwd);
1791
1849
  const { config, source, degraded } = loadConfigResolved(projectRoot);
1792
1850
  const diagnostics = { warnings: [] };
1793
- const block = buildAgentSkillsBlock(config, agentType, projectRoot, diagnostics);
1851
+ let block = buildAgentSkillsBlock(config, agentType, projectRoot, diagnostics);
1852
+ // #2454: Agent prompt fallback for AGENTS-native runtimes where named
1853
+ // subagents are NOT dispatchable (kimi-code, kimi, opencode, kilo, etc.).
1854
+ // On these runtimes, workflows inject ${AGENT_SKILLS_*} into the dispatch
1855
+ // prompt of a built-in subagent (coder/explore/plan). If no
1856
+ // model_profile_overrides or agent_skills config entry exists, the block
1857
+ // is empty — but the agent's prompt CONTENT is installed on disk at the
1858
+ // runtime's agents directory. Read it as a fallback so the persona survives
1859
+ // the dispatch even without explicit config opt-in.
1860
+ //
1861
+ // GATED to non-claude runtimes: Claude Code supports named subagent dispatch
1862
+ // and its ${AGENT_SKILLS_*} contract is a skills-injection path, not a
1863
+ // persona fallback. Triggering the fallback for claude would change the
1864
+ // documented "unconfigured → empty block" contract that agent-skills tests
1865
+ // pin.
1866
+ if (!block) {
1867
+ const runtime = (config && config['runtime']) || process.env['GSD_RUNTIME'] || 'claude';
1868
+ if (runtime !== 'claude') {
1869
+ const agentCheck = checkAgentsInstalled(runtime);
1870
+ const agentsDir = agentCheck?.agents_dir;
1871
+ if (typeof agentsDir === 'string' && agentsDir.length > 0) {
1872
+ const agentFile = node_path_1.default.join(agentsDir, `${agentType}.md`);
1873
+ try {
1874
+ const content = (0, shell_command_projection_cjs_1.platformReadSync)(agentFile);
1875
+ if (content && content.length > 0) {
1876
+ block = content;
1877
+ }
1878
+ }
1879
+ catch { /* agent file not found — fall through to empty block */ }
1880
+ }
1881
+ }
1882
+ }
1794
1883
  // Compute configured + reason for diagnostic output.
1795
1884
  const agentSkillsMap = (config && config['agent_skills'] && typeof config['agent_skills'] === 'object')
1796
1885
  ? config['agent_skills']
@@ -1989,8 +2078,8 @@ function buildSkillManifest(cwd, skillsDir = null) {
1989
2078
  // posix-style for cross-platform stability; flat entries use template
1990
2079
  // literals that always produce '/'; nested entries are joined below
1991
2080
  // with explicit '/' separators rather than path.join).
1992
- relPath, content) {
1993
- const frontmatter = extractFrontmatter(content);
2081
+ relPath, content, sourcePath) {
2082
+ const frontmatter = extractFrontmatter(content, sourcePath);
1994
2083
  const dirPart = relPath.replace(/\/SKILL\.md$/, '');
1995
2084
  const stem = dirPart.includes('/') ? dirPart.split('/').pop() : dirPart;
1996
2085
  const name = frontmatter['name'] || stem;
@@ -2031,7 +2120,7 @@ function buildSkillManifest(cwd, skillsDir = null) {
2031
2120
  const skillMdPath = node_path_1.default.join(rootPath, entry.name, 'SKILL.md');
2032
2121
  const content = (0, shell_command_projection_cjs_1.platformReadSync)(skillMdPath);
2033
2122
  if (content !== null) {
2034
- if (pushSkillEntry(`${entry.name}/SKILL.md`, content))
2123
+ if (pushSkillEntry(`${entry.name}/SKILL.md`, content, skillMdPath))
2035
2124
  skillCount++;
2036
2125
  }
2037
2126
  // Nested layout: <entry>/skills/<stem>/SKILL.md
@@ -2060,7 +2149,7 @@ function buildSkillManifest(cwd, skillsDir = null) {
2060
2149
  // Use forward-slash separator explicitly so manifest paths are posix-style
2061
2150
  // on all platforms, matching the flat-layout behaviour above.
2062
2151
  const relPath = `${entry.name}/skills/${nested.name}/SKILL.md`;
2063
- if (pushSkillEntry(relPath, nestedContent))
2152
+ if (pushSkillEntry(relPath, nestedContent, nestedSkillMd))
2064
2153
  skillCount++;
2065
2154
  }
2066
2155
  }