fix(hooks): prevent bd hooks install --chain from destroying user's original hook

When bd init installs hooks with chaining, it renames the user's original
hook to .old and creates a new bd hook that chains to it. If bd doctor
later runs bd hooks install --chain, it would:

1. Not recognize the inline bd hook (from bd init) as a bd hook
2. Rename it to .old, overwriting the user's original hook
3. Install a new shim hook

This fix addresses two root causes:

1. Detection mismatch: areBdShimsInstalled() and getHookVersion() now
   recognize inline bd hooks (which have "# bd (beads)" marker) in
   addition to shim hooks (which have "# bd-shim" marker)

2. No .old protection: bd hooks install --chain now checks if .old
   already exists before renaming, preserving the user's original hook

Fixes #1120

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steven Syrek
2026-01-17 08:44:30 +01:00
parent aee86dfae2
commit a5140075f1
3 changed files with 278 additions and 23 deletions

View File

@@ -24,6 +24,10 @@ const (
// bdShimMarker identifies bd shim hooks (GH#946)
const bdShimMarker = "# bd-shim"
// bdInlineHookMarker identifies inline hooks created by bd init (GH#1120)
// These hooks have the logic embedded directly rather than calling bd hooks run
const bdInlineHookMarker = "# bd (beads)"
// bdHooksRunPattern matches hooks that call bd hooks run
var bdHooksRunPattern = regexp.MustCompile(`\bbd\s+hooks\s+run\b`)
@@ -150,10 +154,11 @@ func CheckGitHooks() DoctorCheck {
}
}
// areBdShimsInstalled checks if the installed hooks are bd shims or call bd hooks run.
// areBdShimsInstalled checks if the installed hooks are bd shims, call bd hooks run,
// or are inline bd hooks created by bd init.
// This helps detect when bd hooks are installed directly but an external manager config exists.
// Returns (true, installedHooks) if bd shims are detected, (false, nil) otherwise.
// (GH#946)
// Returns (true, installedHooks) if bd hooks are detected, (false, nil) otherwise.
// (GH#946, GH#1120)
func areBdShimsInstalled(hooksDir string) (bool, []string) {
hooks := []string{"pre-commit", "post-merge", "pre-push"}
var bdHooks []string
@@ -165,8 +170,10 @@ func areBdShimsInstalled(hooksDir string) (bool, []string) {
continue
}
contentStr := string(content)
// Check for bd-shim marker or bd hooks run call
if strings.Contains(contentStr, bdShimMarker) || bdHooksRunPattern.MatchString(contentStr) {
// Check for bd-shim marker, bd hooks run call, or inline bd hook marker (from bd init)
if strings.Contains(contentStr, bdShimMarker) ||
strings.Contains(contentStr, bdInlineHookMarker) ||
bdHooksRunPattern.MatchString(contentStr) {
bdHooks = append(bdHooks, hookName)
}
}