Pular para o conteúdo principal
Z

dsh-file-state-cache

zzzk187/dsh-file-state-cache

File-state cache skill for coding agents: one explore call replaces ls+grep+read, reads remember they already happened, and edits return a diff plus the exact changed line range instead of a full re-read. State is a JSON cache on disk, so it survives across tool calls and restarts. Requires Python 3.8+ (standard library only).

Instalar

dsh plugin --profile web add github:zzzk187/dsh-file-state-cache

README

dsh-file-state-cache · 文件状态缓存

English · 简体中文

让 AI 记住「这个文件我读过」,别每次都从头读一遍。

一个 DSH 插件兼技能。装好之后,Agent 读文件会变「有记忆」。


它解决什么

Agent 干活时经常这样绕圈:

ls 看目录 → grep 找关键词 → read 打开文件 → 改完再 read 确认

四步里三步是重复劳动。装了它:

以前现在
ls + grep + 盲目 read,三轮一句 explore "关键词",直接告诉你哪个文件、第几行是定义、下一步读哪段
同一个文件每次都重读全文第二次只回一行「内容未变」
改完再读全文确认edit 直接回 diff + 改的是第几行
读 10 个文件才看懂结构一句 digest,一屏 10 个函数签名

省下来的是上下文(token),代价是硬盘上一点缓存。

一句话判断:这件事会发生第二遍吗?会 → 用它;不会 → 用系统自带的读文件工具。


安装

dsh plugin --profile web add Zzzk187/dsh-file-state-cache
  • 需要 Python 3.8+,不用装任何第三方包(纯标准库)
  • 装完技能列表里会出现 file-state-cache
  • 本机开发时装本地目录:dsh plugin --profile web add /你的路径/dsh-file-state-cache

30 秒上手

F="<包目录>/scripts/fstate"          # 技能会告诉你包在哪

<包目录> 就是装完后的插件目录,通常长这样:

~/.dsh/profiles/<profile>/node_modules/dsh-file-state-cache

(Windows 上是 C:\Users\你\.dsh\profiles\<profile>\node_modules\dsh-file-state-cache。不确定的话 python "<包目录>/scripts/fstate.py" -h 能跑通就说明对了。)

"$F" explore "handleRequest" --root .   # 不知道代码在哪时先问它
"$F" read src/app.py                    # 第一次给全文;第二次只给一行
"$F" edit src/app.py --old 'return 1' --new 'return 2'
"$F" digest src/                        # 一眼看完整目录结构

read 默认会自己判断该给你什么:

磁盘状态返回
从没读过正文
读过、没变,且本会话已完整交付过正文一行摘要(不吐正文)
读过、但被改过只回 diff + 改动在第几行

两个细节新手容易踩:

  • 一次最多给 400 行(大文件会被截断)。这时它会明说「已显示前 400/1000 行,不算读过」并给续读命令 —— 下次 read 会接着给,不会拿「内容未变」把你打发走。
  • 摘要那行只在本会话内成立。换了会话或上下文被压缩过,它会重新给正文。这是故意的:「省上下文」不能变成「让你以为自己看过」。

edit 的输出长这样,这就是验证,不用再读一遍:

# 已改 src/app.py  → v2  19 行  区间 14..14
--- a/app.py
+++ b/app.py
@@ -11,7 +11,7 @@
-    return {"ok": True, "uptime": 0}
+    return {"ok": True, "uptime": 42}

区间 14..14 指的是真正变动的那一行 —— 不包含 diff 里的 3 行上下文。


命令一览

命令干什么
explore "关键词"找代码:命中文件 + 定义行号 + 片段 + 下一步命令
read 文件带缓存的读:没变给摘要,变了给 diff
read 文件 --mode range --start 40 --end 90只读某几行
edit 文件 --old 旧 --new 新改一处,回 diff + 精确行号
write 文件 --content-file 内容文件整篇写入
digest 目录/多个文件的结构总览
state 文件这个文件的缓存状态(版本、读过几次、是否过期)
diff 文件看上次的完整 diff
reset 文件清掉这个文件的缓存
hot我在反复读哪些文件(自查用)

--help 看全部参数。

几个常用开关:

  • --mode full / range / changed:显式指定读法,不让它自己判断
  • --max-chars 20000:输出上限,超了会截断,但一定会告诉你截了
  • --label "配置解析":在输出里用这个名字代替长路径
  • write --mkdir:父目录不存在时替你建(默认会报错,防止路径打错还静默成功)

它是怎么记住的

关键:状态存在硬盘上,不在上下文里。 所以它能跨工具调用、跨轮次、跨重启活着。

每个文件一条 JSON,存在 ~/.dsh/fstate-cache/,外加一份「上次读到的原文」影子副本(≤2 MB 的文件才有),用来算 diff —— 不需要装 git。

