第 30 章:第一次修改

源码验证日期:2026-05-15,基于 commit
0d81bb6
上一章你搭好了工坊,源码可以跑起来了。这一章,你要亲手改点什么——哪怕只是改一行文字。
别小看”改一行文字”。这意味着你理解了代码结构,找到了目标位置,做了修改,构建成功,并且看到了效果。这是 Contributor 的第一步。
路线图
1 | graph LR |
目标
修改 FileReadTool 的工具描述(description),重新构建 Claude Code,然后验证 AI 确实使用了你修改后的描述。
完成后你会获得三样东西:
- 知道工具的 prompt/description 在哪个文件定义
- 知道怎么构建和测试修改
- 拥有你的第一个 git commit
第一步:找到目标
Claude Code 有三十多个工具。每个工具是一个目录,放在 src/tools/ 下面。你可以浏览一下:1
ls src/tools/
你会看到 FileReadTool/、BashTool/、FileEditTool/ 等等。每个目录里都有该工具的全部代码。
我们要改的是 FileReadTool——它负责读取文件,是 AI 最常用的工具之一。
打开 src/tools/FileReadTool/,看看里面有什么:1
ls src/tools/FileReadTool/
你会看到四个文件:
FileReadTool.ts—— 主文件,定义工具的行为prompt.ts—— prompt 模板,定义 AI 看到的工具说明UI.tsx—— UI 渲染,定义用户看到的界面imageProcessor.ts—— 图片处理辅助
我们要改的就是 prompt.ts。
第二步:读懂结构
打开 src/tools/FileReadTool/prompt.ts。这个文件不长,但结构清晰。你会看到:1
2
3
4
5
6
7
8export const FILE_READ_TOOL_NAME = 'Read'
export const DESCRIPTION = 'Read a file from the local filesystem.'
export const MAX_LINES_TO_READ = 2000
export const LINE_FORMAT_INSTRUCTION =
'- Results are returned using cat -n format, with line numbers starting at 1'
这里有三个关键概念:
DESCRIPTION —— 工具的一句话描述。这个值会被送到 AI 那里,作为工具列表中每个工具的简介。当你输入一条消息后,AI 在决定使用哪个工具时,首先看到的就是这段文字。
renderPromptTemplate() —— 工具的详细说明模板。当 AI 决定使用 Read 工具时,这段文字会被完整注入到上下文中,告诉 AI 怎么正确使用这个工具。
常量指令片段 —— LINE_FORMAT_INSTRUCTION、OFFSET_INSTRUCTION_DEFAULT 等,是详细说明中的可配置段落,可以根据运行时条件组合出不同的 prompt。
这种”常量 + 函数模板”的模式在 Claude Code 的工具系统中很常见。它把 prompt 拆成小块,方便单独修改和测试。
第三步:做出修改
我们要改的是 DESCRIPTION 常量——那句话的简介。把它改成更有个性一点的文字。
找到这一行:1
export const DESCRIPTION = 'Read a file from the local filesystem.'
把它改成:1
export const DESCRIPTION = 'Read a file from the local filesystem. Your trusty companion for exploring code.'
就是加了一句俏皮话。这不会影响工具的功能,但能让你在验证时一眼确认修改生效了。
保存文件。
第四步:理解修改怎么生效
你改了一行字符串常量。那这行常量是怎么跑到 AI 那里的?
打开 src/tools/FileReadTool/FileReadTool.ts,找到工具注册的部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17export const FileReadTool = buildTool({
name: FILE_READ_TOOL_NAME,
// ...
async description() {
return DESCRIPTION
},
async prompt() {
const limits = getDefaultFileReadingLimits()
// ...
return renderPromptTemplate(
pickLineFormatInstruction(),
maxSizeInstruction,
offsetInstruction,
)
},
// ...
})
看到了吗?description() 方法返回的就是你刚才改的 DESCRIPTION 常量。而 buildTool() 函数(定义在 src/Tool.ts 里)把这个方法注册到工具对象上。
数据流是这样的:1
2
3
4
5
6
7
8
9graph LR
A["DESCRIPTION 常量"] --> B["description() 方法"]
B --> C["buildTool() 注册"]
C --> D["启动时收集所有工具描述"]
D --> E["发送给 AI 的系统消息"]
E --> F["AI 读到你的修改"]
style A fill:#FFF9C4,stroke:#333
style F fill:#C8E6C9,stroke:#333
一行修改,经过了五六层传递,最终到达 AI 的”眼前”。这就是工具 prompt 的生命周期。
第五步:构建并测试
现在你需要让修改生效。由于这是从 npm 包提取的源码,你有两条路:
如果 bun install 成功了:1
2
3bun run build
# 或者
bun run dev
如果依赖装不上——直接改打包后的文件:
在 cli.js 中搜索 Read a file from the local filesystem.,把这段文字替换成你修改后的版本。注意只替换这一处,不要改动其他内容。
启动 Claude Code,随便找一个文件让 AI 去读。比如你可以说:1
请帮我读一下 package.json 的前 10 行
AI 会使用 Read 工具。此时它内部看到的就是你修改后的描述。
第六步:验证修改生效
有几种方法可以验证:
方法一:看日志。 如果你开启了 debug 模式,系统消息会被打印出来,你可以在其中搜索 “Your trusty companion”。
方法二:加一行 console.log。 在 FileReadTool.ts 的 description() 方法里加一行(如果你能运行 TypeScript 源码):1
2
3
4async description() {
console.log('[MY_CHANGE] FileReadTool description:', DESCRIPTION)
return DESCRIPTION
},
当你让 AI 读文件时,如果这行日志出现在终端里,说明你的修改路径是通的。
方法三:让 AI “看到”自己的工具描述。 你可以对 AI 说:1
请告诉我你有哪些工具可用,特别是 Read 工具的描述是什么?
AI 有时能回溯自己的系统提示并告诉你它看到的工具描述。不过这种方法不太可靠。
推荐使用方法二——加 console.log 最直接、最确定。
第七步:你的第一个 commit
验证成功了?现在把它提交到 git。这是你的第一个贡献。
先看看改了什么:1
git diff
你应该看到类似这样的输出:1
2- export const DESCRIPTION = 'Read a file from the local filesystem.'
+ export const DESCRIPTION = 'Read a file from the local filesystem. Your trusty companion for exploring code.'
提交:1
2git add src/tools/FileReadTool/prompt.ts
git commit -m "chore: customize FileReadTool description for learning"
恭喜,这是你在 Claude Code 源码上的第一个 commit。
常见错误
| 常见错误 | 检查方法 |
|---|---|
| 构建报 TypeScript 类型错误 | 你改的只是一个字符串常量,不应该触发类型检查。如果类型错误来自其他地方,回顾上一章的搭建步骤 |
| 找不到 bun 命令 | 确认 Bun 已安装:bun --version |
| 构建缓存问题 | 清理后重新构建:rm -rf dist/ && bun run build |
| console.log 没有输出 | 检查是否在正确的位置加了日志,是否真的触发了 Read 工具 |
| 改了但看不到效果 | description 是给 AI 看的,不是给用户看的。用验证方法确认 |
试试看
- 打开
src/tools/BashTool/prompt.ts,看看 Bash 工具的描述是什么样的。它比 FileReadTool 复杂得多——因为 Bash 是最强大也最危险的工具。 - 在
cli.js中搜索description(),数一数有多少个工具的 description 方法。每个工具都有自己独特的描述风格。 - 试着修改
renderPromptTemplate()的返回值——在末尾加一行"IMPORTANT: Always read files with enthusiasm."。这个修改会直接影响 AI 使用 Read 工具时的行为。观察 AI 是否变得更”热情”了。
检查点
- 工具 prompt 定义在
prompt.ts文件中:DESCRIPTION是简介,renderPromptTemplate()是详细说明 - 数据流:
DESCRIPTION→description()→buildTool()→ 系统消息 → AI - 验证方法:console.log、debug 模式、AI 自述
- 构建方式:
bun run build或直接修改cli.js