Writing a skill¶
A skill is a named piece of instruction you make available to agents. The agent encounters the skill's content while it works, so the LLM has the procedure or knowledge in front of it when it decides what to do.
Trimus has two skill systems. They look similar but reach the agent through different paths, so pick deliberately:
| System | Stored as | How the agent reads it |
|---|---|---|
DB skill (company_skills) |
A row in PostgreSQL | Its full markdown body is inlined into the heartbeat prompt's ## Available Skills section (for every agent in the company, once an operator enables the skill). |
Filesystem skill (fsskills) |
slug/SKILL.md (+ optional skill.yaml) on disk |
Materialised into the agent's workspace directory. Reachable by CLI-backed adapters (claude_local, codex_local, and their sandboxed variants) that run in that workspace. |
In practice: DB skills are how you push guidance into an llm-adapter
agent's prompt; filesystem skills are how you give a CLI-backed agent a
library of SKILL.md files on disk to open and follow. The shipped
built-in skills are seeded into the DB and materialised to disk, so they
work for both kinds of agent.
Delivery parity (#109). Whichever adapter runs an agent — the in-process
llmprovider, aclaude_local/codex_localCLI, or a remote worker runningllmordocker— the agent sees the same merged skill set: instance + company + agent skills, withagent > company > instanceprecedence. On prompt-only paths (llm) the bodies are inlined into the## Available Skillssection; on CLI/dockerpaths the skills are delivered as files (--add-dirlocally, a read-only/skillsmount with aTRIMUS_SKILLS_DIRenv var in a container). Either way the set is identical — and disabling a company skill or hiding an instance skill (below) removes it from every path at once.
DB skills¶
Creating one¶
Sidebar → Skills → add a skill (or POST
/api/companies/{id}/skills). A skill has a name, an optional
description, and a definition — a JSON-encoded markdown string
that becomes the body inlined into agent prompts. The Skills UI gives you
a markdown editor; what you type is stored as the definition.
How it reaches the agent¶
A DB skill only surfaces to agents once it is enabled (it's tracked as
a skill-kind capability; an operator enables it). When enabled, the
engine inlines its full body into the ## Available Skills section of
every heartbeat prompt for that company:
## Available Skills
These are reusable prompt snippets you can pull from when responding...
### draft-incident-postmortem
_Structure a postmortem: timeline, impact, root cause, remediation._
# Draft Incident Postmortem
Use this when asked to write a postmortem...
The body is inlined whole — there is no "the agent fetches it on demand" mechanism. That has two consequences:
- Make the first line earn its place. Lead with what the skill does and when to use it.
- Mind the token budget. Every enabled skill rides in every
heartbeat for the company. Disable skills you don't want crowding the
prompt; the Skills/Capabilities page is where you toggle them. As of
#109 disabling a skill withholds it from every execution path — the
in-process prompt,
claude_local, and remotellm/dockerworkers — not just the in-process prompt, so a disabled skill is never shipped to a worker or container either.
An agent records that it used a skill by emitting /use-skill <name> —
that's an audit marker, not a fetch (the body is already in context).
Agents installing DB skills¶
An agent can propose a new DB skill mid-run with
/install-skill. Those install disabled
and raise an approval — a skill body injects directly into prompts, so a
human audits before it goes live. See ../concepts/approvals.md.
Filesystem skills¶
A filesystem skill is a directory containing a SKILL.md body and an
optional skill.yaml metadata file.
Scopes¶
There are three scopes, picked by where on disk the skill lives. At run time the engine merges them with agent > company > instance precedence into a single workspace skills directory for the run:
| Scope | Path on disk | Visibility |
|---|---|---|
| Instance | $TRIMUS_HOME/skills/<slug>/ |
Every company on this server. |
| Company | <company-home>/skills/<slug>/ |
All agents in that company. |
| Per-agent | <agent-home>/skills/<slug>/ |
Only that one agent. |
Use the smallest scope that fits.
File layout¶
The directory name is the slug. Slugs must be lowercase, contain only
[a-z0-9-], have no path separators, and not start with a dot. If you
drop a flat my-skill.md file directly into a skills directory, Trimus
auto-promotes it to my-skill/SKILL.md on the next listing pass
(slugifying the filename).
skill.yaml¶
Optional, and parsed strictly — an unknown field is a parse error, not a silently ignored line. The recognised fields are:
name: my-skill # human-facing name
description: One sentence. # short description
enabled: true # optional bool
created_by: builtin # audit metadata (free-form)
tags: # optional list
- incident
- ops
All fields are optional. created_by is just audit metadata — the shipped
built-in skills use created_by: builtin; skills Trimus materialises from
the DB onto disk are stamped created_by: db. Omitting skill.yaml
entirely is fine; the slug then carries the name.
SKILL.md¶
The body the agent reads. Free-form markdown. Conventions that work:
- First H1 — the human-facing title; match the slug.
- First paragraph — one sentence on what the skill does and when to use it. Don't bury the trigger.
- "When to use" / "When NOT to use" — be explicit about the boundaries. Agents will reach for a skill they shouldn't if you don't say "no".
- "How to use" — a fully worked example. Agents copy examples
literally; if your example uses
curl -H "Authorization: Bearer $TRIMUS_API_KEY", the agent will too. - "Notes" / "Rules" — invariants the agent must preserve.
A template (the SKILL.md body — write it as ordinary markdown):
# My Skill
One sentence describing what this skill does. Use this skill whenever you are asked to "X", "Y", or "Z".
## When to use - The task matches X. - The user explicitly asked for Y.
## When NOT to use - The task is actually a Z (use the
z-skillinstead).## How to use
## Notes - Rule the agent must follow. - What this skill is NOT.
Calling back into Trimus from a skill¶
During a run, CLI-backed adapters get two environment variables exported
into the workspace: TRIMUS_API_KEY (an ephemeral key minted for this
run and revoked when it ends) and TRIMUS_BASE_URL. A skill's example
commands can use those to call the Trimus HTTP API — this is how the
shipped hire-agent, terminate-agent, and list-team skills work. The
key is scoped to the running agent and lives only for the run.
Materialisation and updates¶
The engine writes the company's DB skills to disk and merges the three
filesystem scopes at the start of each run — materialisation is
additive (your hand-written filesystem skills are never deleted by a
DB-skill pass). Edits to a SKILL.md take effect on the next heartbeat;
there is no rebuild step. Version filesystem skills in git like any other
markdown.
The shipped built-in skills¶
Seeded into every instance from internal/skills/data/ at boot:
| Skill | What it covers |
|---|---|
hire-agent |
Provision a new agent via POST /api/companies/{id}/agents. |
terminate-agent |
Delete an agent, with pre-flight safety checks. |
list-team |
Read the org tree (GET /api/companies/{id}/org-tree). |
write-project-file |
Persist a file with /write-file, including the fallback cascade. |
request-approval |
When and how to pause for human sign-off. |
para-memory-files |
The agent's memory-files convention. |
Read those SKILL.md files for patterns to copy — especially
write-project-file (opinionated rules + "what this skill is NOT") and
hire-agent (a multi-step curl procedure with a full worked example).
Where to next¶
- the-write-file-action.md — deep dive on
/write-file, which most "write a report" skills reference. - hiring-and-firing.md — using the shipped hire/terminate skills.
- system-prompts.md — when guidance belongs in the prompt rather than a skill.
- debugging-agents.md — when the agent isn't using a skill you expected.