skill-health
Scan the skill library for dangling references and build a per-skill health table.
Files9
View on GitHubSKILL.mdskills/skill-health/SKILL.md
GitHubskill-health — Skill-Library Structural Debt Scan
Detect skill technical debt — library-level defects that do not break a
single skill in isolation but degrade the library over time (SkillOps, Pu/Song/
Zhao 2026, arXiv:2605.13716). This skill
owns the one debt pattern no other harness skill checks: missing artifacts
— a SKILL.md that references a scripts/ module, a bash script, an agent, or
a sibling skill that no longer exists on disk (it silently dangles after a
rename or delete).
This is a structural property — decidable from the literal text plus a
filesystem exists() check — so deterministic code owns it. The semantic, risk,
and validation dimensions are delegated, never re-implemented here.
Only dangling is a defect claim
The scanner (--help lists flags, --json the keys) reports three lists:
dangling(exit code 1) — a written path that does not exist. This is the only defect claim.unresolved_names— skill / command names written without a path that match no file under the skills root. An enumeration handed to judgment, not a defect: the name may be a CLI builtin, a plugin command, or a skill in another repo, and the slash-command namespace is not enumerable from disk._KNOWN_NON_FILE_SKILLSin the scanner is noise reduction, not an authority.external— skills whose directory is a symlink out of the skills root. Also an enumeration, not a defect: it answers "would a fix applied here survive?" — it would not, since the owning tree overwrites it on upgrade. Route these upstream (issue / PR). References inside them are still scanned.
Boundary (read first — this skill does not overlap its neighbours)
SkillOps frames library health as four dimensions. This harness already covers
three; skill-health adds the missing structural one and federates the rest:
| Dimension (SkillOps) | Owner | skill-health does |
|---|---|---|
| Compatibility (refs resolve) | skill-health ← here |
the deterministic scan (Phase 2) |
| Utility (frequency, value) | skill-stocktake (holistic Keep/Improve/Retire/Merge) |
read its signal; flag over-specialized (LLM) |
| Risk (security, side effects) | /claude-security plugin |
delegate; prompt to run if stale |
| Validation (tests/consistency) | skill-comply / skill-creator |
note which skills lack a validator |
Phase 1 — Inventory
Enumerate skill definitions with Glob (~/.claude/skills/*/SKILL.md; plus
{cwd}/.claude/skills/*/SKILL.md if the project has local skills). State up
front how many skills will be scanned.
Phase 2 — Compatibility scan (the deterministic core)
Run the structural reference scanner. It extracts every explicit local
reference from each SKILL.md (python -m scripts.X, bash …/x.sh,
~/.claude/agents/X.md, and Markdown links to local files / sibling skills) and
reports those whose target does not exist:
Paths below start at
${CLAUDE_SKILL_DIR}, the directory holding this SKILL.md; an agent that does not substitute the variable reads it as that directory.
uv run --frozen --project "${CLAUDE_SKILL_DIR}" \
python -m scripts.scan_refs ~/.claude/skills --json
--external-urls on the same scanner lists the external URLs the corpus names,
fence- and placeholder-aware, and exits 0 — it is the input to this skill's other
probe, python -m scripts.url_liveness --urls-from - (URL reachability as
live / dead / blocked / skip; blocked is not dead). Reachability is
checked by whoever owns the audit, once and serially, never inside parallel batch
agents — skill-stocktake Phase 1 is the caller.
Exit code is the code-owned gate: 0 clean, 1 dangling references found, 2
scan root missing. The scanner is conservative by design — it skips template
placeholders (<your-repo>/x.sh), illustrative example links ([](url)), and
--directory-overridden commands, because a false "missing artifact" is worse
than a missed one. Run --help for flags; omit --json for a human report.
The same run also reports external skills (external in JSON). Surface these
before any repair is proposed — they set where a fix can go — and name the
owner of each (handling per external above).
Present each dangling reference with: skill, ref type, the raw reference, the
resolved path, and the line. Do not auto-fix — a dangling reference may mean the
artifact was deleted (remove the reference) or renamed (repoint it) or,
for a ../-escaping link, that the skill was authored in a repo and vendored
into the harness (a portability issue per the skill-creator skill's
references/portability.md). The
repair is a human judgment; surface the fact, let the user decide.
Phase 3 — Federate the other three dimensions (read, don't re-implement)
For the scanned skills, surface the existing signals so the report is a single health view — labelling each value's source:
Utility — do not read
~/.claude/metrics/skill-usage.jsonlby hand. The corrections that decide whether its numbers mean anything live inusage_stats(in the siblingskill-stocktakeskill, when it is installed next to this one); run the script once per window:uv run --frozen --project "${CLAUDE_SKILL_DIR}/../skill-stocktake" python -m scripts.usage_stats --days 90Where a skill is rarely or never triggered, judge [LLM] whether the cause is over-specialized scope (a trigger so narrow it never fires) versus simply new —
last_usedis not clipped to the window, so "used 100 days ago" is distinguishable from "never". Ifmeasurableis false, orspan_shorter_than_windowis true, render usage asunmeasured— never0.Enumerate residency-fold candidates: skills with nonzero
context(the/skill-doctorresidency column;-means the description is already off the listing and there is nothing left to fold) and zerodeliberateuse in the window. Their description resides in every system prompt without having driven a single selection — the description is the fold candidate. Hand the list to the author with the decision axis attached (default: add an explicit one-line reference from a related skill / rule, thendisable-model-invocation: true— even a one-line description still resides in the system prompt as an unaudited instruction; a one-line description is the fallback only when no reference site exists); exclude skills whose window is shorter than their age would need (span_shorter_than_window, or added days ago). This is an enumeration, not a verdict — folding is the author's call.Size — enumerate SKILL.md bodies over the 500-line limit (
wc -l ~/.claude/skills/*/SKILL.md | sort -rn; limit per skill-creator §3, sourced from Anthropic's official best practices as of 2026-08-29 — re-check on doc updates). Growth after creation is the case the creation-time gate cannot catch. The verdict on whether an oversized body is bloat or load-bearing belongs to skill-stocktake's Hygiene question, not here.Risk — point to the latest
CLAUDE-SECURITY-*/CLAUDE-SECURITY-RESULTS.mdif present; if none/stale, recommend running/claude-security. Do not re-scan for vulnerabilities here.Validation — note (missing validators debt) which scanned skills have no
skill-complyspec and no record of passingskill-creator's gates — the fresh-context draft verdict (its §4) and, for a skill with verifiable output, the native ablation screenclaude plugin eval <skill dir> --ablation with-without --runs 3(its §5) — i.e. no way to verify their behaviour. Judge [LLM] trigger↔body consistency only where it is in doubt.
Not measured: skill success rate (did using the skill improve the outcome, not just fire?). It needs counterfactuals the harness does not capture; left as a known gap rather than a fabricated number.
Phase 4 — Report & ledger
Render a skill × dimension table: Skill | Compatibility | Utility | Risk | Validation, where Compatibility shows the dangling-reference count (the only
hard number), the others show the federated signal or unmeasured. List each
dangling reference with its repair options (remove / repoint / portability).
Persist the scan to ${CLAUDE_SKILL_DIR}/results.json (the --json
output) with a real UTC scanned_at (date -u +%Y-%m-%dT%H:%M:%SZ), so the
next run can diff. Update it inline with Read/Write.
Related
- Your publish step (the author's harness uses
harness-sync) — use it to publish this skill to a public repo.