diff --git a/internal/cmd/account.go b/internal/cmd/account.go index 17d594f8..07882b4d 100644 --- a/internal/cmd/account.go +++ b/internal/cmd/account.go @@ -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 diff --git a/internal/cmd/agents.go b/internal/cmd/agents.go index c4757b4e..370f89a0 100644 --- a/internal/cmd/agents.go +++ b/internal/cmd/agents.go @@ -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. diff --git a/internal/cmd/broadcast.go b/internal/cmd/broadcast.go index 056e8e69..bfa22aeb 100644 --- a/internal/cmd/broadcast.go +++ b/internal/cmd/broadcast.go @@ -23,8 +23,9 @@ func init() { } var broadcastCmd = &cobra.Command{ - Use: "broadcast ", - Short: "Send a nudge message to all workers", + Use: "broadcast ", + 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. diff --git a/internal/cmd/crew.go b/internal/cmd/crew.go index 17fdfb20..687c936c 100644 --- a/internal/cmd/crew.go +++ b/internal/cmd/crew.go @@ -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: diff --git a/internal/cmd/daemon.go b/internal/cmd/daemon.go index 1bc71ac9..7cdcedb3 100644 --- a/internal/cmd/daemon.go +++ b/internal/cmd/daemon.go @@ -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: diff --git a/internal/cmd/deacon.go b/internal/cmd/deacon.go index febf8fc8..8bba459f 100644 --- a/internal/cmd/deacon.go +++ b/internal/cmd/deacon.go @@ -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) } diff --git a/internal/cmd/doctor.go b/internal/cmd/doctor.go index 3ae03860..a12e1898 100644 --- a/internal/cmd/doctor.go +++ b/internal/cmd/doctor.go @@ -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, diff --git a/internal/cmd/done.go b/internal/cmd/done.go index ba6c03ca..6b93b75a 100644 --- a/internal/cmd/done.go +++ b/internal/cmd/done.go @@ -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: diff --git a/internal/cmd/down.go b/internal/cmd/down.go index 5215ab00..ea1707a0 100644 --- a/internal/cmd/down.go +++ b/internal/cmd/down.go @@ -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: diff --git a/internal/cmd/gitinit.go b/internal/cmd/gitinit.go index bfbe7321..12bc6e29 100644 --- a/internal/cmd/gitinit.go +++ b/internal/cmd/gitinit.go @@ -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: diff --git a/internal/cmd/handoff.go b/internal/cmd/handoff.go index 690f04aa..7609dc82 100644 --- a/internal/cmd/handoff.go +++ b/internal/cmd/handoff.go @@ -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: diff --git a/internal/cmd/hook.go b/internal/cmd/hook.go index 6d6624f9..196e0a20 100644 --- a/internal/cmd/hook.go +++ b/internal/cmd/hook.go @@ -12,8 +12,9 @@ import ( ) var hookCmd = &cobra.Command{ - Use: "hook ", - Short: "Attach work to your hook (durable across restarts)", + Use: "hook ", + 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 diff --git a/internal/cmd/hooks.go b/internal/cmd/hooks.go index 53b44e5b..7c063ab4 100644 --- a/internal/cmd/hooks.go +++ b/internal/cmd/hooks.go @@ -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. diff --git a/internal/cmd/init.go b/internal/cmd/init.go index 02d48970..11b7961d 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -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/, diff --git a/internal/cmd/install.go b/internal/cmd/install.go index 1af96ee7..6922f23e 100644 --- a/internal/cmd/install.go +++ b/internal/cmd/install.go @@ -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 - diff --git a/internal/cmd/issue.go b/internal/cmd/issue.go index e346fdc1..cfbbd39d 100644 --- a/internal/cmd/issue.go +++ b/internal/cmd/issue.go @@ -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{ diff --git a/internal/cmd/mail.go b/internal/cmd/mail.go index 7936ef8d..2d0e8040 100644 --- a/internal/cmd/mail.go +++ b/internal/cmd/mail.go @@ -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. diff --git a/internal/cmd/mayor.go b/internal/cmd/mayor.go index c0cd9ed4..5b45c863 100644 --- a/internal/cmd/mayor.go +++ b/internal/cmd/mayor.go @@ -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) } diff --git a/internal/cmd/molecule.go b/internal/cmd/molecule.go index fcf9dc7d..8ebaa06a 100644 --- a/internal/cmd/molecule.go +++ b/internal/cmd/molecule.go @@ -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. diff --git a/internal/cmd/mq.go b/internal/cmd/mq.go index cbf71bf3..f53d47ac 100644 --- a/internal/cmd/mq.go +++ b/internal/cmd/mq.go @@ -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. diff --git a/internal/cmd/namepool.go b/internal/cmd/namepool.go index 091af0c8..36017cc8 100644 --- a/internal/cmd/namepool.go +++ b/internal/cmd/namepool.go @@ -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 diff --git a/internal/cmd/nudge.go b/internal/cmd/nudge.go index 8dce9098..49142ecf 100644 --- a/internal/cmd/nudge.go +++ b/internal/cmd/nudge.go @@ -14,8 +14,9 @@ func init() { } var nudgeCmd = &cobra.Command{ - Use: "nudge ", - Short: "Send a message to a polecat session reliably", + Use: "nudge ", + 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: diff --git a/internal/cmd/orphans.go b/internal/cmd/orphans.go index 6f77d3c6..f4041658 100644 --- a/internal/cmd/orphans.go +++ b/internal/cmd/orphans.go @@ -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: diff --git a/internal/cmd/peek.go b/internal/cmd/peek.go index 5140f496..cd15fc42 100644 --- a/internal/cmd/peek.go +++ b/internal/cmd/peek.go @@ -16,8 +16,9 @@ func init() { } var peekCmd = &cobra.Command{ - Use: "peek [count]", - Short: "View recent output from a polecat session", + Use: "peek [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 diff --git a/internal/cmd/polecat.go b/internal/cmd/polecat.go index 7812eade..43a9a7a7 100644 --- a/internal/cmd/polecat.go +++ b/internal/cmd/polecat.go @@ -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. diff --git a/internal/cmd/prime.go b/internal/cmd/prime.go index f931e8ad..f9cd1f5f 100644 --- a/internal/cmd/prime.go +++ b/internal/cmd/prime.go @@ -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: diff --git a/internal/cmd/refinery.go b/internal/cmd/refinery.go index 4659b234..5ce3dcb1 100644 --- a/internal/cmd/refinery.go +++ b/internal/cmd/refinery.go @@ -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. diff --git a/internal/cmd/release.go b/internal/cmd/release.go index 30bfe06d..9fe7e12d 100644 --- a/internal/cmd/release.go +++ b/internal/cmd/release.go @@ -12,8 +12,9 @@ import ( var releaseReason string var releaseCmd = &cobra.Command{ - Use: "release ...", - Short: "Release stuck in_progress issues back to pending", + Use: "release ...", + 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. diff --git a/internal/cmd/rig.go b/internal/cmd/rig.go index 5e7c785d..701b6d71 100644 --- a/internal/cmd/rig.go +++ b/internal/cmd/rig.go @@ -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: diff --git a/internal/cmd/role.go b/internal/cmd/role.go index 8cecc27f..a879661a 100644 --- a/internal/cmd/role.go +++ b/internal/cmd/role.go @@ -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: diff --git a/internal/cmd/root.go b/internal/cmd/root.go index b45a47f0..52cc630f 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -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") } diff --git a/internal/cmd/session.go b/internal/cmd/session.go index 37d85870..f8f229d6 100644 --- a/internal/cmd/session.go +++ b/internal/cmd/session.go @@ -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. diff --git a/internal/cmd/sling.go b/internal/cmd/sling.go index b13f3151..7ade5c93 100644 --- a/internal/cmd/sling.go +++ b/internal/cmd/sling.go @@ -13,8 +13,9 @@ import ( ) var slingCmd = &cobra.Command{ - Use: "sling [target]", - Short: "Hook work and start immediately (no restart)", + Use: "sling [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: diff --git a/internal/cmd/spawn.go b/internal/cmd/spawn.go index a4c5420e..5c3f6bdf 100644 --- a/internal/cmd/spawn.go +++ b/internal/cmd/spawn.go @@ -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. diff --git a/internal/cmd/start.go b/internal/cmd/start.go index ac3faded..e0c4c6f3 100644 --- a/internal/cmd/start.go +++ b/internal/cmd/start.go @@ -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) } diff --git a/internal/cmd/status.go b/internal/cmd/status.go index a5726164..bf4dce45 100644 --- a/internal/cmd/status.go +++ b/internal/cmd/status.go @@ -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. diff --git a/internal/cmd/stop.go b/internal/cmd/stop.go index e541d04f..95c5969a 100644 --- a/internal/cmd/stop.go +++ b/internal/cmd/stop.go @@ -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 diff --git a/internal/cmd/swarm.go b/internal/cmd/swarm.go index 2331ba56..1d1c9325 100644 --- a/internal/cmd/swarm.go +++ b/internal/cmd/swarm.go @@ -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 diff --git a/internal/cmd/theme.go b/internal/cmd/theme.go index 4519aeac..998ac348 100644 --- a/internal/cmd/theme.go +++ b/internal/cmd/theme.go @@ -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. diff --git a/internal/cmd/up.go b/internal/cmd/up.go index 43c9a22f..c6700faf 100644 --- a/internal/cmd/up.go +++ b/internal/cmd/up.go @@ -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 } diff --git a/internal/cmd/version.go b/internal/cmd/version.go index cc745438..6635aae1 100644 --- a/internal/cmd/version.go +++ b/internal/cmd/version.go @@ -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) diff --git a/internal/cmd/witness.go b/internal/cmd/witness.go index 7fd46ffe..2018d221 100644 --- a/internal/cmd/witness.go +++ b/internal/cmd/witness.go @@ -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) }