>_devkit
catalogue
Stablev1.1.0

devkit-skill-authoring

by @chempa

Use when writing, validating, publishing, or versioning a skill for a devkit registry - the SKILL.md format, the frontmatter rules, and the init/validate/publish loop.

devkit-skill-authoring

How to write a skill that a devkit registry will accept and that Claude will actually load. Everything here is the behaviour of @devkit/core, which is the one validator the CLI, the server, CI, and the in-browser editor all share - so what passes locally passes on publish, for the same reasons.

When to use this

  • Writing a new skill, or fixing one the registry rejected
  • Deciding what belongs in SKILL.md versus a references/ file
  • Publishing, versioning, yanking, or forking
  • Editing a skill that is already published, and needing the version bump right
  • Working against more than one registry and needing the right one

The whole loop

devkit init my-skill        # scaffold a folder that already validates
$EDITOR my-skill/SKILL.md
devkit validate my-skill    # the registry's own checks, locally
devkit publish my-skill     # upload it

If you do not have the CLI yet, it comes from the registry itself and needs no token - it is a client and holds no skills:

curl -sSL https://your-registry.example.com/install/cli | sh
devkit login https://your-registry.example.com   # prompts for a token

Mint the token at /manage/settings. Publishing needs the publish scope.

How we publish here

The CLI is the only way a skill reaches the registry. Never commit a skill folder to the devkit-site repository and never push it to GitLab. That repo is the site and the validator; it is not the publishing path, and its older "add a folder, open an MR" instructions in CONTRIBUTING.md describe a flow we no longer use. A skill that lands by git is a skill the registry does not have, with no version, no digest, and no way for anyone to install it.

The house setup:

ThingWhere
Registryhttps://devkit.chempa.dev, account @chempa
Skill sources~/skills/<slug>/, one folder per skill
Installed copies~/.claude/skills/<slug>/, written by devkit install
cd ~/skills
devkit init my-skill
$EDITOR my-skill/SKILL.md
devkit validate my-skill
devkit publish my-skill        # this is the push
devkit install my-skill        # put it on this machine

Run devkit whoami before publishing anything you would not want in the wrong place. It prints the active registry, the token, and why that one was chosen.

Changing a skill that is already published

Versions are immutable, so an edit is always a new version. The sequence:

cd ~/skills/my-skill                        # or clone it back, below
$EDITOR SKILL.md                            # edit, then bump version: and updated:
devkit validate .
devkit publish .
devkit update my-skill                      # refresh this machine's copy

Bumping is a judgement about the reader, not about the diff size:

ChangeBump
Typos, a clearer sentence, a fixed linkpatch
New guidance, a new references/ file, a widened descriptionminor
Guidance that reverses what the previous version told people to domajor

No local source folder? Every published file is in the install, so the installed copy is the source:

cp -r ~/.claude/skills/my-skill ~/skills/my-skill
rm -f ~/skills/my-skill/.devkit.json        # the install marker, never published

Forget the bump and publish returns a 409 for the version that already exists. That is the check working: fix the number and publish again.

A skill is one folder

my-skill/
  SKILL.md          ← required, at the root
  references/       ← optional, ships with the skill
    checklist.md

There is no manifest. The YAML frontmatter at the top of SKILL.md is the metadata, and the body below it is what Claude loads.

The folder name is the slug, and it must equal the frontmatter name. Slugs are lowercase, hyphen-separated, 3-64 characters, starting and ending alphanumeric. all and cli are refused - the registry already serves those install URLs.

Frontmatter

---
name: code-reviewer
description: Use when reviewing a diff for correctness, dead code, and missing tests.
version: 1.0.0
author: your-handle
license: MIT
tags: [ai, claude, code-review]
status: beta
created: 2026-08-02
updated: 2026-08-02
---
KeyRequiredRule
nameyesMust equal the folder name
descriptionyes10-200 characters, one line
versionyesSemVer; must not already exist in the registry
authoryesLetters, digits, _ . -; max 40
licenseyesAn SPDX identifier (MIT, Apache-2.0, …)
tagsyesInline list, 1-8 entries
statusyesstable, beta, or deprecated
created / updatednoYYYY-MM-DD
featurednoSet by the registry, not by you

The frontmatter parser is deliberately not YAML. It handles key: scalar, inline lists ([a, b, c]), quoted scalars, and # comments - and it throws on indentation, nested maps, block sequences (- item), and multi-line scalars (|, >) rather than mis-parsing them quietly. Keep every value on one line.

For the exact rules, every error message, and what each one means, read references/frontmatter.md.

The description is the field that matters

It is the only part of the skill Claude sees before deciding whether to load it. It has to carry the trigger conditions by itself. Write when to use this, not what this is:

# good - names the situations that should match
description: Use when reviewing a diff, preparing a PR, or asked to check code for bugs.

# bad - accurate, and gives nothing to match against
description: A code review helper.

A skill with a vague description is not a broken skill; it is a skill that never gets loaded, which is worse, because nothing reports it.

The body

Everything below the closing --- is the skill. It is what Claude loads and what the catalogue renders, so it has two readers and must work for both. It must be non-empty.

Keep SKILL.md focused. Anything long - checklists, tables, worked examples - goes in a sibling file that the body points at on demand:

For the full colour formula, read `references/palette.md`.

Every file in the folder is published and lands in the installer's ~/.claude/skills/<slug>/, so those siblings are on their disk afterwards.

For how to write a body Claude actually follows, read references/writing-well.md.

What gets published

Everything in the folder, except housekeeping: .DS_Store, Thumbs.db, .git, .gitignore, .gitkeep, .keep, node_modules, .devkit.json, and editor leftovers matching ~, .#, or .tmp.. Those are your repository's business and nobody downstream's.

There are no secrets in a skill folder. Everything in it is published, and on a public registry, world-readable.

Limits, all enforced at publish:

LimitValueWhy
Files per skill64A skill is documentation, not a package
One file512 KBSame
Whole skill2 MBBounds the upload and the unpacked size
Path length100 bytes incl. <slug>/ustar's name field

Paths may not be absolute, contain .. or . segments, use backslashes, or contain NUL. The install writes straight into ~/.claude/skills/<slug>/, so this is a security boundary rather than tidiness.

Versions are immutable

Publishing a version that already exists is a 409. To change a published skill, bump version: and publish again.

An install pins a content digest, verified on download, so "the skill I installed" and "the skill in the registry" can be compared - and a bad version is rolled back by installing the previous one rather than by hoping somebody reconstructs it.

latest is the highest release. A pre-release (2.0.0-rc.1) is installable by exact version but never becomes the default.

Publishing, registries, tokens, yanking, forking, and visibility are covered in references/publishing.md.

Before you publish

  • Folder name and frontmatter name are identical
  • description names trigger conditions, 10-200 chars, one line
  • Every frontmatter value is on a single line
  • version is SemVer and has not been published before
  • The body is non-empty and reads on its own
  • No secrets anywhere in the folder
  • updated: is today, if this is a new version of an existing skill
  • You are publishing with the CLI, not committing the folder anywhere
devkit validate my-skill

Then try the real install, somewhere disposable:

export CLAUDE_HOME=$(mktemp -d)
devkit install my-skill
find "$CLAUDE_HOME"

CLAUDE_HOME redirects the install target, so you can inspect exactly what an installer gets without touching your real ~/.claude.

When something is rejected

references/troubleshooting.md maps each error the validator, the CLI, and the server produce to its cause and its fix.