Skills
August 30, 2026 · View on GitHub
Skills are reusable, model-readable instruction folders that the agent loads
on demand. The Inference Gateway CLI uses the same on-disk format as per standard, so a folder
authored for any of those tools drops into .infer/skills/ (or the
.agents/skills/ open standard) unchanged.
Format
A skill is a directory containing a SKILL.md file with YAML frontmatter
at the top:
---
name: pdf-helper
description: Extract text from PDFs. Use when the user asks to read, summarise, or analyse a PDF file.
---
# PDF Helper
Step-by-step instructions for the model:
1. Use the Bash tool to invoke `pdftotext input.pdf -` and capture stdout.
2. If the PDF is image-only, fall back to `tesseract` for OCR.
3. ...
The directory may also ship optional helpers - references/, scripts/,
assets/ - that the model reads (or executes via the Bash tool) once it
has activated the skill.
Frontmatter
name(required): ≤64 chars; lowercase letters, digits and hyphens only; must equal the directory name; must not containinfer,claude,anthropic,geminioropenai.description(required): non-empty, ≤1024 chars.
Unknown frontmatter keys are tolerated, so vendor extensions (e.g. Gemini's
disabled: flag, Anthropic's allowed-tools:) won't cause validation
failures even though the CLI ignores them.
Locations
The CLI scans three directories, in precedence order (first match wins on a
name collision):
- Project-local:
.infer/skills/<name>/SKILL.md - Open standard:
.agents/skills/<name>/SKILL.md - User-global:
~/.infer/skills/<name>/SKILL.md
.agents/skills/ is the emerging cross-tool convention (Claude Code, Gemini
CLI, Codex CLI), so a repo that ships skills there works without moving them
into .infer/skills/. A project's .infer/skills/ still wins over both the
open-standard and user-global locations - useful for overriding a personal or
shared default with a per-project variant.
Progressive discovery from the catalog (optional)
When agent.skills.discovery.enabled is true, the CLI also loads the
centralized skills catalog published by
inference-gateway/skills as
catalog.json. The catalog is the last fallback in the precedence chain:
- Project-local:
.infer/skills/<name>/SKILL.md - Open standard:
.agents/skills/<name>/SKILL.md - User-global:
~/.infer/skills/<name>/SKILL.md - Plugin skills:
<plugins-dir>/<name>/skills/<name>/SKILL.md - Catalog (progressive discovery): fetched from the registry on demand
A local skill with the same name always short-circuits discovery - no catalog lookup or download is performed. Only catalog metadata (name, description) is consulted up front; the full skill body is fetched only when the skill is actually activated (progressive disclosure).
Catalog skills therefore show up in the chat / dropdown and in the agent's
AVAILABLE SKILLS list alongside local ones, marked as not-yet-downloaded.
Invoking one (/rust, or "use the rust skill") downloads its SKILL.md on the
spot. The index fetch and the download both use GITHUB_TOKEN / GH_TOKEN
when set, which raises GitHub's 60-requests-per-hour anonymous rate limit; the
token is only ever sent to GitHub hosts.
Dynamically downloaded skills are stored under ~/.infer/projects/<project-slug>/tmp/skills/ and are
cleaned up after the session ends by default. Set
agent.skills.discovery.cleanup: false to retain them across sessions.
# .infer/config.yaml
agent:
skills:
enabled: true
repository: "inference-gateway/skills"
discovery:
enabled: true
cleanup: true
repository is the <owner>/<repo> GitHub repository skills come from - the
value above is the default, and INFER_AGENT_SKILLS_REPOSITORY overrides it.
It drives everything: the infer skills install <name> shorthand, on-demand
catalog downloads, and the catalog index, which is read from
https://raw.githubusercontent.com/<repository>/main/catalog.json. Point it at
your own fork and all three follow. It is the only knob involved: the index and
the skill bodies always come from the same repository, so there is nothing to
keep in sync.
Catalog size
The whole catalog index is fetched and held in memory, which is cheap even at
a few thousand entries, but the AVAILABLE SKILLS: block in the system prompt
is bounded by agent.skills.max_chars (default 4000). Skills that have a local
SKILL.md are listed first - the model can Read those - and whatever does not
fit is reported as a count, not a list of names. So a large catalog costs a
fixed number of prompt tokens no matter how big it gets; use
infer skills search to find what the prompt does not name.
Searching the catalog
infer skills search rust
infer skills search "pull request" --limit 20
infer skills search --format json
Names are fuzzy-matched and ranked by score, so typos and partial names still
find the skill. Descriptions match on whole words only - a substring match
makes a short query useless, since go otherwise hits "good" and "algorithm"
across half the catalog. Name matches are listed before description matches,
ten results by default. Omit the term to browse.
The Installed column shows the path to the local SKILL.md when the skill is
already on disk, and no when it is not - relative to the working directory for
a project skill, ~-prefixed for a user-global one. The header shows the
catalog's published release. Descriptions are truncated to keep the table inside
the terminal; pass --no-trunc for the full text.
The catalog is versioned as a whole - individual skills carry no version of their own, so there is nothing to pin per skill.
Search works regardless of agent.skills.discovery.enabled - fetching the
index on an explicit search is a direct user action, not background discovery.
Matching happens locally over the fetched index, since catalog.json is a
static file with no server-side query support.
Built-in skills
The CLI ships a small set of built-in skills embedded in the binary. On
infer init they are seeded into the user-global ~/.infer/skills/ - the same
directory the loader reads - only if absent, so they behave exactly like a
skill you authored there yourself. Current built-ins:
tmux- drive interactive terminal programs (TUIs, REPLs, another CLI's chat UI) by scripting tmux withsend-keys/capture-pane.
Because they are ordinary user-scope skills, you customise them with the same knobs as any other skill - there is no special "built-in" mode:
- Edit in place: change
~/.infer/skills/tmux/SKILL.md; a re-run ofinfer initnever re-seeds over your edit. - Override per project: a
.infer/skills/tmux/(or.agents/skills/tmux/) shadows the built-in for that repo (first match wins). - Disable: add the name to
agent.skills.disabled_skills. - Reset to the shipped default:
infer init --overwritere-seeds it. Note this refreshes the other shipped~/.inferdefaults too, so use it when you want a clean baseline; to restore just one skill, replace itsSKILL.mdby hand (a plaininfer initwill not re-seed over an already-initialized home).
Enabling
Skills are disabled by default (zero token cost when off). Enable via config or environment variable:
# .infer/config.yaml
agent:
skills:
enabled: true
disabled_skills: [] # optional list of skill names to skip
INFER_AGENT_SKILLS_ENABLED=true infer chat
When enabled, the agent's system prompt gains an AVAILABLE SKILLS: block
listing each skill's name, description, scope, and the absolute path
to its SKILL.md. The body of SKILL.md is not loaded at startup - the
model reads it on demand using the existing Read tool. This is "progressive
disclosure" and matches the behaviour of other vendors.
Discovering skills
infer skills list
This always works regardless of agent.skills.enabled, so you can verify
discovery before turning the feature on. The output shows each skill's name,
scope, description, absolute path, and any validation errors for skills that
were skipped.
Installing skills from GitHub
You can install a skill folder directly from a GitHub repository:
infer skills install https://github.com/anthropics/skills/tree/main/skills/pdf
The URL must point at a directory inside the repo, formatted as
https://github.com/<owner>/<repo>/tree/<ref>/<path-to-skill-folder>.
URLs that point at a file (/blob/) or at the repo root are rejected
with a clear error.
Flags:
--user- install to~/.infer/skills/instead of the project-local.infer/skills/.--overwrite- replace an existing skill folder of the same name. Without this flag, an existing folder is left untouched and the install fails fast.
After download, the same frontmatter validator that runs at startup runs
against the downloaded folder - so what installs is what loads. If
validation fails (missing name, name doesn't match the directory name,
etc.) the folder is removed and the reason is printed. There is never a
half-installed state.
Authentication
Requests are unauthenticated by default, which GitHub limits to 60 API
requests per hour per IP - easily exhausted on shared CI runners. Set
GITHUB_TOKEN (or GH_TOKEN, matching the gh CLI) in the environment to
authenticate:
GITHUB_TOKEN="$MY_TOKEN" infer skills install acme/internal-comms
Authenticating raises the limit to 5,000 requests per hour and lets you install from private repositories the token can access.
Limitations:
- Each install is one API call (the tree enumeration) plus one raw download per file in the skill folder. Without a token you share the 60 requests/hour anonymous limit with everything else on your IP.
- Refs containing a literal
/(e.g.feature/foobranches) are not supported. Use a tag, the default branch, or a single-segment branch.
Uninstalling skills
infer skills uninstall pdf
infer skills uninstall --user internal-comms
The argument is the on-disk skill directory name (matching the skill's
name frontmatter). By default the project-local .infer/skills/<name>/
is removed; pass --user to remove from ~/.infer/skills/<name>/.
There is no confirmation prompt - matches npm uninstall,
brew uninstall, etc. The skill name is regex-validated before any
filesystem operation, so it cannot be used to traverse outside the
configured skills directory.
Authoring tips
- Make
descriptionactionable. It is the routing signal. Tell the model both what the skill does and when it should activate. "Extract text from PDFs. Use when the user asks to read, summarise, or analyse a PDF file." is a good description. - Keep
SKILL.mdfocused. Usereferences/for long supporting docs; link to them fromSKILL.mdso the model only reads them when needed. - Use the existing tools. The model already has
Read,Bash, etc. Skills are instructions on top of those, not new capabilities.
Security
Skills can instruct the model to run shell commands, read files, or call
external APIs. Treat a skill like any other piece of executable content -
only install skills from trusted sources. The CLI's normal tool-approval
system still gates each command, but a malicious skill could craft a
plausible-looking Bash call.
The frontmatter name validator rejects names containing vendor strings
(claude, anthropic, gemini, openai, infer) so impersonating an
official skill is harder.
Portability
The on-disk contract is intentionally identical to:
- Anthropic Claude Code - https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview
- Google Gemini CLI - https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/skills.md
- OpenAI Codex CLI - https://simonwillison.net/2025/Dec/12/openai-skills/
Folders from github.com/anthropics/skills and github.com/google/skills
work without modification when copied into .infer/skills/ or .agents/skills/.
Out of scope (for now)
- A dedicated
activate_skilltool - the model usesReaddirectly. - A skill marketplace or curated index - discovery is up to the user; see Installing skills from GitHub for the install flow.
- Authenticated installs from private repositories.
- Sandboxing beyond what the existing tool-approval system already provides.