dsh-evolution
v0.1.0
Published
DSH 自动进化(本地优先):记录开发会话账本,用你自己已配好的模型做跨会话复盘,把踩过的坑和有效做法沉淀成本地知识;默认不联网、不上传、可一键导出。
Maintainers
Readme
⚠️ v0.1.0 为占位发布:本版本只注册包名并发布项目介绍,不含功能代码;功能代码将在调试完成后的后续版本(v0.2.0 起)提供。
dsh-evolution
DSH 的本地优先自动进化插件:记录你的开发会话,用你自己已配好的模型做跨会话复盘, 把踩过的坑和有效做法沉淀成本地知识。
默认不联网、不上传、不写你的代码仓库。 数据全是磁盘上的人可读文件,随时可以看、可以导出、可以删。
当前状态:v0.1.0 · Phase 0(数据地基) 已经可用:会话账本采集、数据预览、试跑(看效果、算成本,不调模型)、 脱敏、界面化配置、目录自检。 尚未接通:自动复盘(调模型归纳)与注入。开着插件不会有任何模型费用。
为什么做这个
DSH 本身很会干活,但它不记得你上次踩过什么坑。每次开新会话,你都得重新解释一遍: "这个项目的 ConfirmOut 守卫要放在幂等判断之前"、"别用 float 存金额"、"发版前必须先跑那个脚本"。
现有的做法通常有两个问题:
- 要么全在云端 —— 你的全部开发对话上传到别人的服务器。
- 要么只有事实、没有进化 —— 只是把结论存下来,不会告诉你"这个问题你已经犯过三次了"。
这个插件想做的是第三件事:在你自己的电脑上,把跨会话的模式找出来, 在你下次动手前提醒你。而数据不出你的机器。
想了解背后的设计取舍,看 docs/DESIGN.md。
它到底怎么工作
正常对话
→ 每次事件被结构化记进本地账本(纯本地、零成本、不调模型)
→ 攒够 N 轮 / 空闲时,用你自己配的模型做归纳
→ 归纳结果写成 markdown 放在本地
→ 下次进这个仓库时,把相关的几条注入到上下文里三个关键设计决定:
| 决定 | 为什么 | |---|---| | 账本记录零 LLM、零网络 | 所以可以每轮都记、可以回扫历史,成本近似为零 | | 只用信号,不记工具的输入输出 | 工具参数与输出体积大、几乎不含可复用决策;记它们等于给自己埋一个越来越大的坑 | | 默认一切"往外"的行为都关闭 | 上传、写你的仓库、向量化、跨工作区聚合,全部需要你显式打开 |
安装
dsh plugin --profile web add dsh-evolution装完重启 dsh web,侧边栏底部会出现「自动进化」按钮。
从源码安装(开发用):
git clone <repo> && cd dsh-evolution npm run build dsh plugin --profile web add "link:E:/path/to/dsh-evolution"
client/dist/index.js是构建产物,但故意入库 —— 这样想直接从 git 安装的人 不需要装 esbuild 就能用。改完client/记得跑一次npm run build并提交产物。
装完先做这三件事
- 侧边栏底部点「自动进化」→ 总览 页,确认「运行中」且数据落点路径符合预期。
- 去 数据与隐私 页点一次「重新自检」,确认目录都能创建。
- 正常对话几轮,回 数据预览 页点刷新 —— 看到记录就说明采集通了。
它存了什么,存在哪
全部在 ~/.dsh-evolution/(Windows: C:\Users\<你>\.dsh-evolution\)。
~/.dsh-evolution/
├── config.json # 你的配置(纯 JSON,可手改)
├── state.json # 回扫水位等内部状态
├── ledger/
│ └── 2026-09-17.ndjson # 账本:一行一条事件,按天分片
├── observations/ # (Phase 1)单会话抽取产物
├── drafts/ # skill 草稿,等你决定要不要导出
└── exports/ # 导出留档想换位置:设环境变量 DSH_EVOLUTION_HOME,或在面板「配置」里填数据目录。
一条账本记录长什么样
{"sid":"session-8c32…","seq":412,"ts":1758000000000,"day":"2026-09-17",
"kind":"user_message","turn":7,"repo":"lescent-middle-center",
"text":"把 ConfirmOut 的幂等判断移到守卫之后"}明确不会记录的内容
| 内容 | 记不记 | 说明 |
|---|---|---|
| 你的提问与助手回答 | ✅ 记(截断后可配) | 这是复盘的主要素材 |
| 工具名字与成功/失败 | ✅ 记 | 失败信号正是"踩坑"的来源 |
| 工具的参数与输出 | ❌ 不记 | 体积大、噪音多、含大量无关内容 |
| 模型思考过程 | ❌ 不记 | |
| 图片等非文本块 | ❌ 不记 | |
| 绝对路径 | ⚠️ 默认脱敏成 <path>/文件名 | 见下 |
隐私
脱敏在写磁盘之前发生
不是"上传时才脱敏"。你在面板的「脱敏预览」页粘什么文本,就能看到真正会被记录的样子。
内置规则(全部可关):
sk-前缀的 API key、Bearer/Basic凭证key=value形式的密钥赋值 —— 保留键名,只掩值- JWT、GitHub token、AWS AccessKey、阿里云 AccessKey、私钥块标记
- 绝对路径 →
<path>/文件名 - 你自己的敏感词表
刻意不做的事:不误伤正常代码。
const password = process.env.DB_PASSWORD、apiKey = readFromVault()、this.token = config.db.token这类写法原样保留。 脱敏过头的后果是记忆全变马赛克、功能直接废掉,比漏掉一个密钥更糟。
默认值一览(都是最保守的一侧)
| 开关 | 默认 | |---|---| | 记录会话账本 | ✅ 开(纯本地) | | 自动复盘(消耗模型额度) | ❌ 关 | | 上传到任何服务器 | ❌ 关 | | 写入你的代码仓库 | ❌ 关 | | 向量检索(会把正文发往你配的端点) | ❌ 关 | | 连接外部数据库 | ❌ 关 | | 跨工作区聚合个人画像 | ❌ 关 |
三个承诺
- 不提供、也不托管任何云端服务。 作者不会接触你的任何数据。
- 默认不上传、不外发。 所有外向行为都需要你显式配置。
- 不做任何面向管理者展示个人画像的功能。 这是效率工具,不是监控工具。
面板
侧边栏底部「自动进化」→ 六个页面:
| 页面 | 作用 | |---|---| | 总览 | 采集状态、开关一览、历史回扫结果、数据落点 | | 试跑 | ⭐ 开启自动复盘前先看这里:会不会真的跑、会注入哪几段、大概花多少钱。不调模型、不写文件 | | 数据预览 | 逐条看已写入磁盘的内容;按会话展开折叠摘要(将来喂给模型的就是这个) | | 脱敏预览 | 左原文右结果并排对照,逐行差异 | | 配置 | 全部配置项,带人话说明,保存即生效(不用重启) | | 数据与隐私 | 这个插件会/不会做什么、脱敏现状、目录自检、你的权利 |
试跑页为什么必须存在:归纳质量差的项目,价值是负的——错误结论会被注入上下文 并长期生效。所以你可以先看再决定要不要打开开关。它也会如实告诉你 「当前条件下这次会话会不会被抽取」,而不是让你猜。
配置改动会在底部显示"未保存 N 项",点保存才落盘 —— 避免误触即生效。
配置项
配置文件是 ~/.dsh-evolution/config.json,也可以用面板改。面板是纯数据驱动渲染的:
配置项的说明、范围、可选值都在 lib/config.js 的 SCHEMA_FIELDS 里,
加一个配置项不需要动面板代码。
主要分组:总开关 / 会话账本 / 自动复盘 / 隐私与脱敏 / 本地 skill 落点 /
接口对接(可选) / 数据库对接(可选) / 检索 / 注入。
关于 skill 落点
面板里可选三种:
| 选项 | 实际路径 | 说明 |
|---|---|---|
| 插件数据目录(默认) | ~/.dsh-evolution/skills/ | 不碰你的仓库、不需要改任何 DSH 配置 |
| 仓库内 .agents/skills | <仓库>/.agents/skills/ | DSH 原生发现,随 git 走,团队天然共享 |
| 仓库内 .dsh/skills | <仓库>/.dsh/skills/ | DSH 最高优先级的项目根 |
| 自定义路径 | 你填 | ⚠️ 需要你在 DSH 配置里额外加 customSkillDirs,否则写了不生效 |
⚠️ DSH 的 skill 发现根按 rank 顺序扫描(
dsh-skill-filesystem文档实测):| rank | 来源 | 路径 | |---|---|---| | 100 |
project-dsh|<仓库>/.dsh/skills| | 200 |project-agents|<仓库>/.agents/skills| | 300 |custom|Config.customSkillDirs| | 400 |user-dsh|<$DSH_HOME>/skills| | 500 |user-agents|<~/.agents>/skills| | 600 |bundled|$DSH_BUNDLED_SKILL_DIR|项目根 = 包含
.git的最近祖先目录(没有则用当前 cwd)。 用列表外的目录名(比如.agent单数)不会被自动发现 —— 这时要么选customSkillDirs并在 DSH 配置里加一行,要么就落插件数据目录。 这是选"自定义路径"时面板会提醒的原因。
关于接口与数据库(默认关)
本插件不实现、也不托管任何服务端。 这两个开关只是留给"你有自己的团队服务"的场景:
- 接口:填地址 + token 所在的环境变量名(不要把 token 写进配置文件),
上传模式目前只有
off/redacted两档。 - 数据库:
sqlite用 Node 内置的node:sqlite(零依赖、无需编译);postgres需要你自备,连接串同样从环境变量读。
两者的用途是把知识共享给团队,不是把数据交给作者。
文档
| 文档 | 内容 | |---|---| | docs/DESIGN.md | 为什么这么定:五个核心决策、从 Hermes 学什么/不学什么、Phase 1 设计、已知限制 | | docs/ARCHITECTURE.md | 怎么实现的:五条设计纪律、每个模块的关键契约、踩过的坑 | | docs/HANDOFF.md | 会话交接:当前状态、下一步、调研结论索引、代码地图 |
开发
npm run check # host 半语法检查(12 个文件)
npm run build # 构建浏览器半(产物 client/dist/index.js)
npm test # 冒烟测试 54 项 + 集成自检 11 项
npm run pack # 打包到 release/两个测试脚本分工不同:
| 脚本 | 证明什么 |
|---|---|
| scripts/smoke.mjs | 逻辑对:配置规范化、脱敏规则、事件折叠、账本读写、注入渲染、试跑 |
| scripts/integration.mjs | 能装上去:用假 ctx 真实调用 apply(),验证模块图可解析、事件订阅与路由注册发生在正确时机、试跑确实不写文件 |
其中有三项测试专门守试跑的"不回写"不变式:对比试跑前后整个数据目录的文件指纹、
确认没创建 config.json / state.json、以及扫源码确认 lib/dry-run.js
没有 import 任何写函数或网络调用。
改 host 半(lib/)需要重启 dsh web;改浏览器半(client/)跑一次 npm run build
后刷新页面即可。
目录结构
lib/ host 半(Node,跑在 DSH 进程里)
├── index.js 插件入口:事件订阅、历史回扫、生命周期
├── routes.js Host↔Client HTTP 路由
├── config.js 配置模型 + 默认值 + 面板字段表(单一事实来源)
├── config-store.js 配置读写、mtime 变更检测、坏配置备份
├── paths.js 数据目录解析
├── storage.js 原子写 / NDJSON 追加读 / 按天分片
├── ledger.js 事件 → 账本记录折叠 + 会话摘要聚合
├── ledger-store.js 账本读写与缓冲落盘
├── repo.js git 仓库定位(宁可认不出,不要认错)
├── redact.js 脱敏
├── inject.js 四段注入渲染 + 预算计算(纯函数,零 IO)
└── dry-run.js 试跑编排(只读:不 import 任何写函数)
client/ 浏览器半(React,跑在 Web 里)
├── index.js 面板六页 + 侧边栏入口
├── build.mjs esbuild 构建脚本
└── dist/index.js 构建产物(随包发布)
scripts/smoke.mjs 冒烟测试(逻辑,54 项)
scripts/integration.mjs 集成自检(能否挂载,11 项)三条给后来者的经验
session/event回调里绝不做重活。 宿主每次 append 都会触发它, 在这里await一个 git 子进程或磁盘 IO,会直接拖慢 agent 循环,连"停止"按钮都会失灵。 所以:回调里只做内存折叠,落盘走缓冲+定时器,git 探测走异步+缓存。写测试,尤其测脱敏。 这个仓库的脱敏规则在被冒烟测试覆盖之前, 连续出过三个静默 bug:正则捕获组编号错位导致规则永不生效、 替换时把键名一起掩掉、负向前瞻被回退绕过导致只掩了半个标识符。 三个都不报错,只是悄悄做错事。
测试代码本身也会骗你。 集成自检里最初有 3 个 async 检查写成了同步
check(fn), 断言失败会变成 unhandled rejection 而不计入失败数——看起来全绿,实际什么都没验。 现在同步/异步分成check/atest两个函数。同一类问题还出过一次: 测试数据用宿主事件名(user/message)当账本 kind(应为user_message), 摘要静默变空;现在foldSessionSummary会返回suspiciousInput把这种情况标出来。
路线图
- [x] Phase 0 账本采集、数据预览、脱敏、界面化配置 ← 当前
- [ ] Phase 1 单会话复盘(接
ctx.llm,用你自己的模型)+ 项目级洞察 + 检索注入 - [ ] Phase 2 个人进化(跨工作区,单独开关)+ 一键导出/删除
- [ ] Phase 3 团队共享(git 仓库模式:把洞察写成 skill 草稿,你决定要不要提交)
- [ ] Phase 4 可选能力:向量检索、自带 HTTP 后端
已知限制
- DSH 没在跑的时候不会发生任何复盘。 没有服务端定时任务,复盘由插件进程内的定时器驱动。
- 复盘会消耗你自己的模型额度,所以默认关闭,且提供每日预算刹车。
- 归属判定用
git rev-parse:如果你的工作目录本身是个 git 仓库(比如 dotfiles 管理的家目录), 该会话会被归到那个仓库名下。这是rev-parse的语义,不是 bug,但值得知道。
许可
MIT
