cd /news/ai-tools/claude-code-mods-explained-what-they… · home › topics › ai-tools › article
[ARTICLE · art-157670] src=pub.towardsai.net ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

Claude Code Mods Explained: What They Can Do That Hooks and Skills Can’t

Anthropic shipped mods in Claude Code 2.1.287, a plugin format in which a JavaScript or TypeScript file of event handlers runs inside the tool to change how it looks and behaves, available in the terminal CLI, the Desktop app's Code tab and the VS Code extension with no Node.js, bundler or build step. The author, who built a mod to surface a test-file edit buried on line 31 of a 33-line transcript, reports that mods differ from settings hooks, skills, status lines and MCP servers because those work from outside Claude Code while a mod runs inside it, and that Anthropic shares three sample mods — token-weather, blast-radius and replay-theater — in its claude-code-playground repository.

by read8 min views4 publishedOct 26, 2026

“All tests pass.” Claude Code said it at the end of a long turn, and the run was green. In the same turn, it had also edited tests/conftest.py.

Nothing in the transcript hid that. It sat on line 31 of 33 lines of reads, edits and test logs. I scroll past line 31 on most days. So when Anthropic shipped mods in Claude Code, the first thing I built was a mod that puts that line where I can’t miss it. This article is what I learned about mods while doing it: what they are, how the pieces fit, what a mod can reach on your machine, and the bugs that taught me the most.

A mod is a Claude Code plugin that changes how Claude Code looks and behaves. It is a JavaScript or TypeScript file of event handlers. Claude Code calls a handler when something happens: a tool call, a submitted prompt, a part of the screen about to be drawn. The handler can watch the event, change it, or take it over.

Mods arrived in Claude Code 2.1.287. They run in the terminal CLI, the Code tab of the Desktop app and the VS Code extension. You need no Node.js, bundler or build step: Claude Code loads the .ts file directly.

Claude Code already had ways to extend it, so the first question is when a mod is the right tool. The short answer: settings hooks, skills, status lines and MCP servers work from outside Claude Code. A mod runs inside it.

Being inside buys you five things the others can’t do:

If a settings hook already does the job, use the settings hook. I needed number 2, so a mod was the only option.

Everything in a mod is one of three things.

Events decide when your code runs. The ones I used most follow a turn, which is everything Claude does to answer one prompt:

Render sites are the places in the screen you can draw. A ui.render hook fires each time Claude Code is about to draw one, and you pick the site with a matcher such as { component: 'Spinner' }. Sites include ToolUse, ToolGroup, AssistantMessage, Spinner, TurnDuration, PromptHint, AbovePrompt (the band above the prompt) and Pane. Each site gets e.surface, which says whether the terminal, Desktop or VS Code is drawing.

$ is the mods API. Every hook gets it as its first argument. Its methods sit in namespaces: $.ui (open panes, notify, set the status line), $.session (messages, usage, the project root), $.store (a key-value store every session on the machine shares), $.clock, $.fs, $.process, $.http, $.model and more.

Here is a whole mod in that shape. It adds a line under each answer with the time and the share of context used:

