zut skills
A skill is a reusable instruction set written as a single
SKILL.md file with a YAML frontmatter header. Unless a skill disables
model invocation, zut discovers it at startup and surfaces it to the model
in two ways:
- The system prompt gains a short manifest:
Available skills: ... - code-review — Run a self-review pass...
- A built-in
skill tool lets the model load any one skill's full
body on demand.
The on-demand-load model keeps token usage cheap: only the manifest
goes into every request; the body is fetched as a tool result the
one or two turns the model actually needs it.
Anatomy
---
name: code-review
description: Run a thorough self-review pass on a recent change.
allowed-tools: [read, bash]
permissions:
bash: ["git diff*", "git log*"]
---
# Code review
When asked to review code, ...
Frontmatter fields
| field |
required |
purpose |
name |
optional |
skill identifier; defaults to the directory name |
description |
required |
one-line summary shown in the system prompt |
disable-model-invocation |
optional |
when true, hide the skill from the model's startup manifest; invoke it explicitly with /skill:<name> |
allowed-tools |
optional |
list of tool names the skill is meant to use; informational |
permissions |
optional |
per-tool patterns; informational |
allowed-tools and permissions are parsed but not enforced in
this version. They appear in the rendered skill body so the model can
see them and self-regulate. Future versions may enforce.
The body (everything after the second ---) is plain markdown.
There's no template engine; the model sees what you write.
Discovery
zut looks in these directories, in priority order, and registers the
first SKILL.md it finds for each unique name:
| location |
scope |
./.zut/skills/<name>/SKILL.md |
project (native) |
$ZUT_HOME/skills/<name>/SKILL.md |
global (native) |
./.claude/skills/<name>/SKILL.md |
project (claude-compat) |
~/.claude/skills/<name>/SKILL.md |
global (claude-compat) |
./.agents/skills/<name>/SKILL.md |
project (agent-compat) |
~/.agents/skills/<name>/SKILL.md |
global (agent-compat) |
The compat paths are deliberate: a SKILL.md written for an existing
skill ecosystem works in zut unchanged. Drop your existing
.claude/skills/ or .agents/skills/ directories into a project and
zut will pick them up. User skill roots are scanned recursively, so
nested files such as ~/.agents/skills/systems-backend/subskills/golang-patterns/SKILL.md
are discovered too. The frontmatter name is the canonical identifier;
for nested user skills, the slash-separated directory path relative to the
skill root is also accepted by the skill tool and /skill: commands.
Enabled extensions may bundle skills by declaring relative directories in
extension.json, for example "skills": ["skills"], with files at
skills/<name>/SKILL.md. A declared directory may also contain a single
SKILL.md directly; its directory name supplies the fallback skill name and
child directories are not scanned. Bundled skills are copied by zut ext install and loaded automatically. User/project skills take precedence over
bundled skills; bundled skills take precedence over embedded built-ins. When
two extensions provide the same skill name, the first declaration wins. The
host rejects manifest paths that escape the extension directory, including
symlinked skill files that resolve outside it.
When XDG_STATE_HOME is set on any platform, $ZUT_HOME defaults to
$XDG_STATE_HOME/zut. Otherwise it defaults to ~/Library/Application Support/zut/
on macOS, ~/.local/state/zut on Linux, or %LOCALAPPDATA%\zut on Windows.
Inspecting installed skills
In zut, run /skills. A picker lists every discovered skill with its
description and source path. Press enter on a row to view the full
body inline. Press esc to go back.
Invoking skills
For normal skills, the system prompt tells the model the skill names and
short descriptions. When a request matches, the model calls the skill tool
to load the full instructions on demand.
To force a specific skill, invoke it as a slash command. Typing /skill: opens a filtered list of discovered user skills. Use the arrow keys to select one, tab to complete its name, or enter to invoke the highlighted skill.
/skill:code-review
/skill:code-review focus on security issues
zut expands the command into a user message containing the complete skill
body, its directory for resolving relative references, and any text following
the command as the request. This bypasses model-side skill selection.
Set disable-model-invocation: true in a skill's frontmatter when it should
only run after explicit user invocation. The skill remains visible in
/skills and available through /skill:<name>, but its name and description
are omitted from the model's startup context.
Writing good skills
- Be procedural. Number steps. Tell the model what to do in what
order. Skills are habits, not knowledge dumps.
- Be precise about boundaries. "Stop after step 4" is more
effective than "don't go too far".
- Trim aggressively. A 200-line skill bloats every turn the
model uses it. Aim for 20–80 lines.
- One skill per behaviour. Don't pack three workflows into one
SKILL.md; the model picks one path. Two separate skills work better.
- Lead with the trigger. First paragraph should make it
obvious when to use the skill so the model self-selects correctly.
Examples
See examples/skills/ for starter skills:
code-review/ — self-review pass on a recent diff
tasked-phases/ — spec-to-phase checklist workflow for the tasked-phases extension and its tasked_phases tool
test-fix/ — diagnose + minimally fix a failing test
Comparison to other discovery layouts
| ecosystem |
path |
zut reads it? |
| (native) |
.zut/skills/<name>/SKILL.md |
yes |
| (claude-style) |
.claude/skills/<name>/SKILL.md |
yes |
| (agent-style) |
.agents/skills/<name>/SKILL.md |
yes |
Cross-pollination is intentional: pick whichever convention you're
already using and zut tags along.
source
1# zut skills
2
3A skill is a reusable instruction set written as a single
4`SKILL.md` file with a YAML frontmatter header. Unless a skill disables
5model invocation, zut discovers it at startup and surfaces it to the model
6in two ways:
7
81. The system prompt gains a short manifest:
9 `Available skills: ... - code-review — Run a self-review pass...`
102. A built-in `skill` tool lets the model load any one skill's full
11 body on demand.
12
13The on-demand-load model keeps token usage cheap: only the manifest
14goes into every request; the body is fetched as a tool result the
15one or two turns the model actually needs it.
16
17## Anatomy
18
19```markdown
20---
21name: code-review
22description: Run a thorough self-review pass on a recent change.
23allowed-tools: [read, bash]
24permissions:
25 bash: ["git diff*", "git log*"]
26---
27
28# Code review
29
30When asked to review code, ...
31```
32
33### Frontmatter fields
34
35| field | required | purpose |
36|---|---|---|
37| `name` | optional | skill identifier; defaults to the directory name |
38| `description` | required | one-line summary shown in the system prompt |
39| `disable-model-invocation` | optional | when `true`, hide the skill from the model's startup manifest; invoke it explicitly with `/skill:<name>` |
40| `allowed-tools` | optional | list of tool names the skill is meant to use; informational |
41| `permissions` | optional | per-tool patterns; informational |
42
43`allowed-tools` and `permissions` are **parsed but not enforced** in
44this version. They appear in the rendered skill body so the model can
45see them and self-regulate. Future versions may enforce.
46
47The body (everything after the second `---`) is plain markdown.
48There's no template engine; the model sees what you write.
49
50## Discovery
51
52zut looks in these directories, in priority order, and registers the
53first `SKILL.md` it finds for each unique name:
54
55| location | scope |
56|---|---|
57| `./.zut/skills/<name>/SKILL.md` | project (native) |
58| `$ZUT_HOME/skills/<name>/SKILL.md` | global (native) |
59| `./.claude/skills/<name>/SKILL.md` | project (claude-compat) |
60| `~/.claude/skills/<name>/SKILL.md` | global (claude-compat) |
61| `./.agents/skills/<name>/SKILL.md` | project (agent-compat) |
62| `~/.agents/skills/<name>/SKILL.md` | global (agent-compat) |
63
64The compat paths are deliberate: a `SKILL.md` written for an existing
65skill ecosystem works in zut unchanged. Drop your existing
66`.claude/skills/` or `.agents/skills/` directories into a project and
67zut will pick them up. User skill roots are scanned recursively, so
68nested files such as `~/.agents/skills/systems-backend/subskills/golang-patterns/SKILL.md`
69are discovered too. The frontmatter `name` is the canonical identifier;
70for nested user skills, the slash-separated directory path relative to the
71skill root is also accepted by the `skill` tool and `/skill:` commands.
72
73Enabled extensions may bundle skills by declaring relative directories in
74`extension.json`, for example `"skills": ["skills"]`, with files at
75`skills/<name>/SKILL.md`. A declared directory may also contain a single
76`SKILL.md` directly; its directory name supplies the fallback skill name and
77child directories are not scanned. Bundled skills are copied by `zut ext
78install` and loaded automatically. User/project skills take precedence over
79bundled skills; bundled skills take precedence over embedded built-ins. When
80two extensions provide the same skill name, the first declaration wins. The
81host rejects manifest paths that escape the extension directory, including
82symlinked skill files that resolve outside it.
83
84When `XDG_STATE_HOME` is set on any platform, `$ZUT_HOME` defaults to
85`$XDG_STATE_HOME/zut`. Otherwise it defaults to `~/Library/Application Support/zut/`
86on macOS, `~/.local/state/zut` on Linux, or `%LOCALAPPDATA%\zut` on Windows.
87
88## Inspecting installed skills
89
90In zut, run `/skills`. A picker lists every discovered skill with its
91description and source path. Press enter on a row to view the full
92body inline. Press esc to go back.
93
94## Invoking skills
95
96For normal skills, the system prompt tells the model the skill names and
97short descriptions. When a request matches, the model calls the `skill` tool
98to load the full instructions on demand.
99
100To force a specific skill, invoke it as a slash command. Typing `/skill:` opens a filtered list of discovered user skills. Use the arrow keys to select one, `tab` to complete its name, or `enter` to invoke the highlighted skill.
101
102```text
103/skill:code-review
104/skill:code-review focus on security issues
105```
106
107zut expands the command into a user message containing the complete skill
108body, its directory for resolving relative references, and any text following
109the command as the request. This bypasses model-side skill selection.
110
111Set `disable-model-invocation: true` in a skill's frontmatter when it should
112only run after explicit user invocation. The skill remains visible in
113`/skills` and available through `/skill:<name>`, but its name and description
114are omitted from the model's startup context.
115
116## Writing good skills
117
118- **Be procedural.** Number steps. Tell the model what to do in what
119 order. Skills are habits, not knowledge dumps.
120- **Be precise about boundaries.** "Stop after step 4" is more
121 effective than "don't go too far".
122- **Trim aggressively.** A 200-line skill bloats every turn the
123 model uses it. Aim for 20–80 lines.
124- **One skill per behaviour.** Don't pack three workflows into one
125 SKILL.md; the model picks one path. Two separate skills work better.
126- **Lead with the trigger.** First paragraph should make it
127 obvious *when* to use the skill so the model self-selects correctly.
128
129## Examples
130
131See `examples/skills/` for starter skills:
132
133- `code-review/` — self-review pass on a recent diff
134- `tasked-phases/` — spec-to-phase checklist workflow for the `tasked-phases` extension and its `tasked_phases` tool
135- `test-fix/` — diagnose + minimally fix a failing test
136
137## Comparison to other discovery layouts
138
139| ecosystem | path | zut reads it? |
140|---|---|---|
141| (native) | `.zut/skills/<name>/SKILL.md` | yes |
142| (claude-style) | `.claude/skills/<name>/SKILL.md` | yes |
143| (agent-style) | `.agents/skills/<name>/SKILL.md` | yes |
144
145Cross-pollination is intentional: pick whichever convention you're
146already using and zut tags along.