feat: Add gcloud-style command grouping to gt help output

Organize 43 commands into 7 logical groups using cobra's built-in
AddGroup/GroupID feature:

- Work Management: spawn, sling, hook, handoff, done, mol, mq, etc.
- Agent Management: mayor, witness, refinery, deacon, polecat, etc.
- Communication: mail, nudge, broadcast, peek
- Services: daemon, start, stop, up, down, shutdown
- Workspace: rig, crew, init, install, git-init, namepool
- Configuration: account, theme, hooks, issue, completion
- Diagnostics: status, doctor, prime, version, help

Also renamed molecule to mol as the primary command name
(molecule is now an alias).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-25 02:31:32 -08:00
parent cd109e9db7
commit f86a73c2f0
42 changed files with 149 additions and 75 deletions
+3 -2
View File
@@ -21,8 +21,9 @@ var (
)
var accountCmd = &cobra.Command{
Use: "account",
Short: "Manage Claude Code accounts",
Use: "account",
GroupID: GroupConfig,
Short: "Manage Claude Code accounts",
Long: `Manage multiple Claude Code accounts for Gas Town.
This enables switching between accounts (e.g., personal vs work) with
+1
View File
@@ -59,6 +59,7 @@ var AgentTypeIcons = map[AgentType]string{
var agentsCmd = &cobra.Command{
Use: "agents",
Aliases: []string{"ag"},
GroupID: GroupAgents,
Short: "Switch between Gas Town agent sessions",
Long: `Display a popup menu of core Gas Town agent sessions.
+3 -2
View File
@@ -23,8 +23,9 @@ func init() {
}
var broadcastCmd = &cobra.Command{
Use: "broadcast <message>",
Short: "Send a nudge message to all workers",
Use: "broadcast <message>",
GroupID: GroupComm,
Short: "Send a nudge message to all workers",
Long: `Broadcasts a message to all active workers (polecats and crew).
By default, only workers (polecats and crew) receive the message.
+3 -2
View File
@@ -17,8 +17,9 @@ var (
)
var crewCmd = &cobra.Command{
Use: "crew",
Short: "Manage crew workspaces (user-managed persistent workspaces)",
Use: "crew",
GroupID: GroupWorkspace,
Short: "Manage crew workspaces (user-managed persistent workspaces)",
Long: `Crew workers are user-managed persistent workspaces within a rig.
Unlike polecats which are witness-managed and transient, crew workers are:
+3 -2
View File
@@ -14,8 +14,9 @@ import (
)
var daemonCmd = &cobra.Command{
Use: "daemon",
Short: "Manage the Gas Town daemon",
Use: "daemon",
GroupID: GroupServices,
Short: "Manage the Gas Town daemon",
Long: `Manage the Gas Town background daemon.
The daemon is a simple Go process that:
+3 -1
View File
@@ -21,6 +21,7 @@ const DeaconSessionName = "gt-deacon"
var deaconCmd = &cobra.Command{
Use: "deacon",
Aliases: []string{"dea"},
GroupID: GroupAgents,
Short: "Manage the Deacon session",
Long: `Manage the Deacon tmux session.
@@ -156,7 +157,8 @@ func startDeaconSession(t *tmux.Tmux) error {
// Launch Claude directly (no shell respawn loop)
// Restarts are handled by daemon via ensureDeaconRunning on each heartbeat
// The startup hook handles context loading automatically
if err := t.SendKeys(DeaconSessionName, "claude --dangerously-skip-permissions"); err != nil {
// Export GT_ROLE in the command since tmux SetEnvironment only affects new panes
if err := t.SendKeys(DeaconSessionName, "export GT_ROLE=deacon && claude --dangerously-skip-permissions"); err != nil {
return fmt.Errorf("sending command: %w", err)
}
+3 -2
View File
@@ -16,8 +16,9 @@ var (
)
var doctorCmd = &cobra.Command{
Use: "doctor",
Short: "Run health checks on the workspace",
Use: "doctor",
GroupID: GroupDiag,
Short: "Run health checks on the workspace",
Long: `Run diagnostic checks on the Gas Town workspace.
Doctor checks for common configuration issues, missing files,
+3 -2
View File
@@ -16,8 +16,9 @@ import (
)
var doneCmd = &cobra.Command{
Use: "done",
Short: "Signal work ready for merge queue",
Use: "done",
GroupID: GroupWork,
Short: "Signal work ready for merge queue",
Long: `Signal that your work is complete and ready for the merge queue.
This is a convenience command for polecats that:
+3 -2
View File
@@ -12,8 +12,9 @@ import (
)
var downCmd = &cobra.Command{
Use: "down",
Short: "Stop all Gas Town services",
Use: "down",
GroupID: GroupServices,
Short: "Stop all Gas Town services",
Long: `Stop all Gas Town long-lived services.
This gracefully shuts down all infrastructure agents:
+3 -2
View File
@@ -18,8 +18,9 @@ var (
)
var gitInitCmd = &cobra.Command{
Use: "git-init",
Short: "Initialize git repository for a Gas Town HQ",
Use: "git-init",
GroupID: GroupWorkspace,
Short: "Initialize git repository for a Gas Town HQ",
Long: `Initialize or configure git for an existing Gas Town HQ.
This command:
+3 -2
View File
@@ -14,8 +14,9 @@ import (
)
var handoffCmd = &cobra.Command{
Use: "handoff [bead-or-role]",
Short: "Hand off to a fresh session, work continues from hook",
Use: "handoff [bead-or-role]",
GroupID: GroupWork,
Short: "Hand off to a fresh session, work continues from hook",
Long: `End watch. Hand off to a fresh agent session.
This is the canonical way to end any agent session. It handles all roles:
+3 -2
View File
@@ -12,8 +12,9 @@ import (
)
var hookCmd = &cobra.Command{
Use: "hook <bead-id>",
Short: "Attach work to your hook (durable across restarts)",
Use: "hook <bead-id>",
GroupID: GroupWork,
Short: "Attach work to your hook (durable across restarts)",
Long: `Attach a bead (issue) to your hook for durable work tracking.
The hook is the "durability primitive" - work on your hook survives session
+3 -2
View File
@@ -18,8 +18,9 @@ var (
)
var hooksCmd = &cobra.Command{
Use: "hooks",
Short: "List all Claude Code hooks in the workspace",
Use: "hooks",
GroupID: GroupConfig,
Short: "List all Claude Code hooks in the workspace",
Long: `List all Claude Code hooks configured in the workspace.
Scans for .claude/settings.json files and displays hooks by type.
+3 -2
View File
@@ -15,8 +15,9 @@ import (
var initForce bool
var initCmd = &cobra.Command{
Use: "init",
Short: "Initialize current directory as a Gas Town rig",
Use: "init",
GroupID: GroupWorkspace,
Short: "Initialize current directory as a Gas Town rig",
Long: `Initialize the current directory for use as a Gas Town rig.
This creates the standard agent directories (polecats/, witness/, refinery/,
+3 -2
View File
@@ -26,8 +26,9 @@ var (
)
var installCmd = &cobra.Command{
Use: "install [path]",
Short: "Create a new Gas Town HQ (workspace)",
Use: "install [path]",
GroupID: GroupWorkspace,
Short: "Create a new Gas Town HQ (workspace)",
Long: `Create a new Gas Town HQ at the specified path.
The HQ (headquarters) is the top-level directory where Gas Town is installed -
+3 -2
View File
@@ -9,8 +9,9 @@ import (
)
var issueCmd = &cobra.Command{
Use: "issue",
Short: "Manage current issue for status line display",
Use: "issue",
GroupID: GroupConfig,
Short: "Manage current issue for status line display",
}
var issueSetCmd = &cobra.Command{
+3 -2
View File
@@ -41,8 +41,9 @@ var (
)
var mailCmd = &cobra.Command{
Use: "mail",
Short: "Agent messaging system",
Use: "mail",
GroupID: GroupComm,
Short: "Agent messaging system",
Long: `Send and receive messages between agents.
The mail system allows Mayor, polecats, and the Refinery to communicate.
+3 -1
View File
@@ -17,6 +17,7 @@ const MayorSessionName = "gt-mayor"
var mayorCmd = &cobra.Command{
Use: "mayor",
Aliases: []string{"may"},
GroupID: GroupAgents,
Short: "Manage the Mayor session",
Long: `Manage the Mayor tmux session.
@@ -126,7 +127,8 @@ func startMayorSession(t *tmux.Tmux) error {
// Launch Claude - the startup hook handles 'gt prime' automatically
// Use SendKeysDelayed to allow shell initialization after NewSession
claudeCmd := `claude --dangerously-skip-permissions`
// Export GT_ROLE in the command since tmux SetEnvironment only affects new panes
claudeCmd := `export GT_ROLE=mayor && claude --dangerously-skip-permissions`
if err := t.SendKeysDelayed(MayorSessionName, claudeCmd, 200); err != nil {
return fmt.Errorf("sending command: %w", err)
}
+3 -2
View File
@@ -21,8 +21,9 @@ var (
)
var moleculeCmd = &cobra.Command{
Use: "molecule",
Aliases: []string{"mol"},
Use: "mol",
Aliases: []string{"molecule"},
GroupID: GroupWork,
Short: "Molecule workflow commands",
Long: `Manage molecule workflow templates.
+3 -2
View File
@@ -50,8 +50,9 @@ var (
)
var mqCmd = &cobra.Command{
Use: "mq",
Short: "Merge queue operations",
Use: "mq",
GroupID: GroupWork,
Short: "Merge queue operations",
Long: `Manage the merge queue for a rig.
The merge queue tracks work branches from polecats waiting to be merged.
+3 -2
View File
@@ -18,8 +18,9 @@ var (
)
var namepoolCmd = &cobra.Command{
Use: "namepool",
Short: "Manage polecat name pools",
Use: "namepool",
GroupID: GroupWorkspace,
Short: "Manage polecat name pools",
Long: `Manage themed name pools for polecats in Gas Town.
By default, polecats get themed names from the Mad Max universe
+3 -2
View File
@@ -14,8 +14,9 @@ func init() {
}
var nudgeCmd = &cobra.Command{
Use: "nudge <rig/polecat> <message>",
Short: "Send a message to a polecat session reliably",
Use: "nudge <rig/polecat> <message>",
GroupID: GroupComm,
Short: "Send a message to a polecat session reliably",
Long: `Sends a message to a polecat's Claude Code session.
Uses a reliable delivery pattern:
+3 -2
View File
@@ -15,8 +15,9 @@ import (
)
var orphansCmd = &cobra.Command{
Use: "orphans",
Short: "Find lost polecat work",
Use: "orphans",
GroupID: GroupWork,
Short: "Find lost polecat work",
Long: `Find orphaned commits that were never merged to main.
Polecat work can get lost when:
+3 -2
View File
@@ -16,8 +16,9 @@ func init() {
}
var peekCmd = &cobra.Command{
Use: "peek <rig/polecat> [count]",
Short: "View recent output from a polecat session",
Use: "peek <rig/polecat> [count]",
GroupID: GroupComm,
Short: "View recent output from a polecat session",
Long: `Capture and display recent terminal output from a polecat session.
This is the ergonomic alias for 'gt session capture'. Use it to check
+1
View File
@@ -30,6 +30,7 @@ var (
var polecatCmd = &cobra.Command{
Use: "polecat",
Aliases: []string{"cat", "polecats"},
GroupID: GroupAgents,
Short: "Manage polecats in rigs",
Long: `Manage polecat lifecycle in rigs.
+3 -2
View File
@@ -33,8 +33,9 @@ const (
)
var primeCmd = &cobra.Command{
Use: "prime",
Short: "Output role context for current directory",
Use: "prime",
GroupID: GroupDiag,
Short: "Output role context for current directory",
Long: `Detect the agent role from the current directory and output context.
Role detection:
+1
View File
@@ -23,6 +23,7 @@ var (
var refineryCmd = &cobra.Command{
Use: "refinery",
Aliases: []string{"ref"},
GroupID: GroupAgents,
Short: "Manage the merge queue processor",
Long: `Manage the Refinery merge queue processor for a rig.
+3 -2
View File
@@ -12,8 +12,9 @@ import (
var releaseReason string
var releaseCmd = &cobra.Command{
Use: "release <issue-id>...",
Short: "Release stuck in_progress issues back to pending",
Use: "release <issue-id>...",
GroupID: GroupWork,
Short: "Release stuck in_progress issues back to pending",
Long: `Release one or more in_progress issues back to open/pending status.
This is used to recover stuck steps when a worker dies mid-task.
+3 -2
View File
@@ -23,8 +23,9 @@ import (
)
var rigCmd = &cobra.Command{
Use: "rig",
Short: "Manage rigs in the workspace",
Use: "rig",
GroupID: GroupWorkspace,
Short: "Manage rigs in the workspace",
Long: `Manage rigs (project containers) in the Gas Town workspace.
A rig is a container for managing a project and its agents:
+3 -2
View File
@@ -34,8 +34,9 @@ type RoleInfo struct {
}
var roleCmd = &cobra.Command{
Use: "role",
Short: "Show or manage agent role",
Use: "role",
GroupID: GroupAgents,
Short: "Show or manage agent role",
Long: `Display the current agent role and its detection source.
Role is determined by:
+26
View File
@@ -32,10 +32,36 @@ func Execute() {
}
}
// Command group IDs - used by subcommands to organize help output
const (
GroupWork = "work"
GroupAgents = "agents"
GroupComm = "comm"
GroupServices = "services"
GroupWorkspace = "workspace"
GroupConfig = "config"
GroupDiag = "diag"
)
func init() {
// Enable prefix matching for subcommands (e.g., "gt ref at" -> "gt refinery attach")
cobra.EnablePrefixMatching = true
// Define command groups (order determines help output order)
rootCmd.AddGroup(
&cobra.Group{ID: GroupWork, Title: "Work Management:"},
&cobra.Group{ID: GroupAgents, Title: "Agent Management:"},
&cobra.Group{ID: GroupComm, Title: "Communication:"},
&cobra.Group{ID: GroupServices, Title: "Services:"},
&cobra.Group{ID: GroupWorkspace, Title: "Workspace:"},
&cobra.Group{ID: GroupConfig, Title: "Configuration:"},
&cobra.Group{ID: GroupDiag, Title: "Diagnostics:"},
)
// Put help and completion in a sensible group
rootCmd.SetHelpCommandGroupID(GroupDiag)
rootCmd.SetCompletionCommandGroupID(GroupConfig)
// Global flags can be added here
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file")
}
+1
View File
@@ -34,6 +34,7 @@ var (
var sessionCmd = &cobra.Command{
Use: "session",
Aliases: []string{"sess"},
GroupID: GroupAgents,
Short: "Manage polecat sessions",
Long: `Manage tmux sessions for polecats.
+3 -2
View File
@@ -13,8 +13,9 @@ import (
)
var slingCmd = &cobra.Command{
Use: "sling <bead-id> [target]",
Short: "Hook work and start immediately (no restart)",
Use: "sling <bead-id> [target]",
GroupID: GroupWork,
Short: "Hook work and start immediately (no restart)",
Long: `Sling work onto an agent's hook and start working immediately.
Unlike 'gt handoff', sling does NOT restart the session. It:
+1
View File
@@ -40,6 +40,7 @@ var (
var spawnCmd = &cobra.Command{
Use: "spawn [rig/polecat | rig]",
Aliases: []string{"sp"},
GroupID: GroupWork,
Short: "Spawn a polecat with work assignment",
Long: `Spawn a polecat with a work assignment.
+8 -5
View File
@@ -31,8 +31,9 @@ var (
)
var startCmd = &cobra.Command{
Use: "start",
Short: "Start Gas Town",
Use: "start",
GroupID: GroupServices,
Short: "Start Gas Town",
Long: `Start Gas Town by launching the Deacon and Mayor.
The Deacon is the health-check orchestrator that monitors Mayor and Witnesses.
@@ -46,8 +47,9 @@ To stop Gas Town, use 'gt shutdown'.`,
}
var shutdownCmd = &cobra.Command{
Use: "shutdown",
Short: "Shutdown Gas Town",
Use: "shutdown",
GroupID: GroupServices,
Short: "Shutdown Gas Town",
Long: `Shutdown Gas Town by stopping agents and cleaning up polecats.
By default, preserves crew sessions (your persistent workspaces).
@@ -240,7 +242,8 @@ func ensureRefinerySession(rigName string, r *rig.Rig) (bool, error) {
_ = t.ConfigureGasTownSession(sessionName, theme, rigName, "refinery", "refinery")
// Launch Claude in a respawn loop
loopCmd := `while true; do echo "🛢️ Starting Refinery for ` + rigName + `..."; claude --dangerously-skip-permissions; echo ""; echo "Refinery exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done`
// Export GT_ROLE in the command since tmux SetEnvironment only affects new panes
loopCmd := `export GT_ROLE=refinery && while true; do echo "🛢️ Starting Refinery for ` + rigName + `..."; claude --dangerously-skip-permissions; echo ""; echo "Refinery exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done`
if err := t.SendKeysDelayed(sessionName, loopCmd, 200); err != nil {
return false, fmt.Errorf("sending command: %w", err)
}
+1
View File
@@ -22,6 +22,7 @@ var statusJSON bool
var statusCmd = &cobra.Command{
Use: "status",
Aliases: []string{"stat"},
GroupID: GroupDiag,
Short: "Show overall town status",
Long: `Display the current status of the Gas Town workspace.
+3 -2
View File
@@ -21,8 +21,9 @@ var (
)
var stopCmd = &cobra.Command{
Use: "stop",
Short: "Emergency stop for sessions",
Use: "stop",
GroupID: GroupServices,
Short: "Emergency stop for sessions",
Long: `Emergency stop command for Gas Town sessions.
Stops all running polecat sessions across rigs. Use for emergency shutdown
+3 -2
View File
@@ -34,8 +34,9 @@ var (
)
var swarmCmd = &cobra.Command{
Use: "swarm",
Short: "Manage multi-agent swarms",
Use: "swarm",
GroupID: GroupWork,
Short: "Manage multi-agent swarms",
Long: `Manage coordinated multi-agent work units (swarms).
A swarm coordinates multiple polecats working on related tasks from a shared
+3 -2
View File
@@ -19,8 +19,9 @@ var (
)
var themeCmd = &cobra.Command{
Use: "theme [name]",
Short: "View or set tmux theme for the current rig",
Use: "theme [name]",
GroupID: GroupConfig,
Short: "View or set tmux theme for the current rig",
Long: `Manage tmux status bar themes for Gas Town sessions.
Without arguments, shows the current theme assignment.
+8 -5
View File
@@ -17,8 +17,9 @@ import (
)
var upCmd = &cobra.Command{
Use: "up",
Short: "Bring up all Gas Town services",
Use: "up",
GroupID: GroupServices,
Short: "Bring up all Gas Town services",
Long: `Start all Gas Town long-lived services.
This is the idempotent "boot" command for Gas Town. It ensures all
@@ -213,12 +214,13 @@ func ensureSession(t *tmux.Tmux, sessionName, workDir, role string) error {
}
// Launch Claude
// Export GT_ROLE in the command since tmux SetEnvironment only affects new panes
var claudeCmd string
if role == "deacon" {
// Deacon uses respawn loop
claudeCmd = `while true; do echo "⛪ Starting Deacon session..."; claude --dangerously-skip-permissions; echo ""; echo "Deacon exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done`
claudeCmd = `export GT_ROLE=deacon && while true; do echo "⛪ Starting Deacon session..."; claude --dangerously-skip-permissions; echo ""; echo "Deacon exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done`
} else {
claudeCmd = `claude --dangerously-skip-permissions`
claudeCmd = fmt.Sprintf(`export GT_ROLE=%s && claude --dangerously-skip-permissions`, role)
}
if err := t.SendKeysDelayed(sessionName, claudeCmd, 200); err != nil {
@@ -252,7 +254,8 @@ func ensureWitness(t *tmux.Tmux, sessionName, rigPath, rigName string) error {
_ = t.ConfigureGasTownSession(sessionName, theme, "", "Witness", rigName)
// Launch Claude
claudeCmd := `claude --dangerously-skip-permissions`
// Export GT_ROLE in the command since tmux SetEnvironment only affects new panes
claudeCmd := `export GT_ROLE=witness && claude --dangerously-skip-permissions`
if err := t.SendKeysDelayed(sessionName, claudeCmd, 200); err != nil {
return err
}
+3 -2
View File
@@ -15,8 +15,9 @@ var (
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version information",
Use: "version",
GroupID: GroupDiag,
Short: "Print version information",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(style.Bold.Render("gt") + " - Gas Town CLI")
fmt.Printf("Version: %s\n", Version)
+5 -3
View File
@@ -22,8 +22,9 @@ var (
)
var witnessCmd = &cobra.Command{
Use: "witness",
Short: "Manage the polecat monitoring agent",
Use: "witness",
GroupID: GroupAgents,
Short: "Manage the polecat monitoring agent",
Long: `Manage the Witness monitoring agent for a rig.
The Witness monitors polecats for stuck/idle state, nudges polecats
@@ -330,7 +331,8 @@ func ensureWitnessSession(rigName string, r *rig.Rig) (bool, error) {
// Launch Claude directly (no shell respawn loop)
// Restarts are handled by daemon via LIFECYCLE mail or deacon health-scan
// NOTE: No gt prime injection needed - SessionStart hook handles it automatically
if err := t.SendKeys(sessionName, "claude --dangerously-skip-permissions"); err != nil {
// Export GT_ROLE in the command since tmux SetEnvironment only affects new panes
if err := t.SendKeys(sessionName, "export GT_ROLE=witness && claude --dangerously-skip-permissions"); err != nil {
return false, fmt.Errorf("sending command: %w", err)
}