claudeers.
// Productivity

claude-code-roof-mod

用 Claude Mods 给 Claude Code 换屋顶:不改二进制,把系统提示和英文提醒换成你自己的字(2.1.287+)

// Productivity[ cli ][ claude ]#claude#productivity◷ MIT$open-sourceupdated 4 days ago

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.
// or install directly (claude-plugin)

⚠ 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
// or clone
git clone https://github.com/kagamiurayama/claude-code-roof-mod

// compatibility

Platformscli
Operating systems—
AI compatibilityclaude
LicenseMIT
Pricingopen-source
LanguageJavaScript

Get your FREE $2.50 API credits to access TickAtlas financial data ↗

用 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.attachmentClaude 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、工作目录没被信任、组织的托管设置禁止。这几种都是整类关掉,不会时有时无。

第六步:升级以后

  1. 跑一遍探针,对比节名和提醒类型;
  2. 重跑 gen_fragments.py,重新抽原句;
  3. 想看本版本的完整定义:用 --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.

5 views
★ 12 stars
unclaimed
updated 4 days ago

// embed badge

claude-code-roof-mod on Claudeers
[![Claudeers](https://claudeers.com/api/badge/claude-code-roof-mod.svg)](https://claudeers.com/claude-code-roof-mod)

// retro hit counter

claude-code-roof-mod hit counter
[![Hits](https://claudeers.com/api/counter/claude-code-roof-mod.svg)](https://claudeers.com/claude-code-roof-mod)

// reviews

// guestbook

0/500

// related in Productivity

🔓

Agent skills for Obsidian. Teach your agent to use Obsidian CLI and open formats including Markdown, Bases, JSON Canvas.

// productivitykepano/★ 49,094◷ MIT[ claude ]
🔓

Garry's Opinionated OpenClaw/Hermes Agent Brain

// productivitygarrytan/⟨TypeScript⟩★ 30,518◷ MIT[ claude ]
🔓

Open source repository of plugins primarily intended for knowledge workers to use in Claude Cowork

// productivityanthropics/⟨Python⟩★ 25,627◷ Apache-2.0[ claude ]
🔓

An open-source alternative to Claude Cowork (powered by opencode)

// productivitydifferent-ai/⟨TypeScript⟩★ 23,819◷ NOASSERTION[ claude ]
→ see how claude-code-roof-mod connects across the ecosystem