☰
oh-my-pi Hashline 补丁语言深度指南:基于 `[PATHTAG]` 行锚定的源码编辑引擎
2026/10/10 1:49:29 网站建设 项目流程
  • 人工智能
  • AI Agent
  • 代码智能体
  • 工具调用
  • CLI
  • MCP Clients

【免费下载链接】oh-my-pi

⌥ Coding agent with the IDE wired in. Built by Stencil Labs.

项目地址:https://gitcode.com/GitHub_Trending/oh/oh-my-pi
点击查看免费下载

Hashline 是 oh-my-pi 中edit工具默认的补丁模式,也是 Coding Agent 面向模型设计的源码编辑语言:模型只需给出「文件路径 + 快照标签 + 基于原始行号的操作」,引擎即可在同一工具调用内完成对现有文件的修改、块级替换、剪切/粘贴与文件级操作。本文以 Hashline 权威文档 为主线,结合 引擎实现、Lark 语法定义、模型提示词 与 测试套件 等仓库证据,完整讲解 Hashline 的协议格式、操作原语、寄存器语义、配置选型、校验边界与常见失败,帮助读者彻底掌握这套行锚定编辑协议,并理解其背后的工程设计与可靠性保障。

Hashline 的定位:为什么需要「行锚定 + 快照标签」

