Hooks let a shell command run at fixed points in Claude Code’s loop. A mod goes a level deeper: it’s a plugin whose JavaScript or TypeScript functions run inside Claude Code, called on its events: a tool call, a submitted prompt, a turn, even a piece of the interface being drawn. Because it stays loaded, a mod can keep state between events, add a pane beside the transcript, or register a slash command that runs your code at once, with no Claude turn.
How a mod handles an event
A mod exports register(on). Each call to on attaches a function to an event, optionally narrowed by a matcher:
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
if (/git push .*--force/.test(e.command)) return { deny: 'Push to a new branch instead.' }
return next(e)
})The handlers for an event form a chain, like middleware. next(e) passes the event on to other mods and finally to Claude Code’s own behavior. So a handler has three moves: observe (do something, return next(e)), rewrite (call next with a changed copy), or answer (return a result without calling next, so the usual behavior never runs; above, the push is refused and Claude reads the reason). Anything beyond its own code (drawing, files, processes, the network, a model call) goes through the mods API, $.
Where it lives
A mod is three files: .claude-plugin/plugin.json, hooks/hooks.json with "modules": ["./register.js"], and the module itself. Try one for a session with claude --plugin-dir ./my-mod, share it through a marketplace like any plugin, or describe what you want and have Claude write it. Mods need Claude Code v2.1.287 or later and are on by default. Some of Claude Code’s own features, such as /diff, are built as mods.
Hooks run wherever the plugin loads, including claude -p, but what a mod draws appears only in the terminal and the Desktop app’s Code tab.
The catch
A mod is code running with your permissions and isn’t sandboxed. It can read your files and secrets, see every prompt and tool call, approve a call before you’re asked, and spend your usage. Install only from authors you trust, and before installing, run claude plugin validate ./some-mod: its hooks: and calls: lines list what the mod hooks and asks Claude Code to do. Started with --safe-mode, a session leaves installed mods out.
If a settings hook, a skill or an MCP server already does the job, reach for that first.