@opengsd/gsd-core 1.7.0 → 1.8.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 (165) 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 +14 -0
  4. package/README.md +2 -0
  5. package/agents/gsd-debug-session-manager.md +42 -4
  6. package/agents/gsd-debugger.md +87 -29
  7. package/agents/gsd-executor.md +29 -2
  8. package/agents/gsd-planner.md +29 -36
  9. package/agents/gsd-verifier.md +2 -2
  10. package/bin/install.js +1152 -80
  11. package/commands/gsd/ai-integration-phase.md +1 -1
  12. package/commands/gsd/mempalace-capture.md +9 -5
  13. package/commands/gsd/new-milestone.md +1 -1
  14. package/commands/gsd/plan-phase.md +5 -3
  15. package/commands/gsd/plan-review-convergence.md +3 -2
  16. package/gsd-core/bin/gsd-tools.cjs +1878 -2507
  17. package/gsd-core/bin/lib/adapter-imperative.cjs +8 -1
  18. package/gsd-core/bin/lib/agent-command-router.cjs +20 -5
  19. package/gsd-core/bin/lib/api-coverage.cjs +338 -45
  20. package/gsd-core/bin/lib/broken-windows.cjs +716 -0
  21. package/gsd-core/bin/lib/capability-command-router.cjs +733 -0
  22. package/gsd-core/bin/lib/capability-registry.cjs +155 -86
  23. package/gsd-core/bin/lib/capability-writer.cjs +6 -1
  24. package/gsd-core/bin/lib/check-command-router.cjs +128 -25
  25. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +115 -27
  26. package/gsd-core/bin/lib/claude-orchestration.cjs +84 -9
  27. package/gsd-core/bin/lib/command-aliases.cjs +14 -0
  28. package/gsd-core/bin/lib/commands.cjs +81 -4
  29. package/gsd-core/bin/lib/config-loader.cjs +14 -2
  30. package/gsd-core/bin/lib/config.cjs +69 -18
  31. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  32. package/gsd-core/bin/lib/decisions.cjs +32 -8
  33. package/gsd-core/bin/lib/docs.cjs +6 -0
  34. package/gsd-core/bin/lib/external-descriptor-trust.cjs +14 -2
  35. package/gsd-core/bin/lib/gap-checker.cjs +17 -2
  36. package/gsd-core/bin/lib/init.cjs +111 -47
  37. package/gsd-core/bin/lib/install-engine.cjs +298 -23
  38. package/gsd-core/bin/lib/install-profiles.cjs +239 -1
  39. package/gsd-core/bin/lib/installer-migrations/005-opencode-baseline-commands-dir.cjs +146 -0
  40. package/gsd-core/bin/lib/installer-migrations/006-pi-extension-cjs-to-js.cjs +91 -0
  41. package/gsd-core/bin/lib/installer-migrations.cjs +44 -5
  42. package/gsd-core/bin/lib/markdown-sectionizer.cjs +107 -0
  43. package/gsd-core/bin/lib/milestone.cjs +246 -12
  44. package/gsd-core/bin/lib/model-catalog.cjs +19 -4
  45. package/gsd-core/bin/lib/model-resolver.cjs +189 -7
  46. package/gsd-core/bin/lib/onboard-projection.cjs +11 -8
  47. package/gsd-core/bin/lib/phase-id.cjs +26 -4
  48. package/gsd-core/bin/lib/phase.cjs +201 -12
  49. package/gsd-core/bin/lib/plan-scan.cjs +70 -2
  50. package/gsd-core/bin/lib/roadmap-parser.cjs +7 -4
  51. package/gsd-core/bin/lib/roadmap.cjs +13 -3
  52. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +7 -1
  53. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +22 -8
  54. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +16 -0
  55. package/gsd-core/bin/lib/smart-entry.cjs +69 -4
  56. package/gsd-core/bin/lib/state-document.cjs +7 -4
  57. package/gsd-core/bin/lib/state-transition.cjs +22 -1
  58. package/gsd-core/bin/lib/state.cjs +65 -11
  59. package/gsd-core/bin/lib/surface.cjs +51 -9
  60. package/gsd-core/bin/lib/uat.cjs +420 -5
  61. package/gsd-core/bin/lib/validate.cjs +12 -8
  62. package/gsd-core/bin/lib/verification.cjs +112 -17
  63. package/gsd-core/bin/lib/verify.cjs +220 -22
  64. package/gsd-core/bin/shared/config-schema.manifest.json +3 -2
  65. package/gsd-core/references/api-coverage.md +37 -7
  66. package/gsd-core/references/checkpoints.md +1 -1
  67. package/gsd-core/references/common-bug-patterns.md +13 -0
  68. package/gsd-core/references/debugger-bug-taxonomy.md +111 -0
  69. package/gsd-core/references/debugger-fix-acceptance.md +157 -0
  70. package/gsd-core/references/debugger-philosophy.md +1 -0
  71. package/gsd-core/references/debugger-prevention.md +98 -0
  72. package/gsd-core/references/debugger-rca-branching.md +98 -0
  73. package/gsd-core/references/debugger-repro-hardening.md +130 -0
  74. package/gsd-core/references/debugger-sbfl.md +110 -0
  75. package/gsd-core/references/debugger-semantic-recall.md +81 -0
  76. package/gsd-core/references/execute-phase-quota-recovery.md +55 -0
  77. package/gsd-core/references/execute-phase-requirement-revert.md +8 -0
  78. package/gsd-core/references/execute-phase-response-language.md +7 -0
  79. package/gsd-core/references/planner-antipatterns.md +6 -0
  80. package/gsd-core/references/planner-mvp-mode.md +12 -13
  81. package/gsd-core/references/planner-preconditions.md +156 -0
  82. package/gsd-core/references/planner-reversibility.md +132 -0
  83. package/gsd-core/references/reviewer-instances.md +9 -7
  84. package/gsd-core/references/skeleton-template.md +1 -1
  85. package/gsd-core/references/thinking-models-planning.md +3 -1
  86. package/gsd-core/templates/DEBUG.md +5 -3
  87. package/gsd-core/workflows/add-phase.md +2 -0
  88. package/gsd-core/workflows/add-tests.md +3 -1
  89. package/gsd-core/workflows/add-todo.md +32 -1
  90. package/gsd-core/workflows/ai-integration-phase.md +4 -2
  91. package/gsd-core/workflows/audit-fix.md +2 -2
  92. package/gsd-core/workflows/check-todos.md +3 -1
  93. package/gsd-core/workflows/cleanup.md +7 -1
  94. package/gsd-core/workflows/code-review.md +17 -5
  95. package/gsd-core/workflows/complete-milestone.md +3 -0
  96. package/gsd-core/workflows/debug.md +25 -5
  97. package/gsd-core/workflows/diagnose-issues.md +1 -1
  98. package/gsd-core/workflows/discovery-phase.md +7 -0
  99. package/gsd-core/workflows/discuss-phase/templates/context.md +16 -2
  100. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -0
  101. package/gsd-core/workflows/do.md +7 -1
  102. package/gsd-core/workflows/docs-update.md +1 -0
  103. package/gsd-core/workflows/eval-review.md +3 -0
  104. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +4 -4
  105. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -2
  106. package/gsd-core/workflows/execute-phase.md +25 -34
  107. package/gsd-core/workflows/execute-plan.md +15 -4
  108. package/gsd-core/workflows/graduation.md +3 -0
  109. package/gsd-core/workflows/health.md +7 -1
  110. package/gsd-core/workflows/help/modes/full.md +6 -2
  111. package/gsd-core/workflows/import.md +8 -2
  112. package/gsd-core/workflows/inbox.md +7 -0
  113. package/gsd-core/workflows/ingest-docs.md +15 -10
  114. package/gsd-core/workflows/manager.md +3 -1
  115. package/gsd-core/workflows/map-codebase.md +4 -4
  116. package/gsd-core/workflows/mvp-phase.md +3 -0
  117. package/gsd-core/workflows/new-milestone.md +69 -21
  118. package/gsd-core/workflows/new-project.md +17 -15
  119. package/gsd-core/workflows/new-workspace.md +3 -1
  120. package/gsd-core/workflows/onboard.md +3 -0
  121. package/gsd-core/workflows/plan-phase.md +14 -5
  122. package/gsd-core/workflows/plan-review-convergence.md +48 -3
  123. package/gsd-core/workflows/plant-seed.md +3 -0
  124. package/gsd-core/workflows/profile-user.md +7 -1
  125. package/gsd-core/workflows/progress.md +31 -3
  126. package/gsd-core/workflows/quick.md +19 -7
  127. package/gsd-core/workflows/remove-workspace.md +3 -0
  128. package/gsd-core/workflows/review.md +89 -73
  129. package/gsd-core/workflows/scan.md +1 -1
  130. package/gsd-core/workflows/secure-phase.md +3 -0
  131. package/gsd-core/workflows/settings-integrations.md +3 -0
  132. package/gsd-core/workflows/settings.md +3 -0
  133. package/gsd-core/workflows/ship.md +50 -3
  134. package/gsd-core/workflows/sketch.md +3 -0
  135. package/gsd-core/workflows/smart-entry.md +3 -0
  136. package/gsd-core/workflows/spike.md +7 -1
  137. package/gsd-core/workflows/ui-phase.md +3 -1
  138. package/gsd-core/workflows/ui-review.md +3 -0
  139. package/gsd-core/workflows/undo.md +7 -0
  140. package/gsd-core/workflows/update.md +2 -0
  141. package/gsd-core/workflows/validate-phase.md +3 -0
  142. package/gsd-core/workflows/verify-phase.md +2 -2
  143. package/gsd-core/workflows/verify-work.md +7 -3
  144. package/hooks/dist/gsd-context-monitor.js +27 -9
  145. package/hooks/dist/gsd-statusline.js +88 -3
  146. package/hooks/gsd-context-monitor.js +27 -9
  147. package/hooks/gsd-statusline.js +88 -3
  148. package/package.json +6 -4
  149. package/pi/gsd.cjs +8 -2
  150. package/scripts/changeset/lint.cjs +1 -0
  151. package/scripts/changeset/parse.cjs +26 -0
  152. package/scripts/check-glossary-refs.cjs +220 -0
  153. package/scripts/ci-rebase-check.cjs +48 -4
  154. package/scripts/gen-adr-index.cjs +526 -0
  155. package/scripts/gen-test-timings.cjs +201 -0
  156. package/scripts/lint-portable-timeout.cjs +140 -0
  157. package/scripts/lint-test-file-count.allowlist.json +1 -0
  158. package/scripts/release-tarball-smoke.cjs +18 -11
  159. package/scripts/run-tests.cjs +420 -58
  160. package/skills/gsd-ai-integration-phase/SKILL.md +1 -1
  161. package/skills/gsd-mempalace-capture/SKILL.md +9 -5
  162. package/skills/gsd-new-milestone/SKILL.md +1 -1
  163. package/skills/gsd-plan-phase/SKILL.md +5 -3
  164. package/skills/gsd-plan-review-convergence/SKILL.md +3 -2
  165. package/vscode/package.json +1 -1
