Skip to content

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 llm provider, a claude_local/codex_local CLI, or a remote worker running llm or docker — the agent sees the same merged skill set: instance + company + agent skills, with agent > company > instance precedence. On prompt-only paths (llm) the bodies are inlined into the ## Available Skills section; on CLI/docker paths the skills are delivered as files (--add-dir locally, a read-only /skills mount with a TRIMUS_SKILLS_DIR env 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 remote llm/docker workers — 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

my-skill/
├── SKILL.md      # The body — free-form markdown
└── skill.yaml    # Optional metadata

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-skill instead).

## How to use

curl -s -H "Authorization: Bearer $TRIMUS_API_KEY" \
  "$TRIMUS_BASE_URL/api/example"

## 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