@opengsd/gsd-core 1.5.0-rc.4 → 1.5.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 (89) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/LICENSE +1 -1
  3. package/agents/gsd-executor.md +22 -0
  4. package/agents/gsd-phase-researcher.md +1 -1
  5. package/agents/gsd-planner.md +4 -0
  6. package/agents/gsd-project-researcher.md +1 -1
  7. package/agents/gsd-verifier.md +45 -13
  8. package/bin/install.js +56 -100
  9. package/commands/gsd/autonomous.md +1 -1
  10. package/commands/gsd/execute-phase.md +1 -1
  11. package/commands/gsd/plan-phase.md +1 -1
  12. package/gemini-extension.json +1 -1
  13. package/gsd-core/bin/gsd-tools.cjs +85 -53
  14. package/gsd-core/bin/lib/agent-command-router.cjs +2 -2
  15. package/gsd-core/bin/lib/agent-install-check.cjs +143 -0
  16. package/gsd-core/bin/lib/audit-command-router.cjs +4 -4
  17. package/gsd-core/bin/lib/capability-activation.cjs +37 -10
  18. package/gsd-core/bin/lib/capability-registry.cjs +2 -0
  19. package/gsd-core/bin/lib/capability-state.cjs +80 -30
  20. package/gsd-core/bin/lib/capability-writer.cjs +5 -4
  21. package/gsd-core/bin/lib/check-command-router.cjs +50 -51
  22. package/gsd-core/bin/lib/commands.cjs +20 -2
  23. package/gsd-core/bin/lib/config-loader.cjs +3 -4
  24. package/gsd-core/bin/lib/config-schema.cjs +1 -1
  25. package/gsd-core/bin/lib/config-types.cjs +2 -1
  26. package/gsd-core/bin/lib/config.cjs +5 -2
  27. package/gsd-core/bin/lib/decisions.cjs +19 -1
  28. package/gsd-core/bin/lib/docs.cjs +14 -2
  29. package/gsd-core/bin/lib/frontmatter.cjs +2 -2
  30. package/gsd-core/bin/lib/gap-checker.cjs +5 -2
  31. package/gsd-core/bin/lib/git-base-branch.cjs +27 -1
  32. package/gsd-core/bin/lib/graphify-command-router.cjs +6 -8
  33. package/gsd-core/bin/lib/graphify.cjs +7 -31
  34. package/gsd-core/bin/lib/gsd2-import.cjs +2 -2
  35. package/gsd-core/bin/lib/init.cjs +30 -4
  36. package/gsd-core/bin/lib/intel-command-router.cjs +6 -3
  37. package/gsd-core/bin/lib/intel.cjs +28 -34
  38. package/gsd-core/bin/lib/io.cjs +2 -4
  39. package/gsd-core/bin/lib/learnings.cjs +2 -2
  40. package/gsd-core/bin/lib/loop-resolver.cjs +45 -167
  41. package/gsd-core/bin/lib/milestone.cjs +13 -5
  42. package/gsd-core/bin/lib/model-resolver.cjs +3 -4
  43. package/gsd-core/bin/lib/phase-id.cjs +3 -5
  44. package/gsd-core/bin/lib/phase-locator.cjs +3 -6
  45. package/gsd-core/bin/lib/phase.cjs +59 -13
  46. package/gsd-core/bin/lib/probe-core.cjs +40 -11
  47. package/gsd-core/bin/lib/profile-output.cjs +5 -2
  48. package/gsd-core/bin/lib/prohibition-enforcement.cjs +660 -0
  49. package/gsd-core/bin/lib/roadmap-command-router.cjs +2 -2
  50. package/gsd-core/bin/lib/roadmap-parser.cjs +28 -25
  51. package/gsd-core/bin/lib/roadmap.cjs +9 -4
  52. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +4 -1
  53. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +24 -3
  54. package/gsd-core/bin/lib/state.cjs +75 -17
  55. package/gsd-core/bin/lib/task-command-router.cjs +2 -2
  56. package/gsd-core/bin/lib/teams-status.cjs +74 -0
  57. package/gsd-core/bin/lib/template.cjs +11 -2
  58. package/gsd-core/bin/lib/uat.cjs +64 -2
  59. package/gsd-core/bin/lib/verification.cjs +8 -5
  60. package/gsd-core/bin/lib/verify.cjs +311 -4
  61. package/gsd-core/bin/lib/workstream-inventory.cjs +2 -2
  62. package/gsd-core/bin/lib/workstream.cjs +8 -2
  63. package/gsd-core/bin/lib/worktree-safety.cjs +44 -3
  64. package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
  65. package/gsd-core/references/planner-antipatterns.md +46 -0
  66. package/gsd-core/references/planning-config.md +5 -1
  67. package/gsd-core/references/prohibition-probe.md +80 -2
  68. package/gsd-core/references/worktree-branch-check.md +11 -5
  69. package/gsd-core/templates/verification-report.md +16 -3
  70. package/gsd-core/workflows/docs-update.md +23 -31
  71. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +9 -0
  72. package/gsd-core/workflows/execute-phase.md +7 -2
  73. package/gsd-core/workflows/map-codebase.md +8 -10
  74. package/gsd-core/workflows/plan-phase.md +6 -0
  75. package/gsd-core/workflows/quick.md +26 -2
  76. package/gsd-core/workflows/settings-advanced.md +5 -5
  77. package/gsd-core/workflows/settings-integrations.md +5 -5
  78. package/gsd-core/workflows/spec-phase.md +30 -2
  79. package/gsd-core/workflows/verify-phase.md +22 -7
  80. package/hooks/dist/gsd-worktree-path-guard.js +34 -18
  81. package/hooks/gsd-worktree-path-guard.js +34 -18
  82. package/package.json +3 -3
  83. package/scripts/ci-prepare-test-scope.cjs +56 -14
  84. package/scripts/diff-touches-shipped-paths.cjs +5 -11
  85. package/scripts/gen-capability-registry.cjs +27 -1
  86. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  87. package/scripts/research-profiles.cjs +2 -2
  88. package/scripts/run-tests.cjs +1 -0
  89. package/gsd-core/bin/lib/core.cjs +0 -345
