fix: filter stale hooks check to gsd-prefixed files only (#1200)

gsd-check-update.js scanned ALL .js files in the hooks directory and
flagged any without a gsd-hook-version header as stale. This incorrectly
flagged user-created hooks (e.g. guard-edits-outside-project.js),
producing a persistent 'stale hooks' warning that /gsd:update couldn't
resolve.

Fix: filter hookFiles to f.startsWith('gsd-') && f.endsWith('.js')
since all GSD hooks follow the gsd-* naming convention.

Includes regression test validating the filter excludes user hooks.

Closes #1200
This commit is contained in:
Tom Boucher
2026-03-18 23:29:38 -04:00
parent 5fd384f336
commit 9ca03ec35e
2 changed files with 31 additions and 1 deletions

View File

@@ -70,7 +70,7 @@ const child = spawn(process.execPath, ['-e', `
const hooksDir = path.join(configDir, 'hooks');
try {
if (fs.existsSync(hooksDir)) {
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js'));
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.startsWith('gsd-') && f.endsWith('.js'));
for (const hookFile of hookFiles) {
try {
const content = fs.readFileSync(path.join(hooksDir, hookFile), 'utf8');

View File

@@ -867,3 +867,33 @@ describe('normalizeMd', () => {
assert.ok(result.includes('\n\n- Decision 1'), 'list needs blank line before');
});
});
// ─── Stale hook filter regression (#1200) ─────────────────────────────────────
describe('stale hook filter', () => {
test('filter should only match gsd-prefixed .js files', () => {
const files = [
'gsd-check-update.js',
'gsd-context-monitor.js',
'gsd-statusline.js',
'gsd-workflow-guard.js',
'guard-edits-outside-project.js', // user hook
'my-custom-hook.js', // user hook
'gsd-check-update.js.bak', // backup file
'README.md', // non-js file
];
const gsdFilter = f => f.startsWith('gsd-') && f.endsWith('.js');
const filtered = files.filter(gsdFilter);
assert.deepStrictEqual(filtered, [
'gsd-check-update.js',
'gsd-context-monitor.js',
'gsd-statusline.js',
'gsd-workflow-guard.js',
], 'should only include gsd-prefixed .js files');
assert.ok(!filtered.includes('guard-edits-outside-project.js'), 'must not include user hooks');
assert.ok(!filtered.includes('my-custom-hook.js'), 'must not include non-gsd hooks');
});
});