diff --git a/docs/HELP.md b/docs/HELP.md index 6c81514..825b980 100644 --- a/docs/HELP.md +++ b/docs/HELP.md @@ -487,12 +487,16 @@ List recent scans for a repository Install Detail skills (default: detail-bugs) -**Usage:** `detail skill [COMMAND]` +**Usage:** `detail skill [OPTIONS] [COMMAND]` ###### **Subcommands:** * `rules` — Install the detail-create-rules skill +###### **Options:** + +* `--user` — Install to user-level ~/.claude/skills instead of the current repo + ## `detail skill rules` diff --git a/src/commands/skill.rs b/src/commands/skill.rs index a49e9ac..9505a8b 100644 --- a/src/commands/skill.rs +++ b/src/commands/skill.rs @@ -5,6 +5,7 @@ use std::str; use anyhow::{Context, Result}; use clap::Subcommand; +use homedir::my_home; const BUGS_SKILL_CONTENT: &str = include_str!("../../.claude/skills/detail-bugs/SKILL.md"); const RULES_SKILL_CONTENT: &str = include_str!("../../.claude/skills/detail-create-rules/SKILL.md"); @@ -22,14 +23,19 @@ fn parse_git_root_output(success: bool, stdout: &[u8]) -> Result { Ok(PathBuf::from(root.trim())) } -fn skill_install_path(repo_root: &Path, skill_name: &str) -> PathBuf { - repo_root - .join(".claude") +fn skill_install_path(base: &Path, skill_name: &str) -> PathBuf { + base.join(".claude") .join("skills") .join(skill_name) .join("SKILL.md") } +fn user_home() -> Result { + my_home() + .context("failed to determine home directory")? + .context("home directory not found") +} + fn git_root() -> Result { let output = Command::new("git") .args(["rev-parse", "--show-toplevel"]) @@ -52,12 +58,12 @@ fn install_skill(repo_root: &Path, skill_name: &str, content: &str) -> Result<() Ok(()) } -pub fn handle(command: Option<&SkillCommands>) -> Result<()> { - let root = git_root()?; +pub fn handle(command: Option<&SkillCommands>, user: bool) -> Result<()> { + let base = if user { user_home()? } else { git_root()? }; match command { - None => install_skill(&root, "detail-bugs", BUGS_SKILL_CONTENT), + None => install_skill(&base, "detail-bugs", BUGS_SKILL_CONTENT), Some(SkillCommands::Rules) => { - install_skill(&root, "detail-create-rules", RULES_SKILL_CONTENT) + install_skill(&base, "detail-create-rules", RULES_SKILL_CONTENT) } } } @@ -101,4 +107,13 @@ mod tests { PathBuf::from("/work/repo/.claude/skills/detail-create-rules/SKILL.md") ); } + + #[test] + fn skill_install_path_user_level() { + let path = skill_install_path(Path::new("/home/alice"), "detail-bugs"); + assert_eq!( + path, + PathBuf::from("/home/alice/.claude/skills/detail-bugs/SKILL.md") + ); + } } diff --git a/src/lib.rs b/src/lib.rs index 4d9daa6..6e4c0d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,7 +148,7 @@ impl Cli { Commands::SatisfyingSort => commands::satisfying_sort::handle().await, Commands::Repos { command } => commands::repos::handle(command, &self).await, Commands::Scans { command } => commands::scans::handle(command, &self).await, - Commands::Skill { command } => commands::skill::handle(command.as_ref()), + Commands::Skill { user, command } => commands::skill::handle(command.as_ref(), *user), Commands::Update => commands::update::handle().await, Commands::Version => { console::Term::stdout().write_line(&format!("detail-cli v{VERSION}"))?; @@ -209,6 +209,10 @@ enum Commands { /// Install Detail skills (default: detail-bugs) Skill { + /// Install to user-level ~/.claude/skills instead of the current repo + #[arg(long, global = true)] + user: bool, + #[command(subcommand)] command: Option, },