Claude Code has a new kind of plugin. The old kind is a shell script that Claude Code calls out to. The new kind is TypeScript that Claude Code loads into its own process. Anthropic named these plugins Claude Mods on September 9. Same user, same credentials. Set CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 and Claude Code can load the hooks module from every installed plugin that has one.
On September 15, I ran the scanner across 92 candidate repositories. After fixtures and copies of Anthropic's three built-ins were removed, 31 mods remained. The validator found that 14 can run host processes, 4 can write files, 7 can read files, and 4 can reach the network. It also found that 13 hook every tool call, 11 hook every prompt, and 1 fails on Claude Code 2.1.272. No mod code ran during the scan.
A Claude Mod is a Claude Code plugin whose hooks are TypeScript functions loaded into the Claude Code process. Every side effect goes through one object, $. The loader inventories $.noun.verb calls before code runs, and claude plugin validate prints that inventory. The nightly scanner puts it on every row of awesome-claude-code-mods, with badges for reach and validation version. A footprint is capability, not a verdict.
Hooks are infrastructure now
Spotify reported around 90 percent bulk-read savings on a Java monorepo from two PreToolUse hooks (reported, not independently confirmed), while Anthropic wrote "We're now committed to shipping function hooks" and named them Claude Mods. iTerm2 3.7 shipped a Claude Code session-status integration, and Boris Cherny posted a Tetris mod someone else built.
A mod is a plugin with a hooks module
The whole design fits in one signature. A hooks module exports register(on), and every hook it registers looks like this.
export function register(on) {
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
if (e.command.includes("rm -rf /")) return { deny: "no" }
const r = await next(e) // every hook beneath, then core
return { ...r, text: redact(r.text) } // refine on the way up
})
}
e is the event. next(e) runs every hook registered beneath you and then the engine itself, Koa style. And $ is everything a hook can see or do. Files, processes, the network, the model, the screen, all of it is a method on $, and there is no other door. The architecture doc is explicit that this is settled at load time, before anything runs. A call is always spelled $.noun.event(...), and "a module that manipulates $ any other way fails claude plugin validate, such that a plugin's side effects is statically determinable."
I checked the promise on my own machine first. A throwaway mod with one session.start hook that writes a file. With the flag set, the file appeared before the model was even called. Without the flag, same binary, same minute, nothing. Then I ran the validator on a real mod.
$ cd cc-pr-tracker && claude plugin validate .claude-plugin/plugin.json
❯ ./register.tsx hooks: session.start, prompt.submit, tool.call{tool=Bash}, turn.complete, ui.render{component=AbovePrompt}, ui.render{component=Pane}
❯ ./register.tsx calls: $.clock.after, $.clock.every, $.clock.now, $.env.get, $.fs.exists, $.process.run, $.ui.close, $.ui.invalidate, $.ui.log, $.ui.open, $.ui.resolve, $.ui.toast
✔ Validation passed
The output tells you that cc-pr-tracker sees every prompt you type and every Bash call, reads environment variables, and runs processes. Which is what a PR tracker built on gh has to do. No mod code ran to learn that.
To try one, set the flag for a single session and install cc-arcade, the Tetris mod Boris amplified. A minimal mod is three files, if you would rather write your own.
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude
/plugin marketplace add sezaakgun/cc-arcade
/plugin install cc-arcade@cc-arcade
What the scan found
I grade each mod by the widest thing its $ calls can touch. L0 only draws and remembers ($.ui, $.store). L1 reads, meaning files, env vars, settings or the transcript. L2 writes files, runs processes or drives Claude through $.model, $.agent.spawn or $.prompt.submit. L3 calls $.http.fetch. The rules are forty lines and tested.
| Scan of September 15, 2026, Claude Code 2.1.272 | Mods |
|---|---|
| Candidate repositories from code search | 92 |
| Mods, after dropping fixtures and built-in mirrors | 31 |
| Can run host processes | 14 |
| Can read files | 7 |
| Can write files | 4 |
| Can reach the network | 4 |
| Hook every tool call | 13 |
| Hook every prompt | 11 |
| L0 / L1 / L2 / L3 | 12 / 2 / 13 / 4 |
| Fail to validate on this version | 1 |
Every game and every breathing band sits at L0 with $.store and $.ui and nothing else. cc-arcade hooks every tool call, because the pet eats your commits. The most-starred mod, Mindful-Claude at 56 stars as of September 15, hooks the turn boundaries, its own command and its own band, and nothing you type. At the other end, cctop reads and writes files, runs processes, reads env vars and hooks every tool call, which is roughly what a btop for Claude Code needs to do. The four that reach the network are lcm, autotel, honmoon-redact and pr-bridge-watch, and two of those are telemetry and CI bridges, where a network call is the feature.
The one that fails is signet-eval's adapter. Its module still says on("PreToolUse", ...), and the validator rejects it with a message that spells out the new spelling. Its own description says it was written for 2.1.263. On 2.1.272 it does not load. That is why every row also records the version it validated on, and why the scan reruns every night against whatever Claude Code is current.
What a footprint is not
It is static. The validator inventories source, so the footprint is what the code declares. What happens once the code is loaded is a separate question, and the scan has no answer for it yet. A mod that calls $.process.run might run gh pr view or might run anything else, and the scan cannot tell you which. And it carries no judgment. Eleven of the 31 can persist state, fourteen can run processes, and for most of them that is the whole point of the mod.
What the footprint gives you is the same view an organisation gets. The cheat sheet Anthropic published with the September 9 update says orgs govern mods by admission. A hook on plugin.register sees each plugin's static uses and may refuse it. The built-in sec-default mod seats outermost on managed machines and Team or Enterprise plans, per Anthropic's own README for it. On a personal laptop nothing seats there. This list is the admission view for the rest of us, one row per mod, before you install it.
How the scan runs, and how to add yours
Four scripts, no dependencies beyond Node and the claude binary. Discovery searches GitHub, the scanner clones each repo shallow and runs the validator on every plugin with a hooks module, the grader turns the printed calls into a level, and the renderer writes the tables, badges and scoreboard page. A GitHub Action reruns it nightly and opens a pull request only when a mod appeared, disappeared, changed footprint, stopped validating, or the Claude Code version moved.
The part I have not solved is runtime. The design says a hook on * sees every event, including every $ call every other plugin makes, at its own position in the chain. That is an audit log in one function, and it would turn "can run processes" into "ran gh pr view four times this session". I am building that one next, and when it exists the static column and the runtime column will sit side by side on the same row. If you have a better way to measure what a mod does without executing untrusted code first, the issue tracker is open.
References
- awesome-claude-code-mods (the list, the scanner, and
data/mods.jsonwith every row's raw validator output) - Function Hooks proposal and community updates, anthropics/claude-code issue 91870
- Function Hooks: Core Architecture (PDF, Anthropic, August 2026)
- The $ cheat sheet (Anthropic, September 9, 2026)
- Anthropic's built-in mods (diff, sec-default, telemetry)
- Portal by Spotify cut my Claude Code token usage by 90% (Spotify Engineering, September 3, 2026)
- iTerm2 3.7.0 changelog