Skills
A skill is a user-defined, reusable command — basically a chunk of markdown with frontmatter telling the AI "here's how to handle this kind of task". Once you've written one, it shows up alongside the built-in slash commands in the menu, or can be invoked on-demand by the use_skill tool.
What skills are good for
- Codifying frequent workflows: cutting a release, writing a changelog, running regression tests, generating weekly reports…
- Wrapping complex tasks that need step-by-step guidance: refactor playbooks, bug root-cause analysis, performance tuning
- Making the model follow your defined steps and validation rules on a specific topic
Skill file layout
A skill is usually a directory containing at least a SKILL.md:
my-skills/
├── release/
│ └── SKILL.md
├── write-changelog/
│ └── SKILL.md
└── debug-flaky-test/
├── SKILL.md
└── references/
└── pattern-library.md
A typical SKILL.md:
---
name: release
description: Cut a new release tag, update the changelog, and publish
---
## When to use
When the user says "ship a new version", "cut a release", or "tag it".
## Steps
1. Inspect `git log` for changes since the last release.
2. Ask the user to confirm the semver bump (patch / minor / major).
3. Add the new entry to `CHANGELOG.md`.
4. Run `pnpm build` and ensure it passes.
5. Create the git tag `v<version>` with release notes.
6. Run `pnpm publish` (or the corresponding publish command).
## Rules
- Version numbers must follow semver.
- Never skip tests — fix any failure first.
- If publish fails, roll back the local tag and changelog edits.
Tool preauthorization: allowed-tools
allowed-tools preauthorizes matching tools for the turn that invokes a skill. It is not an exclusive tool whitelist: other tools remain available and follow normal permission and approval rules.
Prefer allowed-tools. The underscore alias allowed_tools accepts the same syntax and validation. Repeated declarations, including mixed spellings, refuse loading.
---
name: setup
description: Install hidapi for vibekeys-plugin
allowed-tools:
- Bash(pip3 *)
- Bash(pip *)
- Bash(python3 -c *)
---
Use a YAML block list, an inline list (allowed-tools: [Bash, read_file]), or a space/comma-separated scalar (allowed-tools: Bash(pip3 *) read_file). Spaces inside patterns are preserved. Names and patterns use the static permissions matcher, including aliases such as Bash/bash and Read/read_file. Malformed declarations refuse loading rather than widening the grant.
Slash commands, the $ menu, /skills and successful use_skill calls share this behavior. Grants belong only to the calling agent and expire on its next user input (including steering), turn completion or cancellation. Session switching, runtime rebuilding and history replay do not restore grants.
Global/project deny rules remain authoritative, and sensitive-path, credential and plan-mode boundaries still apply. AtomCode additionally keeps normal approval for destructive shell commands: a skill cannot preauthorize them. This is a product safety restriction on top of Claude Code compatible preauthorization; explicit static allow rules retain their existing behavior.
Bash preauthorization is not a sandbox. Shell commands may read/write files and access the network. Plugin hooks and shell snippets executed while expanding a skill are separate execution paths, outside this model-tool preauthorization.
Skill directories
AtomCode loads skills from a few well-known locations:
- Global:
~/.atomcode/skills/ - Project-level:
.atomcode/skills/at the current working directory or any ancestor (if present)
Project-level skills override same-named globals, so you can define the same name with different flows per project.
Invocation
Via slash command
Every successfully loaded skill appears in the slash-command completion menu:
> /release
> /write-changelog
> /debug-flaky-test
Via the $ menu
Type a $ at the start of the input box to pop up a skills menu (the same set of invocable skills that /skills lists); keep typing to filter, press Tab to complete the highlighted name, and submit $<name> [args] to invoke that skill. It's a parallel entry point to slash commands: / drives slash-command completion, while $ is dedicated to picking a skill and can carry arguments inline, e.g. $brainstorming help me design login.
In v5.0.3, /skills can match several skill names or fragments in one request and echoes the skills that were actually loaded. Newly installed skills are refreshed automatically, so a restart is normally unnecessary.
Model-driven invocation
The model can call the use_skill tool itself to run a relevant skill as a sub-flow. If you say "publish a new version", the model may pick this skill on its own without you typing /release.
Tips for writing a good skill
- Be specific in the description — it's the menu's blurb, and the main signal the model uses to decide whether this skill applies.
- Order matters — the model reads top-to-bottom; put strong dependencies up front.
- Make branches explicit — "if build fails …; otherwise …" is far more reliable than letting the model guess.
- List red lines — put hard rules in a dedicated "Rules" section to maximise compliance.
- Use sub-docs when needed — park long references in
references/*.mdand leave a one-liner like "see … if needed" inSKILL.md.
Versus project instructions
| Scenario | Where it belongs |
|---|---|
| Always-on project conventions (tech stack, code style, commands) | .atomcode.md |
| A specific workflow that needs to be triggered explicitly | Skill |
| A one-off ad-hoc requirement | Just write it in the prompt |
Next steps
- Basic Usage — back to everyday workflows
- FAQ — why isn't my skill showing up in the menu?