moonbitstack appendix 44 public items ap–04 github

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
VariantPayloadMeaning
MissingFrontmatter—The text does not open with the --- fence.
UnclosedFrontmatter—The opening fence has no closing ---.
BadYamlStringThe 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.
NotUtf8Int32The 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
VariantPayloadMeaning
UnexpectedFieldsArray[String]Frontmatter keys the specification does not define, sorted.
MissingName—The frontmatter has no name.
NameEmpty—name is empty, blank, or not text.
NameTooLongString, Int32, Int32name is longer than the limit: the name, how long it is, and the limit.
NameNotLowercaseStringname has an uppercase letter.
NameEdgeHyphen—name starts or ends with a hyphen.
NameDoubleHyphen—name has two hyphens in a row.
NameBadCharacterStringname has a character that is neither a letter, a digit, nor a hyphen.
NameMismatchString, StringThe 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.
DescriptionTooLongInt32, Int32description is longer than the limit: how long it is, then the limit.
CompatibilityNotText—compatibility is present and is not text.
CompatibilityTooLongInt32, Int32compatibility is longer than the limit: how long it is, then the limit.
MalformedRefusedThe document could not be read as a skill at all.
MissingPathStringThere is nothing at the path.
NotADirectoryStringThe 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.

FieldTypeMeaning
nameInt32The longest name, in characters. The specification says 64.
descriptionInt32The longest description, in characters. The specification says 1024.
compatibilityInt32The 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.

FieldTypeMeaning
nameStringThe name field, trimmed. Empty when the frontmatter has none, or when it is not text.
descriptionStringThe description field, trimmed. Empty when the frontmatter has none.
bodyStringThe Markdown after the frontmatter, trimmed at both ends.
metaMap[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.
rootStringThe 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.

MethodSignatureMeaning
listlist(Self) -> Array[String]The skill roots this source offers.
readread(Self, String) -> BytesThe 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.

FieldTypeMeaning
dirString
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

top