skill-linter
skill-linter checks a repo of Agent Skills before you commit or release it, and keeps the counts and version numbers in its docs current. One [tool.skill-linter] table in pyproject.toml configures it, so a new skill repo gets the same checks as the others without copying validate-skills.py, validate-markdown.py and update-docs.py into it.
It needs Python 3.11 or later. PyYAML is the only dependency, used for workflow YAML.
uvx skill-linter check # run without installing
pipx install skill-linter # or install it once
Status: early (0.x). Rule names and config keys may still change between minor versions.
Commands
skill-linter check [--only GROUP[,GROUP]] [--skill NAME] [--format text|json] [--root PATH]
skill-linter stamp [--check] [--dry-run] [--root PATH]
skill-linter release [--format text|json] [--root PATH]
skill-linter rules [--format text|json]
checkruns every enabled rule except the release checks.stampcounts skills, references, workflows and domains, then writes those counts and the version fromversion.jsoninto the files you list.--checkwrites nothing and exits 1 when anything is stale.releasechecks the tree the plugin directory will read (plugin_rootor ashiplist), then runscheck.ruleslists every rule with its group, layer and severity after your overrides.
skill-linter --version prints the installed version. Exit codes: 0 when there are no errors, 1 when there are errors or stale stamps, 2 for a usage or config error. python -m skill_linter runs the same CLI.
Rules
Rules come in three layers:
- The Agent Skills specification (
SPEC-01toSPEC-10). You can't switch these off. - Conventions shared across the skill repos: frontmatter metadata, the description rule, Markdown lint, workflow definitions, hidden conventions such as the docs backlink and title safety, and the changelog format, and the plugin-directory release checks (
CONV,DESC,MD,WF,H,CL,P). Each one can be switched off or lowered to a warning. - Per-repo policy (
POL,LINK, and your ownX-rules). A policy rule runs only once you give it a value.
Every rule has an ID and a name, such as POL-03 and section-order. Output shows both, and the config accepts either. Run skill-linter rules for the full list.
Cross-skill references
A skill can cite a file in a sibling skill or in a shared folder, written from the skill root: `../vue-expert/references/components.md` or `../_shared/markup-convention.md`. The spec wants every reference to stay inside the skill folder, so SPEC-10 (reference-depth) leaves these paths to two other rules:
CONV-15(cross-skill-references) checks that the file exists, that the folder is a skill or is listed inshared_dirs, and that a cited skill appears inmetadata.related-skills(a warning when it doesn't).P17(cross-skill-targets-ship) checks that the cited file ships with the plugin. A skill installed on its own won't find its sibling, so these references only work when the whole plugin is installed.
Configuration
Everything lives under [tool.skill-linter] in the repo's pyproject.toml. Unknown keys and unknown rule IDs are errors, so a typo can't switch a check off without anyone noticing. tests/adoption/ holds complete configs for claude-skills and writing-with-agents.
[tool.skill-linter]
skills_dir = "plugin/skills"
shared_dirs = ["_shared"]
commands_dir = "plugin/commands"
plugin_root = "plugin"
docs_url = "https://example.github.io/my-skills/skills/{domain}/{name}/"
[tool.skill-linter.policy]
workflow_steps = [5, 7]
license = "MIT"
[tool.skill-linter.severity]
plugin-icon = "off" # or P10
[[tool.skill-linter.rules.custom]]
id = "X-lead-support"
target = "body"
pattern = '(?is)\blead\b.*\bsupport\b'
severity = "warning"
message = "Name a lead and a support role"
The changelog rules (CL-01 to CL-05) expect Keep a Changelog headings, the six standard sections in order with ### Contributors last, an [Unreleased] section, and compare links. [tool.skill-linter.changelog] takes path, extra_sections and since, the first version they apply to, so older entries can stay as published.
Custom rules are regular expressions applied to a frontmatter field, a section, the last line, the body or the reference files. Config never runs code.
Stamping
Each [[tool.skill-linter.stamp.files]] entry names a file and how to update it:
[[tool.skill-linter.stamp.files]]
path = "README.md" # .md, .mdx and .html files get marker stamping by default
patterns = [
{ key = "version", regex = 'version-([\d.]+)-blue\.svg' },
{ key = "referenceFileCount.{skill}", regex = '(?m)^\s+(?P<skill>[\w-]+)/\s+\(SKILL\.md \+ (?P<count>\d+) reference(?P<s>s?)\)' },
]
[[tool.skill-linter.stamp.files]]
path = ".claude-plugin/marketplace.json"
json = { "/metadata/version" = "version", "/plugins/0/version" = "version" }
patterns = [{ key = "skillCount", regex = '(\d+) specialized skills' }]
- Markers:
<!-- SKILL_COUNT -->67<!-- /SKILL_COUNT -->, and likewiseWORKFLOW_COUNT,REFERENCE_COUNT,DOMAIN_COUNTandVERSION.SKILL_COUNT:<domain>counts one domain andREFERENCE_COUNT:<skill>counts one skill's references. jsonmaps a JSON Pointer to a key and replaces only that value, leaving the file's formatting alone.patternsreplace the one group in each regex. A pattern that matches nothing is an error, which catches a sentence reworded after its pattern was written.- The key
referenceFileCount.{skill}stamps a count per skill, for places where an HTML comment would show up as text, such as a file tree inside a code block. Its regex names the skill and count groups. The optionalsgroup becomessunless the count is 1, and a line that names no skill is an error.
Development
uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .
The adoption tests export pinned commits of the two skill repos with git archive and skip when those checkouts aren't present. One of them runs each repo's own update-docs.py and skill-linter stamp on the same change and checks that every file comes out identical.
Metadata
Release files for skill-linter 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| skill_linter-0.2.0.tar.gz | 80.2 kB | With provenance | Details for skill_linter-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| skill_linter-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for skill_linter-0.2.0-py3-none-any.whl |
Total release size: 136.3 kB
Release files / skill_linter-0.2.0.tar.gz
| Download URL | skill_linter-0.2.0.tar.gz |
|---|---|
| Size | 80.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6cf053ace814c4db492debab5394472e7a31569e42bcf6b913d5c77c1f26b0c5
|
|
BLAKE2b-256 checksum How to use checksums |
8680299cdc3d4a539a3d352f53b9f8428f441b3d1f546f3f9d1077728cd02aa3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logRelease files / skill_linter-0.2.0-py3-none-any.whl
| Download URL | skill_linter-0.2.0-py3-none-any.whl |
|---|---|
| Size | 56.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
82d8f6d53a3c3b09f09b63545b07ae4a452751fd55c1f1c1aa5349e7f9d86952
|
|
BLAKE2b-256 checksum How to use checksums |
fcd70df05ac0eb0351796097830d4aa12fc826f7c62f409ce0dfb4d48e7df98a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logSecurity for skill-linter 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for skill_linter-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.