Automated summary
A package manager for Claude Code skills that installs, lists, updates, and removes generic templates from a GitHub registry, customizing each one for the current repository.
Strengths
- Cascading registry resolution (env var, conventional local clone, then gh api), so reads work offline when a clone exists.
- Automatic tailoring of {{CUSTOMIZE}} markers using the repo profile, CLAUDE.md, and the codebase itself.
- Template-hash version pins recorded in .claude/skills.lock, making 'outdated' and 'update' accurate from repo-local state.
- Compiles one provider-neutral source into native command formats for several agents (Claude, Gemini, Codex, Pi).
Limitations
- Tied to a single named registry (blamechris/skill-templates); no multi-registry support is described.
- Requires an authenticated gh CLI or a local clone when the conventional paths are missing.
- Writes files into the repo's .claude/ and, for the pi target, into the user's home directory.
Use it when a repo should pull a skill from the registry the moment it needs one, instead of relying on a CI job or per-repo deploy PRs.
Skip it if your skills live outside this specific registry, or if automated writes to .claude/ and the user home directory are not acceptable.
Security analysis
CautionThis is a legitimate package-manager workflow, but it fetches and executes code from a remote registry and modifies or deletes repo and home-directory files. Those are powerful operations with supply-chain and destructive potential, though there is no evidence of secret exfiltration, obfuscated malicious payloads, or safety-disabling instructions.
- •Fetches and executes remote scripts (`scripts/skill-lint.sh`, `scripts/compile-skill-targets.mjs`) from GitHub via `gh api | base64 -d` without checksum or signature verification; a compromised registry branch or account could run arbitrary code.
- •Writes files into the repo and optionally under `~/.pi/agent/skills`, and `remove` deletes directories; scoped to known skill names, but malformed lockfile data or path handling could remove unintended paths.
- •Recommends an auto-install-on-miss rule in CLAUDE.md, which could lead an agent to fetch and install remote skill templates without explicit user confirmation.
- •Fetches and executes a remote shell script: `gh api .../scripts/skill-lint.sh | base64 -d > $lintdir/skill-lint.sh` then `bash $lintdir/skill-lint.sh` — effectively remote code execution pinned only to the trustworthiness of the blamechris/skill-templates repo and its default branch.
- •Fetches and runs a remote Node script (`compile-skill-targets.mjs`) into `scripts/` and executes it; same supply-chain exposure, though the artifact is repo-tracked and reviewable.
- •Reads and writes outside the repo: the `pi` target emits into `~/.pi/agent/skills/<name>/`, and `skill remove` may delete directories under the user's home dir. The doc does gate this on the lockfile's `targets` and warns the user, which mitigates but does not eliminate the risk.
- •Runs state-mutating git operations on a local clone (`git -C "$REG" pull --ff-only`) and creates/overwrites/deletes files under `.claude/`; requires bash, node, gh and network access even though no allowed-tools are declared.
- •No destructive, obfuscated, or exfiltration behavior: base64 decoding is confined to the standard GitHub contents API response format, and the document explicitly adds guards against silent-pass linter failure modes and against writing on another session's branch.
Examples
/skill add code-review/skill list/skill update/skill
The package manager for Claude Code skills — npm/brew for the skill registry
at blamechris/skill-templates.
Install, list, and update skills on demand, tailored to the current repo by
the agent running this command. No central fan-out, no per-repo deploy PRs.
This replaces the old push-based deploy: instead of a CI job pre-customizing every skill into every repo, a repo pulls a skill the moment it needs one and the invoking agent customizes it inline using the repo's own context.
Arguments
$ARGUMENTS— a subcommand and optional skill name:add <name>— install a skill into this repo (resolve → customize → write → lock).list— show available skills (from the registry index) vs what's installed here.outdated— show installed skills whose template has moved on in the registry.update [name]— re-install stale skills (all outdated, or just<name>).remove <name>— delete an installed skill and its lock entry.- With no argument, default to
list.
Concepts
- Registry —
blamechris/skill-templates. Holds pure generic templates ingeneric/<name>.md(with{{CUSTOMIZE: …}}markers) and an indexregistry.json. - Index —
registry.jsonat the registry root: one entry per skill with its current template githashand a one-line description. The cheap source of truth forlist/outdated. Regenerated byscripts/build-index.sh. - Installed skill —
.claude/commands/<name>.mdin this repo: the template with every{{CUSTOMIZE}}marker filled for this repo, ending with a version stamp. - Version stamp — the last line of an installed skill:
<!-- skill-templates: <name> <hash> <date> -->. The<hash>is the registry template commit the install was built from. This is the per-skill version pin. - Profile —
.claude/skill-profile.mdin this repo: the repo's identity and per-skill tailoring notes. The agent reads it (plusCLAUDE.mdand the code) to fill{{CUSTOMIZE}}markers. If absent, the agent infers fromCLAUDE.md+ repo layout and notes in its report that a profile would sharpen future installs. - Lockfile —
.claude/skills.lock(JSON): the manifest of what's installed and at which template hash. Letsoutdated/updatework from repo-local state. - Targets — the coding agents a skill is compiled for.
.claude/commands/<name>.md(the customized install) is the provider-NEUTRAL source;scripts/compile-skill-targets.mjsemits each agent's NATIVE custom-command format:claude→.claude/skills/<name>/SKILL.md,gemini→.gemini/commands/<name>.toml,codex→.codex/skills/<name>/SKILL.md,pi→~/.pi/agent/skills/<name>/SKILL.md. The first three are repo-tracked;piis the one user-global target — Pi has no repo-local discovery, so its artifact lands in the user's home dir and is opt-in rather than a safe default. The active list comes from thetargets:line in.claude/skill-profile.md(prompt the user if absent; the compiler falls back toclaude). This is what makes a skill model-agnostic — author once, run under any model. The neutral arg token is$ARGUMENTS; the compiler maps it per agent (e.g. Gemini{{args}}). The compiler ships in the registry atassets/compile-skill-targets.mjs.
Resolving the registry
Find the registry once, in this order; use the first that works:
$SKILL_REGISTRY_DIRif set — a local clone path.~/Projects/skill-templatesif it exists — the conventional local clone (fast, works offline for reads).- Otherwise fetch over the network with
gh:- Index:
gh api repos/blamechris/skill-templates/contents/registry.json --jq '.content' | base64 -d - Template:
gh api repos/blamechris/skill-templates/contents/generic/<name>.md --jq '.content' | base64 -d - Hash of a template:
gh api 'repos/blamechris/skill-templates/commits?path=generic/<name>.md&per_page=1' --jq '.[0].sha[0:7]'
- Index:
When using a local clone, refresh it only when online and tolerantly:
git -C "$REG" pull --ff-only --quiet 2>/dev/null || true. A failed fetch (offline) or
a non-fast-forward leaves the existing clone usable — fall back to its current state and
note in the report that hashes may be stale. Record which source you used.
Instructions
skill list
- Resolve the registry and read
registry.json. - Read this repo's
.claude/skills.lock(treat missing as empty) and list.claude/commands/*.md(treat a missing directory as no skills installed — a bare repo being bootstrapped). - Print a table: skill, registry hash, installed hash (from the stamp/lock), and
status —
installed/outdated/not installed. Group installed first.
skill add <name>
-
Resolve & fetch. Read
registry.json; confirm<name>exists (if not, suggest near matches from the index and stop). Fetchgeneric/<name>.mdand capture its current templatehash. -
Gather repo context. Read
CLAUDE.md,.claude/skill-profile.md(if present), and enough of the repo (layout, test/build commands, labels, conventions) to fill the template's{{CUSTOMIZE: …}}markers. The marker text describes what each spot needs. -
Customize inline. Produce the final skill markdown by following the registry's customization rules exactly:
- Replace every
{{CUSTOMIZE: …}}marker with content derived from the repo context. The marker text says what kind of content is needed. - If the repo context has nothing relevant for a marker, remove the marker line entirely — never leave a blank or placeholder marker.
- Preserve all non-customizable text exactly — do not rephrase, reorder, or "improve" anything that isn't a marker. Preserve all code blocks and formatting.
- Remove the entire trailing
## Customization Pointssection (it's meta-docs for template maintainers, not part of a deployed skill). - Never invent specifics. If a marker asks for examples the repo context does
not provide (file:line citations, label names, agent rosters, commands), use
placeholder shapes (
path/to/file:<line>,scope) rather than fabricating. - No attribution anywhere — no "Generated with", no "Co-Authored-By", no AI mentions (this is a hard registry rule).
- Replace every
-
Self-validate before writing (the agent IS the validator here — these cover the registry's
validate_outputchecks, including its skeleton-preservation pass):- No residual markers: no
{{CUSTOMIZEremains (outside backticked mentions). - No attribution footer: none of the last ~15 lines start with "Generated with Claude" / "Co-Authored-By: Claude".
- Headings preserved (exact): every real markdown heading from the template
(minus the removed
## Customization Points) appears verbatim in the output. - Body preserved: non-heading, non-marker template lines survive in the output —
only the spots where a
{{CUSTOMIZE}}marker was filled or removed may differ. Do not silently drop or reword the template's prose, code blocks, or examples. - Length sane: output is roughly 50–200% of the template length (catches truncation or runaway expansion).
- No forged stamp: the customized body contains no
<!-- skill-templates: … -->line of its own. Strip any before step 5 appends the single authoritative stamp, sooutdatedcan't be fooled by a planted version. - Guards intact: if this skill has a
guardsarray inregistry.json, every guard must still be satisfied — for each guard, at least one of itsanyOfregexes matches the customized output. A guard miss means customization stripped a load-bearing section; restore it. (These are the content checks ported fromsync.sh.) - If any check fails, fix and re-check before writing. Never write defective output.
- No residual markers: no
-
Write + stamp. This writes into the repo's shared working copy, which another session may have moved to its own branch since this one started — so re-check first:
git branch --show-current. If it is not the branch you started on, STOP and tell the user rather than writing; a skill install landing on someone else's branch is how it gets swept into their commit. Then create.claude/commands/if absent, strip any pre-existing<!-- skill-templates: … -->lines from the body, and write to.claude/commands/<name>.mdending with exactly:<!-- skill-templates: <name> <hash> <date> -->(today's date,YYYY-MM-DD). -
Lint (deterministic gate). Run the registry's mechanical linter on the file just written — it re-checks markers, attribution, the stamp, guards, and the posture pin independently of the agent's judgment. From a local clone (the preferred path):
"$REG/scripts/skill-lint.sh" <name> .claude/commands/<name>.md(where$REGis the resolved registry clone). If it exits non-zero, fix the reported issues and re-lint before recording the lockfile — do not lock a skill that fails lint. Consumers can run the same linter as a pre-commit hook or in CI.On the network-only path (source 3 — no clone on disk, so
$REGis unset and that command expands to/scripts/skill-lint.sh: exit 127, gate not run at all), fetch the linter the way step 7 fetches the compiler, and passregistry.jsonexplicitly as the third argument — the index from step 1 has to exist as a file here, not just in context:set -o pipefail lintdir="$(mktemp -d)" gh api repos/blamechris/skill-templates/contents/scripts/skill-lint.sh \ --jq '.content' | base64 -d > "$lintdir/skill-lint.sh" || exit 1 gh api repos/blamechris/skill-templates/contents/registry.json \ --jq '.content' | base64 -d > "$lintdir/registry.json" || exit 1 [ -s "$lintdir/skill-lint.sh" ] && [ -s "$lintdir/registry.json" ] || { echo "lint gate unavailable: a fetch produced an empty file" >&2; exit 1; } bash "$lintdir/skill-lint.sh" <name> .claude/commands/<name>.md "$lintdir/registry.json"Three details are load-bearing:
- The fetch guards —
ghfailing (offline, 404, rate limit) leaves a 0-byte file behind, andbashon an empty file exits 0 printing nothing, which this document's own outcome table below reads as "clean, all four checks passed". Unguarded, the network path's failure mode is a silent green gate — worse than the exit 127 this section exists to fix, because 127 is at least loud. Source 3 is reached exactly when there is no clone to fall back on, and "Resolving the registry" above explicitly contemplates being offline. bash <path>— a redirected fetch lands non-executable (the exact mode is umask-dependent; what matters is that no execute bit is set), so running it directly exits 126 (permission denied), not 127. Same skipped gate, different code.- The third argument — with it omitted the linter defaults its registry to
<the script's own dir>/../registry.json, which for a copy under a temp dir is not this registry. Either that path is absent, and the run exits 2 printingERROR: registry not found …on stderr with an empty stdout — the findings it had already collected are discarded unprinted, so a caller watching the output sees exactly what a pass looks like — or some unrelatedregistry.jsonsits there and gets loaded instead, in which case a skill it happens to name with no guards of its own lints✓ <name>: clean (0 guard(s) ok)at exit 0, with this registry's guards never applied.
The linter has three outcomes. Key on the exit code, not on the output markers —
ERROR:is printed for an unknown skill and then followed by findings at exit 1, and a usage error exits 2 printing no marker at all. Only~is exclusive to exit 2.- exit 0 (
✓) — clean, every check passed. - exit 1 (
✗) — real findings. Fix and re-lint; never lock. This includes a file that is stamped but absent from the index: a stamp is proof of a registry install, so an index that has never heard of the skill means a stale clone, a renamed or retired skill, a typo'd<name>, or the wrongregistry.json— and its guards went unapplied. The reported findings name which, and the stamp checks are applied on this path too. - exit 2 — could not be fully verified. Six causes, and only one is benign:
an unstamped skill absent from the index (the
~line — a repo-only skill, with genuinely nothing to verify), plus missing registry file, missingpython3, unreadable skill file, an explicitly passed profile that cannot be read, and usage error. The last five are environment breakage.
For
skill add, exit 2 is always a failure:addstops before writing if the name is absent from the index, so by lint time the skill is in the index by construction — exit 2 there means a stale index, a wrong name, or a broken environment.For a pre-commit hook or CI linting a whole
.claude/commands/directory, exit 2 is far narrower than it was: a stale index — which used to turn a real✗ guard missinto a clean-looking~— is now reported as a finding at exit 1. What remains at 2 is a genuinely repo-only file or environment breakage (missing/wrong-path registry, nopython3, unreadable file), and the linter cannot tell a hook which of those it hit. So keep the version-stamp test: a stamped file can no longer legitimately exit 2, because provenance means the index should know it. Tolerating 2 unconditionally re-opens the hole this change closes — for the 270 stamped files in the current fleet, a mistyped registry path would go from caught to silently green.scripts/skill-lint.sh "$name" "$file" ; rc=$? if [ "$rc" -eq 1 ]; then exit 1; fi # includes a stamped file the index does not knowEarlier revisions of this step told the hook to re-test the file itself with
grep -q '^<!-- skill-templates: 'and fail on a stamped exit 2. That existed only because exit 2 covered both conditions and the caller had to re-derive which one it had hit. Do not reintroduce it: it re-implements, less accurately, a check the linter already runs — the grep cannot see a stamp behind leading whitespace, and cannot distinguish a well-formed stamp from a mangled one. - The fetch guards —
-
Compile to native targets. Ensure the compiler exists in this repo (create
scripts/if absent). Ifscripts/compile-skill-targets.mjsis missing — or you're runningupdate, so it stays current with the registry — (re)fetch it: from a local clone,cp "$REG/assets/compile-skill-targets.mjs" scripts/; on the network-only path (no local clone), fetch it like the templates —gh api repos/blamechris/skill-templates/contents/assets/compile-skill-targets.mjs --jq '.content' | base64 -d > scripts/compile-skill-targets.mjs. Write it toscripts/and track it in VCS so it travels with the repo. Read thetargets:line from.claude/skill-profile.md; if there is none, ask the user which agents to compile for (claude / gemini / codex / pi) and offer to record the choice in the profile (the compiler falls back toclaudeif unset). Then runnode scripts/compile-skill-targets.mjs --name <name>(it reads the profile targets), or--targets <list>to override. It writes the native artifact per target and exits non-zero on any emit failure — treat that as a hard gate: fix and re-run before locking. Codex emits a repo-tracked skill folder under.codex/skills/so it can be reviewed, copied into~/.codex/skills, or used by runners that support repo-local Codex skills.piwrites outside the repo (~/.pi/agent/skills/), so only add it when the user actually drives this repo with Pi — never volunteer it into a committedtargets:line on their behalf. The compiler prints a hint if it sees~/.pion the machine butpiis unselected; that hint is advisory and never adds the target itself. Keep the list oftargetsyou compiled with for the next step. -
Record in the lockfile. Only after a successful compile, create
.claude/skills.lock(schema below) if absent and upsert<name>atomically with: the templatehash; theprofileHashwhen a.claude/skill-profile.mdexists (soupdatecan tell when the profile changed, not just the template); and thetargetsyou just compiled with (soremovecleans exactly the right native artifacts andoutdatedcan detect target drift). Writing the entry only after compile succeeds means a failed compile never leaves a half-written ortargets-less entry. -
Report. State the skill, the hash installed, the registry source used, the targets compiled (with output paths), and any markers dropped for lack of repo context.
skill outdated
- Resolve the registry and read
registry.json. - For each entry in
.claude/skills.lock(or each stamped.claude/commands/*.md), flag it stale if any of:- version drift — installed
hash≠ the registry's current hash; - profile drift — the lockfile's
profileHash≠ the current.claude/skill-profile.mdhash (the skill was tailored against an older profile); - corruption drift — the installed file fails a registry
guard(some guard'sanyOfregexes no longer match — a load-bearing section was lost). - target drift — only when the lock entry has a
targetsarray (a pre-targetsinstall with no such field is treated as "unknown" — fall back to disk inspection, never flagged just for the field's absence): the profile'stargets:list has an agent not in the lock entry'stargets(added to the profile but never compiled), or a recorded target's native artifact is missing on disk — excluding targets the compiler intentionally skips (e.g. Gemini for a body containing{{…}}/!{…}/@{…}, which is never emitted and so isn't real drift). A plain recompile fixes genuine target drift — no registry fetch needed.
- version drift — installed
- Print the drifted skills with the reason, e.g.
name abc1234 → def5678 (version)orname (guard miss: idempotency)orname (target drift: gemini not compiled). If none, say so. (This is the consumer-side port of the registry'ssync.shdrift check — version stamp and content guards.)
skill update [name]
- Run
outdated. If<name>is given, narrow to it. - For each stale skill, re-run
add(which re-fetches, re-customizes against current repo context, and re-stamps). Skip skills already current. - Report what was updated
old → newand what was already current.
skill remove <name>
Read the skill's targets from its .claude/skills.lock entry, then delete
.claude/commands/<name>.md, its lock entry, and exactly the native artifacts for those targets:
claude → .claude/skills/<name>/ (dir), gemini → .gemini/commands/<name>.toml,
codex → .codex/skills/<name>/ (dir), pi → ~/.pi/agent/skills/<name>/ (dir, outside the
repo — remove it only when pi is in the lock entry's targets, and say so in the report since
the user may not expect a repo command to touch their home dir). If the lock entry has no targets
(an older install), fall back to removing whichever of the three repo-local artifacts exist —
never probe ~/.pi on a guess, because an unrelated Pi skill of the same name could live there.
Report what was removed.
Lockfile schema (.claude/skills.lock)
{
"registry": "blamechris/skill-templates",
"skills": {
"full-review": { "hash": "cc062bc", "installed": "2026-06-04", "profileHash": "3f1a9c2", "targets": ["claude", "gemini"] },
"check-pr": { "hash": "a10ef75", "installed": "2026-06-04", "targets": ["claude"] }
}
}
hash— the registry template commit this install was built from (matches the version stamp).outdateddiffs this againstregistry.json.installed— date the install/update ran (YYYY-MM-DD).profileHash(optional) — short hash of.claude/skill-profile.mdat install time. Present only when a profile existed.outdatedflags profile drift when it no longer matches the current profile, so a changed profile triggers a re-tailor onupdate.targets(optional) — the agents this skill was compiled for at install time (e.g.["claude", "gemini"]). Written by theaddcompile step;removeuses it to delete exactly the right native artifacts, andoutdateduses it to detect target drift. Absent on pre-targets installs — treat a missingtargetsas "unknown" and fall back to disk inspection.- The lockfile is the authoritative manifest; the per-file stamp is a convenient inline mirror. Keep them in agreement.
Notes
- The agent is the customizer. No Haiku/API key — the agent running
/skilltailors the template with full repo context, which is richer than a context-blind batch job. The self-validation step replaces the registry's automatedvalidate_output. - Auto-install on miss. A repo or global
CLAUDE.mdrule should say: asked to run/X? Check the neutral source.claude/commands/X.md. Missing → not installed →skill add X. Present but the native artifact (.claude/skills/X/SKILL.md) is missing → just not compiled → recompile withnode scripts/compile-skill-targets.mjs --name X(no registry fetch). Then invoke. That makes "use /full-review in a repo that lacks it" just work without re-fetching when a recompile suffices. - Compile is deterministic. The generic→native transform lives in
scripts/compile-skill-targets.mjs, not in agent judgment, so the native artifacts are reproducible. Edited a skill's generic source (.claude/commands/<name>.md) by hand? Recompile withnode scripts/compile-skill-targets.mjs --name <name>(--dry-runpreviews). - Idempotent. Re-running
addfor a current skill is a no-op re-render; re-running for a moved template updates it. Safe to run anytime. - Self-contained on first use.
/skillneeds no customization to work — it ships with sane registry defaults so it can bootstrap a repo that has nothing else installed.
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/claude-code-skill-manager-msh3uu5a/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/claude-code-skill-manager-msh3uu5a' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/claude-code-skill-manager-msh3uu5a/download' --output '.skill-downloads/claude-code-skill-manager-msh3uu5a/SKILL.md.part' && mv '.skill-downloads/claude-code-skill-manager-msh3uu5a/SKILL.md.part' '.skill-downloads/claude-code-skill-manager-msh3uu5a/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
This file does not provide a usable name/description header for this native procedure. It remains available to download and inspect; use the manual method below.
Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/claude-code-skill-manager-msh3uu5a/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.