import type { Register } from 'claude-code'export const register: Register = (on) => {  on('turn.complete', async ($, e, next) => {    const r = await next(e)    if (e.agentId !== undefined) return r  // skip subagent turns    const { context } = await $.session.usage()    const secs = Math.round(e.durationMs / 1000)    return { text: `${secs}s · context ${context.percent}% full` }  })}

Two habits from that example carry over to every mod. Call next(e) so the mods after yours still run. And check e.agentId, because subagent turns fire the same events.

You can ask Claude to write a mod: describe it in a session, and Claude writes the plugin. To work on one yourself, three commands do most of the work:

claude --plugin-dir .      # run Claude Code with your mod; a save reloads itclaude plugin validate .   # check the manifest and list every hook and callclaude plugin test .       # run the mod's tests

The reload on save is what makes mods fast to build. I change a render hook, save, and the row redraws in the session I already have open.

Anthropic also shares three sample mods in its claude-code-playground repository: token-weather (a forecast of your context window above the prompt), blast-radius (holds a risky command such as rm -rf and shows what it would change) and replay-theater (a /replay command that steps through the last turn's edits). Read them before you write your first one.

This part matters more than the API. A mod is not sandboxed. Once it loads, it runs with your permissions. It can read and write your files, start programs, make network requests, read your environment variables, see every prompt and tool call, and approve a tool call before you are asked. Turning on Claude Code’s sandbox does not cover it: the sandbox wraps the Bash commands Claude runs, not processes a mod starts.

So before you install a mod, read what it does. Clone it and run claude plugin validate on the folder. The hooks: and calls: lines list every event it handles and every API it calls, without running it. A mod that only draws rows should not list $.http.fetch or $.process.run. If you want to stop all mods, claude --safe-mode turns them off for one session, and "disableAllHooks": true in ~/.claude/settings.json turns them off everywhere.

The mod I built is TL;DR for Claude Code. It folds each turn into one row in Claude Code’s own style: the files Claude changed, the tests before and after, and what to check before you trust the result. Every raw line stays one click away. It is free and open source (MIT), and I’m the only author.

Here is how it maps onto the three parts:

The calls it does not make are the point. It makes no $.http or $.model calls and starts no processes. It reads everything from what Claude Code reports.

The row flags the edits that can fake a green run:

TL;DR reads each Bash command as text. It never runs or expands a command to learn what it did. I chose that for privacy and safety. The cost showed up fast. I wrote 30 edge cases, and the first version missed many: quoted paths, sudo tee, a cd before a write, mv into a folder, git restore, find -delete, and echo "npm test", which it counted as a test run.

Then a live turn wrote a script with S=/tmp/x.sh; cat > "$S" and the row showed nothing. Now the reader follows variables the command sets. My rule became: name what the text names, and drop what it can't name. A path built from $OUT drops out, because a row that says $OUT via Bash reads like a real file name.

I run a hook that wraps test output and prints its own summary line, such as Go test: 20 passed, 2 failed. The first parser didn't know that line. A failing run showed green. Pipes do the same: in go test | tail, the exit code is tail's. Now a failure in the output counts as a failure, whatever the exit code says. If your mod reports test results, test it against the wrappers you actually use.

A flag once quoted a whole heredoc command. The check line was 6,000 characters. Now each flag is one line, cut at the row’s edge, and it names the act, not the command: force push, skipped hooks. The commands behind it open in a pane.

The first version was a seven-row box with labels, buttons, a diff pane and hint lines. On a dark terminal the labels were almost invisible, and a 60-file refactor took nine rows of cut-off file names. Version 0.1.5 removed the box, the buttons, the diff pane and every hint line. A clean turn is now three lines, and the dot’s color is the verdict. It reads as part of Claude Code, which is what a mod that redraws Claude Code’s rows should do.

To get listed in Claude’s plugin directory, I moved every function that takes $ into the one hooks module. The draw code takes plain values. Now one file holds every call the mod can make, and anyone can check it in a few minutes.

My latest fix was not in the row. The context bar kept the old numbers after /compact, because it measured the transcript before the new one was in place. It looked right until the next request, so I didn't see it for days.

If you want to see a mod that redraws Claude Code’s own rows, install TL;DR inside Claude Code (2.1.301 or later, terminal only):

/plugin install tldr --marketplace kushalBanda/claude-tldr

The source is on GitHub at kushalBanda/claude-tldr. Read the hooks module and run claude plugin validate on it first. That's the habit I'd want for every mod you install, mine included.

If you build your own, start from Anthropic’s mods overview and the three sample mods. Pick one render site and one event, and get a single line on screen. Everything else grows from there.

Resources

What would you build first with a mod? If you found this useful, give it a clap and drop a comment. I’d love to hear your thoughts 🙌

Follow me here on Medium for more Claude Code deep dives, or connect on LinkedIn.

Claude Code Mods Explained: What They Can Do That Hooks and Skills Can’t was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.

── more in #ai-tools 4 stories · sorted by recency
── more on @anthropic 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/claude-code-mods-exp…] indexed:0 read:8min 2026-10-26 · —