read(文件)
  ├─ 先算当前磁盘上的 SHA-1
  ├─ 没记录 ──────────→ 全文(首次)
  ├─ SHA-1 没变 ──────→ 一行摘要(没变)
  └─ SHA-1 变了 ──────→ 有影子?→ diff + 精确行号
                       没影子?→ 老实说「我没有基线」,给全文

最后那条分支是重点:它不会假装知道改了什么。

一个刻意的限制:「这份正文我给过了」这张凭证只在本会话内有效。换了会话、或上下文被压缩重置,read 会重新把正文给你,而不是用一句「内容未变」把你打发走。省上下文的工具,不能反过来制造「以为自己看过」的错觉。


几个设计上的讲究

  • 编码不会被改。 有 BOM 就认 BOM(UTF-8/16/32 各种组合),没有才依次试 UTF-8 → GBK → Latin-1。写回保持原 BOM、原编码、原换行(CRLF 还是 CRLF)。本来没 BOM 的文件绝不会凭空多出一个 BOM —— 这一条是踩过坑的:早期版本用 utf-8-sig 去「探测」编码,而它对无 BOM 的文件也会成功,结果给每个改过的文件都盖了 BOM,让 JSON.parse 和 shell 脚本当场炸掉。
  • 参数不玩阴的。 edit 不给 --new 会直接报错停下,不会把「你没说」当成「删掉」(旧版会,还回一句「已改」)。要删就得显式写 --new ''。
  • 砍掉什么都告诉你。 输出超限会截断,但一定补一行说清了砍掉多少、怎么接着看。看到 ⚠ 截断 别忽略,答案可能就在被砍的那部分。
  • 没交出去的不算读过。 被截断的读,缓存里不记这个范围 —— 下次 read 会老老实实再给一遍,而不是用「内容未变」把没进过上下文的正文跳过。这是整个工具唯一真正的承诺。
  • 缓存位置可搬。 FSTATE_HOME 换目录;hot 看谁占得多,reset 清单个。

什么时候别用它

  • 已经知道确切行号 → 系统自带的读文件工具更快,进缓存是负担
  • 一次性的事(单条命令、跑完就完的脚本)→ 没有第二遍,缓存不赚钱
  • 二进制 / 图片 / 超过 8 MB → 它按设计拒绝

说实话:「定位问题 → 读一段 → 改一处 → 确认」这种只走一遍的流程,用它反而更贵(实测多花 16.5%,因为 explore 比精确 grep 贵,read 还会加行号前缀)。

它划算在同一批内容被反复用到:一个文件碰 4 次省 65%,改 N 处各确认一次省 88%,一次看懂 20 个文件省 80%。


常见问题

Q:会不会读到我改之前的旧内容? 不会。每次读之前都先算当前文件的 SHA-1 和记录比,不一致就按「被外部改过」处理,用影子副本算 diff。唯一可能退化的情况是影子副本丢了 —— 那时它会明说「降级为全文」。

Q:会不会和系统自带的读文件工具打架? 不会,它不改任何原生工具,只是多给你一个选择。分工是:知道确切行号 → 用原生工具;要第二次碰同一个文件 → 用这个。

Q:缓存脏了 / 想从头来?

"$F" reset src/app.py         # 清一个
rm -rf ~/.dsh/fstate-cache    # 全清,无害,下次自动重建

Q:占多少硬盘? 每个文件约 300 字节 JSON + 一份影子副本(≤2 MB,且只有真读过的文件才有)。上千文件的项目通常几 MB 到几十 MB。介意的话 hot 看谁最大,reset 掉。


排错

症状原因怎么办
ERROR: 找不到 python外壳脚本没找到 Pythonexport FSTATE_PYTHON=/路径/python3
ERROR: 未找到 old 片段--old 和文件内容对不上(空格/缩进)用 read --mode range 把那几行原样复制出来当 --old;换行符不用管,它会自动适配
old 片段出现 N 次,不唯一片段太短,匹配到多处多带一行上下文;确实要全改就加 --all
ERROR: 无法按文本读取(toolarge)文件超过 8 MB用原生工具,或先切分
ERROR: 无法按文本读取(binary)含 NUL 字节二进制文件它不处理
编辑成功但下次读说「变了」你的编辑器在背后改了(格式化、保存钩子)state 看版本号和 SHA-1,揪出是谁改的
技能没出现插件没装成 / 没重启dsh plugin --profile web list 看在不在,重启一次

输出的提示都是中文,但 ERROR: 前缀和退出码(0 成功 / 2 失败)是稳定的,写脚本可以照着匹配。


兼容性

在 DSH 0.2.1-rc.5 上验证通过。技能注册档位 rank 600(bundled)。如果你在 ~/.dsh/dsh-skill/file-state-cache 也放了一份本地版,本地版优先。

许可

MIT

Plugins relacionados