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{