fix(#794): plugins install not-found path returns typed plugin_source_not_found instead of unknown+null

This commit is contained in:
YeonGyu-Kim
2026-05-27 13:08:14 +09:00
parent 57a57ef771
commit 491f179a03
3 changed files with 59 additions and 0 deletions

View File

@@ -7753,3 +7753,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
792. **`claw agents list --bogus-flag` and `claw skills list --bogus-flag` silently returned `status:"ok" count:0` instead of an error** — dogfooded 2026-05-27 on `93a159dc`. The `list <filter>` arm in both handlers treated flag-shaped tokens (`--something`) as name substring filters. Since no agents/skills have `--bogus` in their name, result was empty success list — a false positive that masks typos and unknown flags. Fix: added flag-prefix guard at the top of both `list <args>` arms in `commands/src/lib.rs`; detected filter tokens starting with `-` return `unknown_option` + usage hint. Two new integration tests `agents_list_flag_shaped_filter_returns_unknown_option_792`, `skills_list_flag_shaped_filter_returns_unknown_option_792`. 53 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori agents/skills list probe on `93a159dc`, 2026-05-27.
793. **`claw plugins list --bogus-flag` silent empty success + `plugins uninstall <not-found>` had `hint:null`** — dogfooded 2026-05-27 on `abfa2e4c`. Two gaps: (1) `plugins list` filter branch in `print_plugins` treated `--bogus-flag` as an id substring filter, found no matches, returned `status:"ok"` empty list — same false-positive as #792 for agents/skills. (2) `plugins uninstall no-such` propagated `plugin_not_found` error via `?` with no `\n` delimiter; `plugin_not_found` was missing from `fallback_hint_for_error_kind` table. Fix: (1) added flag-prefix guard in `print_plugins` `is_list_action` branch (detects tokens starting with `-`, returns `unknown_option` + usage hint, exits 1); (2) added `"plugin_not_found"``"Run 'claw plugins list' to see installed plugins."` to fallback table. Two new tests `plugins_list_flag_shaped_filter_returns_unknown_option_793`, `plugins_uninstall_not_found_has_hint_793`. 55 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori plugins lifecycle probe on `abfa2e4c`, 2026-05-27.
794. **`claw plugins install /nonexistent/path` returned `error_kind:"unknown"` + `hint:null`** — dogfooded 2026-05-27 on `57a57ef7`. The error message `"plugin source '/path' was not found"` had no classifier arm, falling to `"unknown"`. Fix: added `plugin_source_not_found` classifier arm (`message.contains("plugin source") && message.contains("was not found")`); added `"plugin_source_not_found"``"Check that the path or URL is correct..."` to `fallback_hint_for_error_kind`. Unit test assertion added to `test_classify_error_kind`; integration test `plugins_install_not_found_path_returns_typed_kind_794` added. 56 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori plugins install probe on `57a57ef7`, 2026-05-27.

View File

@@ -337,6 +337,9 @@ fn classify_error_kind(message: &str) -> &'static str {
"agent_not_found"
} else if message.contains("is not installed") {
"plugin_not_found"
} else if message.contains("plugin source") && message.contains("was not found") {
// #794: `plugins install /nonexistent/path` → "plugin source ... was not found"
"plugin_source_not_found"
} else if (message.contains("skill source") && message.contains("not found"))
|| message.starts_with("skill '")
{
@@ -414,6 +417,10 @@ fn fallback_hint_for_error_kind(kind: &str) -> Option<&'static str> {
// #793: plugins uninstall/enable/disable of non-existing plugin propagates through
// the ? operator with no \n delimiter, so split_error_hint returns None.
"plugin_not_found" => Some("Run `claw plugins list` to see installed plugins."),
// #794: plugins install with a path that doesn't exist
"plugin_source_not_found" => Some(
"Check that the path or URL is correct. Use a local directory or a valid registry id.",
),
_ => None,
}
}
@@ -13178,6 +13185,11 @@ mod tests {
classify_error_kind("my-plugin is not installed"),
"plugin_not_found"
);
// #794: plugins install with missing source path
assert_eq!(
classify_error_kind("plugin source `/nonexistent/path` was not found"),
"plugin_source_not_found"
);
assert_eq!(
classify_error_kind("skill source /path/to/skill not found"),
"skill_not_found"

View File

@@ -3156,3 +3156,48 @@ fn plugins_uninstall_not_found_has_hint_793() {
"hint should reference plugins list, got: {h:?}"
);
}
#[test]
fn plugins_install_not_found_path_returns_typed_kind_794() {
// #794: `claw plugins install /nonexistent/path` returned error_kind:"unknown" + hint:null.
// The message "plugin source ... was not found" had no classifier arm; fell to "unknown".
// Fix: added "plugin_source_not_found" classifier arm + fallback hint table entry.
let root = unique_temp_dir("plugins-install-794");
fs::create_dir_all(&root).expect("temp dir");
std::process::Command::new("git")
.args(["init", "-q"])
.current_dir(&root)
.output()
.ok();
let output = run_claw(
&root,
&[
"--output-format",
"json",
"plugins",
"install",
"/nonexistent-path-xyz-794",
],
&[],
);
assert!(
!output.status.success(),
"plugins install not-found-path must exit non-zero (#794)"
);
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 install not-found should emit JSON error envelope");
assert_eq!(
j["error_kind"], "plugin_source_not_found",
"plugins install not-found should be plugin_source_not_found, got {:?}",
j["error_kind"]
);
let h = j["hint"]
.as_str()
.expect("plugin_source_not_found must have non-null hint (#794)");
assert!(!h.is_empty(), "hint must be non-empty");
}