MrWeiCodes/dsh-fs-encoding ↗★ 6
dsh-fs-encoding
提供支持多种老旧编码读写编辑的文件系统工具。 适合需要处理 GBK、Big5 等非 UTF-8 历史遗留文件的用户。
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:MrWeiCodes/dsh-fs-encoding說明文件
閱讀完整 README ↗使用
开箱即用
装好就能用,不需要任何配置。 插件没有必填项、没有初始化步骤,也不会改动你的项目——装上之后,AI 照常用 read / write / edit,编码的事插件自己处理:
- 读 UTF-8 文件(含带 BOM 的):和原版完全一样,BOM 不会再被吃掉。
- 读带 BOM 的 UTF-16 / UTF-32 文件:自动识别。
- 改 GBK、Shift-JIS 等老编码文件:按原编码写回,不会被悄悄转成 UTF-8。
- 写新文件:默认 UTF-8,需要别的编码时加一个参数即可。
这些都不需要你做任何事,也不需要 AI 改调用习惯。日常使用中,AI 的调用方式与原版完全一致。
AI 的调用区别
只有一种情况会不一样:无 BOM 的非 UTF-8 文件(典型是 GBK / Big5 / Shift-JIS 的老文件)。此时插件不会擅自猜,而是停下来让 AI 用显式编码重读一次:
[E_NOT_TEXT] legacy.txt is not valid UTF-8. Most likely gbk. Re-read with
read({ file_path: "legacy.txt", encoding: "gbk" }) to decode it, or set
autoGuessEncoding: true in the plugin config to decode automatically.
Candidates: gbk("你好,世界"), big5("斕疑"), shift_jis("ト羲")
AI 照着提示里的调用重读一次即可,它会自己完成——你不需要介入。重读之后编码就从「猜测」变成了「已知事实」,后续每次写入都按它进行。
read 和 write 因此各多了一个可选参数:
read({ file_path: "legacy.txt", encoding: "gbk" })
write({ file_path: "run.bat", content: "echo 中文\r\n", encoding: "gbk" })
无 BOM 的 UTF-16 / UTF-32 文件同理,用
read({ file_path: "", encoding: "utf16le" })显式指定即可正常读写(这类文件在 Windows 上较少见,且无 BOM 时无法可靠自动区分字节序,因此不做猜测)。
为什么默认要问一下? GBK、Big5、Shift-JIS 的字节范围在短文本上互相重叠,猜错在界面上是看不出来的——而且会按错误的编码写回,把文件彻底弄坏。所以插件默认选择「宁可失败,不可猜错」。如果你更希望它尽力解码,把
autoGuessEncoding设为true即可。即使开了
autoGuessEncoding,也有一种情况仍然会报错并列出候选:文件极短(几个字节),且两个独立的识别器给出了不同的答案。此时没有任何依据能判断谁对——实测这种情况下的首选有约 79% 是错的——插件宁可让你从候选里挑一个,也不替你赌一把。只要两者给出相同的答案,就会直接采用;文件稍长一些(几十字节以上)也基本不会再出现这种歧义。另一种会报错的情况:识别器把文件判成了 ISO-8859-1 / Windows-1252 这类单字节编码,但按它解码出来的文本几乎全是非 ASCII 字符。真实的西文以字母和空格为主,不会长这样;而中文/日文/韩文/俄文的双字节被当成单字节读时,每个字符会变成两个拉丁字符,正好就是这个特征。这时插件不会采用这个判断,而是报错并列出候选——实测这一步能拦下 24 个本会被静默读错的文件,且不会误伤任何原本读对的文件。
新建指定编码的文件
默认新文件是 UTF-8(无 BOM)。要给旧系统生成一个 GBK 文件,加 encoding 即可:
write({ file_path: "run.bat", content: "echo 中文\r\n", encoding: "gbk" })
支持的编码名与 read 相同,别名和大小写都不敏感(cp936、Shift-JIS 都行)。名字里带 BOM 的(utf8bom、utf16le、utf16be、utf32le、utf32be)会写入对应的 BOM;其余都不写 BOM。编码表示不了的字符同样拒绝写入,不会写成 ?。
encoding只对新建文件有效。 文件已存在时传它会直接报错E_ENCODING_NOT_APPLICABLE,不会转换——因为「保持文件原有编码」是本插件的核心承诺,而转换会重写整个文件,AI 从返回结果里看不出区别。不要为了转换而先删除文件:删除会绕过「先读后写」闸门,让未读过的内容在返回结果里以before: null静默消失;而如果本会话读过该文件,删除后写入会一直失败FS_STALE_VERSION,该路径在本会话内再也建不回来。本插件不提供编码转换;确实需要一份另一种编码的副本时,请写到新路径上。
另外三个工具
除上面三个之外,插件还注册三个工具,同样带编码治理(写入按文件原有编码落盘)。
insert —— 按行号插入,原生没有这个能力。字面替换只能插在能引用到上下文的地方,「在文件开头加一行」之类就得先读到那一行再原样复述,很不方便:
insert({ file_path: "config.ini", insert_line: 0, new_string: "[core]" })
insert_line 指插入到该行之后:0 插到最前,等于文件行数时追加到末尾。行号与 read 显示的完全一致。
undo_last_edit —— 撤销某个文件的上一次编辑,内容与编码一并还原:
undo_last_edit({ file_path: "config.ini" })
改错了、或者 AI 发现自己上一次改动不对时用它。几条要点:
- 只保留最近一次。同一个文件编辑两次,只有第二次能撤销;撤销一次后就没有了(不支持重做)。
- 编码一起还原。如果那次编辑把 GBK 文件转成了 UTF-8(见
normalizeToUtf8),撤销会把文件变回 GBK,而不只是内容回去。 - 文件被改过就拒绝。撤销前会核对文件是否仍等于那次编辑写入的内容;如果之后有人改过(你、其他工具、其他会话),撤销会拒绝并说明,绝不覆盖那些改动。
- 只存在内存里。不写磁盘、不污染仓库,所以 DSH 重启后无法撤销(会明确告诉你没有可撤销的历史,而不是假装成功)。
- 新建文件不产生撤销点:「撤销一次新建」等于删文件,破坏性太大,请直接删。
str_replace_editor —— 4 个子命令的兼容层,让按这套工具写的提示词与习惯直接可用:
| 命令 | 作用 |
|---|---|
view | 显示文件(带行号,可用 view_range 限定范围);路径是目录时列出两层结构,跳过隐藏项、node_modules、__pycache__ |
create | 新建文件,已存在则拒绝 |
str_replace | 替换唯一匹配;old_str 不唯一时拒绝并指出所在行号 |
insert | 同 insert 工具,insert_line 语义一致 |
undo_edit | 同 undo_last_edit,撤销上一次编辑 |
str_replace额外接受一个可选的replace_all:不传就是标准行为(要求唯一匹配),传true才全部替换。这是本插件加的唯一扩展参数。原生的
str_replace与insert只认 UTF-8,遇到 GBK 文件会直接失败或把文件转坏——这正是本插件接管它们的意义。
配置
默认配置就能用,通常不需要动它。 只有想调整「猜编码」的行为时才需要改。
配置文件位置(首次启动自动生成,内含完整注释):
$DSH_HOME/plugins/dsh-fs-encoding/config.yaml
全部选项
| 选项 | 默认值 | 作用 |
|---|---|---|
autoGuessEncoding | false | 遇到读不了的非 UTF-8 文件时:自动猜,还是报错让你选 |
normalizeToUtf8 | false | 保存时是否把 GBK 等老编码转成 UTF-8 |
supportedEncodings | 内置清单 | 参与自动猜测的编码清单 |
excludeEncodings | 空 | 从内置清单里去掉几个编码 |
maxFileBytes | 10 MiB | 单个文件的读取上限 |
常见需求(直接抄)
想让它自己猜,不要每次都来问我
autoGuessEncoding: true
想彻底告别编码问题(GBK 文件第一次保存后就变成 UTF-8)
normalizeToUtf8: true
某个编码老是猜错(例如西里尔文抢走了西欧文本)
excludeEncodings: [windows-1251]
编码清单:两个键有什么区别
插件内置一份用于自动猜测的编码清单。你可以做减法,也可以整个换掉:
| 你的需求 | 该用哪个 | 插件以后新增编码时 |
|---|---|---|
| 只想去掉一两个 | excludeEncodings | ✅ 仍然自动生效 |
| 想完全用自己的清单 | supportedEncodings | ❌ 收不到了 |
为什么建议优先用 excludeEncodings:supportedEncodings 一旦写上,就等于把清单冻结在你的配置文件里——插件以后支持了新编码,你也不会收到,而且它不会替你改回去(插件从不修改已有配置)。
清单只影响"自动猜测",不影响"能不能读"。 任何编码都可以显式指定读取,即使它不在清单里:
read({ file_path: "legacy.txt", encoding: "windows-1253" })
环境变量
适合临时测试或容器部署。环境变量始终优先于配置文件:
| 变量 | 对应选项 |
|---|---|
DSH_FS_ENCODING_AUTO_GUESS | autoGuessEncoding |
DSH_FS_ENCODING_NORMALIZE_TO_UTF8 | normalizeToUtf8 |
DSH_FS_ENCODING_SUPPORTED_ENCODINGS | supportedEncodings |
DSH_FS_ENCODING_MAX_FILE_BYTES | maxFileBytes |
excludeEncodings 没有环境变量——它是唯一只能在配置文件里写的选项。
normalizeToUtf8不会转换 UTF-16 / UTF-32 文件:它们本来就是 Unicode,强行转码反而会破坏依赖它们的程序。