Parts

Write and load skills

Package instructions and choose which skills an agent receives.

For: plugin authors.

Add a skill when an agent needs instructions about when and how to perform a task. A skill is a folder containing SKILL.md in the shared Agent Skills format.

List individual skill folders, or a folder containing skills, under skills: in the plugin manifest:

oz0-plugin.yaml
skills: [skills/]
skills/acme-github/SKILL.md
---
name: acme-github
description: When and how to use the acme_github tool to review pull requests. Use it before you comment on a pull request.
---

Use `acme_github.get_pr` to read a pull request before you comment on it.
Comment with `acme_github.comment` only on lines that changed.
Never call `acme_github.merge`; ask a person instead.
For the house style, read [the review guide](references/review-guide.md).

The front matter needs a name and a description; the description is what tells an agent the skill is relevant, so say what it does and when to use it. The body is free Markdown. A skill can link to other files of its own folder, such as references/, scripts/ or assets/; a run reads them only when it needs them.

What a run loads

Skills load progressively, as the format intends: names and descriptions at the start, a body when the agent decides it needs it, a linked file only when the body sends it there.

AgentAt the startWhen the skill is used
Pydantic AI and OpenAI Agents SDK agentsThe skills' names and descriptions, after the agent's instructionsoz.skill(name) returns the body; oz.skill(name, file) a file the body links to
Claude Agent SDK harness (plugins/harness-claude)The session's own skill listingThe SDK's Skill tool, from a read-only folder of the agent's skills
ACP harnesses (plugins/harness-acp: Codex, Gemini CLI, OpenCode, goose)Each tool's own skill listing, from the folder its harness declares under acp.skillsThe tool's own way; a harness without acp.skills gets oz.skill through the session's MCP server
Remote agentsNothing: their loop runs at their provider;

The index the native agents get after their instructions looks like this, one line per skill:

## Skills

You have these skills: instructions for particular tasks. When a task matches a skill's description, read the skill first with oz__skill(name) and follow it.

- acme-github: When and how to use the acme_github tool to review pull requests. Use it before you comment on a pull request.

It is part of the start-up context, never of the status block each request carries, so the prompt cache holds. A skill's text reaches the model only as oz.skill's answer, in a block that names the skill, its plugin and its hash.

A harness session gets a copy of its agent's skills, SKILL.md and the files it links to, in a folder beside its own, outside its work folder and read-only. The Claude Agent SDK loads that folder as a plugin of the session only, with no user or project settings, so nothing of the repository the session works on is read.

