explainx.ai0k
TrendingAI News TodayPathwaysSkills
Pricing
explainx.ai

Upskill in AI — 16 free pathways, live workshops & bootcamps, and 50+ courses from practitioners. Plus the skills, tools, and MCP servers to practice on.

follow us

follow on google

Add explainx.ai as a preferred source

corporate training

support@explainx.ai

get started

Find your pathTake Free Evaluation

community

Join the community

learn

mind: share how you thinkpathways — start freeworkshopsbootcampscoursescompare Explainxcertificationsmock testsexplainx universitycorporate traininglearn skills & mcp

discover

skillsmcp serversexplainx mcptoolsmdx readeragentsllmsdesignsdictionarypeopleagi trackerfelony benchranks

company

aboutvisionmissionteaminstructorsteach on explainxpartnershipscommunityhackathonscareers

content

daily AI newsstate of AI — live resultsblogreleasespromptsgeneratorsresource libraryfor LLMsexplainx.ai kids

solutions

all solutionsdeveloper upskillingmarketing upskillingproduct manager upskillingleadership upskilling

newsletter · weekly

Get AI news, tools, and insights in your inbox.

supportcontactprivacytermsdata rightshow we create contentsubmission guidelines

© 2026 AISOLO Technologies Pvt Ltd

explainx.ai

On this page

  • TL;DR
  • What changed since the September GitHub-issue teaser
  • How a Claude Code mod actually works
  • How this differs from settings hooks (and from skills)
  • Claude Code already uses mods for /diff and AGENTS.md
  • The shortcut: describe the mod, let Claude write it
  • Build Token Weather without pasting the whole module
  • Validate, test, then share with /plugin
  • Blast Radius: hold a risky Bash call — it is not permissions
  • Replay Theater: /replay the last turn's edits
  • Trust warning: same access as Claude Code
  • Four habits that keep a mod from fighting you
  • What people are asking
  • Honest limitations
  • What to build next (from the official idea list)
  • Related reading
← Back to blog

explainx / blog

Claude Code Mods: Official TypeScript Plugin Guide

Claude Code, Mods, Plugins, TypeScript, Guides

Claude Code 2.1.287+ ships mods on by default: JS/TS plugins that observe, rewrite, or answer events. How to build and share one.

Oct 2, 2026·13 min read·Yash Thakker
add explainx.ai
go deep
Claude Code Mods: Official TypeScript Plugin Guide

On October 1, 2026, Addy Osmani published Getting started with Claude Code mods on claude.dev — the official how-to, not another GitHub-issue teaser. Two weeks earlier, Boris Cherny's "Claude Mods are landing now" post pointed at anthropics/claude-code#91870 and community demos. That September 14 thread was the preview. This is general availability: Claude Code 2.1.287 or later, mods on by default, JavaScript or TypeScript modules that ship inside plugins.

The launch thread on X (tweet 2105721434807083061) crossed about 1.3 million views, with follow-ups for Token Weather, Blast Radius, and Replay Theater. Boris's line on the same day is the product thesis: customize by prompting; share as plugins.

XSource postOpen on X ↗

If you already use Agent Skills, you do not replace them. You get a second axis: rewrite what Claude Code does, and draw UI the session did not have before.

Weekly digest3.5k readers

Catch up on AI

Curated AI updates on agents, skills, and MCP — delivered to your inbox. Unsubscribe anytime.

TL;DR

table · 2 cols
QuestionAnswer
What shipped?Official mods API and getting-started guide (Addy Osmani, Oct 1, 2026)
Minimum version?Claude Code 2.1.287+. Mods are on by default
What is a mod?A plugin whose behavior is a JS/TS module with register(on, options)
What can a hook do?Observe, rewrite, or answer (return without next)
Runtime?Own sandbox. No DOM, no Node. Everything outside goes through $
Same as settings hooks?No. Settings hooks are per-event shell + JSON. Mods stay loaded
First-party examples?/diff and AGENTS.md are themselves mods
Teaching sample?Token Weather — about 80 lines, context-window forecast above the prompt
Risky commands?Blast Radius holds rm -rf / git reset --hard style Bash. Not a permission system
Review last turn?Replay Theater — hint after edits, press r or /replay
How do I ship?Same as any plugin: marketplace + /plugin + /reload-plugins
Trust model?Same access as Claude Code. Read the repo. Install only from people you trust

