skilllint
Static analysis linter for AI agent plugins, skills, and agents — for Claude Code, Cursor, Codex, and any agentskills.io-compatible platform.
What it does
skilllint validates the structure and content of AI agent files: plugins, skills, agents, and commands. It catches broken references, missing frontmatter, oversized skills, invalid hook configurations, and more — before they cause silent failures at runtime.
$ skilllint check plugins/my-plugin
plugins/my-plugin/skills/my-skill/SKILL.md
SK006 Token count 14823 exceeds recommended limit of 8192
plugins/my-plugin/agents/my-agent.md
NR001 Namespace reference 'other-plugin:some-skill' — plugin directory not found
2 errors in 2 files
Screenshots
Validation output with errors and warnings
All available rules
Rule detail
Installation
pip install skilllint
Or with uv:
uv add skilllint # add to a project
uv tool install skilllint # install as a global tool
Requires Python 3.11–3.14.
Quick start
# Validate a plugin directory
skilllint check plugins/my-plugin
# Validate a single skill file
skilllint check plugins/my-plugin/skills/my-skill/SKILL.md
# Validate everything and show a summary
skilllint check --show-summary plugins/
# Auto-fix issues where possible
skilllint check --fix plugins/my-plugin
# Count tokens in any markdown file
skilllint check --tokens-only .claude/CLAUDE.md
Exit codes: 0 = all checks passed · 1 = validation errors · 2 = usage error
GitHub Action
Use bitflight-devops/skilllint as a GitHub Action to validate skills, plugins, and agents in any repository:
- uses: bitflight-devops/skilllint@v1.7.0
with:
paths: "plugins/"
platform: "claude-code"
show-summary: "true"
Full input reference
| Input | Description | Default |
|---|---|---|
paths |
Space-separated paths to validate | . |
platform |
Platform adapter: claude-code, cursor, codex |
(all) |
fix |
Auto-fix issues where possible | false |
check-only |
Validate only, do not auto-fix | false |
verbose |
Show detailed output including info messages | false |
no-color |
Disable color output | true |
tokens-only |
Output only the integer token count | false |
show-progress |
Show per-file PASSED/FAILED status | false |
show-summary |
Show summary panel at the end | true |
filter |
Glob pattern to restrict files within a directory | (none) |
filter-type |
File type filter: skills, agents, commands |
(all) |
version |
skilllint version to install (latest or 1.2.3) |
latest |
python-version |
Python version to use | 3.12 |
Outputs
| Output | Description |
|---|---|
result |
passed when exit code is 0; failed for any non-zero exit code |
exit-code |
Raw exit code: 0 = pass · 1 = errors · 2 = usage error |
Example: fail the CI on validation errors
name: Validate skills
on: [push, pull_request]
jobs:
skilllint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint skills and plugins
uses: bitflight-devops/skilllint@v1.7.0
with:
paths: "plugins/ .claude/"
platform: "claude-code"
show-summary: "true"
verbose: "false"
Example: report results without blocking
- name: Lint skills and plugins
id: lint
uses: bitflight-devops/skilllint@v1.7.0
with:
paths: "plugins/"
continue-on-error: true
- name: Print result
run: echo "skilllint result=${{ steps.lint.outputs.result }}"
Pre-commit hook
Add to .pre-commit-config.yaml:
repos:
- repo: https://github.com/bitflight-devops/skilllint
rev: v1.7.0
hooks:
- id: skilllint
Contributor quality checks (local)
Run this sequence before pushing:
# Auto-fix hooks first (whitespace, pypfmt, oxlint/oxfmt, ruff --fix, etc.)
uv run prek run --all-files
uv run pytest
Platform support
skilllint ships with adapters for three platforms and supports third-party adapters via Python entry points:
| Platform | Adapter ID | Bundled |
|---|---|---|
| Claude Code | claude-code |
✓ |
| Cursor | cursor |
✓ |
| OpenAI Codex | codex |
✓ |
| OpenCode, Gemini, and others | — | via entry points |
Restrict validation to one platform:
skilllint check --platform claude-code plugins/my-plugin
What gets validated
| Code | Category | Description |
|---|---|---|
| FM001–FM007, FM009–FM010 | Frontmatter | Required fields, valid values, schema compliance |
| SK004–SK009 | Skill | Description quality, token limits, complexity, internal links |
| AS001, AS006, AS008–AS009 | AgentSkills | SKILL.md conformance with the AgentSkills open standard |
| LK001 | Links | Broken internal markdown link detection |
| PD001–PD003 | Progressive disclosure | Directory structure for references/, examples/, scripts/ |
| PL001–PL006 | Plugin | Structure, manifest correctness, marketplace layout, subprocess safety |
| HK001–HK005 | Hook | Script existence, configuration validity |
| NR001–NR002 | Namespace refs | Cross-plugin skill/agent/command references |
| SL001 | Symlinks | Symlink hygiene within plugin directory |
| TC001 | Token count | Token count reporting and threshold enforcement |
| PR001–PR005 | Plugin registration | Capability registration completeness and correctness in plugin.json |
| PA001 | Plugin agent | Plugin-packaged agents: hooks / mcpServers / permissionMode unsupported per Anthropic (ignored at load; cite sub-agents doc) |
| AG001–AG003 | Agent frontmatter | Claude Code agents/*.md (any scope): unresolvable tool wildcards, MCP server casing/references, and non-string skills values or members discarded by the file loader; scalar strings and string-only lists are accepted |
| CU001–CU002 | Cursor | Cursor .mdc frontmatter required fields and enum values |
| CX001–CX002 | Codex | Codex AGENTS.md content non-empty and header presence |
CLI reference
Usage: skilllint [OPTIONS] COMMAND [ARGS]...
Commands:
check Validate Claude Code plugins, skills, agents, and commands.
docs Fetch, query, and verify cached vendor documentation.
rule Show documentation for a validation rule.
rules List all available validation rules.
Options:
--help Show this message and exit.
check
Usage: skilllint check [OPTIONS] [PATHS]...
Arguments:
paths Paths to validate
Options:
--check Validate only, don't auto-fix
--fix Auto-fix issues where possible
--verbose, -v Show detailed output
--no-color Disable color
--tokens-only Output token count only
--show-progress Show per-file status
--show-summary Show summary panel
--filter TEXT Glob pattern to match files within a directory
--filter-type TEXT Filter type (skills | agents | commands)
--platform TEXT Platform adapter
--include-gitignore Scan files excluded by .gitignore (default: skip them)
--record PATH Record terminal output to SVG or HTML file
--help Show this message and exit
rules
Usage: skilllint rules [OPTIONS]
Options:
--platform, -p TEXT Filter rules by platform
--category, -c TEXT Filter rules by category
--severity, -s TEXT Filter rules by severity (error, warning, info)
--record PATH Record terminal output to SVG or HTML file
--help Show this message and exit
rule
Usage: skilllint rule [OPTIONS] RULE_ID
Arguments:
rule_id Rule identifier (e.g., "SK004", "FM002", "AS001") [required]
Options:
--record PATH Record terminal output to SVG or HTML file
--help Show this message and exit
docs
Cache and query vendor documentation pages for offline use.
Usage: skilllint docs [OPTIONS] COMMAND [ARGS]...
Commands:
fetch Fetch a documentation page or return a cached copy.
fetch-authorities Fetch documentation for all normalized rule authority URLs.
latest Find the most recent cached file for a page name.
sections Print a table of sections in a cached markdown file.
section Extract the text of a named section from a cached markdown file.
verify Verify a cached file against its .meta.json integrity sidecar.
docs fetch
Usage: skilllint docs fetch [OPTIONS] URL
Arguments:
url Documentation URL to fetch or serve from cache.
Cache Options:
--ttl FLOAT Cache time-to-live in hours before a refresh is attempted. [default: 4.0]
--force Skip the freshness check and always attempt a network fetch.
Options:
--help Show this message and exit.
Prints the cached file path to stdout. Status messages go to stderr. Exits 1 when no cache exists and the network is unavailable.
docs fetch-authorities
Usage: skilllint docs fetch-authorities [OPTIONS]
Cache Options:
--ttl FLOAT Cache time-to-live in hours before a refresh is attempted. [default: 4.0]
--force Skip the freshness check and always attempt a network fetch.
Options:
--help Show this message and exit.
Fetches every unique authority URL declared by the rule registry after normalizing origin-relative references against each rule authority origin. Prints one cached file path per successful fetch. Exits 1 if any URL cannot be fetched and no stale cache is available.
docs latest
Usage: skilllint docs latest [OPTIONS] PAGE_NAME
Arguments:
page_name Filesystem-safe page name (e.g. 'claude-code--settings').
Options:
--help Show this message and exit.
Prints the file path to stdout. Exits 1 when no cached file exists for that page name.
docs sections
Usage: skilllint docs sections [OPTIONS] FILE_PATH
Arguments:
file_path Path to the cached markdown file to index.
Options:
--help Show this message and exit.
Prints a table of headings with their line ranges to stdout.
docs section
Usage: skilllint docs section [OPTIONS] FILE_PATH HEADING
Arguments:
file_path Path to the cached markdown file.
heading Heading text or markdown anchor slug to locate.
Options:
--help Show this message and exit.
Prints the full text of the matching section to stdout. Exits 1 when the heading is not found.
Heading matching is case-insensitive and accepts two forms:
- Heading text:
"Hook input and output" - Markdown anchor slug:
"hook-input-and-output"
Leading # characters are stripped before comparison.
docs verify
Usage: skilllint docs verify [OPTIONS] FILE_PATH
Arguments:
file_path Path to the cached markdown file to verify.
Options:
--help Show this message and exit.
Exits 0 when the file is intact. Exits 1 when the file has been modified or when no sidecar exists.
All four command names are aliases for the same tool:
skilllint # primary
agentlint # alias
pluginlint # alias
skillint # alias
Vendor documentation cache
skilllint docs provides an offline-first cache for vendor documentation pages. Pages are
fetched once and stored locally; subsequent calls within the TTL window are served from
disk without a network request. When the TTL has expired but the network is unavailable,
the stale copy is served automatically.
Cached files are written to .claude/vendor/sources/ with filenames in the format
{page-name}-{YYYY-MM-DD-HHMM}.md. Each file is accompanied by a .meta.json integrity
sidecar that records the SHA-256 digest, byte count, source URL, and fetch timestamp.
# Cache a documentation page (default TTL: 4 hours)
skilllint docs fetch https://docs.anthropic.com/en/docs/claude-code/settings.md
# Pre-fetch all normalized rule authority URLs
skilllint docs fetch-authorities
# Force a network refresh regardless of TTL
skilllint docs fetch https://docs.anthropic.com/en/docs/claude-code/settings.md --force
# Find the most recently cached copy of a page
skilllint docs latest claude-code--settings
# List all sections with line ranges
skilllint docs sections .claude/vendor/sources/claude-code--settings-2025-01-01-1200.md
# Extract a section by heading text or markdown anchor slug
skilllint docs section .claude/vendor/sources/claude-code--settings-2025-01-01-1200.md "Hook input and output"
skilllint docs section .claude/vendor/sources/claude-code--settings-2025-01-01-1200.md "hook-input-and-output"
# Verify a cached file against its sidecar
skilllint docs verify .claude/vendor/sources/claude-code--settings-2025-01-01-1200.md
Section extraction uses marko for AST-based
markdown parsing, so # characters inside fenced code blocks are never mistaken for
headings.
If skilllint is not installed, use uvx to run it without a permanent install:
uvx skilllint docs fetch https://docs.anthropic.com/en/docs/claude-code/settings.md
scripts/fetch_doc_source.py is a PEP 723 standalone script that exposes the same
commands via uv run --script. It is intended for contributors working directly on
the skilllint source tree (where [tool.uv.sources] points the dependency at the
local package), not for end users.
Suppressing warnings
Use a .skilllint.json file to suppress specific rule codes for a directory tree. Place the file at any level — skilllint walks up from each scanned file and uses the nearest config it finds.
{
"ignore": {
"": ["AS008"],
"skills/legacy": ["FM007", "SK006"]
}
}
Key format:
| Key | Scope |
|---|---|
"" (empty string) |
All files under this .skilllint.json |
"skills/legacy" |
Files whose path (relative to the config file) starts with that prefix |
Plugin-level suppression (inside a .claude-plugin/ plugin):
Place validator.json inside .claude-plugin/:
{
"ignore": {
"": ["PA001"],
"agents/generated": ["FM004", "FM007"]
}
}
The same key format applies. Plugin-level config takes priority over a .skilllint.json in a parent directory.
See docs/ignore-config.md for the full reference.
Third-party adapters
Register a custom platform adapter via Python entry points in your pyproject.toml:
[project.entry-points."skilllint.adapters"]
my-platform = "my_package.adapter:MyPlatformAdapter"
Your adapter must implement the AdapterProtocol interface from skilllint.adapters.protocol.
Links
License
MIT
Release files for skilllint 1.19.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 | |
|---|---|---|---|
| skilllint-1.19.0.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| skilllint-1.19.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.2 MB
Release files / skilllint-1.19.0.tar.gz
| Download URL | skilllint-1.19.0.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a8b9a8757763e92d6594ec33ceec35eb7f6178d48f453b6d64f9d2240f95b47d
|
|
BLAKE2b-256 checksum How to use checksums |
fbaa13ebef933a03204855ae975030b7bd540dbc513af7efda76928d803cb07a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / skilllint-1.19.0-py3-none-any.whl
| Download URL | skilllint-1.19.0-py3-none-any.whl |
|---|---|
| Size | 994.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0c7936945298c1a0cd00886381f6d276b15fe4bae27a895ab3a9b907eee36f6a
|
|
BLAKE2b-256 checksum How to use checksums |
812d0b772b92c5999dbc1ac162e32feb940e2aa5505f25369afb202573bc5827
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|