mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-04-26 22:47:38 +08:00
feat: #141 unify claw <subcommand> --help contract across all 14 subcommands
Previously, `claw <subcommand> --help` had 5 different behaviors: - 7 subcommands returned subcommand-specific help (correct) - init/export/state/version silently fell back to global `claw --help` - system-prompt/dump-manifests errored with `unknown <cmd> option: --help` - bootstrap-plan printed its phase list instead of help text Changes: - Extend LocalHelpTopic enum with Init, State, Export, Version, SystemPrompt, DumpManifests, BootstrapPlan variants. - Extend parse_local_help_action() to resolve those 7 subcommands to their local help topic instead of falling through to the main dispatch. - Remove init/state/export/version from the explicit wants_help=true matcher so they reach parse_local_help_action() before being routed to global help. - Add render_help_topic() entries for the 7 new topics with consistent Usage/Purpose/Output/Formats/Related structure. - Add regression test subcommand_help_flag_has_one_contract_across_all_subcommands_141 asserting every documented subcommand + both --help and -h variants resolve to a HelpTopic with non-empty text that contains a Usage line. Verification: - All 14 subcommands now return subcommand-specific help (live dogfood). - Full workspace test green except pre-existing resume_latest flake. Closes ROADMAP #141.
This commit is contained in:
parent
2665ada94e
commit
7763ca3260
@ -375,6 +375,15 @@ enum LocalHelpTopic {
|
|||||||
Sandbox,
|
Sandbox,
|
||||||
Doctor,
|
Doctor,
|
||||||
Acp,
|
Acp,
|
||||||
|
// #141: extend the local-help pattern to every subcommand so
|
||||||
|
// `claw <subcommand> --help` has one consistent contract.
|
||||||
|
Init,
|
||||||
|
State,
|
||||||
|
Export,
|
||||||
|
Version,
|
||||||
|
SystemPrompt,
|
||||||
|
DumpManifests,
|
||||||
|
BootstrapPlan,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@ -421,10 +430,6 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
|||||||
&& matches!(
|
&& matches!(
|
||||||
rest[0].as_str(),
|
rest[0].as_str(),
|
||||||
"prompt"
|
"prompt"
|
||||||
| "version"
|
|
||||||
| "state"
|
|
||||||
| "init"
|
|
||||||
| "export"
|
|
||||||
| "commit"
|
| "commit"
|
||||||
| "pr"
|
| "pr"
|
||||||
| "issue"
|
| "issue"
|
||||||
@ -434,8 +439,10 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
|||||||
// the arg to the API (e.g. `claw prompt --help`) should show
|
// the arg to the API (e.g. `claw prompt --help`) should show
|
||||||
// top-level help instead. Subcommands that consume their own
|
// top-level help instead. Subcommands that consume their own
|
||||||
// args (agents, mcp, plugins, skills) and local help-topic
|
// args (agents, mcp, plugins, skills) and local help-topic
|
||||||
// subcommands (status, sandbox, doctor) must NOT be intercepted
|
// subcommands (status, sandbox, doctor, init, state, export,
|
||||||
// here — they handle --help in their own dispatch paths.
|
// version, system-prompt, dump-manifests, bootstrap-plan) must
|
||||||
|
// NOT be intercepted here — they handle --help in their own
|
||||||
|
// dispatch paths via parse_local_help_action(). See #141.
|
||||||
wants_help = true;
|
wants_help = true;
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
@ -746,6 +753,17 @@ fn parse_local_help_action(rest: &[String]) -> Option<Result<CliAction, String>>
|
|||||||
"sandbox" => LocalHelpTopic::Sandbox,
|
"sandbox" => LocalHelpTopic::Sandbox,
|
||||||
"doctor" => LocalHelpTopic::Doctor,
|
"doctor" => LocalHelpTopic::Doctor,
|
||||||
"acp" => LocalHelpTopic::Acp,
|
"acp" => LocalHelpTopic::Acp,
|
||||||
|
// #141: add the subcommands that were previously falling back
|
||||||
|
// to global help (init/state/export/version) or erroring out
|
||||||
|
// (system-prompt/dump-manifests) or printing their primary
|
||||||
|
// output instead of help text (bootstrap-plan).
|
||||||
|
"init" => LocalHelpTopic::Init,
|
||||||
|
"state" => LocalHelpTopic::State,
|
||||||
|
"export" => LocalHelpTopic::Export,
|
||||||
|
"version" => LocalHelpTopic::Version,
|
||||||
|
"system-prompt" => LocalHelpTopic::SystemPrompt,
|
||||||
|
"dump-manifests" => LocalHelpTopic::DumpManifests,
|
||||||
|
"bootstrap-plan" => LocalHelpTopic::BootstrapPlan,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(Ok(CliAction::HelpTopic(topic)))
|
Some(Ok(CliAction::HelpTopic(topic)))
|
||||||
@ -5369,6 +5387,56 @@ fn render_help_topic(topic: LocalHelpTopic) -> String {
|
|||||||
Formats text (default), json
|
Formats text (default), json
|
||||||
Related ROADMAP #64a (discoverability) · ROADMAP #76 (real ACP support) · claw --help"
|
Related ROADMAP #64a (discoverability) · ROADMAP #76 (real ACP support) · claw --help"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
LocalHelpTopic::Init => "Init
|
||||||
|
Usage claw init [--output-format <format>]
|
||||||
|
Purpose create .claw/, .claw.json, .gitignore, and CLAUDE.md in the current project
|
||||||
|
Output list of created vs. skipped files (idempotent: safe to re-run)
|
||||||
|
Formats text (default), json
|
||||||
|
Related claw status · claw doctor"
|
||||||
|
.to_string(),
|
||||||
|
LocalHelpTopic::State => "State
|
||||||
|
Usage claw state [--output-format <format>]
|
||||||
|
Purpose read the worker state file written by the interactive REPL
|
||||||
|
Output worker id, model, permissions, session reference (text or json)
|
||||||
|
Formats text (default), json
|
||||||
|
Prerequisite run `claw` interactively or `claw prompt <text>` to produce worker state first
|
||||||
|
Related ROADMAP #139 (worker-concept discoverability) · claw status"
|
||||||
|
.to_string(),
|
||||||
|
LocalHelpTopic::Export => "Export
|
||||||
|
Usage claw export [--session <id|latest>] [--output <path>] [--output-format <format>]
|
||||||
|
Purpose serialize a managed session to JSON for review, transfer, or archival
|
||||||
|
Defaults --session latest (most recent managed session in .claw/sessions/)
|
||||||
|
Formats text (default), json
|
||||||
|
Related /session list · claw --resume latest"
|
||||||
|
.to_string(),
|
||||||
|
LocalHelpTopic::Version => "Version
|
||||||
|
Usage claw version [--output-format <format>]
|
||||||
|
Aliases claw --version · claw -V
|
||||||
|
Purpose print the claw CLI version and build metadata
|
||||||
|
Formats text (default), json
|
||||||
|
Related claw doctor (full build/auth/config diagnostic)"
|
||||||
|
.to_string(),
|
||||||
|
LocalHelpTopic::SystemPrompt => "System Prompt
|
||||||
|
Usage claw system-prompt [--cwd <path>] [--date YYYY-MM-DD] [--output-format <format>]
|
||||||
|
Purpose render the resolved system prompt that `claw` would send for the given cwd + date
|
||||||
|
Options --cwd overrides the workspace dir · --date injects a deterministic date stamp
|
||||||
|
Formats text (default), json
|
||||||
|
Related claw doctor · claw dump-manifests"
|
||||||
|
.to_string(),
|
||||||
|
LocalHelpTopic::DumpManifests => "Dump Manifests
|
||||||
|
Usage claw dump-manifests [--manifests-dir <path>] [--output-format <format>]
|
||||||
|
Purpose emit every skill/agent/tool manifest the resolver would load for the current cwd
|
||||||
|
Options --manifests-dir scopes discovery to a specific directory
|
||||||
|
Formats text (default), json
|
||||||
|
Related claw skills · claw agents · claw doctor"
|
||||||
|
.to_string(),
|
||||||
|
LocalHelpTopic::BootstrapPlan => "Bootstrap Plan
|
||||||
|
Usage claw bootstrap-plan [--output-format <format>]
|
||||||
|
Purpose list the ordered startup phases the CLI would execute before dispatch
|
||||||
|
Output phase names (text) or structured phase list (json) — primary output is the plan itself
|
||||||
|
Formats text (default), json
|
||||||
|
Related claw doctor · claw status"
|
||||||
|
.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8519,7 +8587,7 @@ mod tests {
|
|||||||
parse_git_status_branch, parse_git_status_metadata_for, parse_git_workspace_summary,
|
parse_git_status_branch, parse_git_status_metadata_for, parse_git_workspace_summary,
|
||||||
parse_history_count, permission_policy, print_help_to, push_output_block,
|
parse_history_count, permission_policy, print_help_to, push_output_block,
|
||||||
render_config_report, render_diff_report, render_diff_report_for, render_memory_report,
|
render_config_report, render_diff_report, render_diff_report_for, render_memory_report,
|
||||||
render_prompt_history_report, render_repl_help, render_resume_usage,
|
render_help_topic, render_prompt_history_report, render_repl_help, render_resume_usage,
|
||||||
render_session_markdown, resolve_model_alias, resolve_model_alias_with_config,
|
render_session_markdown, resolve_model_alias, resolve_model_alias_with_config,
|
||||||
resolve_repl_model, resolve_session_reference, response_to_events,
|
resolve_repl_model, resolve_session_reference, response_to_events,
|
||||||
resume_supported_slash_commands, run_resume_command, short_tool_id,
|
resume_supported_slash_commands, run_resume_command, short_tool_id,
|
||||||
@ -9487,6 +9555,50 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subcommand_help_flag_has_one_contract_across_all_subcommands_141() {
|
||||||
|
// #141: every documented subcommand must resolve `<subcommand> --help`
|
||||||
|
// to a subcommand-specific help topic, never to global help, never to
|
||||||
|
// an "unknown option" error, never to the subcommand's primary output.
|
||||||
|
let cases: &[(&str, LocalHelpTopic)] = &[
|
||||||
|
("status", LocalHelpTopic::Status),
|
||||||
|
("sandbox", LocalHelpTopic::Sandbox),
|
||||||
|
("doctor", LocalHelpTopic::Doctor),
|
||||||
|
("acp", LocalHelpTopic::Acp),
|
||||||
|
("init", LocalHelpTopic::Init),
|
||||||
|
("state", LocalHelpTopic::State),
|
||||||
|
("export", LocalHelpTopic::Export),
|
||||||
|
("version", LocalHelpTopic::Version),
|
||||||
|
("system-prompt", LocalHelpTopic::SystemPrompt),
|
||||||
|
("dump-manifests", LocalHelpTopic::DumpManifests),
|
||||||
|
("bootstrap-plan", LocalHelpTopic::BootstrapPlan),
|
||||||
|
];
|
||||||
|
for (subcommand, expected_topic) in cases {
|
||||||
|
for flag in ["--help", "-h"] {
|
||||||
|
let parsed = parse_args(&[subcommand.to_string(), flag.to_string()])
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("`{subcommand} {flag}` should parse as help but errored: {error}")
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
parsed,
|
||||||
|
CliAction::HelpTopic(*expected_topic),
|
||||||
|
"`{subcommand} {flag}` should resolve to HelpTopic({expected_topic:?})"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// And the rendered help must actually mention the subcommand name
|
||||||
|
// (or its canonical title) so users know they got the right help.
|
||||||
|
let rendered = render_help_topic(*expected_topic);
|
||||||
|
assert!(
|
||||||
|
!rendered.is_empty(),
|
||||||
|
"{subcommand} help text should not be empty"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
rendered.contains("Usage"),
|
||||||
|
"{subcommand} help text should contain a Usage line"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_single_word_command_aliases_without_falling_back_to_prompt_mode() {
|
fn parses_single_word_command_aliases_without_falling_back_to_prompt_mode() {
|
||||||
let _guard = env_lock();
|
let _guard = env_lock();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user