@opengsd/gsd-core 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) 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 +31 -1
  4. package/agents/gsd-code-fixer.md +107 -34
  5. package/agents/gsd-codebase-mapper.md +1 -1
  6. package/agents/gsd-debug-session-manager.md +36 -0
  7. package/agents/gsd-executor.md +20 -7
  8. package/agents/gsd-intel-updater.md +3 -3
  9. package/agents/gsd-phase-researcher.md +4 -2
  10. package/agents/gsd-plan-checker.md +20 -0
  11. package/agents/gsd-planner.md +15 -23
  12. package/agents/gsd-project-researcher.md +2 -2
  13. package/agents/gsd-ui-auditor.md +0 -40
  14. package/bin/install.js +236 -107
  15. package/commands/gsd/plan-review-convergence.md +5 -1
  16. package/gsd-core/bin/gsd-tools.cjs +882 -4
  17. package/gsd-core/bin/lib/api-coverage.cjs +22 -8
  18. package/gsd-core/bin/lib/audit.cjs +8 -8
  19. package/gsd-core/bin/lib/capability-consent.cjs +40 -1
  20. package/gsd-core/bin/lib/capability-lifecycle.cjs +58 -0
  21. package/gsd-core/bin/lib/capability-loader.cjs +23 -1
  22. package/gsd-core/bin/lib/capability-registry.cjs +1353 -132
  23. package/gsd-core/bin/lib/capability-trust.cjs +468 -33
  24. package/gsd-core/bin/lib/capability-validator.cjs +882 -6
  25. package/gsd-core/bin/lib/check-command-router.cjs +12 -2
  26. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +15 -0
  27. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +102 -12
  28. package/gsd-core/bin/lib/claude-orchestration.cjs +125 -22
  29. package/gsd-core/bin/lib/commands.cjs +246 -18
  30. package/gsd-core/bin/lib/config-loader.cjs +200 -28
  31. package/gsd-core/bin/lib/config.cjs +90 -5
  32. package/gsd-core/bin/lib/estimate-cli.cjs +336 -0
  33. package/gsd-core/bin/lib/frontmatter.cjs +125 -15
  34. package/gsd-core/bin/lib/host-integration.cjs +215 -8
  35. package/gsd-core/bin/lib/init.cjs +44 -19
  36. package/gsd-core/bin/lib/install-engine.cjs +1 -0
  37. package/gsd-core/bin/lib/milestone.cjs +36 -9
  38. package/gsd-core/bin/lib/model-catalog.cjs +51 -1
  39. package/gsd-core/bin/lib/observability/logger.cjs +7 -2
  40. package/gsd-core/bin/lib/phase-command-router.cjs +10 -1
  41. package/gsd-core/bin/lib/phase-estimation.cjs +398 -0
  42. package/gsd-core/bin/lib/phase-id.cjs +278 -5
  43. package/gsd-core/bin/lib/phase.cjs +61 -6
  44. package/gsd-core/bin/lib/plan-drift-guard.cjs +1 -1
  45. package/gsd-core/bin/lib/plan-scan.cjs +1 -1
  46. package/gsd-core/bin/lib/planning-workspace.cjs +9 -2
  47. package/gsd-core/bin/lib/profile-output.cjs +34 -8
  48. package/gsd-core/bin/lib/project-root.cjs +48 -0
  49. package/gsd-core/bin/lib/review-lane-descriptor.cjs +927 -0
  50. package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
  51. package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
  52. package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
  53. package/gsd-core/bin/lib/roadmap-parser.cjs +54 -6
  54. package/gsd-core/bin/lib/roadmap.cjs +10 -4
  55. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +31 -4
  56. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +1 -1
  57. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +140 -0
  58. package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
  59. package/gsd-core/bin/lib/smart-entry.cjs +1 -1
  60. package/gsd-core/bin/lib/state-document.cjs +164 -20
  61. package/gsd-core/bin/lib/state-transition.cjs +28 -10
  62. package/gsd-core/bin/lib/state.cjs +141 -21
  63. package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
  64. package/gsd-core/bin/lib/uat.cjs +9 -7
  65. package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
  66. package/gsd-core/bin/lib/unusable-input.cjs +216 -0
  67. package/gsd-core/bin/lib/validate.cjs +32 -0
  68. package/gsd-core/bin/lib/verification.cjs +51 -14
  69. package/gsd-core/bin/lib/verify.cjs +146 -22
  70. package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
  71. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  72. package/gsd-core/bin/shared/config-schema.manifest.json +1 -13
  73. package/gsd-core/bin/shared/model-catalog.json +5 -0
  74. package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
  75. package/gsd-core/references/context-budget.md +40 -0
  76. package/gsd-core/references/gate-prompts.md +6 -3
  77. package/gsd-core/references/model-profile-resolution.md +64 -13
  78. package/gsd-core/references/offer-next.md +88 -0
  79. package/gsd-core/references/planning-config.md +2 -1
  80. package/gsd-core/references/reviewer-instances.md +28 -21
  81. package/gsd-core/references/runtime-aware-dispatch.md +42 -0
  82. package/gsd-core/references/ui-consideration-probe.md +2 -2
  83. package/gsd-core/references/worktree-branch-check.md +4 -4
  84. package/gsd-core/templates/summary-minimal.md +4 -0
  85. package/gsd-core/templates/summary-standard.md +4 -0
  86. package/gsd-core/templates/summary.md +7 -0
  87. package/gsd-core/workflows/ai-integration-phase.md +4 -4
  88. package/gsd-core/workflows/audit-fix.md +4 -0
  89. package/gsd-core/workflows/audit-milestone.md +8 -0
  90. package/gsd-core/workflows/autonomous.md +19 -15
  91. package/gsd-core/workflows/check-todos.md +2 -2
  92. package/gsd-core/workflows/code-review-fix.md +14 -6
  93. package/gsd-core/workflows/code-review.md +93 -21
  94. package/gsd-core/workflows/debug.md +10 -2
  95. package/gsd-core/workflows/diagnose-issues.md +4 -0
  96. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
  97. package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
  98. package/gsd-core/workflows/discuss-phase-assumptions.md +15 -9
  99. package/gsd-core/workflows/discuss-phase.md +2 -2
  100. package/gsd-core/workflows/docs-update.md +8 -0
  101. package/gsd-core/workflows/eval-review.md +1 -1
  102. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
  103. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
  104. package/gsd-core/workflows/execute-phase.md +85 -115
  105. package/gsd-core/workflows/execute-plan.md +5 -4
  106. package/gsd-core/workflows/explore.md +4 -0
  107. package/gsd-core/workflows/extract-learnings.md +21 -0
  108. package/gsd-core/workflows/help/modes/full.md +3 -3
  109. package/gsd-core/workflows/import.md +4 -1
  110. package/gsd-core/workflows/ingest-docs.md +4 -0
  111. package/gsd-core/workflows/map-codebase.md +13 -6
  112. package/gsd-core/workflows/new-milestone.md +10 -2
  113. package/gsd-core/workflows/new-project.md +11 -4
  114. package/gsd-core/workflows/next.md +5 -2
  115. package/gsd-core/workflows/plan-phase.md +42 -46
  116. package/gsd-core/workflows/plan-review-convergence.md +18 -14
  117. package/gsd-core/workflows/progress.md +1 -1
  118. package/gsd-core/workflows/quick.md +14 -3
  119. package/gsd-core/workflows/review.md +146 -575
  120. package/gsd-core/workflows/scan.md +9 -1
  121. package/gsd-core/workflows/secure-phase.md +10 -2
  122. package/gsd-core/workflows/ship.md +41 -11
  123. package/gsd-core/workflows/smart-entry.md +1 -1
  124. package/gsd-core/workflows/ui-phase.md +8 -1
  125. package/gsd-core/workflows/ui-review.md +8 -1
  126. package/gsd-core/workflows/update.md +104 -5
  127. package/gsd-core/workflows/validate-phase.md +10 -2
  128. package/gsd-core/workflows/verify-work.md +8 -1
  129. package/hooks/dist/gsd-cursor-session-start.js +6 -2
  130. package/hooks/dist/gsd-cursor-stop.js +6 -2
  131. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  132. package/hooks/dist/gsd-graphify-update.sh +9 -0
  133. package/hooks/dist/gsd-phase-boundary.sh +14 -2
  134. package/hooks/dist/gsd-prompt-guard.js +101 -2
  135. package/hooks/dist/gsd-read-guard.js +100 -2
  136. package/hooks/dist/gsd-read-injection-scanner.js +109 -2
  137. package/hooks/dist/gsd-statusline.js +9 -6
  138. package/hooks/dist/gsd-workflow-guard.js +110 -6
  139. package/hooks/dist/gsd-worktree-path-guard.js +132 -8
  140. package/hooks/dist/lib/cursor-workspace.js +74 -0
  141. package/hooks/gsd-cursor-session-start.js +6 -2
  142. package/hooks/gsd-cursor-stop.js +6 -2
  143. package/hooks/gsd-cursor-subagent-start.js +6 -2
  144. package/hooks/gsd-graphify-update.sh +9 -0
  145. package/hooks/gsd-phase-boundary.sh +14 -2
  146. package/hooks/gsd-prompt-guard.js +101 -2
  147. package/hooks/gsd-read-guard.js +100 -2
  148. package/hooks/gsd-read-injection-scanner.js +109 -2
  149. package/hooks/gsd-statusline.js +9 -6
  150. package/hooks/gsd-workflow-guard.js +110 -6
  151. package/hooks/gsd-worktree-path-guard.js +132 -8
  152. package/hooks/lib/cursor-workspace.js +74 -0
  153. package/package.json +7 -7
  154. package/pi/gsd.cjs +26 -1
  155. package/scripts/check-coverage-gate.cjs +51 -0
  156. package/scripts/check-glossary-refs.cjs +24 -0
  157. package/scripts/ci-test-scope.cjs +67 -17
  158. package/scripts/gen-adr-index.cjs +6 -4
  159. package/scripts/gen-capability-matrix.cjs +26 -2
  160. package/scripts/gen-capability-registry.cjs +132 -34
  161. package/scripts/gen-emitted-baseline.cjs +145 -0
  162. package/scripts/gen-registry.cjs +39 -15
  163. package/scripts/lint-compiled-artifact-sync.cjs +146 -0
  164. package/scripts/lint-emitted-drift-ack.cjs +149 -0
  165. package/scripts/lint-fix-has-regression-test.cjs +131 -0
  166. package/scripts/lint-resolution-provenance.cjs +9 -0
  167. package/scripts/mutation-matrix.cjs +4 -0
  168. package/scripts/prompt-injection-scan.sh +6 -0
  169. package/scripts/registry-schema.cjs +372 -94
  170. package/scripts/release-notes/conventional-title.cjs +19 -1
  171. package/scripts/release-notes/format-github-release-notes.cjs +7 -3
  172. package/scripts/validate-registry.cjs +10 -6
  173. package/scripts/workflow-size.cjs +16 -8
  174. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  175. package/vscode/package.json +1 -1
  176. package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
  177. package/scripts/update-size-baseline.cjs +0 -68
