bots-garden/mini-mepublic Fork
⑂ v0.2.3
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. 37a5a39 · on v0.2.3 · k33g · Sep 21, 2026
skill-format.md · 95 lines · 5.9 KBmarkdown
Blame HistoryOpen raw

Reference: skill file format

Neutral description of the markdown files the read_skill tool serves.

Location

Rule Value
Directory skillsDir (default skills).
Patterns <skillsDir>/<name>.md (flat) and <skillsDir>/<name>/SKILL.md (one directory per skill), mixed freely. Other files in a skill's directory are ignored. A README.md at the root of the directory is documentation, not a skill: it is skipped.
Skill name on disk The file name without .md, or the directory name for a SKILL.md — used only as the default name when the front matter gives none.
Resolution read_skill resolves the name through the catalogue, not through a path: it accepts exactly the names the catalogue shows, so a name: that differs from the file name works. Surrounding spaces and quotes are trimmed and the case is forgiven; an exact name always wins over a case-insensitive one.
Both forms for one name read_skill returns the flat file. The banner warns that the other is unreachable.
Missing directory Not an error: zero skills, read_skill not declared.

Front matter

Optional. When present, the file starts with a line --- and the header ends at the next --- line. Only three keys are read; the others are ignored.

Key Type Default Description
name string file name without .md Name shown in the catalogue and accepted by read_skill. An empty value keeps the default.
description string "" One-line summary shown in the catalogue. A skill without one appears as a bare name, which the model cannot match a request against; the start-up banner warns about it.
keywords comma-separated list empty Words a user might actually type, in any language. Shown in the catalogue after the description, capped at the first 8 — the catalogue is sent with every request. This is the lexical bridge: "salue Bob" reaches greetings through saluer, never through the name. Order matters: past the eighth word nothing reaches the model, so put the most distinctive first. The banner warns when a list is being truncated. Do not spend the eight on words already in the description — the catalogue shows both, and a description is one language, so the keywords are where the others belong.

A description: line in the body, after the closing ---, is not metadata.

Catalogue rendering

The tool description is:

Load a skill: the step-by-step procedure to follow for this kind of task. Call it BEFORE doing the work, with the name of the matching skill, and then follow what it says. Available skills:
  <name> — <description> (<keyword>, <keyword>, …)
  <name>

Entries are sorted by name; the — <description> part is omitted when the description is empty, and the (…) part when there are no keywords.

Example

---
name: what-time-is-it
description: Give the current date and time.
keywords: time, date, clock
---

# What time is it?

Run:

```bash
date "+%A %d %B %Y — %H:%M:%S (%Z)"

The `keywords` of that example reach the catalogue as `what-time-is-it — Give the current date and time. (time, date, clock, now, today, heure, date du jour, quelle heure)`.

## Asking for a skill by name

`$name` in a question names a skill to carry out, with the rest of the line as its parameters — `use $greetings Bob`. The window's `$` picker writes it for you; typed by hand it works in the line mode and over ACP too. In an editor the skill also appears in the client's own `/` menu, with an input hint for its parameters — see [ACP — skills as commands](acp.md#skills-as-commands). It appends one line to what the model reads, and the model still makes the `read_skill` call itself. See [Slash commands — the three pickers](slash-commands.md#the-three-pickers).

## Writing a skill the model will ACT on

A skill that tells the model to run a command has one failure mode, and it is not the one you expect: the model **reproduces the command instead of running it**, in a fenced code block, and answers as if it had done the work.

Measured on `mellum2-12b-a2.5b-instruct`, the same skill either way:

| The skill says | Result |
|---|---|
| ` ```bash ` fenced block | `⚙ 0 command(s)` — the model printed the command back, three runs out of three |
| the command **indented**, and "call your `bash` tool with this command" | `⚙ 1 command(s)` — three runs out of three |

A fenced block is a *display* affordance. Put one in the model's context and the most probable continuation is the same block, which is exactly what a small model produces. Naming the tool and indenting the command removes the invitation.

Note this is model-dependent — `qwen3.5-4b` ran the fenced version correctly — so a skill that works on your model may still fail on someone else's. Write it the safe way regardless.

The rule, for a skill whose point is that a command runs:

