Fix: Unknown subcommands now error instead of silently showing help

Parent commands (mol, mail, crew, polecat, etc.) previously showed help
and exited 0 for unknown subcommands like "gt mol foobar". This masked
errors in scripts and confused users.

Added requireSubcommand() helper to root.go and applied it to all parent
commands. Now unknown subcommands properly error with exit code 1.

Example before: gt mol unhook → shows help, exits 0
Example after:  gt mol unhook → "Error: unknown command "unhook"", exits 1

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gastown/crew/joe
2025-12-30 20:52:23 -08:00
committed by Steve Yegge
parent 3d09c679e2
commit df46e75a51
16 changed files with 29 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ var accountCmd = &cobra.Command{
Use: "account",
GroupID: GroupConfig,
Short: "Manage Claude Code accounts",
RunE: requireSubcommand,
Long: `Manage multiple Claude Code accounts for Gas Town.
This enables switching between accounts (e.g., personal vs work) with

View File

@@ -37,6 +37,7 @@ var convoyCmd = &cobra.Command{
Use: "convoy",
GroupID: GroupWork,
Short: "Track batches of work across rigs",
RunE: requireSubcommand,
Long: `Manage convoys - the primary unit for tracking batched work.
A convoy is a persistent tracking unit that monitors related issues across

View File

@@ -24,6 +24,7 @@ var crewCmd = &cobra.Command{
Use: "crew",
GroupID: GroupWorkspace,
Short: "Manage crew workspaces (user-managed persistent workspaces)",
RunE: requireSubcommand,
Long: `Crew workers are user-managed persistent workspaces within a rig.
Unlike polecats which are witness-managed and transient, crew workers are:

View File

@@ -17,6 +17,7 @@ var daemonCmd = &cobra.Command{
Use: "daemon",
GroupID: GroupServices,
Short: "Manage the Gas Town daemon",
RunE: requireSubcommand,
Long: `Manage the Gas Town background daemon.
The daemon is a simple Go process that:

View File

@@ -26,6 +26,7 @@ var deaconCmd = &cobra.Command{
Aliases: []string{"dea"},
GroupID: GroupAgents,
Short: "Manage the Deacon session",
RunE: requireSubcommand,
Long: `Manage the Deacon tmux session.
The Deacon is the hierarchical health-check orchestrator for Gas Town.

View File

@@ -46,6 +46,7 @@ var mailCmd = &cobra.Command{
Use: "mail",
GroupID: GroupComm,
Short: "Agent messaging system",
RunE: requireSubcommand,
Long: `Send and receive messages between agents.
The mail system allows Mayor, polecats, and the Refinery to communicate.

View File

@@ -21,6 +21,7 @@ var mayorCmd = &cobra.Command{
Aliases: []string{"may"},
GroupID: GroupAgents,
Short: "Manage the Mayor session",
RunE: requireSubcommand,
Long: `Manage the Mayor tmux session.
The Mayor is the global coordinator for Gas Town, running as a persistent

View File

@@ -14,6 +14,7 @@ var moleculeCmd = &cobra.Command{
Aliases: []string{"molecule"},
GroupID: GroupWork,
Short: "Agent molecule workflow commands",
RunE: requireSubcommand,
Long: `Agent-specific molecule workflow operations.
These commands operate on the current agent's hook and attached molecules.
@@ -204,6 +205,7 @@ a permanent (but compact) record.`,
var moleculeStepCmd = &cobra.Command{
Use: "step",
Short: "Molecule step operations",
RunE: requireSubcommand,
Long: `Commands for working with molecule steps.
A molecule is a DAG of steps. Each step is a beads issue with the molecule root

View File

@@ -53,6 +53,7 @@ var mqCmd = &cobra.Command{
Use: "mq",
GroupID: GroupWork,
Short: "Merge queue operations",
RunE: requireSubcommand,
Long: `Manage the merge queue for a rig.
The merge queue tracks work branches from polecats waiting to be merged.
@@ -168,6 +169,7 @@ Example:
var mqIntegrationCmd = &cobra.Command{
Use: "integration",
Short: "Manage integration branches for epics",
RunE: requireSubcommand,
Long: `Manage integration branches for batch work on epics.
Integration branches allow multiple MRs for an epic to target a shared

View File

@@ -33,6 +33,7 @@ var polecatCmd = &cobra.Command{
Aliases: []string{"cat", "polecats"},
GroupID: GroupAgents,
Short: "Manage polecats in rigs",
RunE: requireSubcommand,
Long: `Manage polecat lifecycle in rigs.
Polecats are worker agents that operate in their own git worktrees.

View File

@@ -25,6 +25,7 @@ var refineryCmd = &cobra.Command{
Aliases: []string{"ref"},
GroupID: GroupAgents,
Short: "Manage the merge queue processor",
RunE: requireSubcommand,
Long: `Manage the Refinery merge queue processor for a rig.
The Refinery processes merge requests from polecats, merging their work

View File

@@ -26,6 +26,7 @@ var rigCmd = &cobra.Command{
Use: "rig",
GroupID: GroupWorkspace,
Short: "Manage rigs in the workspace",
RunE: requireSubcommand,
Long: `Manage rigs (project containers) in the Gas Town workspace.
A rig is a container for managing a project and its agents:

View File

@@ -2,6 +2,7 @@
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
@@ -85,3 +86,14 @@ func buildCommandPath(cmd *cobra.Command) string {
}
return strings.Join(parts, " ")
}
// requireSubcommand returns a RunE function for parent commands that require
// a subcommand. Without this, Cobra silently shows help and exits 0 for
// unknown subcommands like "gt mol foobar", masking errors.
func requireSubcommand(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("requires a subcommand\n\nRun '%s --help' for usage", buildCommandPath(cmd))
}
return fmt.Errorf("unknown command %q for %q\n\nRun '%s --help' for available commands",
args[0], buildCommandPath(cmd), buildCommandPath(cmd))
}

View File

@@ -37,6 +37,7 @@ var sessionCmd = &cobra.Command{
Aliases: []string{"sess"},
GroupID: GroupAgents,
Short: "Manage polecat sessions",
RunE: requireSubcommand,
Long: `Manage tmux sessions for polecats.
Sessions are tmux sessions running Claude for each polecat.

View File

@@ -40,6 +40,7 @@ var swarmCmd = &cobra.Command{
GroupID: GroupWork,
Short: "[DEPRECATED] Use 'gt convoy' instead",
Deprecated: "Use 'gt convoy' for work tracking. A 'swarm' is now just the ephemeral workers on a convoy.",
RunE: requireSubcommand,
Long: `DEPRECATED: Use 'gt convoy' instead.
The term "swarm" now refers to the ephemeral set of workers on a convoy's issues,

View File

@@ -29,6 +29,7 @@ var witnessCmd = &cobra.Command{
Use: "witness",
GroupID: GroupAgents,
Short: "Manage the polecat monitoring agent",
RunE: requireSubcommand,
Long: `Manage the Witness monitoring agent for a rig.
The Witness monitors polecats for stuck/idle state, nudges polecats