dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH plugin: decision notes (Agent Notes) — 把「为什么这样改、放弃了什么」沉淀成可机械校验的仓库笔记:技能、会话开关、真门禁、上游合并
English | 中文
把「为什么这样改、放弃了什么」变成仓库里可机械校验的决策笔记,装进 DeepSeek Harness。
方法提炼自 czm15053/write-notes-like-deepseek(它又提炼自 DeepSeek Harness 的 .agents/notes/ 实践)。本插件把那套方法做成 DSH 原生插件:技能、会话开关、真门禁、上游合并。
代码能表达「系统现在怎么跑」,表达不了「为什么必须这样跑、以及放弃了什么」。于是每个新会话都会: 自作聪明地「优化」掉一段当年为避免死锁而刻意写别扭的代码;为赶局部需求打穿全局结构;把三个月前已证明会内存溢出的方案再提一遍。
本插件让规矩绕不开:和代码同仓库、同一次改动、机械校验、旧决定封存带封印。
# 从本地目录装进你的 profile(开发期)
dsh plugin add --profile <你的profile> /path/to/dsh-agent-notes
# 或从 GitHub
dsh plugin add --profile <你的profile> github:iuuuuuuuu/dsh-agent-notes注意:`plugin add` 只写依赖,不会把插件加进启用列表。 实测确认:它把
包名写进profiles//package.json的dependencies,但
dsh.profile.bundles里没有这一项 —— 而 DSH 是按bundles逐层加载
插件的(每层读该包自己的cordis.patch.yml)。所以只add的话插件装了
却不生效,--dump-config 里也看不到它。还需要手动把包名加进 bundles:```jsonc
// $DSH_HOME/profiles//package.json
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-agent-notes" // ← 加这一行
]
}
}
`然后重启 DSH。验证是否真加载:dsh --profile --dump-config里应出现# == dsh-agent-notes与- id: agent-notes。若只在依赖里而不在
bundles 里,启动日志不会有报错,插件却完全没跑 —— 这是最容易踩空的一步。/agent-notes 查看开关与笔记树状态
/agent-notes on 开启本会话的决策笔记(持久化,重载后保持)
/agent-notes on --global 改全局默认(影响所有未单独覆盖的会话)
/agent-notes reset 清除本会话覆盖,回到全局默认
/agent-notes init 在当前仓库建出 .agents/notes 骨架
/agent-notes gate 跑真门禁(也可 gate tree / gate format / gate archived)装好插件后,新会话和旧会话默认都不启用,需要时手动打开(点胶囊,或 /agent-notes on)。这样装上插件不会悄悄改变任何既有会话的行为。
想让所有会话默认开启,二选一:
/agent-notes on --global # 运行时改全局默认,落盘持久化或在插件配置里写 enabled: true(仅在该配置生效的 profile 里)。
会话级开关优先级高于全局:单独关掉某个会话后,改全局也不会影响它。
不用敲命令也能一眼看到当前会话是否生效:输入框工具行左侧有一枚绿色胶囊 「● 笔记规范」,就挨着「完全权限/工作区内修改」那一排,点击即切换; 关闭时胶囊变灰。
它读的是会话投影(agentNotes),所以刷新、重连、切换会话都自动正确。 点击开关走的是官方斜杠命令通道(/agent-notes on|off),因此和手敲命令 完全等价,并且开关操作零 token。
设计说明:投影只折叠宿主自己写的 command/run 事件,本插件不写会话日志。原因是插件零@deepseek-ai依赖,拿不到appendPluginRecord;而退路
session.append('plugin:…')实测会写出ignorable=undefined的残废记录
(pluginRecordOf 读不回),看着成功实则污染日志。走命令事件则完全绕开这个坑。对话 / 轨迹 旁边会多出一个 「笔记」 tab,里面就是上游那套成品看板 (架构基线 / 演进时间线 / 避坑智库 / 决策清单四个视图),不自绘 UI:
tab 里用 iframe 加载 vendored 的上游资产
skills/agent-notes/assets/agent-notes-board.html,宿主只负责把当前会话仓库的笔记实时注入它预留的 window.__INLINE_DATA__ 插槽。理由:官方看板是 76K 成熟 UI(交叉引用入度、因果链路、多维筛选),自绘只会
更丑、越走越远。iframe 让视觉与交互跟随上游更新,本插件只维护数据适配层
(lib/board.js的buildInlineData)与一处正文渲染补丁:上游parseMarkdown
不认 GFM 表格,lib/board-markdown.js 只补这一条,其余沿用上游输出。它读的是实时磁盘内容而不是静态快照:宿主注册只读 HTTP 路由 (/agent-notes/ui、/agent-notes/board、/agent-notes/note、/agent-notes/health), 每次切到该 tab 现读。
不用会话投影的原因:投影的 view 必须是同步纯函数,读不到文件系统(已核对源码 view: state => wire.view(state))。安全边界:路由只接受固定路径,只读notesRoot之下且拒绝任何..;
会话解析不出笔记根时返回 409,绝不猜一个路径或退回进程目录。
注入数据必须防住两件事(都有回归测试):
、 本仓库 --(插件启动时异步)--> 用户机器两段都不再需要人记得。任何一段失败都会让 job 变红,不会静默跳过。
**为什么用 `git ls-remote` 而不是 GitHub API**:未认证请求 60 次/小时就 403,
定时任务天天跑必撞墙。git 协议没这个限制。
**为什么拿到 commit 而不直接锚分支名**:jsDelivr 对分支名的清单缓存会滞后,
锚 commit 才能拿到该次提交的真实树。
### 副本更新后,模型什么时候看到新规则
`lib/skill.js` 的 `get()` **每次调用都现读磁盘**,没有缓存。所以:
- 自更新写入新内容后会调 `invalidate()`,宿主丢弃缓存的技能目录,
模型下一次加载技能就是新内容,**不需要重启 DSH**;
- 但技能内容不是每轮都注入的 —— 它按需加载(`SkillDefinition` 语义),
已加载过的会话可能还持有旧正文。
判据:让模型重新触发一次技能加载即可;要绝对确定就重启宿主。
## 自检与测试npm test # 行为测试(真跑子进程、真写文件) npm run gate:fault-injection # 证明门禁会红,而不是恒绿 npm run verify # 包自检:清单 / patch / 零依赖 / 双语文档与徽标 / vendored 与上游哈希 npm run sync:check # 上游是否已漂移:副本与钉住 ref 是否逐字相同(会真变红) npm run evidence # 实机证据:用 DSH 自带的真实 SkillRegistry 验证注册 npm run check # 全部纯本地门禁串跑(提交后跑这个) npm run check:full # check + evidence(需本机 DSH) npm run hooks:install # 装 commit-msg 钩子:每个 clone 跑一次,此后漏写笔记会被当场拦下 npm run archive:note -- # 归档并封印(哈希由脚本算,勿手改 manifest) npm run doc:budget # 文档字数预算(超了会红,提示压缩而非扩预算)
`fault-injection` 会为每条门禁注入它该拦的故障(非法分类、坏文件名、缺备选方案、`implemented` 残留提案标题、篡改/删除已封印归档……),断言必须非零退出;再清掉故障断言回到零。**任何一条对故障无反应,该脚本自身非零退出**——它自己也引入了一条必然失败的断言验证过会红。
其中 `hooks:install` 值得单独说一句:它把「该写笔记而没写」这道检查挂到本地 `commit-msg` 上,当场给出反馈。但钩子能被 `git commit --no-verify` 绕过,**CI 那一道才是权威的**——两道都要在。
> `npm run evidence` 与集成测试需要找到 DSH 安装位置。会自动探测 `$DSH_HOME`、`~/.dsh` 及其各 profile 的 `node_modules`;桌面端内置安装等非标准位置请显式指定:
>
> ```sh
> DSH_DIST=/path/to/dsh/lib/bin.js npm run evidence
> ```
>
> 探测不到时 `evidence` **明确失败(退出码 2)**,不假装通过;集成测试则降级为同契约承接器,并在输出里标注实际用的是哪种模式。
## 设计约束(为什么零 `@deepseek-ai` 依赖)
本插件**不 import 任何 `@deepseek-ai/*`**:profile 的 `node_modules` 里 `@deepseek-ai` 只解析到 `cosmokit`/`schemastery`,插件运行时拿不到 DSH 的类型包。因此:
- 宿主 API 全部按**鸭子类型**使用(`ctx.skills` / `ctx.tools` / `ctx.commands` / `ctx.systemPrompt` / `ctx.agents`),缺失时降级关闭而不是崩;
- `lib/` 下只用 node 内建模块(浏览器半边与宿主半边都是);
- 工具用**裸对象**注册(DSH 接受原始 `ToolDefinition`,MCP 工具就这么进来)。
谁往 `lib/` 里加了 `@deepseek-ai/*` import,`tools/verify-plugin.mjs` 的自检立刻红。唯一运行时依赖 `mdast-util-from-markdown` 只被 `tools/` 下的校验脚本使用,不参与插件运行时。
## 组件
| 文件 | 职责 |
|---|---|
| `lib/index.js` | 组装;会话级规范注入的挂载/卸载 |
| `lib/state.js` | 全局默认 + 会话覆盖双持久化(原子写) |
| `lib/notes.js` | 笔记树探测、真门禁执行、脚手架 |
| `lib/skill.js` | `agent-notes` 技能 provider(剥 frontmatter) |
| `lib/policy.js` | 注入的规范文本 |
| `lib/commands.js` | `/agent-notes` 命令 |
| `lib/tools.js` | `agent_notes_gate` / `agent_notes_status` 工具 |
| `skills/agent-notes/` | vendored 上游内容(SKILL.md、references、templates、scripts、assets) |
| `tools/` | 上游合并、自检、故障注入、测试(校验脚本在此) |
## 已知限制
- **看板模板引 Google Fonts 外链**,离线环境下会退化到系统字体;功能不受影响。
- 上游内容同步依赖 jsDelivr 可访问;不可达时 `sync` 会明确失败而不是静默跳过。
- DSH 处于 developer preview,宿主 API 若变更需跟随。
- 会话开关按**会话 id**持久化;会话被删除后其覆盖项仍留在状态文件中,目前不做 GC。
- 宿主仓库的 CI 接入需自行配置;插件不代为修改仓库。
## 许可
MIT,全文见 [LICENSE](LICENSE)。
本插件分发的 `skills/agent-notes/` 下 21 个文件是上游
`czm15053/write-notes-like-deepseek` 的 vendored 副本,版权归其作者 ——
来源、固定 commit、许可证与文件数逐项列在 [第三方归属声明](THIRD_PARTY_NOTICES.md)。
---
[](https://github.com/deepseek-ai/deepseek-harness)