For skill authors
Skill specification
How to author a Pectus skill. This is the contract — the runner and the CMS depend on it.
Folder layout
skills/<skill-name>/
├── SKILL.md required — manifest + prompt
├── schema.ts required — Zod schema for structured output
└── reference/ optional — knowledge files the skill loads
├── *.md
└── *.json
The folder name is the skill name. Use kebab-case.
SKILL.md frontmatter
---
name: my-skill # required, must match folder name
description: One line shown in UI # required
version: 1.0.0 # required, semver
inputs: # required, named inputs gathered by the runner
- project_id
- some_other_input
outputs: # required, named outputs returned to the caller
- some_named_output
schema: ./schema.ts # required, path to Zod schema
model: claude-opus-4-7 # optional, default claude-opus-4-7
cache_inputs: # optional, inputs to mark with cache_control
- brand
- icp_profile
---
Body
The body of SKILL.md is the system prompt Claude receives. Plain markdown. Reference files in the reference/ subfolder are inlined by the runner when the body contains {{ reference/<file> }} directives.
Keep the body under 4000 tokens. Long reference material goes in reference/ so prompt caching can absorb it.
Inputs
Named inputs the runner knows how to gather:
| Input | Source |
|---|---|
project_id | Always required. The project (market) the skill runs against. |
brand_id | Resolved from the project. Skills that need brand-level data declare this. |
week_start | ISO date. Defaults to current week’s Monday. |
top_keywords | Top 200 keywords by search volume from keywords for the project. |
recent_articles | Last 500 articles from articles for the project. |
icp_profile | The icp_profiles row for the project. |
brand | The brands row for the project’s brand (voice, tonality, colors, fonts). |
knowledge_insights | Contents of brands/<slug>/knowledge/insights.md if present. |
answer_public_entries | Up to 2000 rows from answer_public_entries. |
last_run | Most recent skill_runs row for this skill + project. |
sitemap | All content_source_pages rows for the project’s primary content source. |
article_bodies | Block content from articles. |
Skills declare which they need. The runner fetches only those.
Outputs
The Zod schema in schema.ts defines exactly what the skill returns. Outputs land in:
skill_runs.output(always — every run logs the full output here)- Skill-specific destinations:
weekly_analysesforweekly-analysis, file writes forinternal-linkingandknowledge-digest
Skills don’t write to arbitrary tables. If a skill needs to land data somewhere structural, that’s a CMS concern — the runner exposes hooks the CMS uses.
Caching
Mark large, slow-changing inputs with cache_inputs: in frontmatter. The runner adds cache_control: { type: "ephemeral" } to those input blocks. Cache TTL is 5 minutes — back-to-back skill runs in a session reuse the cache.
Don’t cache inputs that change every run (keywords, articles). The cache miss costs more than no cache at all in those cases.
Versioning
Bump version in frontmatter when you change the schema or the prompt in a way that produces materially different output. The runner records the version on every skill_runs row so changes are auditable.
Breaking changes (schema field removed, semantics changed) require a major bump. Additions are minor. Prompt tweaks that don’t change schema are patch.
Authoring locally vs upstream
Skills you write yourself live in skills/ alongside the upstream-shipped ones. pectus update is a git rebase, so your locally-added skill files are preserved untouched. If you edit an upstream skill, the rebase surfaces a conflict; resolve by keeping your changes if they’re brand-specific, or by sending a PR upstream if they’re general.
The fastest path to a new skill is the make-it skill:
npx pectus make-it skill
The CLI prompts you for the brief (what does it do, what core blocks does it consume, what inputs does it need, what does it produce), calls make-it, and writes a complete scaffold. Then:
- Test in your fork via
npm installandnpx pectus analyze --project <code> --skill <your-skill>against your dev Supabase. - Open a PR to
pectusai/pectusfor an official skill, or push to your own GitHub repo for community distribution.
Community contributions are tracked at pectus.dev.
Authoring an app instead
If what you want to add is a publisher, a data source, or anything that talks to an external service, you want an app, not a skill. See apps-spec. The decision rule: if the unit of work is a verb the model performs (“write”, “analyze”, “cluster”), it’s a skill. If it’s a surface (“WordPress”, “GA4”, “content-insights”), it’s an app.