From 14cc81b9d2b8da4abc995d2a87732e201c45e4dc Mon Sep 17 00:00:00 2001 From: nas Date: Thu, 4 Jun 2026 09:34:05 -0400 Subject: [PATCH] feat(skills): add --all option and interactive catalog flow --- docs/cli-reference.md | 3 +- src/commands/init/mod.rs | 35 ++++++++++++++--------- src/commands/mod.rs | 1 + src/commands/skills/mod.rs | 58 +++++++++++++++++++++++++++++++++++--- 4 files changed, 78 insertions(+), 19 deletions(-) diff --git a/docs/cli-reference.md b/docs/cli-reference.md index a723f91..be8b4c3 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -209,11 +209,12 @@ steel skills list [--offline] ## steel skills install -Install one or more Steel Skills through `npx skills`. +Install all Steel Skills, or one or more selected Steel Skills, through `npx skills`. ### Usage ``` +steel skills install --all [-a ] [-g] [-y] steel skills install ... [-a ] [-g] [-y] ``` diff --git a/src/commands/init/mod.rs b/src/commands/init/mod.rs index 42db036..285cdb0 100644 --- a/src/commands/init/mod.rs +++ b/src/commands/init/mod.rs @@ -10,9 +10,8 @@ pub struct Args { #[arg(long)] pub agent: bool, - /// Install selected Steel skills via the skills installer. With no value, - /// installs the recommended bootstrap set - #[arg(long, num_args = 0..=1, value_delimiter = ',', default_missing_value = "steel-browser,steel-developer")] + /// Open the Steel skills installer flow. With no value, lets you choose skills interactively + #[arg(long, num_args = 0..=1, value_delimiter = ',', default_missing_value = "__all__")] pub skills: Option>, /// Skip Steel skill installation @@ -63,24 +62,32 @@ async fn install_skills(args: &Args) -> anyhow::Result<()> { return Ok(()); } - let selected = args - .skills - .clone() - .unwrap_or_else(|| vec!["steel-browser".to_string(), "steel-developer".to_string()]); + let install_result = match &args.skills { + Some(selected) if selected.is_empty() => return Ok(()), + Some(selected) if is_all_selection(selected) => skills::install_catalog_flow().await, + Some(selected) => skills::install_names(selected, false).await, + None => skills::install_catalog_flow().await, + }; - if selected.is_empty() { - return Ok(()); - } - - match skills::install_names(&selected, args.agent).await { + match install_result { Ok(()) => Ok(()), Err(error) => { status!("Could not install Steel skills through npx skills: {error:#}"); status!("Install manually with:"); - for name in &selected { - status!(" npx skills add steel-dev/skills --skill {name}"); + if let Some(selected) = &args.skills + && !is_all_selection(selected) + { + for name in selected { + status!(" npx skills add steel-dev/skills --skill {name}"); + } + } else { + status!(" npx skills add steel-dev/skills"); } Ok(()) } } } + +fn is_all_selection(selected: &[String]) -> bool { + selected.len() == 1 && matches!(selected[0].as_str(), "__all__" | "all") +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 0ad3926..37d0325 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -30,6 +30,7 @@ Getting Started: steel init Log in, verify, and install Steel skills into detected agents --agent Print the agent onboarding guide to stdout and exit steel skills list List available Steel Skills + steel skills install --all Install all Steel Skills through npx skills steel skills install Install a Steel Skill through npx skills steel skills doctor Check skills installer, auth, manifest, and paths diff --git a/src/commands/skills/mod.rs b/src/commands/skills/mod.rs index 5658093..5395a0c 100644 --- a/src/commands/skills/mod.rs +++ b/src/commands/skills/mod.rs @@ -128,11 +128,14 @@ pub enum SkillsCommand { #[arg(long)] offline: bool, }, - /// Install one or more Steel skills through npx skills + /// Install all or selected Steel skills through npx skills Install { /// Skill name(s) to install - #[arg(required = true)] + #[arg(required_unless_present = "all")] names: Vec, + /// Install all Steel skills from the catalog + #[arg(long)] + all: bool, /// Target agent passed to npx skills (-a) #[arg(short = 'a', long)] agent: Option, @@ -236,10 +239,20 @@ pub async fn run(command: SkillsCommand) -> anyhow::Result<()> { SkillsCommand::List { offline } => list(offline).await, SkillsCommand::Install { names, + all, agent, global, yes, - } => install_names_with_options(&names, agent.as_deref(), global, yes).await, + } => { + if all { + if !names.is_empty() { + anyhow::bail!("cannot combine --all with explicit skill names"); + } + install_all_with_options(agent.as_deref(), global, yes).await + } else { + install_names_with_options(&names, agent.as_deref(), global, yes).await + } + } SkillsCommand::Update { names, yes } => update(names, yes).await, SkillsCommand::Doctor { offline } => doctor(offline).await, SkillsCommand::Open { name } => open_skill(&name).await, @@ -248,7 +261,18 @@ pub async fn run(command: SkillsCommand) -> anyhow::Result<()> { } pub async fn install_names(names: &[String], yes: bool) -> anyhow::Result<()> { - install_names_with_options(names, None, true, yes).await + install_names_with_options(names, None, false, yes).await +} + +pub async fn install_all(yes: bool) -> anyhow::Result<()> { + install_all_with_options(None, false, yes).await +} + +pub async fn install_catalog_flow() -> anyhow::Result<()> { + run_npx(&["skills", "add", REPO_SPEC]) + .context("starting Steel skills installer. You can also run manually: npx skills add steel-dev/skills")?; + output::success_silent(); + Ok(()) } async fn list(offline: bool) -> anyhow::Result<()> { @@ -286,6 +310,10 @@ async fn install_names_with_options( global: bool, yes: bool, ) -> anyhow::Result<()> { + if names.is_empty() { + anyhow::bail!("provide a skill name or use --all"); + } + let manifest = load_manifest(false).await?; for name in names { ensure_known_skill(&manifest, name)?; @@ -312,6 +340,28 @@ async fn install_names_with_options( Ok(()) } +async fn install_all_with_options( + agent: Option<&str>, + global: bool, + yes: bool, +) -> anyhow::Result<()> { + let mut args = vec!["skills", "add", REPO_SPEC, "--all"]; + if let Some(agent) = agent { + args.push("-a"); + args.push(agent); + } + if global { + args.push("-g"); + } + if yes { + args.push("-y"); + } + run_npx(&args).context("installing all Steel skills. You can also run manually: npx skills add steel-dev/skills --all")?; + status!("Installed all Steel skills"); + output::success_silent(); + Ok(()) +} + async fn update(names: Vec, yes: bool) -> anyhow::Result<()> { let manifest = load_manifest(false).await?; let selected = if names.is_empty() {