What changed since the September GitHub-issue teaser

explainx.ai covered the community extension teaser when the only public source was an issue thread and three demos (Tetris, Mindful Claude, a CI panel). That post is still useful as a snapshot of the first 24 hours. It is not the spec.

As of the official guide:

  • There is a documented hook shape, a $ host API, and a validate/test CLI.
  • Anthropic says some of Claude Code's own features are mods — including AGENTS.md support and the /diff pane beside the conversation. Source with tests lives in the public anthropics/claude-code repo under mods/.
  • You write one module per mod. The folder is a normal plugin (.claude-plugin/plugin.json). hooks/hooks.json lists that module under modules.
  • Sharing is not a new protocol. If you already know /plugin, you already know the distribution path.

The API can change between releases. When Claude Code loads a mod, it writes type declarations for your build into .claude-plugin/types/. Treat those generated types — not a blog post — as the contract.

How a Claude Code mod actually works

A Claude Code mod is a plugin whose behavior lives in a JavaScript or TypeScript file:

  1. The folder is a normal plugin, with .claude-plugin/plugin.json.
  2. hooks/hooks.json names exactly one module under modules.
  3. The module exports register(on, options). Inside it, on(event, matcher?, hook) adds a hook.

Every hook has the same three arguments:

javascript
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
  // $    mods API: ui, session, state, store, fs, process, clock, http, tool, command, model, ...
  // e    this event's input, as plain data
  // next passes e to the other plugins and then to Claude Code
  return next(e);
});

Hooks form a middleware chain. Yours runs, next(e) hands the event to the next plugin, and at the bottom Claude Code does what it would have done anyway. You pick one of three moves:

table · 3 cols
MoveHowExample
Observeconst r = await next(e); then look; return rRecord every file edit. Read usage after a turn
Rewritereturn next({ ...e, command: safer })Change what the rest of the chain sees
AnswerReturn { deny: "…" } without calling nextRefuse a tool call. Serve a command or a tool yourself

Events cover tool calls, the submitted prompt, turns starting and finishing, session start and end, slash commands, and ui.render — every piece of the interface as it is drawn. The module runs in a sandbox of its own: no DOM and no Node. File I/O, processes, HTTP, UI, and session data all go through $.

That is the line that matters for security reviews. A settings hook is a subprocess you can reason about as "this script ran." A mod is in-process with Claude Code's session, with a typed host API instead of require("fs").

How this differs from settings hooks (and from skills)

Claude Code already had lifecycle hooks: a settings hook runs a shell command for each event and passes JSON over stdin and stdout. Useful. Stateless across reloads unless you invent your own store. No live UI.

A mod is loaded once and stays in the session. It can:

  • Keep named values in $.state (survives hot reload; a module-level let does not).
  • Draw UI that updates as events happen (AbovePrompt, panes, status, toasts).
  • Call back into Claude Code: open a pane, run a process, register a slash command, or register a tool the model can call.

Skills still win when the model should pull a packaged workflow. MCP still wins when the model should call an external tool. Reach for a mod when you need to sit on the event chain or paint the session.

Claude Code already uses mods for /diff and AGENTS.md

The official guide is explicit: some first-party features are mods. We already wrote up AGENTS.md support in 2.1.277 as the first public, shipped example — a built-in mod with source under mods/agents-md. The October guide adds /diff to that list: the pane beside the conversation is a mod, not a mysterious core widget.

That closes the September question of "is this only for Tetris?" Anthropic is using the same surface for product features. Your plugin is the same shape as theirs, with the same validate/test path.

The shortcut: describe the mod, let Claude write it

