mcpField
C
cheez-write

cheez-write

github.com/paulnsorensen开发19158 次安装6 天前更新
代码仓库

关于 cheez-write

cheez-write 是用于修改受跟踪源代码的编码技能,通过防止过期写入的锚定后端执行编辑。它要求先读取当前内容,再应用限定范围的修改,或将语义与结构化变更交由合适工具处理。

主要功能

  1. 01按编辑类型选择后端
  2. 02锚定行与代码块编辑
  3. 03过期标签检测与恢复
  4. 04批量及跨文件编辑
  5. 05语义重构与代码转换路由

适用场景

  1. 01安全替换已知函数体
  2. 02对多个文件应用编辑
  3. 03处理读取后文件发生变化
  4. 04将符号重命名交由 LSP

MCP 相关

该技能说明如何使用 tilth MCP 的 tilth_write 工具,根据文件标签和编号行应用编辑。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/paulnsorensen/easy-cheese --skill cheez-write

安装后在 Claude Code 中输入/cheez-write确认已加载。

附带文件references/edit-patterns.mdreferences/routing.md

详细介绍

cheez-write

后端契约:使用具备陈旧状态保护的编辑后端。后端必须锚定当前文件状态(tilth 文件标签、快照标签或 LSP 工作区编辑),或者在干净的代码树上执行有意的 AST 代码修改。

后端检测

