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

@@ -177,7 +177,8 @@ var rootCmd = &cobra.Command{
// No database found - error out instead of falling back to ~/.beads
fmt.Fprintf(os.Stderr, "Error: no beads database found\n")
fmt.Fprintf(os.Stderr, "Hint: run 'bd init' to create a database in the current directory\n")
fmt.Fprintf(os.Stderr, " or set BEADS_DB environment variable to specify a database\n")
fmt.Fprintf(os.Stderr, " or set BEADS_DIR to point to your .beads directory\n")
fmt.Fprintf(os.Stderr, " or set BEADS_DB to point to your database file (deprecated)\n")
os.Exit(1)
}
}
@@ -438,13 +439,29 @@ var rootCmd = &cobra.Command{
// Handle --no-db mode: write memory storage back to JSONL
if noDb {
if store != nil {
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get current directory: %v\n", err)
os.Exit(1)
// Determine beads directory (respect BEADS_DIR)
var beadsDir string
if envDir := os.Getenv("BEADS_DIR"); envDir != "" {
// Canonicalize the path
if absDir, err := filepath.Abs(envDir); err == nil {
if canonical, err := filepath.EvalSymlinks(absDir); err == nil {
beadsDir = canonical
} else {
beadsDir = absDir
}
} else {
beadsDir = envDir
}
} else {
// Fall back to current directory
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get current directory: %v\n", err)
os.Exit(1)
}
beadsDir = filepath.Join(cwd, ".beads")
}
beadsDir := filepath.Join(cwd, ".beads")
if memStore, ok := store.(*memory.MemoryStorage); ok {
if err := writeIssuesToJSONL(memStore, beadsDir); err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to write JSONL: %v\n", err)

View File

@@ -18,14 +18,31 @@ import (
// This is called when --no-db flag is set
func initializeNoDbMode() error {
// Find .beads directory
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current directory: %w", err)
var beadsDir string
// Check BEADS_DIR environment variable first
if envDir := os.Getenv("BEADS_DIR"); envDir != "" {
// Canonicalize the path
if absDir, err := filepath.Abs(envDir); err == nil {
if canonical, err := filepath.EvalSymlinks(absDir); err == nil {
beadsDir = canonical
} else {
beadsDir = absDir
}
} else {
beadsDir = envDir
}
} else {
// Fall back to current directory
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current directory: %w", err)
}
beadsDir = filepath.Join(cwd, ".beads")
}
beadsDir := filepath.Join(cwd, ".beads")
if _, err := os.Stat(beadsDir); os.IsNotExist(err) {
return fmt.Errorf("no .beads directory found (hint: run 'bd init' first)")
return fmt.Errorf("no .beads directory found (hint: run 'bd init' first or set BEADS_DIR)")
}
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")