You do not need to memorize the API to get a first band on screen. Start claude and describe the readout. The official Token Weather prompt only specifies what to show (icon, percent, tokens used / window, last-12-turn sparkline, last-turn delta) and that it should update after every turn. Claude Code's built-in guide for writing mods covers where to keep state, how to run claude plugin validate, and which events to hook.

Claude asks once whether to turn on hot reloading for the session. Allow it. Tweaks ("make Storm start at 70%", "add dollar cost") reload in place. A session-only folder is cleaned up later — copy it out and install it like any plugin if you want to keep it.

Boris's "customize by prompting" line is this loop, not a slogan. The rest of this post is what to check after Claude writes the files.

Build Token Weather without pasting the whole module

Token Weather is the official first mod: a live forecast of the context window in the band above the prompt. It is about 80 lines. After each main-loop turn it reads usage and paints weather:

table · 2 cols
UsedForecast
under 25%Clear
25–49%Cloudy
50–74%Showers
75–89%Storm
90% and upCompact soon

Confirm the binary first:

bash
claude --version   # 2.1.287 or later

Layout (names match the official tutorial):

text
token-weather/
├── .claude-plugin/
│   ├── plugin.json
│   └── types/            # written by Claude Code on load
├── hooks/
│   ├── hooks.json
│   └── token-weather.mjs
├── types/
│   └── index.d.ts
└── tests/
    └── token-weather.test.ts

plugin.json is a normal plugin manifest (name, version, description, author). Point it at a types contract with "types": "./types/index.d.ts". hooks/hooks.json is only:

json
{
  "modules": ["./token-weather.mjs"]
}

The band above the prompt is AbovePrompt. Claude Code draws nothing there by default, so it is a good first ui.render target. Constructors are not globals — you get them from $.ui.resolve(e) because each surface supports a slightly different set. JSX works with h as the factory.

Load it:

bash
claude --plugin-dir ./token-weather

The folder is watched. Saves reload the module with no restart.

Keep readings in $.state, not in let

$.session.usage() returns the same figures as the status line. context.tokens is the input the last response was answered over, context.window is the model window, context.percent is one over the other. The call is cheap: it only sends a token-count request if you ask for a breakdown.

Take a reading on session.start and on turn.complete (skip subagents with e.agentId). Both hooks should await next(e) first, then observe.

A module-level array looks convenient and dies on hot reload. register runs again, session.start fires again, and your sparkline history is gone. Put history in $.state with a { plugin, key } handle. Declare that key on PluginState in types/index.d.ts or claude plugin validate fails with an error that names the missing contract field.

While a render hook runs, $.state.get subscribes that drawing. Later $.state.set redraws the band. You never call $.ui.invalidate for this pattern.

Copy three habits from the official module (not the whole file):

  • Component props live on e.props. hasSurvey means a survey wants the band — yield with next(e). bodyColumns is the band's real width (narrower when a pane is docked). Only e.component, e.surface, e.requestId, and e.viewport sit at the top level of e.
  • Pass when you have nothing to draw. return next(e) gives the band back to Claude Code and other mods.
  • Use single-width symbols, not emoji, so columns line up in every terminal font.

We are not reprinting Token Weather's full source here. Read the official guide or the module Claude writes, then change the forecast table.

Validate, test, then share with /plugin

claude plugin validate ./token-weather reads the manifest and source the way Claude Code will. A passing run reports which events you hook, which $ methods you call, and which state keys you read and write.

claude plugin test runs the plugin's *.test.ts files against the real runtime. Hooks a test registers with on run after the mod and stub what Claude Code would answer — so you can fake $.session.usage() and assert the band text without burning a real context window.

A marketplace can be a folder with .claude-plugin/marketplace.json listing { "name": "token-weather", "source": "./token-weather" }:

bash
claude plugin marketplace add ./my-mods
claude plugin install token-weather@my-mods --scope user

From a GitHub repo, the in-session path is three commands:

text
/plugin marketplace add your-org/my-mods
/plugin install token-weather@my-mods
/reload-plugins

If it does not show up, restart Claude Code. The Claude directory accepts plugins that include mods; submit at the directory manage URL Anthropic documents on claude.dev.

Blast Radius: hold a risky Bash call — it is not permissions

Token Weather only watches and draws. Blast Radius steps into tool.call on Bash. When the command text looks like rm -rf, git reset --hard, git clean, a force push, or a database migration, it holds the call, measures what would change (git status, git clean -n, du, and similar via $.process.run with an argv array), and opens a pane with Proceed (1) and Cancel (2). Cancel returns a deny object so Claude sees a refusal with a reason. Proceed calls next(e).

Teaching core (not the full classifier):

javascript
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
  const risk = classify(String(e.command ?? ""));
  if (risk === null) return next(e);

  const report = await measure($, risk, await $.session.cwd());
  const opened = await $.ui.open({ id: "blast-radius", title: "Blast Radius", focus: true });
  // wait on $.process.run(["sleep", "0.25"]) until a button sets the decision
  if (held.decision === "proceed") return next(e);
  return { deny: `Blast Radius held this command: … ${report.summary}` };
});

Worth copying:

  • $.process.run for dry runs. Arguments as argv, so a path is not interpolated into a shell string.
  • Holding a call. A hook gets 10 seconds of its own time per dispatch. Time spent waiting inside a $ call does not count. Loop on short sleeps until onPress sets a decision; give up when next.signal aborts (Esc).
  • Buttons with hotkeys. Click, Tab+Enter, or the digit.
  • Degrade to the band. If $.ui.open returns isPlaced: false, draw the same report on AbovePrompt.

It is a safety net, not a permission system. It reads command text. $(…), aliases, and scripts that call rm get past it. Use permission rules for a hard block.

Replay Theater: /replay the last turn's edits

While a turn runs, Replay Theater observes every Edit and Write: file, text before, text after. It never blocks an edit. On turn.start it clears a pending list (main loop only). On turn.complete it freezes that list as the replay. On session.start it registers a slash command; on command.run for replay it opens the pane.

javascript
on("session.start", async ($, e, next) => {
  const r = await next(e);
  await $.command.register({
    name: "replay",
    description: "Step through the last turn's file edits",
  });
  return r;
});
on("command.run", { command: "replay" }, async ($, e) => ({
  text: (await openReplay($)) ? "Replaying" : "No edits",
}));

After the turn, a hint appears above the prompt. Press r or type /replay. The pane walks diffs with numbered steps and Prev / Next / Close. For a Write, $.fs.read captures old contents just before the write lands. Placement is the surface's job: wide terminals dock a pane; at 80 columns the same tree draws inline above the prompt.

Pair turn.start / turn.complete and filter e.agentId if you do not want subagent edits in the same reel.

Trust warning: same access as Claude Code

A mod is code that runs inside Claude Code on your machine, with the same access Claude Code has, written by its publisher, not Anthropic. Install the way you install a package: read the repo, prefer people you already trust. Nothing is installed until you run the plugin command. After that, observe/rewrite/answer is enough to refuse tools, rewrite Bash, or read files through $.

Do not treat claude plugin validate as a security review. Validate checks the contract (hooks, $ calls, declared state). It does not prove the publisher is honest.

Four habits that keep a mod from fighting you

  1. Lean on the types Claude Code writes into .claude-plugin/types/. They are the reference for every event, every $ method, and every element's props. tsc -p should just work.
  2. Read props from e.props. hasSurvey, bodyColumns, and the rest are not on e itself.
  3. Plan for hot reload. Every save re-runs register and session.start. Durable data goes in $.state.
  4. When a drawing does not show, read the log. claude --debug and look for a hook that returned a tree that does not validate.

When you have nothing to teach the band, next(e). When you answer, skip next on purpose — that is how Blast Radius Cancel works.

What people are asking

Do I turn mods on? No. On 2.1.287+ they are default-on.