根据编辑形态选择后端,优先使用代码智能后端(LSP、AST 重写、tilth),而不是基础的 harness 编辑工具——模型调优会倾向于使用宿主 Edit/Write,但这些工具不具备语义或结构感知能力:

  1. 语义工作区编辑优先使用 LSP: 当服务器能够识别符号或修复项时,用于重命名/代码操作。
  2. 结构化代码修改优先使用 AST 重写: 用于带元变量的重复语法结构;先进行试运行。
  3. 锚定块/范围编辑优先使用 tilth MCP: tilth_read 会输出 [path#TAG] 标头,然后 tilth_write 通过 edits: [{path, tag, ops}] 应用带行号的操作。
  4. 原生锚定编辑(回退方案)用于已显示的快照: 会拒绝过期范围的行/快照编辑——仅在没有与编辑形态匹配的代码智能后端时使用。

不要将 sed、awk、patch、shell 重定向或盲目写入视为等效的源代码编辑方式。

过期标签和被拒绝的区段属于内容问题——参见过期标签处理。


示例

“替换 src/auth.ts 中 handleAuth 的函数体”

步骤 1 — 读取行范围以获取文件标签:

tilth_read(paths: ["src/auth.ts#44-89"])
# returns a [src/auth.ts#b2c4] header, then numbered lines 44:... to 89:...

步骤 2 — 针对这些行号应用操作,并原样引用标签:

tilth_write(edits: [{
  "path": "src/auth.ts",
  "tag": "b2c4",
  "ops": [{
    "op": "replace",
    "start": 44,
    "end": 89,
    "content": "export function handleAuth(req, res, next) {\n  const token = extractToken(req);\n  if (!validateToken(token)) return res.status(401).end();\n  next();\n}"
  }]
}])

响应会按 ## src/auth.ts 报告结果,也可能列出需要检查的调用方。

“标签已过期——文件在我操作期间发生了变化”

恢复步骤参见过期标签处理。


核心原则:锚定,而非重写

编辑后端必须识别当前文件状态——tilth 文件标签、harness 快照标签或 LSP 工作区编辑——并在文件发生变化时限制或拒绝写入。

协议:

  1. 使用 cheez-read 读取文件区段 → 获取当前的 [path#TAG] 标头或快照 ID
  2. 记录要修改代码块的行号
  3. 通过锚定后端应用编辑

适用范围:何时使用锚定编辑,何时不使用

cheez-write 负责受版本控制源代码的块编辑——函数体、签名、导入、单行修改、多编辑批次以及跨文件更改。后端必须具备陈旧状态保护;对于任何重要的代码更改,都必须遵循读取-编辑协议。

其他情况应优先使用合适的工具:

更改改用此工具原因
跨多处文件的结构化代码修改(JSON.parse(JSON.stringify($X)) → structuredClone($X))sg --rewrite(参见 ## Structural codemods — sg --rewrite escape)代码修改工具会对变量部分进行模板化;锚定编辑更适用于已知代码块
语义重命名或服务器已知的重构LSP 重命名/代码操作LSP 会跟踪作用域、重载、重新导出和导入
锁文件更改(Cargo.lock、package-lock.json、uv.lock 等)包管理器(cargo update、npm i、uv lock)手动编辑锁文件会破坏校验和完整性
生成文件/构建产物(编译后的 JS、转译输出、*.pb.go)从源文件重新生成编辑产物会在下次构建时失效
全新文件,没有既有内容锚定式创建/写入后端(tilth:省略 tag 的 tilth_write 区段)即使创建文件,也要确保具备陈旧状态保护
仓库外部或依赖缓存(node_modules、.cargo/registry)中的文件不要编辑修改依赖几乎总是错误的做法——应修复源代码或上游问题
二进制文件、图像、PDF生成它的工具代码编辑后端仅支持文本

路由至 LSP 重命名或 Serena

进入 cheez-write 之前的路由——当工作流技能应优先使用 LSP 重命名或 Serena 符号范围编辑,而非 tilth_write 时——详见 references/routing.md。


锚点格式

使用所选后端生成的锚点格式:

  • tilth:[path#TAG] 标头位于带编号的 N:content 行上方;将 4 位十六进制 TAG 复制到 tilth_write 的 tag 中,并在 ops 中引用这些行号。切勿自行编造 TAG(mode: "stripped" 读取结果不含 TAG,无法往返用于编辑)。
  • OMP 风格的快照编辑:[file#TAG] 加显示的行号;将 tag 和原始行范围传递给编辑工具。
  • LSP 重构:符号位置加工作区版本;让服务端构建工作区编辑。

不要在不同锚点系统之间转换。读取和写入时使用同一系列的后端。


MCP 工具参考

tilth_write — 精确文件编辑

最简格式——一个文件区段,一个 op:

tilth_write(edits: [{
  "path": "src/auth.ts",
  "tag": "a3f9",
  "ops": [
    { "op": "replace", "start": 42, "end": 42, "content": "  let x = recompute();" }
  ]
}])

edits 是一个 JSON 数组,由 {path, tag?, ops} 区段对象组成(每次调用最多 20 个)——不能是字符串,也不能包在 files 参数中。每个 op 都由 op 标记:replace {start, end, content}、delete {start, end}、insert_before / insert_after {line, content}、prepend / append {content}、replace_block / insert_after_block {at, content} 和 delete_block {at}(at 是行号或 "#symbol")、delete_file、move_file {dest}。只有在创建全新文件时才省略 tag。

各 op 的示例、跨文件批处理以及 diff: true 响应选项,请参阅 references/edit-patterns.md。


过期 Tag 的处理

TAG 会将你的 ops 绑定到读取时的内容。如果文件在读取后发生变化,tilth 会将你的 ops 三方合并到变更后的文件上;如果无法安全合并,它会拒绝该区段,并在每个 ## <path> 的结果中说明原因。

恢复步骤:

  1. 再次读取该区段 → 获取当前的 [path#TAG] 和内容。
  2. 检查当前内容(其他人可能已经做了更改)。
  3. 使用新的 tag,针对最新行号重新应用 ops。
连续遭到拒绝 → 停止,不要循环重试

如果同一文件连续两次遭到拒绝,说明你正与并发写入者发生冲突。没有“忽略 tag,只匹配此字符串”的覆盖选项——第三次重试很可能会再次遭遇相同冲突。

正确做法是停止并报告:

  1. 最后再读取一次最新区段并捕获当前内容。
  2. 准备新内容的统一差异或完整区块,但不要应用。
  3. 报告 "tag race on <path>; current content and proposed replacement attached. Retry once the file is quiescent or apply manually.",并附上捕获的 tag 和建议内容。
  4. 停止。由编排器(或人工)决定是应用更改还是升级处理。

签名变更后的调用方更新

更改签名时,请在完成前使用 LSP 引用或编辑后端提供的调用方提示。遗漏调用位置属于错误。


常见模式

目标参考
替换一行edit-patterns.md#single-line-replacement
替换一段范围edit-patterns.md#multi-line-range-replacement
删除区块edit-patterns.md#delete-a-block
在某行之前 / 之后插入edit-patterns.md#insert-before-or-after-a-line
替换 / 删除整个符号edit-patterns.md#symbol-anchored-block-ops
在一个文件中进行多项编辑edit-patterns.md#multiple-edits-in-one-file
跨文件更改edit-patterns.md#edits-across-multiple-files

何时可以接受完整文件重写

默认采用带锚点的精准编辑。只有当所选后端仍会拒绝过时快照,且文件足够小,以至于替换内容比一堆逐行编辑更容易审核时,才可以完整重写文件。

文件大小策略
> 150 lines绝不重写整个文件。始终使用带锚点的块编辑。
≤ 150 lines优先采用带锚点的单次编辑,但当文件中 ≥ 80% 的内容都要更改时,可以完整重写。低于此阈值时,应进行精准编辑。

150 行 / 80% 的阈值依据 2026 年的数据(Cursor、can.ac、Morph 基准测试):对于小文件,完整重写的表现与 diff 风格编辑相当或更好,而大文件始终应采用带锚点的编辑。

对于小文件,不要退回到不带锚点的主机 Write;文件大小并不能消除过时写入的风险。


结构性 codemod——sg --rewrite 例外

带锚点的编辑用于处理已知块,每个位置读取一次以获取锚点。对于周边文本存在差异的跨域结构性 codemod——例如“将每个 JSON.parse(JSON.stringify($X)) 重写为 structuredClone($X)”——应通过 Bash 使用 sg --rewrite(ast-grep),它会通过 metavars 为变量部分构建模板。它没有哈希锚点安全保障:每个 codemod 都要作为一次事务性更改,在两个干净的 git 状态之间执行。有关 metavar 语法和不可妥协的先试运行协议,请参阅 ../cheez-search/references/sg-patterns.md。


禁止事项

  • 禁止臆造标签——始终先读取,并逐字复制 [path#TAG] 标题。
  • 禁止忽略被拒绝的部分——重新读取并重试(参阅“过时标签处理”)。
  • 禁止使用 sed / awk / perl -i 编辑代码——它们会绕过锚点和结构安全机制,且没有不匹配检测功能。sg --rewrite 是唯一获准的 Shell 例外——参阅 ## 结构性 codemod——sg --rewrite 例外。
  • 禁止使用 patch 将 diff 应用于代码——带锚点的范围编辑是安全的等效方式。
  • 禁止使用 tee 或 Shell 重定向(>、>>) 覆盖 / 追加代码文件——两者都会绕过锚点。请使用带锚点的编辑后端。
  • 禁止使用不带锚点的主机 Edit/Write 工具——请使用 tilth_write、框架原生的带锚点编辑,或 LSP 工作区编辑。
  • 禁止使用 sg --rewrite 进行一次性的块编辑——应使用带锚点的编辑。何时适用 codemod 例外,请参阅 ## 结构性 codemod——sg --rewrite 例外。
  • 禁止跳过 sg --rewrite 的先试运行协议——请参阅 ## 结构性 codemod——sg --rewrite 例外。
  • 禁止不先读取就编辑——锚点来自读取步骤。
  • 禁止通过此技能运行测试、提交、发布或审查——请使用项目的测试 / 构建门禁,使用 /plate 进行提交 / 发布,使用 /age 进行审查。
  • 禁止将此技能用于读取或搜索——使用 /cheez-read 读取,使用 /cheez-search 搜索。

评论

支持 Markdown