@@ -16,8 +16,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
16
16
  const node_fs_1 = __importDefault(require("node:fs"));
17
17
  const node_path_1 = __importDefault(require("node:path"));
18
18
  // eslint-disable-next-line @typescript-eslint/no-require-imports
19
- const core = require("./core.cjs");
20
- const { output, loadConfig, resolveModelInternal, pathExistsInternal, toPosixPath, checkAgentsInstalled } = core;
19
+ const io = require("./io.cjs");
20
+ const { output } = io;
21
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
22
+ const configLoader = require("./config-loader.cjs");
23
+ const { loadConfig } = configLoader;
24
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
25
+ const modelResolver = require("./model-resolver.cjs");
26
+ const { resolveModelInternal } = modelResolver;
27
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
28
+ const coreUtils = require("./core-utils.cjs");
29
+ const { pathExistsInternal, toPosixPath } = coreUtils;
30
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
31
+ const agentInstallCheck = require("./agent-install-check.cjs");
32
+ const { checkAgentsInstalled } = agentInstallCheck;
21
33
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
22
34
  // ─── Constants ────────────────────────────────────────────────────────────────
23
35
  const GSD_MARKER = '<!-- generated-by: gsd-doc-writer -->';
@@ -12,8 +12,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  const node_fs_1 = __importDefault(require("node:fs"));
13
13
  const node_path_1 = __importDefault(require("node:path"));
14
14
  // eslint-disable-next-line @typescript-eslint/no-require-imports
15
- const core = require("./core.cjs");
16
- const { output, error } = core;
15
+ const ioMod = require("./io.cjs");
16
+ const { output, error } = ioMod;
17
17
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
18
18
  // ─── Parsing engine ───────────────────────────────────────────────────────────
