feat(shell-projection): centralize managed hook command policy (#3450)

* feat(shell-projection): route persistent PATH hints through projection seam

* refactor(shell-projection): unify managed hook command policy

* fix(install): guard malformed settings hooks during uninstall

* chore(changeset): add fragment for pr 3450

* fix(install): guard malformed settings hook entries
This commit is contained in:
Tom Boucher
2026-05-12 20:15:43 -04:00
committed by GitHub
parent 16f5009270
commit bf3c736029
10 changed files with 341 additions and 42 deletions

View File

@@ -13,6 +13,7 @@ const {
hookCommandNeedsPowerShellCallOperator,
formatHookCommandForRuntime,
isManagedHookBasename,
isManagedHookCommand,
projectLocalHookPrefix,
projectLegacySettingsHookCommand,
projectPortableHookBaseDir,
@@ -141,4 +142,48 @@ describe('bug #3439: shell projection module owns managed-hook policy and legacy
'/opt/custom/.claude',
);
});
test('isManagedHookCommand classifies managed settings hooks and leaves user commands untouched', () => {
assert.equal(
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.claude/hooks/gsd-statusline.js"', {
surface: 'settings-json',
}),
true,
);
assert.equal(
isManagedHookCommand('"C:/Program Files/Git/bin/bash.exe" "C:/Users/me/.claude/hooks/gsd-session-state.sh"', {
surface: 'settings-json',
}),
true,
);
assert.equal(
isManagedHookCommand('bash /Users/me/.claude/hooks/custom-lint.sh', {
surface: 'settings-json',
}),
false,
);
});
test('isManagedHookCommand supports codex surfaces and optional legacy alias matching', () => {
const command = '"/usr/local/bin/node" "/Users/me/.codex/hooks/gsd-check-update.js"';
assert.equal(
isManagedHookCommand(command, {
surface: 'codex-toml',
}),
true,
);
assert.equal(
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.codex/hooks/gsd-update-check.js"', {
surface: 'codex-toml',
}),
false,
);
assert.equal(
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.codex/hooks/gsd-update-check.js"', {
surface: 'codex-toml',
includeLegacyAliases: true,
}),
true,
);
});
});