Skip to main content

harness_skills/
lib.rs

1//! `harness-skills` — agentskills.io-compliant skill loader, validator, and registry.
2//!
3//! ## What this crate enforces
4//!
5//! Strict subset of <https://agentskills.io/specification>:
6//!
7//! - `<name>/SKILL.md` directory layout.
8//! - Frontmatter fields: `name`, `description` (required) + `license`,
9//!   `compatibility`, `metadata`, `allowed-tools` (optional). Unknown
10//!   top-level fields are rejected.
11//! - `name`: 1–64 chars, lowercase `[a-z0-9]` + hyphen, no leading/trailing
12//!   hyphen, no `--`, and **must equal the parent directory name**.
13//! - `description`: 1–1024 chars.
14//! - `compatibility`: ≤500 chars.
15//!
16//! Framework extensions live in `metadata.harness.*` per DESIGN.md §6.1.
17
18pub mod export;
19pub mod lint;
20pub mod loader;
21pub mod registry;
22pub mod validate;
23pub mod write;
24
25pub use export::*;
26pub use lint::*;
27pub use loader::*;
28pub use registry::*;
29pub use validate::*;
30pub use write::*;
31
32use harness_core::{Resource, Skill, SkillError, SkillManifest};
33use std::borrow::Cow;
34use std::sync::Arc;
35
36/// A skill loaded from disk: manifest, full body, and resource index.
37#[derive(Debug, Clone)]
38pub struct FileSkill {
39    manifest: SkillManifest,
40    body: String,
41    resources: Vec<Resource>,
42}
43
44impl FileSkill {
45    pub fn new(manifest: SkillManifest, body: String, resources: Vec<Resource>) -> Self {
46        Self {
47            manifest,
48            body,
49            resources,
50        }
51    }
52}
53
54impl Skill for FileSkill {
55    fn manifest(&self) -> &SkillManifest {
56        &self.manifest
57    }
58    fn body(&self) -> Cow<'_, str> {
59        Cow::Borrowed(&self.body)
60    }
61    fn resources(&self) -> &[Resource] {
62        &self.resources
63    }
64}
65
66/// Load a single skill from `<path>/SKILL.md`, returning an opaque trait object.
67pub fn load_skill_dir(path: &std::path::Path) -> Result<Arc<dyn Skill>, SkillError> {
68    let s = loader::load(path)?;
69    Ok(Arc::new(s))
70}