# Contributing a Skill

**Parent:** [`spec.md`](spec.md) · **Siblings:** [`skills-protocol.md`](skills-protocol.md) · [`architecture.md`](architecture.md) · [`modes.md`](modes.md)

> Want to read the protocol spec instead? See [`skills-protocol.md`](skills-protocol.md). This file is the **how-to** for shipping a skill upstream — what to write, how to run it locally, what we'll send back at review.

A skill is the most leverage you can ship into Open Design without writing framework code. One folder, one Markdown file with frontmatter, a hand-built example, and the picker shows it. This guide walks you through the path from `git clone` to merged PR, plus the bar we hold skill PRs to and the patterns that get bounced.

If you only have ten seconds, the picture is:

> **Drop a folder under `skills/`, restart the daemon, your skill shows up in the picker. The whole rest of this doc is about making that folder good enough to merge.**

---

## 1. Ship a skill in 30 minutes — the happy path

```bash
# 1. Fork & clone
git clone git@github.com:<your-username>/open-design.git
cd open-design
git checkout -b skill/<your-skill-name>

# 2. Bootstrap (Node 24, pnpm 10.33.x)
corepack enable
pnpm install

# 3. Copy the closest existing skill as a starting point
cp -r skills/dating-web skills/<your-skill-name>
# Edit skills/<your-skill-name>/SKILL.md — change name, description, triggers,
# rewrite the workflow body, replace example.html with your own hand-built sample.

# 4. Run the dev loop and verify the picker
pnpm tools-dev run web
# Open the URL it prints (typically http://127.0.0.1:5173).
# Switch to the mode you set in od.mode — see "Skill modes" below for the
# full list (Prototype / Deck / Template / Design system / Image / Video / Audio).
# Your skill's name should appear in the picker. Click it, send the example_prompt.

# 5. Open a PR
git add skills/<your-skill-name>
git commit -m "skills: add <your-skill-name>"
git push -u origin skill/<your-skill-name>
gh pr create --title "skills: add <your-skill-name>" --body "..."
```

That's the whole loop. The next sections explain each step in depth and tell you what we look at when the PR lands in review.

---

## 2. What a skill IS, and what it isn't

A skill is a **recipe for producing one kind of artifact**. Not a feature, not an integration, not a marketing page.

**Yes:**
- "A 6–10 page investor pitch deck with editorial typography" → deck-skill
- "A single-screen consumer dashboard with stats, charts, and a community ticker" → prototype-skill
- "A populated copy of our PM-spec template with the brief filled in" → template-skill
- "A `DESIGN.md` for the Linear brand sampled from their site" → design-system-skill
- "A 9:16 short-form video reel from a script + b-roll prompts" → video-skill
- "A square poster from a one-line brief" → image-skill
- "A 30-second jingle from a mood description" → audio-skill

**No:**
- A wrapper around a third-party API (Stripe, Alipay, Slack API, GitHub API). That's a feature; submit it via the agent / daemon path, not as a skill.
- A model loader, vendor SDK bundle, or "BYOK for `<provider>`". OD's bet is "your existing CLI is enough."
- A brand-promotion bundle for a sponsor or product launch. Skills are reusable artifact recipes, not campaigns.
- A duplicate of an existing skill with marginal differentiation. Before opening, search `skills/` and read the descriptions of the closest 2–3 — if you can't articulate the differentiator in one sentence, fold your work into the existing skill instead.
- A skill whose only output is a screenshot or a video. The artifact has to be something the agent generates from a prompt, not a static asset shipped in `assets/`.

