There’s a hook in this repository that cannot fail, and it happens to be the one that checks my articles. blog-gates.mjs runs four editorial checks after every write, and its last line is:
main().catch(() => {}).finally(() => process.exit(0));
Every error disappears into that empty catch. If it finds a made-up number, it prints that to stderr and exits with zero. Claude Code sees a successful hook.
In early September I read a proposal that would fix exactly that. An issue in the Claude Code repo titled “Function Hooks - make plugins 10x more powerful”, asking the community whether it should be built. I went looking in my own install and found the engine already sitting in the build from August 29, five days before the question was asked. On October 1 it shipped under a different name: mods.
I’ve written before about automating with hooks as they existed until now, in the piece on the plugin that designs your Claude Code setup for you. This one is about what’s been added, and about one sentence in the announcement I had to read twice.
What a mod is
Until now, a hook was a shell command. On an event, Claude Code starts a script, hands it JSON on stdin and reads back the exit code. A mod is a TypeScript or JavaScript module inside an ordinary plugin. hooks/hooks.json gets one modules entry, and the module exports a function register(on, options). Inside it you attach functions to events:
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
return next(e);
});
Three arguments. e is the event as plain data. next passes it on to the next plugin and finally to Claude Code itself. $ is everything a mod is allowed to do: the interface, the session, files, processes, the network, the model.
A mod loads once and stays in the session. So it can remember what happened earlier, and it reloads itself when you save the file. A shell hook starts fresh on every event and knows nothing.
Mods stack like middleware. Whoever loads first sees an event first and the result last. That gives three moves:
| move | what the mod does | shape |
|---|---|---|
| observe | let the rest run, then read the result | const r = await next(e) |
| rewrite | pass on a changed event | return next({ ...e, command: safer }) |
| answer | decide on its own, skip the rest | return { deny: "..." } |
With that, a mod can rewrite a prompt before the model sees it, block or retry a tool call, answer a permission request itself, and strip secrets from tool output before Claude reads them. Drawing is an event too, so a mod can replace part of the interface or add buttons to it.
For my own hook, it means failure becomes something you can express. A function that returns a value can return something other than “all good”.
Anthropic uses the model itself. The repo now has a mods/ folder with four mods built into Claude Code: /diff, AGENTS.md support, telemetry, and sec-default. I’ll come back to the last two.
What changed between proposal and release
The issue is still open. Its title now reads “Mods - make Claude 10x more extensible”. On September 8 it had 143 comments and 126 emoji reactions; on October 2, 233 comments and 218 reactions, 130 of them thumbs up and still zero thumbs down. The announcement thanks everyone who weighed in.
What I found in my own builds on September 8:
| build | file date | hits for functionHooks |
|---|---|---|
| 2.1.233 | August 15, 2026 | 0 |
| 2.1.241 | August 24, 2026 | 0 |
| 2.1.251 | August 29, 2026 | 138 |
The issue is dated September 3. The engine was there before anyone was asked whether it should exist. Building a prototype so you can demo a proposal is normal work, so I’m not accusing anyone. But whoever commented in September was shaping the finish of something that already existed.
Mods need version 2.1.287. My install is on 2.1.283, the September 28 build, which already contains functionHooks 151 times and sec-default as well. I can’t run mods yet.
The caveat
On your own machine, nobody stands at the door. The announcement says it plainly: mods have the same access to your machine as Claude Code itself, they aren’t sandboxed, and you should only install them from sources you trust. The protective mod, sec-default, loads first, but only on Team and Enterprise plans or on a machine with managed settings. If you’re on Pro or Max on your own laptop, that first seat in the chain is empty. On top of that, sec-default “adds no policy of its own”, according to its README. It protects an organization’s rules, such as deny rules and managed settings. Reading files, starting processes and going online all pass straight through. The launch guide says a mod runs in “a sandbox of its own”, and that’s also true: the module itself has no Node. But $ hands it fs, process and http. Both are true.
A plugin update is now a code update. I have eleven plugins installed from nine marketplaces. None of them ships a mod today; I searched the hooks.json of all eleven for modules and found nothing. But a mod arrives inside an ordinary plugin, so the next update of any of those eleven could carry one, with access to everything Claude Code sees. With a shell hook you could read twenty lines of bash. A mod that seats itself first sees every event, including what other plugins do. The announcement pitches that as a feature for companies: a mod that loads first can record every call every other mod makes. For someone at home, the same seat is a place to listen in.
The helpful mod costs you telemetry. The same release adds “You should know”, a built-in mod where a side agent watches the session and flags things you or Claude might miss. It only works in sessions with telemetry on. If you turned analytics off, usually for a reason, you don’t get it. Telemetry itself is now a mod too. Its README says it sends nothing when analytics are off and refuses calls from plugins you install yourself. That’s handled carefully. It still shifts the trade-off, because the feature that protects you from mistakes now sits behind the switch that governs your privacy.
What I’m doing next
I haven’t tested mods myself yet. At the time of writing this machine runs 2.1.283, and mods need 2.1.287. Everything above about how they work comes from the announcement, the guide and the source code, not from my own sessions.
I do plan to test them. The obvious first candidate is blog-gates.mjs, which still ends on the same empty catch. As a mod, it could actually report that something went wrong. Whether that’s worth it I’ll only know once I’ve tried. I’d be trading a 92-line script I can read end to end for a module in a chain whose order depends on whatever else is loaded. Once I’ve done it, I’ll write a follow-up on what worked and what didn’t.
The check I ran today is one anyone can repeat: search your installed plugins’ hooks.json files for modules. It’s a single grep, and without sec-default it’s the only place on your own machine where you’ll see a mod coming in.
Frequently asked questions
What’s the difference between a hook and a mod?
A hook is a shell command that starts fresh on every event, gets JSON on stdin and answers with an exit code. A mod is a module that loads once, stays in the session, can keep state, can draw interface and can rewrite or answer events itself. Hooks still exist; a mod is an additional kind, named under modules in the same hooks.json.
Do I get sec-default’s protection if I work alone?
No, unless you’ve set up managed settings yourself. sec-default loads for Team and Enterprise organizations and on machines with managed settings. Even there, it protects the organization’s rules, not your files or your network.
Should I turn on “You should know”?
That depends on how you feel about telemetry. The mod is off by default, you enable it with /plugin enable cc-plugin-you-should-know@builtin, and it only works in sessions with telemetry on. If you’ve turned that off, it won’t run.
Can I see whether a plugin contains a mod?
Yes. A mod is listed in a plugin’s hooks.json under the modules key. Installed plugins live in ~/.claude/plugins/cache/, and searching those files for "modules" shows which ones ship one.
Sources
- Anthropic, “Customize Claude Code with mods”, claude.com/blog, October 1, 2026, accessed October 2, 2026.
- Addy Osmani, “Getting started with Claude Code mods”, claude.dev, October 1, 2026, accessed October 2, 2026.
- Claude Code changelog, version 2.1.287, accessed October 2, 2026.
- The
mods/folder in anthropics/claude-code, READMEs forsec-defaultandtelemetry, accessed October 2, 2026. - GitHub issue anthropics/claude-code#91870, opened September 3, 2026, accessed September 8 and October 2, 2026.
- My own measurements in
~/.local/share/claude/versions/on September 8 and October 2, 2026.
Checked on October 2, 2026. The comment counts come from that day’s GitHub API and keep climbing. I couldn’t run mods myself, since my install is on 2.1.283 and mods need 2.1.287; what’s described here about their behavior comes from the announcement, the guide and the source in mods/. The announcement says mods aren’t sandboxed; the guide says the module runs in its own sandbox without Node. I follow the announcement on the security question, because the guide itself describes $ passing on files, processes and network access.
