npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dreamtof/dsh-apply-patch

v3.7.0

Published

One-call multi-file, multi-span editing with exact matching, original-file coordinates, byte fidelity and conditional failure restoration.

Readme

apply_patch

版本 3.7.0。只做文件内容编辑:让 Agent 在一次调用中,准确修改多个文件的多个片段。

工具名就是 apply_patch。本包是唯一需要安装的编辑工具,v1 / v2 已由它取代;包名仍保留 -v3 只是为了不使已安装 Profile 的配置失效。

批量优先

一次调用改多个文件、多个片段才是这个工具的默认形态。逐文件单独调用是亏的:每次调用都占一个完整往返,而整批会在写入任何一个文件之前校验全部目标,任何一处不匹配就零写入。

只有一种情况值得拆开:后一步需要先看到前一步的结果(比如要看命令输出,或上一处匹配失败必须重读)。仅仅因为「改的是不同文件」而拆分,是把批量优势白白丢掉。

{
  "operations": [
    { "op": "replace_text", "path": "src/a.ts", "search": "return 1", "replace": "return 2" },
    { "op": "replace_range", "path": "src/a.ts", "startLine": 20, "endLine": 20, "oldText": "const old = true", "text": "const old = false" },
    { "op": "set_json", "path": "config.jsonc", "pointer": "/enabled", "value": true },
    { "op": "insert_json", "path": "config.jsonc", "pointer": "/hosts", "index": 1, "value": "beta" },
    { "op": "append_text", "path": "notes.txt", "text": "\n- one more note" },
    { "op": "create_file", "path": "notes.txt", "text": "new content\n" }
  ]
}

所有操作都以每个文件修改前的内容为基准。模型不用计算前面修改造成的行号偏移;重复匹配、原文不符或操作重叠时,整批在写入前拒绝。可选 dryRun: true 只返回预览。

真实调用长这样:五个文件、六处不相交编辑、五种原语混用,一次往返完成。

Success: 5 file(s), 6 span(s), +30 -4 lines
M src/a.ts
M src/b.ts
M config.jsonc
M docs/d.md
M notes.txt

同一次调用中,同一个文件可以接收多处编辑,但它们必须在原文里互不重叠。JSON 写操作会各自解析出最小差异跨度,所以相邻键(例如 /name 与紧跟其后的 /enabled)同批修改会被判为重叠并整批拒绝——这是刻意的:报告重叠比猜测应用顺序安全。

  • replace_text:精确原文替换,默认必须恰好匹配一次;可用 expect 声明多次匹配。同一操作内的多处匹配若互相重叠,报 MATCH_COUNT。可选 fuzzy 处理「读文件之后被格式化改动」的情况,见下节。
  • replace_range:按行替换,必须提供 oldText 校验。可用绝对行号(startLine/endLine),也可用一个精确 search 锚点加 offset 与 lines 相对定位。text 内部的换行会改写成该文件自己的行分隔符;text 为空串时删除这些整行。
  • set_json:按 JSON Pointer 修改 JSON/JSONC 值,保留注释和无关格式;不相交的目标可以同批修改,重叠拒绝。
  • insert_json:在数组的 index 位置之前插入元素,保留注释与排版。index 必须小于数组长度(追加请用 set_json 的 /-);空数组同样拒绝。
  • append_text:追加到文件末尾,不改变原有末字节的换行状态;只作用于已存在文件。
  • create_file:只新建,不覆盖;父目录默认须事先存在,加 parents: true 则由本工具创建缺失的祖先目录。

保留 UTF-8 BOM、原有换行、尾换行状态和未指定的文本。拒绝不支持的编码和损坏的 Unicode。只做内容定位与写入,不评判项目代码的类型正确性。

insert_json 的注释归属是刻意固定的:插入点取目标元素的起始位置,新元素落在该元素前导注释之后,注释仍属于原元素。四个边界(注释归属、index === length 拒绝、空数组拒绝、与另一操作重叠拒绝)见 PROTOCOL.md。

不提供复制、移动、删除、历史管理、撤销、快照会话或计划提交。无辅助模型调用、Shell 依赖和工作区状态目录。v1、v2 目录及工具保持独立。

新建模块的首个文件(parents)

批量新建模块时,src/feature/x.ts 的父目录通常还不存在。默认行为仍是拒绝(父目录须已存在),需要时显式加 parents: true:

{ "op": "create_file", "path": "src/feature/x.ts", "text": "export {}\n", "parents": true }
Success: 1 file(s), 1 span(s), +1 -0 lines
A src/feature/x.ts
  (created directory src/)
  (created directory src/feature/)