oh-my-pi 的edit工具负责把模型产出的编辑意图真正落到磁盘文件上,而 Hashline 是它的默认工作模式。它面向一个核心问题:LLM 在生成编辑指令时,行号是相对于"它看到的那个文件快照"而言的,而不是相对于后续某个 hunk 偏移后的行号。因此 Hashline 强制要求两点:

  1. 每个文件分区以[PATH#TAG]开头,其中TAG是 4 位大写十六进制快照标签,必须原样复制自最近一次read、grep或成功的edit结果;
  2. 所有行号都是「原始快照行号」,绝不做 hunk 偏移。多行替换时,正文行数可以与被替换行数不一致,因为引擎内部会把一次范围替换分解为「逐行 Insert + 逐行 Delete」,而不是依赖统一 diff 的上下文对齐。

从 语法定义 可以看到整套协议被约束为严格的上下文无关文法,模型输出可被 tokenizer + parser 流式解析,甚至支持对尚未生成完整的补丁做流式预览。

与仓库中同级别的其他 wire contract 相比(replace、patch、apply_patch、sloppy,详见 docs/tools/edit.md),Hashline 的核心差异在于:

  • 不用旧文本作为锚点(区别于replace/sloppy),而是用「快照标签 + 行号 + 语法块」三重定位;
  • 正文是最终内容而非 diff(区别于apply_patch),不存在-old行与裸上下文;
  • 天然支持跨文件、跨分区的一次性编辑:一次工具调用可包含多个[PATH#TAG]分区,操作按从上到下顺序执行,前一分区的CUT结果可被后一分区的PUT引用。

一条补丁的完整链路:从模型提示词到磁盘写入

从源码结构可以梳理出 Hashline 编辑请求的完整处理管线:

  1. 模型侧约束:模型提示词 定义了 ops(操作)与 rules(规则),紧凑变体 面向editPromptVariant: "compact"的模型;部分场景下还可用 Lark 语法 做受约束解码。
  2. 入口与模式注册:packages/coding-agent/src/edit/index.ts负责模式注册、宿主写入、LSP 集成与解析回归处理;模式 schema 在 schemas.ts。
  3. 原生桥接:crates/pi-natives/src/edit.rs导出EditSession、EditStore、检查与提示词/语法资源。
  4. 解析与分派:HashlineEngine的stage()调用Patch::parse,随后由 patcher.rs 做stage_patch;preview()走 preview.rs 产出只读预览;inspect()则从部分输入中提取路径、新增行摘要与文件级操作(REM/MV)。
  5. 快照与寄存器状态:store.rs管理快照标签与寄存器;session.rs负责暂存、流式预览与结果整形。
  6. 执行与回写:解析与校验全部通过后才开始写入;写入经由 ACP 桥或 LSP 直写,因此配置的格式化器可能改变最终落盘文本。

关键设计是「先解析全部、再统一写入」:多分区调用中,所有分区都会先完成解析与准备,语法、锚点与 no-op 错误会快速失败,避免写入一半才发现问题;随后文件按顺序写入,若操作系统写入失败,可能只落下一部分前缀,此时命名寄存器的会话状态只对已落地的前缀推进(见 docs/tools/edit.md 输出与副作用一节)。

协议格式:[PATH#TAG]分区与可选信封

Hashline 的输入是一个字符串,由一或多个[PATH#TAG]分区组成。每个分区只编辑一个已存在的文件;新建或整体覆写文件应使用write工具。Hashline 在应用阶段会拒绝未带标签的锚定编辑。

标准封装形式(自定义工具语法要求信封,普通解析器也接受不带信封的载荷):

*** Begin Patch [src/example.ts#1A2B] PUT 4.=4: +const value = 2; *** End Patch

Lark 语法 定义了精确的文法骨架:

  • 顶层为begin_patch file_patch+ end_patch,begin_patch为"*** Begin Patch"加换行,end_patch为"*** End Patch"(可省略尾部换行);
  • 每个file_patch由file_header hunk+组成,file_header匹配[filename#file_hash];
  • file_hash必须匹配/[0-9A-F]{4}/(4 位大写十六进制);
  • hunk 分为put_hunk(PUT)、cut_hunk(CUT)、rem_hunk(REM)、mv_hunk(MV);
  • 行号LID为/[1-9]\d*/(从 1 开始、无前导零);
  • 寄存器register为" @" /[A-Za-z0-9_-]+/;
  • 正文body为"+" /(.*)/ LF。

快照标签由「归一化文件内容」计算而来,并记录在会话快照存储中。模型必须从最近一次锚定的read、grep或成功edit的输出中复制该标签——它既是内容版本指纹,也是引擎判断「模型看到的是哪一版文件」的依据,是陈旧内容与并发修改的第一道防线。

操作原语:PUT / CUT / REM / MV 完整参考

所有行号都指向被标签标记的原始快照,而不是同一调用中更早 hunk 修改后的行号。核心操作如下表(完整版见 docs/tools/edit.md 的 Canonical patch language 一节):

形式效果
PUT N.=M:用随后的+TEXT行替换原始行N..M(含端点)
PUT N*:替换从第N行开始的多行语法块
PUT <N:/PUT >N:在第N行之前 / 之后插入正文;PUT <1:即文件头部
PUT >$:在文件尾部追加正文
PUT >N*:在从第N行开始的语法块之后插入
CUT N.=M/CUT N*删除并捕获区间或已解析的块;加@name写入命名寄存器
PUT <N/PUT >N/PUT >$将匿名寄存器内容粘贴到空隙
PUT <N @name/PUT >N @name/PUT >$ @name将命名寄存器内容粘贴到空隙
PUT N.=M @name/PUT N* @name用命名寄存器内容替换区间或块(区间/块粘贴必须用命名寄存器)
REM删除分区指向的文件
MV DEST把编辑后的文件写入目标路径并删除源文件;目标可被覆盖;路径含空格需加引号

REM与MV是文件级操作,一个分区内只能有一个文件级操作(解析器在set_file_op中强制),且REM不能与行操作混用——REM会删除整个文件(见 parser.rs 的finish与set_file_op)。

块锚定(*形式)

*形式通过 tree-sitter 语法树把起始行解析为完整语法节点:从该节点的 opener 行一直延伸到节点结束,因此必须锚定构造的开头(函数/类/装饰器链的第一个装饰器),绝不能锚定闭括号、最后可见行、空行或内部语句。对单行节点,引擎会拒绝块操作并引导使用显式行操作。PUT >N*:在解析不到块时会降级为普通PUT >N:并给出警告;而替换/剪切块形式在解析失败时会直接报错,绝不猜测(见 docs/tools/edit.md 的 Block anchors 一节,实现见 block.rs)。

几条与块相关的重要规则:

  • 装饰器、属性、文档注释可能是独立语法节点:若解析器把它们与声明归组,就锚定第一个装饰器;否则改用显式区间。
  • 独立的行注释不会被自动扫入块,需要显式区间。
  • 在 Markdown 中,标题块会一直延伸到下一个同级或更高级标题之前,包括其正文与更深层小节;在PUT >N*:插入小节后,正文应以空行结尾(见 模型提示词 的 rules)。

正文(body)规则

只有带冒号的PUT ...:类头部接收正文。每行正文都以+开头,+单独出现表示插入空行;正文是最终内容,不是 diff 的前后对。字面内容若以-或+开头,需写成+-...或++...转义。CUT、寄存器版PUT、REM、MV均不接受正文(docs/tools/edit.md)。注意在 提示词 中该规则写作:Literal - item/+ item→+- item/++ item;禁止发送-行、裸上下文行或未变化的行;区间长度与正文长度相互独立;删除用CUT而非空PUT。

寄存器:跨分区、跨文件的剪切粘贴

Hashline 的寄存器(register)机制让它具备「文本搬运」能力:

  • 寄存器名由 ASCII 字母、数字、_、-组成;
  • 匿名寄存器按批次隔离,每次调用开始时为空;重复粘贴不会消耗寄存器;
  • 命名寄存器(@name)在会话内持久,且只在对应写入落盘后发布;
  • 操作跨分区按从上到下顺序执行,因此较早分区的CUT可以喂养较晚分区的PUT;
  • 区间/块粘贴必须指定命名寄存器;空隙粘贴(PUT <N @name等)可以匿名,但带寄存器版本更明确。

跨文件移动的典型写法(来自 edit.md 的示例):在源文件分区CUT 1* @fn,再到目标文件分区PUT <1 @fn。寄存器粘贴没有正文。从实现看,粘贴落点分为Gap(空隙)与Span(替换区间)两类(见 types.rs 的PasteTarget),剪切则在 clipboard.rs 中做预扫描捕获,并校验剪切/粘贴序列的合法顺序——若同路径分区交错导致寄存器顺序歧义,粘贴会被拒绝。

模式选择与运行配置

edit是内置的必需工具,按以下顺序解析当前生效的 wire 契约(resolveEditMode(),实现见 edit-mode.ts 与 settings.ts):

  1. 模型专属的配置变体(edit.modelVariants,按活动模型字符串做大小写不敏感的子串匹配);
  2. 环境变量PI_EDIT_VARIANT(精确固定模式);
  3. edit.mode配置项;
  4. 默认值hashline。

支持的 mode 为hashline、apply_patch、patch、replace、sloppy。对由设置解析出的hashline,PI_STRICT_EDIT_MODE可关闭「模型族回退到replace」的行为(受影响模型族包括 Kimi、MiMo、MiniMax、DeepSeek、StepFun、Codex Spark 与 GLM 5.3 Flash)。editPromptVariant: "compact"的模型收到的是 紧凑版提示词。此外,edit工具是严格(strict)、必需(essential)且使用独占并发(exclusive concurrency)的。

校验边界与可靠性保障

Hashline 的鲁棒性来自多层校验,均在写入前完成:

  • 可见行强制:edit.enforceSeenLines=true(默认)时,目标行若不在最近read/grep记录的可见范围内,编辑会被拒绝——必须先重新读取被省略或未显示的范围;
  • 区间合法性:区间含端点、必须有序,且受解析放大上限 100,000 行约束(MAX_EXPANDED_RANGE_LINES,见 parser.rs),超限提示拆分;
  • 重叠拒绝:相互冲突的重叠区间会被拒绝;完全相同的重复替换区间可合并到后者正文并给出警告——每个区间只应写一个「最终内容」hunk;
  • 同路径合并:同路径分区会被合并,使原始行锚点共同生效;交错剪切/粘贴会被拒绝;
  • 陈旧标签恢复:标签过期时尝试基于快照链的恢复,仅当快照链能证明唯一安全结果时才会应用,否则返回上下文不匹配错误(见 recovery.rs);
  • no-op 语义:单分区字节级相同的编辑返回 no-change 诊断而不写入;连续第三次相同的 no-op 视为错误;多分区调用中任何 no-op 都会在写入前拒绝整批;
  • 生成文件保护:edit.blockAutoGenerated=true(默认)拒绝识别为生成物的文件;Plan 模式只允许在可写根内更新,且拒绝所有删除/移动;
  • 非普通文件拒绝:FIFO、socket、字符/块设备(含经符号链接)在任何模式下都拒绝编辑——进程内读取可能永久阻塞;
  • 只读 scheme 拒绝:agent://、proc://等处理程序所有的写入被拒绝,应改用write;文件型可变内部 URL 可以编辑;部分/行选择器不是编辑目标;
  • 审批:使用所有目标与移动目的地中最严格的写入层级。

应用阶段的关键实现细节见 apply.rs 与 ApplyResult:text、first_changed_line(1 起始的首个变化行,no-op 为None)、warnings以及块操作的解析跨度block_resolutions。

输出、副作用与容错

Hashline 在一次工具调用内完成应用,不使用ast_edit的xd://resolve/xd://reject暂存流程。成功分区返回:新的[path#TAG]头部、可选的块解析行与移动行、可用的紧凑编辑后预览,以及出现恢复/归一化警告时的Warnings:块。EditToolDetails可携带统一 diff、firstChangedLine、诊断、操作类型(hashline 下为update或delete)、路径/移动元数据、oldText/newText、snapshotsPruned与逐文件结果。多分区输入返回一个聚合结果;存储的前后快照文本按文件与跨文件结果各上限 32,768 字符,更晚的条目可能保留 diff 但省略快照文本。

容错方面值得注意:

  • 原生解析/匹配/应用与写入失败返回isError: true及诊断文本;原生桥失败可能抛异常;
  • 新引入的语法解析失败不会回滚编辑:它产生警告,或在edit.autoRepair.enabled启用时产生额外的修复提示;
  • 流式渲染器会解析在途载荷的完整部分并计算只读 diff;流式预览跳过暂时未解析的块、陈旧标签与空粘贴,而不是把部分输入当作最终失败;正式执行仍会重新读取并完整校验。

常见失败模式与排错

综合 docs/tools/edit.md 的 Common failures 一节与解析器实现,以下是高频错误与成因:

  • 缺失/格式错误的[PATH#TAG]、未知快照标签、路径已不存在;
  • 锚点在文件外、记录可见范围外、被省略区域,或基于无法安全恢复的陈旧快照;
  • 区间反向(end < start)或相互重叠——解析器在validate_range与normalize_overlaps中分别处理,反向区间会带出带定位消息的InvalidAbsoluteRange;
  • 需要正文的操作给了空正文、无正文操作带了正文行、未知命名寄存器、或匿名剪切前使用匿名粘贴;
  • 块锚定在不受支持/无效的语法树、空行/闭行或单行节点上;
  • 统一 diff 污染:@@ -N,M +N,M @@头、apply_patch 哨兵(*** Update File:等)与-old行混入——解析器的contamination_message会逐一识别并给出改用PUT/CUT的指引;
  • REM/MV冲突、非法或同源移动目标、文件系统写入失败;
  • 多分区批次中的 no-change 分区,或单分区连续第三次相同 no-op;
  • 非普通文件目标:Cannot edit '<path>': it is a <kind>, not a regular file or directory.

解析器对常见模型笔误有有限恢复能力(可选信封、头部噪音、部分裸行与区间拼写),恢复时会给出警告;但调用方应只生成上述规范文法——恢复行为不是第二套公开语法(docs/tools/edit.md)。

测试与质量保障

Hashline 的可靠性由专门的测试套件背书,可作为协议语义的权威参考:

  • hashline_parse.rs:解析器行为;
  • hashline_patcher.rs:patch 组装;
  • hashline_apply.rs:编辑应用,如头尾插入、替换分解为 Insert+Delete(replacement辅助函数把每行正文转为BeforeAnchor插入再追加逐行删除);
  • hashline_parity.rs 与 fixtures/hashline:与编码代理场景对齐的奇偶校验固件(如parity_coding_agent_hashline.json、parity_diff_preview.json、parity_edit_streaming_preview.json、parity_clipboard.json、parity_boundary_repair.json)。

速查:Hashline 规则一览

  • 只触碰最新read/search中可见的行;…、..、折叠区间与窗口外行视为不可见,先重新读取再编辑;
  • 每次编辑后标签与行号都会变化:使用 edit 响应或重新read;标签过期或意外内容出现应立即停止并重新读取;
  • 严格区间:非相邻修改拆开提交,绝不包含「保留行」,也不在表达式/块中间起止;纯新增用空隙PUT;
  • *需要多行 opener,绝不用于闭行/末行/内部语句;单语句用区间或空隙;
  • 不要顺手重排无关代码(restyle);实质编辑完成后由项目格式化器处理格式(docs/tools/edit.md);
  • 删除用CUT,文件级操作用REM/MV,新建文件用write。
  • 人工智能
  • AI Agent
  • 代码智能体
  • 工具调用
  • CLI
  • MCP Clients

【免费下载链接】oh-my-pi

⌥ Coding agent with the IDE wired in. Built by Stencil Labs.

项目地址:https://gitcode.com/GitHub_Trending/oh/oh-my-pi
点击查看免费下载

相关推荐

上一篇:gh_mirrors/ex/expr函数式编程:高阶函数应用
下一篇:BiliTools深度解析:重新定义B站视频内容管理新范式

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询