Skill Frontmatter Reference
Skills are markdown documents with optional YAML frontmatter. The frontmatter declares operator-facing metadata: required per-skill environment variables, required tools, and ancillary scripts. The body of the document (everything after the closing ---) is the prompt content injected into the supervisor system prompt when the skill is loaded.
Frontmatter is parsed by parseFrontmatter() in packages/engine/src/utils/frontmatter.ts. The parser is intentionally minimal (no full YAML support) — it handles key: value, string lists, and one level of nested object lists. The resulting metadata is stored on the skill_versions.metadata jsonb column (packages/engine/src/skills/schema.ts:29) and the requiredEnv / requiredTools projections are surfaced by skills.service.ts.
Supported fields
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | no | Display name. The slug is derived server-side via slugify(name) — overridden by slug if explicitly set. |
description | string | no | One-line summary shown in the catalog UI and in tool descriptions when the skill is auto-loaded. |
category | string | no | Free-form grouping for the catalog (e.g. operations, marketing). Defaults to general. |
requiredEnv | array of strings or objects | no | Per-skill environment variables the operator must populate via PUT /api/instances/:slug/skills/:name/env. |
requiredTools | array of strings | no | Tool names (camelCase, exactly as registered) that must be enabled on the instance for the skill to work. |
scripts | array of script objects | no | Inline scripts shipped with the skill. Each entry: { file, description, content }. Stored as jsonb. |
requiredEnv entry shape
Two forms are accepted, mixed in the same array:
requiredEnv:
- GITHUB_TOKEN # shorthand (string) — sensitive=true assumed
- name: GITHUB_OWNER # full form
description: GitHub owner / org used in API calls
sensitive: falseNormalised by normalizeRequiredEnv():
| Field | Type | Default | Notes |
|---|---|---|---|
name | string | — | Env var name. Always required. |
description | string | — | Optional help text shown in the admin UI under the input field. |
sensitive | boolean | true | When true the value is masked in the UI and encrypted at rest. Set to false for non-secret config (e.g. an owner name). |
scripts entry shape
| Field | Type | Notes |
|---|---|---|
file | string | Logical filename (used for display / extraction). |
description | string | One-line summary of what the script does. |
content | string | Full script body. No size limit beyond the text column maximum. |
Fields explicitly NOT supported
These are common mis-spellings or assumptions from other ecosystems — the parser ignores them:
| Mistaken field | Use instead |
|---|---|
version | (none — versions are auto-incremented server-side on every PUT /api/skills/:name) |
requiredSecrets | requiredEnv |
dependsOnTools | requiredTools |
language | (not parsed — set it in the body) |
tags | (not parsed — use category) |
author | (not parsed) |
The parser stores any unknown keys it sees in the raw metadata jsonb but they are never surfaced through the API or UI — treat them as dropped.
Full example
---
name: Issue Triage Workflow
description: Standard triage flow for incoming GitHub issues — label, route, escalate
category: operations
requiredEnv:
- GITHUB_TOKEN
- name: GITHUB_OWNER
description: GitHub owner or organisation
sensitive: false
- name: TRIAGE_SLACK_CHANNEL
description: Slack channel where escalations are announced (#ops-triage)
sensitive: false
requiredTools:
- ghIssue
- ghPR
- slackPostMessage
scripts:
- file: extract-metadata.py
description: Pull issue title / labels / reporter into a structured record
content: |
import sys, json
data = json.loads(sys.stdin.read())
print(json.dumps({"title": data["title"], "labels": [l["name"] for l in data["labels"]]}))
---
# Issue Triage
When a new GitHub issue comes in, follow these steps:
1. Read the issue with `ghIssue`.
2. Apply labels based on the title prefix.
3. If the label is `severity:high`, post to `#ops-triage` via `slackPostMessage`.
4. Open a draft PR with `ghPR` if a fix is obvious.The body (everything after the closing ---) is the prompt content that gets injected into the supervisor when the skill is loaded — either auto-loaded via the auto-load flag, or surfaced through readSkill.