@opengsd/gsd-core 1.9.0 → 1.10.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 +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +131 -34
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-executor.md +7 -5
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-plan-checker.md +9 -0
- package/agents/gsd-planner.md +5 -8
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-verifier.md +14 -70
- package/bin/install.js +503 -341
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/plan-phase.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +607 -63
- package/gsd-core/bin/lib/active-workstream-store.cjs +25 -0
- package/gsd-core/bin/lib/agent-install-check.cjs +38 -6
- package/gsd-core/bin/lib/api-coverage.cjs +120 -0
- package/gsd-core/bin/lib/audit.cjs +89 -1
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- package/gsd-core/bin/lib/capability-registry.cjs +96 -110
- package/gsd-core/bin/lib/capability-validator.cjs +12 -2
- package/gsd-core/bin/lib/check-command-router.cjs +43 -1
- package/gsd-core/bin/lib/command-aliases.cjs +72 -0
- package/gsd-core/bin/lib/commands.cjs +26 -25
- package/gsd-core/bin/lib/commonjs-marker.cjs +136 -0
- package/gsd-core/bin/lib/config-loader.cjs +1 -0
- package/gsd-core/bin/lib/config.cjs +12 -1
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +91 -12
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/frontmatter.cjs +84 -12
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +58 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +27 -4
- package/gsd-core/bin/lib/host-integration.cjs +13 -1
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1021 -57
- package/gsd-core/bin/lib/install-engine.cjs +64 -10
- package/gsd-core/bin/lib/install-profiles.cjs +27 -1
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +87 -1
- package/gsd-core/bin/lib/io.cjs +28 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +6 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone.cjs +106 -51
- package/gsd-core/bin/lib/phase-id.cjs +63 -0
- package/gsd-core/bin/lib/phase-locator.cjs +138 -45
- package/gsd-core/bin/lib/phase.cjs +260 -25
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +232 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +4 -0
- package/gsd-core/bin/lib/project-root.cjs +48 -0
- package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +80 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +99 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +30 -6
- package/gsd-core/bin/lib/roadmap-command-router.cjs +42 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +100 -18
- package/gsd-core/bin/lib/roadmap.cjs +37 -7
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +195 -62
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +15 -3
- package/gsd-core/bin/lib/runtime-homes.cjs +154 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +105 -41
- package/gsd-core/bin/lib/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +113 -27
- package/gsd-core/bin/lib/smart-entry.cjs +12 -0
- package/gsd-core/bin/lib/state-transition.cjs +73 -8
- package/gsd-core/bin/lib/state.cjs +151 -62
- package/gsd-core/bin/lib/surface.cjs +12 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +11 -1
- package/gsd-core/bin/lib/uat.cjs +320 -21
- package/gsd-core/bin/lib/unusable-input.cjs +9 -0
- package/gsd-core/bin/lib/verification.cjs +29 -12
- package/gsd-core/bin/lib/verify.cjs +29 -5
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +181 -18
- package/gsd-core/bin/lib/workstream-inventory.cjs +519 -27
- package/gsd-core/bin/lib/workstream.cjs +6 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +276 -118
- package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/debugger-techniques.md +255 -0
- package/gsd-core/references/research-documentation-lookup.md +5 -3
- package/gsd-core/references/specless-probe-fallback.md +7 -6
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/worktree-branch-check.md +2 -2
- package/gsd-core/templates/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +32 -69
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +83 -0
- package/gsd-core/workflows/code-review.md +42 -145
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +23 -81
- package/gsd-core/workflows/debug.md +9 -12
- package/gsd-core/workflows/diagnose-issues.md +22 -0
- package/gsd-core/workflows/discovery-phase.md +4 -4
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +5 -16
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +34 -2
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +19 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +65 -137
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/help/modes/full.md +6 -1
- package/gsd-core/workflows/ingest-docs.md +2 -1
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +21 -38
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +13 -226
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +49 -193
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +11 -153
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +46 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +20 -390
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +15 -8
- package/gsd-core/workflows/section-manifest.json +219 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +17 -14
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +49 -11
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +8 -21
- package/gsd-core/workflows/ui-phase.md +8 -7
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +18 -7
- package/gsd-core/workflows/verify-phase.md +4 -7
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +8 -58
- package/hooks/dist/gsd-agent-isolation-guard.js +428 -0
- package/hooks/dist/gsd-check-update-worker.js +14 -5
- package/hooks/dist/gsd-cursor-subagent-start.js +532 -26
- package/hooks/dist/gsd-read-injection-scanner.js +7 -0
- package/hooks/dist/gsd-statusline.js +72 -6
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- package/hooks/dist/lib/isolation-sentinel.js +268 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +428 -0
- package/hooks/gsd-check-update-worker.js +14 -5
- package/hooks/gsd-cursor-subagent-start.js +532 -26
- package/hooks/gsd-read-injection-scanner.js +7 -0
- package/hooks/gsd-statusline.js +72 -6
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- package/hooks/lib/isolation-sentinel.js +268 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +14 -5
- package/pi/gsd.cjs +57 -12
- package/scripts/build-hooks.js +9 -0
- package/scripts/changeset/lint.cjs +9 -2
- package/scripts/changeset/serialize.cjs +5 -1
- package/scripts/gen-capability-matrix.cjs +1 -1
- package/scripts/gen-context-index.cjs +448 -0
- package/scripts/gen-inventory-manifest.cjs +101 -1
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-registry.cjs +39 -15
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- package/scripts/lint-allow-test-rule-refs.allowlist.json +17 -31
- package/scripts/lint-compiled-artifact-sync.cjs +6 -1
- package/scripts/lint-docs-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-example-parser-parity.cjs +395 -0
- package/scripts/lint-test-file-count.allowlist.json +27 -1
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/prompt-injection-scan.sh +27 -6
- package/scripts/registry-schema.cjs +323 -94
- package/scripts/run-tests.cjs +3 -2
- package/scripts/validate-registry.cjs +10 -6
- package/skills/gsd-autonomous/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-plan-phase/SKILL.md +2 -2
- package/vscode/package.json +1 -1
- package/scripts/gen-emitted-baseline.cjs +0 -145
|
@@ -34,6 +34,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
34
34
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
35
35
|
};
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.LEGACY_NON_REGISTRY_RUNTIME_IDS = void 0;
|
|
38
|
+
exports.isRegisteredRuntimeId = isRegisteredRuntimeId;
|
|
37
39
|
exports.resolveConfigHomeFromDescriptor = resolveConfigHomeFromDescriptor;
|
|
38
40
|
exports.resolveAntigravityGlobalDir = resolveAntigravityGlobalDir;
|
|
39
41
|
exports.detectAntigravityDirAmbiguity = detectAntigravityDirAmbiguity;
|
|
@@ -48,15 +50,36 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
48
50
|
const node_path_1 = __importDefault(require("node:path"));
|
|
49
51
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
50
52
|
/**
|
|
51
|
-
* Expand a leading ~ to os.homedir().
|
|
53
|
+
* Expand a leading ~ to the given home directory (defaults to os.homedir()).
|
|
54
|
+
* Every call site inside resolveConfigHomeFromDescriptor threads its
|
|
55
|
+
* resolved `home` local through here so an injected opts.home (used by
|
|
56
|
+
* hermetic tests) is honored instead of silently falling back to the real
|
|
57
|
+
* home directory.
|
|
52
58
|
*/
|
|
53
|
-
function expandTilde(p) {
|
|
59
|
+
function expandTilde(p, home = node_os_1.default.homedir()) {
|
|
54
60
|
if (!p)
|
|
55
61
|
return p;
|
|
56
62
|
if (p.startsWith('~/') || p === '~')
|
|
57
|
-
return node_path_1.default.join(
|
|
63
|
+
return node_path_1.default.join(home, p.slice(1));
|
|
58
64
|
return p;
|
|
59
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* True when `val` is a usable env-var override: a real string that contains
|
|
68
|
+
* at least one non-whitespace character. Every env-override consumption site
|
|
69
|
+
* in resolveConfigHomeFromDescriptor gates on this instead of a bare truthy
|
|
70
|
+
* check, so `FOO_DIR=''` (empty), `FOO_DIR` unset (`undefined`), and
|
|
71
|
+
* `FOO_DIR=' '` (whitespace-only — e.g. from a shell templating bug that
|
|
72
|
+
* leaves a variable substitution blank but quoted) all fall back to the
|
|
73
|
+
* descriptor default identically. Deliberately does NOT trim: a value that
|
|
74
|
+
* merely has leading/trailing whitespace around otherwise-real content (or
|
|
75
|
+
* interior whitespace, e.g. `~/My Agent Dir`) is passed through byte-for-byte
|
|
76
|
+
* unchanged, exactly as this module already treats every other env-var
|
|
77
|
+
* override (no site here or elsewhere in this file trims a path value) — so
|
|
78
|
+
* default behavior for every non-whitespace value is unaffected by this guard.
|
|
79
|
+
*/
|
|
80
|
+
function hasNonBlankOverride(val) {
|
|
81
|
+
return typeof val === 'string' && val.trim() !== '';
|
|
82
|
+
}
|
|
60
83
|
function resolveDescriptorWithOptions(configHome) {
|
|
61
84
|
return resolveConfigHomeFromDescriptor(configHome, {
|
|
62
85
|
env: process.env,
|
|
@@ -68,6 +91,64 @@ function getRegistry() {
|
|
|
68
91
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
92
|
return require('./capability-registry.cjs');
|
|
70
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Legacy runtime ids that have a genuine, dedicated resolution branch
|
|
96
|
+
* elsewhere in this module but no capability-registry descriptor (#3024
|
|
97
|
+
* review BLOCKER, finding 1).
|
|
98
|
+
*
|
|
99
|
+
* `isRegisteredRuntimeId`'s real contract is "does this id resolve to a
|
|
100
|
+
* real, runtime-specific path?", not "is it a key in the registry map" —
|
|
101
|
+
* registry membership is only a proxy for that, and the proxy is wrong for
|
|
102
|
+
* `grok`: `getGlobalConfigDir` has a live hardcoded branch for it (`~/.agents`,
|
|
103
|
+
* overridable via `GROK_AGENTS_HOME`), predating the capability-registry
|
|
104
|
+
* descriptor system, and it is documented end-user-facing surface
|
|
105
|
+
* (pre-fix `gsd-core/workflows/sync-skills.md` listed it explicitly, and
|
|
106
|
+
* `docs/discussions/grok-build-support-2026-05.md` records the support
|
|
107
|
+
* decision). Rejecting it here would silently remove working, documented
|
|
108
|
+
* `--skills-root`/`sync-skills` support for a runtime that never regressed.
|
|
109
|
+
*
|
|
110
|
+
* This set is enumerated by hand, not derived, and MUST stay in lockstep
|
|
111
|
+
* with `getGlobalConfigDir`'s hardcoded branches: as of #3024, `grok` is
|
|
112
|
+
* the ONLY id in this position (verified by reading the full function body
|
|
113
|
+
* — every other unregistered id there falls through to the claude
|
|
114
|
+
* fallback, which is the behavior this validator exists to reject). Do
|
|
115
|
+
* NOT add an id here just because it "should" work — add it only after
|
|
116
|
+
* confirming `getGlobalConfigDir` has a real dedicated branch for it, the
|
|
117
|
+
* way `grok`'s is confirmed above. Do not "clean this up" by removing it;
|
|
118
|
+
* that is exactly the regression this constant guards against.
|
|
119
|
+
*/
|
|
120
|
+
exports.LEGACY_NON_REGISTRY_RUNTIME_IDS = new Set(['grok']);
|
|
121
|
+
/**
|
|
122
|
+
* True when `runtime` is a real runtime id with a genuine, runtime-specific
|
|
123
|
+
* resolution path — either a registered id in the capability registry
|
|
124
|
+
* (`capability-registry.cjs`'s `runtimes` object) or one of the small,
|
|
125
|
+
* explicitly named `LEGACY_NON_REGISTRY_RUNTIME_IDS` set (currently just
|
|
126
|
+
* `grok`) that resolves via a dedicated hardcoded branch instead of a
|
|
127
|
+
* registry descriptor.
|
|
128
|
+
*
|
|
129
|
+
* Guarded with an own-property lookup — never a bare index — so a
|
|
130
|
+
* prototype-chain id (`__proto__`, `constructor`, `prototype`, `toString`,
|
|
131
|
+
* …) can never resolve to an inherited value and be mistaken for a real
|
|
132
|
+
* entry. Without this, `getGlobalSkillsBase`/`getGlobalConfigDir`'s bare
|
|
133
|
+
* `runtimes[runtime]` lookups fall through the prototype chain for those
|
|
134
|
+
* ids, find no usable descriptor, and silently resolve to claude's
|
|
135
|
+
* fallback path instead of failing loudly (#3024).
|
|
136
|
+
*
|
|
137
|
+
* Single shared validator for every `--skills-root` entry point
|
|
138
|
+
* (`gsd-tools query skills-root`'s `routeSkillsRoot`, `install.js
|
|
139
|
+
* --skills-root`) so the two surfaces can never diverge on which runtime
|
|
140
|
+
* ids they accept.
|
|
141
|
+
*/
|
|
142
|
+
function isRegisteredRuntimeId(runtime) {
|
|
143
|
+
if (typeof runtime !== 'string')
|
|
144
|
+
return false;
|
|
145
|
+
const trimmed = runtime.trim();
|
|
146
|
+
if (!trimmed)
|
|
147
|
+
return false;
|
|
148
|
+
if (Object.prototype.hasOwnProperty.call(getRegistry().runtimes, trimmed))
|
|
149
|
+
return true;
|
|
150
|
+
return exports.LEGACY_NON_REGISTRY_RUNTIME_IDS.has(trimmed);
|
|
151
|
+
}
|
|
71
152
|
/**
|
|
72
153
|
* Resolve a configHome descriptor to an absolute directory path.
|
|
73
154
|
*
|
|
@@ -86,16 +167,16 @@ function resolveConfigHomeFromDescriptor(configHome, opts = {}) {
|
|
|
86
167
|
// First env var that is set wins
|
|
87
168
|
for (const varName of configHome.env) {
|
|
88
169
|
const val = env[varName];
|
|
89
|
-
if (val)
|
|
90
|
-
return expandTilde(val);
|
|
170
|
+
if (hasNonBlankOverride(val))
|
|
171
|
+
return expandTilde(val, home);
|
|
91
172
|
}
|
|
92
173
|
return node_path_1.default.join(home, configHome.name);
|
|
93
174
|
}
|
|
94
175
|
case 'dot-home-nested': {
|
|
95
176
|
// env override
|
|
96
177
|
const nestedEnv0Val = env[configHome.env[0]];
|
|
97
|
-
if (configHome.env[0] && nestedEnv0Val) {
|
|
98
|
-
return expandTilde(nestedEnv0Val);
|
|
178
|
+
if (configHome.env[0] && hasNonBlankOverride(nestedEnv0Val)) {
|
|
179
|
+
return expandTilde(nestedEnv0Val, home);
|
|
99
180
|
}
|
|
100
181
|
const base = node_path_1.default.join(home, configHome.parent);
|
|
101
182
|
if (configHome.probe && configHome.probe.length > 0) {
|
|
@@ -127,49 +208,50 @@ function resolveConfigHomeFromDescriptor(configHome, opts = {}) {
|
|
|
127
208
|
case 'xdg': {
|
|
128
209
|
// env[0]: direct override dir
|
|
129
210
|
const xdgEnv0Val = env[configHome.env[0]];
|
|
130
|
-
if (configHome.env[0] && xdgEnv0Val) {
|
|
131
|
-
return expandTilde(xdgEnv0Val);
|
|
211
|
+
if (configHome.env[0] && hasNonBlankOverride(xdgEnv0Val)) {
|
|
212
|
+
return expandTilde(xdgEnv0Val, home);
|
|
132
213
|
}
|
|
133
214
|
// env[1]: FILE path → dirname
|
|
134
215
|
const xdgEnv1Val = env[configHome.env[1]];
|
|
135
|
-
if (configHome.env[1] && xdgEnv1Val) {
|
|
136
|
-
return node_path_1.default.dirname(expandTilde(xdgEnv1Val));
|
|
216
|
+
if (configHome.env[1] && hasNonBlankOverride(xdgEnv1Val)) {
|
|
217
|
+
return node_path_1.default.dirname(expandTilde(xdgEnv1Val, home));
|
|
137
218
|
}
|
|
138
219
|
// env[2]: XDG_CONFIG_HOME → subdir
|
|
139
220
|
const xdgEnv2Val = env[configHome.env[2]];
|
|
140
|
-
if (configHome.env[2] && xdgEnv2Val) {
|
|
141
|
-
return node_path_1.default.join(expandTilde(xdgEnv2Val), configHome.name);
|
|
221
|
+
if (configHome.env[2] && hasNonBlankOverride(xdgEnv2Val)) {
|
|
222
|
+
return node_path_1.default.join(expandTilde(xdgEnv2Val, home), configHome.name);
|
|
142
223
|
}
|
|
143
224
|
return node_path_1.default.join(home, '.config', configHome.name);
|
|
144
225
|
}
|
|
145
226
|
case 'generic-agents-root': {
|
|
146
227
|
// env override
|
|
147
228
|
const garEnv0Val = env[configHome.env[0]];
|
|
148
|
-
if (configHome.env[0] && garEnv0Val) {
|
|
149
|
-
return expandTilde(garEnv0Val);
|
|
229
|
+
if (configHome.env[0] && hasNonBlankOverride(garEnv0Val)) {
|
|
230
|
+
return expandTilde(garEnv0Val, home);
|
|
150
231
|
}
|
|
151
232
|
// probe each candidate; return first where probeExists subpath exists
|
|
152
233
|
for (const candidate of configHome.probe) {
|
|
153
|
-
const resolved =
|
|
234
|
+
const resolved = expandTilde(candidate, home);
|
|
154
235
|
if (existsSyncFn(node_path_1.default.join(resolved, configHome.probeExists))) {
|
|
155
236
|
return resolved;
|
|
156
237
|
}
|
|
157
238
|
}
|
|
158
239
|
// fallback: first probe candidate
|
|
159
|
-
return
|
|
240
|
+
return expandTilde(configHome.probe[0], home);
|
|
241
|
+
}
|
|
242
|
+
case 'none': {
|
|
243
|
+
// #2103: no file-projected config directory exists for this runtime
|
|
244
|
+
// (e.g. vscode). Previously this kind had no matching case, so the
|
|
245
|
+
// switch fell through and implicitly returned `undefined` — which
|
|
246
|
+
// then crashed a downstream `path.join(undefined, ...)` with a
|
|
247
|
+
// cryptic `TypeError [ERR_INVALID_ARG_TYPE]` far from the real cause.
|
|
248
|
+
// Throwing here makes the failure mode explicit; callers that need a
|
|
249
|
+
// nullable result (getGlobalSkillsBase) check `configHome.kind` and
|
|
250
|
+
// short-circuit BEFORE ever reaching this function.
|
|
251
|
+
throw new Error(`Runtime "${configHome.name}" has no config-home directory (configHome.kind === "none")`);
|
|
160
252
|
}
|
|
161
253
|
}
|
|
162
254
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Expand ~ using an explicit home directory (for hermetic testing).
|
|
165
|
-
*/
|
|
166
|
-
function expandTildeWithHome(p, home) {
|
|
167
|
-
if (!p)
|
|
168
|
-
return p;
|
|
169
|
-
if (p.startsWith('~/') || p === '~')
|
|
170
|
-
return node_path_1.default.join(home, p.slice(1));
|
|
171
|
-
return p;
|
|
172
|
-
}
|
|
173
255
|
/**
|
|
174
256
|
* Resolve Antigravity global config dir across 1.x and 2.x layouts.
|
|
175
257
|
*
|
|
@@ -257,25 +339,45 @@ function resolveKimiGlobalDir(opts = {}) {
|
|
|
257
339
|
}, { env, home, existsSync: existsSyncFn });
|
|
258
340
|
}
|
|
259
341
|
/**
|
|
260
|
-
* Resolve the directory holding Kimi
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
342
|
+
* Resolve the directory holding the Kimi product's OWN native config.toml —
|
|
343
|
+
* the file that product itself reads for providers/models/hooks/etc, and the
|
|
344
|
+
* one GSD writes its `[[hooks]]` block, hooks bundle and CommonJS marker into.
|
|
345
|
+
*
|
|
346
|
+
* The two Kimi runtimes share `hooksSurface: "kimi-hooks-toml"` but are
|
|
347
|
+
* different products with different roots, and this must be selected by
|
|
348
|
+
* `runtime` (#2755). Before that fix this function was unparameterized and a
|
|
349
|
+
* `--kimi-code` install wrote its hooks into Kimi CLI's `~/.kimi`, leaving Kimi
|
|
350
|
+
* Code with none:
|
|
351
|
+
*
|
|
352
|
+
* kimi → `~/.kimi`, overridden by `KIMI_SHARE_DIR`
|
|
353
|
+
* (moonshotai.github.io/kimi-cli/en/configuration/data-locations.html)
|
|
354
|
+
* kimi-code → `~/.kimi-code`, overridden by `KIMI_CODE_HOME`
|
|
355
|
+
* (moonshotai/kimi-code docs/en/configuration/data-locations.md;
|
|
356
|
+
* its hooks doc places `[[hooks]]` in `~/.kimi-code/config.toml`)
|
|
267
357
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
358
|
+
* Each product's env var is scoped to that product: `KIMI_SHARE_DIR` is Kimi
|
|
359
|
+
* CLI's own upstream variable and must NOT redirect kimi-code, nor vice versa.
|
|
360
|
+
*
|
|
361
|
+
* An unrecognised `runtime` (and an omitted one) falls back to `~/.kimi`, which
|
|
362
|
+
* preserves the pre-#2755 behaviour for every existing caller that passes no
|
|
363
|
+
* runtime — this function is exported, so that default is a contract.
|
|
364
|
+
*
|
|
365
|
+
* For BOTH runtimes this is deliberately a SEPARATE directory from the generic
|
|
366
|
+
* Agent-Skills root resolved by `resolveKimiGlobalDir` (`~/.config/agents`):
|
|
367
|
+
* both vendors' docs confirm the Agent-Skills search path is independent of the
|
|
368
|
+
* data-root env var. GSD's native `[[hooks]]` entries go in
|
|
369
|
+
* `<this dir>/config.toml`, never into the skills configDir.
|
|
274
370
|
*/
|
|
275
371
|
function resolveKimiHooksTomlDir(opts = {}) {
|
|
276
372
|
const env = opts.env ?? process.env;
|
|
277
373
|
const home = opts.home ?? node_os_1.default.homedir();
|
|
278
|
-
|
|
374
|
+
// Explicit comparison rather than an object lookup keyed on `runtime`: the
|
|
375
|
+
// value originates from argv, and an index would resolve inherited keys
|
|
376
|
+
// (`constructor`, `__proto__`) to something that is not a descriptor.
|
|
377
|
+
const descriptor = opts.runtime === 'kimi-code'
|
|
378
|
+
? { kind: 'dot-home', name: '.kimi-code', env: ['KIMI_CODE_HOME'] }
|
|
379
|
+
: { kind: 'dot-home', name: '.kimi', env: ['KIMI_SHARE_DIR'] };
|
|
380
|
+
return resolveConfigHomeFromDescriptor(descriptor, { env, home });
|
|
279
381
|
}
|
|
280
382
|
/**
|
|
281
383
|
* Return the global config base directory for the given runtime.
|
|
@@ -317,6 +419,17 @@ function resolveSkillsBaseFromDescriptor(configHome, opts = {}, skillsDestSubpat
|
|
|
317
419
|
function getGlobalSkillsBase(runtime) {
|
|
318
420
|
const runtimeEntry = getRegistry().runtimes[runtime];
|
|
319
421
|
const descriptor = runtimeEntry?.runtime;
|
|
422
|
+
// #2103: a runtime with `configHome.kind === 'none'` (e.g. vscode —
|
|
423
|
+
// Marketplace/VSIX extension, installSurface:'none') has no file-projected
|
|
424
|
+
// config directory at all, and therefore no skills root. Short-circuit to
|
|
425
|
+
// null BEFORE falling through to getGlobalConfigDir below, which would
|
|
426
|
+
// otherwise throw resolving a 'none' configHome (see
|
|
427
|
+
// resolveConfigHomeFromDescriptor's 'none' case). null is the correct
|
|
428
|
+
// answer here, not a crash — the `=== null` guard at every call site
|
|
429
|
+
// (bin/install.js --skills-root, gsd-tools routeSkillsRoot) already
|
|
430
|
+
// handles it as "this runtime does not use a skills directory".
|
|
431
|
+
if (descriptor?.configHome?.kind === 'none')
|
|
432
|
+
return null;
|
|
320
433
|
const globalSkillsKind = descriptor?.artifactLayout?.global?.find((entry) => entry.kind === 'skills');
|
|
321
434
|
// ADR-1239 upgrade 3 (#2088): honor a skills-kind `home` override (e.g. Codex
|
|
322
435
|
// → $HOME/.agents/skills, independent of $CODEX_HOME) so the reported skills
|
|
@@ -28,6 +28,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
29
29
|
const node_path_1 = __importDefault(require("node:path"));
|
|
30
30
|
const node_os_1 = __importDefault(require("node:os"));
|
|
31
|
+
// #2544: the single source of truth for the CommonJS module-type marker. The
|
|
32
|
+
// two helpers below are thin boolean-returning shims over these — see the
|
|
33
|
+
// marker section for why this file no longer carries its own copy.
|
|
34
|
+
const commonjs_marker_cjs_1 = require("./commonjs-marker.cjs");
|
|
31
35
|
const imperative_hook_bus_cjs_1 = require("./host-integration-adapters/imperative-hook-bus.cjs");
|
|
32
36
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
37
|
const shellCmdProjection = require("./shell-command-projection.cjs");
|
|
@@ -166,62 +170,51 @@ function atomicWriteFileSync(target, data, options) {
|
|
|
166
170
|
//
|
|
167
171
|
// The marker content is byte-identical to installSharedHooksBundle's
|
|
168
172
|
// (bin/install.js installSharedHooksBundle): {"type":"commonjs"}\n.
|
|
173
|
+
//
|
|
174
|
+
// #2544: the two helpers below no longer carry their own copy of the write and
|
|
175
|
+
// remove rules — they DELEGATE to src/commonjs-marker.cts, which #2544 makes the
|
|
176
|
+
// single place both rules are enforced. Keeping a second copy here was not
|
|
177
|
+
// merely redundant; the copies had drifted apart on exactly the two properties
|
|
178
|
+
// that matter:
|
|
179
|
+
//
|
|
180
|
+
// - ownership probe: `fs.existsSync` FOLLOWS symlinks and reports `false` for
|
|
181
|
+
// a DANGLING one, so a dangling `package.json` symlink classified as absent
|
|
182
|
+
// and the write below followed the link outside the directory GSD owns.
|
|
183
|
+
// `classifyMarker` uses `lstat` + `isFile()`, so a symlink or a directory at
|
|
184
|
+
// the marker path is classified `foreign` and left strictly alone.
|
|
185
|
+
// - create: a plain `writeFileSync` leaves the classify->write window open.
|
|
186
|
+
// `ensureCommonJsMarker` creates with `flag: 'wx'` (O_EXCL), so anything
|
|
187
|
+
// that appears at the path in between fails with EEXIST instead of being
|
|
188
|
+
// followed or overwritten.
|
|
189
|
+
//
|
|
190
|
+
// The exported signatures are unchanged (both still return a boolean), so every
|
|
191
|
+
// caller and the #2717 tests are unaffected.
|
|
169
192
|
// ---------------------------------------------------------------------------
|
|
170
|
-
/** The exact marker content GSD writes, matching installSharedHooksBundle. */
|
|
171
|
-
const COMMONJS_MARKER_CONTENT = '{"type":"commonjs"}\n';
|
|
172
193
|
/**
|
|
173
194
|
* Ensure a `package.json` forcing CommonJS mode exists in `dir` (the directory
|
|
174
195
|
* holding GSD-staged `.js` hook scripts). Idempotent: a no-op if the marker is
|
|
175
|
-
* already present with GSD's content.
|
|
176
|
-
*
|
|
177
|
-
* user-authored package.json (it leaves such a file in place; the user owns it).
|
|
196
|
+
* already present with GSD's content. Never clobbers a distinct user-authored
|
|
197
|
+
* package.json (it leaves such a file in place; the user owns it).
|
|
178
198
|
*
|
|
179
199
|
* @param dir - absolute path to the directory holding the staged .js hooks
|
|
180
|
-
* @returns `true` if
|
|
200
|
+
* @returns `true` if GSD's marker is present after the call (written or already there)
|
|
181
201
|
*/
|
|
182
202
|
function ensureCommonJsMarker(dir) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (existing.trim() === '{"type":"commonjs"}')
|
|
189
|
-
return true;
|
|
190
|
-
// A distinct package.json the user owns — do NOT clobber. The hook will
|
|
191
|
-
// load as whatever type the user declared; that is the user's choice.
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
node_fs_1.default.writeFileSync(markerPath, COMMONJS_MARKER_CONTENT);
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
// Best-effort: a marker write failure must not fail the whole install.
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
203
|
+
// 'written' | 'unchanged' -> the marker is ours and present.
|
|
204
|
+
// 'preserved-foreign' -> a file GSD does not own is there; left untouched.
|
|
205
|
+
// 'failed' -> environmental (EACCES/EROFS/ENOSPC); best-effort.
|
|
206
|
+
const outcome = (0, commonjs_marker_cjs_1.ensureCommonJsMarker)(dir);
|
|
207
|
+
return outcome === 'written' || outcome === 'unchanged';
|
|
201
208
|
}
|
|
202
209
|
/**
|
|
203
210
|
* Remove the CommonJS marker from `dir` on uninstall — but ONLY if it carries
|
|
204
211
|
* GSD's exact marker content. A user-authored package.json is never deleted.
|
|
205
|
-
* Mirrors the kimi uninstall guard in bin/install.js.
|
|
206
212
|
*
|
|
207
213
|
* @param dir - absolute path to the directory that held the staged .js hooks
|
|
208
214
|
* @returns `true` if a GSD-owned marker was removed
|
|
209
215
|
*/
|
|
210
216
|
function removeCommonJsMarkerIfGsdOwned(dir) {
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
if (!node_fs_1.default.existsSync(markerPath))
|
|
214
|
-
return false;
|
|
215
|
-
const content = node_fs_1.default.readFileSync(markerPath, 'utf8').trim();
|
|
216
|
-
if (content === '{"type":"commonjs"}') {
|
|
217
|
-
node_fs_1.default.unlinkSync(markerPath);
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
217
|
+
return (0, commonjs_marker_cjs_1.removeCommonJsMarker)(dir);
|
|
225
218
|
}
|
|
226
219
|
// ---------------------------------------------------------------------------
|
|
227
220
|
// parseTomlValue + findMultilineBasicStringClose
|
|
@@ -1072,7 +1065,15 @@ function writeCursorHooksJson(targetDir, src, opts) {
|
|
|
1072
1065
|
// installSharedHooksBundle (the only other writer of this marker); without
|
|
1073
1066
|
// it, a ~/.cursor/package.json declaring {"type":"module"} makes Node load
|
|
1074
1067
|
// these require()-using scripts as ESM and every Cursor hook fails silently.
|
|
1075
|
-
|
|
1068
|
+
//
|
|
1069
|
+
// #2544: gated on having actually staged a script, mirroring
|
|
1070
|
+
// installSharedHooksBundle's `stagedHooks` gate. hooks/ is shared space, and
|
|
1071
|
+
// this function mkdirs it unconditionally — so an ungated write drops a GSD
|
|
1072
|
+
// marker into a directory GSD created but did not fill, which is the same
|
|
1073
|
+
// write-into-someone-else's-territory this issue is about.
|
|
1074
|
+
if (installedScripts.size > 0) {
|
|
1075
|
+
ensureCommonJsMarker(hooksDir);
|
|
1076
|
+
}
|
|
1076
1077
|
const hookOpts = { runtime: 'cursor', platform: opts.platform || process.platform };
|
|
1077
1078
|
const commands = {};
|
|
1078
1079
|
for (const ev of events) {
|
|
@@ -1266,7 +1267,12 @@ function writeWindsurfHooksJson(targetDir, src, opts) {
|
|
|
1266
1267
|
// installSharedHooksBundle (the only other writer of this marker); without
|
|
1267
1268
|
// it, a config-root package.json declaring {"type":"module"} makes Node load
|
|
1268
1269
|
// these require()-using scripts as ESM and the Windsurf hooks fail silently.
|
|
1269
|
-
|
|
1270
|
+
//
|
|
1271
|
+
// #2544: gated on having actually staged a script — see the identical gate in
|
|
1272
|
+
// the Cursor writer above and `stagedHooks` in installSharedHooksBundle.
|
|
1273
|
+
if (installedScripts.size > 0) {
|
|
1274
|
+
ensureCommonJsMarker(hooksDir);
|
|
1275
|
+
}
|
|
1270
1276
|
const hookOpts = { runtime: 'windsurf', platform: opts.platform || process.platform };
|
|
1271
1277
|
const commands = {};
|
|
1272
1278
|
for (const ev of WINDSURF_HOOK_EVENTS) {
|
|
@@ -1597,6 +1603,63 @@ function applySettingsJsonHooks(settings, opts) {
|
|
|
1597
1603
|
else if (!hasWorktreePathGuardHook && !node_fs_1.default.existsSync(worktreePathGuardFile)) {
|
|
1598
1604
|
console.warn(` ${yellow}⚠${reset} Skipped worktree path guard hook — gsd-worktree-path-guard.js not found at target`);
|
|
1599
1605
|
}
|
|
1606
|
+
// Configure PreToolUse hook for Agent-dispatch isolation (#3045)
|
|
1607
|
+
// Hard-blocks an executor Agent() dispatch (subagent_type="gsd-executor")
|
|
1608
|
+
// missing its harness isolation parameter when this project's resolved
|
|
1609
|
+
// dispatch isolation is harness-worktree. Prevents the executor from
|
|
1610
|
+
// silently running and committing in the primary checkout when the
|
|
1611
|
+
// model-authored dispatch omits isolation="worktree".
|
|
1612
|
+
const agentIsolationGuardCommand = isGlobal
|
|
1613
|
+
? buildHookCommand(targetDir, 'gsd-agent-isolation-guard.js', hookOpts)
|
|
1614
|
+
: localCmd('gsd-agent-isolation-guard.js');
|
|
1615
|
+
const hasAgentIsolationGuardHook = settings.hooks[preToolEvent].some((entry) => entry.hooks && entry.hooks.some((h) => referencesHook(h, 'gsd-agent-isolation-guard')));
|
|
1616
|
+
const agentIsolationGuardFile = node_path_1.default.join(targetDir, 'hooks', 'gsd-agent-isolation-guard.js');
|
|
1617
|
+
if (!hasAgentIsolationGuardHook && node_fs_1.default.existsSync(agentIsolationGuardFile) && agentIsolationGuardCommand) {
|
|
1618
|
+
settings.hooks[preToolEvent].push({
|
|
1619
|
+
// #3045 MAJOR 1: widened from "Agent"-only — hooks.json's own
|
|
1620
|
+
// PostToolUse precedent (context-monitor) already hedges both names,
|
|
1621
|
+
// and the hook itself now accepts tool_name "Task" too.
|
|
1622
|
+
matcher: 'Agent|Task',
|
|
1623
|
+
hooks: [
|
|
1624
|
+
{
|
|
1625
|
+
type: 'command',
|
|
1626
|
+
command: agentIsolationGuardCommand,
|
|
1627
|
+
timeout: 5
|
|
1628
|
+
}
|
|
1629
|
+
]
|
|
1630
|
+
});
|
|
1631
|
+
console.log(` ${green}✓${reset} Configured agent isolation dispatch guard hook`);
|
|
1632
|
+
}
|
|
1633
|
+
else if (!hasAgentIsolationGuardHook && !node_fs_1.default.existsSync(agentIsolationGuardFile)) {
|
|
1634
|
+
console.warn(` ${yellow}⚠${reset} Skipped agent isolation guard hook — gsd-agent-isolation-guard.js not found at target`);
|
|
1635
|
+
}
|
|
1636
|
+
// Configure PreToolUse hook for catastrophic-shrink protection (#2255, fix 3 of #973)
|
|
1637
|
+
// Hard-blocks a whole-file Write that collapses a curated .planning/ artifact
|
|
1638
|
+
// (ROADMAP.md, milestone roadmaps, STATE.md) far below its on-disk size.
|
|
1639
|
+
// Escape hatches (both named in the block message): the single-use
|
|
1640
|
+
// sentinel .planning/.gsd-allow-shrink (workflow steps — a per-step env
|
|
1641
|
+
// cannot reach a hook) and GSD_ALLOW_PLANNING_SHRINK=1 (interactive).
|
|
1642
|
+
const writeGuardCommand = isGlobal
|
|
1643
|
+
? buildHookCommand(targetDir, 'gsd-write-guard.js', hookOpts)
|
|
1644
|
+
: localCmd('gsd-write-guard.js');
|
|
1645
|
+
const hasWriteGuardHook = settings.hooks[preToolEvent].some((entry) => entry.hooks && entry.hooks.some((h) => referencesHook(h, 'gsd-write-guard')));
|
|
1646
|
+
const writeGuardFile = node_path_1.default.join(targetDir, 'hooks', 'gsd-write-guard.js');
|
|
1647
|
+
if (!hasWriteGuardHook && node_fs_1.default.existsSync(writeGuardFile) && writeGuardCommand) {
|
|
1648
|
+
settings.hooks[preToolEvent].push({
|
|
1649
|
+
matcher: 'Write',
|
|
1650
|
+
hooks: [
|
|
1651
|
+
{
|
|
1652
|
+
type: 'command',
|
|
1653
|
+
command: writeGuardCommand,
|
|
1654
|
+
timeout: 5
|
|
1655
|
+
}
|
|
1656
|
+
]
|
|
1657
|
+
});
|
|
1658
|
+
console.log(` ${green}✓${reset} Configured write guard hook (catastrophic-shrink protection)`);
|
|
1659
|
+
}
|
|
1660
|
+
else if (!hasWriteGuardHook && !node_fs_1.default.existsSync(writeGuardFile)) {
|
|
1661
|
+
console.warn(` ${yellow}⚠${reset} Skipped write guard hook — gsd-write-guard.js not found at target`);
|
|
1662
|
+
}
|
|
1600
1663
|
// Configure commit validation hook (Conventional Commits enforcement, opt-in)
|
|
1601
1664
|
const validateCommitCommand = isGlobal
|
|
1602
1665
|
? buildHookCommand(targetDir, 'gsd-validate-commit.sh', hookOpts)
|
|
@@ -1925,6 +1988,7 @@ function buildKimiHooksTomlBlock(targetDir, opts) {
|
|
|
1925
1988
|
{ event: 'PreToolUse', command: cmd('gsd-prompt-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
|
|
1926
1989
|
{ event: 'PreToolUse', command: cmd('gsd-read-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
|
|
1927
1990
|
{ event: 'PreToolUse', command: cmd('gsd-worktree-path-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
|
|
1991
|
+
{ event: 'PreToolUse', command: cmd('gsd-write-guard.js'), matcher: 'WriteFile', timeout: 5 },
|
|
1928
1992
|
{ event: 'PreToolUse', command: cmd('gsd-workflow-guard.js'), matcher: 'Shell|WriteFile|StrReplaceFile', timeout: 5 },
|
|
1929
1993
|
{ event: 'PreToolUse', command: cmd('gsd-validate-commit.sh'), matcher: 'Shell', timeout: 5 },
|
|
1930
1994
|
// PostToolUse
|