From 9880eaf7344752596c06ee2014e69e584b7456e5 Mon Sep 17 00:00:00 2001 From: wolf Date: Sun, 4 Jan 2026 10:42:02 -0800 Subject: [PATCH] feat(config): support daemon.auto_* settings in config.yaml (GH#871) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow team-wide auto-sync configuration via config.yaml instead of SQLite. This enables teams to share auto-commit/auto-push settings through version control. Changes: - Add daemon.auto_commit, daemon.auto_push, daemon.auto_pull to YamlOnlyKeys - Add daemon.* prefix to YAML-only prefixes - Update daemon startup to read from config.yaml first, then fall back to SQLite - Update bd init --team to write daemon settings to config.yaml Usage: # In .beads/config.yaml (version controlled, shared by team) daemon.auto_commit: true daemon.auto_push: true # Or via bd config set bd config set daemon.auto_commit true 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- cmd/bd/daemon.go | 19 ++++++++++++++++--- cmd/bd/init_team.go | 6 ++++-- internal/config/yaml_config.go | 7 ++++++- internal/config/yaml_config_test.go | 6 ++++++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/cmd/bd/daemon.go b/cmd/bd/daemon.go index 13ea819b..35f4bd6f 100644 --- a/cmd/bd/daemon.go +++ b/cmd/bd/daemon.go @@ -14,6 +14,7 @@ import ( "github.com/spf13/cobra" "github.com/steveyegge/beads/cmd/bd/doctor" "github.com/steveyegge/beads/internal/beads" + "github.com/steveyegge/beads/internal/config" "github.com/steveyegge/beads/internal/daemon" "github.com/steveyegge/beads/internal/rpc" "github.com/steveyegge/beads/internal/storage/sqlite" @@ -67,10 +68,15 @@ Run 'bd daemon' with no flags to see available options.`, } // If auto-commit/auto-push flags weren't explicitly provided, read from config + // GH#871: Read from config.yaml first (team-shared), then fall back to SQLite (legacy) // (skip if --stop, --status, --health, --metrics) if start && !stop && !status && !health && !metrics { if !cmd.Flags().Changed("auto-commit") { - if dbPath := beads.FindDatabasePath(); dbPath != "" { + // Check config.yaml first (GH#871: team-wide settings) + if config.GetBool("daemon.auto_commit") { + autoCommit = true + } else if dbPath := beads.FindDatabasePath(); dbPath != "" { + // Fall back to SQLite for backwards compatibility ctx := context.Background() store, err := sqlite.New(ctx, dbPath) if err == nil { @@ -82,7 +88,11 @@ Run 'bd daemon' with no flags to see available options.`, } } if !cmd.Flags().Changed("auto-push") { - if dbPath := beads.FindDatabasePath(); dbPath != "" { + // Check config.yaml first (GH#871: team-wide settings) + if config.GetBool("daemon.auto_push") { + autoPush = true + } else if dbPath := beads.FindDatabasePath(); dbPath != "" { + // Fall back to SQLite for backwards compatibility ctx := context.Background() store, err := sqlite.New(ctx, dbPath) if err == nil { @@ -97,8 +107,11 @@ Run 'bd daemon' with no flags to see available options.`, // Check environment variable first if envVal := os.Getenv("BEADS_AUTO_PULL"); envVal != "" { autoPull = envVal == "true" || envVal == "1" + } else if config.GetBool("daemon.auto_pull") { + // Check config.yaml (GH#871: team-wide settings) + autoPull = true } else if dbPath := beads.FindDatabasePath(); dbPath != "" { - // Check database config + // Fall back to SQLite for backwards compatibility ctx := context.Background() store, err := sqlite.New(ctx, dbPath) if err == nil { diff --git a/cmd/bd/init_team.go b/cmd/bd/init_team.go index 3dd8475f..d860a0f0 100644 --- a/cmd/bd/init_team.go +++ b/cmd/bd/init_team.go @@ -8,6 +8,7 @@ import ( "os/exec" "strings" + "github.com/steveyegge/beads/internal/config" "github.com/steveyegge/beads/internal/storage" "github.com/steveyegge/beads/internal/ui" ) @@ -117,11 +118,12 @@ func runTeamWizard(ctx context.Context, store storage.Storage) error { autoSync := !(response == "n" || response == "no") if autoSync { - if err := store.SetConfig(ctx, "daemon.auto_commit", "true"); err != nil { + // GH#871: Write to config.yaml for team-wide settings (version controlled) + if err := config.SetYamlConfig("daemon.auto_commit", "true"); err != nil { return fmt.Errorf("failed to enable auto-commit: %w", err) } - if err := store.SetConfig(ctx, "daemon.auto_push", "true"); err != nil { + if err := config.SetYamlConfig("daemon.auto_push", "true"); err != nil { return fmt.Errorf("failed to enable auto-push: %w", err) } diff --git a/internal/config/yaml_config.go b/internal/config/yaml_config.go index a5c3108e..cdeec260 100644 --- a/internal/config/yaml_config.go +++ b/internal/config/yaml_config.go @@ -46,6 +46,11 @@ var YamlOnlyKeys = map[string]bool{ "sync.branch": true, "sync.require_confirmation_on_mass_delete": true, + // Daemon settings (GH#871: team-wide auto-sync config) + "daemon.auto_commit": true, + "daemon.auto_push": true, + "daemon.auto_pull": true, + // Routing settings "routing.mode": true, "routing.default": true, @@ -70,7 +75,7 @@ func IsYamlOnlyKey(key string) bool { } // Check prefix matches for nested keys - prefixes := []string{"routing.", "sync.", "git.", "directory.", "repos.", "external_projects.", "validation."} + prefixes := []string{"routing.", "sync.", "git.", "directory.", "repos.", "external_projects.", "validation.", "daemon."} for _, prefix := range prefixes { if strings.HasPrefix(key, prefix) { return true diff --git a/internal/config/yaml_config_test.go b/internal/config/yaml_config_test.go index de8e7ded..347cd597 100644 --- a/internal/config/yaml_config_test.go +++ b/internal/config/yaml_config_test.go @@ -31,6 +31,12 @@ func TestIsYamlOnlyKey(t *testing.T) { {"repos.primary", true}, {"external_projects.beads", true}, + // Daemon settings (GH#871) + {"daemon.auto_commit", true}, + {"daemon.auto_push", true}, + {"daemon.auto_pull", true}, + {"daemon.custom_setting", true}, // prefix match + // SQLite keys (should return false) {"jira.url", false}, {"jira.project", false},