- OCaml 82%
- Perl 11%
- 5%
- Dune 2%
agent-skills #
Read, check, measure and announce Agent Skills in OCaml.
An agent skill is a directory that gives a coding agent instructions for one
kind of task. It holds a SKILL.md file, made of YAML frontmatter that names
and describes the skill and a Markdown body with the instructions, and may
hold scripts/, references/ and assets/ the agent reads on demand. An
agent lists the name and description of every skill it has in its system
prompt, and loads a skill's body only when the model picks it.
This package reads a SKILL.md and checks it against the specification: the
six fields name, description, license, compatibility, metadata and
allowed-tools, the sort and length of each, the shape of a name, and a name
that is the name of its directory. It measures what each skill costs an agent
against the specification's budgets, renders the <available_skills> block
that announces skills in a prompt, and the agent-skills command does each of
these from the shell.
Installation #
opam install agent-skills
If opam cannot find the package, add the overlay repository first:
opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
opam update
opam install agent-skills
Libraries #
agent-skills, moduleAgent_skills, reads aSKILL.mdgiven as a string and renders the prompt block. It does no I/O.agent-skills.eio, moduleAgent_skills_eio, finds the skills of a directory, reads theSKILL.mdof each with Eio and checks its name against its directory, and lists the other files a skill holds.
Usage #
Agent_skills.of_string reads the frontmatter through a YAML codec, so a field
the specification does not name, a missing one or a value of the wrong sort is
an error that says where it is:
# Agent_skills.of_string "---\nname: pdf\ndescription: Read PDFs.\nauthor: me\n---\n";;
- : (Agent_skills.t, [> `Msg of string ]) result =
Error (`Msg "File \"-\", line 4, characters 0-6:\nunexpected key \"author\"")
A valid file gives the fields, and Agent_skills.prompt the block that
lists the skill with the location of its SKILL.md:
# let skill =
Agent_skills.of_string
"---\nname: pdf\ndescription: Read PDFs & fill forms.\n---\n# PDF\n"
|> Result.get_ok;;
val skill : Agent_skills.t = <abstr>
# Agent_skills.body skill;;
- : string = "# PDF\n"
# Agent_skills.prompt [ (skill, Fpath.v "/skills/pdf/SKILL.md") ]
|> Result.get_ok |> print_endline;;
<available_skills>
<skill>
<name>
pdf
</name>
<description>
Read PDFs & fill forms.
</description>
<location>
/skills/pdf/SKILL.md
</location>
</skill>
</available_skills>
- : unit = ()
Budgets #
The specification has an agent load a skill in three steps: the name and
description of every skill at startup (about 100 tokens each), the body of the
skill the model picks (under 5000 tokens, in a SKILL.md under 500 lines),
then the skill's other files on demand. Agent_skills.advice says which of
these budgets a skill goes over, with tokens estimated as one per four bytes:
# let long =
Agent_skills.of_string
("---\nname: long\ndescription: A long skill.\n---\n"
^ String.make 24_000 'x')
|> Result.get_ok;;
val long : Agent_skills.t = <abstr>
# List.map (Fmt.str "%a" Agent_skills.pp_advice) (Agent_skills.advice long);;
- : string list =
["its instructions are ~6000 tokens, over the 5000 advised"]
The agent-skills command #
Without a command, agent-skills lists the skills of .claude/skills and
~/.claude/skills, or of the directories it is given, with what each costs:
$ agent-skills --sort instructions
╭───┬────────────────┬──────────┬──────────────┬────────────┬───────┬─────────╮
│ │ Skill │ Metadata │ Instructions │ of 5000 │ Lines │ Files │
├───┼────────────────┼──────────┼──────────────┼────────────┼───────┼─────────┤
│ ! │ long │ 5 │ 6000 │ ██████████ │ 604 │ - │
│ ✓ │ pdf-processing │ 21 │ 5 │ ░░░░░░░░░░ │ 10 │ 1 (15B) │
│ ✓ │ code-review │ 11 │ 0 │ ░░░░░░░░░░ │ 4 │ - │
╰───┴────────────────┴──────────┴──────────────┴────────────┴───────┴─────────╯
3 skills, 1 over a budget.
Every conversation pays ~37 tokens to be offered them;
loading them all would add ~6005 tokens of instructions.
agent-skills list --json skills/ # the fields, sizes and advice as JSON
agent-skills validate skills/ # exits 1 on an invalid skill
agent-skills validate --strict -q . # and on one over a budget, quietly
agent-skills prompt skills/ # the <available_skills> block
Specification notes #
- A name is 1 to 64 characters from
a-z,0-9and-, as the specification's field table lists them.skills-refalso accepts lowercase letters outside ASCII; this package does not. metadatamaps strings to strings, so an unquotedversion: 1.0is a float and is refused; quote it as the specification's example does.- The description and compatibility limits count Unicode characters, not bytes.
SKILL.mdis looked up before the lowercaseskill.md, whichskills-refalso accepts.promptescapes&,<and>in each value, the location included, and refuses a value holding a character XML 1.0 forbids.
Related work #
skills-refis the specification's Python reference library and command. Itsvalidateandto-promptarevalidateandprompthere, and itsread-propertiesislist --json; its prompt block has the same layout. It does not measure skills against their budgets.claude-skillsinstalls Claude Code skills from marketplaces and finds a skill'sSKILL.mdwith this package.
License #
ISC. See LICENSE.md.