**Third option: ship as an external skill bundle.** If your workflow is genuinely a recipe (not a daemon feature) but is too vendor-specific or audience-narrow to land in-tree, the skills protocol supports user-global skills via `~/.claude/skills/` (see [`skills-protocol.md` §3](skills-protocol.md#3-skill-discovery--precedence)). Publishing your bundle as a standalone repo lets users `git clone` or `od skill add` it without us taking on the maintenance surface. This is the right path for payment-provider workflows, regional marketplace integrations, in-house design systems, and similar — not a rejection, just a different distribution channel.

If you're not sure your idea fits, **open a discussion first** ([github.com/nexu-io/open-design/discussions](https://github.com/nexu-io/open-design/discussions)) — we'd rather spend 5 minutes redirecting than have you build the wrong thing for a week.

---

## 3. Skill anatomy — the minimum

```text
skills/<your-skill>/
├── SKILL.md                    # required — frontmatter + workflow
├── example.html                # required if od.preview.type is html or jsx — the hand-built sample
├── assets/                     # optional but typical — seed files the skill copies into the artifact
│   └── template.html
└── references/                 # optional — knowledge files the agent reads during planning
    ├── checklist.md            # required for merge — P0 gates the agent must pass
    ├── layouts.md
    └── components.md
```

### `SKILL.md` frontmatter cheat sheet

The first three keys (`name`, `description`, `triggers`) are the [Claude Code base spec](https://docs.anthropic.com/en/docs/claude-code/skills) — your skill works in plain Claude Code with just these. Everything under `od:` is OD-specific and optional, but **`od.mode`** decides which group the skill shows up under.

```yaml
---
name: your-skill
description: |
  One paragraph. The agent reads this verbatim to decide if the user's
  brief matches. Be concrete: surface, audience, what's in the artifact,
  what's not.
triggers:
  - "your trigger phrase"
  - "another phrase"
  - "中文触发词"

od:
  mode: prototype           # prototype | deck | template | design-system | image | video | audio
  platform: desktop         # desktop | mobile
  scenario: marketing       # free-form tag for grouping in the picker
  featured: 1               # any positive integer surfaces under "Showcase examples"
  preview:
    type: html              # html | jsx | pptx | markdown
    entry: index.html
  design_system:
    requires: true          # does the skill read the active DESIGN.md?
    sections: [color, typography, layout, components]
  example_prompt: "A copy-pastable prompt that nicely shows what this skill does."
---

# Your Skill

Free-form Markdown describing the workflow the agent should follow.
Numbered steps work well. Lift the format from skills/dating-web/SKILL.md
or skills/guizang-ppt/SKILL.md.
```

Full grammar — typed inputs, slider parameters (`od.parameters`), capability gating (`od.capabilities_required`), `od.craft.requires` for cross-brand craft references — lives in [`skills-protocol.md`](skills-protocol.md). You don't need any of those to ship v1.

---

## 4. Running it locally

You need exactly four commands once your tree is set up.

```bash
# 1. Bootstrap (only the first time, or after pulling main with manifest changes)
corepack enable
pnpm install

# 2. Run the daemon + web
pnpm tools-dev run web
# Note the URL it prints — usually http://127.0.0.1:5173 for web,
# http://127.0.0.1:7456 for daemon.

# 3. After editing SKILL.md, refresh the picker — the daemon re-scans skills/
#    on every /api/skills request, so reopening the picker (or refreshing the
#    web tab) picks up your edit without a restart. If frontmatter parsing
#    fails or the new skill never shows up, restart pnpm tools-dev run web
#    and check the daemon stderr for the parse error.

# 4. Verify your skill end-to-end:
#    - Switch to the mode you set in od.mode (Prototype / Deck / Template /
#      Design system / Image / Video / Audio)
#    - Find your skill in the picker
#    - Click it, paste the example_prompt
#    - Watch the artifact stream into .od/artifacts/<run-id>/
#    - Verify preview iframe renders correctly
#    - Verify export (PPTX / PDF) works if the mode supports it
```

If the picker doesn't show your skill, check the daemon stderr — the most common cause is a YAML syntax error in frontmatter. The daemon logs the parse error with the offending line.

You don't need any agent CLI on your `PATH` to develop a skill — the daemon falls back to the **Anthropic API · BYOK** path, which is the fastest dev loop anyway. Set your key in Settings once and reuse across runs.

---

## 5. The merge bar — checklist before you open the PR

We hold skill PRs to a higher bar than feature PRs because skills are the user-facing surface. Every item below is something a reviewer will check, so save the round-trip and check it yourself first.

### Content

- [ ] **`example.html` is hand-built.** Opens straight from disk, looks like something a designer would actually deliver. No lorem ipsum, no `<svg><rect/></svg>` placeholder hero. If you can't build the example yourself, the skill probably isn't ready.
- [ ] **No AI slop in the example.** No purple gradients, no generic emoji icons (📊 💡 🚀), no rounded card with a left-border accent, no Inter as a *display* face, no invented stats ("10× faster", "users save 4 hours/week"). Read the **Anti-AI-slop machinery** section of the README for the full list.
- [ ] **Honest placeholders.** When the agent doesn't have a real number, the skill body should instruct it to write `—` or a labelled grey block, not fabricate one.
- [ ] **`references/checklist.md` exists** with at least P0 gates (the rules the agent has to pass before emitting `<artifact>`). Lift the format from [`skills/guizang-ppt/references/checklist.md`](../skills/guizang-ppt/references/checklist.md) or [`skills/web-prototype/references/checklist.md`](../skills/web-prototype/references/checklist.md).
- [ ] **`example_prompt` actually works.** Run it locally end-to-end before submitting. If you wouldn't paste this prompt in front of a stranger to demo the skill, rewrite it.
- [ ] **Triggers are concrete.** "design something cool" is not a trigger. "investor pitch deck", "saas landing page", "约会应用" are.

### Shape

- [ ] **Single self-contained folder + discoverable English display copy.** Everything the skill needs lives under `skills/<your-skill>/`. The folder's `SKILL.md` must include the English display fields consumed by the picker — see "i18n coverage" below. No edits to `apps/daemon/`, `packages/`, `tools/`, etc. in the same PR.
- [ ] **No CDN imports** beyond what other skills already use. If you need a new font CDN, GSAP, three.js, etc., raise it in your PR description.
- [ ] **No images larger than ~250 KB.** If your example genuinely needs a hero photo, run it through an optimizer first. No raw PNG screenshots.
- [ ] **No fonts you didn't license.** System font stack is always safe; Google Fonts and Adobe Fonts free tier are also safe; anything else needs a license file in `references/`.
- [ ] **Slug is ASCII, kebab-case.** `your-skill-name`, not `YourSkillName` or `your_skill_name` or `你的技能`.

### i18n coverage (every skill, not just featured)

The `e2e/tests/localized-content.test.ts` test enforces that every directory under `skills/` with a `SKILL.md` is discoverable and displayable for de / ru / fr. Locales use translated copy when present and otherwise derive the runtime fallback from the English source fields in `SKILL.md`.

For a non-featured skill, the cheap path is to keep the source metadata complete:

- [ ] **Ensure `SKILL.md` has complete English display copy**: title/name, description, example prompt, and any picker metadata required by the skill schema. The localized runtime uses these fields as the fallback display path.
- [ ] **Use optional localized display fields when useful**: `en_name` / `zh_name`, `en_description` / `zh_description`, and `od.example_prompt_i18n.<locale>`. Keep `description` and `od.example_prompt` in English because those are the fallback fields for every locale without localized copy.
- [ ] **Run `pnpm --filter @open-design/web test` and `pnpm --filter @open-design/e2e test tests/localized-content.test.ts`** locally before pushing. These suites catch undisplayable discovered resources and verify localized fallback behavior.

### Featured skills (optional path)

If you set `od.featured: 1`, also:

- [ ] **Add a screenshot** at `docs/screenshots/skills/<skill>.png`. PNG, ~1024×640 retina, captured from the real `example.html` at zoomed-out browser scale.
- [ ] **Optionally add full localized display copy** in `content.ts` (DE), `content.fr.ts` (FR), `content.ru.ts` (RU) — title, summary, scenario tag. The featured row in the picker uses this copy when present; the default fallback path renders English everywhere.

### Forking

If you fork an existing skill (e.g. start from `dating-web` and remix into `recruiting-web`), keep the original LICENSE and authorship in `references/` and call it out in the PR description.

---

## 6. PR description template

Copy-paste this into your PR body and fill it in. Reviewers spend 80% of their first pass checking this template.

```markdown
## Skill: <name>

**Mode:** prototype | deck | template | design-system | image | video | audio
**Platform:** desktop | mobile
**Surface:** one sentence on what artifact this produces

## What it produces
- Brief description of the artifact shape (sections, layout, expected content density)
- Link to the `example.html` rendered output (if you've put it on a gist or pages)

## Triggers
List the trigger phrases. Pick ones you'd actually expect a user to type.

## Why this isn't covered by an existing skill
Search `skills/` first. Name the closest 2 and explain in one sentence each why
they don't cover this case. If you can't, fold into the existing skill instead.

## Validation
- [ ] Ran `pnpm tools-dev run web` and verified the skill appears in the picker
- [ ] Sent the `example_prompt` end-to-end and confirmed the artifact rendered
- [ ] Verified export works (PPTX / PDF / etc.) if the mode supports it
- [ ] Ran `pnpm typecheck`
- [ ] Verified `SKILL.md` has complete English display copy for localized fallback — **required for every skill**
- [ ] Ran `pnpm --filter @open-design/web test` and `pnpm --filter @open-design/e2e test tests/localized-content.test.ts`; localized-content coverage is green

## Screenshot
(Required if `od.featured` is set. Otherwise nice-to-have.)

## Forked from
(Only if applicable. Name the source skill and the LICENSE you preserved.)
```

---

## 7. Common reasons we close skill PRs

So you don't waste a week. Each pattern below has been the close reason on a recent PR — saving the next person from running into the same wall.

- **Sponsor / promo / brand-campaign content.** A skill named "Phantom Motion V8.0 Engine" with a `sponsor-qrcode.png` in `assets/` and marketing copy in the README — that's an ad, not a contribution. We close on sight.
- **Vendor API integration packaged as a skill.** Payment provider integration, marketplace API, vendor SDK wrappers — even when the workflow is real, this is a feature, not a skill. Open it as a daemon PR with proper API contract changes in `packages/contracts`.
- **Duplicate of an existing skill with marginal differentiation.** "Add Trading Terminal X" when "Trading Terminal Y" already exists is a fork-or-fold-in decision, not a new skill PR. Be explicit about the differentiator in the description.
- **Wider repo edits in the same PR.** A skill PR that also bumps `package.json`, modifies `types.ts`, regenerates locale files, or touches `apps/daemon/` is two PRs at minimum. Skill PRs land fast because they're small — keep them small.
- **Stale rebase artefacts.** If your `types.ts` grows by 1000+ lines while you're just adding Turkish, that's a rebase gone wrong, not an i18n addition. Reset the file from main and only touch what you intentionally changed.
- **Lorem ipsum in `example.html`.** The example is the marketing material for the skill. If it has placeholder text, it tells reviewers the skill isn't ready.
- **AI-slop visuals.** Purple-to-pink gradients, hero with three colored squiggles, `Inter` at 64px in a card, `border-l-4 border-violet-500` accent — the README's anti-slop list exists for a reason. We bounce on first pass.
- **Triggers that won't fire.** "creative project", "modern design", "beautiful page" don't disambiguate; they fire for everything. Triggers should be specific enough that the planner knows when to *not* pick your skill.

---

## 8. References

### Skills to imitate

Pick the closest one to your idea and read its `SKILL.md` body before writing your own.

- **Visual showcase, single-screen prototype:** [`skills/dating-web/`](../skills/dating-web/), [`skills/digital-eguide/`](../skills/digital-eguide/)
- **Multi-frame mobile flow:** [`skills/mobile-onboarding/`](../skills/mobile-onboarding/), [`skills/gamified-app/`](../skills/gamified-app/)
- **Document / template (no design system required):** [`skills/pm-spec/`](../skills/pm-spec/), [`skills/weekly-update/`](../skills/weekly-update/)
- **Deck mode:** [`skills/guizang-ppt/`](../skills/guizang-ppt/) (bundled verbatim from [op7418/guizang-ppt-skill](https://github.com/op7418/guizang-ppt-skill)) and [`skills/simple-deck/`](../skills/simple-deck/)
- **Media skills (image / video / audio):** [`skills/image-poster/`](../skills/image-poster/), [`skills/video-shortform/`](../skills/video-shortform/), [`skills/audio-jingle/`](../skills/audio-jingle/)

### Spec & supporting docs

- [`skills-protocol.md`](skills-protocol.md) — full frontmatter grammar, discovery & precedence rules, mode semantics, craft references, testing primitives
- [`architecture.md`](architecture.md) — daemon ↔ web ↔ skill registry data flow
- [`modes.md`](modes.md) — what Prototype / Deck / Template / Design system actually mean to the runtime
- [`../CONTRIBUTING.md`](../CONTRIBUTING.md) — code style, commit conventions, "what we don't accept" for the broader project

### Upstream

- [Claude Code `SKILL.md` convention](https://docs.anthropic.com/en/docs/claude-code/skills) — the base format
- [`VoltAgent/awesome-design-md`](https://github.com/VoltAgent/awesome-design-md) — upstream registry for product design systems (most `design-systems/` PRs belong here, not here)
- [Anti-AI-slop checklist](../README.md) — section in the main README; lift the rules into your `references/checklist.md`

---

## License

By contributing a skill, you agree your contribution is licensed under the [Apache-2.0 License](../LICENSE) of this repository, with the exception of files inside [`skills/guizang-ppt/`](../skills/guizang-ppt/), which retain their original MIT license and authorship attribution to [op7418](https://github.com/op7418).
