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}