---
name: mod-builder
description: Explains, discovers, plans, writes, migrates, validates, audits, debugs and publishes Claude Mods, Claude Code plugins whose hooks/hooks.json names a TypeScript module of function hooks. It uses Anthropic's official issue, cheat sheet, architecture paper, built-in Mods and type declarations, plus the nightly awesome-claude-code-mods catalogue, to choose the smallest $ surface and verify every capability. Trigger phrases include "build a mod", "Claude Mod", "function hook", "hooks module", "mod idea", "brainstorm mods", "find a mod", "review my mod", "debug my mod", "publish my mod", "/mod-builder".
---

# Mod builder

A Claude Mod runs inside Claude Code's own process with the process's reach. The only record of what it can do is the `$` calls it makes, and `claude plugin validate` prints them before any code runs. This skill turns that fact into a pipeline: choose the right form, plan the surface, write the code, read the validator back, then write the threat model. Never generate mod code before the plan exists.

Function hooks are early access. Nothing loads unless `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1` is set, and the API can change between releases. Read `references/gotchas.md` before the first line of code and `references/events.md` or `references/nouns.md` whenever an event or verb is in doubt. Do not invent an event or a verb. Read `references/official-sources.md` to choose the right source. When sources disagree, follow `references/reading.md` in this order: the local `.claude/types/claude-code.d.ts` generated by `/plugin-types`, then `claude plugin validate`, then Anthropic's current types file, then the cheat sheet and architecture paper.

## Pick the mode

| The user says | Mode |
|---|---|
| "what is a Mod", "Mods vs hooks", "how do Mods work" | Learn |
| "find a Mod", "is there a Mod for", "what Mods exist", "install a Mod" | Discover |
| "build a mod", "write a Claude Mod", a concrete feature, a hooks module question | Build |
| "review my mod", "audit my mod", a path to an existing plugin | Review, the order in `references/workflows.md` |
| "mod idea", "brainstorm mods", "what could I build", a workflow complaint with no feature named | Brainstorm |
| "turn this hook into a Mod", "should this be a Mod", "skill or Mod" | Migrate |
| "validator failed", "my Mod does not load", "debug my Mod" | Debug |
| "publish my Mod", "list my Mod", "add badges" | Publish |

When the request is ambiguous, restate it in one line and ask one question.

## Learn, Discover, Migrate, Debug and Publish

Read `references/workflows.md` for these modes before acting. They all start
from the official sources in `references/reading.md`, then use the live
catalogue only for community coverage and examples. Never present a catalogue
row or a community README as an API authority.

## Build a mod

### 1. Fix the trigger and the job

Write two lines before anything else:

- Observe: the event or events the mod must see, with the matcher that narrows each one. Take names from `references/events.md`.
- Do: what the mod must be able to do, as verbs on `$`. Take names from `references/nouns.md`.

If the user gave a feature and not the events, infer both lines from the feature and show them. Prefer a matcher over a bare event: `tool.call{tool=Bash}` over `tool.call`. A mod that hooks `tool.call` with no matcher sees every tool call, `prompt.submit` sees every prompt, and `*` sees everything. Say which applies.

### 2. Budget the surface

For each verb in the Do line, write one line: the hook that calls it, and what breaks if it is removed. Remove any verb where nothing breaks. Then read the reach level off `references/nouns.md` and state it:

```
Surface: $.ui.log, $.store.get, $.store.set
Reach: L0 draws and remembers
Sees: Bash calls
```

Rules for the budget:

- If the job can be done at a lower level, do it at the lower level. Reading `$.session.repo` beats running `git remote -v` with `$.process.run`.
- `$.http.fetch` and `$.mcp.call` need a named host and a named payload in the plan. No host, no network.
- `$.process.run` needs a literal argv in the plan. No interpolated user text into argv.
- `$.model.*`, `$.agent.spawn`, `$.prompt.submit` and `$.tool.call` drive Claude and cost tokens. Name the trigger that bounds how often they run.
- If the job is one shell command on one classic event with no drawing and no state, say that a classic settings hook is enough, and stop.

