Adding a Skill
Author a builtin SKILL.md package, or install an external skill
Skills come in two kinds. A builtin skill is a SKILL.md package you author in this repo and compile into the binary. An external skill is a vendored third-party package you install with a command or a prompt, no code change required. This page covers both.
Adding a builtin skill
Builtin skills are SKILL.md packages under
apps/agent/src/skills/builtin/<id>/. The loader (loadBuiltinPackageSkills)
discovers them automatically, gates them on requires_env, and serves them to
the agent at turn time. There is no index.ts entry to edit: discovery scans
the direct subdirectories of builtin/, so the package must sit one level
down (a package nested deeper is silently skipped).
Minimal example
Create apps/agent/src/skills/builtin/my-new-skill/SKILL.md:
---
name: my-new-skill
description: "Pull research data from My Provider. Use when the user asks about X, Y, or Z."
metadata:
minara:
id: research.my_provider
priority: 50
tool_names: [my_provider_search, my_provider_detail]
requires_env: [MY_PROVIDER_API_KEY]
routing:
keywords: ["my provider", X, Y]
---
You are the My Provider research skill. When the user asks about X, Y, or Z,
call `my_provider_search` and summarize the results.
Prefer fresh data over cached. Cite source URLs.The loader parses that frontmatter into a DomainSkill at boot. Top-level
name + description follow the skill-creator standard; everything
Minara-specific lives under metadata.minara.*. Deep content splits into
references/<area>.md (the model reads them on demand), and structured fields
the frontmatter can't express cleanly go in a SKILL.config.json sidecar.
Conventions
- Body: soft cap ~5000 chars. Split long guidance into
references/<area>.mdrather than bloating the package body. description: builtin skills cap at 200 chars. Write a faithful what-it-does plus when-to-use sentence with strong, collision-free keywords so the router surfaces it.tool_names: must resolve in the tool registry. They feed the activation whitelist, so keep the list minimal.requires_env: when any listed var is missing, the registry hides the skill. This lets operators run the agent without setting every key.- Risk: skills carry no risk tier. Each tool declares its own
PermissionTier, and the tier gate enforces confirmation at the tool-call layer. - Catalog guard: add the new
idtoEXPECTED_IDSinbuiltin-catalog-guard.test.tsin the same commit.
Reference examples
minara-perps/(tool-heavy)deep-research/(prompt-heavy)analysis/(methodology lenses split acrossreferences/)
The inline-TS exception
One builtin ships as inline TypeScript instead of a SKILL.md package:
local.workflow, whose prompt renders a live Custom Agents catalog at
runtime. It registers via BUILTIN_SKILLS in
apps/agent/src/skills/builtin/index.ts. Reach for inline TS only when the
content is genuinely runtime-dynamic, never because a prompt is short.
Adding an external skill
External skills live under apps/agent/src/skills/external/* as vendored
SKILL.md packages with a .minara-skill.json manifest. You install them, you
don't code them:
minara skills add <git-url> [--subpath <dir>] [--id <id>]The install clones the package, detects its license, records it in
.minara-skill.json, and refuses content under a proprietary license.
buildExternalDomainSkills() then builds a DomainSkill from the SKILL.md
frontmatter at boot, so external skills route through the same registry and
tier gate as the builtins.
For the full user-facing flow, including installing by prompt in chat, see Installing Skills.
Reach for an external skill when you want a third-party package maintained by
its upstream (e.g. coingecko, hyperliquid), a non-TypeScript reference
(shell, Python), or content that upgrades independently of the agent binary.
When a capability exists as both, prefer the builtin.