From 4b2d1791ee1fb7c24caab9325ffcf57aca0fcf12 Mon Sep 17 00:00:00 2001 From: beads/crew/elinor Date: Tue, 20 Jan 2026 19:15:04 -0800 Subject: [PATCH] refactor(hooks): consolidate duplicate JSONL file lists into shared constant - Add jsonlFilePaths constant to eliminate duplicate file list definitions - Update all usages in hook.go and hooks.go to use the shared constant - Include beads.jsonl for backwards compatibility with older installations Co-Authored-By: Claude Opus 4.5 --- cmd/bd/hook.go | 19 +++++++++++++------ cmd/bd/hooks.go | 14 ++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/cmd/bd/hook.go b/cmd/bd/hook.go index a0e246d8..92dadc5d 100644 --- a/cmd/bd/hook.go +++ b/cmd/bd/hook.go @@ -20,6 +20,15 @@ import ( "github.com/steveyegge/beads/internal/storage/factory" ) +// jsonlFilePaths lists all JSONL files that should be staged/tracked. +// Includes beads.jsonl for backwards compatibility with older installations. +var jsonlFilePaths = []string{ + ".beads/issues.jsonl", + ".beads/deletions.jsonl", + ".beads/interactions.jsonl", + ".beads/beads.jsonl", // Legacy filename, kept for backwards compatibility +} + // hookCmd is the main "bd hook" command that git hooks call into. // This is distinct from "bd hooks" (plural) which manages hook installation. var hookCmd = &cobra.Command{ @@ -368,17 +377,16 @@ func hookPreCommit() int { } // Stage JSONL files - jsonlFiles := []string{".beads/beads.jsonl", ".beads/issues.jsonl", ".beads/deletions.jsonl", ".beads/interactions.jsonl"} if os.Getenv("BEADS_NO_AUTO_STAGE") == "" { rc, rcErr := beads.GetRepoContext() ctx := context.Background() - for _, f := range jsonlFiles { + for _, f := range jsonlFilePaths { if _, err := os.Stat(f); err == nil { var gitAdd *exec.Cmd if rcErr == nil { gitAdd = rc.GitCmdCWD(ctx, "add", f) } else { - // #nosec G204 -- f comes from jsonlFiles (controlled, hardcoded paths) + // #nosec G204 -- f comes from jsonlFilePaths (controlled, hardcoded paths) gitAdd = exec.Command("git", "add", f) } _ = gitAdd.Run() @@ -532,14 +540,13 @@ func stageJSONLFiles(ctx context.Context) { } rc, rcErr := beads.GetRepoContext() - jsonlFiles := []string{".beads/issues.jsonl", ".beads/deletions.jsonl", ".beads/interactions.jsonl"} - for _, f := range jsonlFiles { + for _, f := range jsonlFilePaths { if _, err := os.Stat(f); err == nil { var gitAdd *exec.Cmd if rcErr == nil { gitAdd = rc.GitCmdCWD(ctx, "add", f) } else { - // #nosec G204 -- f comes from jsonlFiles (hardcoded) + // #nosec G204 -- f comes from jsonlFilePaths (controlled, hardcoded paths) gitAdd = exec.Command("git", "add", f) } _ = gitAdd.Run() diff --git a/cmd/bd/hooks.go b/cmd/bd/hooks.go index 5cf54ff2..03e22412 100644 --- a/cmd/bd/hooks.go +++ b/cmd/bd/hooks.go @@ -575,12 +575,10 @@ func runPreCommitHook() int { // By default, we auto-stage for convenience. Users with conflicting git hooks // (e.g., hooks that read the staging area) can set BEADS_NO_AUTO_STAGE=1 to // disable this and stage manually. See: https://github.com/steveyegge/beads/issues/826 - jsonlFiles := []string{".beads/beads.jsonl", ".beads/issues.jsonl", ".beads/deletions.jsonl", ".beads/interactions.jsonl"} - if os.Getenv("BEADS_NO_AUTO_STAGE") != "" { // Safe mode: check for unstaged changes and block if found var unstaged []string - for _, f := range jsonlFiles { + for _, f := range jsonlFilePaths { if _, err := os.Stat(f); err == nil { if hasUnstagedChanges(f) { unstaged = append(unstaged, f) @@ -601,14 +599,14 @@ func runPreCommitHook() int { // Default: auto-stage JSONL files rc, rcErr := beads.GetRepoContext() ctx := context.Background() - for _, f := range jsonlFiles { + for _, f := range jsonlFilePaths { if _, err := os.Stat(f); err == nil { var gitAdd *exec.Cmd if rcErr == nil { gitAdd = rc.GitCmdCWD(ctx, "add", f) } else { // Fallback if RepoContext unavailable - // #nosec G204 -- f comes from jsonlFiles (controlled, hardcoded paths) + // #nosec G204 -- f comes from jsonlFilePaths (controlled, hardcoded paths) gitAdd = exec.Command("git", "add", f) } _ = gitAdd.Run() // Ignore errors - file may not exist @@ -697,7 +695,7 @@ func runPrePushHook(args []string) int { // Check for uncommitted JSONL changes files := []string{} - for _, f := range []string{".beads/beads.jsonl", ".beads/issues.jsonl", ".beads/deletions.jsonl", ".beads/interactions.jsonl"} { + for _, f := range jsonlFilePaths { // Check if file exists or is tracked if _, err := os.Stat(f); err == nil { files = append(files, f) @@ -707,7 +705,7 @@ func runPrePushHook(args []string) int { if rcErr == nil { checkCmd = rc.GitCmdCWD(ctx, "ls-files", "--error-unmatch", f) } else { - // #nosec G204 - f is from hardcoded list above, not user input + // #nosec G204 - f is from jsonlFilePaths (controlled, hardcoded paths) checkCmd = exec.Command("git", "ls-files", "--error-unmatch", f) } if checkCmd.Run() == nil { @@ -1127,7 +1125,7 @@ func isRebaseInProgress() bool { // hasBeadsJSONL checks if any JSONL file exists in .beads/. func hasBeadsJSONL() bool { - for _, f := range []string{".beads/beads.jsonl", ".beads/issues.jsonl", ".beads/deletions.jsonl", ".beads/interactions.jsonl"} { + for _, f := range jsonlFilePaths { if _, err := os.Stat(f); err == nil { return true }