Repository navigation
Description
Community Micro-Update: Oct 1, 2026
We're live! We're rapidly working down the latest elements of feedback folks have posted. Thank you all again for being amazing stewards of this new extensibility affordance. 🚀
Community Update: Sep 9, 2026
AI;DR: We're shipping in N weeks.
Thank you all for the positive feedback, and especially for the insightful, high-signal feedback that has materially shaped our design.
We're now committed to shipping function hooks, on the scale of weeks in lieu of days or months. As well, from a product perspective, we are going to be calling this functionality "Claude Mods". The engineering term of art 'function hook' will still exist as the documented implementation primitive Mods are built on. A mod is just a plugin that uses function hooks, nothing is changing there. Hopefully the ontology is not too confusing.
We're still rapidly iterating on the interface, design, etc. but much of the semantics are now set in place, and we don't anticipate as many breaking changes as our first week. Therefore, we've made available our source listings for our first three built-in mods, perusable here. Our intent is to take further extant features as they exist in CC today and migrate them to mod form.
Finally, I'm sharing here a cheat sheet reference that enumerates some affordances on v267/v268 (today / tomorrow). As well, I may now publicly acknowledge that any folks who want to test and give feedback may use CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude.
We are still iterating and it is only more important for the community to engage and give critical feedback to ensure this ends up in the right shape. Thank you all.
Original Post: Sep 3, 2026
Hi folks! ✨
AI;DR: Function Hooks let you modify CC very deeply, while still being safe through side-effect tracking over a parameterized $ object, and while composing neatly using a registration-order-based 'next' continuation model a la Express, or Koa.
I was asked to get feedback from the community regarding an internal proposal we have.
Basically, it would be neat if you could hook into CC using TypeScript functions a la Express (or Koa!), and basically change whatever you want. So that's what we're proposing. In the workplace, admins will be able to change what they want too, including your ability to change things, to a very fine level of programmatic detail.
You should even be able to modify how components get rendered. We use React across the board, so why not utilize that fact?
There's a technical architecture doc, that goes through some of the design. As well, I made a few videos for internal folks so they could see how it might feel, which I'm sharing here too. I've tried to ensure that they're informative and engaging.
Algebraically, this is just an effect-parameterized endomorphic continuation model for plugins.
If you want this, please engage here - the response from the community likely dictates whether this ships or not. The $ sigil isn't negotiable though, I follow a more ancient god.
Basic Series
You can either click on the thumbnails or the inline video toggles below. Each Basic and Advanced video is 60 seconds; the full Case Study videos are around 2 minutes long and show more comprehensive uses of function hooks to form cohesive features.
1-a-hook-as-a-function.mp4 |
2-a-hook-that-says-no.mp4 |
3-plugins-can-draw-now.mp4 |
4-admin-control-as-a-hook.mp4 |
⚠️ GitHub mutes videos by default, so be sure to turn sound on.
The four videos
⓵ A hook as a function
We introduce the ability for hooks to be TypeScript functions, with full type definitions and LSP support.
1-a-hook-as-a-function.mp4
⓶ A hook that says no
Function hooks may be used to restrict behavior, for safety and control, as you're used to.
2-a-hook-that-says-no.mp4
⓷ Plugins can draw now
You can hook onto components and modify their props or wrap their returned render nodes.
3-plugins-can-draw-now.mp4
⓸ Admin control as a hook
Admins can remove affordances from $ so that all plugins below cannot invoke that side-effect.
4-admin-control-as-a-hook.mp4
Advanced Series
5-order-is-nesting.mp4 |
6-press-a-plugins-button.mp4 |
7-every-event-at-once.mp4 |
The three videos
⓹ Order is nesting
Plugins nest like middleware. The first one registered wraps the rest, so admins prepend for control and append for defaults.
5-order-is-nesting.mp4
⓺ Press a plugin's button
Hooks catch interactions too. One hook on ui.press sees the same button pressed in the terminal and in the desktop app.
6-press-a-plugins-button.mp4
⓻ Every event at once
A single hook on * sees every event, including every plugin's own calls on $, so an audit log is one function.
7-every-event-at-once.mp4
Case Studies
8-one-sentence-one-plugin.mp4 |
9-change-what-claude-code-shows.mp4 |
The two videos
⓼ One sentence. One plugin
From one short ask, Claude writes, validates and loads a plugin that replaces secrets in tool output before the model reads them.
8-one-sentence-one-plugin.mp4
⓽ Change what Claude Code shows
A plugin hides sensitive values in Claude Code Desktop until you hover over them, so you can share your screen without showing your data.
9-change-what-claude-code-shows.mp4
AI usage disclosure
thumbnails are AI; demos and code are real, running on actual binaries

