* fix(3579): ship graphify hook + lib/ helper through build-hooks + install
`scripts/build-hooks.js` `HOOKS_TO_COPY` did not include
`gsd-graphify-update.sh` (added in #3347 / PR #3557), so it never landed
in `hooks/dist/` and `bin/install.js` — which `readdirSync`s the dist —
never copied it to `~/.claude/hooks/`. The hook's detached rebuild
helper at `hooks/lib/gsd-graphify-rebuild.sh` was also silently dropped
because both build-hooks.js (flat allowlist) and bin/install.js (readdir
+ isFile filter) only walked top-level files.
The published tarball gap (Gap 3 in the issue body) is not reproduced
on origin/main — `npm pack --dry-run --json` shows both source files
are present today. Only Gaps 1 and 2 are in scope.
Changes:
- Add `gsd-graphify-update.sh` to `HOOKS_TO_COPY`.
- Add `HOOKS_SUBDIRS_TO_COPY = ['lib']` and copy whitelisted hook
subdirectories (`hooks/<dir>/*` → `hooks/dist/<dir>/*`) in
build-hooks.js, with the same syntax-check + atomic-rename path the
top-level loop uses.
- `bin/install.js`: when copying `hooks/dist/`, recurse one level into
any directory entry so subdir files (e.g. `lib/gsd-graphify-rebuild.sh`)
land at the mirrored target path the hook's REBUILD_SCRIPT lookup
expects. Top-level if/else structure for files is unchanged.
Regression test `tests/bug-3579-graphify-hook-publish.test.cjs`:
- Drift guard: every top-level `hooks/*.sh` must appear in
`HOOKS_TO_COPY`. Generalizes beyond graphify so the next .sh hook
added cannot regress.
- After build: `hooks/dist/gsd-graphify-update.sh` AND
`hooks/dist/lib/gsd-graphify-rebuild.sh` exist.
- After install: both files land at the target, and no
"Missing expected hook: gsd-graphify-update.sh" warning is emitted.
Fixes#3579
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(3579): replace source-grep drift guard with filesystem-behavior assertion
The Gap-1 drift guard read scripts/build-hooks.js as text and regex-parsed the
HOOKS_TO_COPY literal, which tripped lint-no-source-grep and is brittle under
refactors. Replace with a behavior-based assertion: run the build, then for
every top-level hooks/*.sh assert hooks/dist/<name> exists. Strictly stronger
— catches both the original allowlist gap and any future regression that
silently drops a hook for any other reason.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>