the interface
Every public item, in the order the interface files list it. A signature here is the one the compiler sees.
01moonbitstack/moonskillof 02
The document: what a SKILL.md may say, how it is judged, and how a client reads the three tiers.
Values
base_name
function
fn base_name(StringView) -> String
The last segment of a path, which is the directory name the specification compares a skill's name against.
discover
function
async fn discover(&Source) -> Array[Skill]
Every skill a source offers, read and parsed.
A source that lists a directory with no document is a broken source, not an empty one, so discover refuses rather than skipping it: silently offering a model a skill it cannot open is how an agent comes to invent a tool it does not have.
faults
function
fn faults(StringView, root? : String, limits? : Limits) -> Array[Fault]
Every fault in a document, from the fence to the last rule.
This is the call a validator makes: a document that does not parse is one fault of kind malformed, and the rest of the list is whatever validate finds in the fields. It is the whole check of a SKILL.md in one function, which is what a command line and a test both want.
join_path
function
fn join_path(StringView, StringView) -> String
a and b with exactly one / between them, whichever slashes either already carries.
Paths are joined here rather than with the platform's separator: a skill root is a key in a Source, and a key that reads the same on every system is the one a report can be compared against.
limits
value
let limits : Limits
The limits of the Agent Skills specification.
load
function
async fn load(&Source, root~ : String) -> Skill
One skill from a directory.
Raises when the directory holds no document, or when the document is not readable as one: a caller that wants to carry on past a broken skill uses check, which reports the same trouble as a fault.
parse
function
fn parse(StringView, root? : String) -> Skill raise Refused
Read a skill document.
The document opens with a --- fence, then YAML, then a closing ---, then the Markdown body. The YAML is read as StrictYAML, the dialect the specification was written against: every scalar is text, so name: 42 is the name forty-two rather than a number, and a frontmatter that means something other than what it looks like is refused instead of guessed.
root is the directory the document came from. validate compares the directory name with the skill name, and Skill::location needs it to name the document; leave it out when the text has no directory yet.
Reading is lenient about the fields themselves: a document with no name still parses, and validate says what is wrong with it. Only a document that is not a skill document at all is refused.
prompt
function
fn prompt(ArrayView[Skill]) -> String
The block a client puts in a system prompt so a model knows which skills exist, without putting their bodies in the context.
This is the first tier of the specification's progressive disclosure: one entry per skill, the name and the description, and where the document is so the model can ask for the rest. A skill with an empty description still gets an entry — a model that cannot see the name cannot choose the skill, and the description is what validate reports.
validate
function
fn validate(Skill, limits? : Limits) -> Array[Fault]
Every rule the specification states about the frontmatter, judged against the skill's own text.
The list is empty when the skill is valid. It is not a refusal: a validator reports all the faults at once, because a person fixing a document wants the whole list rather than the first line.
Two things are deliberately not faults. The body length and the token budget are recommendations — the specification asks for a SKILL.md under five hundred lines and says nothing breaks when it is longer — and an unknown key under metadata is the caller's business, since the specification reserves that mapping for clients.
Errors
Refused
error
pub(all) suberror Refused
| Variant | Payload | Meaning |
|---|---|---|
MissingFrontmatter | — | The text does not open with the --- fence. |
UnclosedFrontmatter | — | The opening fence has no closing ---. |
BadYaml | String | The frontmatter is not YAML the strict reader accepts. The text is the reader's complaint, which begins with the line and column. |
NotAMapping | — | The frontmatter is YAML, but not a mapping. |
NotUtf8 | Int32 | The bytes are not UTF-8; the offset is where decoding stopped. |
NoDocument | — | A skill directory offered neither SKILL.md nor skill.md. |
Refused::equal
function
fn Refused::equal(Self, Self) -> Bool
Refused::message
function
fn Refused::message(Self) -> String
The refusal in one sentence, worded as the reference validator words it so a report from either reads the same.
Refused::not_equal
function
fn Refused::not_equal(Self, Self) -> Bool
Refused::output
function
fn Refused::output(Self, &Logger) -> Unit
Refused::to_repr
function
fn Refused::to_repr(Self) -> @debug.Repr
Refused::to_string
function
fn Refused::to_string(Self) -> String
implements Show
Types and methods
Fault
enum
pub(all) enum Fault
| Variant | Payload | Meaning |
|---|---|---|
UnexpectedFields | Array[String] | Frontmatter keys the specification does not define, sorted. |
MissingName | — | The frontmatter has no name. |
NameEmpty | — | name is empty, blank, or not text. |
NameTooLong | String, Int32, Int32 | name is longer than the limit: the name, how long it is, and the limit. |
NameNotLowercase | String | name has an uppercase letter. |
NameEdgeHyphen | — | name starts or ends with a hyphen. |
NameDoubleHyphen | — | name has two hyphens in a row. |
NameBadCharacter | String | name has a character that is neither a letter, a digit, nor a hyphen. |
NameMismatch | String, String | The directory and the name differ: the directory name, then the name. |
MissingDescription | — | The frontmatter has no description. |
DescriptionEmpty | — | description is empty, blank, or not text. |
DescriptionTooLong | Int32, Int32 | description is longer than the limit: how long it is, then the limit. |
CompatibilityNotText | — | compatibility is present and is not text. |
CompatibilityTooLong | Int32, Int32 | compatibility is longer than the limit: how long it is, then the limit. |
Malformed | Refused | The document could not be read as a skill at all. |
MissingPath | String | There is nothing at the path. |
NotADirectory | String | The path is a file, not a skill directory. |
MissingDocument | — | The directory has no SKILL.md or skill.md. |
Fault::equal
function
fn Fault::equal(Self, Self) -> Bool
Fault::kind
function
fn Fault::kind(Self) -> String
A stable tag for the fault, for reports that are read by a program rather than by a person. The wording of Fault::message may be rephrased; these do not change.
Fault::message
function
fn Fault::message(Self) -> String
The fault in one sentence, worded as the reference validator words it so a report from either reads the same.
Fault::not_equal
function
fn Fault::not_equal(Self, Self) -> Bool
Fault::output
function
fn Fault::output(Self, &Logger) -> Unit
Fault::to_repr
function
fn Fault::to_repr(Self) -> @debug.Repr
Fault::to_string
function
fn Fault::to_string(Self) -> String
implements Show
Limits
struct
pub(all) struct Limits
The length limits the specification puts on a frontmatter field.
They are a record rather than three constants because a client may enforce a tighter budget than the specification's — a model with a smaller context wants shorter descriptions — and because a test that reads the boundary should read it from one place.
| Field | Type | Meaning |
|---|---|---|
name | Int32 | The longest name, in characters. The specification says 64. |
description | Int32 | The longest description, in characters. The specification says 1024. |
compatibility | Int32 | The longest compatibility, in characters. The specification says 500. |
Skill
struct
pub(all) struct Skill
A skill document: the frontmatter the specification defines, the Markdown an agent reads when the skill is activated, and the directory it came from.
The specification loads a skill in three tiers, and the fields follow them: Skill::summary is the tier a client reads for every skill it knows about, body is the tier it reads once the skill is chosen, and the files the body points at are read one at a time through Skill::resolve when the task reaches them.
| Field | Type | Meaning |
|---|---|---|
name | String | The name field, trimmed. Empty when the frontmatter has none, or when it is not text. |
description | String | The description field, trimmed. Empty when the frontmatter has none. |
body | String | The Markdown after the frontmatter, trimmed at both ends. |
meta | Map[String, @json.Json] | The whole frontmatter, so a client reads fields this library does not name — license, metadata, allowed-tools — and so validate can judge the text as written rather than as trimmed. |
root | String | The skill directory as given to parse, or as returned by a Source. Empty means there is no directory to check the name against. |
Skill::allowed_tools
function
fn Skill::allowed_tools(Self) -> String?
The experimental allowed-tools field, or None.
Skill::compatibility
function
fn Skill::compatibility(Self) -> String?
The compatibility field, or None when it is absent or not text.
Skill::license
function
fn Skill::license(Self) -> String?
The license field, or None when it is absent or not text.
Skill::location
function
fn Skill::location(Self) -> String
The path of the document, which is what a client hands back to the model so it can open the skill if it wants the body.
Skill::metadata
function
fn Skill::metadata(Self) -> Map[String, String]?
The metadata mapping as text, or None when it is absent. The specification defines it as string keys to string values; a value that is not text is skipped rather than turned into JSON spelling.
Skill::resolve
function
fn Skill::resolve(Self, StringView) -> String?
The path of a file the body refers to, resolved against the skill root, or None when the reference is not a file inside this skill.
This is how the third tier is read: the body says references/REFERENCE.md, the model asks for that file, and the client fetches it from the same Source the skill came from. A reference that climbs out of the directory — ../other-skill/SKILL.md, an absolute path, a URL — is None rather than a path: a skill may point at its own files and nowhere else.
Skill::summary
function
fn Skill::summary(Self) -> String
The metadata tier: what a client puts in front of a model before the model has chosen anything, which the specification budgets at about a hundred tokens per skill. One line, so a list of them stays a list.
Traits
Source
trait
pub(open) trait Source
Where skill documents come from.
The specification says a skill is a directory, and this is that directory seen as two questions: which skills are here, and what is in this file. A client with skills in a tarball, a database, or an HTTP cache implements the two methods and gets discover, the three-tier loading, and everything else unchanged — nothing above this trait knows what a filesystem is.
list returns skill roots: the directories that hold a document, in the order the client wants them offered to a model. read returns the bytes of a path. Both speak of paths with / between segments whatever the platform uses, so the same report reads the same everywhere.
| Method | Signature | Meaning |
|---|---|---|
list | list(Self) -> Array[String] | The skill roots this source offers. |
read | read(Self, String) -> Bytes | The bytes of one file. The path is a skill root from list joined with a relative name, or a root the caller passed to load. |
02moonbitstack/moonskill/fsof 02
The directory: a tree of skills read from disk, as a source the document package can walk.
Values
check
function
async fn check(StringView) -> Array[@moonskill.Fault]
Every fault in the skill at path, judged as a directory rather than as a document that already parsed.
This is the check a repository runs over docs/skills/ in CI: it covers the three things that can go wrong before a document is read — nothing at the path, a file where a directory should be, no document inside — and then the specification's rules about what the document says.
load
function
async fn load(StringView) -> @moonskill.Skill
One skill from a directory.
Raises when the directory holds no document, or when the document is not readable as one: a caller that wants to carry on past a broken skill uses check, which reports the same trouble as a fault.
Types and methods
Dir
struct
pub(all) struct Dir
A directory of skills on disk, seen as a Source(moonbitstack/moonskill).
The tree the specification describes is one directory per skill, each with a SKILL.md; a directory of skills — docs/skills in a repository — is therefore a Dir whose children are the skills. Paths handed back by Dir::list are built with / on every platform, so a report of a tree reads the same on all three systems the tests run on.
| Field | Type | Meaning |
|---|---|---|
dir | String |
Dir::at
function
fn Dir::at(StringView) -> Self
The tree rooted at path.
Dir::list
function
async fn Dir::list(Self) -> Array[String]
Dir::path
function
fn Dir::path(Self) -> String
The root this tree was opened at.
Dir::read
function
async fn Dir::read(Self, String) -> Bytes
implements @moonskill.Source