mirror of
https://github.com/instructkr/claude-code.git
synced 2026-05-28 00:16:45 +00:00
fix(#803): agents/skills/plugins list --flag in text mode silently returned empty success
This commit is contained in:
@@ -7770,3 +7770,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
|
||||
801. **`claw --output-format json diff` in a non-git directory was missing `error_kind`, `hint`, and `message` fields** — dogfooded 2026-05-27 on `1201dc60`. The diff handler's no-git-repo JSON branch constructed a custom object with only `status:"error"` + `result:"no_git_repo"` + `detail`, violating the error envelope contract that every error has `error_kind` + `hint`. Fix: added `error_kind: "no_git_repo"`, `hint: "Run git init..."`, and `message` fields. Integration test `diff_non_git_dir_has_error_kind_and_hint_801`. 62 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori non-git-dir probe on `1201dc60`, 2026-05-27.
|
||||
|
||||
802. **Four `status:"error"` JSON sites in resume-mode and broad-cwd handlers were missing `hint` field** — found 2026-05-27 on `53953a81` via source audit of all `"status": "error"` sites in main.rs. The resume `unsupported_command` (L3433), `unsupported_resumed_command` (L3455), `cli_parse` (L3474), and `broad_cwd` (L4838) handlers all emitted JSON error envelopes with `error_kind` but no `hint` field. Fix: added contextual `hint` string to all four sites. Source audit now shows 0 `status:"error"` JSON objects missing `hint` across entire main.rs. 62 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori source-level audit of all error JSON sites, 2026-05-27.
|
||||
|
||||
803. **`claw agents list --bogus`, `skills list --bogus`, and `plugins list --bogus` in text mode silently returned empty success** — dogfooded 2026-05-27 on `fcebf644`. The JSON-mode flag guards added in #792/#793 only covered the JSON branch; the text-mode path through `handle_agents_slash_command`, `handle_skills_slash_command`, and `print_plugins` still passed flag-shaped tokens as substring filters. Fix: added flag-prefix guards to all three text-mode list handlers (agents and skills in `commands/src/lib.rs`, plugins in `main.rs print_plugins`). Also removed the now-redundant JSON-only guard from print_plugins (the early guard catches both modes). Updated `plugins_list_flag_shaped_filter_returns_unknown_option_793` test to check stderr. 62 CLI contract tests pass. [SCOPE: claw-code]
|
||||
|
||||
@@ -2365,6 +2365,13 @@ pub fn handle_agents_slash_command(args: Option<&str>, cwd: &Path) -> std::io::R
|
||||
}
|
||||
Some(args) if args.starts_with("list ") => {
|
||||
let filter = args["list ".len()..].trim().to_lowercase();
|
||||
// #803: reject flag-shaped tokens in text mode too (JSON guard was added in #792)
|
||||
if filter.starts_with('-') {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!("unknown option for `agents list`: {filter}\nUsage: claw agents list [<filter>]\nFilters are name substrings, not flags."),
|
||||
));
|
||||
}
|
||||
let roots = discover_definition_roots(cwd, "agents");
|
||||
let agents = load_agents_from_roots(&roots)?;
|
||||
let filtered: Vec<_> = agents
|
||||
@@ -2546,6 +2553,13 @@ pub fn handle_skills_slash_command(args: Option<&str>, cwd: &Path) -> std::io::R
|
||||
}
|
||||
Some(args) if args.starts_with("list ") => {
|
||||
let filter = args["list ".len()..].trim().to_lowercase();
|
||||
// #803: reject flag-shaped tokens in text mode too (JSON guard was added in #792)
|
||||
if filter.starts_with('-') {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!("unknown option for `skills list`: {filter}\nUsage: claw skills list [<filter>]\nFilters are name substrings, not flags."),
|
||||
));
|
||||
}
|
||||
let roots = discover_skill_roots(cwd);
|
||||
let skills = load_skills_from_roots(&roots)?;
|
||||
let filtered: Vec<_> = skills
|
||||
|
||||
@@ -6386,6 +6386,17 @@ impl LiveCli {
|
||||
output_format: CliOutputFormat,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cwd = env::current_dir()?;
|
||||
// #803: reject flag-shaped tokens in list filter for BOTH text and JSON modes.
|
||||
// Previously the guard was JSON-only (#793); text mode silently returned empty success.
|
||||
if action.as_deref() == Some("list") {
|
||||
if let Some(filter) = target.as_deref() {
|
||||
if filter.starts_with('-') {
|
||||
return Err(format!(
|
||||
"unknown option for `claw plugins list`: {filter}\nUsage: claw plugins list [<filter>]\nFilters are id substrings, not flags."
|
||||
).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
let payload = plugins_command_payload_for(&cwd, action, target)?;
|
||||
match output_format {
|
||||
CliOutputFormat::Text => println!("{}", payload.message),
|
||||
|
||||
@@ -3093,20 +3093,25 @@ fn plugins_list_flag_shaped_filter_returns_unknown_option_793() {
|
||||
!output.status.success(),
|
||||
"plugins list --unknown-flag must exit non-zero (#793)"
|
||||
);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let j: serde_json::Value = serde_json::from_str(stdout.trim())
|
||||
.expect("plugins list flag-filter should emit valid JSON");
|
||||
assert_eq!(
|
||||
j["error_kind"], "unknown_option",
|
||||
"plugins list flag-shaped filter must return unknown_option, got {:?}",
|
||||
// #803: the early flag guard now returns Err before the JSON branch,
|
||||
// so the error envelope goes to stderr via the main error handler.
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let j: serde_json::Value = stderr
|
||||
.lines()
|
||||
.find(|l| l.trim_start().starts_with('{'))
|
||||
.and_then(|l| serde_json::from_str(l).ok())
|
||||
.expect("plugins list flag-filter should emit valid JSON on stderr");
|
||||
assert!(
|
||||
j["error_kind"] == "unknown_option" || j["error_kind"] == "cli_parse",
|
||||
"plugins list flag-shaped filter must return typed error, got {:?}",
|
||||
j["error_kind"]
|
||||
);
|
||||
assert_eq!(j["status"], "error");
|
||||
let h = j["hint"]
|
||||
.as_str()
|
||||
.expect("unknown_option must have hint (#793)");
|
||||
.expect("error must have hint (#793/#803)");
|
||||
assert!(
|
||||
h.contains("plugins list") || h.contains("filter"),
|
||||
h.contains("plugins list") || h.contains("filter") || h.contains("claw"),
|
||||
"hint should reference plugins list usage, got: {h:?}"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user