pan-skill-creator · git:20260928.1625199 · 2026-09-28 · sha256 f4972df8707b05b5

pan-skill-creator git:20260928.1625199A

Immutable. This exact content never changes and is served at /api/v1/blob/f4972df8707b05b5.

---
name: pan-skill-creator
description: Guide for Overdeck developers on creating and distributing skills
triggers:
  - create a skill
  - add a skill
  - new skill for overdeck
  - make skill available to all users
---

# Overdeck Skill Development Guide

**This skill is for Overdeck developers only.** It teaches how to create skills that are distributed with Overdeck to all users.

## Where Skill Sources Live

All skill sources are under `sync-sources/` in the overdeck repo (PAN-1201). Nothing at the repo root is a skill source.

| Location | Purpose | Distributed to users |
|----------|---------|---------------------|
| `sync-sources/skills/<name>/` | Public skills for all users | Yes |
| `sync-sources/dev-skills/<name>/` | Developer-only skills | Only in dev mode (see below) |
| `<projectRoot>/.pan/skills/<name>/` | One project's own skills | To that project's workspaces |

A skill is a directory that contains `SKILL.md`. It can also contain `references/` and `scripts/`.

## How `pan sync` Distributes Skills

`pan sync` **copies** files. It does not create symlinks.

1. **Source tree.** `pan sync` normally reads the package's own `sync-sources/`. When the `pan` binary runs from a `pan reload` generation (`~/.overdeck/deployments/dashboard/.pan-reload-generation-{a,b}`), it reads `sync-sources/` in the checkout that generation was built from instead (`repoRoot` in `~/.overdeck/active-dashboard-bundle.json`, usually the primary checkout). If that checkout is behind `origin/main`, `pan sync` distributes stale skills and prints a staleness warning.
2. **Cache.** Every directory in `sync-sources/skills/` is copied into `~/.overdeck/skills/`. In dev mode, `sync-sources/dev-skills/` is copied after it, so a dev skill replaces a public skill with the same name.
3. **Harness targets.** From the cache, skills are copied into `~/.claude/skills/` (Claude Code) and `~/.agents/skills/` (Codex, Pi). A hash manifest (`~/.claude/.overdeck-manifest.json`) tracks each copied file. A file that you edited at the target is reported as user-modified and is not overwritten.
4. **Workspaces.** New workspaces get the cached skills copied into `<workspace>/.claude/skills/`.

**Dev mode** means the package root has a `src/` directory: a source checkout or a `pan reload` generation. An npm install has no `src/`, so it never gets dev skills.

Changes reach **new** sessions only. A running session keeps the skill list it started with.

## Creating a New Skill

### Step 1: Pick the location and name

- Public skill → `sync-sources/skills/<name>/`
- Dev-only skill → `sync-sources/dev-skills/<name>/`

Follow [`docs/SKILLS-CONVENTION.md`](../../../docs/SKILLS-CONVENTION.md) for the name:

- A skill that wraps one CLI verb is named `pan-<verb>`, exactly the verb.
- A workflow, reference, or topical skill still takes the `pan-` prefix.

### Step 2: Write `SKILL.md`

```markdown
---
name: pan-my-skill
description: "Workflow: one line that says what it does and when to use it"
triggers:
  - phrase a user would say
  - another phrase
allowed-tools:
  - Bash
  - Read
---

# pan-my-skill

Instructions the agent follows...
```

The `description` is what the agent sees in its skill list. Say when to use the skill, not only what it is.

### Step 3: Update the skill-set fixture (public skills only)

`tests/fixtures/synced-skills.txt` lists every directory in `sync-sources/skills/`, sorted, one per line. `tests/fixtures/synced-skills.test.ts` fails if a skill is added, renamed, or removed without updating it. Regenerate it:

```bash
UPDATE_FIXTURES=1 npx vitest run tests/fixtures/synced-skills.test.ts
```

### Step 4: Lint

```bash
bash scripts/lint-skills.sh   # also runs inside `npm run lint`
```

The linter checks every `pan <verb>` flag and subcommand that a CLI-wrapper skill mentions against the built CLI (`dist/cli/index.js`), so build first. `npm run lint:no-state-layer` also scans every `.md` under `sync-sources/` for references to the deleted state layer.

A skill that only instructs the agent does not add a CLI verb, so it does not touch the composer slash-command manifest (`packages/contracts/src/composer-commands.generated.ts`). That manifest is generated from CLI commands.

### Step 5: Try it locally

```bash
pan sync
pan skills | grep pan-my-skill
```

If your commit is not yet in the checkout that `pan sync` reads (see step 1 of the distribution flow), copy the directory into `~/.claude/skills/` by hand to try it.

### Step 6: Commit and push

```bash
git add sync-sources/skills/pan-my-skill tests/fixtures/synced-skills.txt
git commit -m "feat(skills): add pan-my-skill for <purpose>"
git push
```

Use the `pan-commit` skill for commit-message rules. An unpushed skill exists only on your machine.

## Updating an Existing Skill

1. Edit `sync-sources/skills/<name>/SKILL.md` (or `sync-sources/dev-skills/`).
2. Run the linter.
3. Commit and push.
4. Run `pan sync`. It overwrites the managed copies unless a user edited them.

## Skill Best Practices

1. **Clear triggers.** Include the phrases a user actually says.
2. **Actionable instructions.** Tell the agent exactly what to run, with examples.
3. **One purpose per skill.** Link related skills in a "See also" section instead of merging them.
4. **Record the traps.** When a workflow has a failure mode you hit once, write the check that avoids it into the skill.