Bladeren bron

Implement upstream slash command parity for plugin metadata surfaces

Wire the Rust slash-command surface to expose the upstream-style /plugin entry and add /agents and /skills handling. The plugin command keeps the existing management actions while help, completion, REPL dispatch, and tests now acknowledge the upstream aliases and inventory views.\n\nConstraint: Match original TypeScript command names without regressing existing /plugins management flows\nRejected: Add placeholder commands only | users would still lack practical slash-command output\nConfidence: high\nScope-risk: narrow\nReversibility: clean\nDirective: Keep /plugin as the canonical help entry while preserving /plugins and /marketplace aliases unless upstream naming changes again\nTested: cargo fmt --all; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace\nNot-tested: Manual interactive REPL execution of /agents and /skills against a live user configuration
Yeachan-Heo 2 maanden geleden
bovenliggende
commit
def861bfed
2 gewijzigde bestanden met toevoegingen van 44 en 11 verwijderingen
  1. 9 7
      rust/crates/commands/src/lib.rs
  2. 35 4
      rust/crates/rusty-claude-cli/src/main.rs

+ 9 - 7
rust/crates/commands/src/lib.rs

@@ -721,11 +721,10 @@ fn load_agents_from_roots(
                 continue;
             }
             let contents = fs::read_to_string(entry.path())?;
-            let fallback_name = entry
-                .path()
-                .file_stem()
-                .map(|stem| stem.to_string_lossy().to_string())
-                .unwrap_or_else(|| entry.file_name().to_string_lossy().to_string());
+            let fallback_name = entry.path().file_stem().map_or_else(
+                || entry.file_name().to_string_lossy().to_string(),
+                |stem| stem.to_string_lossy().to_string(),
+            );
             root_agents.push(AgentSummary {
                 name: parse_toml_string(&contents, "name").unwrap_or(fallback_name),
                 description: parse_toml_string(&contents, "description"),
@@ -1227,9 +1226,12 @@ mod tests {
         assert!(help.contains("/export [file]"));
         assert!(help.contains("/session [list|switch <session-id>]"));
         assert!(help.contains(
-            "/plugins [list|install <path>|enable <name>|disable <name>|uninstall <id>|update <id>]"
+            "/plugin [list|install <path>|enable <name>|disable <name>|uninstall <id>|update <id>]"
         ));
-        assert_eq!(slash_command_specs().len(), 23);
+        assert!(help.contains("aliases: /plugins, /marketplace"));
+        assert!(help.contains("/agents"));
+        assert!(help.contains("/skills"));
+        assert_eq!(slash_command_specs().len(), 25);
         assert_eq!(resume_supported_slash_commands().len(), 11);
     }
 

+ 35 - 4
rust/crates/rusty-claude-cli/src/main.rs

@@ -22,8 +22,8 @@ use api::{
 };
 
 use commands::{
-    handle_plugins_slash_command, render_slash_command_help, resume_supported_slash_commands,
-    slash_command_specs, SlashCommand,
+    handle_agents_slash_command, handle_plugins_slash_command, handle_skills_slash_command,
+    render_slash_command_help, resume_supported_slash_commands, slash_command_specs, SlashCommand,
 };
 use compat_harness::{extract_manifest, UpstreamPaths};
 use init::initialize_repo;
@@ -903,6 +903,8 @@ fn run_resume_command(
         | SlashCommand::Permissions { .. }
         | SlashCommand::Session { .. }
         | SlashCommand::Plugins { .. }
+        | SlashCommand::Agents { .. }
+        | SlashCommand::Skills { .. }
         | SlashCommand::Unknown(_) => Err("unsupported resumed slash command".into()),
     }
 }
@@ -1197,6 +1199,14 @@ impl LiveCli {
             SlashCommand::Plugins { action, target } => {
                 self.handle_plugins_command(action.as_deref(), target.as_deref())?
             }
+            SlashCommand::Agents { args } => {
+                Self::print_agents(args.as_deref())?;
+                false
+            }
+            SlashCommand::Skills { args } => {
+                Self::print_skills(args.as_deref())?;
+                false
+            }
             SlashCommand::Unknown(name) => {
                 eprintln!("unknown slash command: /{name}");
                 false
@@ -1397,6 +1407,18 @@ impl LiveCli {
         Ok(())
     }
 
+    fn print_agents(args: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
+        let cwd = env::current_dir()?;
+        println!("{}", handle_agents_slash_command(args, &cwd)?);
+        Ok(())
+    }
+
+    fn print_skills(args: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
+        let cwd = env::current_dir()?;
+        println!("{}", handle_skills_slash_command(args, &cwd)?);
+        Ok(())
+    }
+
     fn print_diff() -> Result<(), Box<dyn std::error::Error>> {
         println!("{}", render_diff_report()?);
         Ok(())
@@ -2734,6 +2756,7 @@ fn describe_tool_progress(name: &str, input: &str) -> String {
 }
 
 #[allow(clippy::needless_pass_by_value)]
+#[allow(clippy::too_many_arguments)]
 fn build_runtime(
     session: Session,
     model: String,
@@ -3058,7 +3081,12 @@ fn collect_tool_results(summary: &runtime::TurnSummary) -> Vec<serde_json::Value
 fn slash_command_completion_candidates() -> Vec<String> {
     slash_command_specs()
         .iter()
-        .map(|spec| format!("/{}", spec.name))
+        .flat_map(|spec| {
+            std::iter::once(spec.name)
+                .chain(spec.aliases.iter().copied())
+                .map(|name| format!("/{name}"))
+                .collect::<Vec<_>>()
+        })
         .collect()
 }
 
@@ -4062,8 +4090,11 @@ mod tests {
         assert!(help.contains("/export [file]"));
         assert!(help.contains("/session [list|switch <session-id>]"));
         assert!(help.contains(
-            "/plugins [list|install <path>|enable <name>|disable <name>|uninstall <id>|update <id>]"
+            "/plugin [list|install <path>|enable <name>|disable <name>|uninstall <id>|update <id>]"
         ));
+        assert!(help.contains("aliases: /plugins, /marketplace"));
+        assert!(help.contains("/agents"));
+        assert!(help.contains("/skills"));
         assert!(help.contains("/exit"));
     }