Skip to main content

Authoring a Skill Pack

A skill pack is a plain git repository. Overdeck reads it, trusts one commit, and mounts the skills it finds into Overdeck-managed Claude Code and Codex launches. This guide covers what makes a repository readable as a pack, so you can author one and publish it for others to add with pan skills pack add. For the operator-facing feature — the quickstart, the pack catalog, and the trust model — see Skill Packs. For every config key, see the Skill settings reference.

What Overdeck reads

Overdeck mounts skill directories only. Nothing else in your repository reaches the agent: see What is not applied for the full list (hooks, MCP servers, commands, agents, context injection, git hooks, and more).

Two layouts

A pack is either a plain repository or a Claude Code plugin. Overdeck detects which one yours is; detection never fails.

Plain

Overdeck scans skills/ (or KnownPack.skillsRoot for a known pack), up to four directory levels deep — the deepest skill it can find is skills/a/b/c/d/SKILL.md. A skill is a directory with a regular-file SKILL.md. The walk does not descend into a skill once it finds one, so a skill nested inside another skill is never found. Symlinked directories are skipped, and directories are walked in sorted order.

Claude Code plugin

A regular file at .claude-plugin/plugin.json makes the repository a claude-plugin pack. Its skills field controls what gets scanned:
  • Absent, or any other type — scan skills/, with the same four-level rule as a plain repository.
  • A string — scan from that path instead, with the same rule.
  • An array — each string names one skill directory, with no scanning at all. An entry that is not a string, does not exist, or escapes the plugin root is dropped silently. An entry with no SKILL.md is dropped too.
Paths resolve against the plugin root. Absolute paths, ., .., and paths that start ../ are rejected. An invalid plugin.json makes the whole pack fail to read, with the error invalid .claude-plugin/plugin.json: …; the plain adapter ignores a bad plugin.json instead, because it never reads one. The mounted plugin is named after the pack id you chose at pan skills pack add, never after plugin.json’s own name field.

Detection order

Overdeck picks the adapter in this order: the --adapter flag or a registry value you set explicitly, then the pack’s KnownPack default (if it is a known pack), then detection — claude-plugin when .claude-plugin/plugin.json exists as a regular file, otherwise plain. Detection never fails; an empty repository is a valid, empty plain pack.

Skill directories and names

A skill’s SKILL.md carries YAML frontmatter, read between two --- lines starting on line 1. Overdeck reads only name and description from it, and keeps only the first line of description. Missing or unparseable frontmatter still keeps the skill, under its directory name with an empty description. The skill’s name is the frontmatter name:, or the leaf directory name when name: is absent. The name must match ^[a-z0-9][a-z0-9-]*$.
A name that fails that pattern is dropped silently — Overdeck does not fall back to the directory name. name: My Skill in my-skill/SKILL.md drops the skill entirely. A duplicate name is also dropped silently (the first one in sorted order wins). Neither case shows up in the pan skills pack add preview, so check your names before you publish.
Remember the depth rule from the two layouts above: four directory levels under the scan root, and no skill nested inside another skill.

License

Overdeck looks for a license in this order: the plugin.json license string, as written, when one is set; otherwise the first non-blank line of LICENSE at the plugin root, or of LICENSE.md when LICENSE is absent or blank. It checks that line for one of four case-sensitive tokens — MIT, Apache, BSD, GPL — and stores only the matching token. With no match (including an unmatched LICENSE whose first line names no token), the pan skills pack add preview shows License unknown — it does not fall through to LICENSE.md in that case. LICENSE.txt and COPYING are not read. Recommend either a license string in plugin.json, or a LICENSE whose first line contains one of the four tokens.

What is copied and what is not

Every symlink, at any depth, is left out of the mount — the mount’s copy filter skips anything lstat reports as a symbolic link. Every other file is copied as is, including hidden files, with no size limit. Scripts inside a skill directory are copied with it, so the agent can still run them; Overdeck itself never runs them (except for a runtime pack like gstack, which is not a layout you can get from a plain repository or a plugin — see What is not applied for the full table of things that do not reach the agent: hooks, MCP servers, commands, agents, context injection, git hooks, executables, and project-mutating skills).

What a custom pack cannot declare

A custom pack (any pack id not in Overdeck’s KNOWN_PACKS) can set only its adapter. Everything else is fixed:
  • No opt-in skills. Every skill in a custom pack turns on with the pack’s toggle. Opt-in skills — ones the pack toggle never turns on by itself — are a KNOWN_PACKS-only option.
  • Plugin at the repo root. A custom pack’s plugin root is always the repository root.
  • Scan root is always skills/. A custom pack cannot point the plain scan at a different directory.
  • --ref is required. A custom pack needs both a url and --ref; a known pack can default one or both.
If you want opt-in skills, a non-root plugin, or a different scan root for your pack, it needs a KNOWN_PACKS entry in Overdeck’s own source — see the developer guide, docs/SKILL-PACKS.md, for how that works. A pack registered under a known id inherits that id’s options even when you pass a different url.

Harness coverage

Minimal example pack

Plain layout:
SKILL.md:
Registering and using it:
The same pack as a Claude Code plugin instead: add .claude-plugin/plugin.json at the repo root with { "name": "my-pack", "license": "MIT", "skills": "skills" }, and keep the same skills/hello-world/SKILL.md.

Check before you publish

Run pan skills pack add yourself and read the preview before telling anyone else to add your pack. The Skills count must equal the number of skill directories you actually have, and License must not read unknown. Both silently drop skills (an invalid or duplicate name) or miss a license (a token the matcher does not recognize) without an error, so the preview is the only check you get.