@@ -305,7 +305,11 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
305
305
  milestone_branch_template: config.milestone_branch_template,
306
306
  verifier_enabled: config.verifier,
307
307
  phase_found: !!phaseInfo,
308
- phase_dir: phaseInfo?.['directory'] || null,
308
+ // #2376: absolute (anchored on cwd/project_root), not orchestrator-cwd-relative —
309
+ // a spawned subagent's own cwd may differ from the orchestrator's.
310
+ phase_dir: phaseInfo?.['directory']
311
+ ? toPosixPath(node_path_1.default.join(cwd, phaseInfo['directory']))
312
+ : null,
309
313
  phase_number: phaseInfo?.['phase_number'] || null,
310
314
  phase_name: phaseInfo?.['phase_name'] || null,
311
315
  phase_slug: phaseInfo?.['phase_slug'] || null,
@@ -331,9 +335,13 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
331
335
  state_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
332
336
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
333
337
  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'))),
338
+ // #2376: emit absolute paths — see comment above on phase_dir.
339
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
340
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
341
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
342
+ // #2376: execute-phase.md's verify_phase_goal step reads this instead of
343
+ // hardcoding '.planning/REQUIREMENTS.md' into the gsd-verifier spawn prompt.
344
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
337
345
  };
338
346
  if (options['validate']) {
339
347
  try {
@@ -405,7 +413,8 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
405
413
  if (slug) {
406
414
  const prefix = rawProjectCodePlan ? `${rawProjectCodePlan}-` : '';
407
415
  const dirName = `${prefix}${paddedNum}-${slug}`;
408
- expectedPhaseDirPlan = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningPaths(cwd).phases, dirName)));
416
+ // #2376: absolute — see comment on phase_dir below.
417
+ expectedPhaseDirPlan = toPosixPath(node_path_1.default.join(planningPaths(cwd).phases, dirName));
409
418
  }
