diff --git a/internal/rig/manager.go b/internal/rig/manager.go index 28536219..0bfcebc5 100644 --- a/internal/rig/manager.go +++ b/internal/rig/manager.go @@ -418,7 +418,19 @@ func (m *Manager) initBeads(rigPath, prefix string) error { // Run bd init if available, with --no-agents to skip AGENTS.md creation cmd := exec.Command("bd", "init", "--prefix", prefix, "--no-agents") cmd.Dir = rigPath - if err := cmd.Run(); err != nil { + output, err := cmd.CombinedOutput() + if err != nil { + lower := strings.ToLower(string(output)) + if strings.Contains(lower, "no-agents") && + (strings.Contains(lower, "unknown flag") || + strings.Contains(lower, "unknown option") || + strings.Contains(lower, "flag provided but not defined")) { + retry := exec.Command("bd", "init", "--prefix", prefix) + retry.Dir = rigPath + if retryErr := retry.Run(); retryErr == nil { + return nil + } + } // bd might not be installed or --no-agents not supported, create minimal structure // Note: beads currently expects YAML format for config configPath := filepath.Join(beadsDir, "config.yaml") @@ -438,6 +450,18 @@ func (m *Manager) initBeads(rigPath, prefix string) error { func (m *Manager) initAgentBeads(rigPath, rigName, prefix string, isFirstRig bool) error { // Run bd commands from mayor/rig which has the beads database mayorRigPath := filepath.Join(rigPath, "mayor", "rig") + beadsDir := filepath.Join(rigPath, ".beads") + prevBeadsDir, hadBeadsDir := os.LookupEnv("BEADS_DIR") + if err := os.Setenv("BEADS_DIR", beadsDir); err != nil { + return fmt.Errorf("setting BEADS_DIR: %w", err) + } + defer func() { + if hadBeadsDir { + _ = os.Setenv("BEADS_DIR", prevBeadsDir) + } else { + _ = os.Unsetenv("BEADS_DIR") + } + }() bd := beads.New(mayorRigPath) // Define agents to create diff --git a/internal/rig/manager_test.go b/internal/rig/manager_test.go index eaf3908f..31fdbdac 100644 --- a/internal/rig/manager_test.go +++ b/internal/rig/manager_test.go @@ -21,6 +21,16 @@ func setupTestTown(t *testing.T) (string, *config.RigsConfig) { return root, rigsConfig } +func writeFakeBD(t *testing.T, script string) string { + t.Helper() + binDir := t.TempDir() + scriptPath := filepath.Join(binDir, "bd") + if err := os.WriteFile(scriptPath, []byte(script), 0755); err != nil { + t.Fatalf("write fake bd: %v", err) + } + return binDir +} + func createTestRig(t *testing.T, root, name string) { t.Helper() @@ -251,3 +261,105 @@ func TestEnsureGitignoreEntry_AppendsToExisting(t *testing.T) { t.Errorf("content = %q, want %q", string(content), expected) } } + +func TestInitBeadsRetriesWithoutNoAgentsFlag(t *testing.T) { + rigPath := t.TempDir() + beadsDir := filepath.Join(rigPath, ".beads") + + script := `#!/usr/bin/env bash +set -e +cmd="$1" +shift +if [[ "$cmd" == "init" ]]; then + for arg in "$@"; do + if [[ "$arg" == "--no-agents" ]]; then + echo "unknown flag: --no-agents" >&2 + exit 1 + fi + done + touch "$EXPECT_BEADS_DIR/created.db" + exit 0 +fi +echo "unexpected command: $cmd" >&2 +exit 1 +` + + binDir := writeFakeBD(t, script) + t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH")) + t.Setenv("EXPECT_BEADS_DIR", beadsDir) + + manager := &Manager{} + if err := manager.initBeads(rigPath, "gt"); err != nil { + t.Fatalf("initBeads: %v", err) + } + + if _, err := os.Stat(filepath.Join(beadsDir, "created.db")); err != nil { + t.Fatalf("expected bd init to create db file: %v", err) + } +} + +func TestInitAgentBeadsUsesRigBeadsDir(t *testing.T) { + rigPath := t.TempDir() + beadsDir := filepath.Join(rigPath, ".beads") + mayorRigPath := filepath.Join(rigPath, "mayor", "rig") + + if err := os.MkdirAll(beadsDir, 0755); err != nil { + t.Fatalf("mkdir beads dir: %v", err) + } + if err := os.MkdirAll(mayorRigPath, 0755); err != nil { + t.Fatalf("mkdir mayor rig: %v", err) + } + + script := `#!/usr/bin/env bash +set -e +if [[ "$1" == "--no-daemon" ]]; then + shift +fi +cmd="$1" +shift +case "$cmd" in + show) + if [[ "$BEADS_DIR" != "$EXPECT_BEADS_DIR" ]]; then + echo "BEADS_DIR mismatch" >&2 + exit 1 + fi + echo "[]" + ;; + create) + if [[ "$BEADS_DIR" != "$EXPECT_BEADS_DIR" ]]; then + echo "BEADS_DIR mismatch" >&2 + exit 1 + fi + id="" + title="" + for arg in "$@"; do + case "$arg" in + --id=*) id="${arg#--id=}" ;; + --title=*) title="${arg#--title=}" ;; + esac + done + printf '{"id":"%s","title":"%s","description":"","issue_type":"agent"}' "$id" "$title" + ;; + slot) + if [[ "$BEADS_DIR" != "$EXPECT_BEADS_DIR" ]]; then + echo "BEADS_DIR mismatch" >&2 + exit 1 + fi + ;; + *) + echo "unexpected command: $cmd" >&2 + exit 1 + ;; +esac +` + + binDir := writeFakeBD(t, script) + t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH")) + t.Setenv("EXPECT_BEADS_DIR", beadsDir) + t.Setenv("BEADS_DIR", "") + + manager := &Manager{} + if err := manager.initAgentBeads(rigPath, "demo", "gt", false); err != nil { + t.Fatalf("initAgentBeads: %v", err) + } +}