Chuyển đến nội dung chính

dsh-code-diff-stat

Đã xác minh

dsh-code-diff-stat · v0.1.0 · MIT · Giao diện web

DSH Web 插件:在聊天流中按轮次汇总代码改动量(+N −M),可展开查看每个文件明细。 · DSH plugin: per-turn code-change stats (+N −M) rendered directly in the chat flow, expandable to a per-file breakdown.

Cài đặt

dsh plugin add dsh-code-diff-stat

Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.

Mã nguồn

Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.

Thẻ

Tác giả

Readme

dsh-code-diff-stat

English | 中文

在 DSH Web 聊天流里,按轮次汇总这次对话到底改了多少代码,并且点开就能看 diff、能撤销:

┌────────────────────────────────────────────────────────────┐
│ 本轮代码改动   +148 −9   15 个文件   [撤销本轮改动]    ⌄   │
└────────────────────────────────────────────────────────────┘

展开 ⌄ 是每个文件的明细;点某个文件,就在右侧栏为它开一个 diff 面板:

┌ 对话流 ───────────────────────┐ ┌ 右侧栏 ──────────────────────┐
│ 本轮代码改动 +148 −9 15 个文件 │ │ [ definition.js ] [ view.js ]│
│   [撤销本轮改动]          ⌃   │ │ ─────────────────────────── │
│  ≡ …/src/definition.js +140 −9│ │ …/src/definition.js   +140 −9│
│  ≡ …/src/view.js       +106 −0│ │ │  const type = event.type  │
│  ≡ …/src/panel.js      +134 −0│ │ │- if (type === 'turn/start'│
└───────────────────────────────┘ │ │+ if (type !== 'turn/start'│
                                  │ │  第 4 轮                   │
                                  └──────────────────────────────┘

面板是右侧栏里的一个标签页,标签名就是文件名。开面板读 diff 不会让对话流重排;同一个面板再点别的文件就换成那个文件。

它解决什么问题

让 agent 改完代码,你只能看到「改好了」,看不到改了多少、改了哪几行。这个插件把每一轮的改动量、逐文件明细和逐 hunk diff 直接放进对话流,并且给你一个按钮把这一轮改回去。

安装

dsh plugin --profile web add dsh-code-diff-stat

本地开发用链接安装:

dsh plugin --profile web add link:/绝对路径/dsh-code-diff-stat

装完重启 DSH。插件只挂在 Web profile 上,不替换任何产品自带的界面元素。

用法

操作 效果
看徽章 每轮结束后,那一轮改了多少行、几个文件
点 ⌄ 展开每个文件各自的 +N −M
点文件行 在右侧栏开一个 diff 面板查看该文件的改动
点 ↗ 在右侧栏用编辑器打开该文件
点「撤销本轮改动」 二次确认后,把这一轮对文件的修改反向应用回去

撤销也可以直接打字:

/undo-diff 4        # 撤销第 4 轮(那个徽章上的轮次号)

命令会把每个文件的结果逐行列出来(✓ 已撤销 / – 跳过 / ✗ 失败)。

撤销的边界(重要)

撤销是反向应用日志里记录的 hunk,不是恢复快照。所以:

  • 本轮新建的文件不会被删除。 fs 服务不提供删除能力,而且一个你后来可能已经写过内容的文件,被悄悄删掉比「撤不干净」更糟。这类文件会标上「本轮新建」并在结果里列为跳过。
  • 文件在本轮之后又被改过,反向 patch 可能找不到原文;fs.editText 的唯一性校验会让它拒绝而不是改错位置,该文件单独失败,其余文件照常撤销。
  • 改动过大时插件只保留行数、不保留 diff 文本,这类文件无法撤销。

如果你要的是完整工作区还原(含删除新建文件),已装的 dsh-rewind-plugin 的 /rewind … both 用的是真正的 before 备份,语义更强。这个插件的撤销是「就地、按轮次、无额外存储」的轻量路线。

原理

数据全部来自已经落在会话日志里的持久事件,不读 git、不额外落盘、不发起任何请求:

来源 用途
tool/result 的 meta.diffs 权威的「已应用改动」hunk,由 dsh-tool-fs 在结果期写入日志
tool/call 的 arguments 当上一条不适用时的回退
turn/start / turn/end 界定一轮的边界与行尾锚点

因此历史会话也能正确显示——重新打开很久以前的对话,徽章和 diff 照样在,因为它是从日志重放出来的。

撤销复用同一套折叠逻辑:宿主半自己从日志重新推导要反向应用的 hunk,而不是信任前端传来的数据。屏幕上显示的和按钮实际撤销的,是同一份计算,不可能对不上。

几个刻意的取舍

为什么对着 hunk 重新 diff,而不是直接数两侧行数。 meta.diffs 的每个 hunk 前后各带 3 行上下文,这些行在 oldText 和 newText 里同时出现。直接数会把「改 1 行」报成「+7 −7」。所以插件对每个 hunk 重新算一次最小行差异,上下文自行抵消。

为什么新建文件走参数回退。 dsh-tool-fs 对不存在的文件刻意写入 diffs: [](空数组),此时「已应用改动」就是整份文件,由调用参数描述。把空数组当成「无改动」会让模型新建的每个文件都消失。

覆盖已有文件走权威路径。 这种情况下日志里是真实的 before/after hunk,而参数只能看到写完的整份内容——用参数会把整个文件报成新增。

失败的调用不计入。 isError 的结果一律跳过:改动没落地,计进去就是虚报。

只认 append 来源的结果。 回退(rewind)产生的 replacement 副本是模型可见性的产物,不是对话记录,计入会重复。

没有改动的轮次不渲染任何行。 纯问答、纯读文件的轮次不会多出一个空框。

兼容性

  • 宿主半只做一件事:注册 /undo-diff 命令。没有常驻状态、没有额外存储。
  • 客户端半注册的是新的 Chat 节点类型(diff-stat)和自己的右侧栏标签页类型,都不是产品自带的任何一个;聊天节点槽是 keyed 且可合并扩展的,未被占用的 kind 直接不渲染,所以不存在替换产品行的问题。标签页的内容来自它自己的导航参数,而不是再读一遍日志。
  • 撤销通过会话自己的命令通道派发(SessionFace.command),不需要额外的 Remote API 或代码生成。
  • 组合里没有 session 服务时,徽章照常显示,只是不出现撤销按钮。
  • 跟随 DSH 主题 token,明暗主题自动适配。
  • 中英双语。

开发

npm run build     # src/ → lib/(无第三方构建依赖)
npm test          # 38 个单元测试
npm run check     # 构建 + 测试 + 打包/渲染/撤销契约校验
node scripts/inspect-session.mjs   # 用真实会话日志对账
node scripts/verify-install.mjs    # 校验安装态能否通过启动扫描

src/ 是可被 node --test 直接导入的标准 ESM;scripts/build.mjs 只做确定的几件事——把本地 import 按依赖顺序内联、把 react 换成加载器的 require、并在写出前先用 vm.Script 解析一遍产物——每一步都断言,源文件一旦偏离假设就构建失败,而不是打包出一个坏 bundle。

宿主半和客户端半共享同一个 src/pure.js,这是「显示的数字」和「撤销的动作」不会漂移的结构保证。

相关

做同一件事的还有 dsh-diff-stat 和 dsh-turn-diff,可以先看看哪个更合你的口味。

License

MIT