暂无菜单项

Claude Code终于能自己换皮了!用Mods做出“雨姐工位”,还能拦截rm -rf

发布于
16

它不只是换头像:面板、状态栏、事件钩子、提示词改写、工具拦截与自动化测试,都能运行在Claude Code进程内部。

先说预测:Claude Code Mods最有价值的地方,不是给终端换一套皮肤,而是让开发者开始构建真正贴在Agent执行链上的交互与安全层:看见工具在做什么、在危险操作前阻断、把状态展示给用户,并用测试证明规则确实生效。

这次公开实测做了一个“雨姐工位”:她坐在Claude Code右侧面板,记录命令、文件修改和报错;测试失败会变脸,任务完成会弹提示,遇到危险删除和强制改写则在执行前拦下。

雨姐Claude Code Mod在命令执行前拦截危险删除操作
雨姐Claude Code Mod在命令执行前拦截危险删除操作。图片来自公开实测,已压缩为WebP,点击查看大图。

它看起来像趣味皮肤,底层却展示了Claude Code Mods的完整能力:Pane面板、AbovePrompt提示条、状态栏、toast、自定义命令、状态管理、prompt.compose和tool.call。

01 / 先搞清楚:Mod不是Skill,也不是MCP

社区创作者展示的粉色雨姐版Codex界面设计
社区创作者展示的粉色雨姐版Codex界面设计。图片来自公开实测,已压缩为WebP,点击查看大图。
扩展方式 主要作用 运行位置
Skill 给Claude可复用的说明、流程和资源 作为上下文与工作方法
MCP 连接外部工具、数据和服务 独立服务或连接器
传统Hooks 在固定时机执行外部命令 Claude Code进程之外
Mods 参与事件链并直接渲染界面 Claude Code进程内部

Mods运行时会持续产生事件:会话开始是session.start,提交提示词是prompt.submit,工具准备执行是tool.call,界面渲染是ui.render,一轮结束是turn.complete。开发者把函数挂到这些事件上,就能观察、改写或接管流程。

02 / 第一步:创建最小Mod和“雨姐工位”

示例的核心结构只有三个文件:

yujie/
├── .claude-plugin/
│   └── plugin.json
└── hooks/
    ├── hooks.json
    └── register.tsx

hooks.json负责声明模块:

{ "modules": ["./register.tsx"] }

register.tsx导出注册函数,在会话启动时注册命令、写状态栏并打开面板;界面渲染时把台词画到输入框上方:

export const register: Register = on => {
  on('session.start', async ($, e, next) => {
    await $.command.register({ name: 'yujie', description: '打开雨姐工位面板' })
    $.ui.status('雨姐在岗 · 铁锅已热')
    $.ui.open({ id: 'yujie', title: '雨姐工位' })
    return next(e)
  })

  on('ui.render', { component: 'AbovePrompt' }, async ($, e) => {
    const { Box, Text } = $.ui.resolve(e)
    return <Box><Text color="#e0567a">雨姐:</Text><Text>{await read($, line)}</Text></Box>
  })
}
Claude Code右侧雨姐面板、输入框提示条与底部状态栏
Claude Code右侧雨姐面板、输入框提示条与底部状态栏。图片来自公开实测,已压缩为WebP,点击查看大图。

开发时可将Mod放在会话专属开发目录中启用热重载,也可以使用claude --plugin-dir ./yujie加载本地目录。原作者测试时使用Claude Code v2.1.287以上;Mods属于早期接口,实际版本要求应以当前官方仓库和本机类型定义为准。

03 / 第二步:用Raster把像素头像搬进终端

终端无法像网页那样随意显示图片,Raster使用“▀”上半块字符和前景色、背景色,把一个字符格拆成上下两个像素。每格的数据由字符、上半像素颜色和下半像素颜色组成。

words[at] = 0x2580
words[at + 1] = top
words[at + 2] = bottom
像素头像原图与Raster字符画布中的终端显示效果
像素头像原图与Raster字符画布中的终端显示效果。图片来自公开实测,已压缩为WebP,点击查看大图。

实践中要特别注意终端色彩。tmux或某些终端会降为256色,Raster还可能压缩颜色通道,深棕和暗红容易偏成绿色或橄榄色。最稳妥的做法是直接按终端可用色板设计大颗粒、少颜色的像素图,并让不同表情保持相同构图尺寸,避免面板跳动。

04 / 第三步:tool.call的三种能力

Claude准备运行命令或修改文件时,会先经过tool.call。Mod可以做三件事:

  1. 旁观:调用next(e)原样放行,只记录结果、更新计数和表情。
  2. 改写:调用next({...e}),修改事件后继续执行。
  3. 接管:不调用next,直接返回deny或自定义结果。

最基础的旁观钩子可以在命令结束后读取结果,测试失败就切换表情并弹出通知:

on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
  const ran = await next(e)
  if (ran.isError || /fail [1-9]/.test(ran.text ?? '')) {
    await say($, 'angry', '又红了,先看看日志')
    $.ui.toast('雨姐:测试报错了')
  }
  return ran
})
雨姐Mod根据正常运行、测试报错和文件修改切换表情与台词
雨姐Mod根据正常运行、测试报错和文件修改切换表情与台词。图片来自公开实测,已压缩为WebP,点击查看大图。

这里有一个很实用的坑:npm test 2>&1 | tail -40的最终退出码可能来自tail,即使前面的测试失败,工具仍可能看起来成功。生产实现应优先保留原始退出码,例如启用pipefail,不要只靠输出文本中的“fail 1”判断。

