dsh-plan-tasks
Verified@chengfa/dsh-plan-tasks · v2.0.0 · MIT · Web UI
DSH 侧边栏「任务列表」插件:读写项目根目录 .plan/plan.json,手风琴展示计划进度
Install
dsh plugin add @chengfa/dsh-plan-tasks Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-plan-tasks
在 DSH 右侧栏给项目加一个「任务列表」页签:把想做的事写成计划排队,让 AI 按顺序一条条往下做,做完回填结果、你逐条打勾审核。
dsh-plan-tasks 是 DeepSeek Harness 的侧边栏插件,依赖 dsh-better-sidebar。它把「提需求 → 排队 → 执行 → 审核」这一整套流程收进一个 JSON 文件(项目根目录 .plan/plan.json),并且可以纯用鼠标操作,也可以丢给 AI 批量跑。
它解决什么问题
用 AI 干活时最难受的三件事:
- 一次只能说一件事,说多了它就漏、就混、就只做一半;
- 需求说完就散了,过两天想不起「上次那个改动到底做没做」;
- 多步任务没有队列,你得盯着它一条条催。
这个插件把计划变成文件里的队列:写在面板里 → 排队 → AI 自己按顺序取 → 做完写回备注 → 你打勾验收。你随时能关掉页面去干别的,回来进度还在。
功能
- 四组状态手风琴:
草稿→待执行→待审核→已审核,点标题展开详情(计划全文、模式、任务类型、创建/开始时间、耗时、备注)。 - 已审核单独归档、不再自动删除:审核通过的条目和备注搬进
.plan/notes.json,plan.json只留草稿 / 待执行 / 待审核。已审核会一直留着,不再按条数裁掉。待审核组头可以设「超过 N 条自动通过最旧的一条」。 - 就地编辑:草稿可直接改正文,停手 600ms 自动落盘,标题跟着正文重新生成;编辑区底部可随手切换任务类型。
- 拖拽排序:在待执行组里拖动调整执行顺序,顺序即
order字段,AI 严格按它取任务。 - 置顶 / 转为草稿 / 转为待执行 / 删除:单条操作 + 多选批量操作。
- 审核工作流:执行完进「待审核」,点「审核通过」进「已审核」。通过后停留在当前组原位刷新(不会自动跳到别的组、也不自动折叠),什么时候切组由你决定。
- 转组动画:换组时那条会「升华 / 蒸腾」一下,执行中还有缓慢流光,一眼看出进度变化。
- 深浅双主题:跟随 DSH 主题,不另起一套配色。
- 复制:一键复制计划正文或
任务id:N(粘到别的会话里能直接对上号)。 - 自动刷新:空闲 5s、执行中 3s,切回页面立刻补一次。顶栏只剩「自动」开关(关掉即停止周期性读盘),不设单独的手点刷新按钮。
- 一条命令开始干活:装好后项目里会出现
plan-tasks技能,说一句「开始干活」,AI 就按队列顺序一条条执行。 - 拆解模式:输入框右侧点亮「拆解」后提交,宿主会现读一份项目快照(目录树 / README / AGENTS.md / 最近提交 / 历史计划标题),调一次模型把这段话拆成 1~8 条(上限 12)子任务。落在哪一组跟右侧「草稿」开关走:点亮草稿 → 进草稿组等你过目;没点亮 → 直接进待执行队列排队。同一句话重复提交不会产出重复条目;模型若提到项目里不存在的文件,会在提示条里点出来。
拆解模式的取舍
- 默认 1~8 条,硬上限 12。宁少勿多,硬上限是防止模型把一句话拆成二十条把列表塞爆。
- 落点听用户:点着「草稿」就进草稿组(想先过目),没点就直接进待执行(想立刻开工)。不管落在哪,条目名都带
[拆]前缀,一眼可辨。 - 验证任务不默认追加:单点改动不再单独拆一条「联合回归验证」。只有改动跨了多个互相调用的模块时,拆解才会把验证放进最后一条。
- 禁止臆造文件名:prompt 要求模型提到的路径必须出现在快照的目录树里;落盘前宿主还会再扫一遍,不存在的路径进
missingPaths提示给用户(不阻断写入)。 - 会主动告诉你该砍什么:模型会在
cut里列出题面中它认为重复、无关或性价比极低的部分。 - 拆解模式与「草稿模式」的关系就是落点:点亮草稿 → 产物进草稿组;不点亮 → 进待执行。与「深入模式」是叠加关系:两个开关都点亮时,宿主会在拆解提示词里加一段「做深而不是做大」的强制要求(每条子任务都要写清完成标准),子任务的原文仍是你输入的原话。
- 原文照抄、默认折叠:每条拆解产物都存下本次输入框里的原始提示词全文(
sourcePrompt,首尾空白与缩进一个字符都不动,事后能一字不差复制走)。展开卡片时它默认收起,只显示「原始提示词 · N 字」一行,点开才铺开 —— 原文常常是一大段需求,默认铺开会把卡片撑得比计划本身还长。手工提交的条目没有这段,整节不渲染。
安装
前置条件:必须先装
dsh-better-sidebar。页签是通过它的betterSidebar.registerTab注册的,缺它时本插件会正常启动、不报任何错,但侧边栏里不会出现「任务列表」。
# 1. 装依赖(来自 npm)
dsh plugin --profile web add dsh-better-sidebar
# 2. 装本插件(本包暂未发布到 npm,用 tgz 或 git 地址)
dsh plugin --profile web add ./dsh-plan-tasks-1.0.0.tgz
装完重启一次 dsh web:dsh.client 的模块表是启动时扫描 Loader 条目生成的,不重启页面拿不到 plugins/??dsh-plan-tasks/client.js。
自己打包分发:
cd dsh-plan-tasks
npm pack # 生成 dsh-plan-tasks-1.0.0.tgz
快速开始
- 重启后打开 DSH 右侧栏,点「任务列表」页签。
- 第一次打开会提示初始化,它只往项目根写一个文件:
执行技能.plan/plan.json 计划队列(只含还在流转的条目) .plan/notes.json 已审核归档(审核通过时自动生成) .plan/settings.json 面板设置(改过档位/开关才会生成)plan-tasks是插件通过平台技能系统(ctx.skills.register,runtime 层)注册的虚拟技能, 正文编译在宿主lib/host/skill.js的skillMarkdown(cwd)里:不落到项目目录、不需要任何「同步技能」操作、 装了插件就等于装好了技能。项目里若还留着旧版本写下的.dsh/skills/plan-tasks/SKILL.md, 插件在读取计划时会自动清掉(只删 frontmatter 里name: plan-tasks的那份,别人的技能一个字节都不动)—— 否则项目层(rank 100)会压住 runtime 层(rank 250)的虚拟技能。 - 在底部输入框写下计划,选任务类型,回车提交。
- 想让它开工时说「开始干活」,或在侧边栏里点行内按钮触发。
数据格式
.plan/plan.json 是一个数组,数组顺序 = 执行顺序(由 order 决定):
[
{
"id": 8,
"order": 7,
"name": "侧边栏任务列表",
"plan": "在右侧栏加一个任务列表页签,读 .plan/plan.json ……",
"mode": "默认",
"taskType": "修改",
"draft": false,
"execute": false,
"review": false,
"time": 0,
"note": "",
"createdAt": "2026-10-27T03:21:51.032Z",
"startedAt": ""
}
]
| 字段 | 含义 |
|---|---|
id |
自增编号 |
order |
排序位置,侧边栏拖拽决定,0 在最上 |
name |
短名,手风琴标题 |
plan |
计划全文(要执行的提示词 / 需求) |
mode |
提交时选择的模式 |
taskType |
仅回答 / 目标 / 修改 |
draft |
是否草稿(草稿不参与执行队列) |
execute |
是否已执行完 |
review |
人工审核是否通过 |
time |
耗时(毫秒) |
note |
执行结果回填 |
createdAt / startedAt |
ISO 时间戳 |
finishedAt |
审核通过的时刻 |
decomposeKey |
拆解模式的幂等键:同一次拆解产生的多条共用一个键 |
sourcePrompt |
拆解产物记下的原始提示词全文(原样保存,不裁剪);手工提交的条目不带这个字段 |
已审核归档(.plan/notes.json)
已审核条目连同 note 一起搬出 plan.json,落在同目录的 notes.json:
{ "ok": true, "version": 1, "notes": [ { "id": 8, "note": "…", "finishedAt": "…" } ] }
- 面板无感:
read会把归档拼在列表最前面(最近的在前),「已审核」组看起来和以前一样。 - 不进执行队列:
plan.json只留草稿 / 待执行 / 待审核。模型记账不读已审核。 - 不自动删除:归档只增不减,要少掉某条就在「已审核」里手动删除。
- 可逆:取消审核 / 转草稿 / 转待执行会把条目搬回
plan.json;删除、批量删除两边一起删。 - 损坏不覆盖:
notes.json解析失败时拒绝写入(notes-broken),不会拿空数组把归档清掉。
三种任务类型
| 类型 | AI 要做什么 |
|---|---|
仅回答 |
只回答问题,不改任何文件 |
目标 |
以计划为目标推进,可能需要多步工具调用,不做额外改动 |
修改 |
直接动手改代码 / 文件 |
plan-tasks 技能
技能是插件内置的虚拟技能(runtime 层,不落盘),触发词是「开始干活」「继续干活」「执行计划」「下一条计划」。
侧边栏的「开始干活」和每一轮结束后的自动派发,发的是同一段正文:id、任务正文、mode、taskType。模型按这段做,不要再读技能,也不要先跑 status。
- 不要读
plan.json。记账只跑一次node .plan/q.mjs done或fail。回执只有{ ok, id, remaining },不带下一条正文。 - 一次只做一条。做完写回答就停,下一条由宿主派发。
- 不要开子代理。子代理是一段新会话,比自己改更贵。
- 用户在对话里只说「开始干活」、消息里没有任务正文时,才允许跑一次
status认出队首。
常见问题
装完侧边栏里没有「任务列表」
按这个顺序查,基本都在前两条:
1)dsh-better-sidebar 没装上(最常见)
profile 的 pnpm-workspace.yaml 里是 autoInstallPeers: false,所以 peerDependencies 只会打一条 WARN、不会自动装:
[WARN] Issues with peer dependencies found
而插件客户端用软注入接 sidebar(ctx.inject(['betterSidebar'], ...)),服务不存在时 Cordis 直接跳过整段回调 —— 插件「加载成功」,页签注册代码一行都不执行,没有页签、也没有任何报错。
dsh plugin --profile web list # 看有没有 dsh-better-sidebar
dsh plugin --profile web add dsh-better-sidebar
dsh --profile web --dump-config # 应同时出现 better-sidebar 和 plan-tasks 两行
2)engines.dsh 兼容门禁把插件静默禁用了
dsh-app-boot 的 preflight() 会按 engines.dsh 校验 profile 插件,不满足时直接禁用该行,只在 stderr 打一行:
dsh: disabling profile plugin "plan-tasks": Plugin ... is incompatible ...
网页上看就是「装了没反应」。范围是 ^0.2.1-rc.3 || >=0.3.0-0(覆盖 0.2.1-rc.3 起的所有版本,含 0.3 的预发布)。对一下 dsh --version,再把启动终端里的 stderr 翻出来。
3)宿主半边没加载
curl -X POST http://127.0.0.1:8848/plan-tasks/api/read \
-H 'content-type: application/json' \
-H 'x-requested-with: XMLHttpRequest' \
-d '{"cwd":"<项目绝对路径>"}'
- 404 → 宿主半边没加载(
dsh.profile.bundles里没有它,或没重启) - 403 → 缺
x-requested-with: XMLHttpRequest(这是防护,不是 bug) - 200 → 宿主正常,问题在页签注册(回去查 better-sidebar)
GET /plugins/dsh-plan-tasks/client.js 单独取是 404
正常现象。这个资源只能通过 /plugins/??a,b,c&rev=... 批量导入,不能单取。
更新了插件但界面没变
先 Ctrl+F5 硬刷新;改动涉及宿主(lib/index.js 或 lib/host/ 下任一模块)时必须手动重启 dsh web(client bundle 会热更,host 路由不会)。
换机器 / 发给同事
不要用 link: 分发(绑死本机绝对路径)。npm pack 出 tgz 给对方,且版本号要对得上——装旧版本 tgz 会缺新功能。升级时先 remove 再 add,避免旧包残留。
兼容性
"engines": { "node": ">=20", "dsh": "^0.2.1-rc.3 || >=0.3.0-0" }
engines.dsh 由兼容性门禁校验;版本落在范围外时插件被跳过并打一条 warning,不会让整个 harness 起不来。
范围里显式保留了 >=0.3.0-0 分支:本插件的对外接口面很窄(宿主侧只用 node:fs/node:path 和 webserver 路由注册,客户端只经 ctx.slots 与 betterSidebar 注册页签),没有理由假定 0.3 会破坏它。若不给这个分支,harness 一升到 0.3,所有用户都会遇到「装了没反应」式的静默禁用。
写成 0.3.0-0 而不是 0.3.0 是因为 node-semver 的规则:只有当范围内某个比较符与被测版本的 major.minor.patch 元组完全一致、且自身带预发布标签时,预发布版本才会被放行。写成 >=0.3.0 会连 0.3.0-rc.1 一起挡掉,用户得手工绕过 ERESOLVE。
实测该范围的判定(semver.satisfies):
| 版本 | 结果 | 版本 | 结果 | |
|---|---|---|---|---|
0.2.1-rc.2 |
❌ | 0.3.0-rc.1 |
✅ | |
0.2.1-rc.3 |
✅ | 0.3.0 |
✅ | |
0.2.1-rc.4 |
✅ | 0.4.2 |
✅ | |
0.2.5 |
✅ | 1.0.0 |
✅ |
目录结构
dsh-plan-tasks/
├── package.json # dsh.bundle.patch + dsh.client 声明
├── cordis.patch.yml # 往 profile 里插一行 plan-tasks
├── icon.svg # 页签图标(矢量,用于 UI)
├── build.mjs # 客户端拼接脚本(node build.mjs)
├── assets/
│ └── icon-180.png # 市场卡片位图(插件市场只收 GitHub 托管的 png/jpg/webp)
├── scripts/ # 零依赖自检脚本(都被 npm test 串起来)
│ ├── check-imports.mjs # 宿主模块依赖图:无环、无缺后缀、无裸模块
│ ├── unused-imports.mjs # import 进来却没用的名字
│ ├── unused-decls.mjs # 声明了却没人引用的顶层函数/常量
│ ├── dead-css.mjs # 样式里定义了、JS 从没挂上去的 .plt-* 类
│ └── dead-code.mjs # 客户端未调用函数 + 宿主未被 import 的导出
├── lib/
│ ├── index.js # 宿主入口:只做装配(35 行)
│ ├── host/ # 宿主各模块
│ │ ├── constants.js # 目录名、字段白名单、拆解数量上下限
│ │ ├── paths.js # 项目根定位、.plan 路径、越界判断
│ │ ├── values.js # 脏数据容错取值:str / pick / isRunning
│ │ ├── item.js # 条目组装
│ │ ├── store.js # 条目规范化、短名生成、lite 投影、plan.json 读写
│ │ ├── notes.js # notes.json(已审核归档)的严格读、原子写、按 id 合并
│ │ ├── archive.js # 已审核归档(只搬不删)
│ │ ├── trim.js # 淘汰时间口径(finishedAt→startedAt→createdAt→id)与 FIFO 保留
│ │ ├── skill.js # 虚拟技能注册 + 旧版技能副本清理
│ │ ├── snapshot.js # 项目快照(目录树 / README / 提交 / 历史标题)
│ │ ├── decompose.js # 拆解模式:prompt、解析、臆造路径检查
│ │ ├── auto-review.js # 待审核超阈值自动通过最旧一条
│ │ ├── ids.js # 层级 id 规则(一级数字 + children 的「一级id-后缀」)
│ │ ├── renumber.js # 旧数据编号一次性归一(可重复执行)
│ │ ├── queue-cli.js # .plan/q.mjs 的模板:取任务(nextTask)与记账
│ │ ├── queue.js # 把 queue-cli.js 抄成项目里的 .plan/q.mjs
│ │ ├── dispatch.js # 派发提示词(「继续做计划 N…」)
│ │ ├── cursor.js # 投递游标:组锁、已投去重(纯函数)
│ │ ├── dispatch-store.js # .plan/dispatch.json 的读写
│ │ ├── api.js # /plan-tasks/api/* 各方法的实现
│ │ ├── router.js # 请求分发、鉴权头校验、错误包装
│ │ └── http.js # JSON 请求体读取与响应写出
│ └── client.js # 客户端产物(由 build.mjs 生成,勿手改)
├── src/client/ # 客户端源码:同一函数作用域的片段,按文件名拼接
│ ├── 00-loader-head.js # 模块头、常量、图标底色
│ ├── 10-icons.js # 内联 SVG 图标组件
│ ├── 15-css-decl.js # CSS_PARTS 声明
│ ├── 20-css/ # 样式分组(组内按文件名字典序,即层叠顺序)
│ │ ├── 10-topbar.js # 顶栏与通用按钮
│ │ ├── 11-overlay.js # 遮罩与行内提示
│ │ ├── 12-composer.js # 输入区骨架
│ │ ├── 13-item.js # 条目卡片
│ │ ├── 14-groups.js # 分组配色与组头
│ │ ├── 15-detail.js # 详情区
│ │ ├── 16-bulk.js # 批量操作条
│ │ ├── 17-input.js # 输入框
│ │ ├── 18-chips.js # 模式胶囊与底栏
│ │ └── 30-light.js # 浅色主题覆盖(必须最后)
│ ├── 25-css-join.js # CSS 文本拼合
│ ├── 30-runtime.js # API 调用、浏览器侧发提示词、复制
│ ├── 50-item.js # Item 卡片组件
│ ├── 60-tab.js # PlanTasksTab:状态、分组、手风琴
│ └── 70-apply.js # apply:注入样式、注册页签
├── docs/
│ └── execution-loop.md # 执行循环/任务链路的现状说明(改造对照用)
├── LICENSE
└── README.md
改客户端逻辑或样式:改 src/client/ 下的片段,然后 node build.mjs(或 npm run build)。
npm test 会校验 lib/client.js 与 src/client/ 是否同步,避免提交半成品。
致谢
- 页签容器与注册 API 来自
dsh-better-sidebar。 - 在 DeepSeek Harness 的插件体系上开发。