Activity
etrippler commentedon Sep 3, 2026
Looks sick. Ship it IMO.
aviseth commentedon Sep 3, 2026
This is sick! Can't wait to try it!
AdityaRon commentedon Sep 3, 2026
I run something in this space already — claude-code-harness, security/audit/context hooks built on today's command-hook model (PreToolUse/PostToolUse via settings.json + bash). A few places this architecture would close gaps we've hit in practice:
Structured args over regex on shell strings. My
env-guard/interpreter-guardhooks pattern-match raw Bash strings, with known bypasses we document ourselves (obfuscated payloads,git -c/-Cprefixes). Atool.callhook receivinge.tool/e.argsas structured fields — matched via{ tool: "Bash" }— closes a whole bypass class regex can't.Nesting instead of replacement. I ship no custom
autoModerules because setting a category today replaces the shipped defaults rather than adding to them, risking a silent drop of Claude Code's 65 defaultsoft_denyrules. The fold in §2.1 (A∘B∘C∘core, earlier registration wraps and controls more) fixes this structurally — our plugin sits above core and vetoes viabefore/insteadwithout needing to own a whole ruleset.§4.2's capability withholding covers real gaps.
gh apiis allowlisted with no guard today (unlikekubectl), and MCP connectors are invisible tonetwork-guardentirely since it only sees Bash/WebFetch. A plugin controlling whatengine.createforwards — withholding or wrapping thenetworknoun — covers both at the capability level instead of needing a guard per integration.Open question: the doc says
$is a plugin's only channel for side effects, withfiles/network/model/terminal/permissionsas the core primitives — MCP isn't named. Do MCP tool calls route through$.tool.calllike any other tool (hookable), or reach the model outside$entirely? That answer determines whether this closes our biggest known gap or leaves it untouched.DareDev256 commentedon Sep 3, 2026
Heavy hook user, so this lands on a real surface: 42 active hooks across 19 events, plus 4 parked.
Strong yes, and my reason is narrow enough to be worth stating. 35 of those 42 hooks cannot emit a non-zero exit. Failure is unrepresentable in their contract, so nothing downstream can distinguish "ran and worked" from "ran and silently did nothing." I ended up writing a second scheduled job that greps log text and staleness, because exit status certifies nothing. A typed return value fixes that at the type level, and Listing 5 replaces most of what I built to watch my own hooks. Self-observation is exactly what shell hooks are worst at.
Three things the doc left me unsure of:
1. Mid-chain throw. §6.2 covers ⊥ throwing. If a hook three deep throws, does the dispatch fail open or closed? For a redaction hook on
tool.call, fail-open is strictly worse than having no hook at all, so I'd want that to be a declared property rather than an implementation detail.2. Hang budget.
next.signalfires when the chain returns or cancels. What cancels a hook whose promise never settles? Is there a per-hook timeout, or does one badawaitwedge the event for everyone beneath it?3.
$reach. §4 says the environment has no ambient filesystem. Several of my hooks read from~/.secrets/and write state outside the project. Does$.fsreach outside the workspace, and is that reach itself matchable so an admin can scope it?The
$-as-only-door property is the good part: "what a plugin did is exactly the calls it made" is the first plugin model I've seen where an audit log is trustworthy by construction rather than by convention.wordingone commentedon Sep 3, 2026
this sounds fantastic. Current hooks are what supplies modern harnesses with most of the control we have, so im excited to see what happens with these function hooks.
I work with a lot of long running gpu heavy work, so I do think the function hooks could be really helpful with catching errors during/before execution.
definitely ship it, at least for a little bit, and I'm sure a bunch of users across different platforms will report on the usefullness and use cases (i.e threads, instagram, x.com).
andreimaxim commentedon Sep 3, 2026
It's a great feature, but I'm shocked by extremely poor quality of the presentation, from technical documentation to the other videos. Considering you expect feedback from humans, did any human at Anthropic actually look at the videos?
For example, I had to watch "Order is nesting" three times to figure out that what it meant and the documentation says "A sequence on(X, A), on(X, B), on(X, C) folds to X = A(B(C(⊥))): plugins registered earlier sit “above” and plugins registered later “beneath” (on ⊥, see §6.2)" and that "earlier registration grants a plugin more authority since it wraps more". Come on, folks! It's just a middleware!
poteat commentedon Sep 3, 2026
@andreimaxim apologies, indeed I did spend a week copy editing these and running many burns - I'm not satisfied either but I wanted to get this out this week vs. another week of demo polish. It is just a middleware, if you've used Express, I think this will be pretty intuitive.
ahjin2 commentedon Sep 3, 2026
Strong yes, and specifically for the capability model rather than the TypeScript ergonomics.
The property that matters is the one in video ⓸: an admin removes an affordance from
$and nothing registered below can invoke it. That is categorically different from asking a plugin not to do something. In agent-heavy workflows, prohibitions expressed as prose — in a brief, in a rules file, in a system prompt — hold most of the time and fail exactly when the stakes are highest. Mechanical constraints hold. Please keep capability removal front and center if this ships; it is the thing shell hooks structurally cannot provide.I say that as someone who runs a production system where most of the code is written by agents, and who deliberately stays in the loop: I confirm actions when agents ask for permission. That posture is exactly what makes the current permission model painful, because it only offers two settings.
Two gaps this would close:
.claude/, the options today are approving interactively every single time, or granting a broadEdit(.claude/**)permission that widens the surface permanently. There is nothing in between. A function hook could express "this path, this shape of edit, nothing else" — so I keep a human gate where it earns its cost and drop it where it doesn't.Two design questions:
One framing note: every demo presents the admin layer as a team lead constraining developers. For a solo operator the admin is the human and the constrained parties are their own agents — same mechanism, different geometry. If capability removal ends up gated behind enterprise plans, it misses a large share of who needs it.
Last question: does the existing shell-hook interface stay supported alongside this, or is this a migration?
juanquijandria commentedon Sep 3, 2026
this is good! i can´t wait
DareDev256 commentedon Sep 3, 2026
Follow-up with the actual inventory, since the shape of a real surface may be useful for pressure-testing this.
59 hook registrations across 19 events, from 42 scripts (several register on more than one event):
Two things fall out of that.
Ordering bites in exactly one place.
PreToolUsecarries 11 hooks and is the only place where "order is nesting" changes behaviour rather than just sequence — a redaction hook and a deny hook disagree about who should see the unredacted command. Today that ordering is implicit array position with no way to express intent. §2.1 fixes it, and it is worth calling out that the fix matters for one event, not nineteen. The long tail of 13 single-hook events is where the typing is the win, not the composition.The
$-only-door rule is where migration actually gets hard. Classifying my 42 by what they touch:~/.claude/, state dirs, log files)osascript,launchctl, a preview binary)~/.secrets/§4 says the environment has "no ambient file system or network," and that what a plugin did is exactly the calls it made on
$. Taken literally, more than half of this surface cannot be expressed as a function hook unless$grows home-relative fs, subprocess, sockets and sqlite nouns — at which point the auditability claim gets much harder to keep, since$.proc.spawnre-admits everything the door was meant to close.That is not an argument against the design. It is the interesting tension in it, and I would rather see it named in the doc than discovered during migration. My guess at the right answer is that these stay
commandhooks forever and function hooks are for the things commands are bad at — which §1 already permits, but the framing ("a fifth kind") reads like a successor rather than a sibling.andreimaxim commentedon Sep 3, 2026
@poteat apologies for the harsh tone, it's an awesome feature that Claude Code needs to have.
poteat commentedon Sep 3, 2026
@DareDev256 to clarify:
$.fs,$.http, and$.processwill very likely exist. Our prerogative is not to restrict what plugins can do; that's your org admin's job. The point is that there's no ambient support, everything goes through$so that admins can audit, allowlist, deny, log, etc. any event. We want to support wide use-cases; we're still designing what goes on$in conjunction with our early access partners.275 remaining items
cstillm commentedon Oct 21, 2026
Feedback from building a small usage-gauge mod for the Desktop App:
rateLimitsis empty "before the first reading"), though the app's own usage card has the numbers at session start. Exposing them at session start would let status mods be correct immediately.deefactorial commentedon Oct 23, 2026
One request from a plugin that gates merges on subagent review: a verifiable record of a run. Mods expose
$.session.id(),$.session.model()andturn.stepwith the per-request model, which is readable but not provable from outside the process. Filed #104409 asking for a harness-signed run receipt (session id, agent type, model, transcript hash) onSubagentStop/turn.complete; a$.session.receipt()method would be the natural Mods surface for it.615morimotorinta-bot commentedon Oct 23, 2026
Our prompt-footer usage Mod (session / weekly usage, context, cache time left) disappeared from the Desktop App today with no notice.
tengu_plugin_hooks_modulesisfalse(fetched 2026-10-24 ~07:47 JST)claude plugin testsays: "hooks modules are turned off in this process ... if this message returns, installed mods are turned off remotely"claude plugin validatepasses, and nothing in the Mod changedTwo asks:
Trollgeir commentedon Oct 25, 2026
I’m building Lacuna, an OpenCode plugin that replaces finished conversation ranges with summaries only in model requests, preserving originals for later restoration.
session.appendcannot support this: a tool result’s usefulness often becomes clear several turns after it arrives.The quadratic-work concern is fair. Could mods instead submit projection patches keyed by stable message IDs, avoiding mandatory full-history copying? The engine could validate tool pairing and preserve untouched thinking/media.
That would enable reversible context compression without changing stored history. This is a concrete use case for #84756.
This is the only blocker I have currently for making a Claude Code Mod for lacuna.
castus commentedon Oct 25, 2026
Pixpal: a pixel-art friend above the prompt that acts out the tool Claude is using, and shows how it feels about the answer.
https://github.com/castus/pixpal · MIT · v1.0.0
/pixpal emotions ai. Nothing leaves the machine unless you turn that on.Feedback from building it:
claude plugin testand$.session.version()made this easy to build and to ship with CI. Thank you.claude-code, so a test cannot read.claude-plugin/plugin.jsonto check that the version the mod reports (/pixpal report, for bug reports) matches the manifest. I keep the version in a constant and in two JSON files, and bump all three by hand. A way to read the plugin's own manifest, or its version, from$would remove that.claude plugin validatewarns that aCLAUDE.mdat the plugin root is not loaded as plugin context. That is fine for me (it is for contributors to the repo), but a plugin that doubles as a repo hits it by default.hg913488 commentedon Oct 26, 2026
Retro Chat: a mod that skins your Claude Code chat as iMessage, MSN Messenger, AIM or IRC.
/skin msnand Claude signs in.https://github.com/hg913488/claude-code-retro-skins · MIT
You says:, "Claude is typing a message…", and a popup with the first line of the reply when it's done[17:24] <You>,* Claude has joined #demo. No sound. It's IRC.Display only: Claude's prompts and answers don't change. Each skin has its own pixel buddy next to the spinner, real pixel art in the desktop app, block characters in the terminal.
/skin offundoes it all, and sounds can be switched off in/config. A skin is one file inskins/if you want to make your own.wkasekende commentedon Oct 26, 2026
Thanks for
$.prompt.paintin 2.1.311. We've moved our between-keys repaint onto it, and it works as described.One more on the same theme: could a mod style the text cursor without a fill? Something like:
or a separate
$.prompt.caret({ ... }), with the same rules as paint: no text, caret position or undo change, and dropped or kept on the next change, whichever fits the engine. That would let a mod's caret match the rest of its look (theme colours, a high-contrast or larger caret for visibility) without writing to the box.Happy to test a build.
wkasekende commentedon Oct 26, 2026
Three more from building on
$.prompt.paint, shortest first:1. Show a character as another, text unchanged. A decoration key like
{ start, end, replaceWith: '⠋' }: the box draws the replacement over that range, the draft keeps its text. A mod's in-place indicator (a spinner while its work runs) is today afillper frame, so every frame is a write to the box. With this it would be a paint like everything else.2. Feature detection, or optional hooks. A module that hooks an event the engine does not know is refused whole, silently (debug log only), so a mod can't adopt a new event without dropping every older version. Either of these would do:
3. The box's layout. To place
virtualTextunder a span we have to know where the box wraps. Today we read the width from aPromptHintrender'sviewport.columns, subtract a guessed prefix, and re-implement the word wrap. When the guess is off the text lands in the wrong column. Something like:or the same shape on
prompt.edit/prompt.changeinputs. Evencolumnsalone, exact, would remove the guess.Happy to test any of these.
mesix971 commentedon Oct 29, 2026
Clawd: a tiny pixel-art Claude who lives above the prompt and silently acts out your session, in 73 animated scenes and no text.
He carries boxes while Claude works, hammers during builds, throws confetti when tests pass and sits under a rain cloud when they fail. Each subagent arrives as a mini-Clawd in its model's color, hands its work back and leaves in one of five ways. He sweats as the context fills up, wears a visor in auto mode, and levels up from tool calls.
It ships with atelier, an Android dev band (APK and phone versions, git, quotas, cost, model advice), and Clawd plays gags across it:
https://github.com/mesix971/mods-claude · MIT · unofficial, fan-made
Built only on function hooks (
ui.renderabove the prompt,tool.call,agent.spawn,session.measure), with no network and no model calls.feat: add Function Hooks component type (experimental) with 10 hooks …
feat: Function Hooks become Mods — Anthropic plugin layout, real type…
echelong commentedon Oct 30, 2026
Feature request: race-resistant filesystem capabilities for Claude Mods
I'm building Cobalt Cockpit, an open-source Claude Code mod that needs to verify worktree state without accessing protected Git administrative content.
The current Mods filesystem and process APIs appear insufficient for a security-sensitive inventory.
We've investigated
$.fs.list,$.process.runand streaming$.process.spawn, including disposable fixtures with large directories, nested repositories, symlinks, hardlinks and concurrent directory replacement.What we found
$.fs.listmaterializes directory entries rather than exposing an incremental cursor.$.process.spawnimproves observed memory behavior, but does not provide object-based filesystem confinement..gitpruning can be defeated by concurrent directory replacement.Requested host capabilities
Would the Mods API consider providing a constrained filesystem interface supporting:
There is also a deeper requirement: protected-object identity.
Even with handle-relative operations, a protected
.gitdirectory renamed before its first observation is indistinguishable from an ordinary directory unless the host provides an independent authority for identifying protected objects.We are not asking for arbitrary filesystem access or claiming that
openat2alone solves this.The goal is to establish whether the host could expose a narrowly scoped, read-only traversal capability with an explicitly documented trust boundary.
We have reproducible Linux fixtures, a proposed host capability contract and acceptance tests that can be shared in sanitized form.
Would this fit the planned Mods API security model, and is there a recommended path for proposing such a capability?
0.1.9: know Claude works from the main loop's turns
0.1.10: the answer field is four rows tall in the terminal
tmoosting commentedon Nov 15, 2026
@poteat thanks for agreeing that asUser: true turns should go into the rewind stack (your reply here). Is that still on the list? It's still the case on 2.1.325; #103248 has the details and a ten-line repro.