Skills and the format
The format
The document shape
An agent.3md file is a 3md document: a frontmatter header (the manifest)
followed by a series of @plane slices. Exactly one plane is the agent's
identity; every other plane is a skill.
---
3md: 1.0
axis: skill
agent: dev
tools: rg, fd
---
Prose intro (optional). The model and host are the runner's choice.
@plane z=0 label="dev" kind=identity
# dev
Operating rules go here.
@plane z=1 label="search" kind=skill triggers="find, search, grep" inputs="pattern:string, path:string" tool="rg --line-number {pattern} {path}"
# Skill: search
Find code by regex. For filenames, see [[z=2|files]].
Frontmatter: the manifest
The frontmatter is lean. The only required keys are 3md and a name. Everything
else, model included, is an optional hint.
| Key | Required? | Meaning |
|---|---|---|
3md | Required | Base 3md format version. Use 1.0. |
title or agent | Required | The agent's name. title is a display name; agent is a short id. At least one must be present. |
model | Optional | A suggested default model the agent was built against. A hint only: the host may override it, and the agent should work on any capable model. Omit it to stay model-agnostic. |
axis | Recommended | skill (the Z axis enumerates skills). |
tools | Optional | Comma-separated names of the real binaries the agent's skills run, e.g. rg, jq, git. When present, each command-backed skill's binary should be one of them. |
persona | Optional | A one-line behavioral summary. |
version | Optional | Agent version, distinct from the format version. |
entry | Optional | The z of the plane to start from. Defaults to the identity plane. |
Any other key is preserved as agent metadata and is implementation-defined. A conforming loader ignores unknown frontmatter keys and unknown plane attributes rather than failing, so the format can grow additively.
model is just a hint
model is deliberately optional. An agent that omits it stays portable across
hosts, and a host is free to override whatever an agent suggests. Do not treat a
model line as a hard requirement.
Identity plane vs skill planes
Each @plane carries a z (its position), an optional label (its name),
optional attributes, and a Markdown body.
- Exactly one identity plane. It carries
kind=identityand is conventionallyz=0. Its body is the agent's system prompt and operating rules. If no plane declareskind=identity, the first plane (lowestz) is treated as the identity by a fallback rule. - Every other plane is a skill (
kind=skill). Itslabelis the skill name and its body is the skill's instructions, the prompt the agent loads when that skill is selected.
Skill attributes
A skill plane is @plane z=N label="<name>" kind=skill plus these optional
attributes:
| Attribute | Meaning |
|---|---|
triggers | Comma-separated trigger phrases used by route(). A phrase matches a request when every word in the phrase appears in the request (case-insensitive). So look up matches only when both look and up are present, never on up alone. |
inputs | Comma-separated typed inputs the skill expects (see below). |
tool | Optional. A runnable command this skill drives, written as a template, e.g. rg --line-number {pattern} {path}. |
cost | Optional tag for a side-effect or resource class, e.g. net, db. |
Triggers and routing
route(text) ranks skills by the number of distinct trigger phrases the request
satisfies, best first, ties broken by lower z. Tokens are maximal runs of
Unicode letters and digits, lowercased. A skill with no triggers can only be
reached by name or z, never by routing, so writing distinct triggers is what
makes routing accurate.
Typed inputs grammar
Each item in inputs is one of:
| Form | Meaning |
|---|---|
name | A required input of type string. |
name:type | A required input of the given type. |
name:type? | An optional input (trailing ?) of the given type. |
type must be one of a closed set: string, number, boolean, object,
array. The bare comma-separated form (inputs="question, schema") is still
valid: every name becomes a required string, so older untyped agents keep
working unchanged.
inputs="pattern:string, count:number, recurse:boolean?, schema:object"
That declares pattern and count and schema as required, and recurse as
an optional boolean. The same name must not be declared twice in one skill.
Tool command templates
When tool is set, it is a command template. Its first token is the binary; its
{placeholder} slots name the skill's inputs.
@plane z=1 label="search" kind=skill triggers="search, find, grep" inputs="pattern:string, path:string" tool="rg --line-number {pattern} {path}"
The contract that ties placeholders to inputs:
- Every
{placeholder}in the command must be a declared input. A placeholder with no matching input is an error (tool-input). - A declared input the command never references is a likely mistake, so loaders
warn (
unused-input). - The command's binary should appear in the agent's frontmatter
toolslist, so the manifest does not lie about what the agent runs.
A loader fills the template from concrete values, shell-quoting each one:
agent.command("search", { pattern: "TODO", path: "src" });
// -> rg --line-number 'TODO' 'src'
A tool with no placeholders (a bare binary, or an opaque id) is still valid; it
just is not parameterized. This is the route then fill then run loop: route a
request to a skill, fill its inputs, run its command.
cost
cost is a free-form tag for the side-effect or resource class of a skill, such
as net for a network call or db for a database write. Loaders surface it in
the manifest and the per-skill listing so a host can gate or warn on it; the
format does not assign it any built-in meaning.
Dependency links
A skill can depend on another skill by linking to its plane in the body, in either 3md cross-plane form:
[[z=N]][[z=N|label]](the label is a display alias)
Both declare that this skill depends on the skill at plane N. A loader resolves
these transitively via resolve(), so a skill arrives with everything it needs,
each dependency included once.
@plane z=5 label="pr" kind=skill triggers="pr, pull request, review" inputs="state:string" tool="gh pr list --state {state}"
# Skill: pr
List pull requests by {state}. To review one, read its commits with [[z=4|commits]].
Links must point at a real plane (a dangling target is a dead-link error), and
the dependency graph must not contain a cycle (cycle error).
Guidance-only skills (no tool)
tool is optional, and not every skill is a CLI command. A skill with no
tool is guidance only: its body is a playbook the agent follows using
whatever capabilities the host already gives it. Web search, a judgment call, an
action routed to a host or MCP tool, none of these need a binary. For such a
skill, command() returns null.
@plane z=7 label="research" kind=skill triggers="research, web, online, docs, look up"
# Skill: research
Guidance only, no bound command. When a question needs the open web rather than
this repo, use the host's own web-search tool: issue two or three focused
queries, read the most authoritative sources, and cite them.
A real agent is usually a mix: some skills run a command, some are a playbook.
See also
- CLI reference: drive any agent from the command line.
- Loaders: the TypeScript, Rust, and Swift loaders.
- Conformance and spec: every validator rule, with examples.