fix(rig): improve UX for rig creation (#7)
- Add progress feedback during slow clone operations (30+ seconds) - Fix README Quick Start to match actual workflow (--git flag, crew add) - Update install output to use 'gt mayor attach' consistently - Clarify "Next steps" wording in rig add output 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
22
README.md
22
README.md
@@ -25,17 +25,27 @@ Multi-agent orchestrator for Claude Code. Track work with convoys; sling to agen
|
|||||||
# Install
|
# Install
|
||||||
go install github.com/steveyegge/gastown/cmd/gt@latest
|
go install github.com/steveyegge/gastown/cmd/gt@latest
|
||||||
|
|
||||||
# Create workspace
|
# Create workspace (--git auto-initializes git repository)
|
||||||
gt install ~/gt
|
gt install ~/gt --git
|
||||||
|
cd ~/gt
|
||||||
|
|
||||||
# Add a project
|
# Add a project
|
||||||
gt rig add myproject https://github.com/you/repo.git
|
gt rig add myproject https://github.com/you/repo.git
|
||||||
|
|
||||||
# Enter the Mayor's office (recommended)
|
# Create your personal workspace
|
||||||
gt mayor attach
|
gt crew add <yourname> --rig myproject
|
||||||
|
|
||||||
|
# Start working
|
||||||
|
cd myproject/crew/<yourname>
|
||||||
```
|
```
|
||||||
|
|
||||||
Once inside the Mayor session, you're talking to Claude with full town context. Just tell it what you want:
|
For advanced multi-agent coordination, use the Mayor session:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gt mayor attach # Enter the Mayor's office
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside the Mayor session, you're talking to Claude with full town context:
|
||||||
|
|
||||||
> "Help me fix the authentication bug in myproject"
|
> "Help me fix the authentication bug in myproject"
|
||||||
|
|
||||||
@@ -78,7 +88,7 @@ The primary Gas Town experience. Agents run in tmux sessions with the Mayor as y
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
gt start # Start Gas Town (daemon + Mayor session)
|
gt start # Start Gas Town (daemon + Mayor session)
|
||||||
cd ~/gt && gt prime # Enter Mayor session
|
gt mayor attach # Enter Mayor session
|
||||||
|
|
||||||
# Inside Mayor session, just ask:
|
# Inside Mayor session, just ask:
|
||||||
# "Create a convoy for issues 123 and 456 in myproject"
|
# "Create a convoy for issues 123 and 456 in myproject"
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ func runInstall(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
fmt.Printf(" %d. Add a rig: %s\n", step, style.Dim.Render("gt rig add <name> <git-url>"))
|
fmt.Printf(" %d. Add a rig: %s\n", step, style.Dim.Render("gt rig add <name> <git-url>"))
|
||||||
step++
|
step++
|
||||||
fmt.Printf(" %d. Start the Mayor: %s\n", step, style.Dim.Render("cd "+absPath+" && gt prime"))
|
fmt.Printf(" %d. Enter the Mayor's office: %s\n", step, style.Dim.Render("gt mayor attach"))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -399,8 +399,8 @@ func runRigAdd(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Printf(" └── polecats/\n")
|
fmt.Printf(" └── polecats/\n")
|
||||||
|
|
||||||
fmt.Printf("\nNext steps:\n")
|
fmt.Printf("\nNext steps:\n")
|
||||||
fmt.Printf(" gt crew add <name> --rig %s # Create your workspace\n", name)
|
fmt.Printf(" gt crew add <name> --rig %s # Create your personal workspace\n", name)
|
||||||
fmt.Printf(" cd %s/crew/<name> # Work in your clone\n", filepath.Join(townRoot, name))
|
fmt.Printf(" cd %s/crew/<name> # Start working\n", filepath.Join(townRoot, name))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
|
|||||||
// Create shared bare repo as source of truth for refinery and polecats.
|
// Create shared bare repo as source of truth for refinery and polecats.
|
||||||
// This allows refinery to see polecat branches without pushing to remote.
|
// This allows refinery to see polecat branches without pushing to remote.
|
||||||
// Mayor remains a separate clone (doesn't need branch visibility).
|
// Mayor remains a separate clone (doesn't need branch visibility).
|
||||||
|
fmt.Printf(" Cloning repository (this may take a moment)...\n")
|
||||||
bareRepoPath := filepath.Join(rigPath, ".repo.git")
|
bareRepoPath := filepath.Join(rigPath, ".repo.git")
|
||||||
if localRepo != "" {
|
if localRepo != "" {
|
||||||
if err := m.git.CloneBareWithReference(opts.GitURL, bareRepoPath, localRepo); err != nil {
|
if err := m.git.CloneBareWithReference(opts.GitURL, bareRepoPath, localRepo); err != nil {
|
||||||
@@ -280,6 +281,7 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
|
|||||||
return nil, fmt.Errorf("creating bare repo: %w", err)
|
return nil, fmt.Errorf("creating bare repo: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Printf(" ✓ Created shared bare repo\n")
|
||||||
bareGit := git.NewGitWithDir(bareRepoPath, "")
|
bareGit := git.NewGitWithDir(bareRepoPath, "")
|
||||||
|
|
||||||
// Detect default branch (main, master, etc.)
|
// Detect default branch (main, master, etc.)
|
||||||
@@ -293,6 +295,7 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
|
|||||||
// Create mayor as regular clone (separate from bare repo).
|
// Create mayor as regular clone (separate from bare repo).
|
||||||
// Mayor doesn't need to see polecat branches - that's refinery's job.
|
// Mayor doesn't need to see polecat branches - that's refinery's job.
|
||||||
// This also allows mayor to stay on the default branch without conflicting with refinery.
|
// This also allows mayor to stay on the default branch without conflicting with refinery.
|
||||||
|
fmt.Printf(" Creating mayor clone...\n")
|
||||||
mayorRigPath := filepath.Join(rigPath, "mayor", "rig")
|
mayorRigPath := filepath.Join(rigPath, "mayor", "rig")
|
||||||
if err := os.MkdirAll(filepath.Dir(mayorRigPath), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(mayorRigPath), 0755); err != nil {
|
||||||
return nil, fmt.Errorf("creating mayor dir: %w", err)
|
return nil, fmt.Errorf("creating mayor dir: %w", err)
|
||||||
@@ -310,6 +313,7 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
|
|||||||
return nil, fmt.Errorf("cloning for mayor: %w", err)
|
return nil, fmt.Errorf("cloning for mayor: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Printf(" ✓ Created mayor clone\n")
|
||||||
|
|
||||||
// Check if source repo has .beads/ with its own prefix - if so, use that prefix.
|
// Check if source repo has .beads/ with its own prefix - if so, use that prefix.
|
||||||
// This ensures we use the project's existing beads database instead of creating a new one.
|
// This ensures we use the project's existing beads database instead of creating a new one.
|
||||||
@@ -347,6 +351,7 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
|
|||||||
// Create refinery as worktree from bare repo on default branch.
|
// Create refinery as worktree from bare repo on default branch.
|
||||||
// Refinery needs to see polecat branches (shared .repo.git) and merges them.
|
// Refinery needs to see polecat branches (shared .repo.git) and merges them.
|
||||||
// Being on the default branch allows direct merge workflow.
|
// Being on the default branch allows direct merge workflow.
|
||||||
|
fmt.Printf(" Creating refinery worktree...\n")
|
||||||
refineryRigPath := filepath.Join(rigPath, "refinery", "rig")
|
refineryRigPath := filepath.Join(rigPath, "refinery", "rig")
|
||||||
if err := os.MkdirAll(filepath.Dir(refineryRigPath), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(refineryRigPath), 0755); err != nil {
|
||||||
return nil, fmt.Errorf("creating refinery dir: %w", err)
|
return nil, fmt.Errorf("creating refinery dir: %w", err)
|
||||||
@@ -354,6 +359,7 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
|
|||||||
if err := bareGit.WorktreeAddExisting(refineryRigPath, defaultBranch); err != nil {
|
if err := bareGit.WorktreeAddExisting(refineryRigPath, defaultBranch); err != nil {
|
||||||
return nil, fmt.Errorf("creating refinery worktree: %w", err)
|
return nil, fmt.Errorf("creating refinery worktree: %w", err)
|
||||||
}
|
}
|
||||||
|
fmt.Printf(" ✓ Created refinery worktree\n")
|
||||||
// Create refinery CLAUDE.md (overrides any from cloned repo)
|
// Create refinery CLAUDE.md (overrides any from cloned repo)
|
||||||
if err := m.createRoleCLAUDEmd(refineryRigPath, "refinery", opts.Name, ""); err != nil {
|
if err := m.createRoleCLAUDEmd(refineryRigPath, "refinery", opts.Name, ""); err != nil {
|
||||||
return nil, fmt.Errorf("creating refinery CLAUDE.md: %w", err)
|
return nil, fmt.Errorf("creating refinery CLAUDE.md: %w", err)
|
||||||
@@ -414,9 +420,11 @@ Use crew for your own workspace. Polecats are for batch work dispatch.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize beads at rig level
|
// Initialize beads at rig level
|
||||||
|
fmt.Printf(" Initializing beads database...\n")
|
||||||
if err := m.initBeads(rigPath, opts.BeadsPrefix); err != nil {
|
if err := m.initBeads(rigPath, opts.BeadsPrefix); err != nil {
|
||||||
return nil, fmt.Errorf("initializing beads: %w", err)
|
return nil, fmt.Errorf("initializing beads: %w", err)
|
||||||
}
|
}
|
||||||
|
fmt.Printf(" ✓ Initialized beads (prefix: %s)\n", opts.BeadsPrefix)
|
||||||
|
|
||||||
// Create agent beads for this rig (witness, refinery) and
|
// Create agent beads for this rig (witness, refinery) and
|
||||||
// global agents (deacon, mayor) if this is the first rig.
|
// global agents (deacon, mayor) if this is the first rig.
|
||||||
|
|||||||
Reference in New Issue
Block a user