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 plugin-directory release checks (CONV, DESC, MD, WF, H, 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"

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.1.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.1.0
File Size Uploaded Provenance Actions
skill_linter-0.1.0.tar.gz 75.0 kB With provenance Details for skill_linter-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for skill-linter 0.1.0
File Interpreter ABI Platform Actions
skill_linter-0.1.0-py3-none-any.whl Python 3 none any Details for skill_linter-0.1.0-py3-none-any.whl

Total release size: 127.8 kB

Release files / skill_linter-0.1.0.tar.gz

Download URL skill_linter-0.1.0.tar.gz
Size 75.0 kB
Tags Source
SHA-256 checksum
How to use checksums
1c95c3fe714b0bec3a30eeb9dd65e851039de09982c9f74683b46410bdebfcfc
BLAKE2b-256 checksum
How to use checksums
dd57f271069a7b5f9cd193ac6bacbc0bdf892fbe68dfa4d3249c7f2de60d8ea5
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.1.0-py3-none-any.whl

Download URL skill_linter-0.1.0-py3-none-any.whl
Size 52.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6e01505278e2e4435cb81d46ff6b73f7190fa127c244fe490e829cd10e313078
BLAKE2b-256 checksum
How to use checksums
0dbf5310f910e0132ad79ffe6459f912051267aee0ea719a9977b22ce1ad87fa
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.1.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.1.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.1.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