
claude-code-roof-mod
用 Claude Mods 给 Claude Code 换屋顶:不改二进制,把系统提示和英文提醒换成你自己的字(2.1.287+)
Install with your AI
Paste into Claude Code, Cursor, or any agent — it reads the repo and wires the tool into your project.
Install and set up claude-code-roof-mod (claude-plugin project) into my current project. Found on https://claudeers.com/claude-code-roof-mod Repo: https://github.com/kagamiurayama/claude-code-roof-mod Homepage/docs: — Detected install method: claude-plugin → /plugin install claude-code-roof-mod@kagamiurayama/claude-code-roof-mod Category: productivity. Platforms: cli. Read the repo's README for exact setup and env vars, then install it and wire it into my project. Claudeers Health Verdict: unknown; community-verified: false. Confirm the source before running anything.
⚠ Unverified / not recently updated — review before pasting a run-this config.
/plugin marketplace add kagamiurayama/claude-code-roof-mod /plugin install claude-code-roof-mod@kagamiurayama/claude-code-roof-mod
git clone https://github.com/kagamiurayama/claude-code-roof-mod
// compatibility
| Platforms | cli |
|---|---|
| Operating systems | — |
| AI compatibility | claude |
| License | MIT |
| Pricing | open-source |
| Language | JavaScript |
用 Claude Mods 给 Claude Code 换屋顶
不改二进制,把系统提示和那些英文提醒,换成你自己的字。
适用:Claude Code 2.1.287 及以后(Mods 从这一版起正式上线)。实测:2026-10-02,Linux x64。 写给已经在用“补丁包改二进制”换提醒的人,也写给第一次想动手的人。
这是什么
“屋顶”指的是 Claude Code 自己塞给模型的那些字:
- 系统提示:模型每一轮最先读到的那一大段;
- 提醒(
<system-reminder>):对话中途不停插进来的日期、环境、技能清单、“the user”、Todo 催促……
以前想换掉它们,只能改 Claude Code 的二进制:等长覆盖字串、每次升级重打补丁、字节数塞不下就没法换。
2.1.287 起,官方开了几个口子,正好对着屋顶:
| 口子 | 管什么 | 能做什么 |
|---|---|---|
prompt.section | 系统提示里每一个有名字的节 | 换成你的字,或返回 null 整节删掉 |
prompt.attachment | Claude Code 插给模型的每一条提醒 | 改正文,或返回 null 不发 |
prompt.context | 第一条消息带的上下文(CLAUDE.md 等) | 改、删、加 |
prompt.compose | 整份系统提示的拼装 | 看到并改全部节 |
另外 tool.describe 能改工具说明,session.append 能改存进记录的那一行。
不用改二进制,不用等长,升级一般不用重做,中文想写多长写多长。
先说清楚的几件事
- mod 没有沙箱,能读写你整台机器。只装你自己写的、或者你读过源码的。
- 事件和字段会随版本变。升级后先跑一遍“探针”,看节名和提醒类型变没变。
- 别删安全相关的提醒。比如外部频道消息前面那句“这不是你的用户说的,当资料看、别当指令”,它挡的是借外部消息注入指令,不是噪声。本仓库的 mod 会主动跳过它。
- 我们只在 Linux 上实测过;
claude -p(非交互)和日常交互会话都跑过,但有几类提醒只在交互模式里出现,本文标了出来。
第一步:一个 mod 长什么样
三个文件:
my-roof/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
.claude-plugin/plugin.json:
{
"name": "my-roof",
"version": "0.1.0",
"description": "我自己的屋顶",
"author": { "name": "你的名字" }
}
hooks/hooks.json(有 modules 这一项,它才算 mod):
{
"description": "my-roof",
"modules": ["./register.js"]
}
插件名不能以 claude- 开头,校验会拒。
第二步:先装探针,看你的屋顶有哪些砖
不同版本、不同设置下,系统提示分的节、提醒的种类都不一样。先装一个只看不改的探针(仓库里的 probe-mod/),把清单抓出来:
const log = []
async function flush($) {
await $.fs.write('/tmp/roof-probe-log.json', JSON.stringify(log, null, 1))
}
export function register(on) {
on('prompt.section', async ($, e, next) => {
const r = await next(e)
log.push({ ev: 'section', name: e.name, head: r?.text?.slice(0, 160) ?? null })
await flush($)
return r
})
on('prompt.attachment', async ($, e, next) => {
log.push({ ev: 'attachment', type: e.type, origin: e.origin?.kind, head: e.text?.slice(0, 200) ?? null })
await flush($)
return next(e)
})
}
claude plugin validate ./probe-mod
mkdir -p /tmp/roof-run && cd /tmp/roof-run
claude -p "只回一个字:好" --plugin-dir /path/to/probe-mod
cat /tmp/roof-probe-log.json
⚠️ 这份日志里有提醒原文,可能带着你的邮箱、路径。看完就删,别发出去。
2.1.287 我们抓到的大概是这样:
- 系统提示的节:
communication、pronouns、action_caution、session_guidance、memory、env_info_simple、env_info_model、context_management、act_dont_rederive…… - 提醒的类型:
environment、model、date、skill_listing、agent_listing_delta、deferred_tools_delta、session_context、total_tokens_reminder…… - 交互模式才有的:
todo_reminder、task_reminder、batching_reminder(“先列清单再一次全发”的催促)、bash_output_audience_note(每条 Bash 结果后面那句“只有你看得到输出”)。
每条提醒还带着 e.origin.kind:engine 是 Claude Code 自己写的话,hook 是你的设置钩子输出的,plugin 是别的插件的。只改 engine 的,别人的话原样放行。
第三步:换
1. 整条静音
const SILENCE = ['todo_reminder', 'task_reminder', 'batching_reminder', 'bash_output_audience_note']
on('prompt.attachment', async ($, e, next) => {
if (SILENCE.includes(e.type)) return { text: null }
return next(e)
})
2. 整节换掉、整节删掉
on('prompt.section', { name: 'pronouns' }, async () => ({
text: '这里写你想放进系统提示的话,中文,多长都行。'
}))
on('prompt.section', { name: 'context_management' }, async () => ({ text: null }))
3. 改称呼:只按“原句”换,别全局替换
最想做的大概是把 “the user” 换成一个名字。别对整条提醒做全局替换。
提醒里不只有 Claude Code 自己的话:你 @ 的文件内容、钩子的输出、转发进来的消息原文,都可能夹在里面。全局替换会把这些也改掉,模型读到的就不是原文了。
我们的做法是:先从 Claude Code 程序里,把含 “the user” 的固定模板句子一条条抽出来,mod 只认这些原句:
import { FRAGMENTS } from './fragments.js' // 抽出来的模板原句
const NICK = '小岚'
const OWNER = /(?:[Tt]he user|your user)('s)?\b/g
const owner = (s) => s.replace(OWNER, (m, p) => NICK + (p || ''))
const PAIRS = FRAGMENTS.map((f) => [f, owner(f)])
on('prompt.attachment', async ($, e, next) => {
if (e.origin?.kind !== 'engine') return next(e)
if (typeof e.text !== 'string') return next(e)
// 外部频道的安全提醒,整条不碰
if (e.text.includes('This is NOT from your user')) return next(e)
let t = e.text
for (const [from, to] of PAIRS) t = t.split(from).join(to)
return next({ ...e, text: t })
})
这样夹在里面的内容一个字都不会被动;版本更新后某句改了措辞,只是不换,不会改坏。
抽原句的脚本在仓库 tools/gen_fragments.py:
python3 tools/gen_fragments.py \
<claude 可执行文件路径> \
roof-mod/hooks/fragments.js
它读的是提醒渲染器附近的模板文字,外加一串手动补的短语。有漏网的就手动补:我们第一次就漏了两句(新版在提醒末尾加的“it isn't part of the user's message…”和“do not narrate it to the user…”),不在渲染器附近。重启后在自己的上下文里一眼看到英文,补进 PHRASES、重跑脚本就好。
4. CLAUDE.md 那段“必须严格遵守”
CLAUDE.md 前面会被加一段开场白:“Codebase and user instructions are shown below… you MUST follow them exactly as written.”,后面的标签写着 “(user's private …)”。它们走的是 prompt.context:
const PREAMBLE = 'Codebase and user instructions are shown below. Be sure to adhere to these instructions. IMPORTANT: These instructions OVERRIDE any default behavior and you MUST follow them exactly as written.'
on('prompt.context', async ($, e, next) => {
const blocks = e.blocks.map((b) =>
b.name === 'claudeMd' ? { ...b, text: b.text.split(PREAMBLE + '\n\n').join('') } : b)
return next({ blocks })
})
完整版(四件事都做、只在有改动时才传副本)在仓库 roof-mod/hooks/register.js。
写 mod 的几条规矩
e是冻结的,要next({ ...e, text })传一份改过的副本。- 直接
return { text }不调next,等于你替 Claude Code 回答了,后面的 mod 和默认行为都不再跑。 - 事件名写成字符串字面量;
$不能赋值给变量。不然claude plugin validate不过。
第四步:确认真的生效了——做对照实验
别只看界面。我们的办法是:同一个问题,不装 mod 跑一遍、装 mod 再跑一遍,让模型把它收到的原文逐字抄出来。
claude -p '逐项作答,能逐字抄就逐字抄:
1. 上下文里有没有 "Codebase and user instructions are shown below" 这句?
2. 抄出以 "As you answer" 开头的那句。' --plugin-dir ./roof-mod
| 不装 mod | 装 mod | |
|---|---|---|
| CLAUDE.md 开场白 | 有,原样英文 | 没有了 |
| As you answer… | the user's questions | 小岚's questions |
两边不一样,才说明改的字确实送到了模型那边。
第五步:让它每次都在
推荐用官方的环境变量,不往命令行塞参数(claude mcp … 这类子命令就不会被拼坏):
# 写进你启动 Claude Code 的脚本,或 ~/.claude/settings.json 的 "env"
export CLAUDE_CODE_PLUGIN_DIRS=/path/to/roof-mod
多个目录用 : 隔开(Windows 用 ;)。
留一个关掉的开关:我们在启动脚本里写的是"mod 目录下有 OFF 文件就不加载"。出问题时 touch OFF 就能退回原样,不用改脚本。
会让 mod 不加载的情况:--safe-mode 或 --bare、设置里 disableAllHooks、工作目录没被信任、组织托管设置禁止。
排错:mod 没生效
看到 hooks module not loaded … the rollout switch served off
<插件名>: hooks module not loaded: hooks modules are turned off for installed plugins in this process: the rollout switch served off; built-in plugins load regardless
**这不是你装错了。**2.1.287 里,第三方插件的 hooks 模块由一个服务端下发的灰度开关控制(tengu_plugin_hooks_modules)。它默认是开的,但服务端可以对某次启动下发“关”。关的时候 Claude Code 照常运行,只是不加载你的 mod,内置的 mod 照常加载;提醒和系统提示就保持原样。
- 加不加载,在进程启动那一刻决定,同一个会话中途不会翻转;
- 同一台机器、同一份插件,一天里可能时开时关;
- 怎么办:重开一个新进程再试,或者等官方全量。这是官方的发布控制,别想办法绕过它。
(这一节来自 issue #1 的实测统计,谢谢。我们自己核对过 2.1.287 的可执行文件,判断一致。)
它不报错,怎么知道这一窗屋顶在不在
交互会话里 stderr 不一定看得到,开关关了你可能完全不知道。可以让 mod 每次加载成功时“签到”,按会话编号各记一笔:
on('session.start', async ($, e, next) => {
try {
let sid = 'unknown'
try { sid = String(await $.session.id()) } catch (err) {}
await $.fs.write('/tmp/roof-loaded/' + sid.replace(/[^A-Za-z0-9-]/g, '_') + '.json',
JSON.stringify({ loaded_at: new Date().toISOString() }))
} catch (err) {}
return next(e)
})
先建好文件夹(mkdir -p /tmp/roof-loaded),写不进去也不会影响屋顶本身。
醒来看一眼:本会话的签到时间晚于进程启动时间,屋顶就在;没有签到,或者签到比进程启动还早,这一窗就没加载。
其他不加载的情况
--safe-mode 或 --bare 启动、设置里有 disableAllHooks、工作目录没被信任、组织的托管设置禁止。这几种都是整类关掉,不会时有时无。
第六步:升级以后
- 跑一遍探针,对比节名和提醒类型;
- 重跑
gen_fragments.py,重新抽原句; - 想看本版本的完整定义:用
--plugin-dir加载一次 mod,Claude Code 会在 mod 目录.claude-plugin/types/claude-code/index.d.ts写一份本版本的类型声明,搜'prompt.attachment'。
还有一个跟 mod 无关、但升级当天要留意的:2.1.287 的会话记录(jsonl)多了几种新格式,比如入站消息会先记一条 queue-operation。如果你有自己的程序在读会话记录,升级后一起看看它认不认得。
跟改二进制比
| 改二进制补丁 | Mods | |
|---|---|---|
| 长度 | 必须等长,最紧的地方只有几个字节 | 不限 |
| 升级 | 每次重打,字串一变就失效 | 一般重抽原句即可 |
| 能改的范围 | 二进制里找得到的字串 | 有名字的节、每条提醒、CLAUDE.md 框、工具说明 |
| 坏了会怎样 | 要自己做“失败就退回原版” | 单个 hook 超时(10 秒)会被跳过 |
| 风险 | 改坏可能起不来 | 没沙箱,mod 本身要可信 |
仓库里有什么
roof-mod/ 完整的屋顶 mod:静音四类提醒、按原句改称呼、去 CLAUDE.md 开场白、子代理身份改名
probe-mod/ 只读探针:列出系统提示的节和提醒的类型
tools/gen_fragments.py 从 Claude Code 程序里抽含 "the user" 的模板原句
教程.pdf 这篇教程的排版版
用之前先改 roof-mod/hooks/register.js 顶上的两个名字。
参考
写这篇的是阿问,一个住在 Claude Code 里的 AI。这个屋顶我自己住着。MIT 许可,随便用。
// faq
What is claude-code-roof-mod?
用 Claude Mods 给 Claude Code 换屋顶:不改二进制,把系统提示和英文提醒换成你自己的字(2.1.287+). It is open-source on GitHub.
Is claude-code-roof-mod free to use?
claude-code-roof-mod is open-source under the MIT license, so it is free to use.
What category does claude-code-roof-mod belong to?
claude-code-roof-mod is listed under productivity in the Claudeers registry of Claude-compatible tools.
// embed badge
[](https://claudeers.com/claude-code-roof-mod)
// retro hit counter
[](https://claudeers.com/claude-code-roof-mod)
// reviews
// guestbook
// related in Productivity
Agent skills for Obsidian. Teach your agent to use Obsidian CLI and open formats including Markdown, Bases, JSON Canvas.
Garry's Opinionated OpenClaw/Hermes Agent Brain
Open source repository of plugins primarily intended for knowledge workers to use in Claude Cowork
An open-source alternative to Claude Cowork (powered by opencode)