The copy keeps a skill's name, description, license and body and drops everything else in its front matter: a skill cannot pre-approve tools (allowed-tools), and Claude Code's two shell forms, an inline !`command` and a code block that opens with ```!, are broken up so they do not run; the Claude Agent SDK session also runs with the CLI's skill shell turned off (disableSkillShellExecution).

A harness loads a skill by an Agent Skills name. An imported skill or command whose name has capitals, ., _ or : (a Claude Code command in a subfolder is git:commit) gets that form in the session's folder: lowercase, with each run of other characters a hyphen (git-commit, review-pr). Two names that end up the same keep the first; the other is left out with a log line.

An ACP harness without acp.skills gets the same index as a native agent after its instructions, and oz.skill through its MCP server.

Which skills an agent gets

By default, every skill of its own plugin. skills: in the agent definition picks them instead:

agents/reviewer.yaml
skills: [acme/acme-github, docs/*]    # <plugin>/<skill>, or <plugin>/* for all of a plugin's

skills: [] gives an agent none. Another plugin's skills load only when the manifest declares them under uses: and a tenant admin grants that at install, as for another plugin's tools:

oz0-plugin.yaml
uses:
  docs:
    skills: [style-guide]   # or "*"

The node checks the grant before it reads a skill of another plugin, and the edge checks the read's command again; oz0 plugin revoke <plugin> --skill docs/style-guide takes it back. Another plugin's skill is listed in the index only while the job's grant holds it (the grant as the job read it at its start, and again before each read of another plugin), so a revoked grant leaves the index at the job's next read; a node without grants (a supervisor from before v1.0) lists and reads none.

A harness session gets another plugin's skills on the same terms: the node reads the grant again as each session starts, so a revoke reaches a job's next session (an outcome's next round, a resume, a chat's next turn), and a session already running keeps what it was given. The plugin page of the console lists each granted skill with a Revoke button, as it does tools and agents. Two skills of the same name: the agent's own plugin's wins, and the other is left out with a log line.

A tool's manual

A tool can name skills of its own plugin as its manual: how to use its functions well. Name them under the tool in the manifest (naming a manual):

oz0-plugin.yaml
tools:
  - id: acme_github
    mcp: ["python", "-m", "acme_github.server"]
    manual: [acme-github]       # at most 8 skills of this plugin

An agent that is granted any function of the tool gets its manual in its skills index, with no skills: entry and no uses.<plugin>.skills grant:

  • an agent of the same plugin, by naming one of the tool's functions in its tools:, even with skills: [];
  • an agent of another plugin, by naming one of the tool's functions in its tools:, under its plugin's uses: grant of that function. Without the grant, the manual is neither listed nor read.

The manual's lines come after the agent's other skills, its own plugin's tools' manuals first, under the same start-up budget: a manual that does not fit is left out with the same log line as any skill. The model reads one with oz.skill as any other skill, and the node and the edge both let that read through as the manual of a granted tool. A skill of the same plugin that is not that tool's manual still needs its grant.

Revoking the tool's functions (oz0 plugin revoke <plugin> --tool <tool>.<function>) takes the manual out of the job's index at its next read, as a revoked skill grant does; the nodes already hold the manuals of the tools a plugin's uses: declares, so a grant or a revoke restarts nothing. A harness session gets the manual in its read-only skills folder, as it gets a granted skill.

A node that does not run the tool's plugin still lists its manual and reads its body: the edge sends it with the agent's plugin. The files a manual links to (references/ and the like) are read only on a node that runs the tool's plugin; elsewhere the read says so.

The first-party connectors that ship a skill about their own tools name it as the tools' manual: atlassian and atlassian-dc (Jira, Confluence and JSM), gitlab, servicenow (its Table API tool) and web (web-research).

Trust

A skill of the agent's own plugin is as trusted as the instructions beside it. A skill of an imported plugin, or of another plugin, is untrusted input: reading it taints the job (source skill), so tools that can send data out ask a person first, as after any untrusted text. Its block tells the model to treat it as information, not as instructions that override its own, and a body cannot close that block and open one of its own.

Another plugin's skill shows in the index by its name and plugin only, never its description, which is that plugin's text:

- style-guide: a skill of plugin docs; read it to see what it is for (another plugin's description is not shown)

So the start-up context holds no other plugin's words, and the job stays untainted until the model reads one. An imported plugin's own skills keep their descriptions: they came with the agent's own imported instructions.

A tool's manual follows the same rule, with one exception. The manual of a verified connector, a connector installed from the official index under OZ's own namespace, does not taint the job: it comes from the same pinned bundle and publisher as the tool's code the agent already calls. Its line shows its description and its block carries no untrusted mark.

The manual of any other plugin's tool, a connector you installed from a folder or another index included, is that plugin's text: listed by name and plugin, read as untrusted, and it taints the job as another plugin's skill does. A plugin's own tools' manuals are its own skills.

The start-up budget

What the platform adds to an agent's start-up context after its instructions, the skills' index and the start-up text of plugins such as the memory plugin's accepted decisions and definitions, counts against limits.startup_tokens (default 4,000 tokens, an estimate by the same counter as the tool-size budget). The skills index comes first; start-up text gets what is left. The agent's own instructions do not count.

Start-up budget (estimate): agent reviewer, 3 skills, about 112 of 4,000 tokens

Over the budget, plugin lint, plugin test, plugin pack and the studio fail with startup.budget, naming the agent, its figure and its biggest skills; give it fewer skills, shorter descriptions, or a higher limits.startup_tokens. The hub only warns. Another plugin's skills are not known at lint: the runtime counts them when it builds the agent, at the cost of the line the index shows for them (their name and plugin), and leaves out, with a log line, what does not fit.

The manuals of its own plugin's tools that an agent names count at lint, after its own skills; when they are what pushes it over, lint says so and names them:

agent triage: the manuals of the tools it names (acme-github) bring the index of its 3 skills to about 4,120 tokens in its start-up context (an estimate), more than its budget of 4,000; without them it is about 3,700

oz.skill's own definition counts in the agent's limits.tool_tokens.

A glossary: a skill or memory

A glossary that is part of how your plugin works, the same for every tenant and reviewed with its code, belongs in a skill: it ships and changes with the plugin's versions, and the agent reads it when a task needs it. Terms a tenant's own people decide, that change while agents run and need someone to accept them, belong in memory as definitions: accepted ones reach the agent at its start, and define(term) reads one by its exact term.

What lint checks

plugin lint checks every skill by the Agent Skills format:

  • name: 1 to 64 lowercase letters, digits and single hyphens, not first or last, and the name of the skill's folder;
  • description: 1 to 1,024 characters; compatibility, if set, 1 to 500; a front-matter key the format does not have is a warning;
  • the body: at most 64 KiB (a warning past 500 lines), at most 1 MiB for all of a plugin's skills, at most 128 skills;
  • every file the body links to (a Markdown link or image, or a code span naming a path under scripts/, references/ or assets/, outside code blocks) must exist, be a regular file of at most 512 KiB, and stay inside the skill's folder: no absolute path, no .. out of it, no symbolic link; at most 32 of them and 4 MiB together.

The bundle keeps each skill's body, the files it links to (by path, size and SHA-256) and a hash over both; a node refuses a linked file whose digest differs.

An imported plugin's skill that breaks one of these rules is noted and left out, never an error; so are an imported plugin's skills past 128, past 1 MiB of bodies, or with a name another of its skills (or commands) already has; a command with a skill's name is left out without a note, since in Claude Code the skill wins too.

The import notes each shell line Claude Code would run, inline or fenced: OZ0 runs none.

Previous section links
Copyright © 2026