From 38bf896296a15388027f17e851a4101d703ac344 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Tue, 30 Dec 2025 19:16:17 -0800 Subject: [PATCH] Support non-main default branches in rig add - Add DefaultBranch() method to git package to detect repo's default branch - Store default_branch in rig config.json - Use detected branch for refinery worktree instead of hardcoding 'main' - Add LoadRigConfig() to read rig config - Display correct branch name in rig add output Fixes wyvern rig creation (uses 'master' not 'main'). --- internal/cmd/rig.go | 10 +++++++-- internal/git/git.go | 13 ++++++++++++ internal/rig/manager.go | 45 +++++++++++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/internal/cmd/rig.go b/internal/cmd/rig.go index f8b01447..7c7fd5fe 100644 --- a/internal/cmd/rig.go +++ b/internal/cmd/rig.go @@ -262,6 +262,12 @@ func runRigAdd(cmd *cobra.Command, args []string) error { elapsed := time.Since(startTime) + // Read default branch from rig config + defaultBranch := "main" + if rigCfg, err := rig.LoadRigConfig(filepath.Join(townRoot, name)); err == nil && rigCfg.DefaultBranch != "" { + defaultBranch = rigCfg.DefaultBranch + } + fmt.Printf("\n%s Rig created in %.1fs\n", style.Success.Render("✓"), elapsed.Seconds()) fmt.Printf("\nStructure:\n") fmt.Printf(" %s/\n", name) @@ -269,8 +275,8 @@ func runRigAdd(cmd *cobra.Command, args []string) error { fmt.Printf(" ├── .repo.git/ (shared bare repo for refinery+polecats)\n") fmt.Printf(" ├── .beads/ (prefix: %s)\n", newRig.Config.Prefix) fmt.Printf(" ├── plugins/ (rig-level plugins)\n") - fmt.Printf(" ├── mayor/rig/ (clone: main)\n") - fmt.Printf(" ├── refinery/rig/ (worktree: main, sees polecat branches)\n") + fmt.Printf(" ├── mayor/rig/ (clone: %s)\n", defaultBranch) + fmt.Printf(" ├── refinery/rig/ (worktree: %s, sees polecat branches)\n", defaultBranch) fmt.Printf(" ├── crew/%s/ (your workspace)\n", crewName) fmt.Printf(" ├── witness/\n") fmt.Printf(" └── polecats/\n") diff --git a/internal/git/git.go b/internal/git/git.go index 4353842d..2f270f1d 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -213,6 +213,19 @@ func (g *Git) CurrentBranch() (string, error) { return g.run("rev-parse", "--abbrev-ref", "HEAD") } +// DefaultBranch returns the default branch name (what HEAD points to). +// This works for both regular and bare repositories. +// Returns "main" as fallback if detection fails. +func (g *Git) DefaultBranch() string { + // Try symbolic-ref first (works for bare repos) + branch, err := g.run("symbolic-ref", "--short", "HEAD") + if err == nil && branch != "" { + return branch + } + // Fallback to main + return "main" +} + // HasUncommittedChanges returns true if there are uncommitted changes. func (g *Git) HasUncommittedChanges() (bool, error) { status, err := g.Status() diff --git a/internal/rig/manager.go b/internal/rig/manager.go index 5af62246..b5d88136 100644 --- a/internal/rig/manager.go +++ b/internal/rig/manager.go @@ -24,12 +24,13 @@ var ( // RigConfig represents the rig-level configuration (config.json at rig root). type RigConfig struct { - Type string `json:"type"` // "rig" - Version int `json:"version"` // schema version - Name string `json:"name"` // rig name - GitURL string `json:"git_url"` // repository URL - CreatedAt time.Time `json:"created_at"` // when rig was created - Beads *BeadsConfig `json:"beads,omitempty"` + Type string `json:"type"` // "rig" + Version int `json:"version"` // schema version + Name string `json:"name"` // rig name + GitURL string `json:"git_url"` // repository URL + DefaultBranch string `json:"default_branch,omitempty"` // main, master, etc. + CreatedAt time.Time `json:"created_at"` // when rig was created + Beads *BeadsConfig `json:"beads,omitempty"` } // BeadsConfig represents beads configuration for the rig. @@ -227,9 +228,17 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) { } bareGit := git.NewGitWithDir(bareRepoPath, "") + // Detect default branch (main, master, etc.) + defaultBranch := bareGit.DefaultBranch() + rigConfig.DefaultBranch = defaultBranch + // Re-save config with default branch + if err := m.saveRigConfig(rigPath, rigConfig); err != nil { + return nil, fmt.Errorf("updating rig config with default branch: %w", err) + } + // Create mayor as regular clone (separate from bare repo). // Mayor doesn't need to see polecat branches - that's refinery's job. - // This also allows mayor to stay on main without conflicting with refinery. + // This also allows mayor to stay on the default branch without conflicting with refinery. mayorRigPath := filepath.Join(rigPath, "mayor", "rig") if err := os.MkdirAll(filepath.Dir(mayorRigPath), 0755); err != nil { return nil, fmt.Errorf("creating mayor dir: %w", err) @@ -242,14 +251,14 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) { return nil, fmt.Errorf("creating mayor CLAUDE.md: %w", err) } - // Create refinery as worktree from bare repo on main. - // Refinery needs to see polecat branches (shared .repo.git) and merges them to main. - // Being on main allows direct merge workflow. + // Create refinery as worktree from bare repo on default branch. + // Refinery needs to see polecat branches (shared .repo.git) and merges them. + // Being on the default branch allows direct merge workflow. refineryRigPath := filepath.Join(rigPath, "refinery", "rig") if err := os.MkdirAll(filepath.Dir(refineryRigPath), 0755); err != nil { return nil, fmt.Errorf("creating refinery dir: %w", err) } - if err := bareGit.WorktreeAddExisting(refineryRigPath, "main"); err != nil { + if err := bareGit.WorktreeAddExisting(refineryRigPath, defaultBranch); err != nil { return nil, fmt.Errorf("creating refinery worktree: %w", err) } // Create refinery CLAUDE.md (overrides any from cloned repo) @@ -344,6 +353,20 @@ func (m *Manager) saveRigConfig(rigPath string, cfg *RigConfig) error { return os.WriteFile(configPath, data, 0644) } +// LoadRigConfig reads the rig configuration from config.json. +func LoadRigConfig(rigPath string) (*RigConfig, error) { + configPath := filepath.Join(rigPath, "config.json") + data, err := os.ReadFile(configPath) + if err != nil { + return nil, err + } + var cfg RigConfig + if err := json.Unmarshal(data, &cfg); err != nil { + return nil, err + } + return &cfg, nil +} + // initAgentStates creates initial state.json files for agents. func (m *Manager) initAgentStates(rigPath string) error { agents := []struct {