Skip to content

Commit 2311c87

Browse files
gtrrz-victorclaude
andcommitted
feat(cli): group root help by user journey
Root help rendered 26 visible commands as one flat alphabetical list. Add cobra Groups so help shows Entire Setup, Sessions & Checkpoints, Account, and Control Plane sections; version/labs/agent-help stay in Additional Commands on purpose. Pure help presentation — no command paths, flags, or behavior change. agent-help ignores GroupID. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 01KXG9WXCXE55FFMK3564NA5R7
1 parent f4a0b3d commit 2311c87

2 files changed

Lines changed: 119 additions & 25 deletions

File tree

cmd/entire/cli/root.go

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ Environment Variables:
3131
TUI elements, which works better with screen readers.
3232
`
3333

34+
// Help groups for the root command. AddGroup order is display order.
35+
// Visible commands without a GroupID render under "Additional Commands"
36+
// (version, labs, agent-help, help) — that placement is intentional.
37+
const (
38+
groupSetup = "setup"
39+
groupSessions = "sessions"
40+
groupAccount = "account"
41+
groupControlPlane = "controlplane"
42+
)
43+
44+
// inGroup assigns a help group to a command at registration time so all
45+
// grouping stays visible in NewRootCmd rather than spread across constructors.
46+
func inGroup(c *cobra.Command, groupID string) *cobra.Command {
47+
c.GroupID = groupID
48+
return c
49+
}
50+
3451
func NewRootCmd() *cobra.Command {
3552
cmd := &cobra.Command{
3653
Use: "entire",
@@ -84,39 +101,47 @@ func NewRootCmd() *cobra.Command {
84101
},
85102
}
86103

104+
// Help groups; AddGroup order is display order in `entire --help`.
105+
cmd.AddGroup(
106+
&cobra.Group{ID: groupSetup, Title: "Entire Setup:"},
107+
&cobra.Group{ID: groupSessions, Title: "Sessions & Checkpoints:"},
108+
&cobra.Group{ID: groupAccount, Title: "Account:"},
109+
&cobra.Group{ID: groupControlPlane, Title: "Control Plane:"},
110+
)
111+
87112
// Noun groups (canonical homes for subcommands).
88-
cmd.AddCommand(newSessionsCmd()) // 'session' (with 'sessions' as Cobra alias)
89-
cmd.AddCommand(newCheckpointGroupCmd()) // 'checkpoint' / 'cp' / 'checkpoints'
90-
cmd.AddCommand(newTokensGroupCmd()) // 'tokens'
91-
cmd.AddCommand(newAgentGroupCmd()) // 'agent'
92-
cmd.AddCommand(newAuthCmd()) // 'auth'
93-
cmd.AddCommand(newDoctorCmd()) // 'doctor' (group: trace/logs/bundle)
94-
cmd.AddCommand(newLabsCmd()) // 'labs' (experimental workflow discovery)
95-
cmd.AddCommand(newPluginGroupCmd()) // 'plugin' (managed install/list/remove)
96-
cmd.AddCommand(newImportCmd()) // 'import' (hidden; import pre-existing agent history)
97-
cmd.AddCommand(newOrgCmd()) // 'org' — control-plane org management
98-
cmd.AddCommand(newProjectCmd()) // 'project' — control-plane project management
99-
cmd.AddCommand(newRepoCmd()) // 'repo' — control-plane repo lifecycle
100-
cmd.AddCommand(newGrantCmd()) // 'grant' — control-plane access grants
113+
cmd.AddCommand(inGroup(newSessionsCmd(), groupSessions)) // 'session' (with 'sessions' as Cobra alias)
114+
cmd.AddCommand(inGroup(newCheckpointGroupCmd(), groupSessions)) // 'checkpoint' / 'cp' / 'checkpoints'
115+
cmd.AddCommand(newTokensGroupCmd()) // 'tokens'
116+
cmd.AddCommand(inGroup(newAgentGroupCmd(), groupSetup)) // 'agent'
117+
cmd.AddCommand(inGroup(newAuthCmd(), groupAccount)) // 'auth'
118+
cmd.AddCommand(inGroup(newDoctorCmd(), groupSetup)) // 'doctor' (group: trace/logs/bundle)
119+
cmd.AddCommand(newLabsCmd()) // 'labs' (experimental workflow discovery)
120+
cmd.AddCommand(inGroup(newPluginGroupCmd(), groupSetup)) // 'plugin' (managed install/list/remove)
121+
cmd.AddCommand(newImportCmd()) // 'import' (hidden; import pre-existing agent history)
122+
cmd.AddCommand(inGroup(newOrgCmd(), groupControlPlane)) // 'org' — control-plane org management
123+
cmd.AddCommand(inGroup(newProjectCmd(), groupControlPlane)) // 'project' — control-plane project management
124+
cmd.AddCommand(inGroup(newRepoCmd(), groupControlPlane)) // 'repo' — control-plane repo lifecycle
125+
cmd.AddCommand(inGroup(newGrantCmd(), groupControlPlane)) // 'grant' — control-plane access grants
101126

102127
// Top-level lifecycle and standalone commands.
103128
cmd.AddCommand(cliReview.NewCommand(buildReviewDeps())) // `review`; hidden during maturation
104129
cmd.AddCommand(investigate.NewCommand(buildInvestigateDeps())) // hidden during maturation; runs a multi-agent investigation
105-
cmd.AddCommand(newCleanCmd())
106-
cmd.AddCommand(newSetupCmd()) // 'configure' — non-agent settings; agent CRUD lives under 'agent'
107-
cmd.AddCommand(newEnableCmd())
108-
cmd.AddCommand(newDisableCmd())
109-
cmd.AddCommand(newStatusCmd())
130+
cmd.AddCommand(inGroup(newCleanCmd(), groupSetup))
131+
cmd.AddCommand(inGroup(newSetupCmd(), groupSetup)) // 'configure' — non-agent settings; agent CRUD lives under 'agent'
132+
cmd.AddCommand(inGroup(newEnableCmd(), groupSetup))
133+
cmd.AddCommand(inGroup(newDisableCmd(), groupSetup))
134+
cmd.AddCommand(inGroup(newStatusCmd(), groupSetup))
110135
cmd.AddCommand(newBlameCmd())
111136
cmd.AddCommand(newWhyCmd())
112-
cmd.AddCommand(newLoginCmd())
113-
cmd.AddCommand(newLogoutCmd())
137+
cmd.AddCommand(inGroup(newLoginCmd(), groupAccount))
138+
cmd.AddCommand(inGroup(newLogoutCmd(), groupAccount))
114139
cmd.AddCommand(newVersionCmd())
115-
cmd.AddCommand(newDispatchCmd())
116-
cmd.AddCommand(newActivityCmd())
117-
cmd.AddCommand(newRecapCmd())
118-
cmd.AddCommand(newAPICmd()) // authenticated passthrough to core/cell APIs
119-
cmd.AddCommand(newAgentHelpCmd(cmd)) // visible: agents on transports without context injection discover it via `entire help`
140+
cmd.AddCommand(inGroup(newDispatchCmd(), groupSessions))
141+
cmd.AddCommand(inGroup(newActivityCmd(), groupSessions))
142+
cmd.AddCommand(inGroup(newRecapCmd(), groupSessions))
143+
cmd.AddCommand(inGroup(newAPICmd(), groupControlPlane)) // authenticated passthrough to core/cell APIs
144+
cmd.AddCommand(newAgentHelpCmd(cmd)) // visible: agents on transports without context injection discover it via `entire help`
120145

121146
// Hidden top-level shortcuts. Functional but print a deprecation hint.
122147
cmd.AddCommand(hideAsAlias(newResumeCmd(), "entire session resume"))

cmd/entire/cli/root_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,75 @@ func TestCheckpointPolicyCommandIsHiddenDuringDevelopment(t *testing.T) {
256256
}
257257
}
258258

259+
func TestRoot_VisibleCommandsAreGrouped(t *testing.T) {
260+
t.Parallel()
261+
262+
// Commands intentionally left out of any group; cobra renders them
263+
// under "Additional Commands".
264+
ungrouped := map[string]bool{
265+
"version": true,
266+
"labs": true,
267+
"agent-help": true,
268+
"help": true,
269+
"completion": true,
270+
}
271+
272+
wantGroups := map[string]string{
273+
"enable": groupSetup,
274+
"disable": groupSetup,
275+
"configure": groupSetup,
276+
"agent": groupSetup,
277+
"plugin": groupSetup,
278+
"status": groupSetup,
279+
"doctor": groupSetup,
280+
"clean": groupSetup,
281+
"session": groupSessions,
282+
"checkpoint": groupSessions,
283+
"recap": groupSessions,
284+
"activity": groupSessions,
285+
"dispatch": groupSessions,
286+
"login": groupAccount,
287+
"logout": groupAccount,
288+
"auth": groupAccount,
289+
"org": groupControlPlane,
290+
"project": groupControlPlane,
291+
"repo": groupControlPlane,
292+
"grant": groupControlPlane,
293+
"api": groupControlPlane,
294+
}
295+
296+
root := NewRootCmd()
297+
298+
registered := make(map[string]bool)
299+
for _, g := range root.Groups() {
300+
registered[g.ID] = true
301+
}
302+
303+
for _, c := range root.Commands() {
304+
if c.Hidden || c.Deprecated != "" {
305+
continue
306+
}
307+
name := c.Name()
308+
if ungrouped[name] {
309+
if c.GroupID != "" {
310+
t.Errorf("%q should stay ungrouped, got GroupID %q", name, c.GroupID)
311+
}
312+
continue
313+
}
314+
want, ok := wantGroups[name]
315+
if !ok {
316+
t.Errorf("visible command %q missing from group table; assign it a group or add it to the ungrouped allowlist", name)
317+
continue
318+
}
319+
if c.GroupID != want {
320+
t.Errorf("%q GroupID = %q, want %q", name, c.GroupID, want)
321+
}
322+
if !registered[want] {
323+
t.Errorf("group %q used by %q is not registered on root (cobra panics at Execute)", want, name)
324+
}
325+
}
326+
}
327+
259328
func containsString(values []string, want string) bool {
260329
for _, value := range values {
261330
if value == want {

0 commit comments

Comments
 (0)