Version
2.1.287
Required. On by default. Check claude --version. ✓
CMS-TK/1.0 · companion of CMS-WB/1.0 · 4 October 2026
Official handler, drawing, and organisation controls, from docs fetched 4 October 2026. For Karolis Valickas. British English. If a shape was not fetched, it stays unverified.
flowchart TB plugin["Plugin"] mod["Mod: JS or TS event handlers"] event["Claude Code calls a handler when an event happens"] watch["Watch"] change["Change"] takeover["Take over"] settingsHook["Settings-file hook"] bothCalled["Both are called"] plugin --> mod mod --> event event --> watch event --> change event --> takeover mod --> bothCalled settingsHook --> bothCalled
1. Check the version2. See where drawing works3. Read the trust quotes4. Open controls before you install
Version
2.1.287
Required. On by default. Check claude --version. ✓
Changelog
Added Claude Mods: plugins may now modify deeper behavior.
Words
Handler can watch, change, or take over. JavaScript or TypeScript. ✓
1
DEC-S01
A settings-file hook is a settings hook. Both are called.
2
DEC-S02
If the 4 October 2026 fetch did not name it, it is not here.
3
DEC-S03
A missing page is not evidence the control does nothing.
4
DEC-S04
That safe-mode list is not the mods-page list.
5
DEC-S05
Offered, title confirmed, transcript not used.
flowchart TB host["Where the session runs"] term["Terminal, editor terminal, JetBrains: hooks yes, drawing yes"] desk["Desktop Code tab except WSL: hooks yes, drawing yes except terminal-only"] wsl["Desktop WSL: hooks no, drawing no"] vsc["VS Code extension chat: hooks yes, drawing no"] pipe["claude -p and Agent SDK: hooks yes, drawing no"] remote["Remote Control: hooks on your machine, drawing in that terminal"] cloud["Cloud: hooks if a plugin reaches the session, drawing no"] host --> term host --> desk host --> wsl host --> vsc host --> pipe host --> remote host --> cloud
| Surface | Hooks | Drawing | Badge |
|---|---|---|---|
| claude in a terminal | yes | yes | ✓ |
| editor integrated terminal | yes | yes | ✓ |
| JetBrains plugin | yes | yes | ✓ |
| Desktop Code tab except WSL | yes | yes, except elements marked terminal-only | ✓ |
| Desktop WSL | no (plugins unavailable) | no | ✓ |
| VS Code extension chat | yes | no | ✓ |
| claude -p and Agent SDK | yes | no | ✓ |
| Remote Control from claude.ai or mobile | yes, in the session on your machine | in the terminal on your machine | ✓ |
| Cloud session | yes, for a plugin that reaches the cloud session | no | ✓ |
flowchart TB loaded["Once loaded, the mod runs as you"] files["Read and write files"] proc["Start processes"] net["Make network requests"] secrets["Read secrets in env and settings, including an API key"] see["See every prompt and tool call"] rewrite["Rewrite a prompt or tool call, submit a prompt, or message another session"] approve["Approve a tool call before you are asked"] spend["Spend usage"] loaded --> files loaded --> proc loaded --> net loaded --> secrets loaded --> see loaded --> rewrite loaded --> approve loaded --> spend
A mod is code that runs with your permissions. It can read and write your files, start processes, and make network requests. Install mods only from authors and marketplaces you trust.
Mods aren't sandboxed. If you turn on sandboxing, the sandbox isolates the Bash commands Claude runs, and a process that a mod starts runs outside it.
None of these controls sandboxes a mod. A mod you allow runs as the user, with the user's access to files, processes, and the network.
flowchart TB face["Interface"] much["Much of the interface can be restyled"] ask["AskUserQuestion is a render site"] perm["The permission prompt is not, and a mod cannot change what it shows"] face --> much face --> ask face --> perm
A mod can approve a call an ask rule would prompt for, or that one of your own PreToolUse hooks blocked. In auto mode that call runs without a classifier check.
Where the guard loads, a user mod cannot approve a call a deny rule refuses, unless allowModsToOverrideDenyRules is set. A managed PreToolUse block is final.
Neither deny rules nor managed PreToolUse apply to a mod's own $.fs and $.process calls.
Network policy covers $.http.fetch only. $.process.run is not covered.
Trust prompts come first. An untrusted directory loads no mod until you answer.
flowchart LR handler["Handler receives the mods API, e, and next"] watch["next of e: watch"] rewrite["next with a changed field: rewrite"] answer["Return without next: answer and short-circuit"] handler --> watch handler --> rewrite handler --> answer
| Event | Watch spelled | Rewrite spelled | Answer or other return | Notes | Badge |
|---|---|---|---|---|---|
| tool.call | next(e) | change args via next; retry by calling next(e) again | {deny: reason} or {result} | Includes subagent and MCP. {result} answers without a permission prompt and without running the tool. After next(e): permission check, then the tool. A result may have deny or isError. | ✓ |
| tool.check | next(e) resolves allow, ask, or deny | not spelled | {decision} | Can approve a call a non-managed PreToolUse blocked. | ✓ |
| prompt.submit | not spelled as bare next(e) | rewrite text or context | {drop: reason} | Rewrite is spelled. The field names inside the rewrite are not spelled beyond text or context. | ✓ |
| prompt.fill | not spelled as a separate shape | next(e) with changed text | no separate answer shape spelled | Do not invent an answer object. | ✓ |
| prompt.suggest | not spelled as a separate shape | next(e) with changed text | no separate answer shape spelled | Same spelling as prompt.fill. | ✓ |
| prompt.edit | next(e) | doc does not say | doc does not say | Observe is the only spelled path. | ✓ |
| command.run | next(e) | not spelled | {text}, or {} | {} prints nothing. | ✓ |
| config.set | not spelled as bare next(e) | next with a changed value | {deny: reason} | Rewrite or deny. | ✓ |
| turn.start | next(e). Guide says observe | doc does not say | doc does not say | Observe only, as spelled. | ✓ |
| turn.step | yield* next(e) | next with model or effort | Guide: answer without calling the model. No separate object shape is spelled for that answer. | Async generator. result.usage fields: input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens, plus model. | ✓ |
| turn.complete | next(e) | not spelled | {text}, a line under the answer | e.isAborted, e.answer, e.durationMs, e.usage. | ✓ |
| session.start | next(e) | not spelled | not spelled | Once per loaded mod before the first prompt, and again after reload of that mod. Not after /clear, /resume, or /branch. | ✓ |
| session.append | not spelled as bare next(e) | next with changed message content | not spelled | Rewrite is the spelled path. | ✓ |
| agent.spawn | not spelled | {model} is a return, not described as next | {deny: reason} | Do not recast {model} as a next() rewrite. | ✓ |
| ui.render | next(e) leaves the drawing | next with changed props changes a detail | a tree without next replaces the drawing | For AskUserQuestion, a tree must hold the engine reference exactly once. | ✓ |
| plugin.register | not spelled | not spelled | {refuse: reason} | e.tier is prepend, user, append, or builtin. e.uses lists events, API calls, env, state. If this hook throws or times out with no .catch, the check fails open and the mod loads. | ✓ |
| telemetry.log | next(e) | not spelled | {deny: reason} | An installed mod must filter {to: 'collector'} or validate fails. * does not match telemetry. | ✓ |
sequenceDiagram
participant Managed as Managed PreToolUse
participant Mod
participant Other as Other PreToolUse
participant Perm as Permission check
participant Tool
Managed->>Mod: Runs before the first tool.call. A block is final
alt Return without next
Mod-->>Mod: Answer. Later PreToolUse hooks do not run
else next of e
Mod->>Other: Other PreToolUse after the last next
Other->>Perm: Permission check
Perm->>Tool: Then the tool
end
flowchart LR dev["Person who trusts the author"] goal["See a tool-call count on the spinner"] mod["Handler on tool.call and ui.render"] dev --> goal goal --> mod
1
Install only from an author you trust.
2
tool.call counts and calls next(e).
3
ui.render for Spinner rewrites props.suffix.
4
The permission prompt stays as Claude Code drew it.
flowchart LR settings["Settings-file hook"] modh["Mod handler, called a hook on these pages"] both["Both are called"] settings --> both modh --> both
flowchart LR watch["tool.call calls next of e and counts"] match["ui.render matcher component Spinner"] rewrite["next with props.suffix changed"] watch --> match match --> rewrite
export function register(on) {
on('tool.call', async ($, e, next) => {
calls += 1
$.ui.invalidate('ui.render')
return next(e)
})
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}flowchart TB deny["A deny rule refuses the call"] guard["The built-in guard is loaded"] unset["allowModsToOverrideDenyRules is unset"] noApprove["A user mod cannot approve that call"] allow["Set true: a user mod can approve a denied call"] deny --> guard guard --> unset unset --> noApprove guard --> allow
flowchart LR rules["Deny rules and managed PreToolUse"] toolCall["They can stop a tool call"] ownCalls["They do not apply to the mod own fs and process calls"] rules -->|"apply"| toolCall rules -->|"do not apply"| ownCalls
flowchart LR crash["Hooks worker crashes three times"] unload["Unload every non-built-in mod"] back["Until reload-plugins or a new session"] crash --> unload unload --> back
flowchart TB sec["1 sec-default at builtin, where it loads"] pre["1 then prependPlugins"] org["1 then other organisation mods not in appendPlugins"] user["2 Mods you install"] app["3 appendPlugins"] builtin["4 Other built-in mods"] sec --> pre pre --> org org --> user user --> app app --> builtin
Managed PreToolUse runs before the first mod's tool.call. A block is final.
Other PreToolUse hooks run after the last mod calls next. Answering tool.call without next keeps them from running.
stateDiagram-v2 [*] --> Running Running --> Skipped: no catch, and throw, timeout, or wrong shape before next Running --> Stands: the same failure after next has resolved Running --> Unloaded: hooks worker crashes three times Unloaded --> Back: reload-plugins or a new session
stateDiagram-v2 [*] --> Check Check --> Refused: return refuse with a reason Check --> Loads: check passes Check --> LoadsOpen: throw or timeout with no catch fails open
| Limit | Spelled | Badge |
|---|---|---|
| .catch | 1 second | ✓ |
| Hook's own time | 10 seconds, not counting next or a mods API call other than $.clock.sleep | ✓ |
| All session.end hooks together | 1.5 seconds | ✓ |
| Time inside $.ui.ask | Does not count | ✓ |
| Time awaiting your own promise | Does count | ✓ |
| claude plugin test, one test | 5 seconds unless timeoutMs | ✓ |
| Worker crashes | Three times, then unload every non-built-in mod | ✓ |
flowchart TB brought["Who brought the mod"] yours["Counts as yours"] orgmod["Organisation mod"] blocked["allowManagedModsOnly: a user-brought mod does not load"] still["Organisation mods still load"] brought --> yours brought --> orgmod yours --> blocked orgmod --> still
flowchart TB test["Counts as organisation-owned only when all three hold"] en["managed enabledPlugins sets it true"] dir["Marketplace named as a directory by absolute path"] rel["Plugin listed by a relative path, so it loads in place"] cache["Cache copy from GitHub, git, URL, or npm"] userCopy["Counts as a user mod even if enabledPlugins enables it"] test --> en test --> dir test --> rel cache --> userCopy
flowchart TB machine["This machine"] managed["Has managed settings: guard loads"] team["Signed in with Team or Enterprise: guard loads"] apikey["API key, Bedrock, Agent Platform, or Foundry: guard only with managed settings"] unread["Guard cannot read managed settings"] refuseMods["Refuses every user mod"] unchecked["Cannot check deny rules for a call a user mod approved"] refuseCall["Refuses that call"] machine --> managed machine --> team machine --> apikey unread --> refuseMods unchecked --> refuseCall
refused by cc-plugin-sec-default: mods are limited to your organization's by policy (allowManagedModsOnly)
Stops mods in every installed plugin, yours included. Turns off every settings-file hook, so a managed PreToolUse no longer blocks. Custom status lines and /goal stop. Does not stop built-in mods. Skills and MCP are not mentioned on this sentence.
Stops every mod you installed, your settings hooks, and your custom status line. What the organisation manages keeps running. The plugin stays installed. Skills, commands, agents, and MCP still load. Built-ins are not affected.
Every installed mod off for one session, organisation mods included, plus your other customisations. Built-ins keep running. The mods pages do not list the full set. A June 2026 what's-new page names CLAUDE.md, skills, plugins, hooks, MCP servers, and custom commands and agents. That page is older than mods. ~
Installed plugins that are not managed load no hooks module. Built-ins still run. ✓
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS is ignored at any value from v2.1.287 on. A 0 does not keep mods off. Replace it with allowManagedModsOnly. Settings hooks are not deprecated.
| Control | Where | User-brought mods | Organisation mods | Settings hooks | Built-ins | Also stated | Badge |
|---|---|---|---|---|---|---|---|
| allowManagedModsOnly | pluginConfigs on cc-plugin-sec-default@builtin, managed settings only. The same entry in user, project, local, or --settings changes nothing. | No user-brought mod loads: installed, --plugin-dir, or written mid-session. A GitHub or remote marketplace mod, and one the organisation turns on for members on claude.ai, counts as a user's and does not load. Users cannot undo it. | Organisation mods still load. | Users' settings hooks, status lines, and /goal keep working. | Built-ins keep running. | File or MDM policy covers Bedrock, Google Agent Platform, and Foundry. Debug line is quoted in Part 8. | ✓ |
| disableAllHooks in managed settings | managed settings, true | Stops mods in every installed plugin, yours included. | Not given a separate exemption in that sentence. Do not invent one. | Turns off every hook in settings files, so a managed PreToolUse no longer blocks. Custom status lines and /goal stop. | Does not stop built-in mods. | Skills, commands, agents, and MCP are not mentioned on this sentence. | ✓ |
| disableAllHooks in ~/.claude/settings.json | overview of the user settings file | Stops every mod you installed. | What the organisation manages keeps running. | Stops your settings hooks and custom status line. | Built-ins not affected. | Plugin stays installed. Skills, commands, agents, and MCP still load. | ✓ |
| --safe-mode | one session | Every installed mod off, and your other customisations. | Organisation mods included, so they are off too. | The mods pages do not give the full list. | Does not stop built-in mods. | Admin: start with claude --safe-mode to check whether a mod caused a problem. A June 2026 what's-new page lists CLAUDE.md, skills, plugins, hooks, MCP servers, and custom commands and agents. That page is older than mods. | ~ |
| allowManagedHooksOnly | Admin page points elsewhere. This fetch did not include that page. | Only organisation mods and built-ins, so a user mod is not in that set. | Organisation mods load. | Also blocks hooks in users' own settings files. | Built-ins load. | Detailed page not fetched. Do not add controls from memory. | ~ |
| disableSideloadFlags | named control | Rejects --plugin-dir and --plugin-url, and keeps mods Claude writes from loading. | Not stated. | Not stated. | Not stated. | Also rejects --agents and --mcp-config. | ✓ |
| allowModsToOverrideDenyRules | unset, or true | Unset: deny wins, so a user's approving mod cannot approve a denied call. true: it can. | Not a loader switch. | Not a settings-hook switch. | Where the guard loads, this is the exception to deny-wins. | Does not sandbox the mod. | ✓ |
| prependPlugins and appendPlugins | managed settings. User settings only on a machine with no managed settings, and only when not signed in with Team or Enterprise. | Order group 2 is mods you install. | prepend runs in group 1 after sec-default, where the guard loads. append is group 3. Other organisation mods not in appendPlugins stay in group 1. | Not a hook switch. | If you set prependPlugins, name sec-default@builtin or the guard does not load. pluginConfigs reads only cc-plugin-sec-default@builtin. | Within dependencies, a mod runs before the mods it lists. | ✓ |
| --bare | one process flag | Installed plugins that are not managed load no hooks module. | Managed plugins are the ones this sentence does not switch off. No further split is spelled. | Not stated. | Built-ins still run. | Does not stop built-in mods. | ✓ |
| CLAUDE_CODE_ENABLE_FUNCTION_HOOKS | ignored | v2.1.287+ ignores it at any value, so 0 does not keep mods off. | Not a replacement for organisation policy. | Settings hooks are not deprecated. | Not a built-in switch. | Replace a 0 with allowManagedModsOnly. | ✓ |
| Remote off | Anthropic, not a local setting | Installed mods can be turned off. No local setting turns them back on. | Not stated as a separate class. | Not stated. | cc-plugin-plugin-authoring is off in that case. Other built-ins are not stated. | claude plugin test: hooks modules are turned off in this process. | ✓ |
flowchart TB ev["Events named in the fetched pages"] ev --> tool["tool"] ev --> prompt["prompt, skill.prompt, attribution.text"] ev --> turn["turn and session"] ev --> ui["ui"] ev --> other["command, config, agent"] ev --> meta["plugin.register, engine.create, telemetry"] ev --> classic["classic dot name. Full list not fetched"] ev --> api["namespace.method for each mods API method"]
| Event | Watch spelled | Rewrite spelled | Answer or other return | Notes | Badge |
|---|---|---|---|---|---|
| tool.call | next(e) | change args via next; retry by calling next(e) again | {deny: reason} or {result} | Includes subagent and MCP. {result} answers without a permission prompt and without running the tool. After next(e): permission check, then the tool. A result may have deny or isError. | ✓ |
| tool.check | next(e) resolves allow, ask, or deny | not spelled | {decision} | Can approve a call a non-managed PreToolUse blocked. | ✓ |
| tool.describe | not spelled | not spelled | {description}, optionally isDeferred | No other return is spelled. | ✓ |
| prompt.submit | not spelled as bare next(e) | rewrite text or context | {drop: reason} | Rewrite is spelled. The field names inside the rewrite are not spelled beyond text or context. | ✓ |
| prompt.fill | not spelled as a separate shape | next(e) with changed text | no separate answer shape spelled | Do not invent an answer object. | ✓ |
| prompt.suggest | not spelled as a separate shape | next(e) with changed text | no separate answer shape spelled | Same spelling as prompt.fill. | ✓ |
| prompt.edit | next(e) | doc does not say | doc does not say | Observe is the only spelled path. | ✓ |
| prompt.compose | not spelled | not spelled | {sections}, a list of {id, text, scope} | Answer shape only. | ✓ |
| prompt.section | not spelled | not spelled | {text} or {text: null} | Answer shape only. | ✓ |
| prompt.context | not spelled | not spelled | {blocks} | Answer shape only. | ✓ |
| prompt.attachment | not spelled | not spelled | {text} or {text: null} | Answer shape only. | ✓ |
| skill.prompt | not spelled | not spelled | {text} | Answer shape only. | ✓ |
| attribution.text | not spelled | not spelled | {text} | Answer shape only. | ✓ |
| command.run | next(e) | not spelled | {text}, or {} | {} prints nothing. | ✓ |
| command.describe | not spelled | not spelled | {description, argumentHint, isHidden} | Answer shape only. | ✓ |
| config.set | not spelled as bare next(e) | next with a changed value | {deny: reason} | Rewrite or deny. | ✓ |
| config.describe | not spelled | not spelled | {label, description, isHidden} | Answer shape only. | ✓ |
| turn.start | next(e). Guide says observe | doc does not say | doc does not say | Observe only, as spelled. | ✓ |
| turn.step | yield* next(e) | next with model or effort | Guide: answer without calling the model. No separate object shape is spelled for that answer. | Async generator. result.usage fields: input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens, plus model. | ✓ |
| turn.complete | next(e) | not spelled | {text}, a line under the answer | e.isAborted, e.answer, e.durationMs, e.usage. | ✓ |
| session.start | next(e) | not spelled | not spelled | Once per loaded mod before the first prompt, and again after reload of that mod. Not after /clear, /resume, or /branch. | ✓ |
| session.end | next(e) | not spelled | not spelled | e.reason is clear, resume, logout, prompt_input_exit, or other. /branch reports resume. | ✓ |
| session.compact | not spelled | not spelled | {skip: reason} | Answer shape only. | ✓ |
| session.receive | not spelled | not spelled | {consumed: reason}, to keep it from Claude | origin.kind: peer, peer-send-message, task-notification, scheduled-trigger. | ✓ |
| session.send | not spelled | not spelled | {isDelivered: false, reason} | origin.kind: model or plugin. | ✓ |
| session.append | not spelled as bare next(e) | next with changed message content | not spelled | Rewrite is the spelled path. | ✓ |
| session.attach | next(e) | doc does not say | doc does not say | Observe only, as spelled. | ✓ |
| session.detach | next(e) | doc does not say | doc does not say | Observe only, as spelled. | ✓ |
| session.measure | next(e) | doc does not say | doc does not say | Observe only, as spelled. | ✓ |
| agent.offer | not spelled | not spelled | {isOffered: false} | Answer shape only. | ✓ |
| agent.spawn | not spelled | {model} is a return, not described as next | {deny: reason} | Do not recast {model} as a next() rewrite. | ✓ |
| ui.render | next(e) leaves the drawing | next with changed props changes a detail | a tree without next replaces the drawing | For AskUserQuestion, a tree must hold the engine reference exactly once. | ✓ |
| ui.resolve | doc does not say watch, rewrite, or take over | doc does not say | the result is what $.ui.resolve(e) reads | Take-over words are not in the doc. | ~ |
| ui.press | no return shape in the table | no return shape in the table | no return shape in the table | Do not invent one. | ?? |
| ui.input | no return shape in the table | no return shape in the table | no return shape in the table | Do not invent one. | ?? |
| ui.select | no return shape in the table | no return shape in the table | no return shape in the table | Do not invent one. | ?? |
| ui.focus | no return shape | no return shape | no return shape | Do not invent one. | ?? |
| ui.scroll | no return shape | no return shape | no return shape | Do not invent one. | ?? |
| ui.close | no return shape | no return shape | no return shape | e.origin.kind is plugin, person, or unload. That is event data, not a return shape. | ?? |
| ui.message | no return shape | no return shape | no return shape | Do not invent one. | ?? |
| plugin.register | not spelled | not spelled | {refuse: reason} | e.tier is prepend, user, append, or builtin. e.uses lists events, API calls, env, state. If this hook throws or times out with no .catch, the check fails open and the mod loads. | ✓ |
| engine.create | not spelled | not spelled as next | a hook can return a changed mods API | The doc spells a changed API, not watch or rewrite. | ✓ |
| telemetry.log | next(e) | not spelled | {deny: reason} | An installed mod must filter {to: 'collector'} or validate fails. * does not match telemetry. | ✓ |
| telemetry.mark | next(e) | not spelled | {deny: reason} | Same collector rule as telemetry.log. * does not match. | ✓ |
| classic.<settings hook name> | not enumerated | not enumerated | not enumerated | Examples named: classic.Stop, classic.PostToolUse, classic.SessionStart. 'classic.*' matches every settings-hook event. The full list was not fetched. | ?? |
| namespace.method | next(e) | not spelled as a field rewrite | {deny} or {value} | Every mods API method is also an event under this name. This is not a catalogue of methods. | ✓ |
flowchart TB sites["Render sites not marked terminal-only"] sites --> pane["Pane"] sites --> above["AbovePrompt"] sites --> user["UserMessage"] sites --> asst["AssistantMessage"] sites --> use["ToolUse"] sites --> result["ToolResult"] sites --> group["ToolGroup"] sites --> cmd["CommandOutput"] sites --> ask["AskUserQuestion"] sites --> spin["Spinner"] sites --> mode["SessionMode"] sites --> hint["PromptHint"]
flowchart TB termOnly["Marked terminal-only"] termOnly --> prog["ToolProgress"] termOnly --> dur["TurnDuration"] termOnly --> info["InfoNotice"] termOnly --> raster["Raster"] termOnly --> image["Image"] deskOnly["Desktop only"] deskOnly --> svg["Svg"]
| Render site | Drawing note | Badge |
|---|---|---|
| Pane | not marked terminal-only | ✓ |
| AbovePrompt | not marked terminal-only | ✓ |
| UserMessage | not marked terminal-only | ✓ |
| AssistantMessage | not marked terminal-only | ✓ |
| ToolUse | not marked terminal-only | ✓ |
| ToolResult | not marked terminal-only | ✓ |
| ToolGroup | not marked terminal-only | ✓ |
| CommandOutput | not marked terminal-only | ✓ |
| AskUserQuestion | not marked terminal-only. A replacement tree must hold the engine reference exactly once. | ✓ |
| Spinner | not marked terminal-only | ✓ |
| SessionMode | not marked terminal-only | ✓ |
| PromptHint | not marked terminal-only | ✓ |
| ToolProgress | terminal only | ✓ |
| TurnDuration | terminal only | ✓ |
| InfoNotice | terminal only | ✓ |
| Element | Where | Limit spelled | Badge |
|---|---|---|---|
| Box | named | no limit spelled | ✓ |
| Text | named | one string child, 10000 characters | ✓ |
| Button | named | no limit spelled | ✓ |
| Link | named | no limit spelled | ✓ |
| Code | named | up to 10000 characters | ✓ |
| Markdown | named | up to 10000 characters | ✓ |
| Input | named | no limit spelled | ✓ |
| Select | named | no limit spelled | ✓ |
| Svg | Desktop only | source up to 131072 characters | ✓ |
| Client | named | no limit spelled | ✓ |
| Raster | terminal only | columns up to 512, rows up to 256 | ✓ |
| Image | terminal only | PNG or RGBA up to 2 MiB, or a file path | ✓ |
| $.ui name | What is spelled | Badge |
|---|---|---|
| resolve | result of the ui.resolve hook is what $.ui.resolve(e) reads. Guides: resolve elements this app can draw. | ✓ |
| invalidate | Redraw. Throttled to 10 a second, or 30 a second in the terminal, for a visible pane, the expanded band, and the hint line. | ✓ |
| open | focus, closeOnEscape, and holdToasts accept only true. false throws. Resolves isPlaced true, or false with a reason. A pane opened without the user asking waits until 144 columns, or 110 after they opened one once. | ✓ |
| close | Named in the method list. No further behaviour is spelled here. | ~ |
| panes | Name only. | ~ |
| focus | Name only. | ~ |
| scroll | Name only. | ~ |
| toast | 4 seconds unless timeoutMs. | ✓ |
| status | One line under the prompt. | ✓ |
| log | A dim transcript line Claude does not read. log to debug writes the debug log. | ✓ |
| notice | Name only. | ~ |
| ask | Question above a numbered list, then a row for typing and Chat about this. Rejects if dismissed, if Chat about this, or under claude -p. Time inside $.ui.ask does not count toward the hook limit. | ✓ |
| copy | The test stub returns isCopied true. Guides do not describe it beyond the name. | ~ |
| blit | Repaints one Raster. | ✓ |
| selection | Not in the reference method list. Changelog 2.1.288: returns the text you last selected in fullscreen mode and, when the selection lies within one transcript row, that row. | ~ |
| Name | What is spelled | Badge |
|---|---|---|
| $.http.fetch | Network policy covers this call only. | ✓ |
| $.process.run | Not covered by that network policy. | ✓ |
| $.fs and $.process | Deny rules and managed PreToolUse do not apply to a mod's own calls. | ✓ |
| $.fs.write | Not atomic. No other write contract is spelled. | ✓ |
| $.fs.read | Validate example lists it. Packaging says write the call in full, as $.fs.read(...). Arguments are not spelled. | ✓ |
| $.store | JSON under ~/.claude/plugins/store/. 4 MiB total. Not atomic. | ✓ |
| $.store.set | Named on a validate example line. No signature is spelled. | ✓ |
| $.ui.* | Method names in the interface section. Not a signature list, except where a guide spells behaviour. | ✓ |
| $.ui.resolve | Reads the ui.resolve result. | ✓ |
| $.ui.invalidate | Used in the smallest example as $.ui.invalidate('ui.render'), and described as redraw. | ✓ |
| $.ui.ask | Ask behaviour and the timing exception are spelled. | ✓ |
| $.ui.selection | Changelog 2.1.288 only. Absent from the reference method list. | ~ |
| $.clock.sleep | Named only because its time counts toward the 10 second hook limit. Other mods API calls do not. | ✓ |
| $.state | Named only as a reason to ship types/index.d.ts, or when adding a namespace. No methods are spelled. | ~ |
flowchart TB root["Plugin directory"] manifest[".claude-plugin/plugin.json"] hooks["hooks/hooks.json modules array"] reg["exports register of on and options"] esm["ES module, no Node bundler, no require"] root --> manifest root --> hooks hooks --> reg reg --> esm
stateDiagram-v2 [*] --> Written: Claude writes under dev-mods for that session Written --> ThisSession: you approve hot reload ThisSession --> Removed: folder older than cleanupPeriodDays
Path: ~/.claude/dev-mods/<session-id>/<name>/. $.store is JSON under ~/.claude/plugins/store/, 4 MiB total, not atomic. $.fs.write is not atomic either.
| Name | What is spelled | Badge |
|---|---|---|
| cc-plugin-agents-md | Loads AGENTS.md. | ✓ |
| cc-plugin-diff | Takes over /diff. | ✓ |
| cc-plugin-plugin-authoring | Skill only, no mod code. Off when installed mods are turned off remotely. | ✓ |
| cc-plugin-sec-default | Users cannot turn it off. | ✓ |
| cc-plugin-telemetry | Named. No further behaviour is spelled here. | ~ |
| cc-plugin-you-should-know | Off by default. /plugin enable cc-plugin-you-should-know@builtin. Changelog: first-party sessions with telemetry on. | ✓ |
sequenceDiagram participant You participant Validate as claude plugin validate participant Test as claude plugin test You->>Validate: Point it at the directory Note over Validate: Does not run the code Validate-->>You: Hooks and calls it can read You->>Test: Run tests Note over Test: One test is 5 seconds unless timeoutMs
Example lines: hooks: session.start, tool.call, ui.render{component=Pane} and calls: $.fs.read, $.http.fetch, $.store.set, $.ui.open.
| Message | Means | Badge |
|---|---|---|
| no hooks module to load | mods can load | ✓ |
| hooks modules are turned off here | disableAllHooks | ✓ |
| hooks modules are turned off in this process | Anthropic turned installed mods off remotely | ✓ |
| not reported | This command does not report allowManagedModsOnly | ✓ |
flowchart TB gaps["Labelled unverified or incomplete"] gaps --> classic["classic star list not fetched"] gaps --> page["allowManagedHooksOnly page not fetched"] gaps --> days["cleanupPeriodDays has no number here"] gaps --> guard["Guard hooked event names not given"] gaps --> readme["Sample READMEs not fetched"] gaps --> shape["Several UI events have no return shape"] gaps --> sel["selection is not in the method list"]
| ID | Badge | Gap |
|---|---|---|
| GAP-01 | ?? | The reference says as of v2.1.287. The changelog top section is 2.1.288. This page does not invent what else 2.1.288 changed. |
| GAP-02 | ?? | $.ui.selection() is in the 2.1.288 changelog and is not in the reference method list. |
| GAP-03 | ?? | ui.press, ui.input, ui.select, ui.focus, ui.scroll, ui.close, and ui.message have no return shape. |
| GAP-04 | ?? | The full classic.* list was not fetched. Named examples only: classic.Stop, classic.PostToolUse, classic.SessionStart. |
| GAP-05 | ~ | panes, focus, scroll, and notice are names only. close is a method name without further behaviour here. copy is the test stub isCopied true, and guides do not describe it beyond the name. |
| GAP-06 | ?? | cleanupPeriodDays is not defined on the mods pages. The dev-mod folder is deleted once older than that number. |
| GAP-07 | ?? | allowManagedHooksOnly: the admin page points elsewhere, and this fetch did not include that page. |
| GAP-08 | ?? | Sample READMEs were not fetched. Names only: token-weather, blast-radius, replay-theater, tutorials first-mod and hello-tabs. gallery is a sample harness. |
| GAP-09 | ?? | The guard's exact hooked event names are not given. |
| GAP-10 | ?? | The full mods API method catalogue is not in this fact pack. Only names that appear are listed. |
| GAP-11 | ~ | process.spawn is named as an async generator. Its return shape is not in the event list. |
| GAP-12 | ~ | The June 2026 what's-new safe-mode list is older than mods. Its URL was not in this fact pack. The mods pages do not define the full safe-mode list. |
| GAP-13 | ?? | The declaration of calls was not in the quoted smallest example. |
| GAP-14 | ?? | YouTube https://youtu.be/Rn4nmFRPe0s was offered. Title confirmed via oEmbed. Transcript was not downloaded (HTTP 429). Not used as a source. |
flowchart TB src["This edition"] docs["Mods docs fetched 4 October 2026"] chg["Changelog 2.1.287 and top section 2.1.288"] old["June 2026 what is new, older than mods"] yt["YouTube offered. Transcript not used"] src --> docs src --> chg src --> old src -->|"not a source"| yt
| ID | Tier | Status | Name | URL |
|---|---|---|---|---|
| SRC-001 | T1 | ✓ ✓ Fetched 4 October 2026 | Mods overview | https://code.claude.com/docs/en/plugins/mods/overview.md |
| SRC-002 | T1 | ✓ ✓ Fetched 4 October 2026 | Mods admin | https://code.claude.com/docs/en/plugins/mods/admin.md |
| SRC-003 | T1 | ✓ ✓ Fetched 4 October 2026 | Mods reference | https://code.claude.com/docs/en/plugins/mods/reference.md |
| SRC-004 | T1 | ✓ ✓ Fetched 4 October 2026 | Mods events | https://code.claude.com/docs/en/plugins/mods/events.md |
| SRC-005 | T1 | ✓ ✓ Fetched 4 October 2026 | Mods API | https://code.claude.com/docs/en/plugins/mods/api.md |
| SRC-006 | T1 | ✓ ✓ Fetched 4 October 2026 | Mods interface | https://code.claude.com/docs/en/plugins/mods/interface.md |
| SRC-007 | T1 | ✓ ✓ Fetched 4 October 2026 | Mods gallery | https://code.claude.com/docs/en/plugins/mods/gallery.md |
| SRC-008 | T1 | ✓ ✓ Fetched 4 October 2026 | Create a mod | https://code.claude.com/docs/en/plugins/mods/create.md |
| SRC-009 | T1 | ✓ ✓ Fetched 4 October 2026 | Test a mod | https://code.claude.com/docs/en/plugins/mods/test.md |
| SRC-010 | T1 | ✓ ✓ Fetched 4 October 2026 | Troubleshoot mods | https://code.claude.com/docs/en/plugins/mods/troubleshoot.md |
| SRC-011 | T1 | ✓ ✓ Fetched 4 October 2026 | Docs changelog, generated from the GitHub file | https://code.claude.com/docs/en/changelog.md |
| SRC-012 | T1 | ✓ ✓ Fetched 4 October 2026 | GitHub CHANGELOG.md | https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md |
| SRC-013 | T3 | ?? ?? Not used | YouTube, Chase AI. Title via oEmbed only | https://youtu.be/Rn4nmFRPe0s |