@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
@@ -8,11 +8,13 @@
8
8
  var __importDefault = (this && this.__importDefault) || function (mod) {
9
9
  return (mod && mod.__esModule) ? mod : { "default": mod };
10
10
  };
11
- const node_fs_1 = __importDefault(require("node:fs"));
12
11
  const node_path_1 = __importDefault(require("node:path"));
13
12
  // eslint-disable-next-line @typescript-eslint/no-require-imports
14
13
  const planningWorkspaceMod = require("./planning-workspace.cjs");
15
14
  const { planningDir, planningRoot } = planningWorkspaceMod;
15
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- config-file-read.cjs is an export= CommonJS module
16
+ const configFileReadMod = require("./config-file-read.cjs");
17
+ const { readConfigFileBounded } = configFileReadMod;
16
18
  function _getNestedConfigValue(config, dotKey) {
17
19
  const segments = dotKey.split('.');
18
20
  let current = config;
@@ -34,7 +36,11 @@ function _getNestedConfigValue(config, dotKey) {
34
36
  const _warnedRawConfigPaths = new Set();
35
37
  function _readRawConfigKey(filePath, dotKey, quiet = false) {
36
38
  try {
37
- const raw = node_fs_1.default.readFileSync(filePath, 'utf8');
39
+ // Bounded read (#5759): null is an absent file; a FIFO/device/oversized one throws to the
40
+ // catch below, which reports the key as not found.
41
+ const raw = readConfigFileBounded(filePath);
42
+ if (raw === null)
43
+ return { found: false, value: undefined };
38
44
  let parsed;
39
45
  try {
40
46
  parsed = JSON.parse(raw);
@@ -88,10 +88,14 @@ async function routeCapabilityCommand({ args, cwd, raw }) {
88
88
  } catch {
89
89
  return undefined; // cannot even resolve the project config dir — permissive default
90
90
  }
91
- if (!fs.existsSync(cfgPath)) return undefined; // no project config — permissive default
92
91
  let cfg;
93
92
  try {
94
- cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
93
+ // #5759: bounded regular-file read — a FIFO/device/oversized config throws (fails closed
94
+ // below) instead of blocking; null is an absent file.
95
+ const { readConfigFileBounded } = require('./config-file-read.cjs');
96
+ const rawCfg = readConfigFileBounded(cfgPath);
97
+ if (rawCfg === null) return undefined; // no project config — permissive default
98
+ cfg = JSON.parse(rawCfg);
95
99
  } catch {
96
100
  // Config is PRESENT but unreadable/unparseable: a security policy must not silently
97
101
  // downgrade to permissive. Fail CLOSED — lockdown ([]) blocks external installs (local
@@ -158,7 +158,7 @@ function readSmallRegularFile(filePath, maxBytes) {
158
158
  */
159
159
  function readSmallRegularFileBuffer(filePath, maxBytes) {
160
160
  // O_RDONLY | O_NONBLOCK: never block on opening a FIFO/device — return the fd so fstat can reject it.
161
- const openFlags = node_fs_1.default.constants.O_RDONLY | node_fs_1.default.constants.O_NONBLOCK;
161
+ const openFlags = node_fs_1.default.constants.O_RDONLY | node_fs_1.default.constants.O_NONBLOCK | (node_fs_1.default.constants.O_NOCTTY || 0);
162
162
  let fd;
163
163
  try {
164
164
  fd = node_fs_1.default.openSync(filePath, openFlags);
@@ -80,6 +80,16 @@ const LOCK_MAX_BODY_BYTES = 64 * 1024;
80
80
  */
81
81
  const LOCK_MAX_ATTEMPTS = 8;
82
82
  const LOCK_RETRY_BACKOFF_MS = 25;
83
+ /** The exact (dev, ino) of `p`, or nulls when it cannot be statted. */
84
+ function statIdentity(p) {
85
+ try {
86
+ const st = node_fs_1.default.statSync(p, { bigint: true });
87
+ return { dev: typeof st.dev === 'bigint' ? st.dev : null, ino: typeof st.ino === 'bigint' ? st.ino : null };
88
+ }
89
+ catch {
90
+ return { dev: null, ino: null };
91
+ }
92
+ }
83
93
  // ---------------------------------------------------------------------------
84
94
  // Tokens + backoff
85
95
  // ---------------------------------------------------------------------------
@@ -308,16 +318,9 @@ function readParsedLockBounded(lockPath) {
308
318
  * "changed" and retries rather than stealing. Never throws.
309
319
  */
310
320
  function lockIdentity(lockPath) {
311
- let dev = null;
312
- let ino = null;
313
- try {
314
- const st = node_fs_1.default.statSync(lockPath);
315
- dev = typeof st.dev === 'number' ? st.dev : null;
316
- ino = typeof st.ino === 'number' ? st.ino : null;
317
- }
318
- catch {
321
+ const { dev, ino } = statIdentity(lockPath);
322
+ if (dev === null || ino === null)
319
323
  return { dev: null, ino: null, ts: null }; // vanished/unstatable — treat as changed.
320
- }
321
324
  const ts = readParsedLockBounded(lockPath).ts;
322
325
  return { dev, ino, ts };
323
326
  }
@@ -437,14 +440,7 @@ function acquireLock(lockPath, opts) {
437
440
  }
438
441
  // Capture the lock inode's (dev, ino) so releaseLock can confirm, immediately before rmSync, that
439
442
  // the path still holds OUR inode. Best-effort: a null dev/ino just falls back to the token check.
440
- let dev = null;
441
- let ino = null;
442
- try {
443
- const lst = node_fs_1.default.statSync(lockPath);
444
- dev = typeof lst.dev === 'number' ? lst.dev : null;
445
- ino = typeof lst.ino === 'number' ? lst.ino : null;
446
- }
447
- catch { /* best-effort — release falls back to the token check alone */ }
443
+ const { dev, ino } = statIdentity(lockPath); // best-effort — null falls back to the token check alone
448
444
  return { path: lockPath, token, dev, ino };
449
445
  }
450
446
  catch (err) {
@@ -456,7 +452,7 @@ function acquireLock(lockPath, opts) {
456
452
  // Held — decide whether to steal.
457
453
  let st;
458
454
  try {
459
- st = node_fs_1.default.statSync(lockPath);
455
+ st = node_fs_1.default.statSync(lockPath, { bigint: true });
460
456
  }
461
457
  catch {
462
458
  continue; // lock vanished between open and stat — retry the create immediately.
@@ -466,7 +462,7 @@ function acquireLock(lockPath, opts) {
466
462
  // even if the file `mtime` is stale-old. A legacy/garbage/no-`ts` body — and a FUTURE/implausible
467
463
  // `ts` — falls back to the file `mtime` age so a planted/clock-skewed future ts can never deadlock.
468
464
  const parsed = readParsedLockBounded(lockPath);
469
- const age = lockAgeMs(parsed.ts, st.mtimeMs);
465
+ const age = lockAgeMs(parsed.ts, Number(st.mtimeMs));
470
466
  if (age <= LOCK_STALE_MS) { // genuinely held (fresh) — blocked.
471
467
  const b = blocked(attempt);
472
468
  if (b === 'retry')
@@ -474,8 +470,8 @@ function acquireLock(lockPath, opts) {
474
470
  return b;
475
471
  }
476
472
  const decisionIdentity = {
477
- dev: typeof st.dev === 'number' ? st.dev : null,
478
- ino: typeof st.ino === 'number' ? st.ino : null,
473
+ dev: typeof st.dev === 'bigint' ? st.dev : null,
474
+ ino: typeof st.ino === 'bigint' ? st.ino : null,
479
475
  ts: parsed.ts,
480
476
  };
481
477
  if (isSameHost(parsed) && parsed.pid !== null) {
@@ -553,13 +549,9 @@ function releaseLock(handle) {
553
549
  if (lockBodyToken(body) !== handle.token && body !== handle.token)
554
550
  return; // not our token (PRIMARY).
555
551
  if (handle.dev !== null && handle.ino !== null) {
556
- let cur;
557
- try {
558
- cur = node_fs_1.default.statSync(handle.path);
559
- }
560
- catch {
552
+ const cur = statIdentity(handle.path);
553
+ if (cur.dev === null || cur.ino === null)
561
554
  return; // vanished/unstatable between read and rmSync → nothing of ours to release.
562
- }
563
555
  if (cur.dev !== handle.dev || cur.ino !== handle.ino)
564
556
  return; // successor inode — not ours.
565
557
  }