05 / 第四步:用prompt.compose切换“东北话模式”

斜杠命令/yujie-talk可以切换一个状态,再由prompt.compose向系统提示词追加风格要求。这样用户原始输入不会被改写,关闭后下一轮也不再携带方言要求。

on('prompt.compose', async ($, e, next) => {
  const composed = await next(e)
  if (!(await read($, isDialect))) return composed
  return {
    ...composed,
    sections: [...composed.sections, {
      id: 'yujie:dialect',
      text: DIALECT,
      scope: 'session'
    }]
  }
})
方言模式下Claude使用东北大姐口吻解释代码修复结果
方言模式下Claude使用东北大姐口吻解释代码修复结果。图片来自公开实测,已压缩为WebP,点击查看大图。

这种做法不仅能做趣味口吻,也可用于团队规范:要求回答包含风险、测试结果、变更文件和回滚方法。但系统提示词改写会影响模型行为,应在界面上明确展示当前模式,避免用户不知道Agent为何突然改变表达。

06 / 第五步:在危险命令真正执行前接管

演示中对删除、强推和硬重置设置了阻断规则。命中时不调用next,工具不会执行,拒绝原因会回到Claude上下文,让模型沿着安全规则继续寻找替代方案。

if (DANGER.test(e.command)) {
  $.ui.toast('雨姐:危险命令,先别执行')
  return {
    deny: '这条命令可能删除或强行改写数据。请换一个更安全的做法,或者让用户确认后自行执行。'
  }
}
危险删除被拦截后Claude解释原因并把最终决定交还用户
危险删除被拦截后Claude解释原因并把最终决定交还用户。图片来自公开实测,已压缩为WebP,点击查看大图。

安全边界:简单正则不是权限系统。rm -r -f、脚本包装、变量展开和其他等价写法都可能绕过字符串匹配。真正的安全Mod应解析命令结构、评估文件范围、阻断符号链接与路径逃逸,并在高风险步骤显示“继续/取消”确认。

07 / 第六步:验证源码,再用事件测试证明没有真执行

claude plugin validate会静态读取清单和源码,列出注册的事件及调用接口。建议发布前加严格模式,并修复全部警告。但静态检查只能告诉你“写法大致合法”,不能证明运行时行为一定正确。

自动化测试的关键是设置一个计数器:如果危险命令被正确拒绝,真正的工具处理器一次都不应运行。

test('dangerous delete is denied before the tool runs', async ($, on) => {
  let ran = 0
  on('tool.call', () => {
    ran += 1
    return { result: { stdout: '', stderr: '', interrupted: false } }
  })

  const answer = await $.tool.call({ tool: 'Bash', command: 'rm -rf node_modules' })
  expect(answer.deny).toContain('删除或强行改写数据')
  expect(ran).toBe(0)
})

还应补测普通命令正常放行、模式开关可恢复、状态变化触发渲染,以及claude -p等无界面环境。示例就发现过一个问题:阻断命令时尝试拉起面板,在无UI测试环境会报错,最终通过捕获界面调用异常解决。

08 / 从业者观点:最值得做的不是角色皮肤,而是“可见的安全闸”

趣味角色让Mods容易传播,但真正有生产价值的方向是:

  • 把Agent正在执行的命令、文件、测试和权限状态做成可见面板;
  • 对删除、覆盖、强推、发布和凭证操作设置影响范围评估;
  • 在规则阻断后给Claude结构化原因,让它自动选择更安全的替代方案;
  • 把团队编码规范、发布检查和审计信息做成可测试的运行时策略。

我们的判断:Mods把Claude Code从“可配置的Agent”推向“可编程的Agent宿主”。但它越靠近工具执行链,插件代码的权限与风险就越高。安装第三方Mod前必须审查源码、固定版本、查看它监听的事件和调用的接口,并在隔离项目中测试。

官方资料与站内延伸

Claude Code Mods站内导航:
https://www.zuoshipin.com/link/47313.html

Anthropic官方Mods目录:
https://github.com/anthropics/claude-code/tree/main/mods

Anthropic站内导航:
https://www.zuoshipin.com/link/1191.html

站内Codex插件生态观察:
https://www.zuoshipin.com/article/33506

站内Claude与Codex效率工具合集:
https://www.zuoshipin.com/article/31931

常见问题(FAQ)

Claude Code Mods是什么?
Claude Code Mods是运行在Claude Code进程内部的JavaScript或TypeScript扩展机制,可监听会话、提示词、工具调用和界面渲染事件。
Claude Code Mods和Skill、MCP有什么区别?
Skill主要提供工作说明,MCP连接外部工具;Mods则直接参与Claude Code运行时事件并渲染面板、状态栏和通知。
如何加载本地Claude Code Mod?
开发者可使用会话开发目录启用热重载,或通过claude --plugin-dir加本地插件目录。版本要求和目录结构应以当前官方Mods资料为准。
用正则拦截rm -rf安全吗?
不够安全。命令存在空格变化、变量、脚本包装和等价写法,生产实现需要结构化解析、路径范围检查、权限限制和用户确认。
Claude Code Mod发布前如何测试?
先用claude plugin validate检查清单和接口,再用claude plugin test模拟工具事件,确认危险命令没有进入真实处理器,并测试普通命令、模式开关与无界面环境。
0 点赞
0 收藏
分享
0 讨论
反馈
热门资讯
相关素材

暂无数据