Lint The Skills
Question
Great, now I have all these skills to manage and keep straight. How do I do that?
Solution
More skills (and linters)!
Explanation
skill-lint is a skill whose job is checking that every other skill
under .agents/skills/ is shaped correctly - including itself. It
enforces the exact layout every skill in this talk already follows:
.agents/skills/shell-fmt/
├── SKILL.md
├── scripts/
│ └── shfmt-wrapper
├── docs/
│ └── shfmt-wrapper.md
└── tests/
└── shfmt-wrapper/
├── already-formatted/
│ ├── test
│ └── rc
├── needs-reformatting/
│ ├── test
│ └── rc
├── unparseable/
│ ├── test
│ └── rc
├── usage-flag/
│ ├── test
│ └── rc
└── run-tests.sh
Source: .agents/skills/shell-fmt/
Skill Layout
.agents/skills/skill-lint/
├── SKILL.md
├── scripts/
├── tests/
├── docs/
└── resources/
Source: .agents/skills/skill-lint/
SKILL.md must exist and be non-empty - the one file every skill is
required to have, since it is what makes a skill discoverable at all.
Nothing else is allowed to live directly in the skill directory except
these four canonical subdirectories, and all four are themselves
optional: a skill with no scripts of its own, or no shared
resources/ to vendor, simply omits the subdir rather than leaving an
empty placeholder behind.
Paired Tests
.agents/skills/shell-fmt/tests/shfmt-wrapper/
├── already-formatted/
│ ├── test
│ └── rc
├── needs-reformatting/
│ ├── test
│ └── rc
├── unparseable/
│ ├── test
│ └── rc
├── usage-flag/
│ ├── test
│ └── rc
└── run-tests.sh
Source: .agents/skills/shell-fmt/tests/shfmt-wrapper/
Every file under scripts/ needs a matching tests/<script>/
directory with at least two cases inside it - one that expects
success, one that expects failure, so a script is never trusted just
because it ran without crashing. Each case is an executable test
file paired with an rc file naming the exact exit code that test
must return; shfmt-wrapper happens to have four, not two, because
unparseable and usage-flag are each their own distinct failure and
success shape worth covering on their own.
Canonical Copy
.agents/skills/skill-lint/resources/
└── run-tests.sh canonical
.agents/skills/shell-fmt/tests/shfmt-wrapper/
└── run-tests.sh sha256 match
.agents/skills/shell-lint/tests/shellcheck-wrapper/
└── run-tests.sh sha256 match
Source (canonical copy): .agents/skills/skill-lint/resources/run-tests.sh
Every tests/<script>/run-tests.sh across every skill is required to
be byte-identical to the one canonical copy kept at
skill-lint/resources/run-tests.sh - checked by comparing a sha256
hash, never by eye. A test runner that quietly drifted between skills
would mean "pass" stopped meaning the same thing everywhere - hashing
closes that gap mechanically, rather than trusting that nobody ever
hand-edits a copy.
Fixed Headings
SKILL.md
├── # Skill Purpose
└── # Scripts
└── ## shfmt-wrapper - Purpose
├── ### shfmt-wrapper - Running Directly
├── ### shfmt-wrapper - Running via Pre-commit Githook
└── ### shfmt-wrapper - Running via CI
Source: .agents/skills/shell-fmt/SKILL.md
SKILL.md itself has to document each script under four fixed
headings, nested exactly this way - a ## naming the script and its
Purpose, then three ### siblings underneath it for the three ways
that script actually gets run. "How do I run this by hand" is never a
question only the script's own source code can answer, and an agent
documenting a new skill is handed the same fixed shape to fill in
rather than a blank page to improvise on.
None of that is enforced by convention or a code review checklist -
skill-structure-check is a script, with its own test cases, exactly
like every skill it checks. An agent asked to add a new skill is
handed the same rules a human would have to remember, as a command it
can run before claiming the work is done, not a style guide it has to
infer from examples.