Add BEADS_NO_DAEMON escape hatch and improve daemon detection
Changes to cmd/bd/main.go: - Add BEADS_NO_DAEMON env var to explicitly disable daemon (single-user mode) - Keep BEADS_AUTO_START_DAEMON for backward compatibility - Lower global daemon threshold from 4+ repos to 2+ repos - Document always-daemon mode as default behavior Also create bd-161 epic for label enhancements with child tasks. Amp-Thread-ID: https://ampcode.com/threads/T-675a2db5-b1b3-480d-a108-b003d8139d08 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -359,13 +359,19 @@ func emitVerboseWarning() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func shouldAutoStartDaemon() bool {
|
func shouldAutoStartDaemon() bool {
|
||||||
// Check environment variable (default: true)
|
// Check BEADS_NO_DAEMON first (escape hatch for single-user workflows)
|
||||||
|
noDaemon := strings.ToLower(strings.TrimSpace(os.Getenv("BEADS_NO_DAEMON")))
|
||||||
|
if noDaemon == "1" || noDaemon == "true" || noDaemon == "yes" || noDaemon == "on" {
|
||||||
|
return false // Explicit opt-out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check legacy BEADS_AUTO_START_DAEMON for backward compatibility
|
||||||
autoStart := strings.ToLower(strings.TrimSpace(os.Getenv("BEADS_AUTO_START_DAEMON")))
|
autoStart := strings.ToLower(strings.TrimSpace(os.Getenv("BEADS_AUTO_START_DAEMON")))
|
||||||
if autoStart != "" {
|
if autoStart != "" {
|
||||||
// Accept common falsy values
|
// Accept common falsy values
|
||||||
return autoStart != "false" && autoStart != "0" && autoStart != "no" && autoStart != "off"
|
return autoStart != "false" && autoStart != "0" && autoStart != "no" && autoStart != "off"
|
||||||
}
|
}
|
||||||
return true // Default to enabled
|
return true // Default to enabled (always-daemon mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldUseGlobalDaemon determines if global daemon should be preferred
|
// shouldUseGlobalDaemon determines if global daemon should be preferred
|
||||||
@@ -375,59 +381,59 @@ func shouldUseGlobalDaemon() bool {
|
|||||||
if pref := os.Getenv("BEADS_PREFER_GLOBAL_DAEMON"); pref != "" {
|
if pref := os.Getenv("BEADS_PREFER_GLOBAL_DAEMON"); pref != "" {
|
||||||
return pref == "1" || strings.ToLower(pref) == "true"
|
return pref == "1" || strings.ToLower(pref) == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Heuristic: detect multiple beads repositories
|
// Heuristic: detect multiple beads repositories
|
||||||
home, err := os.UserHomeDir()
|
home, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count .beads directories under home
|
// Count .beads directories under home
|
||||||
repoCount := 0
|
repoCount := 0
|
||||||
maxDepth := 5 // Don't scan too deep
|
maxDepth := 5 // Don't scan too deep
|
||||||
|
|
||||||
var countRepos func(string, int) error
|
var countRepos func(string, int) error
|
||||||
countRepos = func(dir string, depth int) error {
|
countRepos = func(dir string, depth int) error {
|
||||||
if depth > maxDepth || repoCount > 3 {
|
if depth > maxDepth || repoCount > 1 {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := os.ReadDir(dir)
|
entries, err := os.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil // Skip directories we can't read
|
return nil // Skip directories we can't read
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if !entry.IsDir() {
|
if !entry.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
name := entry.Name()
|
name := entry.Name()
|
||||||
|
|
||||||
// Skip hidden dirs except .beads
|
// Skip hidden dirs except .beads
|
||||||
if strings.HasPrefix(name, ".") && name != ".beads" {
|
if strings.HasPrefix(name, ".") && name != ".beads" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip common large directories
|
// Skip common large directories
|
||||||
if name == "node_modules" || name == "vendor" || name == "target" || name == ".git" {
|
if name == "node_modules" || name == "vendor" || name == "target" || name == ".git" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(dir, name)
|
path := filepath.Join(dir, name)
|
||||||
|
|
||||||
// Check if this is a .beads directory with a database
|
// Check if this is a .beads directory with a database
|
||||||
if name == ".beads" {
|
if name == ".beads" {
|
||||||
dbPath := filepath.Join(path, "db.sqlite")
|
dbPath := filepath.Join(path, "db.sqlite")
|
||||||
if _, err := os.Stat(dbPath); err == nil {
|
if _, err := os.Stat(dbPath); err == nil {
|
||||||
repoCount++
|
repoCount++
|
||||||
if repoCount > 3 {
|
if repoCount > 1 {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recurse into subdirectories
|
// Recurse into subdirectories
|
||||||
if depth < maxDepth {
|
if depth < maxDepth {
|
||||||
countRepos(path, depth+1)
|
countRepos(path, depth+1)
|
||||||
@@ -435,7 +441,7 @@ func shouldUseGlobalDaemon() bool {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan common project directories
|
// Scan common project directories
|
||||||
projectDirs := []string{
|
projectDirs := []string{
|
||||||
filepath.Join(home, "src"),
|
filepath.Join(home, "src"),
|
||||||
@@ -444,22 +450,23 @@ func shouldUseGlobalDaemon() bool {
|
|||||||
filepath.Join(home, "workspace"),
|
filepath.Join(home, "workspace"),
|
||||||
filepath.Join(home, "dev"),
|
filepath.Join(home, "dev"),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, dir := range projectDirs {
|
for _, dir := range projectDirs {
|
||||||
if _, err := os.Stat(dir); err == nil {
|
if _, err := os.Stat(dir); err == nil {
|
||||||
countRepos(dir, 0)
|
countRepos(dir, 0)
|
||||||
if repoCount > 3 {
|
if repoCount > 1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
if os.Getenv("BD_DEBUG") != "" {
|
||||||
fmt.Fprintf(os.Stderr, "Debug: found %d beads repositories, prefer global: %v\n", repoCount, repoCount > 3)
|
fmt.Fprintf(os.Stderr, "Debug: found %d beads repositories, prefer global: %v\n", repoCount, repoCount > 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use global daemon if we found more than 3 repositories
|
// Use global daemon if we found more than 1 repository (multi-repo workflow)
|
||||||
return repoCount > 3
|
// This prevents concurrency issues when multiple repos are being worked on
|
||||||
|
return repoCount > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryAutoStartDaemon attempts to start the daemon in the background
|
// tryAutoStartDaemon attempts to start the daemon in the background
|
||||||
|
|||||||
Reference in New Issue
Block a user