v0.3.85 573dcec

skills.md RawHistoryFolder

zot skills

A skill is a reusable instruction set written as a single SKILL.md file with a YAML frontmatter header. Skills may be supplied by an extension as well as by the normal skill directories. Unless a skill disables model invocation, zot discovers it at startup and surfaces it to the model in two ways:

  1. The system prompt gains a short manifest: Available skills: ... - code-review — Run a self-review pass...
  2. 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 normalized relative directory path
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

zot recursively looks in these directories, in priority order, and registers the first SKILL.md it finds for each unique name:

location scope
./.zot/skills/<name>/SKILL.md project (native)
$ZOT_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 zot unchanged. Drop your existing .claude/skills/ or .agents/skills/ directories into a project and zot will pick them up. When no frontmatter name is present, nested path components are lowercased and normalized to kebab-case and joined with -. Only files named exactly SKILL.md are read.

Extensions namespace their skills, for example git-tools:writing-git-commits. Explicit --ext bundles take precedence over environment, project, global, and compatibility sources. Duplicate names from different files keep the higher-precedence file and report a diagnostic. Rediscovering the same file under the same skill name (including through a symlink) is silently ignored. This includes overlapping project and global locations when running from your home directory. --no-ext omits implicit extension bundles; --no-ext --ext PATH loads only the explicitly named bundle. --no-skill disables all skills, including extension and built-in skills.

When XDG_STATE_HOME is set on any platform, $ZOT_HOME defaults to $XDG_STATE_HOME/zot. Otherwise it defaults to ~/Library/Application Support/zot/ on macOS, ~/.local/state/zot on Linux, or %LOCALAPPDATA%\zot on Windows.

Inspecting installed skills

In zot, 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.

Pinning skills

In the /skills list, press p to toggle a project pin or g to toggle a global pin. Rows show [p-], [-g], or [pg] for the selected scopes. Global and project pins are combined in alphabetical name order, with each skill loaded only once. Unpinning one scope leaves the other scope intact.

On a fresh interactive session, a read-only [Pinned skills] section above the input lists the skills that will accompany your next message. Its heading, spacing, and indented names match the other startup information sections. Each name shows its pin scope: (project), (global), or (project + global). Opening zot does not start a model turn. The complete skill bodies and their source directories are included in your first submitted message and persisted with it, without changing the editor text. /clear prepares pins again. Switching working directories with /cd prepares the new directory's pins. Resuming, importing, or branching an existing session does not inject pins again, even when its transcript is empty. Changing models and compacting context do not reload pins.

Changing pins before the first message updates the pending selection. After that message, pin changes apply on the next fresh conversation or /clear. Unpinning does not remove instructions already in the transcript. There are no per-request removal controls in the editor.

Preferences live in $ZOT_HOME/skill-pins.json, not in the repository. Project keys are absolute starting directories with symlinks resolved when possible, not Git roots. Subdirectories have separate project pins. For example:

{
  "global": ["code-review"],
  "projects": {
    "/home/me/work/app": ["test-fix"]
  }
}

Pins store discovered names, including extension namespaces, rather than copies of skill files. Normal discovery precedence still applies, so a global pin may resolve to a project's higher-priority skill of the same name. Missing or disabled sources produce a warning and are skipped. A malformed preferences file is reported and is not overwritten by a toggle. --no-skill disables preloading. Explicitly pinning a skill with disable-model-invocation: true is allowed.

Print, stream, and JSON modes also include pins with the first main prompt of a fresh session, including runs with session persistence disabled. Zotfile startup pre commands run before pins are applied. RPC, standalone bot modes, the SDK, and swarm background agents do not automatically load pin preferences. The interactive Telegram bridge uses the interactive conversation's pins.

Pinning saves repeated invocation, not tokens: complete skill bodies add to the context window and may later be summarized by compaction. Keep pins small, especially with local models. Preferences are saved via temporary-file replacement. Concurrent edits from separate zot processes are not merged. An already open interactive session refreshes preferences on /skills or /clear, not continuously.

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

By default, suggestions match case-insensitive name prefixes and are sorted alphabetically. Enable fuzzy skill suggestions in /settings to match characters in order anywhere in a skill name: /skill:review and /skill:crv can both find code-review. Results are ranked by relevance, with alphabetical tie-breaking, and matching characters are shown in bold, including in the selected row. Only names are searched, not descriptions or the /skill: prefix. An empty query still lists skills alphabetically; an exact name shows only that skill. Other slash-command suggestions and exact skill lookup during invocation are unchanged. Complete a fuzzy query before adding request text.

The setting takes effect immediately and persists as fuzzy_skill_suggest in $ZOT_HOME/config.json. Missing or false preserves the default prefix behavior. You can turn it off at any time. Bold emphasis depends on terminal support.

