mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-03 22:04:48 +08:00
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
This commit is contained in:
@@ -721,11 +721,10 @@ fn load_agents_from_roots(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let contents = fs::read_to_string(entry.path())?;
|
let contents = fs::read_to_string(entry.path())?;
|
||||||
let fallback_name = entry
|
let fallback_name = entry.path().file_stem().map_or_else(
|
||||||
.path()
|
|| entry.file_name().to_string_lossy().to_string(),
|
||||||
.file_stem()
|
|stem| stem.to_string_lossy().to_string(),
|
||||||
.map(|stem| stem.to_string_lossy().to_string())
|
);
|
||||||
.unwrap_or_else(|| entry.file_name().to_string_lossy().to_string());
|
|
||||||
root_agents.push(AgentSummary {
|
root_agents.push(AgentSummary {
|
||||||
name: parse_toml_string(&contents, "name").unwrap_or(fallback_name),
|
name: parse_toml_string(&contents, "name").unwrap_or(fallback_name),
|
||||||
description: parse_toml_string(&contents, "description"),
|
description: parse_toml_string(&contents, "description"),
|
||||||
@@ -1227,9 +1226,12 @@ mod tests {
|
|||||||
assert!(help.contains("/export [file]"));
|
assert!(help.contains("/export [file]"));
|
||||||
assert!(help.contains("/session [list|switch <session-id>]"));
|
assert!(help.contains("/session [list|switch <session-id>]"));
|
||||||
assert!(help.contains(
|
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);
|
assert_eq!(resume_supported_slash_commands().len(), 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ use api::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use commands::{
|
use commands::{
|
||||||
handle_plugins_slash_command, render_slash_command_help, resume_supported_slash_commands,
|
handle_agents_slash_command, handle_plugins_slash_command, handle_skills_slash_command,
|
||||||
slash_command_specs, SlashCommand,
|
render_slash_command_help, resume_supported_slash_commands, slash_command_specs, SlashCommand,
|
||||||
};
|
};
|
||||||
use compat_harness::{extract_manifest, UpstreamPaths};
|
use compat_harness::{extract_manifest, UpstreamPaths};
|
||||||
use init::initialize_repo;
|
use init::initialize_repo;
|
||||||
@@ -903,6 +903,8 @@ fn run_resume_command(
|
|||||||
| SlashCommand::Permissions { .. }
|
| SlashCommand::Permissions { .. }
|
||||||
| SlashCommand::Session { .. }
|
| SlashCommand::Session { .. }
|
||||||
| SlashCommand::Plugins { .. }
|
| SlashCommand::Plugins { .. }
|
||||||
|
| SlashCommand::Agents { .. }
|
||||||
|
| SlashCommand::Skills { .. }
|
||||||
| SlashCommand::Unknown(_) => Err("unsupported resumed slash command".into()),
|
| SlashCommand::Unknown(_) => Err("unsupported resumed slash command".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1197,6 +1199,14 @@ impl LiveCli {
|
|||||||
SlashCommand::Plugins { action, target } => {
|
SlashCommand::Plugins { action, target } => {
|
||||||
self.handle_plugins_command(action.as_deref(), target.as_deref())?
|
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) => {
|
SlashCommand::Unknown(name) => {
|
||||||
eprintln!("unknown slash command: /{name}");
|
eprintln!("unknown slash command: /{name}");
|
||||||
false
|
false
|
||||||
@@ -1397,6 +1407,18 @@ impl LiveCli {
|
|||||||
Ok(())
|
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>> {
|
fn print_diff() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("{}", render_diff_report()?);
|
println!("{}", render_diff_report()?);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -2734,6 +2756,7 @@ fn describe_tool_progress(name: &str, input: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn build_runtime(
|
fn build_runtime(
|
||||||
session: Session,
|
session: Session,
|
||||||
model: String,
|
model: String,
|
||||||
@@ -3058,7 +3081,12 @@ fn collect_tool_results(summary: &runtime::TurnSummary) -> Vec<serde_json::Value
|
|||||||
fn slash_command_completion_candidates() -> Vec<String> {
|
fn slash_command_completion_candidates() -> Vec<String> {
|
||||||
slash_command_specs()
|
slash_command_specs()
|
||||||
.iter()
|
.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()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4062,8 +4090,11 @@ mod tests {
|
|||||||
assert!(help.contains("/export [file]"));
|
assert!(help.contains("/export [file]"));
|
||||||
assert!(help.contains("/session [list|switch <session-id>]"));
|
assert!(help.contains("/session [list|switch <session-id>]"));
|
||||||
assert!(help.contains(
|
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"));
|
assert!(help.contains("/exit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user