410
419
  }
411
420
  const granularityOverride = options['granularity'];
@@ -427,7 +436,10 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
427
436
  auto_chain_active: !!(config._auto_chain_active),
428
437
  mode: config.mode || 'interactive',
429
438
  phase_found: !!phaseInfo,
430
- phase_dir: phaseDirPlan,
439
+ // #2376: absolute (anchored on cwd/project_root) — path.join(cwd, phaseDirPlan)
440
+ // handed to a spawned subagent must resolve regardless of that subagent's own cwd.
441
+ // phaseDirPlan itself stays relative — phase_status below still joins it against cwd.
442
+ phase_dir: phaseDirPlan ? toPosixPath(node_path_1.default.join(cwd, phaseDirPlan)) : null,
431
443
  expected_phase_dir: expectedPhaseDirPlan,
432
444
  phase_number: phaseNumberPlan,
433
445
  phase_name: phaseNamePlan,
@@ -444,9 +456,10 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
444
456
  plan_count: phaseInfo?.['plans']?.length || 0,
445
457
  planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
446
458
  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'))),
459
+ // #2376: absolute — see comment on phase_dir above.
460
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
461
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
462
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
450
463
  patterns_path: null,
451
464
  };
452
465
  if (phaseInfo?.['directory']) {
@@ -455,27 +468,27 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
455
468
  const files = node_fs_1.default.readdirSync(phaseDirFull);
456
469
  const contextFile = findContextMdIn(phaseDirFull);
457
470
  if (contextFile) {
458
- result['context_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], contextFile));
471
+ result['context_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, contextFile));
459
472
  }
460
473
  const researchFile = files.find((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
461
474
  if (researchFile) {
462
- result['research_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], researchFile));
475
+ result['research_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, researchFile));
463
476
  }
464
477
  const verificationFile = files.find((f) => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
465
478
  if (verificationFile) {
466
- result['verification_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], verificationFile));
479
+ result['verification_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, verificationFile));
467
480
  }
468
481
  const uatFile = files.find((f) => f.endsWith('-UAT.md') || f === 'UAT.md');
469
482
  if (uatFile) {
470
- result['uat_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], uatFile));
483
+ result['uat_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, uatFile));
471
484
  }
472
485
  const reviewsFile = files.find((f) => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md');
473
486
  if (reviewsFile) {
474
- result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], reviewsFile));
487
+ result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, reviewsFile));
475
488
  }
476
489
  const patternsFile = files.find((f) => f.endsWith('-PATTERNS.md') || f === 'PATTERNS.md');
477
490
  if (patternsFile) {
478
- result['patterns_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], patternsFile));
491
+ result['patterns_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, patternsFile));
479
492
  }
480
493
  }
481
494
  catch {
@@ -536,7 +549,14 @@ function cmdInitNewProject(cwd, raw) {
536
549
  brave_search_available: hasBraveSearch,
537
550
  firecrawl_available: hasFirecrawl,
538
551
  exa_search_available: hasExaSearch,
539
- project_path: '.planning/PROJECT.md',
552
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
553
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
554
+ // #2376: new-project.md's research-synthesizer/roadmapper spawn prompts
555
+ // read these instead of hardcoding '.planning/...' literals.
556
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
557
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
558
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
559
+ research_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'research')),
540
560
  };
541
561
  output(withProjectRoot(cwd, result), raw);
542
562
  }
@@ -570,15 +590,22 @@ function cmdInitNewMilestone(cwd, raw) {
570
590
  latest_completed_milestone: latestCompleted?.version || null,
571
591
  latest_completed_milestone_name: latestCompleted?.name || null,
572
592
  phase_dir_count: phaseDirCount,
593
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
573
594
  phase_archive_path: latestCompleted
574
- ? toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningRoot(cwd), 'milestones', `${latestCompleted.version}-phases`)))
595
+ ? toPosixPath(node_path_1.default.join(planningRoot(cwd), 'milestones', `${latestCompleted.version}-phases`))
575
596
  : null,
576
597
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
577
598
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
578
599
  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'))),
600
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
601
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
602
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
603
+ // #2376: new-milestone.md's research-synthesizer/roadmapper spawn prompts
604
+ // read these instead of hardcoding '.planning/...' literals.
605
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
606
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
607
+ research_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'research')),
608
+ milestones_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'MILESTONES.md')),
582
609
  };
583
610
  output(withProjectRoot(cwd, result), raw);
584
611
  }
@@ -616,8 +643,11 @@ function cmdInitQuick(cwd, description, raw) {
616
643
  description: description || null,
617
644
  date: clock_cjs_1.realClock.localToday(),
618
645
  timestamp: clock_cjs_1.realClock.nowIso(),
619
- quick_dir: '.planning/quick',
620
- task_dir: slug ? `.planning/quick/${quickId}-${slug}` : null,
646
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
647
+ quick_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'quick')),
648
+ task_dir: slug
649
+ ? toPosixPath(node_path_1.default.join(planningDir(cwd), 'quick', `${quickId}-${slug}`))
650
+ : null,
621
651
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
622
652
  planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
623
653
  };
@@ -629,7 +659,18 @@ function cmdInitIngestDocs(cwd, raw) {
629
659
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
630
660
  planning_exists: node_fs_1.default.existsSync(planningRoot(cwd)),
631
661
  ...getInitGitState(cwd),
632
- project_path: '.planning/PROJECT.md',
662
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase. The
663
+ // classify_parallel/synthesize/route_new_mode spawns in ingest-docs.md
664
+ // (gsd-doc-classifier, gsd-doc-synthesizer, gsd-roadmapper) previously
665
+ // hardcoded bare '.planning/intel/...', '.planning/PROJECT.md', etc.
666
+ // literals into their Agent(prompt=...) blocks; those now interpolate
667
+ // these fields instead.
668
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
669
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
670
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
671
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
672
+ intel_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'intel')),
673
+ conflicts_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'INGEST-CONFLICTS.md')),
633
674
  commit_docs: config.commit_docs,
