Claude Code mods 入门:构建上下文用量面板与会话扩展
Getting started with Claude Code mods
Claude Code mods 入门指南以约 80 行的 Token Weather 为例,讲解如何构建实时显示上下文窗口用量的会话扩展。Mods 适用于 Claude Code 2.1.287 及以上版本且默认开启,通过插件中的 JavaScript 或 TypeScript 模块观察、改写或接管事件,并绘制自定义界面;教程涵盖热重载、状态保存、验证、测试与分发。
通过上下文用量面板的完整实现,展示如何把事件钩子、热重载状态和界面绘制组合成可测试、可分享的会话扩展。
mod 是一个小型 JavaScript 或 TypeScript 文件,运行在你的 Claude Code 会话中。它可以观察正在发生的事情,改变 Claude Code 的行为,或在终端或桌面应用中绘制自己的 UI。你不需要先学习 API 就能试用一个。运行 claude,然后描述你想要的 mod。它请求时允许热重载,这个 mod 就会在本轮结束时出现。
Claude Code 已经允许你改变很多行为方式:设置、权限规则、斜杠命令、技能和状态栏。mod 更进一步:它们可以重写或替换 Claude Code 的行为,并绘制自定义 UI。在底层,mod 是随插件一起提供的钩子,每一个都会在你的会话中每个事件发生时看到它。
这让 mod 成为让 Claude Code 适配你工作方式的一种手段。你可以添加一个你会不断查看的读数面板,在让你紧张的命令前加一道防护,或者按照你喜欢查看变更的方式构建一个审阅视图。
本指南从一个空文件夹开始构建一个 mod,Token Weather,这是绘制在提示词上方、对上下文窗口进行实时预报的工具。它大约只有 80 行。然后它会介绍两个更大的 mod,Blast Radius 和 Replay Theater,展示这个 API 还能做什么。三者的完整代码都在 anthropics/claude-code-playground 中。

