@@ -375,6 +375,15 @@ enum LocalHelpTopic {
375375 Sandbox ,
376376 Doctor ,
377377 Acp ,
378+ // #141: extend the local-help pattern to every subcommand so
379+ // `claw <subcommand> --help` has one consistent contract.
380+ Init ,
381+ State ,
382+ Export ,
383+ Version ,
384+ SystemPrompt ,
385+ DumpManifests ,
386+ BootstrapPlan ,
378387}
379388
380389#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
@@ -421,10 +430,6 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
421430 && matches ! (
422431 rest[ 0 ] . as_str( ) ,
423432 "prompt"
424- | "version"
425- | "state"
426- | "init"
427- | "export"
428433 | "commit"
429434 | "pr"
430435 | "issue"
@@ -434,8 +439,10 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
434439 // the arg to the API (e.g. `claw prompt --help`) should show
435440 // top-level help instead. Subcommands that consume their own
436441 // args (agents, mcp, plugins, skills) and local help-topic
437- // subcommands (status, sandbox, doctor) must NOT be intercepted
438- // here — they handle --help in their own dispatch paths.
442+ // subcommands (status, sandbox, doctor, init, state, export,
443+ // version, system-prompt, dump-manifests, bootstrap-plan) must
444+ // NOT be intercepted here — they handle --help in their own
445+ // dispatch paths via parse_local_help_action(). See #141.
439446 wants_help = true ;
440447 index += 1 ;
441448 }
@@ -746,6 +753,17 @@ fn parse_local_help_action(rest: &[String]) -> Option<Result<CliAction, String>>
746753 "sandbox" => LocalHelpTopic :: Sandbox ,
747754 "doctor" => LocalHelpTopic :: Doctor ,
748755 "acp" => LocalHelpTopic :: Acp ,
756+ // #141: add the subcommands that were previously falling back
757+ // to global help (init/state/export/version) or erroring out
758+ // (system-prompt/dump-manifests) or printing their primary
759+ // output instead of help text (bootstrap-plan).
760+ "init" => LocalHelpTopic :: Init ,
761+ "state" => LocalHelpTopic :: State ,
762+ "export" => LocalHelpTopic :: Export ,
763+ "version" => LocalHelpTopic :: Version ,
764+ "system-prompt" => LocalHelpTopic :: SystemPrompt ,
765+ "dump-manifests" => LocalHelpTopic :: DumpManifests ,
766+ "bootstrap-plan" => LocalHelpTopic :: BootstrapPlan ,
749767 _ => return None ,
750768 } ;
751769 Some ( Ok ( CliAction :: HelpTopic ( topic) ) )
@@ -5369,6 +5387,56 @@ fn render_help_topic(topic: LocalHelpTopic) -> String {
53695387 Formats text (default), json
53705388 Related ROADMAP #64a (discoverability) · ROADMAP #76 (real ACP support) · claw --help"
53715389 . to_string ( ) ,
5390+ LocalHelpTopic :: Init => "Init
5391+ Usage claw init [--output-format <format>]
5392+ Purpose create .claw/, .claw.json, .gitignore, and CLAUDE.md in the current project
5393+ Output list of created vs. skipped files (idempotent: safe to re-run)
5394+ Formats text (default), json
5395+ Related claw status · claw doctor"
5396+ . to_string ( ) ,
5397+ LocalHelpTopic :: State => "State
5398+ Usage claw state [--output-format <format>]
5399+ Purpose read the worker state file written by the interactive REPL
5400+ Output worker id, model, permissions, session reference (text or json)
5401+ Formats text (default), json
5402+ Prerequisite run `claw` interactively or `claw prompt <text>` to produce worker state first
5403+ Related ROADMAP #139 (worker-concept discoverability) · claw status"
5404+ . to_string ( ) ,
5405+ LocalHelpTopic :: Export => "Export
5406+ Usage claw export [--session <id|latest>] [--output <path>] [--output-format <format>]
5407+ Purpose serialize a managed session to JSON for review, transfer, or archival
5408+ Defaults --session latest (most recent managed session in .claw/sessions/)
5409+ Formats text (default), json
5410+ Related /session list · claw --resume latest"
5411+ . to_string ( ) ,
5412+ LocalHelpTopic :: Version => "Version
5413+ Usage claw version [--output-format <format>]
5414+ Aliases claw --version · claw -V
5415+ Purpose print the claw CLI version and build metadata
5416+ Formats text (default), json
5417+ Related claw doctor (full build/auth/config diagnostic)"
5418+ . to_string ( ) ,
5419+ LocalHelpTopic :: SystemPrompt => "System Prompt
5420+ Usage claw system-prompt [--cwd <path>] [--date YYYY-MM-DD] [--output-format <format>]
5421+ Purpose render the resolved system prompt that `claw` would send for the given cwd + date
5422+ Options --cwd overrides the workspace dir · --date injects a deterministic date stamp
5423+ Formats text (default), json
5424+ Related claw doctor · claw dump-manifests"
5425+ . to_string ( ) ,
5426+ LocalHelpTopic :: DumpManifests => "Dump Manifests
5427+ Usage claw dump-manifests [--manifests-dir <path>] [--output-format <format>]
5428+ Purpose emit every skill/agent/tool manifest the resolver would load for the current cwd
5429+ Options --manifests-dir scopes discovery to a specific directory
5430+ Formats text (default), json
5431+ Related claw skills · claw agents · claw doctor"
5432+ . to_string ( ) ,
5433+ LocalHelpTopic :: BootstrapPlan => "Bootstrap Plan
5434+ Usage claw bootstrap-plan [--output-format <format>]
5435+ Purpose list the ordered startup phases the CLI would execute before dispatch
5436+ Output phase names (text) or structured phase list (json) — primary output is the plan itself
5437+ Formats text (default), json
5438+ Related claw doctor · claw status"
5439+ . to_string ( ) ,
53725440 }
53735441}
53745442
@@ -8519,7 +8587,7 @@ mod tests {
85198587 parse_git_status_branch, parse_git_status_metadata_for, parse_git_workspace_summary,
85208588 parse_history_count, permission_policy, print_help_to, push_output_block,
85218589 render_config_report, render_diff_report, render_diff_report_for, render_memory_report,
8522- render_prompt_history_report, render_repl_help, render_resume_usage,
8590+ render_help_topic , render_prompt_history_report, render_repl_help, render_resume_usage,
85238591 render_session_markdown, resolve_model_alias, resolve_model_alias_with_config,
85248592 resolve_repl_model, resolve_session_reference, response_to_events,
85258593 resume_supported_slash_commands, run_resume_command, short_tool_id,
@@ -9487,6 +9555,50 @@ mod tests {
94879555 ) ;
94889556 }
94899557
9558+ #[ test]
9559+ fn subcommand_help_flag_has_one_contract_across_all_subcommands_141 ( ) {
9560+ // #141: every documented subcommand must resolve `<subcommand> --help`
9561+ // to a subcommand-specific help topic, never to global help, never to
9562+ // an "unknown option" error, never to the subcommand's primary output.
9563+ let cases: & [ ( & str , LocalHelpTopic ) ] = & [
9564+ ( "status" , LocalHelpTopic :: Status ) ,
9565+ ( "sandbox" , LocalHelpTopic :: Sandbox ) ,
9566+ ( "doctor" , LocalHelpTopic :: Doctor ) ,
9567+ ( "acp" , LocalHelpTopic :: Acp ) ,
9568+ ( "init" , LocalHelpTopic :: Init ) ,
9569+ ( "state" , LocalHelpTopic :: State ) ,
9570+ ( "export" , LocalHelpTopic :: Export ) ,
9571+ ( "version" , LocalHelpTopic :: Version ) ,
9572+ ( "system-prompt" , LocalHelpTopic :: SystemPrompt ) ,
9573+ ( "dump-manifests" , LocalHelpTopic :: DumpManifests ) ,
9574+ ( "bootstrap-plan" , LocalHelpTopic :: BootstrapPlan ) ,
9575+ ] ;
9576+ for ( subcommand, expected_topic) in cases {
9577+ for flag in [ "--help" , "-h" ] {
9578+ let parsed = parse_args ( & [ subcommand. to_string ( ) , flag. to_string ( ) ] )
9579+ . unwrap_or_else ( |error| {
9580+ panic ! ( "`{subcommand} {flag}` should parse as help but errored: {error}" )
9581+ } ) ;
9582+ assert_eq ! (
9583+ parsed,
9584+ CliAction :: HelpTopic ( * expected_topic) ,
9585+ "`{subcommand} {flag}` should resolve to HelpTopic({expected_topic:?})"
9586+ ) ;
9587+ }
9588+ // And the rendered help must actually mention the subcommand name
9589+ // (or its canonical title) so users know they got the right help.
9590+ let rendered = render_help_topic ( * expected_topic) ;
9591+ assert ! (
9592+ !rendered. is_empty( ) ,
9593+ "{subcommand} help text should not be empty"
9594+ ) ;
9595+ assert ! (
9596+ rendered. contains( "Usage" ) ,
9597+ "{subcommand} help text should contain a Usage line"
9598+ ) ;
9599+ }
9600+ }
9601+
94909602 #[ test]
94919603 fn parses_single_word_command_aliases_without_falling_back_to_prompt_mode ( ) {
94929604 let _guard = env_lock ( ) ;
0 commit comments