Skip to content

fix(build): ship gsd-read-injection-scanner hook to users#2407

Merged
jeremymcs merged 2 commits intomainfrom
fix/2406-ship-read-injection-scanner
Apr 19, 2026
Merged

fix(build): ship gsd-read-injection-scanner hook to users#2407
jeremymcs merged 2 commits intomainfrom
fix/2406-ship-read-injection-scanner

Conversation

@jeremymcs
Copy link
Copy Markdown
Collaborator

@jeremymcs jeremymcs commented Apr 18, 2026

Linked Issue

Fixes #2406

What was broken

The gsd-read-injection-scanner PostToolUse hook (added in #2201) never landed in installed copies of GSD on 1.37.0 or 1.37.1. Every install printed:

⚠ Skipped read injection scanner hook — gsd-read-injection-scanner.js not found at target

…and the read-time prompt-injection scanning feature was silently disabled for every user.

What this fix does

Adds gsd-read-injection-scanner.js to the HOOKS_TO_COPY allowlist in scripts/build-hooks.js so the build step copies it into hooks/dist/. bin/install.js reads from hooks/dist/, so the file now lands at ~/.claude/hooks/gsd-read-injection-scanner.js on install, the skip warning goes away, and the PostToolUse Read matcher gets wired up as designed.

Root cause

scripts/build-hooks.js uses a hardcoded HOOKS_TO_COPY allowlist rather than enumerating hooks/*.js/hooks/*.sh. #2328 added the scanner source under hooks/ but didn't update the allowlist, and nothing in CI caught the gap — the npm tarball shipped the file at hooks/gsd-read-injection-scanner.js but not at hooks/dist/gsd-read-injection-scanner.js, which is what install.js:5812 reads.

Confirmed against the published v1.37.1 tarball:

$ npm pack get-shit-done-cc@1.37.1 --dry-run | grep inject
npm notice 5.6kB hooks/gsd-read-injection-scanner.js
# (absent from hooks/dist/)

Testing

How I verified the fix

  • node scripts/build-hooks.js — output now includes ✓ Copying gsd-read-injection-scanner.js...
  • node --test tests/install-hooks-copy.test.cjs — 24/24 passing, including the regression test below
  • Manual: the missing file now exists in hooks/dist/ and the copy-simulation test finds it at the install target

Regression test added?

  • Yes — added gsd-read-injection-scanner.js to EXPECTED_ALL_HOOKS in tests/install-hooks-copy.test.cjs, so the existing "all expected hooks are copied from hooks/dist/ to target" test now fails fast if any future hook is added to hooks/ but forgotten in the build allowlist.

Platforms tested

  • macOS
  • Windows — no platform-specific code touched
  • Linux — no platform-specific code touched
  • N/A (not platform-specific)

Runtimes tested

  • Claude Code
  • N/A (build-time fix, runtime-agnostic)

Checklist

  • Issue linked above with Fixes #2406
  • Linked issue has the confirmed-bug label (pending maintainer triage)
  • Fix is scoped to the reported bug — no unrelated changes included
  • Regression test added
  • All existing tests pass (install-hooks-copy suite)
  • CHANGELOG.md updated
  • No unnecessary dependencies added

Breaking changes

None.

Summary by CodeRabbit

  • Bug Fixes

    • The gsd-read-injection-scanner hook is now correctly included in user installations (previously omitted in some builds).
  • Tests

    • Test suite updated to assert the newly included hook is copied and marked executable where applicable.

The scanner was added in #2201 but never added to the HOOKS_TO_COPY
allowlist in scripts/build-hooks.js, so it never landed in hooks/dist/.
install.js reads from hooks/dist/, so every install on 1.37.0/1.37.1
emitted "Skipped read injection scanner hook — not found at target"
and the read-time prompt-injection scanner was silently disabled.

- Add gsd-read-injection-scanner.js to HOOKS_TO_COPY
- Add it to EXPECTED_ALL_HOOKS regression test in install-hooks-copy

Fixes #2406

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3989beaf-714c-45b1-9f5d-a7fcb1d309e4

📥 Commits

Reviewing files that changed from the base of the PR and between 13a96ee and 3856b53.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • hooks/gsd-read-injection-scanner.js
✅ Files skipped from review due to trivial changes (2)
  • hooks/gsd-read-injection-scanner.js
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Adds the previously omitted gsd-read-injection-scanner.js hook to the build allowlist, updates tests and changelog, and removes a redundant .claude/hooks/ path check in the hook file.

Changes

Cohort / File(s) Summary
Build script & test
scripts/build-hooks.js, tests/install-hooks-copy.test.cjs
Added gsd-read-injection-scanner.js to HOOKS_TO_COPY and updated EXPECTED_ALL_HOOKS in tests to include the new hook.
Hook logic
hooks/gsd-read-injection-scanner.js
Removed a redundant .claude/hooks/ path check; kept the existing exclusion check p.includes('/.claude/hooks/').
Changelog
CHANGELOG.md
Documented the fix under Unreleased → Fixed noting the hook is now included and redundant path check removal.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Possibly related PRs

Suggested labels

size/M

Suggested reviewers

  • glittercowboy

Poem

🐰
A tiny hook once missed the pack,
I hopped and fixed the build's small crack.
Now scanner sails in users' homes,
No more warnings—no more moans.
Hooray for hooks and happy combs! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(build): ship gsd-read-injection-scanner hook to users' directly and clearly describes the main change—adding the gsd-read-injection-scanner hook to the build output so it ships to users.
Description check ✅ Passed The PR description provides comprehensive context: the linked issue (#2406), clear explanation of what was broken, detailed fix description, root cause analysis, testing evidence, regression test confirmation, and a completed checklist.
Linked Issues check ✅ Passed All coding requirements from issue #2406 are met: gsd-read-injection-scanner.js is added to HOOKS_TO_COPY in scripts/build-hooks.js, regression test added to tests/install-hooks-copy.test.cjs, hook logic adjusted in hooks/gsd-read-injection-scanner.js, and CHANGELOG.md updated.
Out of Scope Changes check ✅ Passed All changes directly address fixing #2406: CHANGELOG.md documents the fix, scripts/build-hooks.js adds the scanner to HOOKS_TO_COPY, test suite includes regression coverage, and the hook source is minimally adjusted to remove redundant path checks.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/2406-ship-read-injection-scanner

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/install-hooks-copy.test.cjs (1)

33-42: Extend uninstall-path test vectors for the new scanner hook as well.

EXPECTED_ALL_HOOKS now includes gsd-read-injection-scanner.js, but uninstall-related vectors later in this file still omit it (expectedHookNames, mirrored isGsdHookCommand, and gsdCommands). Adding it there keeps lifecycle coverage symmetric.

✅ Minimal test updates
   const expectedHookNames = [
     'gsd-check-update', 'gsd-statusline', 'gsd-session-state',
     'gsd-context-monitor', 'gsd-phase-boundary', 'gsd-prompt-guard',
-    'gsd-read-guard', 'gsd-validate-commit', 'gsd-workflow-guard',
+    'gsd-read-guard', 'gsd-read-injection-scanner', 'gsd-validate-commit', 'gsd-workflow-guard',
   ];
   const isGsdHookCommand = (cmd) =>
     cmd && (cmd.includes('gsd-check-update') || cmd.includes('gsd-statusline') ||
       cmd.includes('gsd-session-state') || cmd.includes('gsd-context-monitor') ||
       cmd.includes('gsd-phase-boundary') || cmd.includes('gsd-prompt-guard') ||
-      cmd.includes('gsd-read-guard') || cmd.includes('gsd-validate-commit') ||
+      cmd.includes('gsd-read-guard') || cmd.includes('gsd-read-injection-scanner') || cmd.includes('gsd-validate-commit') ||
       cmd.includes('gsd-workflow-guard'));
   const gsdCommands = [
     'node /path/gsd-check-update.js',
     'node /path/gsd-statusline.js',
     'bash /path/gsd-session-state.sh',
     'node /path/gsd-context-monitor.js',
     'bash /path/gsd-phase-boundary.sh',
     'node /path/gsd-prompt-guard.js',
     'node /path/gsd-read-guard.js',
+    'node /path/gsd-read-injection-scanner.js',
     'bash /path/gsd-validate-commit.sh',
     'node /path/gsd-workflow-guard.js',
   ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/install-hooks-copy.test.cjs` around lines 33 - 42, The uninstall-path
test vectors are missing the new scanner hook; update the uninstall-related
arrays/sets so they match EXPECTED_ALL_HOOKS by including
'gsd-read-injection-scanner.js'. Specifically, add
'gsd-read-injection-scanner.js' to expectedHookNames, ensure isGsdHookCommand
recognizes it, and include it in the gsdCommands list used by the uninstall-path
tests so lifecycle coverage remains symmetric with EXPECTED_ALL_HOOKS.
scripts/build-hooks.js (1)

17-30: Add an inventory drift guard around the manual hook allowlist.

This fixes the current miss, but future omissions are still easy because HOOKS_TO_COPY is hand-maintained. Add a check that all hooks/*.js and hooks/*.sh are either explicitly allowlisted or explicitly excluded.

♻️ Suggested hardening (example)
 const HOOKS_TO_COPY = [
   'gsd-check-update-worker.js',
   'gsd-check-update.js',
   'gsd-context-monitor.js',
   'gsd-prompt-guard.js',
   'gsd-read-guard.js',
   'gsd-read-injection-scanner.js',
   'gsd-statusline.js',
   'gsd-workflow-guard.js',
   // Community hooks (bash, opt-in via .planning/config.json hooks.community)
   'gsd-session-state.sh',
   'gsd-validate-commit.sh',
   'gsd-phase-boundary.sh'
 ];
+
+// Hooks intentionally present in hooks/ but not shipped.
+const HOOKS_EXCLUDED_FROM_DIST = new Set([
+  // e.g. 'experimental-hook.js'
+]);
+
+function validateHookInventory() {
+  const discovered = fs.readdirSync(HOOKS_DIR, { withFileTypes: true })
+    .filter((d) => d.isFile())
+    .map((d) => d.name)
+    .filter((name) => name.endsWith('.js') || name.endsWith('.sh'));
+
+  const unmanaged = discovered.filter(
+    (name) => !HOOKS_TO_COPY.includes(name) && !HOOKS_EXCLUDED_FROM_DIST.has(name)
+  );
+
+  if (unmanaged.length) {
+    throw new Error(
+      `Unmanaged hooks in hooks/: ${unmanaged.join(', ')}. ` +
+      `Add to HOOKS_TO_COPY or HOOKS_EXCLUDED_FROM_DIST.`
+    );
+  }
+}
 
 function build() {
+  validateHookInventory();
   // Ensure dist directory exists
   if (!fs.existsSync(DIST_DIR)) {
     fs.mkdirSync(DIST_DIR, { recursive: true });
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build-hooks.js` around lines 17 - 30, HOOKS_TO_COPY is a
hand-maintained allowlist that can drift from the actual hooks directory; add an
inventory drift guard that enumerates files under hooks/*.js and hooks/*.sh, and
ensures every discovered filename is either present in HOOKS_TO_COPY or present
in a short explicit EXPLICIT_EXCLUDES array; implement this check right after
the HOOKS_TO_COPY declaration (or at the start of the existing copy routine) and
fail fast (throw or process.exit(1) with a clear message listing missing
allowlist entries) so CI/builds break when new hook files are added but not
reviewed/allowlisted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/build-hooks.js`:
- Around line 17-30: HOOKS_TO_COPY is a hand-maintained allowlist that can drift
from the actual hooks directory; add an inventory drift guard that enumerates
files under hooks/*.js and hooks/*.sh, and ensures every discovered filename is
either present in HOOKS_TO_COPY or present in a short explicit EXPLICIT_EXCLUDES
array; implement this check right after the HOOKS_TO_COPY declaration (or at the
start of the existing copy routine) and fail fast (throw or process.exit(1) with
a clear message listing missing allowlist entries) so CI/builds break when new
hook files are added but not reviewed/allowlisted.

In `@tests/install-hooks-copy.test.cjs`:
- Around line 33-42: The uninstall-path test vectors are missing the new scanner
hook; update the uninstall-related arrays/sets so they match EXPECTED_ALL_HOOKS
by including 'gsd-read-injection-scanner.js'. Specifically, add
'gsd-read-injection-scanner.js' to expectedHookNames, ensure isGsdHookCommand
recognizes it, and include it in the gsdCommands list used by the uninstall-path
tests so lifecycle coverage remains symmetric with EXPECTED_ALL_HOOKS.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 99c16994-595c-4fa3-9e76-880f538ae476

📥 Commits

Reviewing files that changed from the base of the PR and between 28d6649 and 13a96ee.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • scripts/build-hooks.js
  • tests/install-hooks-copy.test.cjs

@github-actions github-actions Bot added size/S and removed size/S labels Apr 18, 2026
@jeremymcs jeremymcs merged commit f330ab5 into main Apr 19, 2026
9 checks passed
@github-actions github-actions Bot deleted the fix/2406-ship-read-injection-scanner branch April 19, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

build-hooks.js omits gsd-read-injection-scanner.js — scanner hook never ships to users

1 participant