Quality linter for AI agent skill files — readability, clarity, and graph integrity checks
Project description
skillscan-lint
Quality linter for AI agent skill files. Catches weasel words, ambiguous instructions, missing metadata, and skill graph problems (cycles, dangling references, broken file links) before they reach production.
Works with skills from skills.sh, ClawHub, and any SKILL.md-based skill package.
Install
pip install skillscan-lint
Or run without installing:
docker run --rm -v "$PWD:/work" kurtpayne/skillscan-lint scan /work/skills/
Quick Start
# Lint a single skill file
skillscan-lint scan SKILL.md
# Lint an entire skills directory (including graph checks)
skillscan-lint scan ./skills/ --graph
# Output as JSON for CI integration
skillscan-lint scan ./skills/ --format json
# List all rules
skillscan-lint rules
Example output
SKILL.md
QL-004 WARNING Weasel intensifier "basically" weakens the instruction.
QL-009 ERROR Description too short (8 words). Minimum is 10.
QL-015 WARNING TODO marker found — remove before publishing.
GR-002 ERROR Skill "data-fetcher" references "parser" which does not exist.
GR-006 WARNING Broken file reference: "references/auth.md" does not exist.
2 errors, 3 warnings
Rules
skillscan-lint ships with two rule families:
Quality rules (QL-*)
| Rule | Severity | What it catches |
|---|---|---|
| QL-001 | WARNING | Passive voice in instructions |
| QL-004 | WARNING | Weasel intensifiers (basically, very, essentially) |
| QL-008 | WARNING | Vague verbs (handle, manage, deal with) |
| QL-009 | ERROR | Description too short (< 10 words) |
| QL-010 | WARNING | Description too long (> 150 words) |
| QL-011 | ERROR | Missing description field |
| QL-012 | ERROR | Missing name field |
| QL-013 | WARNING | Name not in snake_case |
| QL-014 | WARNING | Missing version field |
| QL-015 | WARNING | TODO/FIXME marker in body |
| QL-016 | WARNING | Superlatives (best, perfect, ultimate) |
| QL-017 | WARNING | Nominalisations (optimization → optimize) |
| QL-018 | WARNING | Redundant phrases (end result, past history) |
| QL-019 | WARNING | Buzzwords (leverage, synergy, paradigm) |
| QL-020 | WARNING | Hedging language (might, could possibly) |
| QL-021 | ERROR | Sentence too long (> 35 words) |
| QL-022 | WARNING | Instruction body too long (> 500 lines) |
| QL-023 | WARNING | Missing ## Usage or ## Overview section |
| QL-024 | WARNING | Missing ## Examples section |
Run skillscan-lint rules for the full list with descriptions.
Graph rules (GR-*, requires --graph)
Graph rules analyze the invocation graph across a skills directory — which skills call which, what files they reference, and whether the dependency structure is sound.
| Rule | Severity | What it catches |
|---|---|---|
| GR-001 | ERROR | Cycle in skill invocation graph (A → B → A) |
| GR-002 | ERROR | Dangling reference (skill invokes a skill that doesn't exist) |
| GR-003 | WARNING | Orphan entry-point (invokes others but is never invoked — add entry_point: true if intentional) |
| GR-004 | WARNING | Hub skill (unusually high in-degree — single point of failure) |
| GR-005 | WARNING | Undocumented dependency (referenced skill has no ## Usage or ## Overview section) |
| GR-006 | WARNING | Broken intra-skill file reference (Markdown link points to a .md file that doesn't exist) |
Graph edges are resolved from three sources, in order of reliability:
- Front-matter keys —
invoke:,calls:,depends_on:,uses:,requires:with skill names or file paths - Markdown links to
SKILL.mdfiles —[skill name](../other-skill/SKILL.md) - Path resolution — front-matter values containing
/or ending in.mdare resolved relative to the skill file
Reference docs, README files, and other supporting .md files within a skill package are not treated as graph nodes — they are checked by GR-006 for broken links instead.
CI Integration
GitHub Actions
- name: Lint skills
run: |
pip install skillscan-lint
skillscan-lint scan ./skills/ --graph --format json > report.json
Pre-commit hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/kurtpayne/skillscan-lint
rev: v0.3.0
hooks:
- id: skillscan-lint
args: [--graph]
Docker in CI
- name: Lint skills
run: |
docker run --rm -v "${{ github.workspace }}:/work" \
kurtpayne/skillscan-lint scan /work/skills/ --graph --format json \
> skillscan-lint-report.json
Skill Graph Example
Given a skills directory:
skills/
data-fetcher/SKILL.md (invoke: parser)
parser/SKILL.md (invoke: data-fetcher) ← cycle!
formatter/SKILL.md (invoke: missing-skill) ← dangling ref
Running skillscan-lint scan ./skills/ --graph will report:
GR-001 ERROR Cycle detected: data-fetcher → parser → data-fetcher
GR-002 ERROR Skill "formatter" references "missing-skill" which does not exist.
Related
- skillscan-security — Security scanner for AI skills: prompt injection, IOC matching, malware detection, ML-based classifier
- skills.sh — Community registry of AI agent skills
- ClawHub — MCP skill marketplace
License
Apache-2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file skillscan_lint-1.0.0.tar.gz.
File metadata
- Download URL: skillscan_lint-1.0.0.tar.gz
- Upload date:
- Size: 47.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62135791721c0876aaab03aa13405f8452e7e55931ab98d6a5f3d7b8f3344f65
|
|
| MD5 |
48c2972a5adc1eb7f2292974a272e11e
|
|
| BLAKE2b-256 |
ea172c7ef87bd4255f52bf1989cc6fb944a0a152ac70afde43992e02bf1fc288
|
Provenance
The following attestation bundles were made for skillscan_lint-1.0.0.tar.gz:
Publisher:
release-pypi.yml on kurtpayne/skillscan-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skillscan_lint-1.0.0.tar.gz -
Subject digest:
62135791721c0876aaab03aa13405f8452e7e55931ab98d6a5f3d7b8f3344f65 - Sigstore transparency entry: 1341669603
- Sigstore integration time:
-
Permalink:
kurtpayne/skillscan-lint@436ea4c6db858f4b07c462787fcbd9b867de6d02 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/kurtpayne
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@436ea4c6db858f4b07c462787fcbd9b867de6d02 -
Trigger Event:
push
-
Statement type:
File details
Details for the file skillscan_lint-1.0.0-py3-none-any.whl.
File metadata
- Download URL: skillscan_lint-1.0.0-py3-none-any.whl
- Upload date:
- Size: 38.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba88abd088e95eced8b18509188fc989953f9d176ab76884cd48d0c43736fc98
|
|
| MD5 |
6d6c44cf40ebe56c0b08c0f66182487e
|
|
| BLAKE2b-256 |
eb0566b99eeafd4f494483a22c88c339e101e03397e81bcd458718949602d5b4
|
Provenance
The following attestation bundles were made for skillscan_lint-1.0.0-py3-none-any.whl:
Publisher:
release-pypi.yml on kurtpayne/skillscan-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skillscan_lint-1.0.0-py3-none-any.whl -
Subject digest:
ba88abd088e95eced8b18509188fc989953f9d176ab76884cd48d0c43736fc98 - Sigstore transparency entry: 1341669607
- Sigstore integration time:
-
Permalink:
kurtpayne/skillscan-lint@436ea4c6db858f4b07c462787fcbd9b867de6d02 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/kurtpayne
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@436ea4c6db858f4b07c462787fcbd9b867de6d02 -
Trigger Event:
push
-
Statement type: