spec_driven_docs/commands/
skill.rs1use camino::Utf8PathBuf;
9
10use crate::cli::skill::{Agent, SkillArgs, SkillCommand};
11use crate::context::AppContext;
12use crate::domain::paths::{
13 AgentId, LEGACY_SHARED_ROOT, LEGACY_SKILL_RECEIPT_PATH, SKILL_RECEIPT_FILE, UserEnv,
14};
15use crate::error::AppError;
16use crate::output;
17use crate::services::skill_installer::{self, Layout, home};
18
19fn layout(agent: Agent) -> Result<Layout, AppError> {
21 let home = home()?;
22 let env = UserEnv::from_process();
23 let state_root = env
24 .state_root()
25 .ok_or_else(|| AppError::Usage("no state root resolves".to_string()))?
26 .path;
27 Ok(Layout {
28 roots: roots(&env, agent),
29 receipt: state_root.join(SKILL_RECEIPT_FILE),
30 legacy_receipt: home.join(LEGACY_SKILL_RECEIPT_PATH),
31 legacy_shared: home.join(LEGACY_SHARED_ROOT),
32 state_root,
33 })
34}
35
36fn roots(env: &UserEnv, agent: Agent) -> Vec<Utf8PathBuf> {
38 let selected: &[AgentId] = match agent {
39 Agent::Claude => &[AgentId::Claude],
40 Agent::Agents => &[AgentId::Agents],
41 Agent::All => &[AgentId::Claude, AgentId::Agents],
42 };
43 env.agent_roots(selected)
44 .into_iter()
45 .map(|entry| entry.path)
46 .collect()
47}
48
49pub fn run(_ctx: &AppContext, args: SkillArgs) -> Result<(), AppError> {
56 match args.command {
57 SkillCommand::List => {
58 for name in crate::embedded::skill_names() {
59 output::line(name);
60 }
61 Ok(())
62 }
63 SkillCommand::Show(show) => {
64 let Some(text) = crate::embedded::skill(&show.name) else {
65 return Err(AppError::Usage(format!("no such skill: {}", show.name)));
66 };
67 output::line(text.trim_end_matches('\n'));
68 Ok(())
69 }
70 SkillCommand::Install(install) => {
71 let layout = layout(install.agent)?;
72 let lines = skill_installer::install(&layout, install.apply, install.force)?;
73 for line in lines {
74 output::line(line);
75 }
76 Ok(())
77 }
78 SkillCommand::Uninstall(uninstall) => {
79 let layout = layout(uninstall.agent)?;
80 let lines = skill_installer::uninstall(&layout, uninstall.apply)?;
81 for line in lines {
82 output::line(line);
83 }
84 Ok(())
85 }
86 }
87}