# EONYX skills: how Agent Skills and SKILL.md work


A **skill** is a folder with a `SKILL.md` inside.
It is an instruction the agent loads when the task calls for it.
Package a repetitive procedure once: a code review, a deploy checklist, a report format.
It then costs no context until it is actually needed.

<!--more-->

## 🧩 How a skill works

### Progressive disclosure

Only the **name and description** reach the system prompt.
They form a short catalogue under `## Available Skills`.
The full text arrives only when the model calls `load_skill` itself:

```text
## Available Skills
- **review**: Check the diff for bugs and style before committing
- **deploy**: Production rollout checklist

→ the model decides the task is about a review
→ calls load_skill {"name": "review"}
→ receives the full text of SKILL.md
```

So a dozen skills cost a couple of hundred tokens instead of tens of thousands.
The model decides when to load one.
That is why the `description` field decides everything.

### The structure of a skill

A skill is a subdirectory holding a `SKILL.md`:

```text
skills/
└── review/
    └── SKILL.md
```

The file opens with frontmatter between `---`, then the body:

```markdown
---
name: review
description: Check the diff for bugs, style, and tests before committing. Use it for phrases like "review", "check my changes", "before I commit".
tags: [git, quality]
---

# Reviewing changes

1. Look at the diff: `git_diff`, with staged=true if needed.
2. Go through every changed file: logic, mistakes, stray edits.
3. Verify that the tests were run: `go test ./...`.
4. Structure the answer like this: critical → nice to have → nitpicks.
```

Frontmatter fields:

| Field         | Required   | Purpose                                                 |
| ------------- | ---------- | ------------------------------------------------------- |
| `name`        | no         | the skill name; defaults to the directory name          |
| `description` | desirable  | the model uses it to decide whether to load the skill   |
| `tags`        | no         | a list of labels, shown in the catalog                  |

Frontmatter is YAML, `key: value`.
A JSON object works too.
Without a `description` the catalogue shows `No description provided`, and the model will almost never load that skill.

{{< admonition type="tip" title="How to write a description" >}}
The description is a trigger.
Say **what the skill does** and **when to apply it**.
Include the words you actually use: "use it when asked to prepare a release, roll out, deploy".
A vague "useful deployment tips" will not fire.
{{< /admonition >}}

### Skill examples

{{< tabs defaultTab="0" >}}
{{< tab title="Deploy checklist" markdown="true" >}}

```markdown
---
name: deploy
description: Production rollout checklist. Use it when asked to deploy, roll out, or prepare a release.
tags: [ops]
---

# Deploy

1. `git_status` — the working tree is clean, the branch is main.
2. `go test ./...` and `go vet ./...` — green.
3. Build the tag: the version from `git describe --tags` + an increment.
4. NEVER push a tag without confirmation from the user.
```

{{< /tab >}}
{{< tab title="Commit style" markdown="true" >}}

```markdown
---
name: commits
description: The rules for writing commit messages in this project. Use it before any git_commit.
tags: [git]
---

# Commits

- Subject: imperative mood, English, up to 60 characters.
- No mentions of tools or bots.
- A body only if the "why" isn't obvious from the subject.
```

{{< /tab >}}
{{< /tabs >}}

{{< admonition type="note" title="Skills are data, not code" >}}
`SKILL.md` is text handed to the model.
It is never executed.
If a skill needs commands, the model runs them through the normal tools, with every permission gate in place.
{{< /admonition >}}

## 🔍 Discovery and management

### Search directories

Directories are scanned in priority order.
**On a name collision the first match wins.**

| Priority | Directory                       | What it is                     |
| -------- | ------------------------------- | ------------------------------ |
| 1        | `<project>/.eonyx/skills`       | project skills                 |
| 2        | `~/.config/eonyx/skills`        | personal skills                |
| 3        | `~/.config/agents/skills`       | the shared agent skills folder |

So a project `review` overrides your global one.
Two more levers:

- `EONYX_SKILLS_DIR` — a list of paths in the OS separator. It **replaces** the whole list.
- The `skills` array in `eonyx.json` — it **appends** after the standard directories:

```json
"skills": ["/opt/team-skills", "../shared-skills"]
```

`eonyx config` shows what was found, in the Skills section.
The TUI header shows the active ones in the `# skills` segment.

### Managing skills in a session

**Ctrl+K** opens the capabilities dialog, on the **Skills** tab.
Any skill flips off and on with `✓` and `✗`.
The toggle rebuilds the catalogue in the system prompt at once and rebinds `load_skill` to the active set.
A disabled skill disappears from the model's view and cannot be loaded.

`load_skill` is read-only by nature, but it is not on the always-allowed list.
In the TUI its first call goes through the usual permission dialog.
Add it to `allowed_tools` to skip that.

## 📋 Summary

{{< admonition type="abstract" title="Cheat sheet" >}}
- **A skill**
  - a directory with a `SKILL.md`: frontmatter `name`/`description`/`tags` + the instruction body
  - `SKILL.md` is not executed — it's data for the model
- **Progressive disclosure**
  - the prompt holds only the catalog of names and descriptions
  - the model gets the full text through `load_skill`
- **Discovery**
  - the project's `.eonyx/skills` → `~/.config/eonyx/skills` → `~/.config/agents/skills`; the first match by name wins
  - `EONYX_SKILLS_DIR` replaces the list, the `skills` config key extends it
- **Management**
  - `Ctrl+K` → the Skills tab: on/off on the fly
  - active skills are visible in the header
- **The key field**
  - `description`: what the skill does and when to apply it, in the user's own words
{{< /admonition >}}

