feat(hooks): Add git hook infrastructure for Dolt backend

This commit implements the git hook infrastructure for Dolt storage backend
as specified in the design document.

Changes:
- Add `bd hook` command (singular) for git hooks to call directly
- Implement per-worktree export state tracking in .beads/export-state/
- Add post-checkout guard to only import if JSONL changed
- Add hook chaining configuration (chain_strategy, chain_timeout_ms)
- Support hooks in .beads/hooks/ directory with git config core.hooksPath
- Implement branch-then-merge import pattern for Dolt storage
- Update bd init to install hooks to .beads/hooks/ for Dolt backend
- Add --beads flag to `bd hooks install` command

The new `bd hook` command supports:
- pre-commit: Export database to JSONL, stage changes
- post-merge: Import JSONL to database after pull/merge
- post-checkout: Import JSONL after branch checkout (with guard)

For Dolt backend, uses branch-then-merge pattern:
1. Create jsonl-import branch
2. Import JSONL data to branch
3. Merge branch to main (cell-level conflict resolution)
4. Delete branch on success

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
quartz
2026-01-16 12:24:09 -08:00
committed by gastown/crew/dennis
parent 3a7971d2b6
commit 15d74a9a49
3 changed files with 834 additions and 12 deletions

View File

@@ -509,10 +509,27 @@ With --stealth: configures per-repository git settings for invisible beads usage
// Check if we're in a git repo and hooks aren't installed
// Install by default unless --skip-hooks is passed
// For Dolt backend, install hooks to .beads/hooks/ (uses git config core.hooksPath)
if !skipHooks && isGitRepo() && !hooksInstalled() {
if err := installGitHooks(); err != nil && !quiet {
fmt.Fprintf(os.Stderr, "\n%s Failed to install git hooks: %v\n", ui.RenderWarn("⚠"), err)
fmt.Fprintf(os.Stderr, "You can try again with: %s\n\n", ui.RenderAccent("bd doctor --fix"))
if backend == configfile.BackendDolt {
// Dolt backend: install hooks to .beads/hooks/
embeddedHooks, err := getEmbeddedHooks()
if err == nil {
if err := installHooksWithOptions(embeddedHooks, false, false, false, true); err != nil && !quiet {
fmt.Fprintf(os.Stderr, "\n%s Failed to install git hooks to .beads/hooks/: %v\n", ui.RenderWarn("⚠"), err)
fmt.Fprintf(os.Stderr, "You can try again with: %s\n\n", ui.RenderAccent("bd hooks install --beads"))
} else if !quiet {
fmt.Printf(" Hooks installed to: .beads/hooks/\n")
}
} else if !quiet {
fmt.Fprintf(os.Stderr, "\n%s Failed to load embedded hooks: %v\n", ui.RenderWarn("⚠"), err)
}
} else {
// SQLite backend: use traditional hook installation
if err := installGitHooks(); err != nil && !quiet {
fmt.Fprintf(os.Stderr, "\n%s Failed to install git hooks: %v\n", ui.RenderWarn("⚠"), err)
fmt.Fprintf(os.Stderr, "You can try again with: %s\n\n", ui.RenderAccent("bd doctor --fix"))
}
}
}