19
19
  /**
@@ -22,8 +22,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  const node_fs_1 = __importDefault(require("node:fs"));
23
23
  const node_path_1 = __importDefault(require("node:path"));
24
24
  // eslint-disable-next-line @typescript-eslint/no-require-imports
25
- const core = require("./core.cjs");
26
- const { escapeRegex, output, error } = core;
25
+ const io = require("./io.cjs");
26
+ const { output, error } = io;
27
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
28
+ const phaseId = require("./phase-id.cjs");
29
+ const { escapeRegex } = phaseId;
27
30
  // eslint-disable-next-line @typescript-eslint/no-require-imports
28
31
  const planningWorkspace = require("./planning-workspace.cjs");
29
32
  const { planningPaths, planningDir, findContextMdIn } = planningWorkspace;
@@ -32,6 +32,7 @@ exports.trySymbolicRef = trySymbolicRef;
32
32
  exports.tryRemoteShow = tryRemoteShow;
33
33
  exports.tryLocalBranch = tryLocalBranch;
34
34
  exports.resolveBaseBranch = resolveBaseBranch;
35
+ exports.gitWorktreeInfoInternal = gitWorktreeInfoInternal;
35
36
  exports.cmdGitBaseBranch = cmdGitBaseBranch;
36
37
  const node_fs_1 = __importDefault(require("node:fs"));
37
38
  const node_path_1 = __importDefault(require("node:path"));
@@ -159,7 +160,7 @@ function tryLocalBranch(cwd, execGit) {
159
160
  */
