Implement BEADS_DIR environment variable (bd-e16b)

Add BEADS_DIR as a replacement for BEADS_DB to point to the .beads
directory instead of the database file directly.

Rationale:
- With --no-db mode, there's no .db file to point to
- The .beads directory is the logical unit (contains config.yaml, db
  files, jsonl files)
- More intuitive: point to the beads directory not the database file

Implementation:
- Add BEADS_DIR environment variable support to FindDatabasePath()
- Priority order: BEADS_DIR > BEADS_DB > auto-discovery
- Maintain backward compatibility with BEADS_DB (now deprecated)
- Update --no-db mode to respect BEADS_DIR
- Update MCP integration (config.py, bd_client.py)
- Update documentation to show BEADS_DIR as preferred method

Testing:
- Backward compatibility: BEADS_DB still works
- BEADS_DIR works with regular database mode
- BEADS_DIR works with --no-db mode
- Priority: BEADS_DIR takes precedence over BEADS_DB

Follow-up issues for refactoring:
- bd-efe8: Refactor path canonicalization into helper function
- bd-c362: Extract database search logic into helper function

Closes bd-e16b

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-11-02 18:34:34 -08:00
parent c5b2fbbc9d
commit 6ecfd04ec8
8 changed files with 187 additions and 54 deletions

View File

@@ -118,12 +118,61 @@ func NewSQLiteStorage(dbPath string) (Storage, error) {
}
// FindDatabasePath discovers the bd database path using bd's standard search order:
// 1. $BEADS_DB environment variable
// 2. .beads/*.db in current directory or ancestors
// 1. $BEADS_DIR environment variable (points to .beads directory)
// 2. $BEADS_DB environment variable (points directly to database file, deprecated)
// 3. .beads/*.db in current directory or ancestors
//
// Returns empty string if no database is found.
func FindDatabasePath() string {
// 1. Check environment variable
// 1. Check BEADS_DIR environment variable (preferred)
if beadsDir := os.Getenv("BEADS_DIR"); beadsDir != "" {
// Canonicalize the path to prevent nested .beads directories
var absBeadsDir string
if absPath, err := filepath.Abs(beadsDir); err == nil {
if canonical, err := filepath.EvalSymlinks(absPath); err == nil {
absBeadsDir = canonical
} else {
absBeadsDir = absPath
}
} else {
absBeadsDir = beadsDir
}
// Check for config.json first (single source of truth)
if cfg, err := configfile.Load(absBeadsDir); err == nil && cfg != nil {
dbPath := cfg.DatabasePath(absBeadsDir)
if _, err := os.Stat(dbPath); err == nil {
return dbPath
}
}
// Fall back to canonical beads.db for backward compatibility
canonicalDB := filepath.Join(absBeadsDir, CanonicalDatabaseName)
if _, err := os.Stat(canonicalDB); err == nil {
return canonicalDB
}
// Look for any .db file in the beads directory
matches, err := filepath.Glob(filepath.Join(absBeadsDir, "*.db"))
if err == nil && len(matches) > 0 {
// Filter out backup files and vc.db
var validDBs []string
for _, match := range matches {
baseName := filepath.Base(match)
if !strings.Contains(baseName, ".backup") && baseName != "vc.db" {
validDBs = append(validDBs, match)
}
}
if len(validDBs) > 0 {
return validDBs[0]
}
}
// BEADS_DIR is set but no database found - this is OK for --no-db mode
// Return empty string and let the caller handle it
}
// 2. Check BEADS_DB environment variable (deprecated but still supported)
if envDB := os.Getenv("BEADS_DB"); envDB != "" {
// Canonicalize the path to prevent nested .beads directories
if absDB, err := filepath.Abs(envDB); err == nil {
@@ -135,7 +184,7 @@ func FindDatabasePath() string {
return envDB // Fallback to original if Abs fails
}
// 2. Search for .beads/*.db in current directory and ancestors
// 3. Search for .beads/*.db in current directory and ancestors
if foundDB := findDatabaseInTree(); foundDB != "" {
// Canonicalize found path
if absDB, err := filepath.Abs(foundDB); err == nil {