Show the plan to the user and get a yes before step 3. This is the one blocking question in the pipeline.

### 3. Write the files

Copy shapes from `references/templates.md`. Write exactly these files into the target directory:

- `.claude-plugin/plugin.json` with `name`, `version`, `description`, `author`, `license`.
- `hooks/hooks.json` with `{ "modules": ["./register.ts"] }`.
- `hooks/register.ts` exporting `register(on)`. Use `.tsx` only when the mod draws with JSX.
- `.gitignore` with `.claude/types/`.

Add `tsconfig.json` only when the mod draws or the user wants `tsc`. Add a surface module only when the mod needs its own frame clock or keyboard on the drawing thread. A surface module is a `Client` element with a string-literal path.

Code rules, each from a shipped mod or the cheat sheet:

- Spell every call `$.noun.verb(...)` in full. No computed access, no optional chaining on `$`, no aliasing `$` into a variable.
- Pass `next` a copy to change the event: `next({ ...e, timeout: 30 })`. Ids on `e` are pinned.
- Call `next(e)` exactly once on events where core has a side effect (`tool.call`, `prompt.submit`, `prompt.fill`, `prompt.suggest`, `turn.step`, `session.compact`, `agent.spawn`, `command.run`, `config.set`, `ui.press`, `ui.input`, `ui.message`, `classic.*`), unless the hook answers instead.
- Add `.catch` to any hook whose failure the user must notice. Without it, a throw or a 10 second overrun skips the hook with one dim line.
- In a surface module never name a local variable `h`. Clamp drawn rows to `e.props.maxRows`.
- Comment only a non-obvious constraint. No narration.

### 4. Validate and paste

Run the bundled script, which wraps the validator and grades the result with the scanner's rules:

```sh
node <skill-dir>/scripts/footprint.mjs <mod-dir> --plan '<the Surface line from step 2, comma separated>'
```

`<skill-dir>` is the directory this SKILL.md lives in. Paste the full output into the reply. The script exits 1 when validation fails or when the validator printed a call the plan did not list. Without Node, run `claude plugin validate .claude-plugin/plugin.json` directly and paste that.

### 5. Compare calls to plan

Read the `calls:` line against the Surface line.

- A call in the output and not in the plan: remove the call, or write one sentence that states why the plan grows and update the Surface and Reach lines. Never widen silently.
- A call in the plan and not in the output: drop it from the plan.
- A reach level higher than planned: the user must say yes again.

A review of an existing mod follows the order in `references/workflows.md` and uses this step for the footprint comparison. Take the plan from the mod's README. Every call the README does not explain is a finding.

### 6. Prove the behaviour

Static validation proves the declared footprint, not that the hook behaves as
intended. For every Mod, write the smallest useful check before handoff:

- A pure helper: run its test with the project's existing test runner.
- An event rewrite, refusal or `next` chain: add a focused `claude plugin test`
  case using Anthropic's mock environment when the plugin has tests.
- A UI Mod: manually load it in an interactive terminal and exercise the one
  render or input path that changed. Do not substitute a fabricated capture.

When writing tests, copy the closest shape from `references/testing.md`. Keep
the test under `tests/`, named after the hook file it covers; answer every lower event
the Mod calls, or the official test kit throws the unhandled event name.

Run `claude plugin test <mod-dir>` when tests exist. If the test kit is not
available or the user did not ask for test files, say that runtime behaviour is
unverified. Never call a passing validator a behavioural test.

### 7. Write the threat model

Five lines, template and worked examples in `references/threat-model.md`. Fill each line from the validator output, not from intent. Line 5 names what happens when the prompt, a tool result, a file or a network reply is crafted against this mod.

### 8. Hand over

End the reply with, in this order:

