fix(install): preserve non-array hook entries during uninstall (#1824)

* fix(install): preserve non-array hook entries during uninstall

Uninstall filtering returned null for hook entries without a hooks
array, silently deleting user-owned entries with unexpected shapes.
Return the entry unchanged instead so only GSD hooks are removed.

* test(install): add regression test for non-array hook entry preservation (#1825)

Fix mirrored filterGsdHooks helper to match production code and add
test proving non-array hook entries survive uninstall filtering.
This commit is contained in:
Jeremy McSpadden
2026-04-05 22:07:59 -05:00
committed by GitHub
parent d52c880eec
commit 00c6a5ea68
2 changed files with 15 additions and 2 deletions

View File

@@ -4432,7 +4432,7 @@ function uninstall(isGlobal, runtime = 'claude') {
const before = JSON.stringify(settings.hooks[eventName]);
settings.hooks[eventName] = settings.hooks[eventName]
.map(entry => {
if (!entry.hooks || !Array.isArray(entry.hooks)) return null;
if (!entry.hooks || !Array.isArray(entry.hooks)) return entry;
// Filter out individual GSD hooks, keep user hooks
entry.hooks = entry.hooks.filter(h => !isGsdHookCommand(h.command));
return entry.hooks.length > 0 ? entry : null;

View File

@@ -325,7 +325,7 @@ describe('uninstall settings cleanup preserves user hooks', () => {
function filterGsdHooks(entries) {
return entries
.map(entry => {
if (!entry.hooks || !Array.isArray(entry.hooks)) return null;
if (!entry.hooks || !Array.isArray(entry.hooks)) return entry;
entry.hooks = entry.hooks.filter(h => !isGsdHookCommand(h.command));
return entry.hooks.length > 0 ? entry : null;
})
@@ -372,6 +372,19 @@ describe('uninstall settings cleanup preserves user hooks', () => {
assert.strictEqual(result[0].hooks.length, 1, 'user hook should remain');
});
test('non-array hook entries are preserved during uninstall (#1825)', () => {
const entries = [
{ type: 'custom', command: 'echo hello' },
{ matcher: 'Bash', hooks: [{ type: 'command', command: 'node /path/to/gsd-prompt-guard.js' }] },
{ url: 'https://example.com/webhook' },
];
const result = filterGsdHooks(JSON.parse(JSON.stringify(entries)));
assert.strictEqual(result.length, 2, 'both non-array entries should survive');
assert.deepStrictEqual(result[0], { type: 'custom', command: 'echo hello' }, 'first non-array entry preserved');
assert.deepStrictEqual(result[1], { url: 'https://example.com/webhook' }, 'second non-array entry preserved');
});
test('all GSD hook names are recognized by isGsdHookCommand', () => {
const gsdCommands = [
'node /path/gsd-check-update.js',