From c319a1c93776f4cf4351df267cc563ed21d6f606 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Fri, 21 Aug 2026 14:30:31 +1000 Subject: [PATCH 1/4] fix(cli): make install guidance actionable --- README.md | 5 +++ crates/skilld-command/src/lib.rs | 41 ++++++++++++++++++---- crates/skilld-command/src/remote.rs | 4 +-- crates/skilld-command/tests/remote.rs | 18 ++++++++++ crates/skilld-native/tests/cli.rs | 50 +++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bc0f864f..114056e2 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,11 @@ The API does not expose private storage addresses. ### Direct mode `--direct` fetches a public GitHub Repository without the skilld.dev API. + +```sh +skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex +``` + The installed Skill receives the `unverified` source status. The user reviews the Skill before use. diff --git a/crates/skilld-command/src/lib.rs b/crates/skilld-command/src/lib.rs index 4100a15b..b3c6574d 100644 --- a/crates/skilld-command/src/lib.rs +++ b/crates/skilld-command/src/lib.rs @@ -26,6 +26,8 @@ use skilld_core::{ InstallRequest, InstallScope, InstallSource, LockedSource, VERSION, select_target_ids, }; +const DIRECT_SOURCE_GUIDANCE: &str = "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Run: skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex"; + #[derive(Debug, Parser)] #[command( name = "skilld", @@ -43,15 +45,35 @@ enum Command { /// Search for Skills. Search { query: Vec }, /// Install a Skill or restore lockfile state. + #[command( + long_about = "Install a Skill or restore lockfile state.\n\nAccepted SOURCE values:\n skilld:OWNER/REPOSITORY/SKILL\n Install a hosted Artifact.\n github:OWNER/REPOSITORY/SKILL_PATH\n github:OWNER/REPOSITORY/SKILL_PATH#branch:BRANCH\n github:OWNER/REPOSITORY/SKILL_PATH#tag:TAG\n github:OWNER/REPOSITORY/SKILL_PATH#commit:SHA\n https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH\n These public GitHub Repository sources require --direct.\n ./RELATIVE_PATH or ABSOLUTE_PATH\n Install a local Skill.\n skilld\n Install the skilld-maintained Skill with --global.\n\nRun skilld install without SOURCE to restore .skills/skilld-lock.yaml.", + after_long_help = "Examples:\n skilld install skilld:skilld-dev/skills/vue --agent codex\n skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex\n skilld install" + )] Install { + /// Select the Skill source. Omit SOURCE to restore lockfile state. + #[arg(value_name = "SOURCE")] source: Option, - #[arg(long)] + #[arg( + long, + long_help = "Use account-level Agent targets. Default scope: current project." + )] global: bool, - #[arg(long = "agent")] + #[arg( + long = "agent", + value_name = "AGENT", + long_help = "Select an Agent target. Repeat --agent to select more than one Agent target.\nValues: claude-code, cursor, windsurf, cline, codex, github-copilot,\n gemini-cli, goose, amp, opencode, roo, antigravity.\nDefault: detected Agent targets. If none exist, use agent.targets." + )] agents: Vec, - #[arg(long)] + #[arg( + long, + value_name = "MODE", + long_help = "Set how Agent targets receive the Skill.\nValues: copy, symlink. Default: install.mode. Its initial value is copy." + )] mode: Option, - #[arg(long)] + #[arg( + long, + long_help = "Fetch a public GitHub Repository without skilld.dev.\nRequires a github: source or GitHub tree URL.\nDirect installs set source status to unverified." + )] direct: bool, }, /// List installed Skills. @@ -222,6 +244,13 @@ impl CommandError { } } + fn direct_source_required() -> Self { + Self { + code: "DIRECT_SOURCE_REQUIRED", + message: DIRECT_SOURCE_GUIDANCE.to_owned(), + } + } + pub fn config(message: impl Into) -> Self { Self { code: "INVALID_CONFIG", @@ -353,9 +382,7 @@ fn dispatch(command: Command, host: &H) -> Result, CommandE InstallOperation::Install(InstallSource::DirectRemote(source)) } (true, _) => { - return Err(CommandError::input( - "--direct needs an explicit public GitHub Repository selector", - )); + return Err(CommandError::direct_source_required()); } (false, source) => InstallOperation::Install(source), }, diff --git a/crates/skilld-command/src/remote.rs b/crates/skilld-command/src/remote.rs index a108efd1..d757d42f 100644 --- a/crates/skilld-command/src/remote.rs +++ b/crates/skilld-command/src/remote.rs @@ -679,14 +679,14 @@ impl SkilldRemote { if !selector.is_explicit_github() { return Err(RemoteError::new( "DIRECT_SOURCE_REQUIRED", - "--direct needs an explicit public GitHub Repository selector", + crate::DIRECT_SOURCE_GUIDANCE, )); } let source = selector.source(); let SourceSelector::Path { path: skill_path } = &source.selector else { return Err(RemoteError::new( "DIRECT_SOURCE_REQUIRED", - "--direct needs an explicit GitHub Skill path", + crate::DIRECT_SOURCE_GUIDANCE, )); }; let repository_url = format!( diff --git a/crates/skilld-command/tests/remote.rs b/crates/skilld-command/tests/remote.rs index fa1864ac..20bcac76 100644 --- a/crates/skilld-command/tests/remote.rs +++ b/crates/skilld-command/tests/remote.rs @@ -689,6 +689,24 @@ fn direct_github_access_resolves_an_exact_public_commit_without_tokens() { })); } +#[test] +fn direct_install_error_gives_an_agent_a_copyable_source_command() { + let remote = SkilldRemote::new( + Arc::new(FakeHttp::default()), + Arc::new(NoTokenProvider), + NativeRemoteConfig::Unconfigured, + ); + let selector = RemoteSelector::parse("skilld:skilld-dev/skills/example").unwrap(); + + let error = remote.prepare(&selector, true).unwrap_err(); + + assert_eq!(error.code, "DIRECT_SOURCE_REQUIRED"); + assert_eq!( + error.message, + "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Run: skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex" + ); +} + #[test] fn direct_github_access_rejects_private_repositories() { let http = Arc::new(FakeHttp::with([response( diff --git a/crates/skilld-native/tests/cli.rs b/crates/skilld-native/tests/cli.rs index 24d720eb..ec8c0dd2 100644 --- a/crates/skilld-native/tests/cli.rs +++ b/crates/skilld-native/tests/cli.rs @@ -56,6 +56,56 @@ fn version_reports_the_rust_package_version() { assert!(output.stderr.is_empty()); } +#[test] +fn install_help_gives_agents_actionable_source_and_target_grammar() { + let output = Command::new(binary()) + .args(["install", "--help"]) + .output() + .unwrap(); + + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + let help = String::from_utf8(output.stdout).unwrap(); + for guidance in [ + "skilld:OWNER/REPOSITORY/SKILL", + "github:OWNER/REPOSITORY/SKILL_PATH", + "github:OWNER/REPOSITORY/SKILL_PATH#branch:BRANCH", + "github:OWNER/REPOSITORY/SKILL_PATH#tag:TAG", + "github:OWNER/REPOSITORY/SKILL_PATH#commit:SHA", + "https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH", + "Values: claude-code, cursor, windsurf, cline, codex, github-copilot,", + "gemini-cli, goose, amp, opencode, roo, antigravity.", + "Repeat --agent to select more than one Agent target.", + "Values: copy, symlink. Default: install.mode. Its initial value is copy.", + "Default: detected Agent targets. If none exist, use agent.targets.", + "Default scope: current project.", + "Direct installs set source status to unverified.", + "Run skilld install without SOURCE to restore .skills/skilld-lock.yaml.", + "skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex", + ] { + assert!(help.contains(guidance), "missing help guidance: {guidance}"); + } +} + +#[test] +fn direct_source_error_gives_an_agent_a_copyable_command() { + let temporary = tempfile::tempdir().unwrap(); + let project = temporary.path().join("project"); + let data = temporary.path().join("data"); + let home = temporary.path().join("home"); + fs::create_dir_all(&project).unwrap(); + fs::create_dir_all(&home).unwrap(); + + let output = run(&project, &data, &home, &["install", "./skill", "--direct"]); + + assert_eq!(output.status.code(), Some(2)); + assert!(output.stdout.is_empty()); + assert_eq!( + String::from_utf8(output.stderr).unwrap(), + "DIRECT_SOURCE_REQUIRED: --direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Run: skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex\n" + ); +} + #[test] fn local_install_list_view_and_remove_use_project_state() { let temporary = tempfile::tempdir().unwrap(); From 6345457176d60fd0a555b950e97abf718039f2df Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Fri, 21 Aug 2026 14:50:17 +1000 Subject: [PATCH 2/4] fix(cli): make install recovery actionable --- crates/skilld-command/src/lib.rs | 28 +++++++++++--- crates/skilld-command/tests/remote.rs | 4 +- crates/skilld-native/tests/cli.rs | 54 ++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/crates/skilld-command/src/lib.rs b/crates/skilld-command/src/lib.rs index b3c6574d..7af6bcac 100644 --- a/crates/skilld-command/src/lib.rs +++ b/crates/skilld-command/src/lib.rs @@ -26,7 +26,7 @@ use skilld_core::{ InstallRequest, InstallScope, InstallSource, LockedSource, VERSION, select_target_ids, }; -const DIRECT_SOURCE_GUIDANCE: &str = "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Run: skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex"; +const DIRECT_SOURCE_GUIDANCE: &str = "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Remove --direct and retry the same command."; #[derive(Debug, Parser)] #[command( @@ -47,7 +47,7 @@ enum Command { /// Install a Skill or restore lockfile state. #[command( long_about = "Install a Skill or restore lockfile state.\n\nAccepted SOURCE values:\n skilld:OWNER/REPOSITORY/SKILL\n Install a hosted Artifact.\n github:OWNER/REPOSITORY/SKILL_PATH\n github:OWNER/REPOSITORY/SKILL_PATH#branch:BRANCH\n github:OWNER/REPOSITORY/SKILL_PATH#tag:TAG\n github:OWNER/REPOSITORY/SKILL_PATH#commit:SHA\n https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH\n These public GitHub Repository sources require --direct.\n ./RELATIVE_PATH or ABSOLUTE_PATH\n Install a local Skill.\n skilld\n Install the skilld-maintained Skill with --global.\n\nRun skilld install without SOURCE to restore .skills/skilld-lock.yaml.", - after_long_help = "Examples:\n skilld install skilld:skilld-dev/skills/vue --agent codex\n skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex\n skilld install" + after_long_help = "Examples:\n skilld install skilld:skilld-dev/skills/find-skill --agent codex\n skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex\n skilld install" )] Install { /// Select the Skill source. Omit SOURCE to restore lockfile state. @@ -244,10 +244,20 @@ impl CommandError { } } - fn direct_source_required() -> Self { + fn direct_local_source() -> Self { Self { code: "DIRECT_SOURCE_REQUIRED", - message: DIRECT_SOURCE_GUIDANCE.to_owned(), + message: + "--direct cannot install a local Skill. Remove --direct and retry the same command." + .to_owned(), + } + } + + fn direct_bundled_source() -> Self { + Self { + code: "DIRECT_SOURCE_REQUIRED", + message: "--direct cannot install the skilld-maintained Skill. Run: skilld install skilld --global" + .to_owned(), } } @@ -381,8 +391,14 @@ fn dispatch(command: Command, host: &H) -> Result, CommandE (true, InstallSource::Remote(source)) => { InstallOperation::Install(InstallSource::DirectRemote(source)) } - (true, _) => { - return Err(CommandError::direct_source_required()); + (true, InstallSource::DirectRemote(source)) => { + InstallOperation::Install(InstallSource::DirectRemote(source)) + } + (true, InstallSource::Local(_)) => { + return Err(CommandError::direct_local_source()); + } + (true, InstallSource::BundledSkilld) => { + return Err(CommandError::direct_bundled_source()); } (false, source) => InstallOperation::Install(source), }, diff --git a/crates/skilld-command/tests/remote.rs b/crates/skilld-command/tests/remote.rs index 20bcac76..536b6d3e 100644 --- a/crates/skilld-command/tests/remote.rs +++ b/crates/skilld-command/tests/remote.rs @@ -690,7 +690,7 @@ fn direct_github_access_resolves_an_exact_public_commit_without_tokens() { } #[test] -fn direct_install_error_gives_an_agent_a_copyable_source_command() { +fn direct_install_error_gives_an_agent_an_exact_recovery() { let remote = SkilldRemote::new( Arc::new(FakeHttp::default()), Arc::new(NoTokenProvider), @@ -703,7 +703,7 @@ fn direct_install_error_gives_an_agent_a_copyable_source_command() { assert_eq!(error.code, "DIRECT_SOURCE_REQUIRED"); assert_eq!( error.message, - "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Run: skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex" + "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Remove --direct and retry the same command." ); } diff --git a/crates/skilld-native/tests/cli.rs b/crates/skilld-native/tests/cli.rs index ec8c0dd2..588f89da 100644 --- a/crates/skilld-native/tests/cli.rs +++ b/crates/skilld-native/tests/cli.rs @@ -81,6 +81,7 @@ fn install_help_gives_agents_actionable_source_and_target_grammar() { "Default scope: current project.", "Direct installs set source status to unverified.", "Run skilld install without SOURCE to restore .skills/skilld-lock.yaml.", + "skilld install skilld:skilld-dev/skills/find-skill --agent codex", "skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex", ] { assert!(help.contains(guidance), "missing help guidance: {guidance}"); @@ -88,7 +89,7 @@ fn install_help_gives_agents_actionable_source_and_target_grammar() { } #[test] -fn direct_source_error_gives_an_agent_a_copyable_command() { +fn direct_local_source_error_gives_an_agent_an_exact_recovery() { let temporary = tempfile::tempdir().unwrap(); let project = temporary.path().join("project"); let data = temporary.path().join("data"); @@ -102,7 +103,56 @@ fn direct_source_error_gives_an_agent_a_copyable_command() { assert!(output.stdout.is_empty()); assert_eq!( String::from_utf8(output.stderr).unwrap(), - "DIRECT_SOURCE_REQUIRED: --direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Run: skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex\n" + "DIRECT_SOURCE_REQUIRED: --direct cannot install a local Skill. Remove --direct and retry the same command.\n" + ); +} + +#[test] +fn direct_bundled_source_error_gives_an_agent_an_exact_recovery() { + let temporary = tempfile::tempdir().unwrap(); + let project = temporary.path().join("project"); + let data = temporary.path().join("data"); + let home = temporary.path().join("home"); + fs::create_dir_all(&project).unwrap(); + fs::create_dir_all(&home).unwrap(); + + let output = run(&project, &data, &home, &["install", "skilld", "--direct"]); + + assert_eq!(output.status.code(), Some(2)); + assert!(output.stdout.is_empty()); + assert_eq!( + String::from_utf8(output.stderr).unwrap(), + "DIRECT_SOURCE_REQUIRED: --direct cannot install the skilld-maintained Skill. Run: skilld install skilld --global\n" + ); +} + +#[test] +fn direct_hosted_source_error_gives_an_agent_an_exact_recovery() { + let temporary = tempfile::tempdir().unwrap(); + let project = temporary.path().join("project"); + let data = temporary.path().join("data"); + let home = temporary.path().join("home"); + fs::create_dir_all(&project).unwrap(); + fs::create_dir_all(&home).unwrap(); + + let output = run( + &project, + &data, + &home, + &[ + "install", + "skilld:skilld-dev/skills/find-skill", + "--direct", + "--agent", + "codex", + ], + ); + + assert_eq!(output.status.code(), Some(2)); + assert!(output.stdout.is_empty()); + assert_eq!( + String::from_utf8(output.stderr).unwrap(), + "DIRECT_SOURCE_REQUIRED: --direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Remove --direct and retry the same command.\n" ); } From 1d7a769c0237a3f70a55fe531179b4424bc00269 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Fri, 21 Aug 2026 14:52:49 +1000 Subject: [PATCH 3/4] fix(cli): explain exact lockfile restore --- crates/skilld-command/src/lib.rs | 2 +- crates/skilld-native/tests/cli.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/skilld-command/src/lib.rs b/crates/skilld-command/src/lib.rs index 7af6bcac..e855e2f7 100644 --- a/crates/skilld-command/src/lib.rs +++ b/crates/skilld-command/src/lib.rs @@ -46,7 +46,7 @@ enum Command { Search { query: Vec }, /// Install a Skill or restore lockfile state. #[command( - long_about = "Install a Skill or restore lockfile state.\n\nAccepted SOURCE values:\n skilld:OWNER/REPOSITORY/SKILL\n Install a hosted Artifact.\n github:OWNER/REPOSITORY/SKILL_PATH\n github:OWNER/REPOSITORY/SKILL_PATH#branch:BRANCH\n github:OWNER/REPOSITORY/SKILL_PATH#tag:TAG\n github:OWNER/REPOSITORY/SKILL_PATH#commit:SHA\n https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH\n These public GitHub Repository sources require --direct.\n ./RELATIVE_PATH or ABSOLUTE_PATH\n Install a local Skill.\n skilld\n Install the skilld-maintained Skill with --global.\n\nRun skilld install without SOURCE to restore .skills/skilld-lock.yaml.", + long_about = "Install a Skill or restore lockfile state.\n\nAccepted SOURCE values:\n skilld:OWNER/REPOSITORY/SKILL\n Install a hosted Artifact.\n github:OWNER/REPOSITORY/SKILL_PATH\n github:OWNER/REPOSITORY/SKILL_PATH#branch:BRANCH\n github:OWNER/REPOSITORY/SKILL_PATH#tag:TAG\n github:OWNER/REPOSITORY/SKILL_PATH#commit:SHA\n https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH\n These public GitHub Repository sources require --direct.\n ./RELATIVE_PATH or ABSOLUTE_PATH\n Install a local Skill.\n skilld\n Install the skilld-maintained Skill with --global.\n\nRun skilld install without SOURCE to restore .skills/skilld-lock.yaml.\nVerified remote Skills restore the exact locked Git commit.", after_long_help = "Examples:\n skilld install skilld:skilld-dev/skills/find-skill --agent codex\n skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex\n skilld install" )] Install { diff --git a/crates/skilld-native/tests/cli.rs b/crates/skilld-native/tests/cli.rs index 588f89da..640a8353 100644 --- a/crates/skilld-native/tests/cli.rs +++ b/crates/skilld-native/tests/cli.rs @@ -81,6 +81,7 @@ fn install_help_gives_agents_actionable_source_and_target_grammar() { "Default scope: current project.", "Direct installs set source status to unverified.", "Run skilld install without SOURCE to restore .skills/skilld-lock.yaml.", + "Verified remote Skills restore the exact locked Git commit.", "skilld install skilld:skilld-dev/skills/find-skill --agent codex", "skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex", ] { From 0e0386902cf78efcf22382881ea1cb0a649ed428 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Sat, 22 Aug 2026 00:01:45 +1000 Subject: [PATCH 4/4] fix(cli): humanize install help and error copy --- crates/skilld-command/src/lib.rs | 20 ++++++++++---------- crates/skilld-command/tests/remote.rs | 2 +- crates/skilld-native/tests/cli.rs | 18 ++++++++++-------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/crates/skilld-command/src/lib.rs b/crates/skilld-command/src/lib.rs index bab30c83..a43d7204 100644 --- a/crates/skilld-command/src/lib.rs +++ b/crates/skilld-command/src/lib.rs @@ -38,7 +38,7 @@ use output::{ resolve_mode, }; -const DIRECT_SOURCE_GUIDANCE: &str = "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Remove --direct and retry the same command."; +const DIRECT_SOURCE_GUIDANCE: &str = "--direct requires a github:OWNER/REPOSITORY/SKILL_PATH source or a GitHub tree URL. Remove --direct, then run the same command again."; #[derive(Debug, Parser)] #[command( @@ -62,35 +62,35 @@ pub struct Cli { enum Command { /// Search for Skills. Search { query: Vec }, - /// Install a Skill or restore lockfile state. + /// Install a Skill, or restore the Skills recorded in your lockfile. #[command( - long_about = "Install a Skill or restore lockfile state.\n\nAccepted SOURCE values:\n skilld:OWNER/REPOSITORY/SKILL\n Install a hosted Artifact.\n github:OWNER/REPOSITORY/SKILL_PATH\n github:OWNER/REPOSITORY/SKILL_PATH#branch:BRANCH\n github:OWNER/REPOSITORY/SKILL_PATH#tag:TAG\n github:OWNER/REPOSITORY/SKILL_PATH#commit:SHA\n https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH\n These public GitHub Repository sources require --direct.\n ./RELATIVE_PATH or ABSOLUTE_PATH\n Install a local Skill.\n skilld\n Install the skilld-maintained Skill with --global.\n\nRun skilld install without SOURCE to restore .skills/skilld-lock.yaml.\nVerified remote Skills restore the exact locked Git commit.", + long_about = "Install a Skill, or restore the Skills recorded in your lockfile.\n\nGive SOURCE as:\n skilld:OWNER/REPOSITORY/SKILL\n Install a hosted Artifact.\n github:OWNER/REPOSITORY/SKILL_PATH\n github:OWNER/REPOSITORY/SKILL_PATH#branch:BRANCH\n github:OWNER/REPOSITORY/SKILL_PATH#tag:TAG\n github:OWNER/REPOSITORY/SKILL_PATH#commit:SHA\n https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH\n Public GitHub Repository paths. Each one requires --direct.\n ./RELATIVE_PATH or ABSOLUTE_PATH\n Install a local Skill.\n skilld\n Install the skilld-maintained Skill with --global.\n\nRun skilld install without SOURCE to restore .skills/skilld-lock.yaml.\nVerified remote Skills restore the exact locked Git commit.", after_long_help = "Examples:\n skilld install skilld:skilld-dev/skills/find-skill --agent codex\n skilld install github:skilld-dev/skilld/skills/skilld --direct --agent codex\n skilld install" )] Install { - /// Select the Skill source. Omit SOURCE to restore lockfile state. + /// The Skill source to install. Omit SOURCE to restore .skills/skilld-lock.yaml. #[arg(value_name = "SOURCE")] source: Option, #[arg( long, - long_help = "Use account-level Agent targets. Default scope: current project." + long_help = "Install to your account-level Agent targets. The default is the current project." )] global: bool, #[arg( long = "agent", value_name = "AGENT", - long_help = "Select an Agent target. Repeat --agent to select more than one Agent target.\nValues: claude-code, cursor, windsurf, cline, codex, github-copilot,\n gemini-cli, goose, amp, opencode, roo, antigravity.\nDefault: detected Agent targets. If none exist, use agent.targets." + long_help = "Select an Agent target. Repeat --agent to select several.\nValues: claude-code, cursor, windsurf, cline, codex, github-copilot,\n gemini-cli, goose, amp, opencode, roo, antigravity.\nDefault: every Agent target skilld detects. If skilld detects none, it uses agent.targets." )] agents: Vec, #[arg( long, value_name = "MODE", - long_help = "Set how Agent targets receive the Skill.\nValues: copy, symlink. Default: install.mode. Its initial value is copy." + long_help = "Choose how each Agent target receives the Skill.\nValues: copy, symlink. The default comes from install.mode. A fresh configuration sets install.mode to copy." )] mode: Option, #[arg( long, - long_help = "Fetch a public GitHub Repository without skilld.dev.\nRequires a github: source or GitHub tree URL.\nDirect installs set source status to unverified." + long_help = "Fetch a public GitHub Repository without going through skilld.dev.\nGive a github: source or a GitHub tree URL.\nA direct install records the unverified source status." )] direct: bool, }, @@ -300,14 +300,14 @@ impl CommandError { fn direct_local_source() -> Self { Self::usage( "DIRECT_SOURCE_REQUIRED", - "--direct cannot install a local Skill. Remove --direct and retry the same command.", + "--direct cannot install a local Skill. Remove --direct, then run the same command again.", ) } fn direct_bundled_source() -> Self { Self::usage( "DIRECT_SOURCE_REQUIRED", - "--direct cannot install the skilld-maintained Skill. Run: skilld install skilld --global", + "--direct cannot install the skilld-maintained Skill. Run skilld install skilld --global instead", ) } diff --git a/crates/skilld-command/tests/remote.rs b/crates/skilld-command/tests/remote.rs index 8cf9467b..39f665c3 100644 --- a/crates/skilld-command/tests/remote.rs +++ b/crates/skilld-command/tests/remote.rs @@ -1324,7 +1324,7 @@ fn direct_install_error_gives_an_agent_an_exact_recovery() { assert_eq!(error.code, "DIRECT_SOURCE_REQUIRED"); assert_eq!( error.message, - "--direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Remove --direct and retry the same command." + "--direct requires a github:OWNER/REPOSITORY/SKILL_PATH source or a GitHub tree URL. Remove --direct, then run the same command again." ); } diff --git a/crates/skilld-native/tests/cli.rs b/crates/skilld-native/tests/cli.rs index 1eee0a37..0690e64a 100644 --- a/crates/skilld-native/tests/cli.rs +++ b/crates/skilld-native/tests/cli.rs @@ -188,11 +188,13 @@ fn install_help_gives_agents_actionable_source_and_target_grammar() { "https://github.com/OWNER/REPOSITORY/tree/REF/SKILL_PATH", "Values: claude-code, cursor, windsurf, cline, codex, github-copilot,", "gemini-cli, goose, amp, opencode, roo, antigravity.", - "Repeat --agent to select more than one Agent target.", - "Values: copy, symlink. Default: install.mode. Its initial value is copy.", - "Default: detected Agent targets. If none exist, use agent.targets.", - "Default scope: current project.", - "Direct installs set source status to unverified.", + "Repeat --agent to select several.", + "Default: every Agent target skilld detects.", + "If skilld detects none, it uses agent.targets.", + "Values: copy, symlink. The default comes from install.mode.", + "A fresh configuration sets install.mode to copy.", + "The default is the current project.", + "A direct install records the unverified source status.", "Run skilld install without SOURCE to restore .skills/skilld-lock.yaml.", "Verified remote Skills restore the exact locked Git commit.", "skilld install skilld:skilld-dev/skills/find-skill --agent codex", @@ -217,7 +219,7 @@ fn direct_local_source_error_gives_an_agent_an_exact_recovery() { assert!(output.stdout.is_empty()); assert_eq!( String::from_utf8(output.stderr).unwrap(), - "DIRECT_SOURCE_REQUIRED: --direct cannot install a local Skill. Remove --direct and retry the same command.\n" + "DIRECT_SOURCE_REQUIRED: --direct cannot install a local Skill. Remove --direct, then run the same command again.\n" ); } @@ -236,7 +238,7 @@ fn direct_bundled_source_error_gives_an_agent_an_exact_recovery() { assert!(output.stdout.is_empty()); assert_eq!( String::from_utf8(output.stderr).unwrap(), - "DIRECT_SOURCE_REQUIRED: --direct cannot install the skilld-maintained Skill. Run: skilld install skilld --global\n" + "DIRECT_SOURCE_REQUIRED: --direct cannot install the skilld-maintained Skill. Run skilld install skilld --global instead\n" ); } @@ -266,7 +268,7 @@ fn direct_hosted_source_error_gives_an_agent_an_exact_recovery() { assert!(output.stdout.is_empty()); assert_eq!( String::from_utf8(output.stderr).unwrap(), - "DIRECT_SOURCE_REQUIRED: --direct requires github:OWNER/REPOSITORY/SKILL_PATH or a GitHub tree URL. Remove --direct and retry the same command.\n" + "DIRECT_SOURCE_REQUIRED: --direct requires a github:OWNER/REPOSITORY/SKILL_PATH source or a GitHub tree URL. Remove --direct, then run the same command again.\n" ); }