One place for every custom skill, agent, and hook. Reused across every project, read by Claude Code and Codex.
Overview
One place for every custom skill, agent, and hook. Reused across every project, read by Claude Code and Codex. One method, symlinks, for both hosts: Claude Code reads ~/.claude/skills/, Codex reads ~/.agents/skills/. - re-run the loop when a skill is added. git pull updates every linked skill. - when a skill is removed, delete its link from both ~/.claude/skills/ and ~/.agents/skills/. The loop never deletes. - MACHINE.md holds the rules every session on the machine loads, in both hosts. The last two lines of the block link it once per machine. Claude Code reads the @ line as an import and keeps what ~/.claude/CLAUDE.md already holds. Codex reads no import line, so the link replaces ~/.codex/AGENTS.md; when that file holds lines of its own, the block leaves it alone: move the lines elsewhere and run the block again. A fresh session in each host then has the rules loaded.
README
brickswhale plugin
One place for every custom skill, agent, and hook. Reused across every project, read by Claude Code and Codex.
.claude-plugin/plugin.json the plugin manifest
MACHINE.md the machine briefing: rules every session loads, both hosts
skills//SKILL.md skills; one the owner normally starts by typing is a command
agents/.md subagent definitions, when earned
hooks/hooks.json shared hooks, when earned
reviews/.md what a review left unsettled, when one was run
Install, once per machine
git clone https://github.com/brickswhale/skills.git ~/skills
mkdir -p ~/.claude/skills ~/.agents/skills ~/.codex
for d in ~/skills/skills/*/; do n=$(basename "$d"); ln -sfn "$d" ~/.claude/skills/$n; ln -sfn "$d" ~/.agents/skills/$n; done
grep -sqxF '@~/skills/MACHINE.md' ~/.claude/CLAUDE.md || printf '\n@~/skills/MACHINE.md\n' >> ~/.claude/CLAUDE.md
[ -s ~/.codex/AGENTS.md ] && [ ! -L ~/.codex/AGENTS.md ] || ln -sfn ~/skills/MACHINE.md ~/.codex/AGENTS.md
One method, symlinks, for both hosts: Claude Code reads ~/.claude/skills/, Codex reads ~/.agents/skills/.
- Add: re-run the loop when a skill is added.
git pullupdates every linked skill. - Remove: when a skill is removed, delete its link from both
~/.claude/skills/and~/.agents/skills/. The loop never deletes. - The machine briefing:
MACHINE.mdholds the rules every session on the machine loads, in both hosts. The last two lines of the block link it once per machine. Claude Code reads the@line as an import and keeps what~/.claude/CLAUDE.mdalready holds. Codex reads no import line, so the link replaces~/.codex/AGENTS.md; when that file holds lines of its own, the block leaves it alone: move the lines elsewhere and run the block again. A fresh session in each host then has the rules loaded. - Plugin install is not an install method here. A plugin install would load every skill a second time in Claude Code under a prefixed name (
brickswhale:) beside the linked ``, while the links already serve both hosts. The eval runners load the repo per run with--plugin-dir; that is test wiring, not an install. The links carry skills only:agents/andhooks/hooks.jsonreach a session only through that plugin load, so an agent or hook that earns its place also earns its own install line, as the hook below does.
Off by default, Claude Code only: hooks/fresh-call-guard.py refuses a SendMessage to a finished subagent’s raw id, a backstop for route’s fresh-call floor (#28). The cause it met was a session that read the rules once, so the pointer line now has a session re-read them before every round; enable the guard only if a session that has read the rules still sends a round to an earlier agent. Symlinks load no hooks, so it would go in the user settings as a PreToolUse command hook with the matcher SendMessage, running f=/hooks/fresh-call-guard.py; [ ! -f "$f" ] || python3 "$f": a bare python3 exits 2 when the file is missing, and exit 2 blocks the call, so a moved clone would block every message. Its test: bash hooks/fresh-call-guard.test.sh.
Off by default, Claude Code only: hooks/ask-link-check.py stops the desktop app’s next-step box (mcp__ccd_turn__wrap_up) once per turn when a ticket named in the box or in that turn’s text is not a link in that text (#61: a rule line alone left 9 of 12 asks without links). Switch it on per project, in that project’s settings, as a PreToolUse command hook with the matcher mcp__ccd_turn__wrap_up, running f=/hooks/ask-link-check.py; [ ! -f "$f" ] || python3 "$f" --id '', so a moved clone passes instead of blocking every box. Its test: bash hooks/ask-link-check.test.sh.
Who owns what
| Owner | What | Where |
|---|---|---|
| This repo | skills, the machine briefing’s text, hooks, eval cases | skills/, MACHINE.md, hooks/, evals/ |
| The machine | the links into this repo; each skill’s data between runs (rule 9); the two host files that load the machine briefing | ~/.claude/skills/, ~/.agents/skills/, ~/.config//, ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md |
| A project | its own briefing (project facts and any rule that overrides the machine briefing), its tickets, at most one pointer line per shared skill it uses | the project’s AGENTS.md / CLAUDE.md |
| The Claude app | its own synced skills and the copies it moves aside; nothing here writes to them | ~/.claude/skills/synced/, ~/.claude/skills/.trash/ |
Where two owners say different things, the project wins in that project: the machine briefing is the default, a project’s briefing the override.
How work flows
From an idea to the main branch. The discussion, the coordinator and each ticket’s worker run in sessions of their own, so the owner knows which session holds what:
- Idea → discussion session. A fresh session takes the idea:
consultpressure-tests it,pairadds a second model’s view,intentwrites the ticket,planwrites its steps. It ends with a decided ticket, or the reason there is none. - Ticket → queue. The ticket joins a queue ticket, the coordinator’s list.
- Queue → coordinator.
/supervisemakes the session the owner talks to the coordinator: it finds the queue, the open ticket whose title begins[supervise](or the one/supervise #names), keeps the list, hands out each ticket and checks each report on disk. A new idea or an undecided ticket that reaches it goes back to stage 1. - Ticket → worker. One fresh worker session per ticket, running
flow: re-check, plan, build, review, commit. - Merge and push on the owner’s word.
What a project gets
The contract onboard applies to a consumer project.
- Overrides kept. A project rule that differs from the machine briefing or a shared skill stays in the project’s briefing, with its effect intact.
- Every old-kit rule gets a home. Each rule in the project’s old-kit briefing maps to exactly one of: covered by a named shared skill; covered by the machine briefing; kept as a project rule; retired on the owner’s word. A rule with no home is listed for the owner; none is dropped silently.
- Pointers, not copies. The project gets at most one pointer line per shared skill (rule 9 in
AGENTS.md); no skill text is pasted into the project. - A second run adds nothing. Running the setup again on a set-up project changes no file.
Structure
Flat. One folder per skill under skills/, holding SKILL.md and, where a skill needs reference data too long to inline, a references/ beside it — the hook word-caps and genericity-checks SKILL.md alone, so anything else there is scrubbed by hand or not committed. No category folders: the plugin manifest can list several skill directories, and the install loop reads one level, so a category, when there are enough skills to need one, becomes a second directory listed in plugin.json and read by its own install loop, never a nested path. Until then the table below groups skills by SDLC stage. A command is a skill the owner normally starts by typing its name (rule 2); there is no commands/ folder.
Frontmatter keys: only those in Claude Code’s frontmatter reference, which is the list, not a copy here. model and effort are among them and switch the model and effort while the skill runs, so neither ever labels the model a skill was written for.
Evals
Real use is the proof (owner’s ruling 2026-10-11, #40): a skill that fails in real use gets a ticket. Eval runs proved that some skills change behaviour (consult 5/5 against 0/5 bare) and that some should not be built (route’s bare setup passed 5/5), but no eval caught a broken skill before real use did, and their batches burned the usage window four times (#37).
Every skill has a fire case under evals/-/, in the layout claude plugin eval reads: prompt.md with frontmatter, graders/*.md — skill-fired.md proving the skill fired, plus an llm grader with a PASS/FAIL rubric for the outcome — optional case.yaml with a scaffold_script that builds a self-contained fixture, an optional followup.md as a second user turn, and a writes file (rule 9). A case that starts its skill by a bare slash name makes no Skill call, so its skill-fired grader is an llm one matching a line only its body produces; plan’s cases call /brickswhale:plan, since a bare /plan reaches Claude Code’s built-in command.
- Every commit:
evals/check-static.sh, from the pre-commit hook — frontmatter, scrub, case files, script syntax. Free. - A new skill, or a changed
description:line: one fire check, about 2 calls, on the owner’s OK:evals/run-eval.sh --model --budget --tracer. The commit message names it in afire-check:line;.githooks/commit-msgasks for that line, and for a fire case, only in these two situations. - A body edit: no live run.
The eval tools stay as they are: nothing new is added, and a piece is deleted only when it breaks. evals/run-eval.sh is the runner (a named model, a budget printed before the first call, one run at a time, a stop at the first usage-limit line); evals/run-valid.sh is its gate; evals/replay.sh re-checks saved route runs without a new session; route’s two-stage cases run through evals/run-route-case.sh, with the account in reviews/route-design.md. Judges stay on the default model: in the route record haiku misgraded, then sonnet at low effort misgraded five of twenty-five verdicts. CI skips the 17 pinned replay checks that require saved runs on the owner’s machine; changes to evals/replay.sh or evals/route-checks/ also require running evals/replay.test.sh there.
Working on this repo
Two repo-local skills in .claude/skills/, not installed globally: /new-skill scaffolds a skill and its eval case, /eval-skill runs it fenced and says whether the result counts. Both are model-invocable, so an agent can run them, which is what rule 7 asks for. Their job is to make sure every skill here is well made. They never leave this repo.
Rules
- This repo is public. No client names, no home paths, no credentials in any skill, ever.
- A skill is a checklist, never a script. Target under 300 words — a target, not a blocker. Exceed it when the extra lines change what the agent does.
- A skill that a tool can replace is deleted the day the tool exists.
- A skill is self-contained: its rules stay in its own folder, its machine data under
~/.config//, and a consumer repository gets only what the owner asked for plus at most one pointer line. Rule 9 inAGENTS.md. - Nothing copied from agent-kit verbatim. A
kit-*skill is rewritten here only when a real ticket shows its judgment step done badly twice, or on the owner’s word. Itskit-*symlink stays until the last project using it moves to the new name.
Skills
| Skill | Use |
|---|---|
consult |
an idea gets pressure-tested before anyone builds it — its real terms, the assumptions, the strongest objection, a verdict that may be “don’t”. Writes nothing, produces no plan. /consult, or any “what if we” |
flow |
one task runs from ask to closed ticket — the ticket in the project’s own place first, findings written to it as they appear, a re-check before building, test then review then commit, a commit that lands an item of the plan list ticks it with that commit, and a six-line wrap-up block; a typo, comment or format fix takes the fast lane. A project connects with one briefing line naming its ticket place: “Tickets: . Work beyond a typo, comment or format fix follows the flow skill: call it before the first edit.” /flow |
gate |
a phase boundary gets a go or no-go — every criterion labelled against an artifact that was actually run, never a note asserting it, and “not yet” is a real verdict carrying what would flip it. /gate, or “are we ready for the next phase” |
intent |
a raw ask becomes an issue — problem, outcome, affected, constraints, open questions, interrogated until each is concrete. /intent , or a session calls it by name |
learn |
a mistake becomes one rule on the highest rung that can catch it — test, hook, skill line, briefing — replacing a line, never adding one. /learn, or when a lesson needs to stick |
onboard |
one project is set up on the shared skills, or checked — an inventory of its agent setup and the machine side, a status table per layer, a duty table giving every old rule one home with its source line (a named skill, the machine briefing, a project rule, or retired on the owner’s word) and a “what you lose” list; then one diff applied on the owner’s yes, the flow pointer line, the owner’s own /route connect, and a wiring check that ends in pass or fail. “Check only” writes nothing; a re-run on a set-up project changes nothing. A command: /onboard, /onboard check |
pair |
a second opinion from another model on one question — your own position written first, the ask put blind, both views attributed and the dissent kept rather than averaged. /pair, or “second model opinion” |
plan |
an ask becomes ordered steps — files, the test per step, blast radius, riskiest step, numbered alternatives; nothing is written until the asker picks, and then the picked steps go into the ticket as a - [ ] list, each with its proof. /plan , or a session calls it by name |
progress |
the list re-checked against its proofs before any report — found in the ticket, or drafted from evidence as a DRAFT with a source per item; state changes need proof, scope changes are proposed; then what changed, what waits on the owner, the list, the next step. /progress, or “what’s the progress” |
review |
four lenses over a diff — bugs, security, does it match the plan, scope creep — every finding verified against the code and given a counted class, verdict as JSON. /review |
route |
the machine’s model dictionary and its jobs with ranked models get written or brought up to date, under ~/.config/route/ and shared by every project — which models this machine has and what each is good for, from its own tools and the owner’s answers, and which one builds and which reviews each job. Drafts, checks, then writes on the owner’s yes; a project gets one pointer line to the skill’s rules, which sessions read in place with the two files. A command: /route |
supervise |
the session the owner talks to supervises an effort while other sessions do the work — the list re-checked through progress at the start, on each progress ask and after each worker report; before a hand-off, a check that no other worktree or branch changes the same files; a brief that ends in a six-line callback; each report checked on disk, where a change counts as done only once its commit is on the main branch; decisions relayed in the owner’s words. /supervise, which finds the open ticket whose title begins [supervise]; /supervise ; or “supervise this” |
Photography
| Skill | Use |
|---|---|
photo-taste |
photographic direction, reference interpretation, critique and reversible editing; judges composition, light and colour against visual intent. “Make this photo professional”, “critique this edit”. LightCraft guidance included. |
agent-kit skills, where each one goes
| kit skill | goes to |
|---|---|
| kit-lens-review, kit-eng-pass | review — judgment only; kit-eng-pass ran the tests and mapped coverage, review runs nothing |
| kit-adr, kit-consult’s ranking | plan — done |
| kit-consult’s advisory mode | consult — done |
| kit-idea, kit-setup’s interview | intent — judgment only; kit-idea appended one dated line and left, intent interrogates until each field is concrete |
| kit-setup’s setup and update | onboard — judgment only: a project is moved onto the shared skills with every old rule given a home; the template scaffold and the version sync did not come across |
| kit-doc-sync | learn — judgment only; the per-commit sweep over every doc surface did not come across |
| kit-milestone-gate | gate — done |
| kit-recenter | attempted, rejected — see below |
| kit-report | progress — judgment only: what waits on the owner comes first, and every state is re-checked on disk before it is shown; the CLI did not come across |
| kit-problem-log, kit-pause | wrappers round a CLI that does not exist here |
| kit-log, kit-phase-map | git and the issue tracker already hold this |
| kit-batch, kit-prompt-cycle, kit-inline | the framework’s own orchestration; it retires with it |
| kit-pair | pair — done, on the owner’s word; the catalog came too, the machine-local record did not |
| skills-extra (agent-kit’s folder) | a category for skills outside the main flow, with its own README heading as Photography has — no folder here; its three skills each have a row below |
| design-flow | flow’s design-change rule and route’s Design job — judgment only: the transports catalog, the live specialist pick and the route receipt did not come across; a project’s own preview gate, psychology lens and decision fields stay in the project |
| deepen | mattpocock-skills improve-codebase-architecture (installed plugin) — judgment only: the two-signal scope, the reading ceiling, the adversarial verification and the lasting docs/reviews/ report with dispositions did not come across |
| naming-review | not coming — no replacement; one run (2026-07), no live reference; rewritten here only on the step-1 trigger or the owner’s word |
“Done” means the judgment came across, not the job. Seven rows above say so
explicitly, because a reader migrating a project off these names will otherwise
swap kit-doc-sync for learn and silently lose a commit gate. Checked against
the skills as written: learn carries nothing about reconciling a doc surface,
review never runs a test, and intent has no one-line capture. kit-adr’s
decision record did come across, as plan step 6. A cross-model review found
this and it was verified before being believed; the table had read “done” for
all six since the migration closed.
A project cannot be migrated by renaming. Each reference has to be read for what it obliges, and an obligation with no new home is either restated in the project or deliberately retired.
How a kit-* skill migrates here
Trigger-based, never scheduled. One skill at a time.
- Trigger. A real ticket shows that judgment step done badly twice. Note the ticket in the commit message.
- Rewrite. To the word target under Rules, generic, no kit vocabulary, no project names. The old skill is source material, not text to copy.
- Test. One fire check, as for any new skill (rule 6); real use decides whether it earned its place.
- Retire. Remove that
kit-*symlink when the last project using it migrates to the driver. Projects still on the old name keep working until then; the new name differs, so the two coexist without colliding. One copy of each thing, once nothing reads the old one.
Nine, after three reopenings. review, plan, intent, learn, consult and gate came
across, and the migration was declared closed at six. pair reopened it on the owner’s word,
against the line this file used to carry — that its value was a machine-local transport record and
a catalog of sharp edges, with no home here. Half of that objection held. The record is genuinely
machine-local and stays there, at ~/.config/pair/ (rule 9); the skill names that path and the
lines the file must carry. The catalog was the half that did not hold: it is generic knowledge about public
CLIs, it costs nothing against the word cap because the hook reads only SKILL.md, and a skill
pointing at a catalog it cannot name is worse than either keeping it out or bringing it in. It was
rewritten, scrubbed and now sits at skills/pair/references/transports.md. progress reopened it
a second time (#21): kit-report was filed as a wrapper, but only its CLI was; its judgment — what
waits on the owner first, every state re-checked on disk before it is shown — is what the owner kept
asking sessions for. onboard reopened it a third time (#46): kit-setup’s interview had gone to intent,
but moving a project onto the skills was done by hand, and duties were lost on the way. Of the twenty-one kit
skills, ten were absorbed into these nine, two are covered by other skills (design-flow by flow
and route, deepen by an installed plugin), and the other
nine are not coming: two are wrappers round a CLI this repo does not have, two are document layouts
git and the issue tracker already hold, three are the old framework’s own orchestration and retire
with it, one was attempted and rejected, and naming-review has nothing that needs it yet.
kit-recenter is the one that was attempted and rejected, and the reason is worth keeping. It audits a session’s own recent turns against its standing rules. Built here and evaluated, it twice raised a drift finding against a rule that appeared nowhere in its context, and when the rubric was tightened to demand a source, it invented the source too. A session cannot reliably tell a rule it was given from one it believes it was given, and the eval regime makes it worse: a fresh subprocess has no prior turns, so the fixture must hand it a transcript, which is a different skill from the one intended. Do not rebuild it without a way to verify a cited rule against a file the run actually read.
Reopening needs the trigger in step 1, not a tidy-up impulse. The kit-* symlinks stay until the last project using each one moves across.
Recommended Tools
Try a different keyword or remove a filter.
Install
npx skillfish add brickswhale/skills