agentskill-unreferenced-files¶
Every bundled skill file should be referenced from SKILL.md, directly or transitively
| Severity | warning (auto) |
| Autofix | - |
| Since | v0.15.0 |
| Repo Types | agentskills, dot-claude, marketplace, single-plugin |
| Category | agentskills.io |
Why¶
Every file bundled in a skill directory should be reachable from SKILL.md. An unreferenced file is dead weight in the skill package — it ships to every consumer, inflates installs, and rots silently because nothing points at it.
It is also a security smell: research on malicious skills found that most hide their behavior in bundled files SKILL.md never mentions (shadow functionality — OWASP Agentic Skills Top 10, AST01). A script that no instruction references has no legitimate reason to be in the package, and reviewers routinely skip files the skill text never asks an agent to open or run.
What counts as a reference¶
A file is referenced when its path or filename is mentioned in
SKILL.md or transitively in any local file reachable from SKILL.md
(SKILL.md → references/a.md → references/b.md counts). Every
referenced file — scripts and data files included, not just markdown —
becomes a reference source: a data file read by a script that SKILL.md
documents (SKILL.md → check.py → allowed-repos.txt) is covered,
because the whole chain is reviewable. Non-markdown sources contribute
plain-text mentions only (link syntax is resolved only in markdown);
binary files and files over 1 MiB never become sources. A skill-root
README.md also counts as a reference root — a file documented in the
skill's README is neither dead weight nor hidden from review.
Mentions are detected in markdown links, inline code spans, fenced
code blocks (python scripts/run.py), and plain prose:
- Relative paths count:
scripts/run.py,./scripts/run.py, orimg/logo.pngfrom a file in the same directory. - Matching is case-insensitive: SKILL.md saying
FORMS.mdcovers aforms.mdon disk — such references work on case-insensitive filesystems. - Bare filenames count: a mention of
run.pyanywhere marksscripts/run.pyas referenced. Skills routinely refer to bundled scripts by name alone, so requiring full paths would flag heavily-referenced files. - Directory mentions cover their contents (configurable): "read the
files in
references/" marks everything underreferences/as referenced. Prose and code mentions must be path-ish — a trailing slash (references/), a./prefix (./canvas-fonts), or an interior/(assets/fonts); the slash-less forms only count when the directory actually exists in the skill, and a bare word with no path markers never covers anything. Links may target the bare directory. - Python imports are followed: when a reachable file is a
.pyfile, its imports are resolved within the skill (relative to the skill root and to the importing file's directory, including relative imports), so SKILL.md →scripts/recalc.py→from office.soffice import ...coversscripts/office/soffice.py. Imported modules join the traversal, so files they mention (a schema referenced from a docstring) are covered too.from a.b import ccoversa/b/c.pywhen it exists, otherwise thea.bmodule; package__init__.pyfiles along the path are covered as well. Imports shown inside python-labeled (or unlabeled) fenced code blocks of reachable markdown count the same way: a SKILL.md fence teachingfrom core.gif_builder import GIFBuildercoverscore/gif_builder.py.
Never flagged: SKILL.md itself, README.md, CHANGELOG.md, LICENSE and
NOTICE files (any suffix, e.g. LICENSE-MIT), files under evals/
and tests/ (eval/test scaffolding is consumed by external harnesses
by convention, not referenced from the skill text), test_*.py files
and anything under a testdata/ directory at any depth (bundled
scripts routinely ship self-tests and fixtures), hidden files or
directories, and symlinks (which are also never followed). The
exclude option adds glob patterns on top of these defaults.
Examples¶
Bad:
my-skill/
SKILL.md # only mentions scripts/run.py
scripts/
run.py
cleanup.py # never mentioned anywhere — dead or hidden behavior
Good:
my-skill/
SKILL.md # "Run `python scripts/run.py`, then scripts/cleanup.py"
scripts/
run.py
cleanup.py
How to fix¶
Delete the unreferenced file, or mention it from SKILL.md (or from a
markdown file SKILL.md references) so agents and reviewers know why it
is bundled. If the file is intentionally unlisted supporting data,
either mention its directory (assets/) from SKILL.md or add a glob
to the rule's exclude option:
Configuration¶
| Parameter | Description | Default |
|---|---|---|
directory_mention_covers |
Treat a mention of a directory (e.g. references/, ./canvas-fonts, or assets/fonts when the directory exists) as referencing every file under it |
true |
exclude |
Additional glob patterns (matched against skill-relative paths and bare file names; a leading **/ also matches at the skill root) exempt from dead-file detection; extends the built-in exclusions (SKILL.md, README.md, CHANGELOG.md, LICENSE, NOTICE, evals/, tests/, test_*.py, testdata/, hidden files) |
[] |
Run skillsaw explain agentskill-unreferenced-files to see this documentation and the rule's effective configuration in your terminal.