634
675
  };
635
676
  output(withProjectRoot(cwd, result), raw);
@@ -658,9 +699,10 @@ function cmdInitResume(cwd, raw) {
658
699
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
659
700
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
660
701
  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',
702
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
703
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
704
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
705
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
664
706
  has_interrupted_agent: !!interruptedAgentId,
665
707
  interrupted_agent_id: interruptedAgentId,
666
708
  commit_docs: config.commit_docs,
@@ -715,9 +757,15 @@ function cmdInitVerifyWork(cwd, phase, raw) {
715
757
  checker_model: resolveModelInternal(cwd, 'gsd-plan-checker'),
716
758
  commit_docs: config.commit_docs,
717
759
  phase_found: !!phaseInfo,
718
- phase_dir: phaseDir,
760
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase. phaseDir
761
+ // itself stays relative — evaluateUatPassed above still joins it against cwd.
762
+ phase_dir: phaseDir ? toPosixPath(node_path_1.default.join(cwd, phaseDir)) : null,
719
763
  phase_number: phaseInfo?.['phase_number'] || null,
720
764
  phase_name: phaseInfo?.['phase_name'] || null,
765
+ // #2376: verify-work.md's plan_gap_closure step reads these instead of
766
+ // hardcoding '.planning/STATE.md' / '.planning/ROADMAP.md' literals.
767
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
768
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
721
769
  has_verification: phaseInfo?.['has_verification'] || false,
722
770
  phase_completion: {
723
771
  ...completion,
@@ -799,7 +847,8 @@ function cmdInitPhaseOp(cwd, phase, raw) {
799
847
  if (slug) {
800
848
  const prefix = rawProjectCode ? `${rawProjectCode}-` : '';
801
849
  const dirName = `${prefix}${paddedNum}-${slug}`;
802
- expectedPhaseDir = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningPaths(cwd).phases, dirName)));
850
+ // #2376: absolute — see comment on phase_dir below.
851
+ expectedPhaseDir = toPosixPath(node_path_1.default.join(planningPaths(cwd).phases, dirName));
803
852
  }
804
853
  }