1. The file tree.
2. The validator output, verbatim.
3. The Surface, Reach and Sees lines.
4. The threat model and the behavioural check result, or the explicit reason it was not run.
5. How to load it for one session: `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir <mod-dir>`. Then how to keep it on: `{ "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" } }` in `~/.claude/settings.json`, with the note that this loads the hooks module of every installed plugin that has one.
6. How to typecheck: run `/plugin-types` inside a session in the mod folder, then `npx tsc -p .`. Never commit `.claude/types/`.
7. How to get the badges once the repo is public: the nightly scan at https://github.com/karanb192/awesome-claude-code-mods finds any public repo that ships `hooks/hooks.json` with a `modules` key. The badge URLs are `https://raw.githubusercontent.com/karanb192/awesome-claude-code-mods/main/badges/OWNER--REPO--NAME-reach.svg` and the same with `-validates.svg`, where `NAME` is the `name` from `plugin.json`. To be listed sooner, add `owner/repo` to `data/seeds.txt` there by pull request.

## Brainstorm mods

### 1. Start from the workflow problem

Ask for, or take from the request, the moment in the user's day that hurts. One sentence. Do not start from what mods can do.

### 2. Fetch what exists

Run the bundled script:

```sh
node <skill-dir>/scripts/list-mods.mjs            # every mod, newest scan
node <skill-dir>/scripts/list-mods.mjs pr ci      # keyword filter, all words must match
```

It reads https://raw.githubusercontent.com/karanb192/awesome-claude-code-mods/main/data/mods.json and prints name, reach, hooks, calls, URL and description per mod. Without Node, fetch that URL with `curl` and read `mods[]` where `kind` is `mod`. If the fetch fails, say so. Then use the categories known to be covered on 2026-09-15: usage and context dashboards, games and breathing bands above the prompt, PR and CI trackers, secret redaction, mermaid rendering, memory layers, a subagent tree pane.

### 3. Propose 10 to 20 ideas

One row per idea, every column filled:

| Column | Content |
|---|---|
| Idea | one line |
| Trigger | the event with its matcher |
| Benefit | what the user gets, one line |
| Calls | the `$` verbs, from `references/nouns.md` |
| Reach | L0 to L3, from the calls |
| Risk | privacy or security, one line |
| Form | mod, classic shell hook, or external tool, with the reason |
| Exists | the mod that already does it, with its URL, or "nothing on the list" |

Form rules: a classic shell hook when one command on one classic event does the job. An external tool when the job does not need the session's events or `$`. A mod otherwise.

### 4. Rank

Rank by benefit per unit of reach. Two ideas with equal benefit: the lower reach wins. An idea that a listed mod already does ranks below every idea that nothing does. The one exception is a proposal that names a concrete gap in the existing mod. End with the ranked table and one pick, with the reason in one sentence. Offer to build the pick with the Build mode.

## Output contract

Every Build reply contains a plan before code, validator output after code, a behavioural check after the validator, and a threat model after the validator. Every Brainstorm reply contains the fetch result count, the table, the ranking and one pick. A reply that skips a stage says which stage it skipped and why.

## References

- `references/events.md`: every engine event, its payload and result, `next`, the five tiers, failure and recursion rules.
- `references/nouns.md`: every `$` noun and verb with its reach level and scanner label, the visibility rules, signatures seen in shipped source.
- `references/gotchas.md`: what shipped mods learned, each attributed to its README with the URL, plus the validator errors reproduced on 2.1.272.
- `references/threat-model.md`: the five-line template with two worked examples.
- `references/templates.md`: plugin.json, hooks.json, register.ts shapes, tsconfig, the README section.
- `references/testing.md`: official test-kit patterns for a simple hook, a side effect and a clock. A passing example is `plugins/fable-pin/tests/register.test.ts`.
- `references/composing.md`: adding a noun to `$` with its type contract, an organization's three control points, and options from the manifest.
- `references/reading.md`: every link this skill relies on.
- `references/official-sources.md`: which official source answers which question, and the source order.
- `scripts/footprint.mjs`: validator wrapper, reach grader, plan diff.
- `scripts/list-mods.mjs`: fetch and filter the nightly scan.
