#!/usr/bin/env bash set -euo pipefail # Enforces agentskills.io's skill-authoring.md guidance: keep SKILL.md within # 500 lines and roughly 5,000 tokens, so the full body doesn't crowd out # conversation history and other active skills once loaded into context. Vale # can't express a whole-file length ceiling (its checks operate on text # patterns, not raw file size), so this is a plain script instead of a Vale # rule. # # Both ceilings are inclusive: a file at exactly MAX_LINES or MAX_WORDS passes, # and only one past it fails. That matches skill-audit/scripts/validate.sh, # which has always used `line_count <= 500` as its pass condition — the two # previously disagreed at exactly 500 lines, so a SKILL.md could pass its own # audit and still be blocked by the commit hook. # # Token counts aren't computed exactly here — word count (`wc -w`) is used as # a proxy. Measured over this repo's 39 in-scope SKILL.md files, characters per # word runs min 5.97 / median 6.79 / mean 6.77 / max 7.22. At the standard # ~4-characters-per-token English approximation that is 1.49 / 1.70 / 1.69 / # 1.81 tokens per word. # # MAX_WORDS=2770 is therefore calibrated to the corpus WORST case rather than # its median: 2770 words at the densest observed 7.22 chars/word is ~20,000 # characters, or ~5,000 tokens at the 4-characters-per-token approximation. So # what this gate guarantees is "under 5,000 tokens even for the densest prose # the corpus has produced" — the earlier median-calibrated MAX_WORDS=2900 let # such a file sit at exactly the ceiling and still spend ~5,240 tokens. A # median-density file at 2770 words spends ~4,700 tokens, so typical prose # gives up ~130 words of headroom to close that gap. The largest SKILL.md in # the repo is 2,489 words, so no current file is affected. # # It is a one-sided proxy in the useful direction — nothing under the word # ceiling is wildly over the token ceiling — but it is not exact BPE # tokenization and does not replace one. Re-measure the corpus before treating # any of these numbers as still current. # These constants are intentionally duplicated in # skill-audit/scripts/validate.sh (Python) rather than shared from one file: # this script is a standalone bash pre-commit hook, that one is an in-skill # Python validator invoked in a different context (same rationale as # vale-wrap.sh's per-plugin duplication — see its own header comment). # tests/test-skill-size-check.sh asserts both files agree on these values, so # drift between them fails CI rather than silently diverging. MAX_LINES=500 MAX_WORDS=2770 FAIL=0 for f in "$@"; do [[ -f "$f" ]] || continue # Single awk pass computes both line count and word count, avoiding a # second read of the file. NR counts the final line even without a # trailing newline, matching Python's splitlines() semantics (used by # skill-audit/scripts/validate.sh for its own line count) — `wc -l` # undercounts by 1 in that case. Word count uses awk's default # whitespace-splitting NF, matching `wc -w` semantics. read -r lines words <<< "$(awk '{w += NF} END{print NR, w+0}' "$f")" if (( lines > MAX_LINES )); then echo "ERROR: $f has $lines lines, exceeding the $MAX_LINES-line ceiling (agentskills.io skill-authoring.md)" >&2 FAIL=1 fi if (( words > MAX_WORDS )); then echo "ERROR: $f has $words words (proxy for tokens), exceeding the $MAX_WORDS-word ceiling (~5,000 tokens, agentskills.io skill-authoring.md)" >&2 FAIL=1 fi done exit $FAIL