Skip to content

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.