diff --git a/ROADMAP.md b/ROADMAP.md index 4e8231c..d6e98cd 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -444,9 +444,7 @@ Model name prefix now wins unconditionally over env-var presence. Regression tes 39. **Several slash commands are registered but not implemented: /branch, /rewind, /ide, /tag, /output-style, /add-dir** -- dogfooded 2026-04-09. These commands appear in the REPL completions surface but silently print 'Command registered but not yet implemented.' and return false. Users (mezz2301 in #claw-code) hit this as 'many features are not supported in this version now'. Fix shape: either (a) implement the missing commands, or (b) remove them from completions/help output until they are ready, so the discovery surface matches what actually works. Source: mezz2301 in #claw-code 2026-04-09; pinpointed in main.rs:3728. -40. **Surface broken installed plugins before they become support ghosts** — community-support lane. Clawhip commit `ff6d3b7` on worktree `claw-code-community-support-plugin-list-load-failures` / branch `community-support/plugin-list-load-failures`. When an installed plugin has a broken manifest (missing hook scripts, parse errors, bad json), the plugin silently fails to load and the user sees nothing — no warning, no list entry, no hint. Related to ROADMAP #27 (host plugin path leaking into tests) but at the user-facing surface: the test gap and the UX gap are siblings of the same root. Landing on `main` will close the silent-ghost class of support issues where users report "my plugin does nothing" with no error to share. Track until merged to `main`. - -40. **Surface broken installed plugins before they become support ghosts** — community-support lane. Clawhip commit `ff6d3b7` on worktree `claw-code-community-support-plugin-list-load-failures` / branch `community-support/plugin-list-load-failures`. When an installed plugin has a broken manifest (missing hook scripts, parse errors, bad json), the plugin silently fails to load and the user sees nothing — no warning, no list entry, no hint. Related to ROADMAP #27 (host plugin path leaking into tests) but at the user-facing surface: the test gap and the UX gap are siblings of the same same root. Landing on `main` will close the silent-ghost class of support issues where users report "my plugin does nothing" with no error to share. Track until merged to `main`. +40. **Surface broken installed plugins before they become support ghosts** — community-support lane. Clawhip commit `ff6d3b7` on worktree `claw-code-community-support-plugin-list-load-failures` / branch `community-support/plugin-list-load-failures`. When an installed plugin has a broken manifest (missing hook scripts, parse errors, bad json), the plugin silently fails to load and the user sees nothing — no warning, no list entry, no hint. Related to ROADMAP #27 (host plugin path leaking into tests) but at the user-facing surface: the test gap and the UX gap are siblings of the same root. **Done (verified 2026-04-11):** `PluginManager::plugin_registry_report()` and `installed_plugin_registry_report()` now preserve valid plugins while collecting `PluginLoadFailure`s, and the command-layer renderer emits a `Warnings:` block for broken plugins instead of silently hiding them. Fresh proof: `cargo test -p plugins plugin_registry_report_collects_load_failures_without_dropping_valid_plugins -- --nocapture`, `cargo test -p plugins installed_plugin_registry_report_collects_load_failures_from_install_root -- --nocapture`, and a new `commands` regression covering `render_plugins_report_with_failures()` all pass on current main. 41. **Stop ambient plugin state from skewing CLI regression checks** — community-support lane. Clawhip commit `7d493a7` on worktree `claw-code-community-support-plugin-test-sealing` / branch `community-support/plugin-test-sealing`. Companion to #40: the test sealing gap is the CI/developer side of the same root — host `~/.claude/plugins/installed/` bleeds into CLI test runs, making regression checks non-deterministic on any machine with a non-pristine plugin install. Closely related to ROADMAP #27 (dev/rust `cargo test` reads host plugin state). Track until merged to `main`. diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index f81644a..0724b16 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -4121,12 +4121,15 @@ mod tests { handle_plugins_slash_command, handle_skills_slash_command_json, handle_slash_command, load_agents_from_roots, load_skills_from_roots, render_agents_report, render_agents_report_json, render_mcp_report_json_for, render_plugins_report, - render_skills_report, render_slash_command_help, render_slash_command_help_detail, - resolve_skill_path, resume_supported_slash_commands, slash_command_specs, - suggest_slash_commands, validate_slash_command_input, DefinitionSource, SkillOrigin, - SkillRoot, SkillSlashDispatch, SlashCommand, + render_plugins_report_with_failures, render_skills_report, render_slash_command_help, + render_slash_command_help_detail, resolve_skill_path, resume_supported_slash_commands, + slash_command_specs, suggest_slash_commands, validate_slash_command_input, + DefinitionSource, SkillOrigin, SkillRoot, SkillSlashDispatch, SlashCommand, + }; + use plugins::{ + PluginError, PluginKind, PluginLoadFailure, PluginManager, PluginManagerConfig, + PluginMetadata, PluginSummary, }; - use plugins::{PluginKind, PluginManager, PluginManagerConfig, PluginMetadata, PluginSummary}; use runtime::{ CompactionConfig, ConfigLoader, ContentBlock, ConversationMessage, MessageRole, Session, }; @@ -4884,6 +4887,36 @@ mod tests { assert!(rendered.contains("disabled")); } + #[test] + fn renders_plugins_report_with_broken_plugin_warnings() { + let rendered = render_plugins_report_with_failures( + &[PluginSummary { + metadata: PluginMetadata { + id: "demo@external".to_string(), + name: "demo".to_string(), + version: "1.2.3".to_string(), + description: "demo plugin".to_string(), + kind: PluginKind::External, + source: "demo".to_string(), + default_enabled: false, + root: None, + }, + enabled: true, + }], + &[PluginLoadFailure::new( + PathBuf::from("/tmp/broken-plugin"), + PluginKind::External, + "broken".to_string(), + PluginError::InvalidManifest("hook path `hooks/pre.sh` does not exist".to_string()), + )], + ); + + assert!(rendered.contains("Warnings:")); + assert!(rendered.contains("Failed to load external plugin")); + assert!(rendered.contains("/tmp/broken-plugin")); + assert!(rendered.contains("does not exist")); + } + #[test] fn lists_agents_from_project_and_user_roots() { let workspace = temp_dir("agents-workspace");