@opengsd/gsd-core 1.20.0 → 1.21.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 (154) 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 +33 -2
  4. package/agents/gsd-executor.md +1 -1
  5. package/agents/gsd-plan-checker.md +1 -1
  6. package/agents/gsd-planner.md +3 -3
  7. package/agents/gsd-verifier.md +2 -2
  8. package/bin/install.js +265 -161
  9. package/commands/gsd/autonomous.md +1 -1
  10. package/commands/gsd/new-milestone.md +2 -2
  11. package/commands/gsd/new-project.md +2 -2
  12. package/gsd-core/bin/gsd-tools.cjs +554 -823
  13. package/gsd-core/bin/lib/capability-activation.cjs +8 -2
  14. package/gsd-core/bin/lib/capability-command-router.cjs +6 -2
  15. package/gsd-core/bin/lib/capability-ledger.cjs +1 -1
  16. package/gsd-core/bin/lib/capability-lock.cjs +19 -27
  17. package/gsd-core/bin/lib/capability-registry.cjs +74 -71
  18. package/gsd-core/bin/lib/capability-validator.cjs +1 -0
  19. package/gsd-core/bin/lib/capability-writer.cjs +7 -4
  20. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +142 -62
  21. package/gsd-core/bin/lib/claude-orchestration.cjs +214 -122
  22. package/gsd-core/bin/lib/cli-surface.cjs +545 -0
  23. package/gsd-core/bin/lib/command-arg-projection.cjs +356 -103
  24. package/gsd-core/bin/lib/commands.cjs +99 -2
  25. package/gsd-core/bin/lib/config-file-read.cjs +85 -0
  26. package/gsd-core/bin/lib/config-loader.cjs +23 -15
  27. package/gsd-core/bin/lib/config.cjs +139 -101
  28. package/gsd-core/bin/lib/configuration.cjs +41 -2
  29. package/gsd-core/bin/lib/criterion-baseline.cjs +346 -0
  30. package/gsd-core/bin/lib/criterion-replay.cjs +380 -0
  31. package/gsd-core/bin/lib/estimate-cli.cjs +5 -1
  32. package/gsd-core/bin/lib/gap-checker.cjs +12 -1
  33. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +593 -27
  34. package/gsd-core/bin/lib/gate-phase-context.cjs +16 -4
  35. package/gsd-core/bin/lib/graphify.cjs +19 -2
  36. package/gsd-core/bin/lib/gsd-tool-dir.cjs +134 -0
  37. package/gsd-core/bin/lib/health-diagnostic.cjs +5 -1
  38. package/gsd-core/bin/lib/init.cjs +20 -18
  39. package/gsd-core/bin/lib/install-effort-resolver.cjs +24 -4
  40. package/gsd-core/bin/lib/install-engine.cjs +20 -28
  41. package/gsd-core/bin/lib/install-fs-adapter.cjs +5 -1
  42. package/gsd-core/bin/lib/install-manifest-record.cjs +86 -0
  43. package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -3
  44. package/gsd-core/bin/lib/install-profiles.cjs +4 -2
  45. package/gsd-core/bin/lib/io.cjs +6 -0
  46. package/gsd-core/bin/lib/markdown-sectionizer.cjs +62 -31
  47. package/gsd-core/bin/lib/markdown-table.cjs +39 -1
  48. package/gsd-core/bin/lib/milestone.cjs +8 -6
  49. package/gsd-core/bin/lib/model-resolver.cjs +8 -4
  50. package/gsd-core/bin/lib/phase-entry-grammar.cjs +14 -0
  51. package/gsd-core/bin/lib/phase-id.cjs +26 -9
  52. package/gsd-core/bin/lib/phase-locator.cjs +8 -3
  53. package/gsd-core/bin/lib/phase.cjs +170 -45
  54. package/gsd-core/bin/lib/plan-dependency-graph.cjs +46 -23
  55. package/gsd-core/bin/lib/planning-snapshot.cjs +10 -6
  56. package/gsd-core/bin/lib/planning-workspace.cjs +20 -10
  57. package/gsd-core/bin/lib/profile-output.cjs +9 -6
  58. package/gsd-core/bin/lib/project-context.cjs +439 -0
  59. package/gsd-core/bin/lib/project-root.cjs +9 -230
  60. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +1 -1
  61. package/gsd-core/bin/lib/report-parser.cjs +16 -0
  62. package/gsd-core/bin/lib/roadmap-parser.cjs +357 -50
  63. package/gsd-core/bin/lib/roadmap-upgrade.cjs +24 -5
  64. package/gsd-core/bin/lib/roadmap.cjs +2 -2
  65. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +317 -134
  66. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +81 -3
  67. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +6 -0
  68. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +279 -8
  69. package/gsd-core/bin/lib/runtime-slash.cjs +7 -2
  70. package/gsd-core/bin/lib/shell-command-projection.cjs +1010 -10
  71. package/gsd-core/bin/lib/stale-bake-guard.cjs +4 -1
  72. package/gsd-core/bin/lib/state-transition.cjs +103 -46
  73. package/gsd-core/bin/lib/state.cjs +448 -63
  74. package/gsd-core/bin/lib/surface.cjs +4 -7
  75. package/gsd-core/bin/lib/tdd-red-evidence.cjs +10 -3
  76. package/gsd-core/bin/lib/verify-command-router.cjs +6 -1
  77. package/gsd-core/bin/lib/verify.cjs +49 -6
  78. package/gsd-core/bin/lib/workstream-inventory.cjs +4 -4
  79. package/gsd-core/bin/lib/worktree-base-ref.cjs +32 -2
  80. package/gsd-core/bin/lib/worktree-safety.cjs +8 -111
  81. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  82. package/gsd-core/references/autonomous-smart-discuss.md +99 -18
  83. package/gsd-core/references/checkpoints.md +1 -1
  84. package/gsd-core/references/planner-guidance.md +11 -0
  85. package/gsd-core/references/planner-human-verify-mode.md +1 -1
  86. package/gsd-core/references/planner-reversibility.md +2 -0
  87. package/gsd-core/references/planner-verify-command-grounding.md +22 -6
  88. package/gsd-core/workflows/autonomous.md +4 -4
  89. package/gsd-core/workflows/code-review-fix.md +5 -7
  90. package/gsd-core/workflows/execute-phase.md +21 -7
  91. package/gsd-core/workflows/plan-phase/steps/plan-pre-dispatch.md +101 -0
  92. package/gsd-core/workflows/plan-phase.md +18 -19
  93. package/hooks/dist/gsd-agent-isolation-guard.js +4 -3
  94. package/hooks/dist/gsd-config-reload.js +2 -2
  95. package/hooks/dist/gsd-context-monitor.js +2 -1
  96. package/hooks/dist/gsd-cursor-subagent-start.js +9 -6
  97. package/hooks/dist/gsd-read-injection-scanner.js +2 -2
  98. package/hooks/dist/gsd-secret-read-guard.js +141 -6
  99. package/hooks/dist/gsd-statusline.js +3 -2
  100. package/hooks/dist/gsd-workflow-guard.js +2 -1
  101. package/hooks/dist/lib/config-read.js +49 -0
  102. package/hooks/dist/lib/git-cmd.js +3 -1
  103. package/hooks/dist/lib/git-head-tree.js +474 -0
  104. package/hooks/dist/lib/isolation-sentinel.js +168 -13
  105. package/hooks/dist/lib/secret-read-exemptions.js +161 -0
  106. package/hooks/gsd-agent-isolation-guard.js +4 -3
  107. package/hooks/gsd-config-reload.js +2 -2
  108. package/hooks/gsd-context-monitor.js +2 -1
  109. package/hooks/gsd-cursor-subagent-start.js +9 -6
  110. package/hooks/gsd-read-injection-scanner.js +2 -2
  111. package/hooks/gsd-secret-read-guard.js +141 -6
  112. package/hooks/gsd-statusline.js +3 -2
  113. package/hooks/gsd-workflow-guard.js +2 -1
  114. package/hooks/lib/config-read.js +49 -0
  115. package/hooks/lib/git-cmd.js +3 -1
  116. package/hooks/lib/git-head-tree.js +474 -0
  117. package/hooks/lib/isolation-sentinel.js +168 -13
  118. package/hooks/lib/secret-read-exemptions.js +161 -0
  119. package/package.json +7 -2
  120. package/pi/package-agents/gsd-executor.md +1 -1
  121. package/pi/package-agents/gsd-plan-checker.md +1 -1
  122. package/pi/package-agents/gsd-planner.md +3 -3
  123. package/pi/package-agents/gsd-verifier.md +2 -2
  124. package/pi/package-agents/manifest.json +4 -4
  125. package/pi/package-skills/gsd-autonomous/SKILL.md +1 -1
  126. package/pi/package-skills/gsd-new-milestone/SKILL.md +1 -1
  127. package/pi/package-skills/gsd-new-project/SKILL.md +1 -1
  128. package/scripts/changeset/cli.cjs +2 -2
  129. package/scripts/changeset/lint.cjs +28 -3
  130. package/scripts/changeset/serialize.cjs +12 -2
  131. package/scripts/ci-compact-v8-coverage.cjs +204 -0
  132. package/scripts/ci-fix-next-rerun.cjs +149 -0
  133. package/scripts/ci-next-health.cjs +52 -9
  134. package/scripts/ci-test-scope.cjs +77 -71
  135. package/scripts/command-contract-helpers.cjs +56 -7
  136. package/scripts/docs-guard-registry.cjs +3 -0
  137. package/scripts/gen-cli-surface-manifest.cjs +287 -0
  138. package/scripts/gen-platform-conformance-tier.cjs +258 -19
  139. package/scripts/lib/macos-conformance-tier.generated.cjs +59 -47
  140. package/scripts/lib/platform-conformance-tier.generated.cjs +23 -64
  141. package/scripts/lib/shellcheck-fetch.cjs +71 -4
  142. package/scripts/lint-removed-but-needed.cjs +562 -50
  143. package/scripts/merge-commit-pr.cjs +186 -0
  144. package/scripts/release-ancestry.cjs +86 -0
  145. package/scripts/release-notes/changelog-release-notes.cjs +345 -0
  146. package/scripts/release-notes/discord-release-summary.cjs +82 -1
  147. package/scripts/release-pipeline-pr.cjs +92 -0
  148. package/scripts/require-issue-link-policy.cjs +18 -9
  149. package/scripts/sync-next-version.cjs +11 -2
  150. package/scripts/timeout-evidence-from-ci.cjs +964 -0
  151. package/skills/gsd-autonomous/SKILL.md +1 -1
  152. package/skills/gsd-new-milestone/SKILL.md +1 -1
  153. package/skills/gsd-new-project/SKILL.md +1 -1
  154. package/vscode/package.json +1 -1
