Skill maintenance
A standing playbook for keeping a skill library healthy. The point is to never re-derive
the traps or re-hunt for where things live.
Read craft/skill-authoring.md alongside this — that
covers how to write a skill; this covers how to maintain a set of them.
First: know which runtime you are in
Skills that make claims about one runtime — a sandbox, a mount, a host-only tool — without
naming the other case will actively mislead an agent running elsewhere. An agent that
follows sandbox rules on a real host wastes effort staging work it could do directly.
Establish where you are before touching files, and when writing a skill that has runtime
constraints, say which runtime each rule belongs to. scripts/check_scoped.py is the
automated version of this check.
Never edit an installed skill
Installed skills are typically a read-only or app-managed cache. Editing them does
nothing, or worse, appears to work and is silently reverted later.
The loop that actually sticks:
- Edit the skill's source files.
- Package the directory as a
.skill — a zip whose top folder is the skill name and
contains SKILL.md.
- Install from the package.
Keep a source tree for every skill you own. Where none exists, extract the package to
a scratch directory, patch there, rebuild. That is the normal case in most libraries, not
the exception.
The maintenance loop
Inventory. List the skills; separate the ones you own and can edit from stock ones
you can only enable or disable.
Audit each against four lenses:
- Staleness — dead file references, old tool names, dated facts, broken paths.
- Bloat — scratch directories,
__pycache__, temp renders, stray PDFs, a SKILL.md
that has ballooned past ~500 lines.
- Overlap — two skills that trigger on the same request. Tighten one description and
point it at the other (front-door pattern).
- Earns-its-keep — does it actually fire usefully? Use the telemetry, not your
impression (
hooks/log-skill-usage.py), and do not report dormancy until the log
covers the dormancy window.
Decide and say it straight: keep / retire / clean / upgrade.
Make changes in a scratch directory, against real data. A check tool that cries
wolf is worse than none.
Wire skills together where it beats a new skill — a deploy skill that gates on a
content check, for instance. Integration is often the highest-leverage upgrade
available and it is consistently under-considered next to writing something new.
Package and validate:
python scripts/build_skill.py <parent-dir> <skill-name> <out.skill> [expected-file-count]
Checks zip integrity, a single top-level folder matching the skill name, SKILL.md
present, frontmatter parsing, name: exact, an optional file-count guard, no dead
paths in bundled text, and that every .py and .json parses. Exits non-zero on any
failure.
Verify the install, then close the loop. Tell the user exactly what to click and
flag anything still theirs to do.
Quality bar
- Frontmatter:
name plus a pushy description packed with concrete trigger
phrases. Agents under-trigger skills; spell out when to use it, and give every SKIP
a named alternative.
- Lean
SKILL.md (under 500 lines). Depth goes in references/, deterministic work
in scripts/, output assets in assets/.
- Scripts over prose for anything repeatable and checkable — tested on real inputs.
- No scratch in the package. A skill should not ship its author's debris.
- Do not surprise the user: never auto-commit or push to an auto-deploying repo,
never delete files without explicit permission.
Self-modification guard — read before editing THIS skill
This is the skill that edits other skills, so it is the one that can edit itself.
Never patch this skill while running a procedure defined by it. The instructions you
are following are the ones being rewritten; a half-applied edit changes the remaining
steps under you, and the failure is invisible because the run continues against the new
text. If a pass concludes this skill needs changing: finish the pass, report the
finding, then start a fresh run whose only job is that edit.
Patch protocol, for any skill including this one
- Copy the package aside first, with a dated suffix. Before the first edit, not after
the first mistake.
- Patch narrowly. One concern per pass. Do not reflow or reorganise while fixing.
- Re-read the patched file end to end. Not the diff — the file. A diff shows what
changed and hides what the change broke around it.
- Validate with
build_skill.py.
- Auto-revert on failure. Restore the copy from step 1 before diagnosing. Do not
leave a broken package in place while investigating — that is the state an unattended
run or a distracted session picks up.
- Verify the install by file count, never by comparing text.
Four failures this protocol exists to prevent
All four happened, and all four were caught by running a check rather than by reasoning:
name: changed during a rewrite. Install overwrites by frontmatter name, so the
edit silently created a second skill instead of replacing the first. Two versions
then fire on overlapping triggers with no indication of which ran.
- A package went stale against its own installed copy. The package lacked a patch
that both the installed skill and the source tree carried, so "reinstalling from the
package" would have regressed the live skill. Compare package against installed
before treating the package as newer.
- Installing from a bare
SKILL.md silently deleted every bundled script. Eight
skills lost their resources; one lost 101 files including the 37 scripts its own
instructions call. And the obvious check passed — all eight had a byte-perfect
SKILL.md. Count files against the package instead.
- A substring scan produced false alarms. A check for skills making runtime claims
matched
mount inside paramount and blend_amount. Use word boundaries.
Verification requires a fresh session
A running session's skill list is fixed at start. No amount of re-checking inside the
current session proves an install worked.
The scripts
| Script |
What it does |
build_skill.py |
Rebuild and validate a .skill before installing. The gate. |
check_scoped.py |
Flags skills making single-runtime claims without naming the other case. Word-boundary matching. |
scan_skills.py |
Locator, not a health metric. Finds which packages mention which patterns. Tells you where to look, not whether anything is wrong. |
show_hits.py |
Prints the matching lines from scan_skills.py hits, for triage. |
trigger_evals.py |
Deterministic routing evals: positive prompts must rank their skill top-k in a TF-IDF ranking over descriptions, negatives must be won by their owner, and no two descriptions may sit above 0.75 similarity. Cases live in evals/cases.json; add cases whenever a description changes. Run with --root <your skills dir>. |
smell_scan.py |
Ranks skills by patchwork density (dated corrections, EXCEPT WHEN clauses, repeated rules) and prints the matching lines. A density score orders candidates and cannot judge them; read the lines. --self-test proves the patterns still catch real patchwork and ignore topic words. |
check_scoped.py, scan_skills.py and show_hits.py carry pattern lists marked
EDIT THIS FOR YOUR SETUP. They ship with example markers; they are worthless until
they describe your actual retired paths and your actual runtimes.
1---2name: skill-maintenance3description: Audit, clean, upgrade, and repackage your own skills on an ongoing basis. Use whenever the user says "look at my skills", "audit my skills", "are any of my skills stale / bloated / redundant", "upgrade a skill", "clean up a skill", "repackage this skill", "should I retire any skills", or wants to wire skills together. Encodes the packaging contract, the patch protocol, and the failure modes that silently corrupt a skill library. SKIP for building a brand-new skill from scratch (use skill-creator) and for memory-vault work (use project-memory). This skill audits and repackages skills that already exist.4---56# Skill maintenance78A standing playbook for keeping a skill library healthy. The point is to never re-derive9the traps or re-hunt for where things live.1011Read [`craft/skill-authoring.md`](../../craft/skill-authoring.md) alongside this — that12covers how to *write* a skill; this covers how to *maintain* a set of them.1314## First: know which runtime you are in1516Skills that make claims about one runtime — a sandbox, a mount, a host-only tool — without17naming the other case will actively mislead an agent running elsewhere. **An agent that18follows sandbox rules on a real host wastes effort staging work it could do directly.**1920Establish where you are before touching files, and when writing a skill that has runtime21constraints, **say which runtime each rule belongs to.** `scripts/check_scoped.py` is the22automated version of this check.2324## Never edit an installed skill2526Installed skills are typically a **read-only or app-managed cache**. Editing them does27nothing, or worse, appears to work and is silently reverted later.2829The loop that actually sticks:30311. Edit the skill's **source** files.322. Package the directory as a `.skill` — a zip whose top folder is the skill name and33 contains `SKILL.md`.343. Install from the package.3536**Keep a source tree for every skill you own.** Where none exists, extract the package to37a scratch directory, patch there, rebuild. That is the normal case in most libraries, not38the exception.3940## The maintenance loop41421. **Inventory.** List the skills; separate the ones you own and can edit from stock ones43 you can only enable or disable.44452. **Audit each against four lenses:**46 - *Staleness* — dead file references, old tool names, dated facts, broken paths.47 - *Bloat* — scratch directories, `__pycache__`, temp renders, stray PDFs, a `SKILL.md`48 that has ballooned past ~500 lines.49 - *Overlap* — two skills that trigger on the same request. Tighten one description and50 point it at the other (front-door pattern).51 - *Earns-its-keep* — does it actually fire usefully? **Use the telemetry, not your52 impression** (`hooks/log-skill-usage.py`), and do not report dormancy until the log53 covers the dormancy window.54553. **Decide and say it straight:** keep / retire / clean / upgrade.56574. **Make changes in a scratch directory**, against real data. A check tool that cries58 wolf is worse than none.59605. **Wire skills together** where it beats a new skill — a deploy skill that gates on a61 content check, for instance. Integration is often the highest-leverage upgrade62 available and it is consistently under-considered next to writing something new.63646. **Package and validate:**65 ```66 python scripts/build_skill.py <parent-dir> <skill-name> <out.skill> [expected-file-count]67 ```68 Checks zip integrity, a single top-level folder matching the skill name, `SKILL.md`69 present, frontmatter parsing, `name:` exact, an optional file-count guard, no dead70 paths in bundled text, and that every `.py` and `.json` parses. Exits non-zero on any71 failure.72737. **Verify the install, then close the loop.** Tell the user exactly what to click and74 flag anything still theirs to do.7576## Quality bar7778- **Frontmatter:** `name` plus a *pushy* `description` packed with concrete trigger79 phrases. Agents under-trigger skills; spell out when to use it, and give every `SKIP`80 a named alternative.81- **Lean `SKILL.md`** (under 500 lines). Depth goes in `references/`, deterministic work82 in `scripts/`, output assets in `assets/`.83- **Scripts over prose** for anything repeatable and checkable — tested on real inputs.84- **No scratch in the package.** A skill should not ship its author's debris.85- **Do not surprise the user:** never auto-commit or push to an auto-deploying repo,86 never delete files without explicit permission.8788## Self-modification guard — read before editing THIS skill8990This is the skill that edits other skills, so it is the one that can edit itself.9192**Never patch this skill while running a procedure defined by it.** The instructions you93are following are the ones being rewritten; a half-applied edit changes the remaining94steps under you, and **the failure is invisible because the run continues against the new95text.** If a pass concludes this skill needs changing: finish the pass, report the96finding, then start a fresh run whose only job is that edit.9798## Patch protocol, for any skill including this one991001. **Copy the package aside first**, with a dated suffix. Before the first edit, not after101 the first mistake.1022. **Patch narrowly.** One concern per pass. Do not reflow or reorganise while fixing.1033. **Re-read the patched file end to end.** Not the diff — the file. **A diff shows what104 changed and hides what the change broke around it.**1054. **Validate** with `build_skill.py`.1065. **Auto-revert on failure.** Restore the copy from step 1 *before* diagnosing. Do not107 leave a broken package in place while investigating — that is the state an unattended108 run or a distracted session picks up.1096. **Verify the install by file count, never by comparing text.**110111## Four failures this protocol exists to prevent112113All four happened, and all four were caught by running a check rather than by reasoning:114115- **`name:` changed during a rewrite.** Install overwrites by frontmatter name, so the116 edit **silently created a second skill** instead of replacing the first. Two versions117 then fire on overlapping triggers with no indication of which ran.118- **A package went stale against its own installed copy.** The package lacked a patch119 that both the installed skill and the source tree carried, so "reinstalling from the120 package" would have *regressed* the live skill. **Compare package against installed121 before treating the package as newer.**122- **Installing from a bare `SKILL.md` silently deleted every bundled script.** Eight123 skills lost their resources; one lost 101 files including the 37 scripts its own124 instructions call. **And the obvious check passed** — all eight had a byte-perfect125 `SKILL.md`. Count files against the package instead.126- **A substring scan produced false alarms.** A check for skills making runtime claims127 matched `mount` inside `paramount` and `blend_amount`. Use word boundaries.128129## Verification requires a fresh session130131**A running session's skill list is fixed at start.** No amount of re-checking inside the132current session proves an install worked.133134## The scripts135136| Script | What it does |137|---|---|138| `build_skill.py` | Rebuild and validate a `.skill` **before** installing. The gate. |139| `check_scoped.py` | Flags skills making single-runtime claims without naming the other case. Word-boundary matching. |140| `scan_skills.py` | **Locator, not a health metric.** Finds which packages mention which patterns. Tells you where to look, not whether anything is wrong. |141| `show_hits.py` | Prints the matching lines from `scan_skills.py` hits, for triage. |142| `trigger_evals.py` | Deterministic routing evals: positive prompts must rank their skill top-k in a TF-IDF ranking over descriptions, negatives must be won by their owner, and no two descriptions may sit above 0.75 similarity. Cases live in `evals/cases.json`; add cases whenever a description changes. Run with `--root <your skills dir>`. |143| `smell_scan.py` | Ranks skills by patchwork density (dated corrections, `EXCEPT WHEN` clauses, repeated rules) and prints the matching lines. A density score orders candidates and cannot judge them; read the lines. `--self-test` proves the patterns still catch real patchwork and ignore topic words. |144145`check_scoped.py`, `scan_skills.py` and `show_hits.py` carry pattern lists marked146**EDIT THIS FOR YOUR SETUP**. They ship with example markers; they are worthless until147they describe your actual retired paths and your actual runtimes.