package/bin/install.js CHANGED
@@ -325,7 +325,13 @@ const GSD_WINDSURF_HOOK_SCRIPTS = [
325
325
 
326
326
  // GSD-managed files under hooks/lib/ (helpers required by gsd-*.sh hooks).
327
327
  // git-cmd.js does not start with "gsd-" (shared classifier for #3129), gsd-graphify-rebuild.sh does.
328
- const GSD_HOOK_LIB_FILES = ['git-cmd.js', 'gsd-graphify-rebuild.sh'];
328
+ // cursor-workspace.js (#2587) is required by the Cursor lifecycle hooks. Those
329
+ // are staged individually by writeCursorHooksJson (Cursor sets
330
+ // hostBehaviors.skipSharedHooksInstall, so it never reaches the bulk hooks/lib
331
+ // copy below) — that function stages this helper alongside them. Listing it
332
+ // here keeps uninstall and the manifest managing it for every OTHER runtime
333
+ // that does receive hooks/lib.
334
+ const GSD_HOOK_LIB_FILES = ['git-cmd.js', 'gsd-graphify-rebuild.sh', 'cursor-workspace.js'];
329
335
 
330
336
  const CODEX_AGENT_SANDBOX = {
331
337
  'gsd-executor': 'workspace-write',
@@ -627,7 +633,7 @@ if (hasMinimal && _profileArgRaw) {
627
633
 
628
634
  function selectRuntimesFromArgs(runtimeArgs) {
629
635
  if (runtimeArgs.includes('--all')) {
630
- return ['claude', 'kimi', 'kilo', 'opencode', 'pi', 'codex', 'copilot', 'antigravity', 'cursor', 'windsurf', 'augment', 'trae', 'qwen', 'hermes', 'codebuddy', 'cline', 'zcode'];
636
+ return ['claude', 'kimi', 'kimi-code', 'kilo', 'opencode', 'pi', 'codex', 'copilot', 'antigravity', 'cursor', 'windsurf', 'augment', 'trae', 'qwen', 'hermes', 'codebuddy', 'cline', 'zcode'];
631
637
  }
632
638
  if (runtimeArgs.includes('--both')) {
633
639
  return ['claude', 'opencode'];
@@ -648,6 +654,7 @@ function selectRuntimesFromArgs(runtimeArgs) {
648
654
  if (runtimeArgs.includes('--qwen')) selected.push('qwen');
649
655
  if (runtimeArgs.includes('--hermes')) selected.push('hermes');
650
656
  if (runtimeArgs.includes('--kimi')) selected.push('kimi');
657
+ if (runtimeArgs.includes('--kimi-code')) selected.push('kimi-code');
651
658
  if (runtimeArgs.includes('--codebuddy')) selected.push('codebuddy');
652
659
  if (runtimeArgs.includes('--cline')) selected.push('cline');
653
660
  if (runtimeArgs.includes('--zcode')) selected.push('zcode');
@@ -657,6 +664,62 @@ function selectRuntimesFromArgs(runtimeArgs) {
657
664
  // Runtime selection - can be set by flags or interactive prompt
658
665
  let selectedRuntimes = selectRuntimesFromArgs(args);
659
666
 
667
+ // #2505 Phase 5: Kimi variant disambiguation (#2513). Kimi CLI (Python, ~/.kimi/)
668
+ // and Kimi Code (Node, ~/.kimi-code/) are two distinct Moonshot products that
669
+ // share the "kimi" brand. Probe for each product's config.toml and warn when
670
+ // the selected runtime doesn't match the detected install — catches the common
671
+ // "ran --kimi --global but actually on Kimi Code" mistake that produced inert
672
+ // YAMLs and empty agent-skills before the Phase 1 descriptor split.
673
+ function disambiguateKimiVariant(runtimes) {
674
+ const home = os.homedir();
675
+ const hasKimiCli = fs.existsSync(path.join(home, '.kimi', 'config.toml'));
676
+ const hasKimiCode = fs.existsSync(path.join(home, '.kimi-code', 'config.toml'));
677
+ const notices = [];
678
+ if (runtimes.includes('kimi') && hasKimiCode && !hasKimiCli) {
679
+ notices.push({
680
+ kind: 'wrong-variant',
681
+ selected: 'kimi',
682
+ detected: 'kimi-code',
683
+ message: `Detected ~/.kimi-code/config.toml (Kimi Code, Node CLI) but not ~/.kimi/config.toml (Kimi CLI, Python). You selected --kimi but appear to be on Kimi Code. Re-run with --kimi-code for a working install. (Kimi CLI = Python kimi-cli with named subagents; Kimi Code = Node CLI with coder/explore/plan built-ins only.)`,
684
+ });
685
+ }
686
+ if (runtimes.includes('kimi-code') && hasKimiCli && !hasKimiCode) {
687
+ notices.push({
688
+ kind: 'wrong-variant',
689
+ selected: 'kimi-code',
690
+ detected: 'kimi',
691
+ message: `Detected ~/.kimi/config.toml (Kimi CLI, Python) but not ~/.kimi-code/config.toml (Kimi Code, Node CLI). You selected --kimi-code but appear to be on Kimi CLI. Re-run with --kimi for a working install.`,
692
+ });
693
+ }
694
+ // Distinct-entry descriptions when either Kimi variant is selected.
695
+ if (runtimes.includes('kimi')) {
696
+ notices.push({
697
+ kind: 'description',
698
+ runtime: 'kimi',
699
+ message: 'Kimi CLI (Python kimi-cli): named subagents via YAML, config at ~/.kimi/, hooks via ~/.kimi/config.toml [[hooks]].',
700
+ });
701
+ }
702
+ if (runtimes.includes('kimi-code')) {
703
+ notices.push({
704
+ kind: 'description',
705
+ runtime: 'kimi-code',
706
+ message: 'Kimi Code (Node CLI): three built-in subagents (coder/explore/plan), Agent Skills at ~/.kimi-code/skills/, config at ~/.kimi-code/.',
707
+ });
708
+ }
709
+ return notices;
710
+ }
711
+
712
+ if (selectedRuntimes.includes('kimi') || selectedRuntimes.includes('kimi-code')) {
713
+ const kimiNotices = disambiguateKimiVariant(selectedRuntimes);
714
+ for (const notice of kimiNotices) {
715
+ if (notice.kind === 'wrong-variant') {
716
+ console.error(`${yellow}⚠ Kimi variant mismatch (${notice.selected} → ${notice.detected}).${reset} ${notice.message}`);
717
+ } else if (notice.kind === 'description') {
718
+ console.log(`${dim} ${notice.runtime}: ${notice.message}${reset}`);
719
+ }
720
+ }
721
+ }
722
+
660
723
  // #1928: Google sunset Gemini CLI on 2026-06-18; Antigravity CLI is its
661
724
  // official successor. `--gemini` is no longer a valid runtime selector —
662
725
  // selectRuntimesFromArgs above no longer recognizes it, so it never lands in
@@ -3956,10 +4019,14 @@ Typed mapping (agent_type-capable schema only):
3956
4019
  inherited, or unsupported values; do not invent one-off effort literals in
3957
4020
  workflow prose.
3958
4021
  - \`fork_context: false\` by default — GSD agents load their own context via \`<files_to_read>\` blocks
3959
- - \`Task(isolation="worktree")\` / \`Agent(isolation="worktree")\` → no direct Codex mapping.
3960
- Codex \`spawn_agent\` does not create or bind a git worktree automatically.
3961
- Workflows that require this isolation must fail closed or use an explicit
3962
- manual worktree protocol before spawning (#3360).
4022
+ - \`Task(isolation="worktree")\` / \`Agent(isolation="worktree")\` → no direct \`spawn_agent\` mapping,
4023
+ but Codex declares \`dispatch.isolation: orchestrator-worktree\` (#2584). Codex
4024
+ \`spawn_agent\` still does not create or bind a git worktree; instead GSD itself
4025
+ creates the worktree and process-spawns the executor into it with
4026
+ \`codex exec --cd <dir>\`, performing every git operation on the executor's behalf
4027
+ (its \`workspace-write\` sandbox makes \`.git\` read-only). Workflows must therefore
4028
+ never fabricate a manual worktree protocol — route through the negotiated
4029
+ isolation adapter, which still fails closed for hosts declaring \`none\` (#3360).
3963
4030
 
3964
4031
  Generic-agent workaround (multi_agent_v1 schema — NO agent_type field):
3965
4032
  When only the generic \`multi_agent_v1\` schema is available, typed GSD agent dispatch
@@ -6916,6 +6983,47 @@ function writeCopilotHookConfig(targetDir) {
6916
6983
  * Generate config.toml and per-agent .toml files for Codex.
6917
6984
  * Reads agent .md files from source, extracts metadata, writes .toml configs.
6918
6985
  */
6986
+
6987
+ /**
6988
+ * #2834: Write ~/.gsd/defaults.json for non-Claude runtimes — sets
6989
+ * resolve_model_ids="omit" (so resolveModelInternal() returns '' instead of
6990
+ * Claude aliases the runtime can't resolve) and runtime=<runtime> (so
6991
+ * resolveRuntime() resolves correctly out of the box). MUST be called BEFORE
6992
+ * installCodexConfig (or any other step that reads defaults.json at generation
6993
+ * time), so a clean first install produces correctly-model-routed agent TOMLs.
6994
+ * No-op for Claude runtimes (Claude is the resolveRuntime fallback + has native
6995
+ * model aliases). Preserves an explicit `true` opt-in and existing values.
6996
+ */
6997
+ function writeNonClaudeDefaults(runtime) {
6998
+ if (_hostBehaviors(runtime).nativeModelAliases || process.env.GSD_TEST_MODE) return;
6999
+ const gsdDir = path.join(os.homedir(), '.gsd');
7000
+ const defaultsPath = path.join(gsdDir, 'defaults.json');
7001
+ try {
7002
+ fs.mkdirSync(gsdDir, { recursive: true });
7003
+ let defaults = {};
7004
+ try { defaults = JSON.parse(fs.readFileSync(defaultsPath, 'utf8')); } catch { /* new file */ }
7005
+ if (defaults === null || typeof defaults !== 'object' || Array.isArray(defaults)) {
7006
+ defaults = {};
7007
+ }
7008
+ // Three-valued domain: false/absent → aliases; true → full IDs; "omit" → ''.
7009
+ const existing = defaults.resolve_model_ids;
7010
+ const shouldDefaultToOmit = existing !== true && existing !== 'omit';
7011
+ if (shouldDefaultToOmit) {
7012
+ defaults.resolve_model_ids = 'omit';
7013
+ fs.writeFileSync(defaultsPath, JSON.stringify(defaults, null, 2) + '\n');
7014
+ console.log(` ${green}✓${reset} Set resolve_model_ids: "omit" in ~/.gsd/defaults.json`);
7015
+ }
7016
+ // #2395: persist runtime for non-Claude runtimes.
7017
+ if (defaults.runtime === undefined || defaults.runtime === null || defaults.runtime === '') {
7018
+ defaults.runtime = runtime;
7019
+ fs.writeFileSync(defaultsPath, JSON.stringify(defaults, null, 2) + '\n');
7020
+ console.log(` ${green}✓${reset} Set runtime: "${runtime}" in ~/.gsd/defaults.json`);
7021
+ }
7022
+ } catch (e) {
7023
+ console.log(` ${yellow}⚠${reset} Could not write ~/.gsd/defaults.json: ${e.message}`);
7024
+ }
7025
+ }
7026
+
6919
7027
  function installCodexConfig(targetDir, agentsSrc, sandboxTier = 'codex-agent-sandbox') {
6920
7028
  // ADR-1239 Phase B write-confinement: every Codex config write stays under targetDir.
6921
7029
  const configPath = assertDestWithinConfigHome(targetDir, 'config.toml');
@@ -8434,6 +8542,17 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
8434
8542
  console.log(` ${green}✓${reset} Removed ${removedLibFiles} hooks/lib/ helper(s)`);
8435
8543
  }
8436
8544
  }
8545
+
8546
+ // #2717: remove the CommonJS marker GSD wrote into hooks/ for runtimes that
8547
+ // stage .js hooks via dedicated paths (cursor/windsurf/codex) — but ONLY if
8548
+ // it still carries GSD's exact content (a user-authored package.json is
8549
+ // never deleted). Safe no-op for runtimes whose marker lives at the config
8550
+ // root (the shared-bundle path) or that never received one.
8551
+ try {
8552
+ if (hooksSurface.removeCommonJsMarkerIfGsdOwned(hooksDir)) {
8553
+ console.log(` ${green}✓${reset} Removed GSD hooks/package.json (CommonJS marker)`);
8554
+ }
8555
+ } catch { /* best-effort */ }
8437
8556
  }
8438
8557
 
8439
8558
  // 4z. Remove the native plugin adapter (#1914, extended to Kilo by #2093).
@@ -10339,6 +10458,45 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10339
10458
  return Array.isArray(scopeLayout) && scopeLayout.length > 0;
10340
10459
  })();
10341
10460
 
10461
+ // #2624: write the .gsd-source marker. Extracted from its former late position so it can be
10462
+ // called BEFORE staging reads the marker (see the call site below). Scoped to the Claude-global
10463
+ // layout (issue #1477) — the only install path that ships the skills layout without a
10464
+ // commands/gsd source tree, so findInstallSourceRoot's walk-up has nothing to find and
10465
+ // /gsd-surface (list/status) throws without it. Points at the package's own commands/gsd
10466
+ // source. Guarded on source presence so a half-published package never writes a dangling
10467
+ // marker. Write failure is non-fatal (install proceeds; warn so /gsd-surface breakage is
10468
+ // diagnosable) — the same contract the late write had.
10469
+ function _writeGsdSourceMarker(runtime, targetDir, src, isGlobal) {
10470
+ if (_hostBehaviors(runtime).sourceMarkerFile && isGlobal) {
10471
+ const gsdSourceCommands = path.join(src, 'commands', 'gsd');
10472
+ if (fs.existsSync(gsdSourceCommands)) {
10473
+ try {
10474
+ // ADR-1239 Phase B write-confinement: the descriptor-sourced marker filename
10475
+ // must resolve under targetDir (parity with the other descriptor-driven writes).
10476
+ const _markerPath = assertDestWithinConfigHome(targetDir, _hostBehaviors(runtime).sourceMarkerFile);
10477
+ fs.writeFileSync(_markerPath, gsdSourceCommands + '\n', 'utf8');
10478
+ } catch (err) {
10479
+ // Non-fatal: install proceeds. But on the Claude-global layout walk-up
10480
+ // also fails (no commands/gsd source tree), so a silent write failure
10481
+ // still leaves /gsd-surface broken at runtime — warn so it's diagnosable.
10482
+ console.warn(` ${yellow}!${reset} Could not write .gsd-source marker (${err.message}); /gsd-surface list/status may fail`);
10483
+ }
10484
+ }
10485
+ }
10486
+ }
10487
+
10488
+ // #2624: write the .gsd-source marker BEFORE any staging reads it. The marker write
10489
+ // formerly lived AFTER staging; on an upgrade the marker still held the PREVIOUS
10490
+ // install's source path (e.g. an npx per-version cache dir that still exists on disk),
10491
+ // so findInstallSourceRoot(configDir) — called inside installRuntimeArtifacts below —
10492
+ // returned the stale path and every converted skill was generated from the OLD version's
10493
+ // commands/gsd, silently installing prior-version content with a self-consistent manifest
10494
+ // hash. Writing first closes the read-before-write hole for every findInstallSourceRoot
10495
+ // consumer (skills, commands, /gsd-surface, capability-state). Placed here (before the
10496
+ // _isSkillsRuntime branch) so it runs for every Claude-global install, matching the
10497
+ // original write's sourceMarkerFile && isGlobal guard exactly.
10498
+ _writeGsdSourceMarker(runtime, targetDir, src, isGlobal);
10499
+
10342
10500
  if (_isSkillsRuntime) {
10343
10501
  // Layout-driven install for skills-based runtimes (full and minimal modes)
10344
10502
  const scope = isGlobal ? 'global' : 'local';
@@ -10627,35 +10785,11 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10627
10785
  failures.push('gsd-core');
10628
10786
  }
10629
10787
 
10630
- // Write the .gsd-source marker so runtime source resolution succeeds at
10631
- // runtime (#1477). The Claude-global skills layout ships gsd-core/{bin,
10632
- // contexts,references,templates,workflows} but NOT the commands/gsd source
10633
- // tree, and _runLegacyUninstallCleanup actively removes any commands/gsd/
10634
- // for that scope — so findInstallSourceRoot's walk-up has nothing to find
10635
- // and /gsd-surface (list/status) throws. This is the writer half of the
10636
- // marker that runtime-artifact-layout.cjs's finders already read (the reader
10637
- // landed in #1476). It points at the package's own commands/gsd source.
10638
- // Scoped to the Claude-global layout (issue #1477) — the only install path
10639
- // that ships the skills layout without a commands/gsd source tree; every
10640
- // other runtime/scope deploys commands/gsd, so its walk-up already resolves
10641
- // and needs no marker. Guarded on source presence so a half-published
10642
- // package never writes a dangling marker.
10643
- if (_hostBehaviors(runtime).sourceMarkerFile && isGlobal) {
10644
- const gsdSourceCommands = path.join(src, 'commands', 'gsd');
10645
- if (fs.existsSync(gsdSourceCommands)) {
10646
- try {
10647
- // ADR-1239 Phase B write-confinement: the descriptor-sourced marker filename
10648
- // must resolve under targetDir (parity with the other descriptor-driven writes).
10649
- const _markerPath = assertDestWithinConfigHome(targetDir, _hostBehaviors(runtime).sourceMarkerFile);
10650
- fs.writeFileSync(_markerPath, gsdSourceCommands + '\n', 'utf8');
10651
- } catch (err) {
10652
- // Non-fatal: install proceeds. But on the Claude-global layout walk-up
10653
- // also fails (no commands/gsd source tree), so a silent write failure
10654
- // still leaves /gsd-surface broken at runtime — warn so it's diagnosable.
10655
- console.warn(` ${yellow}!${reset} Could not write .gsd-source marker (${err.message}); /gsd-surface list/status may fail`);
10656
- }
10657
- }
10658
- }
10788
+ // #2624: the .gsd-source marker is now written by _writeGsdSourceMarker()
10789
+ // BEFORE staging reads it (see the early call above the _isSkillsRuntime
10790
+ // block). The former write lived here — AFTER staging — which on an upgrade
10791
+ // let staging read a stale prior-version marker and silently install
10792
+ // old-version skill content. Moved up; this site intentionally left empty.
10659
10793
 
10660
10794
  // #1629 critical fix: Windsurf workflow wrappers (convertClaudeCommandToWindsurfWorkflow)
10661
10795
  // delegate to command bodies at <targetDir>/gsd-core/commands/gsd/${stem}.md via a
@@ -11321,7 +11455,15 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11321
11455
  throw _earlyInstallErr;
11322
11456
  }
11323
11457
 
11324
- if (plan.installSurface === 'codex-toml' && !isMinimalMode(_effectiveInstallMode)) {
11458
+ // #2695: this branch runs for BOTH `core` (minimal) and `full` profiles.
11459
+ // Hooks are lightweight infrastructure (update-check + context monitor), not the
11460
+ // "full agent surface" that `core` deliberately omits. The config.toml / agent
11461
+ // generation below is still gated by its own inner `!isMinimalMode` guard, so
11462
+ // `core` enters the branch to receive the hook-file copy + hooks.json wiring but
11463
+ // does NOT get agent roles generated. Before #2695 the outer `!isMinimalMode`
11464
+ // here skipped the whole branch for `core`, so the registered parent hook pointed
11465
+ // at a worker/registry the same installer never delivered.
11466
+ if (plan.installSurface === 'codex-toml') {
11325
11467
  // Capture pre-install snapshots before ANY GSD mutation
11326
11468
  // (#2760 fix 3). On post-write schema-validation failure OR any throw
11327
11469
  // during the mutation sequence (write failure, merge throw, etc.) we
@@ -11485,6 +11627,10 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11485
11627
 
11486
11628
  let agentCount = 0;
11487
11629
  if (!isMinimalMode(_effectiveInstallMode)) {
11630
+ // #2834: write ~/.gsd/defaults.json (resolve_model_ids + runtime) BEFORE generating
11631
+ // agent TOMLs — installCodexConfig reads defaults.json at generation time, so on a
11632
+ // clean first install the runtime-aware model resolver must already know the runtime.
11633
+ writeNonClaudeDefaults(runtime);
11488
11634
  try {
11489
11635
  // Generate Codex config.toml and per-agent .toml files.
11490
11636
  agentCount = installCodexConfig(targetDir, agentsSrc, plan.sandboxTier);
@@ -11504,9 +11650,18 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11504
11650
 
11505
11651
  // Copy only the hook files that Codex actually registers via its hook configuration (#2153).
11506
11652
  // #772: added gsd-context-monitor.js for the new SubagentStart/Stop/PostToolUse events.
11653
+ // #2695: the parent gsd-check-update.js spawn()s gsd-check-update-worker.js, which
11654
+ // require()s managed-hooks-registry.cjs for MANAGED_HOOKS — so all four must be
11655
+ // installed/refreshed together for every profile, or Codex is wired to a dependency
11656
+ // chain the same installer never delivers.
11507
11657
  // We deliberately do *not* copy gsd-graphify-update.sh or hooks/lib/ for Codex
11508
11658
  // in this change (graphify auto-update support for Codex is out of scope for #3579).
11509
- const CODEX_HOOKS_TO_COPY = ['gsd-check-update.js', 'gsd-context-monitor.js'];
11659
+ const CODEX_HOOKS_TO_COPY = [
11660
+ 'gsd-check-update.js',
11661
+ 'gsd-check-update-worker.js',
11662
+ 'managed-hooks-registry.cjs',
11663
+ 'gsd-context-monitor.js',
11664
+ ];
11510
11665
  const codexHooksSrc = path.join(src, 'hooks', 'dist');
11511
11666
  if (fs.existsSync(codexHooksSrc)) {
11512
11667
  const codexHooksDest = path.join(targetDir, 'hooks');
@@ -11536,9 +11691,28 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11536
11691
  content = content.replace(/\{\{GSD_VERSION\}\}/g, pkg.version);
11537
11692
  fs.writeFileSync(destFile, content);
11538
11693
  try { fs.chmodSync(destFile, 0o755); } catch (e) { /* Windows */ }
11694
+ } else {
11695
+ // #2695: raw byte-for-byte copy for allowlisted artifacts that carry
11696
+ // no {{GSD_VERSION}} placeholder and no runtime path token (e.g.
11697
+ // managed-hooks-registry.cjs, whose only `.claude` mention is inside a
11698
+ // doc comment). Version/path transforms would be a no-op at best and a
11699
+ // surprise at worst; the issue requires the registry copied verbatim.
11700
+ fs.copyFileSync(srcFile, destFile);
11701
+ try { fs.chmodSync(destFile, 0o755); } catch (e) { /* Windows */ }
11539
11702
  }
11540
11703
  }
11541
11704
  console.log(` ${green}✓${reset} Installed hooks (Codex)`);
11705
+ // #2717: write the CommonJS marker into hooks/ alongside the staged .js
11706
+ // scripts. Codex is excluded from installSharedHooksBundle by the
11707
+ // !isCodex gate, so it never received the marker the shared-bundle path
11708
+ // writes for the other runtimes. Without it, a ~/.codex/package.json
11709
+ // declaring {"type":"module"} makes Node load gsd-check-update.js /
11710
+ // gsd-context-monitor.js as ESM and their require() calls fail silently.
11711
+ // Reuses the same helper the Cursor/Windsurf writers call so the marker
11712
+ // content + user-file-preservation contract is identical everywhere.
11713
+ if (hooksSurface.ensureCommonJsMarker(codexHooksDest)) {
11714
+ console.log(` ${green}✓${reset} Wrote hooks/package.json (CommonJS mode)`);
11715
+ }
11542
11716
  }
11543
11717
 
11544
11718
  // Add Codex hooks (SessionStart for update checking) — requires codex_hooks feature flag
@@ -12232,58 +12406,11 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
12232
12406
  configureAntigravityMcpConfig(isGlobal, configDir);
12233
12407
  }
12234
12408
 
12235
- // For non-Claude runtimes, DEFAULT resolve_model_ids to "omit" in ~/.gsd/defaults.json
12236
- // when it is absent or falsy, so resolveModelInternal() returns '' instead of Claude
12237
- // aliases (opus/sonnet/haiku) the runtime can't resolve. An explicit `true` opt-in
12238
- // (resolveModelInternal returns full materialized model IDs) MUST be preserved —
12239
- // rewriting it to "omit" would make generated agent manifests inherit the active
12240
- // chat model instead of pinning the resolved model. See #1156 (default-to-omit
12241
- // intent) and #1569 (preserve explicit true). Guard matches the #130-class pattern
12242
- // on configureOpencodePermissions above.
12243
- if (!_hostBehaviors(runtime).nativeModelAliases && !process.env.GSD_TEST_MODE) {
12244
- const gsdDir = path.join(os.homedir(), '.gsd');
12245
- const defaultsPath = path.join(gsdDir, 'defaults.json');
12246
- try {
12247
- fs.mkdirSync(gsdDir, { recursive: true });
12248
- let defaults = {};
12249
- try { defaults = JSON.parse(fs.readFileSync(defaultsPath, 'utf8')); } catch { /* new file */ }
12250
- // Recover a malformed (valid-JSON-but-non-object) defaults.json to a fresh object so
12251
- // the write below succeeds and the file is no longer broken. Without this, `null` /
12252
- // `[]` / a number / a string bypass the parse catch and either throw a TypeError on
12253
- // property access (swallowed by the outer try/catch, leaving the file broken) or get
12254
- // a property set that won't round-trip through JSON.stringify. (#1657)
12255
- if (defaults === null || typeof defaults !== 'object' || Array.isArray(defaults)) {
12256
- defaults = {};
12257
- }
12258
- // Three-valued domain: false/absent → aliases; true → full IDs; "omit" → ''.
12259
- // Honor ONLY an explicit canonical `true` opt-in (full model IDs) and an existing
12260
- // "omit"; default everything else — absent, falsy, OR any non-canonical value — to
12261
- // "omit", the safe non-Claude default. Allowlist-based so malformed values
12262
- // (0, "", "yes", {}, …) don't leak Claude aliases the runtime can't resolve (#1569).
12263
- const existing = defaults.resolve_model_ids;
12264
- const shouldDefaultToOmit = existing !== true && existing !== 'omit';
12265
- if (shouldDefaultToOmit) {
12266
- defaults.resolve_model_ids = 'omit';
12267
- fs.writeFileSync(defaultsPath, JSON.stringify(defaults, null, 2) + '\n');
12268
- console.log(` ${green}✓${reset} Set resolve_model_ids: "omit" in ~/.gsd/defaults.json`);
12269
- }
12270
-
12271
- // #2395: also persist `runtime: <runtime>` for non-Claude runtimes, so
12272
- // resolveRuntime() (precedence: GSD_RUNTIME env > config.runtime > 'claude')
12273
- // resolves to the install's actual runtime identity out of the box — without
12274
- // this, agent_runtime and every runtime-branded slash hint falls through to
12275
- // the hard-coded 'claude' default. Mirrors the resolve_model_ids write above:
12276
- // honor an explicit pre-existing value (any string), only default-populating
12277
- // when absent. Claude is the resolveRuntime() fallback, so it needs no write.
12278
- if (defaults.runtime === undefined || defaults.runtime === null || defaults.runtime === '') {
12279
- defaults.runtime = runtime;
12280
- fs.writeFileSync(defaultsPath, JSON.stringify(defaults, null, 2) + '\n');
12281
- console.log(` ${green}✓${reset} Set runtime: "${runtime}" in ~/.gsd/defaults.json`);
12282
- }
12283
- } catch (e) {
12284
- console.log(` ${yellow}⚠${reset} Could not write ~/.gsd/defaults.json: ${e.message}`);
12285
- }
12286
- }
12409
+ // #2834: defaults.json (resolve_model_ids + runtime) is now written BEFORE
12410
+ // installCodexConfig via writeNonClaudeDefaults(runtime) — extracted into a
12411
+ // function so it can run at the right point in the flow (before agent TOML
12412
+ // generation reads it). This call is idempotent (preserves existing values).
12413
+ writeNonClaudeDefaults(runtime);
12287
12414
 
12288
12415
  // program + command are now single-source lookups (ADR-1239 Phase B / #1679):
12289
12416
  // program is the runtime display label; command is the per-host /gsd-new-project
@@ -12390,16 +12517,17 @@ const runtimeMap = {
12390
12517
  '8': 'cursor',
12391
12518
  '9': 'hermes',
12392
12519
  '10': 'kimi',
12393
- '11': 'kilo',
12394
- '12': 'opencode',
12395
- '13': 'pi',
12396
- '14': 'qwen',
12397
- '15': 'trae',
12398
- '16': 'windsurf',
12399
- '17': 'zcode'
12520
+ '11': 'kimi-code',
12521
+ '12': 'kilo',
12522
+ '13': 'opencode',
12523
+ '14': 'pi',
12524
+ '15': 'qwen',
12525
+ '16': 'trae',
12526
+ '17': 'windsurf',
12527
+ '18': 'zcode'
12400
12528
  };
12401
- const allRuntimes = ['claude', 'antigravity', 'augment', 'cline', 'codebuddy', 'codex', 'copilot', 'cursor', 'hermes', 'kimi', 'kilo', 'opencode', 'pi', 'qwen', 'trae', 'windsurf', 'zcode'];
12402
- const ALL_RUNTIMES_OPTION = '18';
12529
+ const allRuntimes = ['claude', 'antigravity', 'augment', 'cline', 'codebuddy', 'codex', 'copilot', 'cursor', 'hermes', 'kimi', 'kimi-code', 'kilo', 'opencode', 'pi', 'qwen', 'trae', 'windsurf', 'zcode'];
12530
+ const ALL_RUNTIMES_OPTION = '19';
12403
12531
 
12404
12532
  /**
12405
12533
  * Build the runtime-selection prompt text shown by the interactive installer.
@@ -12417,14 +12545,15 @@ function buildRuntimePromptText() {
12417
12545
  ${cyan}8${reset}) Cursor ${dim}(~/.cursor)${reset}
12418
12546
  ${cyan}9${reset}) Hermes Agent ${dim}(~/.hermes)${reset}
12419
12547
  ${cyan}10${reset}) Kimi ${dim}(~/.config/agents, then ~/.agents if existing)${reset}
12420
- ${cyan}11${reset}) Kilo ${dim}(~/.config/kilo)${reset}
12421
- ${cyan}12${reset}) OpenCode ${dim}(~/.config/opencode)${reset}
12422
- ${cyan}13${reset}) pi ${dim}(~/.pi/agent)${reset}
12423
- ${cyan}14${reset}) Qwen Code ${dim}(~/.qwen)${reset}
12424
- ${cyan}15${reset}) Trae ${dim}(~/.trae)${reset}
12425
- ${cyan}16${reset}) Windsurf ${dim}(~/.codeium/windsurf)${reset}
12426
- ${cyan}17${reset}) ZCode ${dim}(~/.zcode)${reset}
12427
- ${cyan}18${reset}) All
12548
+ ${cyan}11${reset}) Kimi Code ${dim}(~/.kimi-code)${reset}
12549
+ ${cyan}12${reset}) Kilo ${dim}(~/.config/kilo)${reset}
12550
+ ${cyan}13${reset}) OpenCode ${dim}(~/.config/opencode)${reset}
12551
+ ${cyan}14${reset}) pi ${dim}(~/.pi/agent)${reset}
12552
+ ${cyan}15${reset}) Qwen Code ${dim}(~/.qwen)${reset}
12553
+ ${cyan}16${reset}) Trae ${dim}(~/.trae)${reset}
12554
+ ${cyan}17${reset}) Windsurf ${dim}(~/.codeium/windsurf)${reset}
12555
+ ${cyan}18${reset}) ZCode ${dim}(~/.zcode)${reset}
12556
+ ${cyan}19${reset}) All
12428
12557
 
12429
12558
  ${dim}Select multiple: 1,2,6 or 1 2 6${reset}
12430
12559
  `;
@@ -12435,7 +12564,7 @@ function buildRuntimePromptText() {
12435
12564
  * Pure function — exported so tests can verify split/dedupe/fallback behavior.
12436
12565
  * - Accepts comma- and/or whitespace-separated choices
12437
12566
  * - Deduplicates while preserving order
12438
- * - Maps option 16 ("All") to every runtime
12567
+ * - Maps option 19 ("All") to every runtime
12439
12568
  * - Falls back to ['claude'] when nothing valid is selected
12440
12569
  */
12441
12570
  function parseRuntimeInput(answer) {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gsd:plan-review-convergence
3
3
  description: "Cross-AI plan convergence - replan until review concerns are resolved."
4
- argument-hint: "<phase> [--codex] [--gemini] [--claude] [--opencode] [--ollama] [--lm-studio] [--llama-cpp] [--agy] [--text] [--ws <name>] [--all] [--max-cycles N]"
4
+ argument-hint: "<phase> [--gemini] [--claude] [--codex] [--coderabbit] [--opencode] [--qwen] [--cursor] [--antigravity] [--agy] [--ollama] [--lm-studio] [--llama-cpp] [--kimi-code] [--text] [--ws <name>] [--all] [--max-cycles N]"
5
5
  allowed-tools:
6
6
  - Read
7
7
  - Write
@@ -44,10 +44,14 @@ Phase number: extracted from $ARGUMENTS (required)
44
44
  - `--gemini` — Use Gemini CLI as reviewer
45
45
  - `--agy` / `--antigravity` — Use Antigravity CLI as reviewer (successor to the discontinued Gemini CLI)
46
46
  - `--claude` — Use Claude CLI as reviewer (separate session)
47
+ - `--coderabbit` — Use CodeRabbit as reviewer (reviews the working-tree diff, not the source tree)
47
48
  - `--opencode` — Use OpenCode as reviewer
49
+ - `--qwen` — Use Qwen Code CLI as reviewer (Alibaba Qwen models)
50
+ - `--cursor` — Use Cursor agent as reviewer
48
51
  - `--ollama` — Use local Ollama server as reviewer (OpenAI-compatible, default host `http://localhost:11434`; configure model via `review.models.ollama`)
49
52
  - `--lm-studio` — Use local LM Studio server as reviewer (OpenAI-compatible, default host `http://localhost:1234`; configure model via `review.models.lm_studio`)
50
53
  - `--llama-cpp` — Use local llama.cpp server as reviewer (OpenAI-compatible, default host `http://localhost:8080`; configure model via `review.models.llama_cpp`)
54
+ - `--kimi-code` — Use Kimi Code CLI as reviewer (Moonshot AI)
51
55
  - `--all` — Use all available CLIs and running local model servers
52
56
  - `--max-cycles N` — Maximum replan→review cycles (default: 3)
53
57