The skill never fired. Nothing errored. The typo was one character.
Skillcheck lints the CLAUDE.md and SKILL.md files that steer coding agents, catching the silent failures (typo'd name, shadowed slug, dead trigger) that no compiler exists to catch.
You write a skill for your coding agent, wire up the frontmatter, describe when it should fire,
and then it just doesn't. No error. No warning. The agent behaves as if the file isn't there,
and an hour later you find it: nmae: pdf-export instead of name:. One transposed pair of
letters, and the skill never registered. Nothing told you, because nothing was watching.
That's the whole problem Skillcheck exists to
solve. The files that steer agents, CLAUDE.md, AGENTS.md, and skill definitions with YAML
frontmatter, have enormous say over which tools fire and which skills activate. And they have no
compiler, no schema, and no linter. A required field that's missing, a name that collides with
another skill, a description with no trigger language: every one of these fails silently at
runtime. Skillcheck is the feedback loop the format never had.
Why "is it valid YAML" misses everything
The tempting move is to grab a YAML library, parse the frontmatter, and call it validated. That
catches roughly none of the bugs that actually cost you a debugging session. name: ~ is
perfectly valid YAML; it just resolves to null, so the skill has no name and never loads.
nmae: is valid YAML too, a real key with a real value, and the field the runtime needs is
simply absent. Two files both declaring name: research are each individually fine, and
together they shadow one another so only one wins, non-deterministically. Valid YAML, broken
skill, every time.
So Skillcheck models the semantic failure modes instead. Each is a small, independent rule
that produces a precise diagnostic: a line number, a severity, and a concrete fix hint. Missing
required field. Unterminated --- block. A name that isn't kebab-case or doesn't match its
directory. A description too long for the host to keep (measured across multi-line | and >
scalars, because that's where the length hides). A /skill-name mentioned in prose that
resolves to nothing. And the highest-value one, name collisions, which is inherently cross-file:
you cannot see it by reading either file alone, so the engine models per-file and per-project
rules as first-class citizens.
The parser that keeps line numbers
Here's the design decision I like most. Skillcheck does not use a YAML library. It ships a
purpose-built frontmatter parser instead, for two reasons. Skill frontmatter is a tiny subset
of YAML, so a general parser is mostly dead weight. More importantly, general parsers throw
away the per-key line numbers, and those line numbers are the product. A diagnostic that says
"description too long" is annoying; one that says 6:14 description-too-long and drops you on
the exact line is a fix. The focused parser exists to preserve position information that a real
YAML library discards on the way to building its tree.
That parser has zero third-party dependencies, and so does the rest of the engine. It's plain
ES-module JavaScript, which is what makes the next part real: the same source files run in
Node and in the browser with no bundler. The npx skillcheck .claude/skills you drop into CI
and the paste-a-file web validator call one identical engine. They can't disagree about what's
valid, because the demo you see on the web page is literally the check that runs in your
pipeline.
The honest edges
Skillcheck suggests fixes; it doesn't rewrite your files yet. It handles the common frontmatter shapes, not the full YAML spec (folded scalars, anchors, deep nesting are out of scope for v1), which is a deliberate bet that skill frontmatter stays simple. And the rule set is internal for now, no plugin API, though rules are data in a registry, so adding one is writing a single file and appending to an array. The roadmap is mostly "more rules," and the architecture was built to make that cheap.
Try it
Paste a SKILL.md or CLAUDE.md into the hosted validator
and you get an annotated, line-referenced report in the tab, no install. Or point the CLI at a
real directory: npx skillcheck .claude/skills groups results by file and exits non-zero on
errors, so it drops straight into a GitHub Actions step or a pre-commit hook. Want to see it
bite? Feed it a skill with name: ~, or two skills claiming the same slug, and watch it name
the exact thing that would have cost you an hour.
This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.
Loading comments…