Skip to content

Commit be18f85

Browse files
committed
Merge PR dlorenc#338: Token warnings in CLI help and status (resolved conflict with dlorenc#337)
2 parents 4181de2 + c8f09e3 commit be18f85

2 files changed

Lines changed: 87 additions & 25 deletions

File tree

internal/cli/cli.go

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,14 @@ func IsDevVersion() bool {
7474

7575
// Command represents a CLI command
7676
type Command struct {
77-
Name string
78-
Description string
79-
Usage string
80-
Run func(args []string) error
81-
Subcommands map[string]*Command
82-
Hidden bool // Don't show in --help (for aliases)
83-
Category string // For grouping in help output
77+
Name string
78+
Description string
79+
LongDescription string // Detailed help text shown with --help
80+
Usage string
81+
Run func(args []string) error
82+
Subcommands map[string]*Command
83+
Hidden bool // Don't show in --help (for aliases)
84+
Category string // For grouping in help output
8485
}
8586

8687
// CLI manages the command-line interface
@@ -354,6 +355,11 @@ func (c *CLI) showCommandHelp(cmd *Command) error {
354355
fmt.Println()
355356
}
356357

358+
if cmd.LongDescription != "" {
359+
fmt.Println(cmd.LongDescription)
360+
fmt.Println()
361+
}
362+
357363
if len(cmd.Subcommands) > 0 {
358364
fmt.Println("Subcommands:")
359365
for name, subcmd := range cmd.Subcommands {
@@ -503,8 +509,27 @@ func (c *CLI) registerCommands() {
503509
repoCmd.Subcommands["hibernate"] = &Command{
504510
Name: "hibernate",
505511
Description: "Hibernate a repository, archiving uncommitted changes",
506-
Usage: "multiclaude repo hibernate [--repo <repo>] [--all] [--yes]",
507-
Run: c.hibernateRepo,
512+
LongDescription: `Hibernating a repository STOPS ALL TOKEN CONSUMPTION by killing agents.
513+
514+
Running agents (supervisor, merge-queue, workspace, workers) continuously
515+
consume API tokens even when idle. Use hibernate to pause billing.
516+
517+
Options:
518+
--repo <name> Repository to hibernate (auto-detected if in worktree)
519+
--all Also hibernate persistent agents (supervisor, workspace)
520+
--yes Skip confirmation prompt
521+
522+
By default, only workers and review agents are hibernated. Use --all to
523+
stop ALL agents including supervisor, merge-queue, and workspace.
524+
525+
Uncommitted changes are archived to ~/.multiclaude/archives/<repo>/ and
526+
can be recovered later.
527+
528+
Example:
529+
multiclaude repo hibernate --all # Stop all agents, stop token usage
530+
multiclaude repo hibernate # Stop workers only, core agents remain`,
531+
Usage: "multiclaude repo hibernate [--repo <repo>] [--all] [--yes]",
532+
Run: c.hibernateRepo,
508533
}
509534

510535
c.rootCmd.Subcommands["repo"] = repoCmd
@@ -984,6 +1009,9 @@ func (c *CLI) systemStatus(args []string) error {
9841009
fmt.Printf(" Repos: %d\n", len(repos))
9851010
fmt.Println()
9861011

1012+
// Track total active agents for token warning
1013+
totalActiveAgents := 0
1014+
9871015
// Show each repo with agents
9881016
for _, repo := range repos {
9891017
repoMap, ok := repo.(map[string]interface{})
@@ -996,10 +1024,8 @@ func (c *CLI) systemStatus(args []string) error {
9961024
if v, ok := repoMap["total_agents"].(float64); ok {
9971025
totalAgents = int(v)
9981026
}
999-
workerCount := 0
1000-
if v, ok := repoMap["worker_count"].(float64); ok {
1001-
workerCount = int(v)
1002-
}
1027+
totalActiveAgents += totalAgents
1028+
10031029
sessionHealthy, _ := repoMap["session_healthy"].(bool)
10041030

10051031
// Repo line
@@ -1009,12 +1035,31 @@ func (c *CLI) systemStatus(args []string) error {
10091035
}
10101036
fmt.Printf(" %s %s\n", repoStatus, format.Bold.Sprint(name))
10111037

1012-
// Agent summary
1013-
coreAgents := totalAgents - workerCount
1014-
if coreAgents < 0 {
1015-
coreAgents = 0
1038+
// Show core agents by name and type
1039+
if coreAgents, ok := repoMap["core_agents"].([]interface{}); ok && len(coreAgents) > 0 {
1040+
var coreNames []string
1041+
for _, ca := range coreAgents {
1042+
if caMap, ok := ca.(map[string]interface{}); ok {
1043+
agentName, _ := caMap["name"].(string)
1044+
agentType, _ := caMap["type"].(string)
1045+
coreNames = append(coreNames, fmt.Sprintf("%s (%s)", agentName, agentType))
1046+
}
1047+
}
1048+
fmt.Printf(" Core: %s\n", strings.Join(coreNames, ", "))
1049+
}
1050+
1051+
// Show workers
1052+
if workerNames, ok := repoMap["worker_names"].([]interface{}); ok && len(workerNames) > 0 {
1053+
var names []string
1054+
for _, wn := range workerNames {
1055+
if name, ok := wn.(string); ok {
1056+
names = append(names, name)
1057+
}
1058+
}
1059+
fmt.Printf(" Workers: %s\n", strings.Join(names, ", "))
1060+
} else {
1061+
fmt.Printf(" Workers: none\n")
10161062
}
1017-
fmt.Printf(" Agents: %d core, %d workers\n", coreAgents, workerCount)
10181063

10191064
// Show fork info if applicable
10201065
if isFork, _ := repoMap["is_fork"].(bool); isFork {
@@ -1027,6 +1072,16 @@ func (c *CLI) systemStatus(args []string) error {
10271072
}
10281073

10291074
fmt.Println()
1075+
1076+
// Token consumption warning
1077+
if totalActiveAgents > 0 {
1078+
fmt.Printf(" %s %d active agent(s) consuming API tokens\n",
1079+
format.Yellow.Sprint("⚠"),
1080+
totalActiveAgents)
1081+
format.Dimmed(" Stop token usage: multiclaude repo hibernate --all")
1082+
fmt.Println()
1083+
}
1084+
10301085
format.Dimmed("Details: multiclaude repo list | multiclaude worker list")
10311086
return nil
10321087
}

internal/daemon/daemon.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -728,12 +728,17 @@ func (d *Daemon) handleListRepos(req socket.Request) socket.Response {
728728
// Return detailed repo info
729729
repoDetails := make([]map[string]interface{}, 0, len(repos))
730730
for repoName, repo := range repos {
731-
// Count agents by type
732-
workerCount := 0
733-
totalAgents := len(repo.Agents)
734-
for _, agent := range repo.Agents {
731+
// Group agents by type
732+
workerNames := []string{}
733+
coreAgents := []map[string]string{} // name -> type for core agents
734+
for agentName, agent := range repo.Agents {
735735
if agent.Type == state.AgentTypeWorker {
736-
workerCount++
736+
workerNames = append(workerNames, agentName)
737+
} else {
738+
coreAgents = append(coreAgents, map[string]string{
739+
"name": agentName,
740+
"type": string(agent.Type),
741+
})
737742
}
738743
}
739744

@@ -753,8 +758,10 @@ func (d *Daemon) handleListRepos(req socket.Request) socket.Response {
753758
"name": repoName,
754759
"github_url": repo.GithubURL,
755760
"tmux_session": repo.TmuxSession,
756-
"total_agents": totalAgents,
757-
"worker_count": workerCount,
761+
"total_agents": len(repo.Agents),
762+
"worker_count": len(workerNames),
763+
"worker_names": workerNames,
764+
"core_agents": coreAgents,
758765
"session_healthy": sessionHealthy,
759766
"is_fork": repo.ForkConfig.IsFork,
760767
"upstream_owner": repo.ForkConfig.UpstreamOwner,

0 commit comments

Comments
 (0)