Can I write TypeScript? Yes. Official samples use .mjs for the first module and .d.ts / *.test.ts beside it. The runtime is still the sandbox, not Node, so you do not import fs.

Where do /diff and AGENTS.md fit? They are first-party mods. Custom project-instruction mods were previewed when AGENTS.md shipped; the October guide is the general plugin path.

Is Token Weather on the directory? Treat the official write-up as a teaching sample (~80 lines). Copy the habits, not a copyrighted dump of the whole file, then change the forecast.

Does Blast Radius replace dontAsk / permission modes? No. Pair it with permission modes.

Can I register my own slash command? Yes — $.command.register on session.start, then answer command.run. That is how /replay exists.

Hot reload wiped my sparkline. You stored history in a module variable. Move it to $.state and declare it on PluginState.

Honest limitations

  • API drift. Types on disk are per build. A mod that validates on 2.1.287 may need edits after an upgrade.
  • No Node, no DOM. If your idea is "just npm install a chart library," you will rewrite it against $ and $.ui.resolve.
  • One module per mod in hooks.json. Split logic with functions in that file, not a second module entry, unless a later release changes the rule.
  • 10-second hook budget for your own JS. Long waits must sit inside $ calls (sleep, UI, process).
  • Blast Radius is bypassable by design of text classification. Do not file it as a compliance control.
  • Marketplace ≠ review. Directory listing is discovery, not an audit.
  • Subagent turns need an e.agentId policy or your band and replay will mix contexts.

What to build next (from the official idea list)

  • A cost or rate-limit meter from $.session.usage(), via $.ui.status.
  • A prompt.submit hook that prepends team conventions.
  • A pane of files Claude read this session.
  • A focus toast with $.ui.toast when a long turn finishes.
  • A tool.call guard for production kubectl contexts or terraform apply.

If you already maintain skills on explainx.ai's skills registry, keep those as model-invoked playbooks. Put session chrome and hard intercepts in a mod.

Related reading

  • Claude Code Mods: September community teaser
  • Claude Code commands: complete slash-command reference
  • What are Agent Skills?
  • claude.dev is Anthropic's developer hub
  • Claude Code now reads AGENTS.md
  • Claude Code function hooks
  • Claude Code permission modes
  • Top 25 Claude plugins (2026)
  • Official guide: Getting started with Claude Code mods
  • First-party mods source: anthropics/claude-code mods/

Specs, version numbers, and directory URLs are accurate as of October 2, 2026 (official guide dated October 1, 2026). Confirm claude --version and the types Claude Code writes into .claude-plugin/types/ before you treat any snippet as a contract.

Spotted something out of date? Let us know.
Yash Thakker

Written by

Yash Thakker

Yash is an AI expert with over 300K learners. Join his workshops →

View Yash Thakker in People in AI →

Related posts

Sep 27, 2026

What a Claude Code Task Costs on Opus 5.5

Anthropic's "40% less to run" line stacks a price cut on top of fewer tokens at Opus 5.5's medium default. Hold the token mix fixed and one illustrative Claude Code session falls from $3.50 to $2.40. This guide prices the knobs that move a single task more than that sticker: cache hit rate, turn count, effort, and /usage.

Sep 23, 2026

How to Actually Use Claude Opus 5.5: Anthropic's Own Prompting Playbook

Anthropic now publishes two layers of Opus 5.5 guidance: a Claude Code playbook from launch week and a deeper Platform doc for API integrators. Both agree on the big shifts — thinking is always on, medium effort is the new default, and vague "don't be generic" design prompts mostly swap one default for another — but the Platform page adds harness fixes for agents that stop early, silent long turns, and prompt injection in pasted email threads.

Sep 17, 2026

Ultracode in Claude Code: What It Actually Does and When to Use It

Ultracode is the highest setting on the Claude Code effort slider, but it is not an effort level at all. It sends xhigh to the model and separately hands Claude permission to write a JavaScript orchestration script for every substantive task, fanning work out across dozens of parallel subagents.