From 45747c355e16cfdc8040e501afb97e0a7a1ee041 Mon Sep 17 00:00:00 2001 From: David Aronchick Date: Thu, 29 Jan 2026 16:16:58 -0500 Subject: [PATCH] feat(cli): improve help output with categorized commands Restructures the CLI help output to be more user-friendly: - Add QUICK START section showing the 4 most common commands - Group commands into 6 categories: DAEMON, REPOSITORIES, AGENTS, COMMUNICATION, MAINTENANCE, META - Hide 7 redundant aliases from help (still functional, just not displayed) - Add Hidden and Category fields to Command struct for flexibility The help output now shows 21 focused commands instead of 28 scattered entries, making it much easier for new users to understand what to do. Also adds CLI_RESTRUCTURE_PROPOSAL.md documenting the analysis and future restructuring options for v2.0. Aligns with ROADMAP P2 "Better onboarding". Co-Authored-By: Claude Opus 4.5 --- docs/CLI_RESTRUCTURE_PROPOSAL.md | 323 +++++++++++++++++++++++++++++++ docs/COMMANDS.md | 13 +- internal/cli/cli.go | 123 ++++++++++-- 3 files changed, 442 insertions(+), 17 deletions(-) create mode 100644 docs/CLI_RESTRUCTURE_PROPOSAL.md diff --git a/docs/CLI_RESTRUCTURE_PROPOSAL.md b/docs/CLI_RESTRUCTURE_PROPOSAL.md new file mode 100644 index 00000000..6d66705e --- /dev/null +++ b/docs/CLI_RESTRUCTURE_PROPOSAL.md @@ -0,0 +1,323 @@ +# CLI Restructure Proposal + +> **Status**: Draft proposal for discussion +> **Author**: cool-wolf worker +> **Aligns with**: ROADMAP P2 - Better onboarding + +## Problem Statement + +The multiclaude CLI has grown organically and now suffers from: + +1. **Too many top-level commands** (28 entries) +2. **Redundant aliases** that pollute `--help` output +3. **Inconsistent naming** (`agent`/`agents`, `work`/`worker`) +4. **Unclear mental model** - is the tool repo-centric or agent-centric? + +### Evidence: Current `--help` Output + +``` +Subcommands: + repair ← maintenance + claude ← agent context + logs ← agent ops + status ← system overview + daemon ← daemon management + worker ← agent creation + work ← ALIAS for worker + agent ← agent ops + attach ← ALIAS for agent attach + review ← agent creation + config ← repo config + start ← ALIAS for daemon start + list ← ALIAS for repo list + workspace ← agent creation + refresh ← agent ops + docs ← meta + diagnostics ← meta + version ← meta + agents ← agent definitions (confusing: singular vs plural) + init ← ALIAS for repo init + cleanup ← maintenance + bug ← meta + stop-all ← daemon control + repo ← repo management + history ← ALIAS for repo history + message ← agent comms +``` + +A new user sees 28 commands and has no idea where to start. + +## Current Command Tree (Actual) + +``` +multiclaude +├── daemon +│ ├── start +│ ├── stop +│ ├── status +│ └── logs +├── repo +│ ├── init +│ ├── list +│ ├── rm +│ ├── use +│ ├── current +│ ├── unset +│ ├── history +│ └── hibernate +├── worker +│ ├── create (default) +│ ├── list +│ └── rm +├── workspace +│ ├── add +│ ├── rm +│ ├── list +│ └── connect (default) +├── agent +│ ├── attach +│ ├── complete +│ ├── restart +│ ├── send-message (alias) +│ ├── list-messages (alias) +│ ├── read-message (alias) +│ └── ack-message (alias) +├── agents +│ ├── list +│ ├── spawn +│ └── reset +├── message +│ ├── send +│ ├── list +│ ├── read +│ └── ack +├── logs +│ ├── list +│ ├── search +│ └── clean +├── start (alias → daemon start) +├── init (alias → repo init) +├── list (alias → repo list) +├── history (alias → repo history) +├── attach (alias → agent attach) +├── work (alias → worker) +├── status +├── stop-all +├── cleanup +├── repair +├── refresh +├── claude +├── review +├── config +├── docs +├── diagnostics +├── version +└── bug +``` + +**Issues by category:** + +| Issue | Count | Examples | +|-------|-------|----------| +| Top-level aliases | 7 | `start`, `init`, `list`, `history`, `attach`, `work` | +| Nested aliases | 4 | `agent send-message` → `message send` | +| Singular/plural confusion | 1 | `agent` vs `agents` | +| Unclear grouping | 5 | `logs`, `refresh`, `status`, `claude`, `review` | + +## Proposed Solutions + +### Option A: Documentation-Only (Minimal Change) + +**Change**: Improve help text and docs, no code changes. + +**Approach**: +1. Rewrite `--help` to show "Getting Started" section first +2. Group commands visually in help output +3. Add `multiclaude quickstart` command that shows common workflows +4. Update COMMANDS.md with clearer structure + +**Pros**: No breaking changes, fast to implement +**Cons**: Still confusing command surface, doesn't fix root cause + +### Option B: Deprecation Warnings (Medium Change) + +**Change**: Add deprecation warnings to aliases, document preferred commands. + +**Approach**: +1. Aliases print `DEPRECATED: Use 'multiclaude repo init' instead` +2. Hide aliases from `--help` output (still work, just not shown) +3. Document migration path in COMMANDS.md +4. Remove aliases in v2.0 + +**Pros**: Gradual migration, preserves backward compat +**Cons**: Two releases needed, some user friction + +### Option C: Restructure Verbs (Breaking Change) + +**Change**: Consolidate commands under clear noun groups. + +**Proposed structure**: +``` +multiclaude +├── daemon (start, stop, status, logs) +├── repo (init, list, rm, use, config, history, hibernate) +├── agent (create, list, rm, attach, restart, complete) ← merges worker+workspace +├── message (send, list, read, ack) +├── logs (view, list, search, clean) +├── status ← comprehensive overview +├── refresh ← sync all worktrees +├── cleanup ← maintenance +├── repair ← maintenance +├── version +├── help ← enhanced help +``` + +**Key changes**: +- Merge `worker`, `workspace`, `agents` under `agent` +- Remove all top-level aliases +- `agent create "task"` replaces `worker create` +- `agent create --workspace` replaces `workspace add` + +**Pros**: Clean, learnable, consistent +**Cons**: Breaking change, migration required + +### Option D: Hybrid (Recommended) + +**Change**: Implement Option B now, plan Option C for v2.0. + +**Phase 1 (Now)**: +1. Hide aliases from `--help` (still work) +2. Group help output by category +3. Add `multiclaude guide` command with interactive walkthrough +4. Rename `agents` → `templates` (avoids `agent`/`agents` confusion) + +**Phase 2 (v2.0)**: +1. Remove deprecated aliases +2. Optionally merge `worker`/`workspace` under `agent` + +## Recommended Immediate Actions + +### 1. Improve Help Output + +Current: +``` +Subcommands: + repair Repair state after crash + claude Restart Claude in current agent context + ... +``` + +Proposed: +``` +Multiclaude - orchestrate multiple Claude Code agents + +QUICK START: + multiclaude repo init Initialize a repository + multiclaude worker "task" Create a worker for a task + multiclaude status See what's running + +DAEMON: + daemon start/stop/status/logs Manage background process + +REPOSITORIES: + repo init/list/rm/use/history Track and manage repos + +AGENTS: + worker create/list/rm Task-focused workers + workspace add/list/rm/connect Persistent workspaces + agent attach/restart/complete Agent operations + +COMMUNICATION: + message send/list/read/ack Inter-agent messaging + +MAINTENANCE: + cleanup, repair, refresh Fix and sync state + logs, config, diagnostics Inspect and configure + +Run 'multiclaude --help' for details. +``` + +### 2. Add `guide` Command + +```bash +$ multiclaude guide + +Welcome to multiclaude! Here's how to get started: + +1. INITIALIZE A REPO + multiclaude repo init https://github.com/you/repo + +2. START THE DAEMON + multiclaude start + +3. CREATE A WORKER + multiclaude worker "Fix the login bug" + +4. WATCH IT WORK + multiclaude agent attach + +Need more? See: multiclaude docs +``` + +### 3. Hide Aliases from Help + +In `cli.go`, add a `Hidden` field to Command: + +```go +type Command struct { + Name string + Description string + Hidden bool // Don't show in --help + ... +} + +// Mark aliases as hidden +c.rootCmd.Subcommands["init"] = repoCmd.Subcommands["init"] +c.rootCmd.Subcommands["init"].Hidden = true +``` + +### 4. Rename `agents` → `templates` + +The current naming creates confusion: +- `agent attach` - operate on running agent +- `agents list` - list agent definitions (templates) + +Rename to: +- `templates list` - list agent templates +- `templates spawn` - spawn from template +- `templates reset` - reset to defaults + +## Migration Path + +| Current | Deprecated In | Removed In | Replacement | +|---------|---------------|------------|-------------| +| `multiclaude init` | v1.x | v2.0 | `multiclaude repo init` | +| `multiclaude list` | v1.x | v2.0 | `multiclaude repo list` | +| `multiclaude start` | v1.x | v2.0 | `multiclaude daemon start` | +| `multiclaude attach` | v1.x | v2.0 | `multiclaude agent attach` | +| `multiclaude work` | v1.x | v2.0 | `multiclaude worker` | +| `multiclaude history` | v1.x | v2.0 | `multiclaude repo history` | +| `multiclaude agents` | v1.x | v2.0 | `multiclaude templates` | + +## Implementation Checklist + +- [ ] Add `Hidden` field to Command struct +- [ ] Mark aliases as hidden +- [ ] Restructure help output with categories +- [ ] Add `guide` command +- [ ] Rename `agents` → `templates` +- [ ] Update COMMANDS.md +- [ ] Update embedded prompts +- [ ] Add deprecation warnings to aliases +- [ ] Update tests + +## Questions for Review + +1. **Keep `start` alias?** It's the most commonly used shortcut. +2. **Merge worker/workspace?** They're conceptually similar (agent instances). +3. **Add `quickstart` or `guide`?** Which name is clearer? +4. **Timeline for v2.0?** When do we remove deprecated aliases? + +--- + +*Generated by cool-wolf worker analyzing CLI structure.* diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 1e886067..7b08c633 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -1,13 +1,22 @@ # Commands Reference -Everything you can tell multiclaude to do. +Everything you can tell multiclaude to do. Run `multiclaude` with no arguments for a quick reference. + +## Quick Start + +```bash +multiclaude repo init # Track a repository +multiclaude start # Start the daemon (alias for daemon start) +multiclaude worker "task" # Create a worker for a task +multiclaude status # See what's running +``` ## Daemon The daemon is the brain. Start it, and agents come alive. ```bash -multiclaude start # Wake up +multiclaude daemon start # Wake up multiclaude daemon stop # Go to sleep multiclaude daemon status # You alive? multiclaude daemon logs -f # What are you thinking? diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 3e06628b..645d1f4e 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "runtime/debug" + "sort" "strconv" "strings" "time" @@ -78,6 +79,8 @@ type Command struct { Usage string Run func(args []string) error Subcommands map[string]*Command + Hidden bool // Don't show in --help (for aliases) + Category string // For grouping in help output } // CLI manages the command-line interface @@ -273,21 +276,77 @@ func (c *CLI) executeCommand(cmd *Command, args []string) error { func (c *CLI) showHelp() error { fmt.Println("multiclaude - repo-centric orchestrator for Claude Code") fmt.Println() - fmt.Println("Usage: multiclaude [options]") + fmt.Println("QUICK START:") + fmt.Println(" repo init Track a GitHub repository") + fmt.Println(" start Start the daemon") + fmt.Println(" worker \"task\" Create a worker for a task") + fmt.Println(" status See what's running") fmt.Println() - fmt.Println("Commands:") + + // Define category order and labels + categories := []struct { + key string + label string + }{ + {"daemon", "DAEMON:"}, + {"repo", "REPOSITORIES:"}, + {"agent", "AGENTS:"}, + {"comm", "COMMUNICATION:"}, + {"maint", "MAINTENANCE:"}, + {"meta", "META:"}, + } + + // Group commands by category + byCategory := make(map[string][]*struct { + name string + cmd *Command + }) for name, cmd := range c.rootCmd.Subcommands { - fmt.Printf(" %-15s %s\n", name, cmd.Description) + if cmd.Hidden || strings.HasPrefix(name, "_") { + continue + } + cat := cmd.Category + if cat == "" { + cat = "meta" // Default category + } + byCategory[cat] = append(byCategory[cat], &struct { + name string + cmd *Command + }{name, cmd}) } - fmt.Println() - fmt.Println("Use 'multiclaude --help' for more information about a command.") + // Sort commands within each category + for _, cmds := range byCategory { + sort.Slice(cmds, func(i, j int) bool { + return cmds[i].name < cmds[j].name + }) + } + + // Print by category + for _, cat := range categories { + cmds := byCategory[cat.key] + if len(cmds) == 0 { + continue + } + fmt.Println(cat.label) + for _, item := range cmds { + fmt.Printf(" %-15s %s\n", item.name, item.cmd.Description) + } + fmt.Println() + } + + fmt.Println("Run 'multiclaude --help' for details.") return nil } // showCommandHelp shows help for a specific command func (c *CLI) showCommandHelp(cmd *Command) error { + // If this is the root command, use the categorized help + if cmd == c.rootCmd { + return c.showHelp() + } + fmt.Printf("%s - %s\n", cmd.Name, cmd.Description) fmt.Println() if cmd.Usage != "" { @@ -298,8 +357,8 @@ func (c *CLI) showCommandHelp(cmd *Command) error { if len(cmd.Subcommands) > 0 { fmt.Println("Subcommands:") for name, subcmd := range cmd.Subcommands { - // Skip internal commands (prefixed with _) - if strings.HasPrefix(name, "_") { + // Skip internal commands (prefixed with _) and hidden commands + if strings.HasPrefix(name, "_") || subcmd.Hidden { continue } fmt.Printf(" %-15s %s\n", name, subcmd.Description) @@ -319,6 +378,8 @@ func (c *CLI) registerCommands() { Description: "Start the daemon (alias for 'daemon start')", Usage: "multiclaude start", Run: c.startDaemon, + Hidden: true, // Alias - prefer 'daemon start' + Category: "daemon", } // Root-level status command - comprehensive system overview @@ -327,12 +388,14 @@ func (c *CLI) registerCommands() { Description: "Show system status overview", Usage: "multiclaude status", Run: c.systemStatus, + Category: "maint", } daemonCmd := &Command{ Name: "daemon", Description: "Manage the multiclaude daemon", Subcommands: make(map[string]*Command), + Category: "daemon", } daemonCmd.Subcommands["start"] = &Command{ @@ -377,6 +440,7 @@ func (c *CLI) registerCommands() { Description: "Stop daemon and kill all multiclaude tmux sessions", Usage: "multiclaude stop-all [--clean] [--yes]", Run: c.stopAll, + Category: "daemon", } // Repository commands (repo subcommand) @@ -384,6 +448,7 @@ func (c *CLI) registerCommands() { Name: "repo", Description: "Manage repositories", Subcommands: make(map[string]*Command), + Category: "repo", } repoCmd.Subcommands["init"] = &Command{ @@ -444,10 +509,18 @@ func (c *CLI) registerCommands() { c.rootCmd.Subcommands["repo"] = repoCmd - // Backward compatibility aliases for root-level repo commands - c.rootCmd.Subcommands["init"] = repoCmd.Subcommands["init"] - c.rootCmd.Subcommands["list"] = repoCmd.Subcommands["list"] - c.rootCmd.Subcommands["history"] = repoCmd.Subcommands["history"] + // Backward compatibility aliases for root-level repo commands (hidden) + initAlias := *repoCmd.Subcommands["init"] + initAlias.Hidden = true + c.rootCmd.Subcommands["init"] = &initAlias + + listAlias := *repoCmd.Subcommands["list"] + listAlias.Hidden = true + c.rootCmd.Subcommands["list"] = &listAlias + + historyAlias := *repoCmd.Subcommands["history"] + historyAlias.Hidden = true + c.rootCmd.Subcommands["history"] = &historyAlias // Worker commands workerCmd := &Command{ @@ -455,6 +528,7 @@ func (c *CLI) registerCommands() { Description: "Manage worker agents", Usage: "multiclaude worker [] [--repo ] [--branch ] [--push-to ]", Subcommands: make(map[string]*Command), + Category: "agent", } workerCmd.Run = c.createWorker // Default action for 'worker' command (same as 'worker create') @@ -482,8 +556,10 @@ func (c *CLI) registerCommands() { c.rootCmd.Subcommands["worker"] = workerCmd - // 'work' is an alias for 'worker' (backward compatibility) - c.rootCmd.Subcommands["work"] = workerCmd + // 'work' is an alias for 'worker' (backward compatibility, hidden) + workAlias := *workerCmd + workAlias.Hidden = true + c.rootCmd.Subcommands["work"] = &workAlias // Workspace commands workspaceCmd := &Command{ @@ -491,6 +567,7 @@ func (c *CLI) registerCommands() { Description: "Manage workspaces", Usage: "multiclaude workspace []", Subcommands: make(map[string]*Command), + Category: "agent", } workspaceCmd.Run = c.workspaceDefault // Default action: list or connect @@ -530,6 +607,7 @@ func (c *CLI) registerCommands() { Name: "agent", Description: "Agent communication commands", Subcommands: make(map[string]*Command), + Category: "agent", } // Legacy message commands (aliases for backward compatibility) @@ -591,6 +669,7 @@ func (c *CLI) registerCommands() { Name: "message", Description: "Manage inter-agent messages", Subcommands: make(map[string]*Command), + Category: "comm", } messageCmd.Subcommands["send"] = &Command{ @@ -623,8 +702,10 @@ func (c *CLI) registerCommands() { c.rootCmd.Subcommands["message"] = messageCmd - // 'attach' is an alias for 'agent attach' (backward compatibility) - c.rootCmd.Subcommands["attach"] = agentCmd.Subcommands["attach"] + // 'attach' is an alias for 'agent attach' (backward compatibility, hidden) + attachAlias := *agentCmd.Subcommands["attach"] + attachAlias.Hidden = true + c.rootCmd.Subcommands["attach"] = &attachAlias // Maintenance commands c.rootCmd.Subcommands["cleanup"] = &Command{ @@ -632,6 +713,7 @@ func (c *CLI) registerCommands() { Description: "Clean up orphaned resources", Usage: "multiclaude cleanup [--dry-run] [--verbose] [--merged]", Run: c.cleanup, + Category: "maint", } c.rootCmd.Subcommands["repair"] = &Command{ @@ -639,6 +721,7 @@ func (c *CLI) registerCommands() { Description: "Repair state after crash", Usage: "multiclaude repair [--verbose]", Run: c.repair, + Category: "maint", } c.rootCmd.Subcommands["refresh"] = &Command{ @@ -646,6 +729,7 @@ func (c *CLI) registerCommands() { Description: "Sync agent worktrees with main branch", Usage: "multiclaude refresh", Run: c.refresh, + Category: "maint", } // Claude restart command - for resuming Claude after exit @@ -654,6 +738,7 @@ func (c *CLI) registerCommands() { Description: "Restart Claude in current agent context", Usage: "multiclaude claude", Run: c.restartClaude, + Category: "agent", } // Debug command @@ -662,6 +747,7 @@ func (c *CLI) registerCommands() { Description: "Show generated CLI documentation", Usage: "multiclaude docs", Run: c.showDocs, + Category: "meta", } // Review command @@ -670,6 +756,7 @@ func (c *CLI) registerCommands() { Description: "Spawn a review agent for a PR", Usage: "multiclaude review ", Run: c.reviewPR, + Category: "agent", } // Logs commands @@ -678,6 +765,7 @@ func (c *CLI) registerCommands() { Description: "View and manage agent output logs", Usage: "multiclaude logs [] [-f|--follow]", Subcommands: make(map[string]*Command), + Category: "maint", } logsCmd.Run = c.viewLogs // Default action: view logs for an agent @@ -711,6 +799,7 @@ func (c *CLI) registerCommands() { Description: "View or modify repository configuration", Usage: "multiclaude config [repo] [--mq-enabled=true|false] [--mq-track=all|author|assigned] [--ps-enabled=true|false] [--ps-track=all|author|assigned]", Run: c.configRepo, + Category: "repo", } // Bug report command @@ -719,6 +808,7 @@ func (c *CLI) registerCommands() { Description: "Generate a diagnostic bug report", Usage: "multiclaude bug [--output ] [--verbose] [description]", Run: c.bugReport, + Category: "meta", } // Diagnostics command @@ -727,6 +817,7 @@ func (c *CLI) registerCommands() { Description: "Show system diagnostics in machine-readable format", Usage: "multiclaude diagnostics [--json] [--output ]", Run: c.diagnostics, + Category: "meta", } // Version command @@ -735,6 +826,7 @@ func (c *CLI) registerCommands() { Description: "Show version information", Usage: "multiclaude version [--json]", Run: c.versionCommand, + Category: "meta", } // Agents command - for managing agent definitions @@ -742,6 +834,7 @@ func (c *CLI) registerCommands() { Name: "agents", Description: "Manage agent definitions", Subcommands: make(map[string]*Command), + Category: "agent", } agentsCmd.Subcommands["list"] = &Command{