Skip to main content

agents_skills/manager/
types.rs

1//! Request and outcome data types carried by the `Manager` facade.
2//!
3//! Pure data (constructors and defaults only); kept in their own module so the
4//! manager implementation in `mod.rs` stays focused on the methods.
5
6use std::path::PathBuf;
7
8use serde::Serialize;
9
10use crate::core::discover::Skill;
11use crate::core::link::LinkOutcome;
12use crate::core::source::Source;
13
14// ============================ Request types (clap-free) ============================
15
16/// Request for [`Manager::add`].
17///
18/// The struct is `Default + Clone`; use [`AddRequest::new`] for the common case
19/// and struct-update syntax (`..Default::default()`) to override just the fields
20/// you need.
21///
22/// `source` accepts exactly two forms: a local skill directory (its `SKILL.md`
23/// must declare a non-empty `name`), or the GitHub id `owner/repo/slug`
24/// for one named skill on GitHub.
25#[derive(Debug, Clone, Default)]
26pub struct AddRequest {
27    /// Local skill directory, or the GitHub id `owner/repo/slug`.
28    pub source: String,
29    /// Branch, tag, or full commit SHA to pin (GitHub sources only; `None` = default branch).
30    pub reference: Option<String>,
31    /// Overwrite an already installed skill instead of skipping it.
32    pub force: bool,
33}
34
35impl AddRequest {
36    /// Create a request that installs the skill named by `source` with default options.
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use agents_skills::{AddRequest, Manager};
42    ///
43    /// let req = AddRequest::new("anthropics/skills/pdf");
44    /// assert_eq!(req.source, "anthropics/skills/pdf");
45    /// assert!(req.reference.is_none()); // default branch
46    /// # let _ = Manager::new().ok();
47    /// ```
48    pub fn new(source: impl Into<String>) -> Self {
49        AddRequest {
50            source: source.into(),
51            ..Default::default()
52        }
53    }
54}
55
56/// Request for [`Manager::remove`], [`Manager::disable`] and [`Manager::enable`]
57/// — the three commands that select installed skills by name.
58///
59/// `Default` is a no-op that only reports the currently selectable names — set
60/// `skills` or `all` to actually apply anything.
61#[derive(Debug, Clone, Default)]
62pub struct SelectionRequest {
63    /// Skill names to select (the CLI merges positional args and `--skill` here).
64    pub skills: Vec<String>,
65    /// Select every currently available skill (all installed for
66    /// remove/disable, all disabled for enable).
67    pub all: bool,
68}
69
70/// Request for [`Manager::agent`] — one entry point mirroring the `agent` CLI command.
71///
72/// `Default` links the auto-detected installed agents.
73#[derive(Debug, Clone, Default)]
74pub struct AgentRequest {
75    /// `"*"` or specific agent names; empty = auto-detect installed agents.
76    pub agents: Vec<String>,
77    /// Unlink (disconnect) the agents' skills dirs instead of linking them.
78    pub unlink: bool,
79}
80
81// ============================ Outcome types ============================
82
83/// Result of [`Manager::add`] — one source always resolves to exactly one skill.
84#[derive(Debug)]
85pub struct AddOutcome {
86    /// The parsed source.
87    pub source: Source,
88    /// The skill that was targeted.
89    pub skill: Skill,
90    /// Canonical directory of the skill.
91    pub canonical_path: PathBuf,
92    /// `true` when nothing was copied because a skill of the same name is
93    /// already installed (enabled or disabled) — `add` never overwrites.
94    pub skipped: bool,
95}
96
97/// Result of linking (or unlinking) one agent's skills dir relative to the
98/// canonical dir.
99#[derive(Debug)]
100pub struct AgentLinkResult {
101    /// Agent identifier (as used on the CLI).
102    pub agent: String,
103    /// Agent display name.
104    pub display: String,
105    /// Link/unlink outcome details.
106    pub outcome: LinkOutcome,
107}
108
109/// Link status of one agent (used by `agent --status`).
110#[derive(Debug)]
111pub struct AgentStatus {
112    /// Agent identifier (as used on the CLI).
113    pub name: String,
114    /// Agent display name.
115    pub display: String,
116    /// Whether the agent's skills dir is linked to the canonical dir.
117    pub linked: bool,
118    /// Whether the agent natively uses the canonical dir (no link involved).
119    pub canonical: bool,
120    /// Skills inside the agent's own skills dir: real subdirs and dir-targeting
121    /// symlinks — the same classification linking uses. Only populated for
122    /// unlinked, non-canonical agents; empty for linked/canonical agents (they
123    /// share the canonical dir, shown by `list`).
124    pub internal_skills: Vec<String>,
125    /// Non-skill entries (files, symlinks to non-directories) inside the agent's
126    /// own skills dir. Same population rules as [`AgentStatus::internal_skills`].
127    pub internal_others: Vec<String>,
128}
129
130/// Result of [`Manager::agent`].
131#[derive(Debug)]
132pub struct AgentOutcome {
133    /// Per-agent link results.
134    pub results: Vec<AgentLinkResult>,
135}
136
137/// A listed skill (serialized by `list --json`).
138///
139/// Fields are serialized in camelCase — the exact JSON shape emitted by the
140/// CLI's `list --json`.
141#[derive(Debug, Serialize)]
142#[serde(rename_all = "camelCase")]
143pub struct ListedSkill {
144    /// Skill slug — the SKILL.md frontmatter `name` slugified (lowercase,
145    /// spaces as dashes). This is the identity every command
146    /// (`add`/`remove`/`disable`/`enable`) selects by, and the on-disk
147    /// directory name of a fresh install. Directories whose manifest declares
148    /// no `name` are not skills and are never listed.
149    pub name: String,
150    /// The frontmatter `name` as declared — a display-only rendition of the
151    /// same skill (it may contain spaces and mixed case the slug folds away).
152    pub display_name: String,
153    /// Skill description (from `SKILL.md` frontmatter).
154    pub description: String,
155    /// The skill's real on-disk directory (in the canonical dir when enabled,
156    /// in the sibling `disabled-skills` dir when disabled). The on-disk
157    /// directory name can differ from the slug for skills adopted from an
158    /// agent dir.
159    pub path: std::path::PathBuf,
160    /// Whether the skill is enabled (`true`) or parked in `disabled-skills` (`false`).
161    pub enabled: bool,
162    /// The skill directory's creation time, as Unix seconds (UTC) — an
163    /// approximation of when it landed on disk. Exact for `add` installs, but a
164    /// skill adopted from an agent dir keeps that dir's original time. `None`
165    /// when the platform/filesystem records no creation time (some Linux
166    /// filesystems).
167    pub installed_at: Option<u64>,
168}
169
170/// Result of [`Manager::remove`], [`Manager::disable`] and [`Manager::enable`]
171/// — the three selection commands share one outcome shape.
172#[derive(Debug)]
173pub struct SelectionOutcome {
174    /// Names of the skills currently selectable by the command (installed
175    /// skills for remove/disable, disabled skills for enable) — used by the
176    /// no-args hint.
177    pub available: Vec<String>,
178    /// Requested names (used by the no-match hint).
179    pub requested: Vec<String>,
180    /// Names the command actually applied to (removed / disabled / enabled),
181    /// as reported.
182    pub applied: Vec<String>,
183    /// Names that were already in the target state (idempotent no-op).
184    pub already: Vec<String>,
185    /// Requested names that matched nothing.
186    pub missing: Vec<String>,
187}