feat: add workspace-backed agent skills
This commit is contained in:
@@ -15,6 +15,7 @@ enum Command {
|
||||
Init(InitOptions),
|
||||
ConfigDefault,
|
||||
ConfigDiff(WorkspacePathOptions),
|
||||
Skills(SkillsCommand),
|
||||
Help,
|
||||
}
|
||||
|
||||
@@ -36,6 +37,13 @@ struct WorkspacePathOptions {
|
||||
workspace: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SkillsCommand {
|
||||
List(WorkspacePathOptions),
|
||||
Lint(WorkspacePathOptions),
|
||||
Show { workspace: PathBuf, name: String },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CliError(String);
|
||||
|
||||
@@ -65,6 +73,7 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Command::Init(options) => run_init(options),
|
||||
Command::ConfigDefault => run_config_default(),
|
||||
Command::ConfigDiff(options) => run_config_diff(options),
|
||||
Command::Skills(command) => run_skills(command),
|
||||
Command::Help => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -84,6 +93,7 @@ fn parse_command(args: &[String]) -> Result<Command, CliError> {
|
||||
Ok(Command::Init(parse_init_options(rest)?))
|
||||
}
|
||||
"config" => parse_config_command(rest),
|
||||
"skills" => parse_skills_command(rest),
|
||||
"serve" => {
|
||||
if rest.iter().any(|arg| arg == "--help" || arg == "-h") {
|
||||
print_serve_help();
|
||||
@@ -96,7 +106,7 @@ fn parse_command(args: &[String]) -> Result<Command, CliError> {
|
||||
Ok(Command::Help)
|
||||
}
|
||||
other => Err(CliError(format!(
|
||||
"unknown command `{other}`; expected `init`, `config`, or `serve`"
|
||||
"unknown command `{other}`; expected `init`, `config`, `skills`, or `serve`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
@@ -123,6 +133,39 @@ fn run_config_diff(options: WorkspacePathOptions) -> Result<(), Box<dyn std::err
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_skills(command: SkillsCommand) -> Result<(), Box<dyn std::error::Error>> {
|
||||
match command {
|
||||
SkillsCommand::List(options) => {
|
||||
let catalog = yoi_workspace_server::skills::catalog(&options.workspace);
|
||||
println!("{}", serde_json::to_string_pretty(&catalog)?);
|
||||
}
|
||||
SkillsCommand::Lint(options) => {
|
||||
let catalog = yoi_workspace_server::skills::lint(&options.workspace);
|
||||
println!("{}", serde_json::to_string_pretty(&catalog)?);
|
||||
if catalog
|
||||
.diagnostics
|
||||
.iter()
|
||||
.chain(
|
||||
catalog
|
||||
.entries
|
||||
.iter()
|
||||
.flat_map(|entry| entry.diagnostics.iter()),
|
||||
)
|
||||
.any(|diagnostic| {
|
||||
diagnostic.severity == worker::skill::SkillDiagnosticSeverity::Error
|
||||
})
|
||||
{
|
||||
return Err(Box::new(CliError("Skill lint found errors".to_string())));
|
||||
}
|
||||
}
|
||||
SkillsCommand::Show { workspace, name } => {
|
||||
let detail = yoi_workspace_server::skills::detail(&workspace, &name)?;
|
||||
println!("{}", serde_json::to_string_pretty(&detail)?);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_serve(options: ServeOptions) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let identity = WorkspaceIdentity::load_required(&options.workspace)?;
|
||||
let config_file = WorkspaceBackendConfigFile::load_for_workspace(&options.workspace)?;
|
||||
@@ -187,6 +230,37 @@ fn parse_config_command(args: &[String]) -> Result<Command, CliError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_skills_command(args: &[String]) -> Result<Command, CliError> {
|
||||
let Some((subcommand, rest)) = args.split_first() else {
|
||||
print_skills_help();
|
||||
return Ok(Command::Help);
|
||||
};
|
||||
match subcommand.as_str() {
|
||||
"list" => Ok(Command::Skills(SkillsCommand::List(
|
||||
parse_workspace_path_options(rest)?,
|
||||
))),
|
||||
"lint" => Ok(Command::Skills(SkillsCommand::Lint(
|
||||
parse_workspace_path_options(rest)?,
|
||||
))),
|
||||
"show" => {
|
||||
let Some((name, rest)) = rest.split_first() else {
|
||||
return Err(CliError("skills show requires a Skill name".to_string()));
|
||||
};
|
||||
Ok(Command::Skills(SkillsCommand::Show {
|
||||
workspace: parse_workspace_path_options(rest)?.workspace,
|
||||
name: name.to_string(),
|
||||
}))
|
||||
}
|
||||
"--help" | "-h" => {
|
||||
print_skills_help();
|
||||
Ok(Command::Help)
|
||||
}
|
||||
other => Err(CliError(format!(
|
||||
"unknown skills subcommand `{other}`; expected `list`, `lint`, or `show`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_workspace_path_options(args: &[String]) -> Result<WorkspacePathOptions, CliError> {
|
||||
let mut workspace = std::env::current_dir()
|
||||
.map_err(|error| CliError(format!("failed to read current dir: {error}")))?;
|
||||
@@ -333,7 +407,7 @@ fn parse_listen(value: &str) -> Result<SocketAddr, CliError> {
|
||||
|
||||
fn print_help() {
|
||||
println!(
|
||||
"yoi-workspace-server\n\nUsage:\n yoi-workspace-server init [OPTIONS]\n yoi-workspace-server config <COMMAND> [OPTIONS]\n yoi-workspace-server serve [OPTIONS]\n\nOptions:\n -h, --help Print help"
|
||||
"yoi-workspace-server\n\nUsage:\n yoi-workspace-server init [OPTIONS]\n yoi-workspace-server config <COMMAND> [OPTIONS]\n yoi-workspace-server skills <COMMAND> [OPTIONS]\n yoi-workspace-server serve [OPTIONS]\n\nOptions:\n -h, --help Print help"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -349,6 +423,12 @@ fn print_config_help() {
|
||||
);
|
||||
}
|
||||
|
||||
fn print_skills_help() {
|
||||
println!(
|
||||
"yoi-workspace-server skills\n\nUsage:\n yoi-workspace-server skills list [OPTIONS]\n yoi-workspace-server skills lint [OPTIONS]\n yoi-workspace-server skills show <NAME> [OPTIONS]\n\nDescription:\n Uses the Workspace backend Skill catalog/lint/detail authority. Catalog output is lightweight and omits full SKILL.md bodies; detail output includes the body. allowed-tools and scripts are diagnostics only.\n\nOptions:\n --workspace <PATH> Workspace root (defaults to cwd)\n -h, --help Print help"
|
||||
);
|
||||
}
|
||||
|
||||
fn print_serve_help() {
|
||||
println!(
|
||||
"yoi-workspace-server serve\n\nUsage:\n yoi-workspace-server serve [OPTIONS]\n\nDescription:\n Serves an already initialized Workspace. Run `yoi workspace init` first.\n\nOptions:\n --workspace <PATH> Workspace root containing .yoi project records (defaults to cwd)\n --db <PATH> SQLite database path (legacy dev override)\n --frontend <PATH> Static SPA build directory to serve (legacy dev override)\n --listen <ADDR> Listen address (legacy dev override; default 127.0.0.1:8787)\n -h, --help Print help"
|
||||
|
||||
Reference in New Issue
Block a user