一个 hook 让 Claude 不再满嘴 load-bearing,但它只遮住了我的眼睛

cover

哈喽,我是飞飞。

天天用 Claude Code 的人,估计都被那几个词磨出茧子了。

你让它改个 bug,它开口先来一句「you’re absolutely right」。你问它这段逻辑,它张嘴就是「这是一处 load-bearing 的设计」。再写两行,「honest take」又冒出来了。不是它说得不对,是那股翻来覆去的腔调,听多了真上头。

前两天刷到一个帖子,作者受够了,动手写了个 hook,把这些词自动换掉。「seam」换成「whatchamacallit」,「you’re absolutely right」换成「I’m a complete clown」,「load-bearing」换成「cooked」。看得我笑了几秒。

但笑完我就顺手把它扒开看了看。这一看,反而想清楚了一件我一直没说透的事:hook 这东西,到底该拿来干嘛。

那个遮词 hook 怎么玩

它用的是 Claude Code 一个不太起眼的 hook,叫 MessageDisplay。

顾名思义,这个 hook 在 Claude 的消息「要显示给你看」的那一刻触发。你挂一个脚本上去,脚本拿到这段要显示的文本,做个正则替换,再把换好的内容吐回去。屏幕上你看到的,就是替换后的版本。

配置也简单,在 settings.json 的 hooks 块里挂一个 MessageDisplay,命令指向你那个替换脚本就行。脚本里写什么词换什么词,随你发挥。原帖作者给的那几个是玩梗用的,他自己也说,你完全可以换成更有用的。

挂好之后要开个新会话才生效,因为 Claude Code 是启动时给 hook 配置拍快照的,中途改不认。

到这儿,你可能觉得这不挺好?眼不见心不烦。别急,坑就在下一步。

它只改你看到的

我盯着这个机制多看了两眼,发现一个要命的地方:它改的,只是「显示」这一层。

Claude 那句原话,一个字没变。它生成的还是「load-bearing」,只是在递到你眼前的最后一步,被脚本偷偷换了个皮。更关键的是,下一轮对话,Claude 读回自己上一条输出时,读到的还是原文,不是你屏幕上那个被换过的版本。

说白了,这个 hook 治的是你的眼睛,不是它的嘴。

它嘴上照样满口 load-bearing,你只是看不见了。就像你嫌同事口头禅烦,没去跟他说,而是给自己戴了副耳机。世界清净了,但那是你这头清净,不是他那头改了。

我为什么敢这么笃定?因为这台机器我熟。这类 hook 挂在整条链路的最末端,Claude 早把话生成完、存进对话历史了,脚本才在出口处拦一道做替换。它够不着 Claude 的脑子,也改不了已经落进上下文的那句原文。你下一轮再问它,它眼里那句还是 load-bearing。

这不是说这 hook 没用。你要的就是「我眼前干净」,那它完美交付。可要是你指望它能让 Claude 从此改了这毛病、连带影响它后面的判断,那就想岔了。它压根没碰到 Claude 干活的那一层。

我拿 hook 干的正经活

这事之所以戳中我,是因为我天天在用 hook,而且用的全是另一头。

Claude Code 的 hook 有一大把生命周期事件,MessageDisplay 只是其中最不起眼的一个。我以前压根没正眼瞧过它,就因为它只管显示。我天天挂的是另外几个。

给你说三个我配置里常年不下岗的。

第一个,类型检查。我 CLAUDE.md 里写过一句「改完代码跑一遍类型检查」。可这是建议式的,长会话上下文一压缩,它转头就忘,好几次带着类型错误就上线了。后来我换成一个 PostToolUse hook:每次编辑完文件自动跑类型检查,一旦报错就用 exit 2 把这步直接拦下来,再把错误甩回给 Claude 让它自己修。exit 2 这个返回码是命门,它不是提个醒,是真把这步堵死,Claude 不修好就过不去。从那以后这毛病根治了。

