MINARA

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>.md rather 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 id to EXPECTED_IDS in builtin-catalog-guard.test.ts in the same commit.

Reference examples

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.

On this page