zot 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
  • test-fix/ — diagnose + minimally fix a failing test
  • recursive/ — nested path-derived naming and explicit-name discovery

Comparison to other discovery layouts

ecosystem path zot reads it?
(native) .zot/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 zot tags along.

source
1# zot skills
2
3A skill is a reusable instruction set written as a single
4`SKILL.md` file with a YAML frontmatter header. Skills may be supplied by
5an extension as well as by the normal skill directories. Unless a skill disables
6model invocation, zot discovers it at startup and surfaces it to the model
7in two ways:
8
91. The system prompt gains a short manifest:
10   `Available skills: ... - code-review — Run a self-review pass...`
112. A built-in `skill` tool lets the model load any one skill's full
12   body on demand.
13
14The on-demand-load model keeps token usage cheap: only the manifest
15goes into every request; the body is fetched as a tool result the
16one or two turns the model actually needs it.
17
18## Anatomy
19
20```markdown
21---
22name: code-review
23description: Run a thorough self-review pass on a recent change.
24allowed-tools: [read, bash]
25permissions:
26  bash: ["git diff*", "git log*"]
27---
28
29# Code review
30
31When asked to review code, ...
32```
33
34### Frontmatter fields
35
36| field | required | purpose |
37|---|---|---|
38| `name` | optional | skill identifier; defaults to the normalized relative directory path |
39| `description` | required | one-line summary shown in the system prompt |
40| `disable-model-invocation` | optional | when `true`, hide the skill from the model's startup manifest; invoke it explicitly with `/skill:<name>` |
41| `allowed-tools` | optional | list of tool names the skill is meant to use; informational |
42| `permissions` | optional | per-tool patterns; informational |
43
44`allowed-tools` and `permissions` are **parsed but not enforced** in
45this version. They appear in the rendered skill body so the model can
46see them and self-regulate. Future versions may enforce.
47
48The body (everything after the second `---`) is plain markdown.
49There's no template engine; the model sees what you write.
50
51## Discovery
52
53zot recursively looks in these directories, in priority order, and registers the
54first `SKILL.md` it finds for each unique name:
55
56| location | scope |
57|---|---|
58| `./.zot/skills/<name>/SKILL.md` | project (native) |
59| `$ZOT_HOME/skills/<name>/SKILL.md` | global (native) |
60| `./.claude/skills/<name>/SKILL.md` | project (claude-compat) |
61| `~/.claude/skills/<name>/SKILL.md` | global (claude-compat) |
62| `./.agents/skills/<name>/SKILL.md` | project (agent-compat) |
63| `~/.agents/skills/<name>/SKILL.md` | global (agent-compat) |
64
65The compat paths are deliberate: a `SKILL.md` written for an existing
66skill ecosystem works in zot unchanged. Drop your existing
67`.claude/skills/` or `.agents/skills/` directories into a project and
68zot will pick them up. When no frontmatter `name` is present, nested path
69components are lowercased and normalized to kebab-case and joined with `-`.
70Only files named exactly `SKILL.md` are read.
71
72Extensions namespace their skills, for example `git-tools:writing-git-commits`.
73Explicit `--ext` bundles take precedence over environment, project, global, and
74compatibility sources. Duplicate names from different files keep the
75higher-precedence file and report a diagnostic. Rediscovering the same file
76under the same skill name (including through a symlink) is silently ignored.
77This includes overlapping project and global locations when running from your
78home directory. `--no-ext` omits implicit extension bundles;
79`--no-ext --ext PATH` loads only the explicitly named bundle. `--no-skill`
80disables all skills, including extension and built-in skills.
81
82When `XDG_STATE_HOME` is set on any platform, `$ZOT_HOME` defaults to
83`$XDG_STATE_HOME/zot`. Otherwise it defaults to `~/Library/Application Support/zot/`
84on macOS, `~/.local/state/zot` on Linux, or `%LOCALAPPDATA%\zot` on Windows.
85
86## Inspecting installed skills
87
88In zot, run `/skills`. A picker lists every discovered skill with its
89description and source path. Press enter on a row to view the full
90body inline. Press esc to go back.
91
92## Pinning skills
93
94In the `/skills` list, press `p` to toggle a project pin or `g` to toggle a
95global pin. Rows show `[p-]`, `[-g]`, or `[pg]` for the selected scopes.
96Global and project pins are combined in alphabetical name order, with each
97skill loaded only once. Unpinning one scope leaves the other scope intact.
98
99On a fresh interactive session, a read-only `[Pinned skills]` section above
100the input lists the skills that will accompany your next message. Its heading,
101spacing, and indented names match the other startup information sections.
102Each name shows its pin scope: `(project)`, `(global)`, or `(project + global)`.
103Opening zot does not start a model turn. The complete skill bodies and their
104source directories are included
105in your first submitted message and persisted with it, without changing the
106editor text. `/clear` prepares pins again. Switching working directories with
107`/cd` prepares the new directory's pins. Resuming, importing, or branching an
108existing session does not inject pins again, even when its transcript is empty.
109Changing models and compacting context do not reload pins.
110
111Changing pins before the first message updates the pending selection.
112After that message, pin changes apply on the next fresh conversation or
113`/clear`. Unpinning does not remove instructions already in the transcript.
114There are no per-request removal controls in the editor.
115
116Preferences live in `$ZOT_HOME/skill-pins.json`, not in the repository.
117Project keys are absolute starting directories with symlinks resolved when
118possible, not Git roots. Subdirectories have separate project pins.
119For example:
120
121```json
122{
123  "global": ["code-review"],
124  "projects": {
125    "/home/me/work/app": ["test-fix"]
126  }
127}
128```
129
130Pins store discovered names, including extension namespaces, rather than
131copies of skill files. Normal discovery precedence still applies, so a global
132pin may resolve to a project's higher-priority skill of the same name.
133Missing or disabled sources produce a warning and are skipped. A malformed
134preferences file is reported and is not overwritten by a toggle.
135`--no-skill` disables preloading. Explicitly pinning a skill with
136`disable-model-invocation: true` is allowed.
137
138Print, stream, and JSON modes also include pins with the first main prompt of
139a fresh session, including runs with session persistence disabled. Zotfile
140startup `pre` commands run before pins are applied. RPC, standalone bot modes,
141the SDK, and swarm background agents do not automatically load pin preferences.
142The interactive Telegram bridge uses the interactive conversation's pins.
143
144Pinning saves repeated invocation, not tokens: complete skill bodies add to
145the context window and may later be summarized by compaction. Keep pins small,
146especially with local models. Preferences are saved via temporary-file replacement.
147Concurrent edits from separate zot processes are not merged. An already open interactive
148session refreshes preferences on `/skills` or `/clear`, not continuously.
149
150## Invoking skills
151
152For normal skills, the system prompt tells the model the skill names and
153short descriptions. When a request matches, the model calls the `skill` tool
154to load the full instructions on demand.
155
156To 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.
157
158```text
159/skill:code-review
160/skill:code-review focus on security issues
161```
162
163By default, suggestions match case-insensitive name prefixes and are sorted
164alphabetically. Enable **fuzzy skill suggestions** in `/settings` to match
165characters in order anywhere in a skill name: `/skill:review` and `/skill:crv`
166can both find `code-review`. Results are ranked by relevance, with alphabetical
167tie-breaking, and matching characters are shown in bold, including in the
168selected row. Only names are searched, not descriptions or the `/skill:` prefix.
169An empty query still lists skills alphabetically; an exact name shows only that
170skill. Other slash-command suggestions and exact skill lookup during invocation
171are unchanged. Complete a fuzzy query before adding request text.
172
173The setting takes effect immediately and persists as `fuzzy_skill_suggest` in
174`$ZOT_HOME/config.json`. Missing or `false` preserves the default prefix behavior.
175You can turn it off at any time. Bold emphasis depends on terminal support.
176
177zot expands the command into a user message containing the complete skill
178body, its directory for resolving relative references, and any text following
179the command as the request. This bypasses model-side skill selection.
180
181Set `disable-model-invocation: true` in a skill's frontmatter when it should
182only run after explicit user invocation. The skill remains visible in
183`/skills` and available through `/skill:<name>`, but its name and description
184are omitted from the model's startup context.
185
186## Writing good skills
187
188- **Be procedural.** Number steps. Tell the model what to do in what
189  order. Skills are habits, not knowledge dumps.
190- **Be precise about boundaries.** "Stop after step 4" is more
191  effective than "don't go too far".
192- **Trim aggressively.** A 200-line skill bloats every turn the
193  model uses it. Aim for 20–80 lines.
194- **One skill per behaviour.** Don't pack three workflows into one
195  SKILL.md; the model picks one path. Two separate skills work better.
196- **Lead with the trigger.** First paragraph should make it
197  obvious *when* to use the skill so the model self-selects correctly.
198
199## Examples
200
201See `examples/skills/` for starter skills:
202
203- `code-review/` — self-review pass on a recent diff
204- `test-fix/` — diagnose + minimally fix a failing test
205- `recursive/` — nested path-derived naming and explicit-name discovery
206
207## Comparison to other discovery layouts
208
209| ecosystem | path | zot reads it? |
210|---|---|---|
211| (native) | `.zot/skills/<name>/SKILL.md` | yes |
212| (claude-style) | `.claude/skills/<name>/SKILL.md` | yes |
213| (agent-style) | `.agents/skills/<name>/SKILL.md` | yes |
214
215Cross-pollination is intentional: pick whichever convention you're
216already using and zot tags along.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close