Skip to content

fix(hooks): preserve husky v8/v9 hook layout when copying to .beads/hooks/#3197

Open
sjsyrek wants to merge 1 commit intogastownhall:mainfrom
sjsyrek:fix/husky-hook-layout-preservation
Open

fix(hooks): preserve husky v8/v9 hook layout when copying to .beads/hooks/#3197
sjsyrek wants to merge 1 commit intogastownhall:mainfrom
sjsyrek:fix/husky-hook-layout-preservation

Conversation

@sjsyrek
Copy link
Copy Markdown
Contributor

@sjsyrek sjsyrek commented Apr 11, 2026

Summary

Fixes #3132. When bd init copies hooks from a husky-managed directory into .beads/hooks/, two bugs break the preserved hooks — one loudly (every commit fails), one silently (user's lint/format hooks never run).

Changes Made

  • Added fixHuskyHookLayout() in cmd/bd/hooks.go — called at the end of preservePreexistingHooks() to handle husky-specific directory layout issues
  • Bug 1 (v8): Creates a relative symlink from .beads/hooks/_/ to the original .husky/_/ helper directory. This makes $(dirname "$0")/_/husky.sh resolve correctly from the new location
  • Bug 2 (v9): Detects husky v9 shims (files that source the h dispatcher) and replaces them with the actual user hook content from .husky/. Adds a shebang if the user hook lacks one. Removes the now-unnecessary h dispatcher from .beads/hooks/
  • Added 3 tests: v8 helper symlink, v9 shim replacement, and no-op for non-husky directories

Backward Compatibility

No breaking changes: fixHuskyHookLayout is a no-op for non-husky hook directories (early returns when _/ dir and h dispatcher are absent)
Existing hooks preserved: All existing TestInstallHooksBeads_Preserves* tests pass unchanged
Beads section injection: Works correctly with both fix paths — the user hook content is written first, then installHooksWithOptions injects the beads section via markers

Technical Details

Bug 1 fix: preservePreexistingHooks skips directories (entry.IsDir() on line 691). Rather than changing that general behavior, fixHuskyHookLayout runs after the copy loop and creates a relative symlink specifically for the husky _/ helper directory.

Bug 2 fix: For husky v9, core.hooksPath=.husky/_/. The files there are shims, not actual user hooks. The h dispatcher resolves up via dirname(dirname($0)) which breaks when relocated. Instead of rewriting the dispatcher's path math, we replace the shims entirely with the user's actual hook commands from .husky/ (one directory up), which is simpler and more robust.

Test plan

  • go test ./cmd/bd/ -run "TestInstallHooksBeads_Husky|TestFixHusky" — 3 new tests pass
  • go test ./cmd/bd/ -run "TestInstallHooksBeads_Preserves" — existing tests pass
  • go vet ./cmd/bd/... — clean
  • go build ./cmd/bd/... — compiles
  • Manual: bd init in a repo with husky v8 → verify _/ symlink and hooks work
  • Manual: bd init in a repo with husky v9 → verify user commands preserved, h removed

Size: Medium ✓

Single function addition (~75 lines) + 3 integration tests (~200 lines). Touches only preservePreexistingHooks flow, no architectural changes.

🤖 Generated with Claude Code

…ooks/ (GH#3132)

When beads sets core.hooksPath=.beads/hooks/ and copies hooks from a
husky-managed directory, two bugs break the preserved hooks:

Bug 1 (v8): Husky v8 hooks source $(dirname "$0")/_/husky.sh, but the
_/ helper directory was skipped by preservePreexistingHooks (which skips
directories). Fix: create a relative symlink from .beads/hooks/_/ to the
original _/ directory.

Bug 2 (v9): Husky v9 sets core.hooksPath=.husky/_/ where each hook is a
shim that sources an "h" dispatcher. The dispatcher uses
dirname(dirname($0)) to resolve user hooks in the parent .husky/
directory — this path math breaks when the shim is relocated. Fix: detect
v9 shims and replace them with the actual user hook content from .husky/,
then remove the now-unnecessary h dispatcher.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bd init --chain copies husky hooks but mishandles helper layout, silently breaking commits

1 participant