@opengsd/gsd-core 1.17.0 → 1.18.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-advisor-researcher.compact.md +1 -1
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-assumptions-analyzer.compact.md +1 -1
- package/agents/gsd-assumptions-analyzer.md +1 -1
- package/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +1 -1
- package/agents/gsd-code-reviewer.md +1 -1
- package/agents/gsd-codebase-mapper.compact.md +1 -1
- package/agents/gsd-codebase-mapper.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-verifier.compact.md +4 -4
- package/agents/gsd-doc-verifier.md +4 -3
- package/agents/gsd-doc-writer.compact.md +23 -5
- package/agents/gsd-doc-writer.md +19 -6
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +7 -3
- package/agents/gsd-integration-checker.compact.md +1 -1
- package/agents/gsd-integration-checker.md +1 -1
- package/agents/gsd-nyquist-auditor.compact.md +1 -1
- package/agents/gsd-nyquist-auditor.md +1 -1
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +2 -1
- package/agents/gsd-planner.md +5 -4
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +5 -5
- package/agents/gsd-research-synthesizer.md +5 -5
- package/agents/gsd-roadmapper.compact.md +1 -1
- package/agents/gsd-roadmapper.md +1 -1
- package/agents/gsd-security-auditor.compact.md +1 -1
- package/agents/gsd-security-auditor.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +1 -1
- package/agents/gsd-ui-auditor.md +1 -1
- package/agents/gsd-ui-checker.compact.md +1 -1
- package/agents/gsd-ui-checker.md +1 -1
- package/agents/gsd-ui-researcher.compact.md +13 -1
- package/agents/gsd-ui-researcher.md +28 -1
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +38 -2
- package/commands/gsd/code-review.md +8 -0
- package/commands/gsd/pause-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +18 -11
- package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
- package/gsd-core/bin/lib/capability-lock.cjs +29 -7
- package/gsd-core/bin/lib/capability-registry.cjs +70 -64
- package/gsd-core/bin/lib/capability-source.cjs +3 -1
- package/gsd-core/bin/lib/capability-state.cjs +6 -3
- package/gsd-core/bin/lib/capability-validator.cjs +6 -4
- package/gsd-core/bin/lib/commands.cjs +5 -1
- package/gsd-core/bin/lib/config-loader.cjs +3 -3
- package/gsd-core/bin/lib/decision-coverage-support.cjs +19 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +15 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +13 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
- package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +60 -5
- package/gsd-core/bin/lib/init.cjs +11 -4
- package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
- package/gsd-core/bin/lib/install-profiles.cjs +5 -3
- package/gsd-core/bin/lib/phase.cjs +120 -11
- package/gsd-core/bin/lib/report-parser.cjs +57 -1
- package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
- package/gsd-core/bin/lib/roadmap.cjs +56 -5
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +47 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +2 -0
- package/gsd-core/bin/lib/smart-entry.cjs +24 -16
- package/gsd-core/bin/lib/state-document.cjs +37 -3
- package/gsd-core/bin/lib/state-transition.cjs +31 -10
- package/gsd-core/bin/lib/state.cjs +90 -60
- package/gsd-core/bin/lib/task-command-router.cjs +2 -3
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
- package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
- package/gsd-core/bin/lib/verify.cjs +36 -1
- package/gsd-core/references/agent-contracts.md +1 -1
- package/gsd-core/references/agent-skills-bootstrap.md +26 -7
- package/gsd-core/references/git-integration.md +3 -3
- package/gsd-core/references/planner-reversibility.md +18 -4
- package/gsd-core/references/planner-verify-command-grounding.md +18 -0
- package/gsd-core/references/tdd.md +4 -3
- package/gsd-core/references/verifier-phase-gates.md +8 -1
- package/gsd-core/workflows/add-backlog.md +10 -2
- package/gsd-core/workflows/add-phase.md +5 -1
- package/gsd-core/workflows/add-tests.md +5 -1
- package/gsd-core/workflows/add-todo.md +5 -1
- package/gsd-core/workflows/ai-integration-phase.md +5 -1
- package/gsd-core/workflows/audit-milestone.md +21 -9
- package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
- package/gsd-core/workflows/autonomous.md +52 -64
- package/gsd-core/workflows/check-todos.md +5 -1
- package/gsd-core/workflows/code-review-fix.md +5 -1
- package/gsd-core/workflows/code-review.md +5 -1
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
- package/gsd-core/workflows/complete-milestone.md +25 -5
- package/gsd-core/workflows/debug.md +5 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +11 -3
- package/gsd-core/workflows/discuss-phase.md +5 -1
- package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
- package/gsd-core/workflows/docs-update.md +27 -5
- package/gsd-core/workflows/edit-phase.md +5 -1
- package/gsd-core/workflows/eval-review.md +11 -3
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +12 -6
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +19 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +5 -1
- package/gsd-core/workflows/execute-phase.md +37 -6
- package/gsd-core/workflows/execute-plan.md +8 -4
- package/gsd-core/workflows/extract-learnings.md +6 -2
- package/gsd-core/workflows/forensics.md +5 -1
- package/gsd-core/workflows/import.md +5 -1
- package/gsd-core/workflows/insert-phase.md +5 -1
- package/gsd-core/workflows/manager.md +10 -2
- package/gsd-core/workflows/map-codebase.md +5 -1
- package/gsd-core/workflows/milestone-summary.md +10 -2
- package/gsd-core/workflows/new-milestone.md +13 -5
- package/gsd-core/workflows/new-project.md +9 -5
- package/gsd-core/workflows/next.md +8 -4
- package/gsd-core/workflows/pause-work.md +3 -3
- package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +5 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
- package/gsd-core/workflows/plan-phase.md +41 -73
- package/gsd-core/workflows/plan-review-convergence.md +17 -5
- package/gsd-core/workflows/plant-seed.md +8 -4
- package/gsd-core/workflows/progress.md +6 -2
- package/gsd-core/workflows/quick-batch.md +5 -1
- package/gsd-core/workflows/quick.md +22 -7
- package/gsd-core/workflows/remove-phase.md +5 -1
- package/gsd-core/workflows/resume-project.md +5 -1
- package/gsd-core/workflows/review.md +6 -2
- package/gsd-core/workflows/scan.md +5 -1
- package/gsd-core/workflows/secure-phase.md +6 -2
- package/gsd-core/workflows/settings.md +3 -3
- package/gsd-core/workflows/ship.md +13 -10
- package/gsd-core/workflows/thread.md +10 -2
- package/gsd-core/workflows/transition.md +5 -1
- package/gsd-core/workflows/ui-phase.md +5 -1
- package/gsd-core/workflows/ui-review.md +6 -2
- package/gsd-core/workflows/ultraplan-phase.md +5 -1
- package/gsd-core/workflows/update.md +5 -1
- package/gsd-core/workflows/validate-phase.md +6 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
- package/gsd-core/workflows/verify-work.md +11 -3
- package/hooks/dist/gsd-agent-isolation-guard.js +3 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +2 -1
- package/hooks/dist/gsd-secret-read-guard.js +72 -14
- package/hooks/dist/gsd-validate-commit.sh +27 -5
- package/hooks/gsd-agent-isolation-guard.js +3 -1
- package/hooks/gsd-cursor-subagent-start.js +2 -1
- package/hooks/gsd-secret-read-guard.js +72 -14
- package/hooks/gsd-validate-commit.sh +27 -5
- package/package.json +1 -1
- package/scripts/audit-workflow-script-paths.cjs +1 -1
- package/scripts/diagnose-validate-commit-in-tier.cjs +270 -0
- package/scripts/diagnose-validate-commit-phases.cjs +348 -0
- package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
- package/scripts/docs-guard-registry.cjs +2 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +9 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +16 -0
- package/scripts/lint-phase-arg-assignment.cjs +2 -2
- package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
- package/scripts/release-tarball-smoke.cjs +1 -0
- package/scripts/xtrace-phase-timing.cjs +205 -0
- package/skills/gsd-code-review/SKILL.md +8 -0
- package/skills/gsd-pause-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
|
@@ -47,6 +47,8 @@ const { loadConfig } = configLoaderMod;
|
|
|
47
47
|
const installProfilesMod = require("./install-profiles.cjs");
|
|
48
48
|
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents, workflowAgentRefs } = installProfilesMod;
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
|
+
const ledgerMod = require("./capability-ledger.cjs");
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
52
|
const surfaceMod = require("./surface.cjs");
|
|
51
53
|
const { resolveSurface } = surfaceMod;
|
|
52
54
|
// ─── Prototype-pollution guard (inline literal, CodeQL barrier) ───────────────
|
|
@@ -265,16 +267,17 @@ function _loadInstalledSkillsManifest(configDir) {
|
|
|
265
267
|
// SKILL.md must NOT register the stem — otherwise the capability would be
|
|
266
268
|
// wrongly reported surfaced/enabled and a verify:post hook would render
|
|
267
269
|
// for a skill that cannot run.
|
|
268
|
-
|
|
269
|
-
continue;
|
|
270
|
+
// #5121: bounded read — a missing, non-regular (FIFO/device) or oversized SKILL.md registers no stem.
|
|
270
271
|
let content = '';
|
|
271
272
|
try {
|
|
272
|
-
content =
|
|
273
|
+
content = ledgerMod.readSkillFile(skillMdPath);
|
|
273
274
|
}
|
|
274
275
|
catch {
|
|
275
276
|
// SKILL.md present but unreadable — register with no deps (parity with
|
|
276
277
|
// loadSkillsManifest's readFileSync catch branch).
|
|
277
278
|
}
|
|
279
|
+
if (content === null)
|
|
280
|
+
continue;
|
|
278
281
|
// Parse requires: via the SAME shared parser loadSkillsManifest uses, so
|
|
279
282
|
// installed-runtime dependency resolution can never silently diverge from
|
|
280
283
|
// the source-tree path (single source of truth — no duplicated regex).
|
|
@@ -1899,6 +1899,10 @@ const REVIEWER_EFFORT_LEVELS = new Set(['minimal', 'low', 'medium', 'high', 'xhi
|
|
|
1899
1899
|
* silently rot away from reality (DEFECT.GENERATIVE-FIX).
|
|
1900
1900
|
*/
|
|
1901
1901
|
const KNOWN_HOST_BEHAVIORS = new Set([
|
|
1902
|
+
// #2586: declares runtime-level feature axes GSD does not/cannot support on
|
|
1903
|
+
// omitting the key on every other runtime carries no inverted meaning.
|
|
1904
|
+
// this host (e.g. Codex's `["context-warnings","phase-lifecycle-display"]`)
|
|
1905
|
+
// — present-and-populated / absent-is-unsupported-empty convention, so
|
|
1902
1906
|
'agentFileExtension',
|
|
1903
1907
|
'agentFrontmatterExtensions',
|
|
1904
1908
|
'agentManifestStyle',
|
|
@@ -1916,6 +1920,7 @@ const KNOWN_HOST_BEHAVIORS = new Set([
|
|
|
1916
1920
|
'combinedFamilyInstall',
|
|
1917
1921
|
'commandBodyConverter',
|
|
1918
1922
|
'contentRewriteProfile',
|
|
1923
|
+
'defaultHomeIncludeRoot',
|
|
1919
1924
|
'doneBannerStyle',
|
|
1920
1925
|
'flatCommandDir',
|
|
1921
1926
|
'frontmatterDialect',
|
|
@@ -1953,6 +1958,7 @@ const KNOWN_HOST_BEHAVIORS = new Set([
|
|
|
1953
1958
|
'requiresSubagentPair',
|
|
1954
1959
|
'restoreAtRefTildeInAgents',
|
|
1955
1960
|
'restoreAtRefTildeInSpecTree',
|
|
1961
|
+
'retargetsGsdCoreProsePath',
|
|
1956
1962
|
'retiredArtifacts',
|
|
1957
1963
|
'rewriteClaudeAtIncludes',
|
|
1958
1964
|
'settingsFileByScope',
|
|
@@ -1972,10 +1978,6 @@ const KNOWN_HOST_BEHAVIORS = new Set([
|
|
|
1972
1978
|
'specRootSkillPass',
|
|
1973
1979
|
'tomlConfigInstall',
|
|
1974
1980
|
'trackCategoryDescription',
|
|
1975
|
-
// #2586: declares runtime-level feature axes GSD does not/cannot support on
|
|
1976
|
-
// this host (e.g. Codex's `["context-warnings","phase-lifecycle-display"]`)
|
|
1977
|
-
// — present-and-populated / absent-is-unsupported-empty convention, so
|
|
1978
|
-
// omitting the key on every other runtime carries no inverted meaning.
|
|
1979
1981
|
'unsupportedFeatures',
|
|
1980
1982
|
'verificationStyle',
|
|
1981
1983
|
'writeCategoryDescription',
|
|
@@ -2986,6 +2986,8 @@ async function cmdWebsearch(query, options, raw) {
|
|
|
2986
2986
|
'Accept': 'application/json',
|
|
2987
2987
|
'X-Subscription-Token': apiKey
|
|
2988
2988
|
},
|
|
2989
|
+
// never follow redirects: X-Subscription-Token must not leave api.search.brave.com (#5409)
|
|
2990
|
+
redirect: 'manual',
|
|
2989
2991
|
signal: ac.signal
|
|
2990
2992
|
});
|
|
2991
2993
|
}
|
|
@@ -3011,7 +3013,9 @@ async function cmdWebsearch(query, options, raw) {
|
|
|
3011
3013
|
const status = response.status;
|
|
3012
3014
|
const isRetryable = status === 429 || status >= 500;
|
|
3013
3015
|
if (!isRetryable) {
|
|
3014
|
-
// Non-retryable 4xx
|
|
3016
|
+
// Non-retryable 4xx and every 3xx (redirect: 'manual' hands them back as-is, #5409) —
|
|
3017
|
+
// fail immediately, no attempts field. Keep 3xx out of isRetryable.
|
|
3018
|
+
response.body?.cancel().catch(() => { });
|
|
3015
3019
|
output({ available: false, error: `API error: ${status}` }, raw, '');
|
|
3016
3020
|
return;
|
|
3017
3021
|
}
|
|
@@ -25,11 +25,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
25
25
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
26
|
};
|
|
27
27
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
28
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
29
28
|
const node_path_1 = __importDefault(require("node:path"));
|
|
30
29
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
31
30
|
// #4717: runtime-identity fill — env rung + per-install marker rung.
|
|
32
31
|
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
32
|
+
const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
|
|
33
33
|
const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
|
|
34
34
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
35
35
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
@@ -935,7 +935,7 @@ function loadConfigResolvedInternal(cwd, options = {}) {
|
|
|
935
935
|
// untouched. Faults in the global file stay silent in this branch (the
|
|
936
936
|
// project config governs; the nearer file is the actionable one).
|
|
937
937
|
try {
|
|
938
|
-
const shadowHome =
|
|
938
|
+
const shadowHome = (0, runtime_homes_cjs_1.resolveGsdHome)();
|
|
939
939
|
const shadowPath = node_path_1.default.join(shadowHome, '.gsd', 'defaults.json');
|
|
940
940
|
const shadowRead = _readConfigFile(shadowPath);
|
|
941
941
|
if (shadowRead.kind === 'ok') {
|
|
@@ -997,7 +997,7 @@ function loadConfigResolvedInternal(cwd, options = {}) {
|
|
|
997
997
|
}
|
|
998
998
|
// Branch D or E: no .planning/
|
|
999
999
|
try {
|
|
1000
|
-
const home =
|
|
1000
|
+
const home = (0, runtime_homes_cjs_1.resolveGsdHome)();
|
|
1001
1001
|
const globalDefaultsPath = node_path_1.default.join(home, '.gsd', 'defaults.json');
|
|
1002
1002
|
const globalRead = _readConfigFile(globalDefaultsPath);
|
|
1003
1003
|
if (globalRead.kind === 'fault') {
|
|
@@ -21,6 +21,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
exports.decisionMentioned = decisionMentioned;
|
|
24
|
+
exports.phaseDirProblem = phaseDirProblem;
|
|
24
25
|
exports.loadPlanContents = loadPlanContents;
|
|
25
26
|
exports.loadSummaryContents = loadSummaryContents;
|
|
26
27
|
exports.loadDecisionExtraction = loadDecisionExtraction;
|
|
@@ -70,6 +71,24 @@ function decisionMentioned(haystack, decision) {
|
|
|
70
71
|
return phrase ? normalizePhrase(haystack).includes(phrase) : false;
|
|
71
72
|
}
|
|
72
73
|
// ─── File reading ─────────────────────────────────────────────────────────────
|
|
74
|
+
// #4939: loadPlanContents answers no files for a path that is not there, and that reads
|
|
75
|
+
// as "no plan cites anything" — so a phase-dir argument that names no directory
|
|
76
|
+
// (the phase NUMBER in the phase-dir slot) must be told apart BEFORE the scan.
|
|
77
|
+
// Answers null for a directory, else what is wrong with the path and what to do
|
|
78
|
+
// about it. Like the plan gate's contextKind (#4794), the message names the real
|
|
79
|
+
// stat outcome: a permission or I/O failure (EACCES, ELOOP, EIO) is reported as
|
|
80
|
+
// unreadable, not as a missing directory. Only ENOENT/ENOTDIR mean the path is not there.
|
|
81
|
+
function phaseDirProblem(dirPath) {
|
|
82
|
+
const passDirectory = 'Pass the phase DIRECTORY (e.g. .planning/phases/01-slug), not the phase number.';
|
|
83
|
+
const st = (0, gate_evidence_cjs_1.statEvidence)(dirPath);
|
|
84
|
+
if (st.kind === 'none')
|
|
85
|
+
return { reason: 'phase directory not found', what: 'does not exist', hint: passDirectory, unreadable: false };
|
|
86
|
+
if (st.kind === 'unreadable')
|
|
87
|
+
return { reason: 'phase directory unreadable', what: `could not be read (${st.reason})`, hint: 'Check that the directory is readable.', unreadable: true };
|
|
88
|
+
if (st.value.isDirectory())
|
|
89
|
+
return null;
|
|
90
|
+
return { reason: 'phase path is not a directory', what: st.value.isFile() ? 'is a file, not a directory' : 'is not a directory', hint: passDirectory, unreadable: false };
|
|
91
|
+
}
|
|
73
92
|
/**
|
|
74
93
|
* The contents of the phase files `pick` selects (#5170, ADR-5057 §4). An ABSENT phase directory
|
|
75
94
|
* is "no files" (`found []`); a directory or file that exists but cannot be read is `unreadable` —
|
|
@@ -145,9 +145,24 @@ function evaluateDecisionCoveragePlan(input) {
|
|
|
145
145
|
if (decisions.length === 0) {
|
|
146
146
|
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { passed: true, skipped: true, reason: 'no trackable decisions', total: 0, covered: 0, uncovered: [], message: 'No trackable decisions in CONTEXT.md.' });
|
|
147
147
|
}
|
|
148
|
+
// #4939: a phase-dir argument that was GIVEN but is not a directory is a caller
|
|
149
|
+
// error like #2770's empty context argument and #4794's non-file context path:
|
|
150
|
+
// no plan was scanned, so nothing was measured. Fail closed with total/covered
|
|
151
|
+
// null and `uncovered` omitted. An OMITTED phase dir is left to the scan below —
|
|
152
|
+
// the #4130 `--context <path>` alone tests pin that as a coverage gap.
|
|
153
|
+
const phaseDirIssue = phaseDir ? (0, decision_coverage_support_cjs_1.phaseDirProblem)(phaseDir) : null;
|
|
154
|
+
if (phaseDirIssue) {
|
|
155
|
+
const phaseDirResult = { passed: false, skipped: false, reason: phaseDirIssue.reason, total: null, covered: null, message: `Decision coverage gate: the phase directory "${positionals[0]}" ${phaseDirIssue.what}, so no plans were scanned. ${phaseDirIssue.hint}` };
|
|
156
|
+
return phaseDirIssue.unreadable ? (0, gate_verdict_cjs_1.gateUnreadable)(true, phaseDirResult) : (0, gate_verdict_cjs_1.gateVerdict)('block', true, phaseDirResult);
|
|
157
|
+
}
|
|
148
158
|
const planContents = (0, decision_coverage_support_cjs_1.loadPlanContents)(phaseDir);
|
|
149
159
|
if (planContents.kind === 'unreadable')
|
|
150
160
|
return unreadableDecisionGate(`${planContents.span ?? phaseDir}: ${planContents.reason}`);
|
|
161
|
+
// #4939: a phase directory that holds no PLAN file is the same caller error: zero plans
|
|
162
|
+
// loaded is not zero coverage.
|
|
163
|
+
if (phaseDir && planContents.value.length === 0) {
|
|
164
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, { passed: false, skipped: false, reason: 'phase directory holds no plans', total: null, covered: null, message: `Decision coverage gate: the phase directory "${positionals[0]}" holds no PLAN files, so no plans were scanned. Pass the phase DIRECTORY that holds the plans (e.g. .planning/phases/01-slug), not the phase number.` });
|
|
165
|
+
}
|
|
151
166
|
const sections = planContents.value.map(decision_coverage_support_cjs_1.extractPlanDesignatedSections);
|
|
152
167
|
const uncovered = [];
|
|
153
168
|
let covered = 0;
|
|
@@ -90,6 +90,14 @@ function evaluateDecisionCoverageVerify(input) {
|
|
|
90
90
|
if (decisions.length === 0) {
|
|
91
91
|
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { skipped: true, blocking: false, reason: 'no trackable decisions', total: 0, honored: 0, not_honored: [], message: 'No trackable decisions in CONTEXT.md.' });
|
|
92
92
|
}
|
|
93
|
+
// #4939: mirror the plan gate. A phase-dir argument that names no directory
|
|
94
|
+
// means no plan or summary was scanned, so no decision can be reported
|
|
95
|
+
// not-honored. Warn (verify stays non-blocking) rather than list every decision.
|
|
96
|
+
const phaseDirIssue = phaseDir ? (0, decision_coverage_support_cjs_1.phaseDirProblem)(phaseDir) : null;
|
|
97
|
+
if (phaseDirIssue) {
|
|
98
|
+
const phaseDirResult = { skipped: false, blocking: false, reason: phaseDirIssue.reason, total: null, honored: null, not_honored: [], message: `Decision coverage verify (warning): the phase directory "${args[0]}" ${phaseDirIssue.what}, so no plans or summaries were scanned. ${phaseDirIssue.hint}` };
|
|
99
|
+
return phaseDirIssue.unreadable ? (0, gate_verdict_cjs_1.gateUnreadable)(false, phaseDirResult) : (0, gate_verdict_cjs_1.gateVerdict)('advisory', false, phaseDirResult);
|
|
100
|
+
}
|
|
93
101
|
// Every file the haystack is built from is typed evidence (#5170): one that exists but cannot be
|
|
94
102
|
// read is `unreadable`, never an empty slice of the haystack that makes a decision look unhonored.
|
|
95
103
|
const planContents = (0, decision_coverage_support_cjs_1.loadPlanContents)(phaseDir);
|
|
@@ -98,6 +106,11 @@ function evaluateDecisionCoverageVerify(input) {
|
|
|
98
106
|
const summaryParts = (0, decision_coverage_support_cjs_1.loadSummaryContents)(phaseDir);
|
|
99
107
|
if (summaryParts.kind === 'unreadable')
|
|
100
108
|
return unreadableVerify(`${summaryParts.span ?? phaseDir}: ${summaryParts.reason}`);
|
|
109
|
+
// #4939: mirror the plan gate. A phase directory with no PLAN and no SUMMARY file gave the
|
|
110
|
+
// verifier nothing to read, which is a caller error, not a phase whose decisions were all missed.
|
|
111
|
+
if (phaseDir && planContents.value.length === 0 && summaryParts.value.length === 0) {
|
|
112
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('advisory', false, { skipped: false, blocking: false, reason: 'phase directory holds no plans', total: null, honored: null, not_honored: [], message: `Decision coverage verify (warning): the phase directory "${args[0]}" holds no PLAN or SUMMARY files, so nothing was scanned. Pass the phase DIRECTORY that holds the plans (e.g. .planning/phases/01-slug), not the phase number.` });
|
|
113
|
+
}
|
|
101
114
|
const modifiedFiles = (0, decision_coverage_support_cjs_1.readModifiedFilesContent)(projectDir, summaryParts.value);
|
|
102
115
|
if (modifiedFiles.kind === 'unreadable')
|
|
103
116
|
return unreadableVerify(`${modifiedFiles.span ?? 'files_modified'}: ${modifiedFiles.reason}`);
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
* This is a leaf pure module: no fs, no child_process, no config — the subprocess
|
|
27
27
|
* seam is injected so the evaluator is trivially testable without spawning.
|
|
28
28
|
*/
|
|
29
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
30
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
|
+
};
|
|
32
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
29
33
|
// ─── Public constants ─────────────────────────────────────────────────────────
|
|
30
34
|
/** Default command timeout for `command-exit-zero` (30s). Matches execTool default. */
|
|
31
35
|
const COMMAND_EXIT_ZERO_DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -116,15 +120,31 @@ function evaluateArtifactFrontmatterEquals(predicate, ctx, deps) {
|
|
|
116
120
|
throw new Error('artifact-frontmatter-equals predicate requires an "equals" key');
|
|
117
121
|
}
|
|
118
122
|
const targetDir = isNonEmptyString(ctx.phaseDir) ? ctx.phaseDir : ctx.cwd;
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
// Plain `.sort()` (no locale) so the outcome never depends on directory listing order (#5313).
|
|
124
|
+
// Set: finders return realpaths, so a symlink to the same file is one artifact, not two.
|
|
125
|
+
const found = deps.findPhaseArtifact(targetDir, artifactSuffix);
|
|
126
|
+
// A pre-#5313 dep returned `string | null`; a silent null -> "not found" block or per-character spread would mislead.
|
|
127
|
+
if (!Array.isArray(found)) {
|
|
128
|
+
throw new Error('predicate deps "findPhaseArtifact" must return an array of paths');
|
|
129
|
+
}
|
|
130
|
+
const candidates = [...new Set(found)].sort();
|
|
131
|
+
if (candidates.length === 0) {
|
|
121
132
|
return {
|
|
122
133
|
block: true,
|
|
123
134
|
message: `Artifact matching ${artifactSuffix} not found in ${targetDir}`,
|
|
124
135
|
details: { kind: 'artifact-frontmatter-equals', artifactNotFound: true },
|
|
125
136
|
};
|
|
126
137
|
}
|
|
127
|
-
|
|
138
|
+
// #5313: never pick one of several matches (listing order is OS-dependent); a throw would fail open via onError.
|
|
139
|
+
if (candidates.length > 1) {
|
|
140
|
+
const names = candidates.map((c) => node_path_1.default.relative(targetDir, c).split(node_path_1.default.sep).join('/')).join(', ');
|
|
141
|
+
return {
|
|
142
|
+
block: true,
|
|
143
|
+
message: `Multiple artifacts match ${artifactSuffix} in ${targetDir}: ${names}; remove or rename all but one`,
|
|
144
|
+
details: { kind: 'artifact-frontmatter-equals', ambiguous: true, candidates },
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const fm = deps.readFrontmatter(candidates[0]);
|
|
128
148
|
const actualValue = fm[field];
|
|
129
149
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
130
150
|
const expectedStr = typeof expectedValue === 'object' ? JSON.stringify(expectedValue) : String(expectedValue);
|
|
@@ -80,31 +80,27 @@ function buildPredicateDeps() {
|
|
|
80
80
|
if (phaseDirStat.kind === 'unreadable')
|
|
81
81
|
return unreadable(phaseDir, phaseDirStat.reason);
|
|
82
82
|
if (phaseDirStat.kind === 'none')
|
|
83
|
-
return
|
|
83
|
+
return [];
|
|
84
84
|
if (artifactSuffix === '.' ||
|
|
85
85
|
artifactSuffix === '..' ||
|
|
86
86
|
artifactSuffix.includes('\0') ||
|
|
87
87
|
node_path_1.default.basename(artifactSuffix) !== artifactSuffix ||
|
|
88
88
|
node_path_1.default.win32.basename(artifactSuffix) !== artifactSuffix) {
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
const directContained = (0, security_cjs_1.tryWithinRoot)(artifactSuffix, phaseDir);
|
|
92
|
-
if (directContained !== null) {
|
|
93
|
-
const direct = regularFileOrNull(directContained);
|
|
94
|
-
if (direct !== null)
|
|
95
|
-
return direct;
|
|
89
|
+
return [];
|
|
96
90
|
}
|
|
91
|
+
// #5313: collect EVERY distinct match; the evaluator fails closed on >1 instead of trusting listing order.
|
|
92
|
+
const found = [];
|
|
97
93
|
const planningContained = (0, security_cjs_1.tryWithinRoot)(node_path_1.default.join('.planning', artifactSuffix), phaseDir);
|
|
98
94
|
if (planningContained !== null) {
|
|
99
95
|
const planning = regularFileOrNull(planningContained);
|
|
100
96
|
if (planning !== null)
|
|
101
|
-
|
|
97
|
+
found.push(planning);
|
|
102
98
|
}
|
|
103
99
|
const listing = (0, gate_evidence_cjs_1.readDirEvidence)(phaseDir);
|
|
104
100
|
if (listing.kind === 'unreadable')
|
|
105
101
|
return unreadable(phaseDir, listing.reason);
|
|
106
102
|
if (listing.kind === 'none')
|
|
107
|
-
return
|
|
103
|
+
return [];
|
|
108
104
|
for (const f of listing.value) {
|
|
109
105
|
if (f.endsWith('-' + artifactSuffix) || f === artifactSuffix) {
|
|
110
106
|
const candidateContained = (0, security_cjs_1.tryWithinRoot)(f, phaseDir);
|
|
@@ -112,10 +108,10 @@ function buildPredicateDeps() {
|
|
|
112
108
|
continue;
|
|
113
109
|
const candidate = regularFileOrNull(candidateContained);
|
|
114
110
|
if (candidate !== null)
|
|
115
|
-
|
|
111
|
+
found.push(candidate);
|
|
116
112
|
}
|
|
117
113
|
}
|
|
118
|
-
return
|
|
114
|
+
return found;
|
|
119
115
|
},
|
|
120
116
|
readFrontmatter(filePath) {
|
|
121
117
|
const content = (0, shell_command_projection_cjs_1.platformReadSync)(filePath);
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
* tolerant. A plan id that is not `<phase>-<plan>` (a plan named `.*-PLAN.md`) matches nothing,
|
|
18
18
|
* and a git failure is "no commits" exactly as before.
|
|
19
19
|
*
|
|
20
|
+
* Cross-plan GREEN (#4867): a gap closure is planned as a RED-only probe plan plus the plan that
|
|
21
|
+
* `depends_on` it and carries the fix. A `type: tdd` plan with its own RED but no GREEN is credited
|
|
22
|
+
* with a `feat(...)` commit under any plan that lists it in `depends_on`, directly or through a
|
|
23
|
+
* chain; the row carries `greenFrom` and the report names the supplier. Its own RED is still
|
|
24
|
+
* required, and a plan nothing depends on is scored on its own commits alone.
|
|
25
|
+
*
|
|
20
26
|
* Argv after the verb: `<phase>` (a number; an unresolvable phase reports zero plans).
|
|
21
27
|
*/
|
|
22
28
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
@@ -31,7 +37,7 @@ const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
|
31
37
|
const gate_evaluation_scope_cjs_1 = require("./gate-evaluation-scope.cjs");
|
|
32
38
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
39
|
const frontmatterMod = require("./frontmatter.cjs");
|
|
34
|
-
const { frontmatterKeyHasValue } = frontmatterMod;
|
|
40
|
+
const { frontmatterKeyHasValue, extractFrontmatter } = frontmatterMod;
|
|
35
41
|
/**
|
|
36
42
|
* True when the plan's frontmatter declares `type: tdd` (CRLF included, #2449). The block is the
|
|
37
43
|
* one fence owner's, read as RAW text — not through the YAML parser — so a block the parser refuses
|
|
@@ -42,6 +48,35 @@ const { frontmatterKeyHasValue } = frontmatterMod;
|
|
|
42
48
|
function isTddPlan(content) {
|
|
43
49
|
return frontmatterKeyHasValue(content, 'type', 'tdd');
|
|
44
50
|
}
|
|
51
|
+
/** A `depends_on` token as a plan id: `01.1-20`, `01.1-20-PLAN` and `01.1-20-PLAN.md` all name plan `01.1-20`. */
|
|
52
|
+
function toPlanId(token) {
|
|
53
|
+
return token.trim().replace(/-PLAN(?:\.md)?$/i, '');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The plan ids a plan lists in `depends_on` (#4867). A plan whose frontmatter does not parse, or has
|
|
57
|
+
* no `depends_on`, depends on nothing: the pairing below only ever ADDS credit, so an unreadable
|
|
58
|
+
* link can never turn a failing plan into a passing one.
|
|
59
|
+
*/
|
|
60
|
+
function dependsOnOf(content) {
|
|
61
|
+
// `extractFrontmatter` does not throw: an unparseable block is an empty result, so `depends_on` is absent.
|
|
62
|
+
const value = extractFrontmatter(content)['depends_on'];
|
|
63
|
+
const tokens = Array.isArray(value) ? value.map(String) : typeof value === 'string' ? value.split(',') : [];
|
|
64
|
+
return tokens.map(toPlanId).filter((id) => id !== '');
|
|
65
|
+
}
|
|
66
|
+
/** Every plan that depends on `planId`, directly or through a chain (#4867); a cycle is walked once. */
|
|
67
|
+
function transitiveDependents(planId, dependents) {
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const queue = [planId];
|
|
70
|
+
while (queue.length > 0) {
|
|
71
|
+
for (const next of dependents.get(queue.shift()) ?? []) {
|
|
72
|
+
if (next !== planId && !seen.has(next)) {
|
|
73
|
+
seen.add(next);
|
|
74
|
+
queue.push(next);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return [...seen].sort();
|
|
79
|
+
}
|
|
45
80
|
/**
|
|
46
81
|
* The commit types (`test`, `feat`, `refactor`, …) among the plan's own commits — those that
|
|
47
82
|
* touched at least one path (`pathspecs: ['.']`, as the old `git log -- .` lookup did). An
|
|
@@ -91,6 +126,9 @@ function evaluateTddReviewCheckpoint(input) {
|
|
|
91
126
|
const phaseDir = located.kind === 'found' ? located.value : '';
|
|
92
127
|
// Find all PLAN.md files with type: tdd in frontmatter
|
|
93
128
|
const tddPlanFiles = [];
|
|
129
|
+
// #4867: plan id -> the plans that list it in `depends_on`, over EVERY plan of the phase (a
|
|
130
|
+
// dependent GREEN plan is usually `type: execute`, not `tdd`).
|
|
131
|
+
const dependents = new Map();
|
|
94
132
|
const unreadable = [];
|
|
95
133
|
if (phaseDir) {
|
|
96
134
|
const entries = (0, gate_evidence_cjs_1.readDirEvidence)(phaseDir);
|
|
@@ -110,8 +148,13 @@ function evaluateTddReviewCheckpoint(input) {
|
|
|
110
148
|
const plan = (0, gate_evidence_cjs_1.readTextEvidence)(planPath);
|
|
111
149
|
if (plan.kind === 'unreadable')
|
|
112
150
|
unreadable.push({ source: planPath, reason: plan.reason });
|
|
113
|
-
else if (plan.kind === 'found'
|
|
114
|
-
|
|
151
|
+
else if (plan.kind === 'found') {
|
|
152
|
+
if (isTddPlan(plan.value))
|
|
153
|
+
tddPlanFiles.push(planPath);
|
|
154
|
+
const planId = node_path_1.default.basename(planPath, '-PLAN.md');
|
|
155
|
+
for (const dep of dependsOnOf(plan.value))
|
|
156
|
+
dependents.set(dep, [...(dependents.get(dep) ?? []), planId]);
|
|
157
|
+
}
|
|
115
158
|
}
|
|
116
159
|
}
|
|
117
160
|
}
|
|
@@ -136,15 +179,24 @@ function evaluateTddReviewCheckpoint(input) {
|
|
|
136
179
|
const planId = node_path_1.default.basename(planPath, '-PLAN.md');
|
|
137
180
|
const kinds = planCommitKinds(projectDir, planId);
|
|
138
181
|
const red = kinds.has('test');
|
|
139
|
-
const green = kinds.has('feat');
|
|
140
182
|
const refactor = kinds.has('refactor');
|
|
183
|
+
// #4867: a RED-only probe plan is satisfied by the GREEN of a plan that (transitively) declares
|
|
184
|
+
// `depends_on` it. Only a real `feat(...)` commit under such a plan counts; the plan's own RED is
|
|
185
|
+
// still required, and a plan nothing depends on is scored on its own commits alone.
|
|
186
|
+
const ownGreen = kinds.has('feat');
|
|
187
|
+
const greenFrom = ownGreen
|
|
188
|
+
? []
|
|
189
|
+
: transitiveDependents(planId, dependents).filter((dependentId) => planCommitKinds(projectDir, dependentId).has('feat'));
|
|
190
|
+
const green = ownGreen || greenFrom.length > 0;
|
|
141
191
|
const missing = [];
|
|
142
192
|
if (!red)
|
|
143
193
|
missing.push('RED');
|
|
144
194
|
if (!green)
|
|
145
195
|
missing.push('GREEN');
|
|
146
196
|
const status = missing.length === 0 ? 'Pass' : 'FAIL';
|
|
147
|
-
rows.push(
|
|
197
|
+
rows.push(greenFrom.length > 0
|
|
198
|
+
? { planId, red, green, refactor, status, missing, greenFrom }
|
|
199
|
+
: { planId, red, green, refactor, status, missing });
|
|
148
200
|
}
|
|
149
201
|
const violations = rows.filter(r => r.status === 'FAIL').length;
|
|
150
202
|
// Build review table
|
|
@@ -160,6 +212,9 @@ function evaluateTddReviewCheckpoint(input) {
|
|
|
160
212
|
tableDivider,
|
|
161
213
|
...tableRows,
|
|
162
214
|
].join('\n');
|
|
215
|
+
for (const r of rows.filter(row => row.greenFrom)) {
|
|
216
|
+
table += `\n\nPlan ${r.planId} GREEN is supplied by dependent plan ${(r.greenFrom ?? []).join(', ')}.`;
|
|
217
|
+
}
|
|
163
218
|
if (violations > 0) {
|
|
164
219
|
table += '\n\n⚠ Gate violations are advisory — review before advancing.';
|
|
165
220
|
for (const r of rows.filter(row => row.status === 'FAIL')) {
|
|
@@ -20,6 +20,8 @@ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
|
20
20
|
const io = require("./io.cjs");
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module
|
|
22
22
|
const configLoader = require("./config-loader.cjs");
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- capability-ledger.cjs is an export= CommonJS module
|
|
24
|
+
const capabilityLedger = require("./capability-ledger.cjs");
|
|
23
25
|
const project_root_cjs_1 = require("./project-root.cjs");
|
|
24
26
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- model-resolver.cjs is an export= CommonJS module
|
|
25
27
|
const modelResolver = require("./model-resolver.cjs");
|
|
@@ -3773,7 +3775,7 @@ function buildAgentSkillsBlock(config, agentType, projectRoot, diagnostics) {
|
|
|
3773
3775
|
}).join('\n');
|
|
3774
3776
|
return `<agent_skills>\nRead these user-configured skills:\n${lines}\n</agent_skills>`;
|
|
3775
3777
|
}
|
|
3776
|
-
function cmdAgentSkills(cwd, agentType, raw, jsonMode) {
|
|
3778
|
+
function cmdAgentSkills(cwd, agentType, raw, jsonMode, skillsOnly = false) {
|
|
3777
3779
|
if (!agentType) {
|
|
3778
3780
|
output('', raw, '');
|
|
3779
3781
|
return;
|
|
@@ -3804,8 +3806,13 @@ function cmdAgentSkills(cwd, agentType, raw, jsonMode) {
|
|
|
3804
3806
|
// (a real exit code) instead of a prose config-get gate. Compact is tried
|
|
3805
3807
|
// first when requested; a missing compact sibling falls back to canonical
|
|
3806
3808
|
// with the fallback disclosed in the payload itself, never a silent switch.
|
|
3809
|
+
//
|
|
3810
|
+
// #5122: `skillsOnly` (the CLI's `--skills-only`) is the agent's own
|
|
3811
|
+
// self-load. It wants configured skills and nothing else, so it skips the
|
|
3812
|
+
// fallback on every runtime — otherwise a non-Claude agent reads its own
|
|
3813
|
+
// persona back into itself as if it were a skills block.
|
|
3807
3814
|
let agentPayloadVariant = null;
|
|
3808
|
-
if (!block) {
|
|
3815
|
+
if (!block && !skillsOnly) {
|
|
3809
3816
|
const runtime = (config && config['runtime']) || process.env['GSD_RUNTIME'] || 'claude';
|
|
3810
3817
|
if (runtime !== 'claude') {
|
|
3811
3818
|
const agentCheck = checkAgentsInstalled(runtime, projectRoot);
|
|
@@ -4071,7 +4078,7 @@ function buildSkillManifest(cwd, skillsDir = null) {
|
|
|
4071
4078
|
if (!entry.isDirectory())
|
|
4072
4079
|
continue;
|
|
4073
4080
|
const skillMdPath = node_path_1.default.join(rootPath, entry.name, 'SKILL.md');
|
|
4074
|
-
const content =
|
|
4081
|
+
const content = capabilityLedger.readSkillFile(skillMdPath);
|
|
4075
4082
|
if (content !== null) {
|
|
4076
4083
|
if (pushSkillEntry(`${entry.name}/SKILL.md`, content, skillMdPath))
|
|
4077
4084
|
skillCount++;
|
|
@@ -4096,7 +4103,7 @@ function buildSkillManifest(cwd, skillsDir = null) {
|
|
|
4096
4103
|
if (!nested.isDirectory())
|
|
4097
4104
|
continue;
|
|
4098
4105
|
const nestedSkillMd = node_path_1.default.join(nestedSkillsDir, nested.name, 'SKILL.md');
|
|
4099
|
-
const nestedContent =
|
|
4106
|
+
const nestedContent = capabilityLedger.readSkillFile(nestedSkillMd);
|
|
4100
4107
|
if (nestedContent === null)
|
|
4101
4108
|
continue;
|
|
4102
4109
|
// Use forward-slash separator explicitly so manifest paths are posix-style
|
|
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
*/
|
|
36
36
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
37
37
|
const node_path_1 = __importDefault(require("node:path"));
|
|
38
|
-
const
|
|
38
|
+
const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
|
|
39
39
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- install-fs-adapter.cjs is an export= CommonJS module
|
|
40
40
|
const installFsAdapter = require("./install-fs-adapter.cjs");
|
|
41
41
|
const { installFs } = installFsAdapter;
|
|
@@ -154,7 +154,7 @@ function _findAncestorGsdConfigPath(targetDir) {
|
|
|
154
154
|
* @param targetDir Runtime install root (walks up to find .planning/).
|
|
155
155
|
*/
|
|
156
156
|
function readGsdEffectiveEffortConfig(targetDir = null) {
|
|
157
|
-
const homeDefaults = _readGsdConfigFile(node_path_1.default.join(
|
|
157
|
+
const homeDefaults = _readGsdConfigFile(node_path_1.default.join((0, runtime_homes_cjs_1.resolveGsdHome)(), '.gsd', 'defaults.json'), '~/.gsd/defaults.json');
|
|
158
158
|
let projectConfig = null;
|
|
159
159
|
if (targetDir) {
|
|
160
160
|
const candidate = _findAncestorGsdConfigPath(targetDir);
|
|
@@ -34,6 +34,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
34
34
|
*/
|
|
35
35
|
const node_path_1 = __importDefault(require("node:path"));
|
|
36
36
|
const node_os_1 = __importDefault(require("node:os"));
|
|
37
|
+
const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
|
|
37
38
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- install-fs-adapter.cjs is an export= CommonJS module
|
|
38
39
|
const installFsAdapter = require("./install-fs-adapter.cjs");
|
|
39
40
|
const { installFs } = installFsAdapter;
|
|
@@ -57,7 +58,7 @@ const { resolveTierEntry: gsdResolveTierEntry, resolveModelPolicy: gsdResolveMod
|
|
|
57
58
|
*/
|
|
58
59
|
function readGsdGlobalModelOverrides(options = {}) {
|
|
59
60
|
try {
|
|
60
|
-
const home = options.homedir ? options.homedir() : node_os_1.default.homedir();
|
|
61
|
+
const home = (0, runtime_homes_cjs_1.resolveGsdHome)(options.homedir ? options.homedir() : node_os_1.default.homedir());
|
|
61
62
|
const defaultsPath = node_path_1.default.join(home, '.gsd', 'defaults.json');
|
|
62
63
|
if (!installFs().existsSync(defaultsPath))
|
|
63
64
|
return null;
|
|
@@ -139,7 +140,7 @@ function readGsdAgentTools(config) {
|
|
|
139
140
|
* model override resolver.
|
|
140
141
|
*/
|
|
141
142
|
function readGsdEffectiveAgentTools(targetDir = null, options = {}) {
|
|
142
|
-
const home = options.homedir ? options.homedir() : node_os_1.default.homedir();
|
|
143
|
+
const home = (0, runtime_homes_cjs_1.resolveGsdHome)(options.homedir ? options.homedir() : node_os_1.default.homedir());
|
|
143
144
|
const globalConfig = _readGsdConfigFile(node_path_1.default.join(home, '.gsd', 'defaults.json'), 'global defaults');
|
|
144
145
|
let projectConfig = null;
|
|
145
146
|
if (targetDir) {
|
|
@@ -170,7 +171,7 @@ function readGsdEffectiveAgentTools(targetDir = null, options = {}) {
|
|
|
170
171
|
* baked, letting the runtime's own default/session model govern).
|
|
171
172
|
*/
|
|
172
173
|
function readGsdRuntimeProfileResolver(targetDir = null) {
|
|
173
|
-
const homeDefaults = _readGsdConfigFile(node_path_1.default.join(
|
|
174
|
+
const homeDefaults = _readGsdConfigFile(node_path_1.default.join((0, runtime_homes_cjs_1.resolveGsdHome)(), '.gsd', 'defaults.json'), '~/.gsd/defaults.json');
|
|
174
175
|
// #2875 defect fix (Generative Fix Divergence): same shared walk as
|
|
175
176
|
// readGsdEffectiveModelOverrides above — see that call site's comment.
|
|
176
177
|
let projectConfig = null;
|
|
@@ -31,6 +31,8 @@ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
|
31
31
|
// #2322: reuse the existing pure path-containment seam (ADR-1239 Phase C-2)
|
|
32
32
|
// instead of hand-rolling a new traversal check for capability skill stems.
|
|
33
33
|
const external_descriptor_trust_cjs_1 = require("./external-descriptor-trust.cjs");
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- capability-ledger.cjs is an export= CommonJS module
|
|
35
|
+
const capabilityLedger = require("./capability-ledger.cjs");
|
|
34
36
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
35
37
|
const conversionModule = require("./runtime-artifact-conversion.cjs");
|
|
36
38
|
const { applyAgentPathRewrites: _applyAgentPathRewrites, processAttribution: _processAttribution, normalizeAgentBodyForRuntime: _normalizeAgentBodyForRuntime, readGsdCommandNames: _readGsdCommandNames, deriveAgentName: _deriveAgentName, applyAgentFrontmatterExtensions: _applyAgentFrontmatterExtensions, appendAgentTools: _appendAgentTools, } = conversionModule;
|
|
@@ -748,9 +750,9 @@ function readInstalledCapabilitySkill(stem, registry) {
|
|
|
748
750
|
// installed as a capability skill (epic #4636).
|
|
749
751
|
if (node_fs_1.default.lstatSync(skillPath).isSymbolicLink())
|
|
750
752
|
return null;
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
return { capId, content
|
|
753
|
+
// #5121: the bounded regular-file read — a FIFO/device/oversized SKILL.md is skipped like a missing one.
|
|
754
|
+
const content = capabilityLedger.readSkillFile(skillPath);
|
|
755
|
+
return content === null ? null : { capId, content };
|
|
754
756
|
}
|
|
755
757
|
catch {
|
|
756
758
|
return null; // missing / unreadable / corrupt entry -> skip
|