feat: add doctor check for issues.jsonl git tracking (GH#796)

Safeguard for users with global gitignore patterns like *.jsonl that
could cause issues.jsonl to be ignored, breaking bd sync.

The check runs git check-ignore and warns if issues.jsonl would be
ignored by any gitignore rule (global, parent directory, etc).
This commit is contained in:
Steve Yegge
2025-12-29 20:58:17 -08:00
parent 61edfafe4c
commit 2b90f51d0c
2 changed files with 51 additions and 0 deletions

View File

@@ -401,6 +401,11 @@ func runDiagnostics(path string) doctorResult {
result.Checks = append(result.Checks, gitignoreCheck)
// Don't fail overall check for gitignore, just warn
// Check 14a: issues.jsonl tracking (catches global gitignore conflicts)
issuesTrackingCheck := convertWithCategory(doctor.CheckIssuesTracking(), doctor.CategoryGit)
result.Checks = append(result.Checks, issuesTrackingCheck)
// Don't fail overall check for tracking issues, just warn
// Check 15: Git merge driver configuration
mergeDriverCheck := convertWithCategory(doctor.CheckMergeDriver(path), doctor.CategoryGit)
result.Checks = append(result.Checks, mergeDriverCheck)

View File

@@ -2,6 +2,7 @@ package doctor
import (
"os"
"os/exec"
"path/filepath"
"strings"
)
@@ -120,3 +121,48 @@ func FixGitignore() error {
return nil
}
// CheckIssuesTracking verifies that issues.jsonl is tracked by git.
// This catches cases where global gitignore patterns (e.g., *.jsonl) would
// cause issues.jsonl to be ignored, breaking bd sync.
func CheckIssuesTracking() DoctorCheck {
issuesPath := filepath.Join(".beads", "issues.jsonl")
// First check if the file exists
if _, err := os.Stat(issuesPath); os.IsNotExist(err) {
// File doesn't exist yet - not an error, bd init may not have been run
return DoctorCheck{
Name: "Issues Tracking",
Status: "ok",
Message: "No issues.jsonl yet (will be created on first issue)",
}
}
// Check if git considers this file ignored
// git check-ignore exits 0 if ignored, 1 if not ignored, 128 if error
cmd := exec.Command("git", "check-ignore", "-q", issuesPath)
err := cmd.Run()
if err == nil {
// Exit code 0 means the file IS ignored - this is bad
// Get details about what's ignoring it
detailCmd := exec.Command("git", "check-ignore", "-v", issuesPath)
output, _ := detailCmd.Output()
detail := strings.TrimSpace(string(output))
return DoctorCheck{
Name: "Issues Tracking",
Status: "warning",
Message: "issues.jsonl is ignored by git (bd sync will fail)",
Detail: detail,
Fix: "Check global gitignore: git config --global core.excludesfile",
}
}
// Exit code 1 means not ignored (good), any other error we ignore
return DoctorCheck{
Name: "Issues Tracking",
Status: "ok",
Message: "issues.jsonl is tracked by git",
}
}