Claude Code 2.1.287 或更高版本。mod 默认已开启,因此无需额外启用。API 可能会在不同版本之间发生变化。每次 Claude Code 加载一个 mod 时,都会把适用于你当前构建版本的类型声明写入该 mod 的 .claude-plugin/types/ 文件夹,而这些声明就是你所用版本的权威依据。
MOD 的工作原理
mod 是一种 Claude Code 插件,其行为定义在一个 JavaScript 或 TypeScript 模块中:
- 这个文件夹是一个普通插件,带有一个
.claude-plugin/plugin.json清单文件。 hooks/hooks.json指定了modules下的一个模块。- 该模块导出
register(on, options)。在其中,on(event, matcher?, hook)会添加一个钩子。
每个钩子的形式都相同:
代码JavaScript
on("tool.call", { tool: "Bash" }, async ($, e, next) => { // $ the 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's own behavior return next(e); });
钩子会形成一条链,就像中间件。你的钩子先运行,next(e) 把事件交给下一个插件,而在最底层,Claude Code 会照常执行它原本要做的事。一个钩子可以做三种事情之一:
钩子形成一条链
一个事件会先经过你的钩子,再经过其他插件,最后到达 Claude Code
一个事件会先经过你的钩子,再经过其他插件,最后到达 Claude Code。每一层都会调用 next(e) 将它继续传下去,结果则会沿路返回。若某个钩子要直接应答,它会在不调用 next 的情况下返回。
| 动作 | 方式 | 示例 |
|---|---|---|
| 观察 | const r = await next(e); /* look */ return r | 记录每一次文件编辑。在每一轮结束后取一次读数。 |
| 重写 | return next({ ...e, command: safer }) | 改变链条其余部分所看到的内容。 |
| 应答 | return { deny: "…" },且不调用 next | 拒绝一次工具调用。自行提供一个命令或工具。 |
这些事件涵盖工具调用、提交后的提示词、回合的开始与结束、会话的开始与结束、斜杠命令,以及 ui.render:界面在绘制时的每一个组成部分。该模块运行在它自己的沙箱中,没有 DOM,也没有 Node,因此它之外的一切都要通过 $。
这与设置钩子的不同之处。设置钩子会为每个事件运行一条 shell 命令,并通过 stdin 和 stdout 传递 JSON。mod 只会加载一次,并在整个会话中保持驻留。它可以保存状态、绘制会随着事件发生而更新的 UI,并回调 Claude Code:打开一个窗格、运行一个进程、注册一个斜杠命令,或注册一个可供模型调用的工具。
Claude Code 自己也在使用它们。Claude Code 自身的一些功能就是以 mod 的形式构建的,包括对 AGENTS.md 的支持,以及对话旁边的 /diff 窗格。它们的源码连同测试一起公开放在 anthropics/claude-code 仓库的 mods/ 下,所以你可以读一读团队是如何构建它们的。
构建你的第一个 MOD:TOKEN WEATHER
Token Weather 会在每轮结束后读取上下文窗口已占用的比例,并在提示符上方绘制一行内容:一个天气图标、百分比、窗口中已使用的 token 数量、最近几轮的小图表,以及上一轮新增了多少。
| 已用 | 预报 |
|---|---|
| 低于 25% | ☀ 晴朗 |
| 25–49% | ☁ 多云 |
| 50–74% | ☂ 阵雨 |
| 75–89% | ☇ 风暴 |
| 90% 及以上 | ↯ 即将压缩 |
下面是在真实会话中的效果。每一轮都会读取更多文件,状态条会从 ☀ 晴朗 逐渐变为 ☂ 阵雨,再到 ☇ 风暴:
捷径:让 Claude 来构建它
你可以跳过这六个步骤。Claude Code 知道如何编写 mod,所以你只要描述你想要的那个,让它来完成即可。用 claude 启动一个会话,然后粘贴下面这段提示词:
代码文本
Make me a Claude Code mod called token-weather: a live forecast of my context window, shown in the band above the prompt.
What it should show, on one line:
- A weather icon and word for how full the context window is: under 25% ☀ Clear (yellow), 25–49% ☁ Cloudy (cyan), 50–74% ☂ Showers (blue), 75–89% ☇ Storm (magenta), 90% and up ↯ Compact soon (red).
- The percentage used, then the tokens used out of the window, like "134.4k / 200k".
- A small chart of the last 12 turns, drawn with ▁▂▃▄▅▆▇█.
- How much the last turn added, like "▲ +98.3k last turn".
It should update after every turn.Claude 会先询问一次,是否要为该会话开启热重载。允许后,当 Claude 的这一轮结束时,状态条就会出现在提示符上方。从那时起,每次修改都会原地重载,所以你可以继续要求微调(“让 Storm 从 70% 开始”“在末尾加上美元成本”),并看着状态条发生变化。该 mod 只会在这个会话中加载,它的文件夹稍后也会被清理掉,所以如果你想保留它,就把该文件夹复制出来,并像安装其他插件一样安装它(第 6 步)。
注意,这段提示词只描述了你想看到什么。你不需要了解 API 也能写出一个。Claude Code 内置的编写 mod 指南会说明怎么做:把状态保存在什么地方才能在重载后继续存在,如何用 claude plugin validate 检查插件,以及该挂接哪些事件。改改“它应该显示什么”那几行,它就成了你的 mod,而不是我们的。
如果你更想先看看它是如何组装起来的,或者想检查 Claude 写了什么,那就继续读下去。
第 1 步:创建文件夹
先确认你的 Claude Code 版本足够新:
代码Shell
claude --version # 2.1.287 or later
创建如下布局:
代码文本
token-weather/
├── .claude-plugin/
│ ├── plugin.json
│ └── types/ (written by Claude Code when it loads the mod)
├── hooks/
│ ├── hooks.json
│ └── token-weather.mjs
├── types/
│ └── index.d.ts (added in step 3)
└── tests/
└── token-weather.test.ts (added in step 5).claude-plugin/plugin.json 是一个标准的插件清单:
代码JSON
{ "name": "token-weather", "version": "0.1.0", "description": "A live forecast of the context window, drawn above the prompt.", "author": { "name": "You" } }
hooks/hooks.json 指向该模块。一个 mod 恰好只有一个模块:
代码JSON
{ "modules": ["./token-weather.mjs"] }
第 2 步:画点东西
提示符正上方的这条带状区域是一个名为 AbovePrompt 的组件。Claude Code 自己不会在那里绘制任何内容,所以它很适合作为第一个目标。挂接它的 ui.render 事件,并返回一个元素树:
代码JavaScript
// hooks/token-weather.mjs export function register(on) { on("ui.render", { component: "AbovePrompt" }, ($, e, next) => { const { Box, Text } = $.ui.resolve(e); return Box({ paddingX: 1, children: [Text({ color: "yellow", bold: true, children: "☀ Clear skies" })], }); }); }
这些元素不是全局对象。$.ui.resolve(e) 会返回当前绘制表面的构造函数,因为 Claude Code 所绘制的每种表面支持的集合都略有不同。JSX 也可以使用,h 可作为工厂。
用已加载该插件的状态启动一个会话:
代码Shell
claude --plugin-dir ./token-weather“☀ Clear skies” 会出现在提示符上方。保持该会话开启。系统会监视这个文件夹,所以每次保存都会原地重载模块,无需重启。这种快速反馈循环正是让编写 mod 如此有趣的主要原因。
提示:一旦你知道了大致结构,就像那个快捷方式那样向 Claude 描述下一个 mod。它会把插件写入一个文件夹,并在同一会话中热重载。
第 3 步:读取真实数字并将它们保存在 $.state 中
$.session.usage() 返回的数字与状态行相同。context.tokens 是上一条回复所基于的输入,context.window 是模型的窗口,context.percent 是前者除以后者。这个调用是免费的:只有当你请求 breakdown 时,它才会发送一个 token 计数请求。
在会话开始时以及每一轮之后取一次读数:
代码JavaScript
on("session.start", async ($, e, next) => { const result = await next(e); await takeReading($); return result; }); on("turn.complete", async ($, e, next) => { const result = await next(e); if (!e.agentId) { await takeReading($); // main-loop turns only, not subagents } return result; });
两个 hook 都会先调用 next(e),然后再观察。两者都不会改变实际发生的事。
把读数保存在什么地方。模块级的 let readings = [] 看起来像是显而易见的选择,但热重载意味着一次全新的加载:register 会再次运行,session.start 会再次触发,模块变量也会重新开始。改为把历史放进 $.state。它会在整个会话期间把具名值保存在宿主中,并且它们在重载后仍然存在。
代码JavaScript
// Held by the host, so the history survives a hot reload of this file. const readings = { plugin: "token-weather", key: "readings" }; async function takeReading($) { const { context } = await $.session.usage(); if (!context?.window) return; const tokens = context.tokens ?? 0; const percent = context.percent ?? Math.round((tokens / context.window) * 100); const { value: history = [] } = await $.state.get(readings); await $.state.set(readings, [...history, { tokens, window: context.window, percent }].slice(-HISTORY)); }
状态值是在插件的类型契约里声明的,这是 manifest 指向的一个很小的 .d.ts 文件。添加 types/index.d.ts:
代码TypeScript
export type TokenWeatherReading = { tokens: number; window: number; percent: number }; declare module "claude-code" { interface PluginState { "token-weather": { readings: TokenWeatherReading[] }; } }
然后把 "types": "./types/index.d.ts" 加到 plugin.json 中。如果你跳过这一步,claude plugin validate 会用一条点明修复方式的错误来阻止你:token-weather.readings is not declared: the manifest's types contract must name it in interface PluginState { … }。
作为回报,你会免费获得重绘。渲染 hook 运行期间进行的 $.state.get 会让那次绘制订阅更新,因此之后每一次 $.state.set 都会重绘这条带。你永远不需要调用 $.ui.invalidate。
第 4 步:绘制预测
这是整个模块:
代码JavaScript
// Token Weather: a live forecast of the context window, above the prompt. const HISTORY = 12; const BARS = "▁▂▃▄▅▆▇█"; const FORECAST = [ { upTo: 25, icon: "☀", word: "Clear", color: "yellow" }, { upTo: 50, icon: "☁", word: "Cloudy", color: "cyan" }, { upTo: 75, icon: "☂", word: "Showers", color: "blue" }, { upTo: 90, icon: "☇", word: "Storm", color: "magenta" }, { upTo: Infinity, icon: "↯", word: "Compact soon", color: "red" }, ]; // Held by the host, so the history survives a hot reload of this file. const readings = { plugin: "token-weather", key: "readings" }; export function register(on) { on("session.start", async ($, e, next) => { const result = await next(e); await takeReading($); return result; }); on("turn.complete", async ($, e, next) => { const result = await next(e); if (!e.agentId) { await takeReading($); // main-loop turns only, not subagents } return result; }); on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => { const { value: history = [] } = await $.state.get(readings); if (e.props.hasSurvey || history.length === 0) { return next(e); } const { Box, Text } = $.ui.resolve(e); return band(Box, Text, history, e.props.bodyColumns); }); } async function takeReading($) { const { context } = await $.session.usage(); if (!context?.window) return; const tokens = context.tokens ?? 0; const percent = context.percent ?? Math.round((tokens / context.window) * 100); const { value: history = [] } = await $.state.get(readings); await $.state.set(readings, [...history, { tokens, window: context.window, percent }].slice(-HISTORY)); } function band(Box, Text, history, columns) { const now = history[history.length - 1]; const f = FORECAST.find((b) => now.percent < b.upTo); const parts = [ Text({ color: f.color, bold: true, children: `${f.icon} ${f.word}` }), Text({ children: ` ${now.percent}% of context` }), Text({ dimColor: true, children: ` ${short(now.tokens)} / ${short(now.window)}` }), ]; if (columns >= 60) { parts.push(Text({ dimColor: true, children: " last turns " })); parts.push(Text({ color: f.color, children: sparkline(history) })); if (history.length > 1) { parts.push(Text({ dimColor: true, children: trend(history) })); } } return Box({ flexDirection: "row", paddingX: 1, children: parts }); } function sparkline(history) { const top = Math.max(...history.map((r) => r.tokens), 1); return history.map((r) => BARS[Math.floor((r.tokens / top) * (BARS.length - 1))]).join(""); } function trend(history) { const delta = history[history.length - 1].tokens - history[history.length - 2].tokens; if (delta === 0) return " steady"; return delta > 0 ? ` ▲ +${short(delta)} last turn` : ` ▼ ${short(-delta)} last turn`; } function short(n) { if (n >= 1_000_000) return `${+(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${+(n / 1_000).toFixed(1)}k`; return String(n); }
有三个细节值得你复制到自己的 mod 里:
- 组件的 props 在
e.props上。hasSurvey会告诉你某个 survey 想要这条带,因此 hook 会用next(e)把它让出去。bodyColumns是这条带的真实宽度,当转录旁边停靠着一个面板时,它会比终端更窄。按这个宽度来设置树的大小。只有e.component、e.surface、e.requestId和e.viewport位于e的顶层。 - 没有内容可画时就放行。返回
next(e)会把这条带交还给 Claude Code 和其他 mod。 - 使用单宽符号,不要用 emoji。☀ ☁ ☂ ☇ ↯ 在每一种终端字体中都能对齐。
保存文件后,正在运行的会话就会把它接上。读取过几个大文件的回合之后,这条带会像本节开头的录屏中那样,从 Clear 变到 Showers,再变到 Storm。
第 5 步:验证和测试
claude plugin validate 会以与 Claude Code 相同的方式读取 manifest 和模块源码,并报告该模块挂接了什么、调用了什么:
代码Text
$ claude plugin validate ./token-weather
> types ./types/index.d.ts declares state: token-weather.readings
> ./token-weather.mjs hooks: session.start, turn.complete, ui.render{component=AbovePrompt}
> ./token-weather.mjs calls: $.session.usage (via takeReading), $.state.get, $.state.set (via takeReading), $.ui.resolve
> ./token-weather.mjs state writes: token-weather.readings
> ./token-weather.mjs state reads: token-weather.readings
√ Validation passedclaude plugin test 会在真实的 Claude Code 运行时中运行插件的 *.test.ts 文件。测试通过 on 注册的 hook 会在链中的 mod 之后运行,并对 Claude Code 本会给出的回答进行桩替换,因此你可以精确控制 $.session.usage() 返回什么:
代码TypeScript
// tests/token-weather.test.ts import { describe, expect, test } from "claude-code/testing"; describe("token-weather", () => { test("the band follows the context window", async ($, on) => { // Hooks registered here run after the mod and stub what Claude Code would answer. let tokens = 36_100; on("session.start", ($, e) => ({ cwd: e.cwd })); on("session.usage", () => ({ value: { startedAt: 0, rateLimits: [], context: { tokens, window: 200_000, percent: Math.round(tokens / 2_000) } }, })); on("turn.complete", () => ({ text: "" })); await $.session.start({ surface: "terminal", isInteractive: true, cwd: "/work" } as any); const ui = await $.ui.mount({ plugin: "token-weather", surface: "terminal", component: "AbovePrompt", props: { hasSurvey: false, isWorking: false, maxRows: 10, bodyColumns: 120 }, } as any); expect(await ui.find({ type: "Text", text: /Clear/ })).toBeDefined(); tokens = 134_400; await $.turn.complete({ reason: "answer", answer: "ok", durationMs: 1 } as any); expect(await ui.find({ type: "Text", text: /Showers/ })).toBeDefined(); expect(await ui.find({ type: "Text", text: /67% of context/ })).toBeDefined(); expect(await ui.find({ type: "Text", text: /▲ \+98\.3k last turn/ })).toBeDefined(); await ui.unmount(); }); });
代码Text
$ claude plugin test ./token-weather
(pass) token-weather > the band follows the context window
1 pass
0 fail测试还会检查第 3 步中的重绘行为。turn.complete 之后,这条带会更新,而 mod 从未请求过重绘。
mod 就是插件,因此它的发布方式也一样。把它放进一个 marketplace 里,而这个 marketplace 可以简单到只是一个带有 .claude-plugin/marketplace.json 的文件夹:
代码JSON
{ "name": "my-mods", "owner": { "name": "You" }, "plugins": [{ "name": "token-weather", "source": "./token-weather" }] }
代码Shell
claude plugin marketplace add ./my-mods
claude plugin install token-weather@my-mods --scope user要把你的版本与一个完成版进行比较,请查看 anthropics/claude-code-playground 中的完整 Token Weather mod。
分享你的 mod
mod 是一个 Claude Code 插件,所以你分享它的方式与分享其他任何插件完全相同,没有任何新东西需要学。把这个 mod 放进一个带有 marketplace 文件的 GitHub 仓库里,那个仓库就会成为你的 marketplace。任何人都可以从中安装,而你可以通过一次普通的 push 来更新它。
在 Claude Code 中安装只需三条命令:
代码文本
/plugin marketplace add your-org/my-mods
/plugin install token-weather@my-mods
/reload-plugins重新加载时 mod 就会启动。如果它没有显示出来,重启 Claude Code。
mod 是在你机器上的 Claude Code 内部运行的代码,拥有与 Claude Code 相同的访问权限,而且它由发布者编写,而不是 Anthropic 编写。所以安装 mod 要像安装软件包一样:先阅读 repo,只从你信任的人那里安装。在你运行命令之前,不会安装任何东西。
Claude 目录接受包含 mod 的插件,你也可以在 claude.ai/directory/manage 提交你的插件,这样人们即使没有你提供的链接也能找到它。
另外两个 MOD
Token Weather 只负责观察和绘制。接下来的两个 mod 会介入事件、打开面板并接收输入。
两者的完整代码都在 anthropics/claude-code-playground 中:Blast Radius 和 Replay Theater,各自都带有一个 README,解释了它是如何构建的。
Blast Radius:在危险命令运行前先看看它会改动什么
当 Claude 调用 Bash 执行 rm -rf、git reset --hard、git clean、强制 push 或数据库迁移时,Blast Radius 会拦住这次调用。它会推算该命令会影响什么,并打开一个带有 继续 和 取消 的面板。按下 2,Claude 会收到一条附带原因的拒绝。按下 1,命令就会按原样运行。
它使用了三个 hook:Bash 上的 tool.call,以及 Pane 和 AbovePrompt 上的 ui.render。它的核心是上表中的 “answer” 动作:
代码JavaScript
on("tool.call", { tool: "Bash" }, async ($, e, next) => { const risk = classify(String(e.command ?? "")); if (risk === null) return next(e); // everything else runs as normal const report = await measure($, risk, await $.session.cwd()); // git status, git clean -n, du, ... held = { command: e.command, risk, report, decision: null }; const opened = await $.ui.open({ id: "blast-radius", title: "Blast Radius", focus: true }); if (!opened.isPlaced) held.where = "band"; // too narrow for a pane: draw above the prompt while (held.decision === null && !next.signal.aborted) { await $.process.run(["sleep", "0.25"]); // time inside $ calls doesn't count against the hook's time limit } if (held.decision === "proceed") return next(e); // let it run return { deny: `Blast Radius held this command: the user pressed Cancel. It would have: ${report.summary}.` }; });
它说明了什么:
- 用
$.process.run进行试运行。 报告来自这些工具自己的命令:git status --porcelain、git clean -n、git log HEAD..origin/main、showmigrations。参数以 argv 数组形式传入,因此路径中的任何内容都不会作为 shell 代码执行。 - 拦住一次调用。 每次分发时,一个 hook 都有 10 秒属于自己的时间,但在
$调用内部等待所花的时间不计入其中。循环会等待短暂的sleep进程,直到某个按钮的onPress设定决定;而当next.signal中止时(你按了 Esc),它就会放弃。 - 带热键的按钮。
Button({ label: "Proceed", hotkey: "1", onPress })可以通过点击、Tab 和 Enter,或数字键来操作。 - 降级到 band。 当终端足够宽时,它会在对话记录旁停靠一个面板。当
$.ui.open回答isPlaced: false时,同样的报告会绘制在提示符上方:

它是安全网,不是权限系统。它读取的是命令文本,所以 $(…)、别名以及调用 rm 的脚本都能绕过它。要进行硬性拦截,请使用权限规则。
Replay Theater:逐步查看上一轮的编辑
在一轮运行期间,Replay Theater 会记录每一次 Edit 和 Write 调用:文件,以及前后的文本。当这一轮结束时,提示符上方会出现一条提示。按下 r(或输入 /replay),一个面板就会一次展示一个 diff 地带你查看这些编辑,并带有编号步骤条以及 上一项、下一项 和 关闭 按钮。
它从不阻止或更改编辑。它只是观察:
代码JavaScript
on("tool.call", async ($, e, next) => { if (EDIT_TOOLS.has(e.tool)) state.pending.push(...(await stepsFor($, e))); // old/new text → diff return next(e); // the edit runs untouched }); on("turn.start", ($, e, next) => { if (!e.agentId) state.pending = []; return next(e); }); on("turn.complete", async ($, e, next) => { const r = await next(e); if (!e.agentId && state.pending.length) state.replay = state.pending; // one replay per turn return r; }); 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" }));
它说明了什么:
- 对事件进行配对。
turn.start和turn.complete把这些编辑括成每轮一次回放,而e.agentId会把 subagent 的轮次排除在分组之外。 - 注册 slash 命令。 在
session.start中使用$.command.register,然后在command.run上响应它。 - 读取文件。对于 Write,
$.fs.read会在写入生效前拿到旧内容,因此 diff 是真实的。 - 放置位置由界面负责。在全屏模式下,面板停靠在右侧。在 80 列宽时,它会以内联方式在提示符上方打开。无论哪种方式,这个 mod 绘制的都是同一棵树。

四个值得保持的习惯
- 善用 Claude Code 为你写出的类型。每次它加载你的 mod 时,Claude Code 都会把适用于你构建的声明写入该 mod 的
.claude-plugin/types/文件夹,因此你的编辑器和tsc -p无需额外步骤就能工作。它们是每个事件、$上每个方法以及每个元素 props 的参考。 - 从
e.props读取 props。hasSurvey、bodyColumns和其他内容都在那里,而不在e本身上。 - 为热重载做好准备。每次保存都会再次运行
register和session.start,所以要把数据放在$.state里,而不是模块变量里。 - 当绘图没有显示出来时,去看日志。运行
claude --debug,并查找一行说明某个 hook 返回了一棵未通过校验的树。
你会做什么样的 mod?
这里的三个 mod 分别源自一个问题:我的上下文有多满?、这个命令将要删除什么?以及Claude 刚刚改了什么?你的问题会不一样,而这正是关键。下面是一些可作为起点的想法:
- 一个来自
$.session.usage()的成本或速率限制计量器,作为带有$.ui.status的状态行 - 一个
prompt.submithook,把你团队的约定添加到每个 prompt 中 - 一个面板,列出 Claude 在本次会话中读取过的文件,作为它已看过内容的实时地图
- 一个专注计时器,在一次长回合结束时用
$.ui.toast发送 toast - 一个针对你的技术栈调校的
tool.call防护,例如生产 kubectl contexts 或terraform apply
做出了一个你现在每天都在用的 mod?把它连同 GIF 或运行截图一起发到 X 或 LinkedIn 上,这样其他开发者就能看到有哪些可能性。把这个插件放到市场中(分享你的 mod)并附上链接,这样任何喜欢它的人都可以用三条命令安装它。
来源:Claude Developer Blog · claude.dev