- **no ` ```bash ` fence around the command to execute.** Indent it by four spaces instead.
- **name the tool**: "call your `bash` tool with this command", not "run:".
- keep saying what the shipped skills say — *the answer is the command's output, nothing else* — because that is what stops the model answering from memory.

A skill that is reference material rather than one command to run — `bg-jobs`, which shows half a dozen — keeps its fences. Nothing there is "the command to execute", so there is no invitation to misread.

## Shipped examples

| Path | `name` | `description` |
|------|--------|---------------|
| `skills/greetings/SKILL.md` | `greetings` | Greet somebody with a festive message. |
| `skills/vulcan-salute/SKILL.md` | `vulcan-salute` | Send a vulcan salute to somebody. |
| `skills/what-time-is-it/SKILL.md` | `what-time-is-it` | Give the current date and time. |

They use the directory-per-skill layout; started from the repository root, `mm` reports `skills: 3`.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# Reference: skill file format

> Neutral description of the markdown files the `read_skill` tool serves.

## Location

| Rule | Value |
|------|-------|
| Directory | `skillsDir` (default `skills`). |
| Patterns | `<skillsDir>/<name>.md` (flat) and `<skillsDir>/<name>/SKILL.md` (one directory per skill), mixed freely. Other files in a skill's directory are ignored. A `README.md` at the root of the directory is documentation, not a skill: it is skipped. |
| Skill name on disk | The file name without `.md`, or the directory name for a `SKILL.md` — used only as the default `name` when the front matter gives none. |
| Resolution | `read_skill` resolves the name **through the catalogue**, not through a path: it accepts exactly the names the catalogue shows, so a `name:` that differs from the file name works. Surrounding spaces and quotes are trimmed and the case is forgiven; an exact name always wins over a case-insensitive one. |
| Both forms for one name | `read_skill` returns the flat file. The banner warns that the other is unreachable. |
| Missing directory | Not an error: zero skills, `read_skill` not declared. |

## Front matter

Optional. When present, the file starts with a line `---` and the header ends at the next `---` line. Only three keys are read; the others are ignored.

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `name` | string | file name without `.md` | Name shown in the catalogue and accepted by `read_skill`. An empty value keeps the default. |
| `description` | string | `""` | One-line summary shown in the catalogue. A skill without one appears as a bare name, which the model cannot match a request against; the start-up banner warns about it. |
| `keywords` | comma-separated list | empty | Words a user might actually type, in any language. Shown in the catalogue after the description, **capped at the first 8** — the catalogue is sent with every request. This is the lexical bridge: *"salue Bob"* reaches `greetings` through `saluer`, never through the name. **Order matters**: past the eighth word nothing reaches the model, so put the most distinctive first. The banner warns when a list is being truncated. Do not spend the eight on words already in the `description` — the catalogue shows both, and a description is one language, so the keywords are where the others belong. |

A `description:` line in the body, after the closing `---`, is not metadata.

## Catalogue rendering

The tool description is:

```
Load a skill: the step-by-step procedure to follow for this kind of task. Call it BEFORE doing the work, with the name of the matching skill, and then follow what it says. Available skills:
  <name> — <description> (<keyword>, <keyword>, …)
  <name>
```

Entries are sorted by name; the ` — <description>` part is omitted when the description is empty, and the ` (…)` part when there are no keywords.

### Example

```markdown
---
name: what-time-is-it
description: Give the current date and time.
keywords: time, date, clock
---

# What time is it?

Run:

```bash
date "+%A %d %B %Y — %H:%M:%S (%Z)"
```
```

The `keywords` of that example reach the catalogue as `what-time-is-it — Give the current date and time. (time, date, clock, now, today, heure, date du jour, quelle heure)`.

## Asking for a skill by name

`$name` in a question names a skill to carry out, with the rest of the line as its parameters — `use $greetings Bob`. The window's `$` picker writes it for you; typed by hand it works in the line mode and over ACP too. In an editor the skill also appears in the client's own `/` menu, with an input hint for its parameters — see [ACP — skills as commands](acp.md#skills-as-commands). It appends one line to what the model reads, and the model still makes the `read_skill` call itself. See [Slash commands — the three pickers](slash-commands.md#the-three-pickers).

## Writing a skill the model will ACT on

A skill that tells the model to run a command has one failure mode, and it is not the one you expect: the model **reproduces the command instead of running it**, in a fenced code block, and answers as if it had done the work.

Measured on `mellum2-12b-a2.5b-instruct`, the same skill either way:

| The skill says | Result |
|---|---|
| ` ```bash ` fenced block | `⚙ 0 command(s)` — the model printed the command back, three runs out of three |
| the command **indented**, and "call your `bash` tool with this command" | `⚙ 1 command(s)` — three runs out of three |

A fenced block is a *display* affordance. Put one in the model's context and the most probable continuation is the same block, which is exactly what a small model produces. Naming the tool and indenting the command removes the invitation.

Note this is model-dependent — `qwen3.5-4b` ran the fenced version correctly — so a skill that works on your model may still fail on someone else's. Write it the safe way regardless.

The rule, for a skill whose point is that a command runs:

- **no ` ```bash ` fence around the command to execute.** Indent it by four spaces instead.
- **name the tool**: "call your `bash` tool with this command", not "run:".
- keep saying what the shipped skills say — *the answer is the command's output, nothing else* — because that is what stops the model answering from memory.

A skill that is reference material rather than one command to run — `bg-jobs`, which shows half a dozen — keeps its fences. Nothing there is "the command to execute", so there is no invitation to misread.

## Shipped examples

| Path | `name` | `description` |
|------|--------|---------------|
| `skills/greetings/SKILL.md` | `greetings` | Greet somebody with a festive message. |
| `skills/vulcan-salute/SKILL.md` | `vulcan-salute` | Send a vulcan salute to somebody. |
| `skills/what-time-is-it/SKILL.md` | `what-time-is-it` | Give the current date and time. |

They use the directory-per-skill layout; started from the repository root, `mm` reports `skills: 3`.