160
161
  function resolveBaseBranch(cwd, deps) {
161
162
  const execGit = deps?.execGit ?? shell_command_projection_cjs_1.execGit;
162
- // Derive .planning dir relative to cwd (mirrors planningDir() in core.cjs)
163
+ // Derive .planning dir relative to cwd (mirrors planningDir() in planning-workspace.cjs)
163
164
  const planningDir = node_path_1.default.join(cwd, '.planning');
164
165
  // 1. Config override
165
166
  const configured = readConfigBaseBranch(planningDir, deps);
@@ -180,6 +181,31 @@ function resolveBaseBranch(cwd, deps) {
180
181
  // 5. Last-resort default
181
182
  return 'main';
182
183
  }
184
+ /**
185
+ * Detect whether `cwd` sits inside a git worktree, and if so, return the
186
+ * absolute path of the worktree root.
187
+ */
188
+ function gitWorktreeInfoInternal(cwd) {
189
+ try {
190
+ const insideResult = (0, shell_command_projection_cjs_1.execGit)(['rev-parse', '--is-inside-work-tree'], { cwd, timeout: 5000 });
191
+ if (insideResult.exitCode !== 0) {
192
+ return { inside: false, worktreeRoot: null };
193
+ }
194
+ const insideStdout = String(insideResult.stdout || '').trim();
195
+ if (insideStdout !== 'true') {
196
+ return { inside: false, worktreeRoot: null };
197
+ }
198
+ const rootResult = (0, shell_command_projection_cjs_1.execGit)(['rev-parse', '--show-toplevel'], { cwd, timeout: 5000 });
199
+ if (rootResult.exitCode !== 0) {
200
+ return { inside: true, worktreeRoot: null };
201
+ }
202
+ const root = String(rootResult.stdout || '').trim();
203
+ return { inside: true, worktreeRoot: root || null };
204
+ }
205
+ catch {
206
+ return { inside: false, worktreeRoot: null };
207
+ }
208
+ }
183
209
  // ─── CLI entry point ──────────────────────────────────────────────────────────
184
210
  /**
185
211
  * CLI command: `gsd-tools git base-branch`
@@ -25,10 +25,8 @@
25
25
  // eslint-disable-next-line @typescript-eslint/no-require-imports
26
26
  const graphify = require("./graphify.cjs");
27
27
  // eslint-disable-next-line @typescript-eslint/no-require-imports
28
- const core = require("./core.cjs");
29
- // eslint-disable-next-line @typescript-eslint/no-require-imports
30
28
  const io = require("./io.cjs");
31
- const { ERROR_REASON } = io;
29
+ const { output, ERROR_REASON } = io;
32
30
  // ─── Implementation ───────────────────────────────────────────────────────────
33
31
  function routeGraphifyCommand({ args, cwd, raw, error, _graphify }) {
34
32
  const subcommand = args[1];
@@ -49,20 +47,20 @@ function routeGraphifyCommand({ args, cwd, raw, error, _graphify }) {
49
47
  }
50
48
  budget = parseInt(rawBudget, 10);
51
49
  }
52
- core.output(g.graphifyQuery(cwd, term, { budget }), raw);
50
+ output(g.graphifyQuery(cwd, term, { budget }), raw);
53
51
  }
54
52
  else if (subcommand === 'status') {
55
- core.output(g.graphifyStatus(cwd), raw);
53
+ output(g.graphifyStatus(cwd), raw);
56
54
  }
57
55
  else if (subcommand === 'diff') {
58
- core.output(g.graphifyDiff(cwd), raw);
56
+ output(g.graphifyDiff(cwd), raw);
59
57
  }
60
58
  else if (subcommand === 'build') {
61
59
  if (args[2] === 'snapshot') {
62
- core.output(g.writeSnapshot(cwd), raw);
60
+ output(g.writeSnapshot(cwd), raw);
63
61
  }
64
62
  else {
65
- core.output(g.graphifyBuild(cwd), raw);
63
+ output(g.graphifyBuild(cwd), raw);
66
64
  }
67
65
  }
68
66
  else {
@@ -13,32 +13,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
15
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
16
- // ─── Config Gate ─────────────────────────────────────────────────────────────
17
- /**
18
- * Check whether graphify is enabled in the project config.
19
- * Reads config.json directly via fs. Returns false by default
20
- * (when no config, no graphify key, or on error).
21
- */
22
- function isGraphifyEnabled(planningDir) {
23
- try {
24
- const configPath = node_path_1.default.join(planningDir, 'config.json');
25
- if (!node_fs_1.default.existsSync(configPath))
26
- return false;
27
- const config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf8'));
28
- if (config &&
29
- typeof config === 'object' &&
30
- 'graphify' in config &&
31
- config.graphify &&
32
- typeof config.graphify === 'object' &&
33
- 'enabled' in config.graphify &&
34
- config.graphify.enabled === true)
35
- return true;
36
- return false;
37
- }
38
- catch {
39
- return false;
40
- }
41
- }
16
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
17
+ const capabilityStateMod = require("./capability-state.cjs");
18
+ const { isCapabilityActive } = capabilityStateMod;
42
19
  /**
43
20
  * Return the standard disabled response object.
44
21
  */
@@ -290,7 +267,7 @@ function countCommitsBetween(cwd, from, to) {
290
267
  */
291
268
  function graphifyQuery(cwd, term, options = {}) {
292
269
  const planningDir = node_path_1.default.join(cwd, '.planning');
293
- if (!isGraphifyEnabled(planningDir))
270
+ if (!isCapabilityActive('graphify', cwd))
294
271
  return disabledResponse();
295
272
  const graphPath = node_path_1.default.join(planningDir, 'graphs', 'graph.json');
296
273
  if (!node_fs_1.default.existsSync(graphPath)) {
@@ -324,7 +301,7 @@ function graphifyQuery(cwd, term, options = {}) {
324
301
  */
325
302
  function graphifyStatus(cwd) {
326
303
  const planningDir = node_path_1.default.join(cwd, '.planning');
327
- if (!isGraphifyEnabled(planningDir))
304
+ if (!isCapabilityActive('graphify', cwd))
328
305
  return disabledResponse();
329
306
  const graphPath = node_path_1.default.join(planningDir, 'graphs', 'graph.json');
330
307
  if (!node_fs_1.default.existsSync(graphPath)) {
@@ -381,7 +358,7 @@ function graphifyStatus(cwd) {
381
358
  */
382
359
  function graphifyDiff(cwd) {
383
360
  const planningDir = node_path_1.default.join(cwd, '.planning');
384
- if (!isGraphifyEnabled(planningDir))
361
+ if (!isCapabilityActive('graphify', cwd))
385
362
  return disabledResponse();
386
363
  const snapshotPath = node_path_1.default.join(planningDir, 'graphs', '.last-build-snapshot.json');
387
364
  const graphPath = node_path_1.default.join(planningDir, 'graphs', 'graph.json');
@@ -422,7 +399,7 @@ function graphifyDiff(cwd) {
422
399
  */
423
400
  function graphifyBuild(cwd) {
424
401
  const planningDir = node_path_1.default.join(cwd, '.planning');
425
- if (!isGraphifyEnabled(planningDir))
402
+ if (!isCapabilityActive('graphify', cwd))
426
403
  return disabledResponse();
427
404
  const installed = checkGraphifyInstalled();
428
405
  if (!installed.installed)
@@ -472,7 +449,6 @@ function writeSnapshot(cwd) {
472
449
  }
473
450
  module.exports = {
474
451
  // Config gate
475
- isGraphifyEnabled,
476
452
  disabledResponse,
477
453
  // Subprocess
478
454
  execGraphify,
@@ -27,8 +27,8 @@ const node_path_1 = __importDefault(require("node:path"));
27
27
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
28
28
  const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
29
29
  // eslint-disable-next-line @typescript-eslint/no-require-imports
30
- const core = require("./core.cjs");
31
- const { output } = core;
30
+ const ioMod = require("./io.cjs");
31
+ const { output } = ioMod;
32
32
  // ─── Utilities ──────────────────────────────────────────────────────────────
33
33
  function readOptional(filePath) {
34
34
  try {
@@ -13,8 +13,22 @@ const node_fs_1 = __importDefault(require("node:fs"));
13
13
  const node_path_1 = __importDefault(require("node:path"));
14
14
  const node_os_1 = __importDefault(require("node:os"));
15
15
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
16
- // eslint-disable-next-line @typescript-eslint/no-require-imports -- core.cjs is an export= CommonJS module
17
- const core = require("./core.cjs");
16
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- io.cjs is an export= CommonJS module
17
+ const io = require("./io.cjs");
18
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module
19
+ const configLoader = require("./config-loader.cjs");
20
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- model-resolver.cjs is an export= CommonJS module
21
+ const modelResolver = require("./model-resolver.cjs");
22
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
23
+ const phaseLocator = require("./phase-locator.cjs");
24
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- roadmap-parser.cjs is an export= CommonJS module
25
+ const roadmapParser = require("./roadmap-parser.cjs");
26
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- core-utils.cjs is an export= CommonJS module
27
+ const coreUtils = require("./core-utils.cjs");
28
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
29
+ const phaseId = require("./phase-id.cjs");
30
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- worktree-safety.cjs is an export= CommonJS module
31
+ const worktreeSafety = require("./worktree-safety.cjs");
18
32
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
19
33
  const planningWorkspace = require("./planning-workspace.cjs");
20
34
  const secrets_cjs_1 = require("./secrets.cjs");
@@ -28,7 +42,20 @@ const security_cjs_1 = require("./security.cjs");
28
42
  const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
29
43
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
30
44
  const frontmatterMod = require("./frontmatter.cjs");
31
- const { loadConfig, resolveModelInternal, resolveGranularityInternal, assertValidGranularityOverride, findPhaseInternal, getRoadmapPhaseInternal, pathExistsInternal, gitWorktreeInfoInternal, generateSlugInternal, getMilestoneInfo, getMilestonePhaseFilter, stripShippedMilestones, extractCurrentMilestone, normalizePhaseName, toPosixPath, output, error, checkAgentsInstalled, phaseTokenMatches, } = core;
45
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- agent-install-check.cjs is an export= CommonJS module
46
+ const agentInstallCheck = require("./agent-install-check.cjs");
47
+ const { checkAgentsInstalled } = agentInstallCheck;
48
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- git-base-branch.cjs is an export= CommonJS module
49
+ const gitBaseBranch = require("./git-base-branch.cjs");
50
+ const { gitWorktreeInfoInternal } = gitBaseBranch;
51
+ const { output, error } = io;
52
+ const { loadConfig } = configLoader;
53
+ const { resolveModelInternal, resolveGranularityInternal, assertValidGranularityOverride } = modelResolver;
54
+ const { findPhaseInternal } = phaseLocator;
55
+ const { getRoadmapPhaseInternal, getMilestoneInfo, getMilestonePhaseFilter, stripShippedMilestones, extractCurrentMilestone, } = roadmapParser;
56
+ const { pathExistsInternal, generateSlugInternal, toPosixPath } = coreUtils;
57
+ const { normalizePhaseName, phaseTokenMatches } = phaseId;
58
+ const { pruneOrphanedWorktrees } = worktreeSafety;
32
59
  const { planningPaths, planningDir, planningRoot, findContextMdIn, } = planningWorkspace;
33
60
  const { determinePhaseStatus } = commandsMod;
34
61
  const { extractFrontmatter } = frontmatterMod;
@@ -1222,7 +1249,6 @@ function cmdInitManager(cwd, raw) {
1222
1249
  }
1223
1250
  function cmdInitProgress(cwd, raw) {
1224
1251
  try {
1225
- const { pruneOrphanedWorktrees } = core;
1226
1252
  pruneOrphanedWorktrees(cwd);
1227
1253
  }
1228
1254
  catch {
@@ -38,17 +38,20 @@
38
38
  * the case block).
39
39
  */
40
40
  // eslint-disable-next-line @typescript-eslint/no-require-imports
41
- const core = require("./core.cjs");
42
- // eslint-disable-next-line @typescript-eslint/no-require-imports
43
41
  const io = require("./io.cjs");
44
42
  // eslint-disable-next-line @typescript-eslint/no-require-imports
43
+ const coreUtils = require("./core-utils.cjs");
44
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
45
45
  const path = require("path");
46
46
  const { ERROR_REASON } = io;
47
+ // Default CoreModule implementation assembled from leaf modules.
48
+ // _core seam overrides this entirely for test injection.
49
+ const _defaultCore = { output: io.output, timeAgo: coreUtils.timeAgo };
47
50
  // ─── Implementation ───────────────────────────────────────────────────────────
48
51
  function routeIntelCommand({ args, cwd, raw, error, _intel, _core }) {
49
52
  // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
50
53
  const intel = _intel ?? require('./intel.cjs');
51
- const c = _core ?? core;
54
+ const c = _core ?? _defaultCore;
52
55
  const subcommand = args[1];
53
56
  if (subcommand === 'query') {
54
57
  const term = args[2];
@@ -6,7 +6,8 @@
6
6
  * Intel files live in .planning/intel/ and store structured data about
7
7
  * the project's files, APIs, dependencies, architecture, and tech stack.
8
8
  *
9
- * All public functions gate on intel.enabled config (no-op when false).
9
+ * All public functions gate on isCapabilityActive('intel', cwd) — the shared
10
+ * tri-state resolver (installed + surfaced + intel.enabled config key).
10
11
  *
11
12
  * ADR-457 build-at-publish: the hand-written bin/lib/intel.cjs collapsed
12
13
  * to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
@@ -19,6 +20,9 @@ const node_fs_1 = __importDefault(require("node:fs"));
19
20
  const node_path_1 = __importDefault(require("node:path"));
20
21
  const node_crypto_1 = __importDefault(require("node:crypto"));
21
22
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
23
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
24
+ const capabilityStateMod = require("./capability-state.cjs");
25
+ const { isCapabilityActive } = capabilityStateMod;
22
26
  // ─── Constants ───────────────────────────────────────────────────────────────
23
27
  const INTEL_DIR = '.planning/intel';
24
28
  const INTEL_FILES = {
@@ -38,30 +42,20 @@ function ensureIntelDir(planningDir) {
38
42
  return intelPath;
39
43
  }
40
44
  /**
41
- * Check whether intel is enabled in the project config.
42
- * Reads config.json directly via fs. Returns false by default
43
- * (when no config, no intel key, or on error).
45
+ * Check whether intel is active (installed, surfaced, and config-enabled) for the project at cwd.
46
+ * Delegates to the shared tri-state capability resolver (isCapabilityActive) which honours the
47
+ * install profile, runtime surface, and activationKey (intel.enabled config gate).
48
+ *
49
+ * NOTE: planningDir is the legacy entry-point; cwd is derived as path.dirname(planningDir).
50
+ * Callers that have cwd directly may call isCapabilityActive('intel', cwd) themselves.
51
+ *
52
+ * INVARIANT: planningDir is always `<cwd>/.planning` (i.e. path.join(cwd, '.planning')).
53
+ * The intel-command-router always constructs planningDir as path.join(cwd, '.planning'),
54
+ * so path.dirname(planningDir) === cwd is guaranteed. If a workstream-aware planningDir
55
+ * were ever passed here, the dirname would be wrong — but no caller does that.
44
56
  */
45
- function isIntelEnabled(planningDir) {
46
- try {
47
- const configPath = node_path_1.default.join(planningDir, 'config.json');
48
- const raw = (0, shell_command_projection_cjs_1.platformReadSync)(configPath);
49
- if (raw === null)
50
- return false;
51
- const config = JSON.parse(raw);
52
- if (config &&
53
- typeof config === 'object' &&
54
- 'intel' in config &&
55
- config.intel &&
56
- typeof config.intel === 'object' &&
57
- 'enabled' in config.intel &&
58
- config.intel.enabled === true)
59
- return true;
60
- return false;
61
- }
62
- catch {
63
- return false;
64
- }
57
+ function isIntelCapabilityActive(planningDir) {
58
+ return isCapabilityActive('intel', node_path_1.default.dirname(planningDir));
65
59
  }
66
60
  /**
67
61
  * Return the standard disabled response object.
@@ -152,7 +146,7 @@ function matchesInValue(value, lowerTerm) {
152
146
  * Searches across all JSON intel files in INTEL_FILES (keys and values), including arch-decisions.json (parsed as JSON, not as text).
153
147
  */
154
148
  function intelQuery(term, planningDir) {
155
- if (!isIntelEnabled(planningDir))
149
+ if (!isIntelCapabilityActive(planningDir))
156
150
  return disabledResponse();
157
151
  const matches = [];
158
152
  let total = 0;
@@ -175,7 +169,7 @@ function intelQuery(term, planningDir) {
175
169
  * A file is considered stale if its updated_at is older than 24 hours.
176
170
  */
177
171
  function intelStatus(planningDir) {
178
- if (!isIntelEnabled(planningDir))
172
+ if (!isIntelCapabilityActive(planningDir))
179
173
  return disabledResponse();
180
174
  const STALE_MS = 24 * 60 * 60 * 1000; // 24 hours
181
175
  const now = Date.now();
@@ -210,7 +204,7 @@ function intelStatus(planningDir) {
210
204
  * Show changes since the last full refresh by comparing file hashes.
211
205
  */
212
206
  function intelDiff(planningDir) {
213
- if (!isIntelEnabled(planningDir))
207
+ if (!isIntelCapabilityActive(planningDir))
214
208
  return disabledResponse();
215
209
  const snapshotPath = intelFilePath(planningDir, '.last-refresh.json');
216
210
  const snapshot = safeReadJson(snapshotPath);
@@ -242,7 +236,7 @@ function intelDiff(planningDir) {
242
236
  * The actual update is performed by the intel-updater agent (PLAN-02).
243
237
  */
244
238
  function intelUpdate(planningDir) {
245
- if (!isIntelEnabled(planningDir))
239
+ if (!isIntelCapabilityActive(planningDir))
246
240
  return disabledResponse();
247
241
  return {
248
242
  action: 'spawn_agent',
@@ -280,7 +274,7 @@ function saveRefreshSnapshot(planningDir) {
280
274
  * Writes .last-refresh.json with accurate timestamps and hashes.
281
275
  */
282
276
  function intelSnapshot(planningDir) {
283
- if (!isIntelEnabled(planningDir))
277
+ if (!isIntelCapabilityActive(planningDir))
284
278
  return disabledResponse();
285
279
  return saveRefreshSnapshot(planningDir);
286
280
  }
@@ -288,7 +282,7 @@ function intelSnapshot(planningDir) {
288
282
  * Validate all intel files for correctness and freshness.
289
283
  */
290
284
  function intelValidate(planningDir) {
291
- if (!isIntelEnabled(planningDir))
285
+ if (!isIntelCapabilityActive(planningDir))
292
286
  return disabledResponse();
293
287
  const errors = [];
294
288
  const warnings = [];
@@ -375,7 +369,7 @@ function intelValidate(planningDir) {
375
369
  * mistake silence for "nothing exists".
376
370
  */
377
371
  function intelApiSurface(planningDir) {
378
- if (!isIntelEnabled(planningDir))
372
+ if (!isIntelCapabilityActive(planningDir))
379
373
  return disabledResponse();
380
374
  const intelPath = ensureIntelDir(planningDir);
381
375
  const apiMapPath = node_path_1.default.join(intelPath, INTEL_FILES.apis);
@@ -426,7 +420,7 @@ function intelApiSurface(planningDir) {
426
420
  * Patch _meta.updated_at in a JSON intel file to the current timestamp.
427
421
  * Reads the file, updates _meta.updated_at, increments version, writes back.
428
422
  *
429
- * NOTE: Does not gate on isIntelEnabled — operates on arbitrary file paths
423
+ * NOTE: Does not gate on isCapabilityActive — operates on arbitrary file paths
430
424
  * for use by agents patching individual files outside the intel store.
431
425
  */
432
426
  function intelPatchMeta(filePath) {
@@ -458,7 +452,7 @@ function intelPatchMeta(filePath) {
458
452
  /**
459
453
  * Extract exports from a JS/CJS file by parsing module.exports or exports.X patterns.
460
454
  *
461
- * NOTE: Does not gate on isIntelEnabled — operates on arbitrary source files
455
+ * NOTE: Does not gate on isCapabilityActive — operates on arbitrary source files
462
456
  * for use by agents building intel data from project files.
463
457
  */
464
458
  function intelExtractExports(filePath) {
@@ -583,7 +577,7 @@ module.exports = {
583
577
  intelApiSurface,
584
578
  // Utilities
585
579
  ensureIntelDir,
586
- isIntelEnabled,
580
+ isIntelCapabilityActive,
587
581
  // Constants
588
582
  INTEL_FILES,
589
583
  INTEL_DIR,
@@ -5,10 +5,8 @@
5
5
  *
6
6
  * Extracted from core.cts (ADR-857 rollout phase 1 / issue #859).
7
7
  * The hand-written bodies are preserved byte-for-behaviour; only the module
8
- * boundary moved. core.cts re-exports every symbol here under its own
9
- * `export =` object so existing consumers are unaffected.
10
- *
11
- * New imports should pull I/O primitives from io.cjs directly.
8
+ * boundary moved. The core.cjs re-export spine was retired in epic #1267;
9
+ * callers import I/O primitives from io.cjs directly.
12
10
  */
13
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -23,8 +23,8 @@ const node_path_1 = __importDefault(require("node:path"));
23
23
  const node_crypto_1 = __importDefault(require("node:crypto"));
24
24
  const node_os_1 = __importDefault(require("node:os"));
25
25
  // eslint-disable-next-line @typescript-eslint/no-require-imports
26
- const core = require("./core.cjs");
27
- const { output, error: coreError } = core;
26
+ const ioMod = require("./io.cjs");
27
+ const { output, error: coreError } = ioMod;
28
28
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
29
29
  // ─── Constants ───────────────────────────────────────────────────────────────
30
30
  const DEFAULT_STORE_DIR = node_path_1.default.join(node_os_1.default.homedir(), '.gsd', 'knowledge');