Skip to main content

agent_config/agents/cline/
skills.rs

1//! Cline skills surface. Directory-scoped skills at `.cline/skills/<name>/`
2//! (Local) or `~/.cline/skills/<name>/` (Global).
3
4use std::path::PathBuf;
5
6use crate::agents::planning as agent_planning;
7use crate::error::AgentConfigError;
8use crate::integration::{InstallReport, SkillSurface, UninstallReport};
9use crate::paths;
10use crate::plan::{InstallPlan, UninstallPlan};
11use crate::scope::{Scope, ScopeKind};
12use crate::spec::SkillSpec;
13use crate::status::StatusReport;
14use crate::util::{ownership, skills_dir};
15
16use super::ClineAgent;
17
18impl ClineAgent {
19    pub(super) fn skills_root(scope: &Scope) -> Result<PathBuf, AgentConfigError> {
20        Ok(match scope {
21            Scope::Global => paths::home_dir()?.join(".cline").join("skills"),
22            Scope::Local(p) => p.join(".cline").join("skills"),
23        })
24    }
25}
26
27impl SkillSurface for ClineAgent {
28    fn id(&self) -> &'static str {
29        "cline"
30    }
31
32    fn supported_skill_scopes(&self) -> &'static [ScopeKind] {
33        &[ScopeKind::Global, ScopeKind::Local]
34    }
35
36    fn skill_status(
37        &self,
38        scope: &Scope,
39        name: &str,
40        expected_owner: &str,
41    ) -> Result<StatusReport, AgentConfigError> {
42        SkillSpec::validate_name(name)?;
43        let root = Self::skills_root(scope)?;
44        let (dir, manifest, ledger) = skills_dir::paths_for_status(&root, name);
45        let recorded = ownership::owner_of(&ledger, name)?;
46        Ok(StatusReport::for_skill(
47            name,
48            dir,
49            manifest,
50            ledger,
51            expected_owner,
52            recorded,
53        ))
54    }
55
56    fn plan_install_skill(
57        &self,
58        scope: &Scope,
59        spec: &SkillSpec,
60    ) -> Result<InstallPlan, AgentConfigError> {
61        agent_planning::skill_install(
62            SkillSurface::id(self),
63            scope,
64            spec,
65            Self::skills_root(scope),
66        )
67    }
68
69    fn plan_uninstall_skill(
70        &self,
71        scope: &Scope,
72        name: &str,
73        owner_tag: &str,
74    ) -> Result<UninstallPlan, AgentConfigError> {
75        agent_planning::skill_uninstall(
76            SkillSurface::id(self),
77            scope,
78            name,
79            owner_tag,
80            Self::skills_root(scope),
81        )
82    }
83
84    fn install_skill(
85        &self,
86        scope: &Scope,
87        spec: &SkillSpec,
88    ) -> Result<InstallReport, AgentConfigError> {
89        let root = Self::skills_root(scope)?;
90        scope.ensure_contained(&root)?;
91        skills_dir::install(&root, spec)
92    }
93
94    fn uninstall_skill(
95        &self,
96        scope: &Scope,
97        name: &str,
98        owner_tag: &str,
99    ) -> Result<UninstallReport, AgentConfigError> {
100        let root = Self::skills_root(scope)?;
101        scope.ensure_contained(&root)?;
102        skills_dir::uninstall(&root, name, owner_tag)
103    }
104}