Skill files are modular Markdown documents that teach agents how to perform specific recurring tasks correctly in your codebase. Where AGENTS.md describes the whole project and its boundaries, a skill describes how to do one particular job — and is only injected into context when it is relevant.
What a skill file is
A skill is a self-contained how-to document written for your AI agents. It captures institutional knowledge that would otherwise live in a Confluence page, a Slack thread, or a senior engineer's head: the exact steps, pitfalls, and conventions for a specific type of work in your repository.
AGENTS.md sets the scope and rules for all agents. Skills are the playbooks agents consult when they encounter a known pattern.
When to write a skill
Write a skill when any of these apply:
- Repeated patterns — The same multi-step operation comes up often (database migrations, adding a new API endpoint, cutting a release).
- Codebase-specific conventions — The "right way" diverges from public documentation or common defaults (a custom ORM, a non-standard branching model).
- Pitfall prevention — An agent has made the same class of mistake more than once. A skill codifies the correction.
- Onboarding acceleration — Human teams document these things for new engineers. Agents benefit from the same documentation.
File format and naming
Skill files live under skills/ in your project root. Use a descriptive, kebab-case filename:
skills/
db-migration.md
api-contract-change.md
pr-workflow.md
data-backfill.md
Each file begins with YAML frontmatter:
---
title: "Database Migrations"
tags: ["database", "migrations", "postgres"]
applies_to: ["*.py", "alembic/**"]
---
...skill body...
| Frontmatter key | Purpose |
|---|---|
title |
Human-readable name shown in The Station and command menu |
tags |
Keywords used by qmd vsearch for semantic matching |
applies_to |
Glob patterns — files that, when mentioned in a task, increase the relevance score |
How ChooChoo selects skills
When an agent starts a task, ChooChoo uses qmd vsearch — a semantic vector search over your skill files — to find the most relevant skills for the current task description and inject only those into the context window.
To write skills that surface reliably:
- Start the body with a one-sentence summary.
qmd vsearchuses the opening text heavily for matching. "How to write and run a database migration using Alembic in this app" beats "Database stuff". - Use the same vocabulary as task descriptions. If engineers say "cut a release", write "cut a release" in the skill — not "create a release tag".
- Tag accurately. Tags are used for exact-match boosting in addition to semantic search.
- Keep skills focused. One skill per distinct task type. Broad skills surface for the wrong tasks; narrow skills surface for the right ones.
Full example
skills/db-migration.md:
---
title: "Database Migrations"
tags: ["database", "migrations", "postgres", "alembic"]
applies_to: ["alembic/**", "app/models/**"]
---
How to write and run a database migration using Alembic in this app.
## Context
This app uses PostgreSQL with Alembic for schema migrations. All migrations
live under `alembic/versions/`. The Alembic config is in `alembic.ini`.
Never edit migration files after they have been merged to main.
## Steps
1. **Generate the migration file:**
alembic revision --autogenerate -m "describe_your_change"
This creates a new file in `alembic/versions/`. Review it before proceeding.
2. **Edit the generated file** if autogenerate missed anything (e.g. index
additions, custom types). Check both `upgrade()` and `downgrade()`.
3. **Apply locally:**
alembic upgrade head
4. **Run the test suite** to confirm nothing is broken:
mise run test
5. **Commit the migration file** together with any model changes in the same
commit. Never commit them separately.
## Pitfalls
- **Do not use `--autogenerate` alone for column renames.** Alembic treats a
rename as drop + add, which destroys data. Write the migration by hand.
- **Check for circular imports.** Alembic imports your models at migration
time; circular imports cause `sqlalchemy.exc.InvalidRequestError`.
- **Do not backfill large tables in the migration.** Write a separate backfill
script and run it asynchronously after the migration.
Registering skills in choochoo.toml
Point ChooChoo at your skills directory:
[context.skills]
sources = ["skills/**/*.md"]
Multiple globs are supported — you can pull skills from subdirectories or from a shared monorepo skills library:
[context.skills]
sources = [
"skills/**/*.md",
"../shared-skills/**/*.md",
]
ChooChoo discovers all matching files at startup. Skills are not injected all at once — qmd vsearch selects the relevant subset per task.
See also
- Context Compilation — How skills integrate with
AGENTS.mdand JIT context injection. - Configuration — Full
choochoo.tomlkey reference includingcontext.skills.