805
854
  const result = {
@@ -814,7 +863,8 @@ function cmdInitPhaseOp(cwd, phase, raw) {
814
863
  ? (0, secrets_cjs_1.maskIfSecret)('exa_search', config.exa_search)
815
864
  : config.exa_search,
816
865
  phase_found: !!phaseInfo,
817
- phase_dir: phaseDir,
866
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
867
+ phase_dir: phaseDir ? toPosixPath(node_path_1.default.join(cwd, phaseDir)) : null,
818
868
  expected_phase_dir: expectedPhaseDir,
819
869
  phase_number: phaseNumber,
820
870
  phase_name: phaseName,
@@ -828,9 +878,10 @@ function cmdInitPhaseOp(cwd, phase, raw) {
828
878
  plan_count: phaseInfo?.['plans']?.length || 0,
829
879
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
830
880
  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'))),
881
+ // #2376: absolute — see comment on phase_dir above.
882
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
883
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
884
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
834
885
  };
835
886
  if (phaseInfo?.['directory']) {
836
887
  const phaseDirFull = node_path_1.default.join(cwd, phaseInfo['directory']);
@@ -838,23 +889,23 @@ function cmdInitPhaseOp(cwd, phase, raw) {
838
889
  const files = node_fs_1.default.readdirSync(phaseDirFull);
839
890
  const contextFile = findContextMdIn(phaseDirFull);
840
891
  if (contextFile) {
841
- result['context_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], contextFile));
892
+ result['context_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, contextFile));
842
893
  }
843
894
  const researchFile = files.find((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
844
895
  if (researchFile) {
845
- result['research_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], researchFile));
896
+ result['research_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, researchFile));
846
897
  }
847
898
  const verificationFile = files.find((f) => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
848
899
  if (verificationFile) {
849
- result['verification_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], verificationFile));
900
+ result['verification_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, verificationFile));
850
901
  }
851
902
  const uatFile = files.find((f) => f.endsWith('-UAT.md') || f === 'UAT.md');
852
903
  if (uatFile) {
853
- result['uat_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], uatFile));
904
+ result['uat_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, uatFile));
854
905
  }
855
906
  const reviewsFile = files.find((f) => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md');
856
907
  if (reviewsFile) {
857
- result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseInfo['directory'], reviewsFile));
908
+ result['reviews_path'] = toPosixPath(node_path_1.default.join(phaseDirFull, reviewsFile));
858
909
  }
859
910
  }
860
911
  catch {
@@ -878,6 +929,9 @@ function cmdInitTodos(cwd, area, raw) {
878
929
  const createdMatch = content.match(/^created:\s*(.+)$/m);
879
930
  const titleMatch = content.match(/^title:\s*(.+)$/m);
880
931
  const areaMatch = content.match(/^area:\s*(.+)$/m);
932
+ // #2337: kept in parity with cmdListTodos — surface severity when
933
+ // present, omit the key entirely for todos with no severity line.
934
+ const severityMatch = content.match(/^severity:\s*(.+)$/m);
881
935
  const todoArea = areaMatch ? areaMatch[1].trim() : 'general';
882
936
  if (area && todoArea !== area)
883
937
  continue;
@@ -887,7 +941,9 @@ function cmdInitTodos(cwd, area, raw) {
887
941
  created: createdMatch ? createdMatch[1].trim() : 'unknown',
888
942
  title: titleMatch ? titleMatch[1].trim() : 'Untitled',
889
943
  area: todoArea,
890
- path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'todos', 'pending', file))),
944
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
945
+ path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'todos', 'pending', file)),
946
+ ...(severityMatch ? { severity: severityMatch[1].trim() } : {}),
891
947
  });
892
948
  }
893
949
  catch {
@@ -905,8 +961,9 @@ function cmdInitTodos(cwd, area, raw) {
905
961
  todo_count: count,
906
962
  todos,
907
963
  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'))),
964
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
965
+ pending_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'todos', 'pending')),
966
+ completed_dir: toPosixPath(node_path_1.default.join(planningDir(cwd), 'todos', 'completed')),
910
967
  planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
911
968
  todos_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'todos')),
912
969
  pending_dir_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'todos', 'pending')),
@@ -1038,7 +1095,8 @@ function cmdInitMapCodebase(cwd, raw) {
1038
1095
  subagent_timeout: config.subagent_timeout,
1039
1096
  date: clock_cjs_1.realClock.localToday(),
1040
1097
  timestamp: clock_cjs_1.realClock.nowIso(),
1041
- codebase_dir: '.planning/codebase',
1098
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
1099
+ codebase_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'codebase')),
1042
1100
  existing_maps: existingMaps,
1043
1101
  has_maps: existingMaps.length > 0,
1044
1102
  planning_exists: pathExistsInternal(cwd, '.planning'),
@@ -1439,7 +1497,10 @@ function cmdInitProgress(cwd, raw) {
1439
1497
  const phaseInfo = {
1440
1498
  number: phaseNumber,
1441
1499
  name: phaseName,
1442
- directory: phaseDirRel,
1500
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
1501
+ // phaseDirRel itself stays relative — buildPhaseCompletionProjection
1502
+ // above still joins it against cwd.
1503
+ directory: toPosixPath(node_path_1.default.join(cwd, phaseDirRel)),
1443
1504
  status,
1444
1505
  plan_count: plans.length,
1445
1506
  summary_count: summaries.length,
@@ -1507,10 +1568,11 @@ function cmdInitProgress(cwd, raw) {
1507
1568
  project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
1508
1569
  roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
1509
1570
  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'))),
1571
+ // #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
1572
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
1573
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
1574
+ project_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'PROJECT.md')),
1575
+ config_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'config.json')),
1514
1576
  };
1515
1577
  output(withProjectRoot(cwd, result), raw);
1516
1578
  }
@@ -1668,7 +1730,9 @@ function cmdInitRemoveWorkspace(cwd, name, raw) {
1668
1730
  dirty_repos: dirtyRepos,
1669
1731
  has_dirty_repos: dirtyRepos.length > 0,
1670
1732
  };
1671
- output(result, raw);
1733
+ // #2402: sibling init commands route through withProjectRoot so response_language
1734
+ // (and project_root/agents_installed) reach the workflow; this one didn't.
1735
+ output(withProjectRoot(cwd, result), raw);
1672
1736
  }
1673
1737
  function buildAgentSkillsBlock(config, agentType, projectRoot, diagnostics) {
1674
1738
  const warn = (message) => {