第二个更糙,格式化。Claude 写完代码从不主动跑 Prettier,我加了个 PostToolUse hook,匹配 Write 和 Edit,三行配置,它一动文件就自动格式化,我再没为缩进跟它拌过嘴。

第三个是防手贱。我用 PreToolUse 卡在它执行命令之前,配上 permissions.deny,把带通配符的 rm、git push –force 这种一锤子买卖的命令直接拦掉。它再自信,这道闸也得先问过我。

这些 hook 还有些藏得深的开关我也摸过。比如 async,让脚本丢后台跑不卡你手上的活;再比如 asyncRewake,后台默默跑,真出岔子了反过来把 Claude 叫醒来收拾。全是冲着「它干活的时候我怎么管住它」去的,没一个是管显示的。

你看出这几个跟遮词 hook 的差别了吗?我这些拦的都是 Claude 的「动作」,它要干一件事,我在那个节点上卡一道关。遮词那个碰的是「显示」,它干完了、也生成完了,我只在递到你眼前之前换了层皮。

一个管它手上做了什么,一个管你眼里看到什么。差着一整层。我心里那句老话是这么排的:CLAUDE.md 管记忆,skill 管流程,hook 管纪律。纪律这活,只能靠动作层的硬拦,靠不了显示层的一层滤镜。

遮丑和治本差一层

真要治 Claude 满嘴 AI 腔那种毛病,我走的也不是遮词这条路。

我那条写作流水线里,专门有个检测 AI 味的 skill,配一套 grep 硬扫。正文里的中文长破折号必须清零,「不是 X 是 Y」那种对仗最多留一处,字数卡在区间里,小标题超字数或者带括号也揪出来。命中了就打回去重写。这套是真把那些味儿从文字里删掉了,不是拿别的词盖住。

更要紧的是它还会自己长本事。我另配了个 skill,每写完一篇,就把这回新踩的坑提炼成一条规则,回写进那份手册。攒到现在一百多次迭代,一百多条。这套规则越用越紧,不是我一开始就想周全的,是一篇篇踩出来的。

删掉和盖住,看着都让画面干净了,骨子里差得远。盖住那个,你撤掉滤镜,原样还在。删掉那个,源头就没了。

所以我心里给 hook 分了两层。一层是显示层,MessageDisplay 这种,治标、给人看个舒服,图一乐可以挂。另一层是动作层,PostToolUse、Stop 这些,卡在 Claude 真正干活的节点上,能拦能改能让它返工,治的是本。想让它别飘,你得往动作层写死规则,别指望在显示层遮丑。

题眼就在这儿:让 Claude 不飘,真正管用的不在遮住你看到的那几个词,在它每次干完活那道你亲手写死的关卡。

该给哪条规矩配 hook

那个遮词 hook 我没往配置里留。图一乐的东西,笑过就好。

但它值一个提醒。你翻翻自己的 CLAUDE.md,是不是也有几条软规矩,写着「应该怎样怎样」,可 Claude 三天两头不当回事?那几条,才是真正该被搬到 hook 里的。

我自己就是这么一条条搬的。哪条规矩我发现它反复不听,我就把它从 CLAUDE.md 里拎出来,钉成一个 hook。不听一次是偶然,同一条栽三回,就说明这活靠不了它自觉,得靠硬拦。CLAUDE.md 那一层,我现在只留它真记得住、也真肯照做的;凡是它爱犯懒、犯了后果还不小的,一律往 hook 搬。这条分界,是我被它坑够了才划出来的。

给你一个今天就能动手的最小动作:挑出你最常被 Claude 跳过的那一条规矩,比如「改完必须跑测试」,把它从 CLAUDE.md 的一句建议,改成一个 PostToolUse hook 的硬拦截。规矩从「它看心情听」变成「它绕不过去」,就这一下。

对了,说回那几个口头禅。你被 Claude 哪句车轱辘话烦得最狠?是「you’re absolutely right」,还是别的?评论区报个数,我看看是不是就我一个人破防了。