Skip to main content

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]
  • check runs every enabled rule except the release checks.
  • stamp counts skills, references, workflows and domains, then writes those counts and the version from version.json into the files you list. --check writes nothing and exits 1 when anything is stale.
  • release checks the tree the plugin directory will read (plugin_root or a ship list), then runs check.
  • rules lists 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:

  1. The Agent Skills specification (SPEC-01 to SPEC-10). You can't switch these off.
  2. 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.
  3. Per-repo policy (POL, LINK, and your own X- 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 in shared_dirs, and that a cited skill appears in metadata.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 likewise WORKFLOW_COUNT, REFERENCE_COUNT, DOMAIN_COUNT and VERSION. SKILL_COUNT:<domain> counts one domain and REFERENCE_COUNT:<skill> counts one skill's references.
  • json maps a JSON Pointer to a key and replaces only that value, leaving the file's formatting alone.
  • patterns replace 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 optional s group becomes s unless 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)

Source distribution for skill-linter 0.2.0
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)

Table of built distributions (wheels) for skill-linter 0.2.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page