工作进度小秘书 · dsh-progress-secretary
把「当前工作区的进度」维护成一份常驻工作区的活笔记本,并提供两个手动操作:
| 操作 | 命令 | 按钮 |
|---|---|---|
| 记录进度 | /note |
记录进度 |
| 汇报进度 | /brief |
汇报进度 |
笔记本是重写式的,只描述此刻活着的东西,不堆历史。
它负责什么,不负责什么
| 关注点 | 归属 |
|---|---|
| 进度笔记本与两个操作 | 本插件 |
| 工作区文件快照与回滚 | 不是本插件 —— 装一个你信得过的检查点插件,用它的界面 |
| 会话分叉 | DSH 自带能力(侧栏会话右键 → 分叉会话) |
本插件不实现快照,也不包装快照:没有回滚命令、没有检查点列表、没有与之相关的配置或降级。包装一份别人的能力会再造一套入口、一套降级和一套措辞 —— 那正是「不造轮子」要避免的。也不提供 fork:会话分叉由 DSH 自带。
命名撞车提醒
DSH 侧栏右键菜单里有一个「归档会话」,那是把会话从侧栏收起来(历史保留)。
本插件的「记录进度」是重写笔记本,就这一件事。
同一个「归档」味道的词,两件不相干的事。 本插件因此不叫「归档」。
安装
前置依赖:没有。 本插件不依赖任何其他插件,也不调用任何别人的命令。
从 GitHub 安装:
dsh plugin --profile web add github:zpda88888a88888-debug/dsh-progress-secretary
要可复现就固定到某个 commit(插件市场也是这么装的):
dsh plugin --profile web add 'github:zpda88888a88888-debug/dsh-progress-secretary#<40-位-commit>'
然后重启该 profile:
dsh --profile web
装完后输入框里出现「记录进度」「汇报进度」两个按钮,命令表里出现 /note 与 /brief。
它不依赖任何其他插件,也不调用任何别人的命令 —— 需要文件快照与回滚时,另外装一个检查点插件,用它自己的界面。
两个操作
/note — 记录进度。 请 agent 把这段时间的对话与工作重写成 .dsh/progress.md。你只会看到一条回执;笔记本在本轮结束时就是最新的。不拍快照 —— 快照不归它管。
结果在哪里显示
命令的结果在聊天框里,不在输入框里。
点「汇报进度」或敲 /brief 之后,回读会以一张卡片出现在对话中:标题是操作名,正文是排版好的报告(交给平台自己的 Markdown 渲染器,所以你不会看到 ## 和 ** 这些源码符号),不需要点开。窗口刷新或换个标签页之后它还在 —— 它来自会话记录,不是浏览器状态。
输入框里那两个按钮旁边只会出现一种回执:命令根本没被受理(命令不存在、执行被拒、派发报错)。这类失败没有进入处理函数,因此不会在会话里留下记录,输入框是它唯一能被看到的地方 —— 所以它保留到你的下一次操作,不会自己消失。
同一个道理:处理函数报的错也在聊天框的卡片上(以「⚠ 失败」标出),而不是在输入框里闪一下就没。
笔记本
笔记本是给「回到这个工作区、要接着干活的人」看的,不是给「接手开发这个插件的 agent」看的。判据只有一条:这条信息,在我重新捡起工作的那一刻,需要被知道吗?
# 工作进度
## 进行中
## 待办
## 生效约束
## 已验证事实
## 产出物索引
## 叙事
每节放什么、不放什么、上限多少:
| 小节 | 放什么 | 不放什么 | 上限 |
|---|---|---|---|
| 进行中 | 现在到哪一步了 | 背景、流水账、待办的重复 | 3 条 |
| 待办 | 要做什么 + 卡在哪个决策上 | 方案小作文(候选方案放它们自己的文件) | 一行一条 |
| 生效约束 | 违反了会出事的边界 | 设计决策、实现细节、方法论、理由 | 8 条 |
| 已验证事实 | 「别再试」「别再问」的确认结论 | 过程(「我跑了个实验…」) | 10 条 |
| 产出物索引 | 路径、标识 | 内容 | 只放指针 |
| 叙事 | 主线与结论 | 技术复盘 | 5 行 |
规则:
- 前五节是状态快照,每节写全量 —— 因为笔记本是重写而非追加。
- 某节为空时写「(无)」,不要省略标题。
- 同一件事只写在一节里。
- 「产出物索引」只放路径/标识。
- 没有「已完成」小节:完成项离开待办、在索引里留一条。
- 条目不带日期,不构成时间线。
上限是写作约束,不是校验:插件不会因为笔记本超标而报错。它的作用是让人回头删一遍。
放不下的信息去哪:设计决策与变更史 → CHANGELOG.md;平台机制、实验过程、实现笔记、方法论 → NOTES.md(不注入、不占上下文);可机检的结论 → 测试。笔记本只留「现在到哪了」。
笔记本由 agent 写,不是插件写 —— 插件只读它。所以你可以手工编辑它,格式宽松,解析器只认 ## 标题、忽略标题之前的内容。
文件
<workspace>/
.dsh/
progress.md # 主笔记本;一个工作区一份
主笔记本建议纳入 git;旧版本由 git 保留,插件不额外备份。
配置
全部可选。默认值:
| Key | 默认 | 含义 |
|---|---|---|
notebook |
.dsh/progress.md |
笔记本路径 |
noteCommand |
note |
|
briefCommand |
brief |
命令名只能是 /^[a-z][a-z0-9_-]*$/(注册表强制),所以中文只做标签。
四条值得知道的设计约束
汇报进度必须写进会话日志,不能写进收件箱。 投进收件箱的消息会被 agent loop 认领为一个用户回合 —— 只想读背景的会话会把笔记本里的「待办」当成工作指令开始执行。追加到日志只进入派生历史,不认领回合。
命令结果渲染在聊天框,不渲染在输入框。 平台已经凭 command/run/command/done 这对持久事件把命令结果渲染成对话里的一行,插件不再自己重复一份。客户端因此持有两张面:聊天框的卡片(承载结果,持久)与输入框的按钮组(只承载准入失败 —— 那是唯一没有持久记录、只能在此处可见的失败)。
卡片把结果当 Markdown 渲染,不当源码打印。 结果文本摘自笔记本,本来就是 Markdown;逐字输出会让用户看到 #、**、- 。渲染器取自平台的 seed 模块表(react 也在那张表里,所以不必也不能作为插件行声明),读取是防御性的:读不到只退回逐字文本,内容不丢,也不能把入口一起带走。
范围边界也约束依赖。 既然不负责快照,就不该知道任何一个检查点插件的存在 —— 连它的命令名、Remote 命名空间、配置键与降级路径都不该出现。这条边界同时管住代码与这份文档:测试把它当作源码级红线钉住,文档里也不点名那个插件的任何标识 —— 加一个按钮很容易,把背后那套依赖一起搬回来才是真删掉。
开发回路
用目录软链安装,不要用 tarball:
dsh plugin --profile web add /path/to/dsh-progress-secretary # -> link:/path/to/...
tarball 会把代码复制进 profile,每次改源码都要改版本号、打包、卸载、重装。link: 装法只需改源码 → 重启。
link: 安装下,本包的 peer 依赖自其 realpath 向上解析,因此工作区必须保留指向 $DSH_HOME/profiles/node_modules/@deepseek-ai 的 node_modules/@deepseek-ai/* 软链,否则启动报 ERR_MODULE_NOT_FOUND。tarball 安装不需要它们。
什么会自己重载
| 改什么 | 要重启吗 |
|---|---|
profile 的 cordis.patch.yml |
不用 —— patchReload: live 会在有效编辑时重新组合 |
lib/index.js(host 半边) |
要,除非开启 cordis-plugin-hmr(它默认 disabled) |
lib/client.js(浏览器半边) |
要 —— dsh-client-hmr 只对被重建的 bundle 反应,而这个 bundle 是手写的 |
测试
npm test
| 套件 | 钉住什么 |
|---|---|
test/notebook.test.mjs |
笔记本格式:小节词汇、指令文本、解析容错、回读形态 |
test/index.smoke.test.mjs |
两个命令的处理器:注入指令、回读、日志而非收件箱,以及「只读不写、不执行别人的命令」 |
test/client.test.mjs |
加载并渲染浏览器半边:哪张面显示什么、卡片正文走谁渲染、准入失败的回执 |
test/client-slots.integration.test.mjs |
注册打进真实 SlotRegistry;卡片渲染器确实是平台发布的模块 |
test/commands-integration.test.mjs |
用真实 dsh-commands 注册表 + 真实 Cordis 验证 host 注册契约 |
test/spec-conformance.test.mjs |
spec.md / spec-ui.md 与本实现的一致性 |
test/client.test.mjs 的 React 是替身(真实 React 无法从本工作区解析):它只实现这几个组件依赖的部分 —— 按调用顺序寻址的 hook 槽、setState 触发重渲染 —— 并且不跑 effect。一个靠 effect 才能正确渲染的组件在它下面测不出来,这条写在文件头部。Markdown 渲染器在它下面也是替身:那套测试钉住的是交接(卡片把结果文本交给了平台渲染器,且降级时仍有一份逐字正文),不是排版本身 —— 排版是平台的。
test/client-slots.integration.test.mjs 的槽位注册表是真的:它把平台的 SlotRegistry 装进真实 Cordis,按真实 owner 的方式声明座位链,再把本插件打进去。它钉住的是注册的合法性(list 座位要 id、keyed 座位要 key、未声明的座位会抛错 —— 所以注册必须走惰性 inject),以及卡片渲染器那条平台假设(shell 必须把它放进自己的静态模块表)。这些平台包是本插件不依赖的,解析不到时该套件跳过并说明原因,不会假装验证过。
test/spec-conformance.test.mjs 是防漂移检查:它反向解析 spec 的配置表与 inject 声明跟代码对拉(两边不能单独改),并把红线断言成源码模式(匹配前先剥掉注释,所以注释既不能满足也不能触发红线)。它还检查两份 spec 的切分边界 —— 界面相关的要求不许漂回 spec.md。它刻意脆弱,那正是它的用处。
改动这段 UI 时值得做一次变异验证:把「输入框回显命令结果」「卡片截断文本」「卡片不注册」分别改回去,确认对应测试变红。0.4.0 那轮六个变异各被拥有该性质的测试抓住;0.4.2 又验了五个 —— 退回「直读 ctx.remote」、不拆结果信封、把降级说明拿掉、抽掉声明路径、抽掉 ctx.get 路径,分别红 4 / 4 / 2 / 1 / 1 条。
已知限制
- 没有预览面板。 当前是两个按钮 + 聊天框里的结果卡片。
- 客户端持有命令名表。 默认
note/brief;host 侧的同名配置可改,客户端这份改不了。两边不一致时按钮会调用一个不存在的命令 —— 表现为输入框里可见的准入失败,不会静默无反应。 - 回滚不由本插件提供,也不由它提示:需要时,用你装的那个检查点插件自己的界面。
- 笔记本的备份与回滚不由本插件承担(见
spec.md第四节)。你另装的检查点插件怎么配置、会不会把笔记本一起抓走,是它自己的配置问题 —— 本插件既不读也不改它的配置。
spec
两份,按关切切分,各管一半,不重复描述同一件事:
spec.md— 系统行为:笔记本、注入语义、失败语义、范围边界、配置、发布契约。spec-ui.md— 交互与呈现:入口、命令结果显示在哪个界面、输入框区域承载什么、客户端依赖的降级。
变更历史见 CHANGELOG.md;当前实现进度与验证状态见开发者本机的工作区笔记本 .dsh/progress.md(那份不在本仓库里,它随工作区走)。
兼容范围
| 维度 | 声明 |
|---|---|
| DSH | peer 声明 @deepseek-ai/dsh-llm >=0.1.2-rc.1 <0.2.0、@deepseek-ai/cordis ^4.0.2、@deepseek-ai/schemastery >=3.0.0。 |
| DSH 实测 | 0.1.5-rc.3(本机长期运行的 profile)、0.1.7-rc.1。见下方「验到了什么」。 |
| Node.js | `^22.19.0 |
| Profile | 只支持 web。dsh.client.platform = "web":两个按钮与结果卡片是 Web 客户端的界面。host 半边本身与 profile 无关,但没有 client 半边就只剩两个命令。 |
| 操作系统 | 没有平台相关代码,macOS / Linux / Windows 通用。本次实测基线为 macOS。 |
| 生命周期脚本 | 无 preinstall / install / postinstall / prepare。安装即用,不需要构建授权。 |
验到了什么,没验到什么
0.1.7-rc.1 的判定基于三组证据,不含「人在浏览器里点过按钮」:
- 整套 55 个用例对着
0.1.7-rc.1的真实平台包跑通(0 skipped)—— 其中client-slots.integration打的是真实SlotRegistry,commands-integration打的是真实dsh-commands注册表 + 真实 Cordis; - 在一次性
$DSH_HOME里用官方 CLI 从 GitHub 固定 commit 安装成功,装进去的lib/index.js与仓库逐字节一致; dsh --profile web --dump-config在0.1.7-rc.1上退出码 0、无报错,本插件的 loader 行正确合成。
没验到:真实进程启动后浏览器里的实际渲染,以及卸载/回滚路径。这两项得在真机上有人看,本插件因此不声称它们已验证。
权限
| 权限 | 范围 | 说明 |
|---|---|---|
| 文件 | 只读,工作区内单一相对路径(默认 .dsh/progress.md) |
经 ctx.fs.resolve(rel, { cwd }) 解析后读取。不写文件、不遍历目录、不读工作区之外的路径。笔记本是 agent 写的,插件只读它。 |
| 网络 | 无 | 不发起任何请求。 |
| 命令 | 注册 /note、/brief 两个命令;不执行任何外部进程 |
不调用 shell,也不调用任何其他插件的命令。 |
| 凭据 | 无 | 不读环境变量,不读配置里的密钥。 |
已知风险
- 笔记本会进入模型上下文。
/note把它注入指令,/brief把它追加进会话日志 —— 两者都要过模型。不要把密钥、令牌、个人隐私写进.dsh/progress.md。 - 插件以 DSH 进程权限运行。它自己只做上面表格里的事,但装第三方插件这件事本身不由本插件担保。
/brief追加的是派生历史而非收件箱,因此只读背景的会话不会被笔记本里的待办当成指令 —— 这是设计约束,不是配置项。
License
MIT
No comments yet. Be the first to write one.