{"slug": "your-skill-s-frontmatter-has-a-typo-nothing-will-ever-tell-you", "title": "Your Skill's Frontmatter Has a Typo. Nothing Will Ever Tell You", "summary": "A developer released frontmatter-guard, a zero-dependency Python CLI that semantically lints Claude Code skill and plugin frontmatter, catching unknown keys and misspelled hook events that the official `claude plugin validate` command silently ignores. The tool flags errors such as an unrecognized `effort` key or a `PreToolUs` hook typo with file:line locations and concrete fix suggestions, and offers CI-ready exit codes plus JSON output.", "body_md": "A few days ago I shipped a Claude Code command with this frontmatter:\n\n```\n---\nname: deploy-helper\ndescription: \"Helps with deployments\"\neffort: high\n---\n```\n\n`effort: high` was supposed to keep the model from going overboard. I ran\n\n`claude plugin validate` before shipping. It passed. Green. Ship it.\n\nExcept `effort` is not a Claude Code frontmatter key. It's a Codex concept. My\n\nsetting silently fell back to the session default, and the official validator\n\n— the thing whose entire job is to validate — had nothing to say about it.\n\nThis is the failure mode that bothers me most: not the loud crash, but the\n\nsilent no-op. A misspelled `PreToolUs` hook never fires. A `licence` key never\n\nlicenses anything. You read your own config, it looks right, and it does\n\nnothing. Forever.\n\nSo I built `frontmatter-guard`: a semantic linter for skill/plugin frontmatter\n\nthat knows the real key vocabulary and the real hook event list, and fails\n\nloudly when you stray from it.\n\n```\npip install frontmatter-guard\nfrontmatter-guard check .claude/ --strict\ncommands/deploy.md:4: error [unknown-key] Unknown key 'effort'.\n    fix: Remove the key -- unknown keys are silently ignored, so it currently does nothing.\ncommands/deploy.md:7: error [unknown-hook-event] Unknown hook event 'PreToolUs'. Did you mean 'PreToolUse'?\n    fix: Rename 'PreToolUs' to 'PreToolUse'. Unknown hook events never fire.\n```\n\n`effort`/` licence`/` descripton` catcher.`PreToolUse`, `PostToolUse`, `SessionStart`, `SessionEnd`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `Notification`, `PreCompact`). A wrong event name means your hook never runs, which is exactly the kind of thing you want CI to scream about.`name`/` description` absent or empty.`version` that isn't semver-ish.`hooks:` as a string, `name:` as a number, that sort of thing.\nEvery finding carries `file:line`, the rule name, and a concrete fix suggestion. Exit codes are CI-ready: `1` on any error (or any warning under `--strict`), `0` when clean, `2` on usage errors. `--format json` for machines.\n\nBecause they answer different questions. `claude plugin validate` asks \"will this plugin load?\" — compatibility and structure. frontmatter-guard asks \"does everything you wrote actually do something?\" — semantic strictness. Unknown keys sail through the official check silently; they fail here. They're complementary, not competing.\n\nThere's also damson/skill-lint, a CI action doing structural checks on skills. frontmatter-guard is a local stdlib-only CLI doing semantic checks — same command works in pre-commit for instant feedback and in CI for enforcement.\n\nZero dependencies, Python 3.9+. The YAML parsing is a hand-rolled subset parser in the standard library — block maps and sequences, inline flow collections, literal blocks, quoted scalars. That sounds risky, but the design decision is deliberate: frontmatter is a small, boring corner of YAML, and a dependency-free parser means the tool installs in one second and runs anywhere, including locked-down CI runners. Anything outside the subset produces a `parse-warning` and the lint continues — a linter that crashes on weird input is worse than useless.\n\nIt scans both morphologies: `*.md` frontmatter behind `---` fences (skills, commands) and `plugin.json` files, with the same rule set applied to both.\n\n```\npip install frontmatter-guard\nfrontmatter-guard check . --strict\n```\n\nGitHub: [https://github.com/hahahahahahahahah6/frontmatter-guard](https://github.com/hahahahahahahahah6/frontmatter-guard)\n\nPyPI: [https://pypi.org/project/frontmatter-guard/](https://pypi.org/project/frontmatter-guard/)\n\nIf it catches a typo that would have silently shipped, that was the whole point.", "url": "https://wpnews.pro/news/your-skill-s-frontmatter-has-a-typo-nothing-will-ever-tell-you", "canonical_source": "https://dev.to/haoli/your-skills-frontmatter-has-a-typo-nothing-will-ever-tell-you-18b5", "published_at": "2026-10-11 09:28:41+00:00", "updated_at": "2026-10-11 09:51:47.505275+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "ai-agents"], "entities": ["frontmatter-guard", "Claude Code", "Codex", "PyPI", "GitHub", "damson/skill-lint"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/your-skill-s-frontmatter-has-a-typo-nothing-will-ever-tell-you", "markdown": "https://wpnews.pro/news/your-skill-s-frontmatter-has-a-typo-nothing-will-ever-tell-you.md", "text": "https://wpnews.pro/news/your-skill-s-frontmatter-has-a-typo-nothing-will-ever-tell-you.txt", "jsonld": "https://wpnews.pro/news/your-skill-s-frontmatter-has-a-typo-nothing-will-ever-tell-you.jsonld"}}