@@ -1972,6 +1972,7 @@ const KNOWN_HOST_BEHAVIORS = new Set([
1972
1972
  'ownsClaudePaths',
1973
1973
  'permissionsSchema',
1974
1974
  'pluginOnlyInstall',
1975
+ 'powershellHooksOnWindows',
1975
1976
  'projectInstructionFile',
1976
1977
  'reapplyCommand',
1977
1978
  'reclaimsKimiLegacyHooksRoot',
@@ -45,10 +45,11 @@ const configMod = require("./config.cjs");
45
45
  const { setConfigValues } = configMod;
46
46
  // eslint-disable-next-line @typescript-eslint/no-require-imports
47
47
  const planningWorkspaceMod = require("./planning-workspace.cjs");
48
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- config-file-read.cjs is an export= CommonJS module
49
+ const configFileReadMod = require("./config-file-read.cjs");
50
+ const { readConfigFileBounded } = configFileReadMod;
48
51
  const { planningDir } = planningWorkspaceMod;
49
52
  // eslint-disable-next-line @typescript-eslint/no-require-imports
50
- const nodefs = require("fs");
51
- // eslint-disable-next-line @typescript-eslint/no-require-imports
52
53
  const nodepath = require("path");
53
54
  const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
54
55
  // ─── Implementation ───────────────────────────────────────────────────────────
@@ -161,8 +162,10 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
161
162
  if (pendingGateWrites.length > 0) {
162
163
  try {
163
164
  const configJsonPath = nodepath.join(planningDir(cwd), 'config.json');
164
- if (nodefs.existsSync(configJsonPath)) {
165
- const raw = nodefs.readFileSync(configJsonPath, 'utf-8');
165
+ // Bounded read (#5759): null is an absent file; a FIFO/device/oversized one throws to the
166
+ // outer catch below ("cannot read the file path — not an error").
167
+ const raw = readConfigFileBounded(configJsonPath);
168
+ if (raw !== null) {
166
169
  try {
167
170
  JSON.parse(raw);
168
171
  }
@@ -14,13 +14,14 @@
14
14
  *
15
15
  * Subcommands:
16
16
  * detect-backend [--runtime <id>] [--agent-sdk-version <ver>] [--no-nested-dispatch]
17
- * Resolves whether the Workflow backend should activate. Both flags are
18
- * OPTIONAL (#2590): `--runtime` falls back to the canonical
19
- * `GSD_RUNTIME > config.runtime > 'claude'` chain, and
20
- * `--agent-sdk-version` to `GSD_AGENT_SDK_VERSION` then the installed
21
- * @anthropic-ai/claude-agent-sdk version. Reads the
22
- * `claude_orchestration.*` keys from .planning/config.json. Emits
23
- * { available, backend, reason }.
17
+ * Resolves whether the Workflow backend should activate. `--runtime` is
18
+ * OPTIONAL (#2590): it falls back to the canonical
19
+ * `GSD_RUNTIME > config.runtime > 'claude'` chain. `--agent-sdk-version`
20
+ * (and GSD_AGENT_SDK_VERSION) is accepted and ignored — deprecated, #4747.
21
+ * Reads the `claude_orchestration.*` keys from .planning/config.json and
22
+ * Claude Code's workflow kill switches: the CLAUDE_CODE_DISABLE_WORKFLOWS
23
+ * env var and `disableWorkflows` / `enableWorkflows` in managed, project
24
+ * local, project and user settings files. Emits { available, backend, reason }.
24
25
  *
25
26
  * emit-workflow --waves <path> --run-id <id> [--phase-dir <dir>] [--budget <n>] [--executor-model <id>]
26
27
  * Reads a wave/plan manifest JSON file and emits the generated Workflow
@@ -31,7 +32,7 @@
31
32
  * (submodule-touching plans — #2772 / #2285 finding 1).
32
33
  *
33
34
  * resolve-wave-dispatch --waves <path> --run-id <id> [--runtime <id>]
34
- * [--agent-sdk-version <ver>] [--no-nested-dispatch] [--phase-dir <dir>]
35
+ * [--agent-sdk-version <ignored, deprecated>] [--no-nested-dispatch] [--phase-dir <dir>]
35
36
  * [--budget <n>] [--executor-model <id>]
36
37
  * #2285 — the single composed seam a PRE-wave dispatch-backend selector
37
38
  * (`execute:wave:pre`) uses: resolves detect-backend + emit-workflow in
@@ -44,6 +45,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
44
45
  return (mod && mod.__esModule) ? mod : { "default": mod };
45
46
  };
46
47
  const node_fs_1 = __importDefault(require("node:fs"));
48
+ const node_os_1 = __importDefault(require("node:os"));
47
49
  const node_path_1 = __importDefault(require("node:path"));
48
50
  // eslint-disable-next-line @typescript-eslint/no-require-imports
49
51
  const io = require("./io.cjs");
@@ -55,14 +57,16 @@ const configLoader = require("./config-loader.cjs");
55
57
  const runtimeSlash = require("./runtime-slash.cjs");
56
58
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- model-resolver.cjs is an export= CommonJS module
57
59
  const modelResolver = require("./model-resolver.cjs");
60
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
61
+ const shellProjection = require("./shell-command-projection.cjs");
58
62
  const { output } = io;
59
63
  const { detectWorkflowBackend, emitWorkflowScript, resolveWaveDispatch } = core;
60
64
  const CAPABLE_HOST = { dispatch: { nested: true, background: true } };
61
65
  function usage(error) {
62
66
  error('Usage: gsd-tools claude-orchestration <detect-backend|emit-workflow|resolve-wave-dispatch> [...]\n' +
63
- ' detect-backend [--runtime <id>] [--agent-sdk-version <ver>] [--no-nested-dispatch]\n' +
67
+ ' detect-backend [--runtime <id>] [--no-nested-dispatch]\n' +
64
68
  ' emit-workflow --waves <path> --run-id <id> [--phase-dir <dir>] [--budget <n>] [--executor-model <id>]\n' +
65
- ' resolve-wave-dispatch --waves <path> --run-id <id> [--runtime <id>] [--agent-sdk-version <ver>] [--no-nested-dispatch] [--phase-dir <dir>] [--budget <n>] [--executor-model <id>]');
69
+ ' resolve-wave-dispatch --waves <path> --run-id <id> [--runtime <id>] [--no-nested-dispatch] [--phase-dir <dir>] [--budget <n>] [--executor-model <id>]');
66
70
  }
67
71
  function argValue(args, flag) {
68
72
  const i = args.indexOf(flag);
@@ -94,55 +98,120 @@ function resolveFlatClaudeOrchestrationConfig(cwd) {
94
98
  return flatConfig;
95
99
  }
96
100
  /**
97
- * Resolve the installed Agent SDK version (#2590).
98
- *
99
- * The `execute:wave:pre` fragment claimed the orchestrator "has no scriptable
100
- * way to introspect the live Agent SDK version" and told callers to omit the
101
- * flag — so gate 5 returned `agent_sdk_version_unknown` on every automated run
102
- * and the Workflow backend never activated, while `capability state` still
103
- * reported it `active: true`. That claim is true for BASH, but this router runs
104
- * in Node: the installed package's own package.json is authoritative and
105
- * requires no flag at all.
106
- *
107
- * Resolution is side-effect-free and fails closed to undefined (gate 5 then
108
- * declines, exactly as before) rather than guessing a version.
101
+ * Documented managed-settings directory per platform (#4747;
102
+ * code.claude.com/docs/en/managed-settings). Any non-darwin, non-win32 platform
103
+ * uses the Linux/WSL path.
104
+ */
105
+ function managedSettingsDir(platform) {
106
+ if (platform === 'darwin')
107
+ return '/Library/Application Support/ClaudeCode';
108
+ if (platform === 'win32')
109
+ return ['C:', 'Program Files', 'ClaudeCode'].join(node_path_1.default.win32.sep);
110
+ return '/etc/claude-code';
111
+ }
112
+ // #4747: a repo-controlled .claude/settings*.json must not hang or exhaust the CLI.
113
+ const MAX_SETTINGS_FILE_BYTES = 1024 * 1024;
114
+ /**
115
+ * Read one JSON settings file. Missing (ENOENT/ENOTDIR) is `absent`; any other
116
+ * read error, invalid JSON, or a non-object root is `unreadable` (fail-closed).
109
117
  */
110
- const AGENT_SDK_PKG = node_path_1.default.join('@anthropic-ai', 'claude-agent-sdk', 'package.json');
111
- function resolveInstalledAgentSdkVersion(cwd) {
112
- // Walk node_modules up the tree by hand rather than require.resolve: the SDK's
113
- // `exports` map does not expose './package.json', so require.resolve throws
114
- // ERR_PACKAGE_PATH_NOT_EXPORTED. Reading the file directly is exports-map
115
- // independent and cannot execute package code.
116
- for (const start of [cwd, __dirname]) {
117
- let dir;
118
+ function readJsonObjectFile(p) {
119
+ let text;
120
+ try {
121
+ const st = node_fs_1.default.statSync(p);
122
+ if (!st.isFile() || st.size > MAX_SETTINGS_FILE_BYTES)
123
+ return { status: 'unreadable' };
124
+ text = node_fs_1.default.readFileSync(p, 'utf8');
125
+ }
126
+ catch (e) {
127
+ const code = e?.code;
128
+ return code === 'ENOENT' || code === 'ENOTDIR' ? { status: 'absent' } : { status: 'unreadable' };
129
+ }
130
+ let parsed;
131
+ try {
132
+ parsed = JSON.parse(text);
133
+ }
134
+ catch {
135
+ return { status: 'unreadable' };
136
+ }
137
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
138
+ return { status: 'unreadable' };
139
+ }
140
+ return { status: 'ok', settings: parsed };
141
+ }
142
+ function readManagedSource(dir) {
143
+ try {
144
+ const base = readJsonObjectFile(node_path_1.default.join(dir, 'managed-settings.json'));
145
+ const dropInDir = node_path_1.default.join(dir, 'managed-settings.d');
146
+ let names = [];
118
147
  try {
119
- dir = node_path_1.default.resolve(start);
148
+ names = node_fs_1.default.readdirSync(dropInDir);
120
149
  }
121
- catch {
122
- continue;
150
+ catch (e) {
151
+ const code = e?.code;
152
+ if (code !== 'ENOENT' && code !== 'ENOTDIR')
153
+ return { scope: 'managed', status: 'unreadable' };
154
+ }
155
+ const dropIns = names.filter((n) => !n.startsWith('.') && n.endsWith('.json')).sort()
156
+ .map((n) => readJsonObjectFile(node_path_1.default.join(dropInDir, n)));
157
+ if (base.status === 'unreadable' || dropIns.some((d) => d.status === 'unreadable')) {
158
+ return { scope: 'managed', status: 'unreadable' };
123
159
  }
124
- for (;;) {
125
- try {
126
- const pkgPath = node_path_1.default.join(dir, 'node_modules', AGENT_SDK_PKG);
127
- if (node_fs_1.default.existsSync(pkgPath)) {
128
- const parsed = JSON.parse(node_fs_1.default.readFileSync(pkgPath, 'utf8'));
129
- if (typeof parsed.version === 'string' && parsed.version.length > 0)
130
- return parsed.version;
131
- }
132
- }
133
- catch { /* unreadable/malformed — keep walking */ }
134
- const parent = node_path_1.default.dirname(dir);
135
- if (parent === dir)
136
- break;
137
- dir = parent;
160
+ const layers = [];
161
+ if (base.status === 'ok')
162
+ layers.push(base.settings);
163
+ for (const d of dropIns) {
164
+ if (d.status === 'ok')
165
+ layers.push(d.settings);
138
166
  }
167
+ if (layers.length === 0)
168
+ return { scope: 'managed', status: 'absent' };
169
+ return { scope: 'managed', status: 'ok', settings: Object.assign({}, ...layers) };
139
170
  }
140
- return undefined;
171
+ catch {
172
+ return { scope: 'managed', status: 'unreadable' };
173
+ }
174
+ }
175
+ /**
176
+ * Read the settings files that can carry Claude Code's workflow kill switches
177
+ * (#4747). Returns exactly four sources, precedence-ordered highest first:
178
+ * managed, projectLocal, project, user. `--settings` and non-file managed
179
+ * sources are not observable. No subprocesses; never throws.
180
+ */
181
+ function readWorkflowSettingsSources(opts) {
182
+ const fileSource = (scope, p) => {
183
+ try {
184
+ const r = readJsonObjectFile(p);
185
+ return r.status === 'ok' ? { scope, status: 'ok', settings: r.settings } : { scope, status: r.status };
186
+ }
187
+ catch {
188
+ return { scope, status: 'unreadable' };
189
+ }
190
+ };
191
+ const managedDir = opts.managedDir ?? managedSettingsDir(opts.platform ?? process.platform);
192
+ const configDir = opts.env ? shellProjection.envGet(opts.env, 'CLAUDE_CONFIG_DIR') : undefined;
193
+ let user;
194
+ if (typeof configDir === 'string' && configDir.length > 0) {
195
+ user = fileSource('user', node_path_1.default.join(configDir, 'settings.json'));
196
+ }
197
+ else if (typeof opts.homedir === 'string' && opts.homedir.length > 0) {
198
+ user = fileSource('user', node_path_1.default.join(opts.homedir, '.claude', 'settings.json'));
199
+ }
200
+ else {
201
+ user = { scope: 'user', status: 'unreadable' };
202
+ }
203
+ return [
204
+ readManagedSource(managedDir),
205
+ fileSource('projectLocal', node_path_1.default.join(opts.cwd, '.claude', 'settings.local.json')),
206
+ fileSource('project', node_path_1.default.join(opts.cwd, '.claude', 'settings.json')),
207
+ user,
208
+ ];
141
209
  }
142
210
  /**
143
- * Resolve `--runtime`/`--agent-sdk-version`/`--no-nested-dispatch` into the
144
- * `{ runtimeId, hostIntegration, agentSdkVersion }` triple both `detect-backend`
145
- * and `resolve-wave-dispatch` pass to the pure detection seam.
211
+ * Resolve `--runtime`/`--no-nested-dispatch` and the Claude Code workflow kill
212
+ * switches into the `{ runtimeId, hostIntegration, workflowAvailability }` triple
213
+ * both `detect-backend` and `resolve-wave-dispatch` pass to the pure detection
214
+ * seam. `--agent-sdk-version` / GSD_AGENT_SDK_VERSION are ignored (#4747).
146
215
  */
147
216
  function resolveDetectionArgs(args, cwd) {
148
217
  // #2590: the old fallback chain was `--runtime > GSD_RUNTIME > 'unknown'`,
@@ -151,14 +220,25 @@ function resolveDetectionArgs(args, cwd) {
151
220
  // --runtime reported `runtime_not_claude` on a perfectly ordinary Claude
152
221
  // project. Delegate to the canonical resolver instead of re-deriving it.
153
222
  const runtimeId = argValue(args, '--runtime') || runtimeSlash.resolveRuntime(cwd || null);
154
- // Explicit flag wins (lets a caller pin a version); then the environment;
155
- // then the actually-installed SDK.
156
- const agentSdkVersion = argValue(args, '--agent-sdk-version')
157
- || process.env['GSD_AGENT_SDK_VERSION']
158
- || resolveInstalledAgentSdkVersion(cwd || process.cwd());
223
+ let home;
224
+ try {
225
+ home = node_os_1.default.homedir();
226
+ }
227
+ catch {
228
+ home = undefined;
229
+ }
230
+ // Test-only seam so CLI tests do not read the host's real managed settings; ignored outside GSD_TEST_MODE.
231
+ const injectedManagedDir = process.env['GSD_TEST_MODE'] === '1'
232
+ ? shellProjection.envGet(process.env, 'GSD_TEST_MANAGED_SETTINGS_DIR')
233
+ : undefined;
234
+ const testManagedDir = typeof injectedManagedDir === 'string' && injectedManagedDir.length > 0 ? injectedManagedDir : undefined;
235
+ const workflowAvailability = core.resolveWorkflowAvailability({
236
+ env: { CLAUDE_CODE_DISABLE_WORKFLOWS: shellProjection.envGet(process.env, 'CLAUDE_CODE_DISABLE_WORKFLOWS') },
237
+ sources: readWorkflowSettingsSources({ cwd: cwd || process.cwd(), env: process.env, platform: process.platform, homedir: home, ...(testManagedDir ? { managedDir: testManagedDir } : {}) }),
238
+ });
159
239
  const noNested = args.includes('--no-nested-dispatch');
160
240
  const hostIntegration = noNested ? { dispatch: { nested: false, background: true } } : CAPABLE_HOST;
161
- return { runtimeId, hostIntegration, agentSdkVersion };
241
+ return { runtimeId, hostIntegration, workflowAvailability };
162
242
  }
163
243
  /**
164
244
  * #2686 — resolve the `gsd-executor` model this dispatch should carry.
@@ -216,9 +296,9 @@ function readWavesManifest(wavesPath, error) {
216
296
  * SDK version come from flags (the orchestrator already knows these) or env.
217
297
  */
218
298
  function cmdDetectBackend(args, cwd, raw) {
219
- const { runtimeId, hostIntegration, agentSdkVersion } = resolveDetectionArgs(args, cwd);
299
+ const { runtimeId, hostIntegration, workflowAvailability } = resolveDetectionArgs(args, cwd);
220
300
  const flatConfig = resolveFlatClaudeOrchestrationConfig(cwd);
221
- const result = detectWorkflowBackend({ runtimeId, hostIntegration, config: flatConfig, agentSdkVersion });
301
+ const result = detectWorkflowBackend({ runtimeId, hostIntegration, config: flatConfig, workflowAvailability });
222
302
  output(result, raw);
223
303
  }
224
304
  /**
@@ -277,7 +357,7 @@ function cmdResolveWaveDispatch(args, cwd, raw, error) {
277
357
  const read = readWavesManifest(wavesPath, (msg) => error('resolve-wave-dispatch: ' + msg));
278
358
  if (!read.ok)
279
359
  return; // read/parse failure — error() already surfaced it loudly above
280
- const { runtimeId, hostIntegration, agentSdkVersion } = resolveDetectionArgs(args, cwd);
360
+ const { runtimeId, hostIntegration, workflowAvailability } = resolveDetectionArgs(args, cwd);
281
361
  const flatConfig = resolveFlatClaudeOrchestrationConfig(cwd);
282
362
  const budgetTokens = budgetRaw !== undefined ? parseInt(budgetRaw, 10) : undefined;
283
363
  const budget = (typeof budgetTokens === 'number' && !Number.isNaN(budgetTokens)) ? budgetTokens : undefined;
@@ -285,7 +365,7 @@ function cmdResolveWaveDispatch(args, cwd, raw, error) {
285
365
  runtimeId,
286
366
  hostIntegration,
287
367
  config: flatConfig,
288
- agentSdkVersion,
368
+ workflowAvailability,
289
369
  phaseDir,
290
370
  runId,
291
371
  waves: read.waves,
@@ -311,4 +391,4 @@ function routeClaudeOrchestrationCommand(opts) {
311
391
  usage(error);
312
392
  }
313
393
  }
314
- module.exports = { routeClaudeOrchestrationCommand };
394
+ module.exports = { routeClaudeOrchestrationCommand, readWorkflowSettingsSources, managedSettingsDir };