目录在任何写入之前创建,因此某个目标校验失败时不会留下空目录;创建失败也不会有文件被改。目录是这个工具唯一能完整撤销的东西:只删除本轮自己创建、且仍然为空的目录(深处优先),新建文件所在目录因文件保留而保留,并在 error.details.recovery.createdDirs 中报告。

DSH 的文件系统服务没有 mkdir 原语,因此这两个系统调用是 node:fs。范围被收得很紧:仍然先经 ctx.fs.resolve 判目录级沙箱包含与符号链接,且 verify:source 断言只有 src/v3/backend.ts 能 import node:fs/promises 的 mkdir/rmdir。

读文件之后被格式化(fuzzy)

replace_text 默认逐字节匹配。但读到的是旧内容、目标是 prettier / go fmt / IDE 保存时格式化过的新内容,这时原文对不上。加 fuzzy 显式放宽:

| 值 | 容忍 | | --- | --- | | trim | 每行行尾空白不同 | | indent | 整块统一平移了缩进(每行移动量相同) |

三条边界是刻意的:

  1. 永不归一字符。 弯引号、破折号、全角空格都是不同的文本,不当作空白放行。
  2. 多处候选就拒绝。 容忍出两个以上位置时报 MATCH_COUNT 并列出 candidates,不按位置挑一个。
  3. 每次命中都上报。 结果里带 fuzzy.line,模型能看到「我按容忍匹配到了第 12 行」并自行核对:
Success: 1 file(s), 1 span(s), +1 -1 lines
M drift.ts
  (fuzzy=indent matched line 1; verify this is the intended site)

匹配顺序是精确优先:search 在原文里逐字节出现过,就按精确命中处理,不会去走容忍路径;fuzzy 与 expect > 1 不能同时用,因为容忍匹配只对唯一位置才可辩护。替换区间仍是文件里被匹配到的那些行,replace 自带缩进,不会叠加。

不做任何符号级 / AST 级编辑。 重命名、改函数体、插成员、补导入这些需要绑定分析的操作不属于文本编辑工具:它们要解析语言、判断作用域与名字冲突、跨项目索引引用,且每种语言各需一套实现与测试。混合在一个工具里只会让它既不是精确的文本编辑器,也不是合格的语义工具。这类需求交给专门的 AST / LSP 工具。

因此本插件没有运行时编译依赖:typescript 只作为构建工具链存在于 devDependencies,不会随包进入你的 Profile。

隐藏原生编辑工具

本包安装后默认隐藏原生 edit 和 str_replace_editor(包括前者的提示词段落与 PTC tools.sdk 声明),所以精确文本修改只有 apply_patch 一条路。

write 不在隐藏集里:本工具没有整文件替换操作(create_file 拒绝覆盖),隐藏 write 会让「重写整个文件」无处可去。确实需要时把它列进 hiddenToolNames。

| 配置 | 默认 | 作用 | | --- | --- | --- | | hideNativeWriteTools | true | 是否隐藏 edit 与 str_replace_editor。开关为 volatile,翻转后下一个请求即生效,不需重新注册工具。 | | hiddenToolNames | [] | 额外要隐藏的工具名,可与上述开关同时用。与自身注册名相同的项会被忽略——工具永远不会隐藏自己。 |

仅在本工具注册成功时隐藏生效。若因名字冲突按 conflictPolicy: skip 未注册,或注册抛错,隐藏层完全不动——否则 Profile 会失去全部编辑能力。

隐藏走请求装配面(system-prompt/assemble,即 agent-loop 真正发送的工具表)加 ctx.tools.guard 兜底。后者覆盖装配过滤到不了的路径:旧会话残留的 schema,或在 PTC 里从 registry.schemas() 绑定名字的程序。被拦截的调用得到一条普通工具错误,明确指出改用 apply_patch 的 operations。

失败语义

先读、校验整批,再写入;每次写入均携带宿主文件版本约束。多文件写入不具备操作系统级原子性。

中途失败时,尝试按本轮写入回执恢复旧文件;外部后续修改不会被覆盖。error.details.recovery 明确列出 restored、remaining 和 uncertain。先更新旧文件,再创建新文件;宿主没有条件删除能力,因此失败前已创建的新文件会保留并报告。进程被强制终止后不提供持久恢复。

安装与验证

包名:@dreamtof/dsh-apply-patch(Loader ID dsh-apply-patch);工具名:apply_patch。目标 DSH:>=0.2.1-rc.2 <0.2.2-0。

安装见 INSTALL.zh.md,参数细则见 PROTOCOL.md,验证记录见 VALIDATION.zh.md。

开发检查:npm run typecheck、npm run lint、npm run build、npm test、npm run verify:source、npm run smoke。发布包检查:npm pack --ignore-scripts 后运行 npm run smoke:packed。