Skip to content

dsh-project-progress

Verified

dsh-project-progress · v0.3.1 · MIT · Web UI

Project Manager — 侧边栏实时进度看板(DSH 双面插件):项目进度树 + 百分比与未完成计数、关注/门控/交接、回滚点与审查。

Install

dsh plugin add dsh-project-progress

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-project-progress

把「会话跑到哪了、还剩多少」从一堆说明文档,变成侧边栏里一张实时进度看板。 DSH 双面插件(Host 面 + Web Client 面),基准 @deepseek-ai/dsh 0.1.5-rc.2。

改名说明:本包曾用名 dsh-project-manager,该名在 npm 上已被他人占用(与本项目无关)。 项目自 0.2.0 起包名与 GitHub 仓库名统一为 dsh-project-progress。

它能做什么

  • 侧边栏实时看板:完成度、未完成清单、进行中 / 异常,按会话精确绑定到对应工作区;
  • 自绘流程图:整树 / 按功能点分区两种形态,左右·上下切换、折叠枝、缩放平移、拖拽改父;
  • AI 建树:读目录骨架与关键文件签名,一次调用产出功能点 / 任务点 + 相对工作量 + 完成度初判;先给成本再花 token,支持分批与增量缓存(命中即复用、不花钱);
  • 进度回写会话:关键事件才推、去重、可静默(省 token);
  • 暂停 / 拦停 + 交接文档:暂停自动打回滚点并生成续接文档;拦停后需重新评审;
  • 快照与回滚:三档快照(git / patch / full),回滚可撤销;
  • 32 个 pm_* 工具:看看板、取下一个任务、汇报进度、合并重复枝、订阅并行治理、节点审查等;
  • 自带诊断:/pm/debug 诊断页(能力探测、降级项、路由与工具清单、客户端上报、宿主错误)。

需求正文《立项》不入库:仓库里只有引用它的代码与测试。


改这个仓库的人:协作纪律、热重载与审批策略的实测结论在 docs/开发须知.md。 本文件只写给使用者看的内容:装、配、用、查。

1. 快速开始

pnpm install
pnpm run build          # tsdown + 包装 client bundle + 产物自检 + 两侧类型检查
pnpm run test           # 448 个测试(领域单测 + 端到端;`node --test` 直跑 TS 源码)
pnpm run watch          # 重建 client bundle + host 产物(开发循环见 docs/开发须知.md,**还需另起 wrap watcher**)

装入 profile 并重启:

dsh plugin --profile web add 'Z:\Projects\project-manager'
# 宿主面改动必须重启 dsh web;client 面改动**刷新页面即生效**
# (Host 侧 HMR 当前是关的 —— 三次受控实验失败 2 次,见 `docs/开发须知.md`;
#  想再试的前提是"先能看见它为什么失败",别直接开开关)
dsh web

2. 调试方式(重点)

2.0 先说结论:DSH 没有给插件留调试入口

实测(dsh --help、lib/bin.js):CLI 只有 web 与 plugin 两个命令, 没有 dsh debug、没有 --log/--verbose,profiles 目录下也不写日志文件。 宿主 logger 只写启动那个终端窗口。

因此插件的可观测性必须自带。本插件为此提供四级入口,从"最省事"到"最底层":

# 入口 看什么 什么时候用
1 http://127.0.0.1:3080/pm/debug 人可读诊断页:能力探测、降级项、存储路线、已注册工具、HTTP 计数、客户端上报、最近诊断记录 首选。插件"看起来没反应"时第一站
2 …/pm/debug?format=json 同上,机器可读(脚本/我给排查时用) 写回归脚本、贴给别人
3 …/pm/debug/logs 仅诊断记录(环形缓冲,最新 200 条) 只想看时间线
4 设置页 →「项目进度」→ 诊断卡片 同一份数据的 UI 摘要 + 三个链接 面板里直接跳

2.1 三条路由各自的用途(按故障层次)

现象 先看 判定
侧边栏完全没有图标 /pm/health 是否 200 404 → 宿主面没加载(重启 dsh web,或看启动终端里的 plugin tree failed to load)
有图标、点开是空白/报错 /pm/debug 的 client 字段 null → 客户端 bundle 没运行(查浏览器控制台;loaded without registering 说明 bundle 语法/注册有问题)
面板有壳但没数据 /pm/board、/pm/debug 的 http.lastPaths 路由 401/404 → 数据通道没通;degradations 非空 → 能力被降级
面板显示 0 个节点 /「未命名项目」 看板头部的「工作区:…(来源:…)」或 /pm/debug 的 已绑定工作区根 根解析不出来 → 在 DSH 侧选中一个工作区(或在本工作区里发起一次工具调用)后刷新;根解析出来了但树是空的 → 面板里「扫描工作区 → 建树」
看板显示的树"不是这个工作区的" /pm/debug 的 当前项目 + 已绑定工作区根 每个工作区根绑自己的项目(§6.7g / FR-140);面板会把当前会话 id 交给宿主来精确解析根,若宿主拿不到会话会退化成"最近使用的工作区"
行为不对(该拒绝没拒绝等) /pm/debug 的 capabilities + logs approval: false 或策略 never → 破坏性操作一律拒绝(这是设计,见 §6.7f)

2.2 客户端侧(浏览器)

  • 控制台把手:window.__PM_DEBUG__ —— 已注册槽位、bundle id、版本、各路由地址。
  • 自我上报:client bundle 每次注册槽位后 POST /pm/debug/client, 于是"浏览器里到底注册上了没有"能在宿主侧看到(client.registeredSlots)。 这是刻意设计:宿主不给插件注入数据全局,只注入 __DSH_BOOT__ / __ModuleLoader__, 所以插件自己的状态必须主动回传。
  • 热重载(实测口径,2026-10-06 更正):分两半,别混。
    • 客户端(lib/client.js):@deepseek-ai/dsh-client-hmr 每 500ms stat 这个文件的 mtime/size,变了就 clientModules.rebuilt(id) → 经 /plugins/events(SSE)推给浏览器, 自动换掉插件那一段(不刷新页面;client 面的 React 状态会丢)。 注意它只认最终产物,而 pnpm run watch 只产出中间产物 lib/client.bundle.js —— 所以必须同时跑:pnpm run watch(tsdown 监听)+ pnpm run watch:wrap (把中间产物包装成 lib/client.js,否则宿主永远看不到变化)。
    • 宿主(lib/index.js):走 @deepseek-ai/cordis-plugin-hmr 重载,但本机实测不可用 —— 产物变化后插件被卸载且不再挂回(/pm/* 全部 404,宿主进程未崩、等待 40s 不自愈, 两次独立复现含一次真实 pnpm run build)。该开关已在 profile 里置回 disabled: true, 机理与复现命令记在 ~/.dsh/profiles/web/cordis.patch.yml 的注释里。 ⇒ 宿主侧改完仍需要重启宿主(或按 HOST_BATCH 确认跑到第几批)。

2.3 构建产物自检(会挡住两类"很难查"的故障)

pnpm run verify
脚本 挡什么
scripts/verify-artifacts.mjs 用 vm 把 lib/client.js 当脚本真跑一遍:语法必须完整、必须调用 __ModuleLoader__.load、id 必须等于包名、factory 必须返回 exports;同时校验 host 产物无 default 导出
scripts/verify-client-combo.mjs 把真实安装的全部 client bundle(本机 57 个)放进同一沙箱按序执行,断言全部注册成功

为什么需要第二个:客户端插件是被拼成一条 combo(多个 bundle 顺序拼进一个 <script>)下发的。 任意一个 bundle 语法不完整,后面所有插件都不执行,而报错只会说 「loaded without registering <第一个缺失的 id>」,指向的是别的插件。 这个脚本就是那次真实故障的回归测试。

2.4 诊断记录(宿主侧日志)

宿主侧没有日志文件,所以插件自己维护一个有界环形缓冲(最新 200 条,自研有界保留):

  • 加载全过程都有记录:能力探测 → 存储初始化 → 确认通道 → 工具注册 → 设置命名空间 → 路由 → provide 服务。
  • 加载期抛错前必先记一条 error(否则只会看到 plugin tree failed to load 这一句)。
  • 加 debugLogging: true(组合配置或设置页)会额外记录 debug 级细节。

2.5 用组合检查确认"插件到底有没有被激活"

dsh --profile web --dump-config | Select-String 'project-manager' -Context 0,2

会看到该插件作为一层 bundle 被插入(- id: project-manager)。 若这里没有,说明 package.json 缺 dsh.bundle.patch,或 profile 的 dsh.profile.bundles 里没有它。


3. 架构

src/
├── index.ts              Host 入口:能力探测 → 存储 → 确认路由 → 工具 → 设置 → 路由 → provide 服务
├── service.ts            应用层:读快照 → 调领域 → 原子写回 → 投影文档 → 记审计
├── domain/               纯领域层(零宿主依赖,Host/Client 两侧共用)
│   ├── graph.ts          树结构、关注归一化(C11)、结构不变量
│   ├── state.ts          状态机 + 计算状态收敛(§9.1 规则 0–6)
│   ├── progress.ts       加权进度(§9.3)、权重对标(§9.3a)
│   ├── validate.ts       C1–C12 判定(顺序写死)、C3/C4 自动修正
│   ├── mutate.ts         写入操作(增/删/改/关注/门控/订阅),纯函数
│   ├── docFormat.ts      doc-guard(§8.2 R1–R6)+ mermaid 子集解析
│   └── project.ts        文档投影与反解析
├── storage/              端口 + KV 主路线(ctx.storageDomain 双领域)
├── adapter/              唯一接触 DSH API 的地方
│   ├── capabilities.ts   能力探测 + 服务解析(属性与 ctx.get 两条路)
│   ├── confirm.ts        审批/提问两条 seam 的唯一出口,fail-closed
│   ├── http.ts           面板数据路由 + 诊断路由
│   ├── debug.ts          诊断总线(环形缓冲)
│   └── runtime.ts        时钟与 UUID 端口实现
├── tools/index.ts        12 个 pm_* 模型工具
└── client/               Web Client 面
    ├── index.tsx         侧边栏图标 + main 面板 + 设置分区注册
    ├── board-panel.tsx   看板(三段式)
    ├── settings-section.tsx 设置分区 + 诊断摘要
    ├── api.ts            同源 HTTP 数据层
    └── contract.ts       面板契约类型(与宿主侧一致性由测试断言)

关键实测约束(都踩过坑,别再踩)

  1. 函数式插件不得有 export default —— Loader 的 unwrapExports 会因此丢掉 inject。
  2. 新服务必须 ctx.provide() —— ctx.set() 只允许覆盖已提供的服务,否则抛 cannot set property "x" without provide(首次装入时整个 web profile 都起不来)。
  3. client bundle 不是 ESM —— 必须是 classic script + __ModuleLoader__.load({id, factory})。 tsdown 0.23 不输出 outro,所以包装由 scripts/wrap-client-bundle.mjs 构建后做。
  4. 平台基座只有 9 个 specifier 可直接 require;其余必须写进 dsh.client.external。
  5. defineDomain 的领域名不允许连字符(^[a-z][a-z0-9_]*$),且记录 schema 用 zod v4。
  6. 客户端插件拿不到自定义 ctx.remote.<ns>(命名空间集合是构建期硬编码清单, 生成器未随发行版安装)→ 数据通路走宿主 HTTP 路由(同 dshmarket 做法)。
  7. sidebar.panellist 的组件只渲染图标;外层按钮/tooltip/文案由侧边栏渲染。 main 槽是 keyed,其 key 必须等于侧边栏项的 id。
  8. 入口 id 必须与包名一致(package.json.name)——combo 自检依赖这个一致性。

4. 进度与改动历史

这里刻意不堆开发日志(原先那 320 行"已完成清单"已删,内容在 git 历史里)。要看"做到哪一批、为什么这么改":

  • git log:提交记录(按主题分组);
  • docs/:代码审查与文档对齐报告;
  • 需求正文与批次台账在仓库之外(未入库,见 .gitignore)——本文件不复制它们的内容。

5. 免责

本插件是辅助工具,不保证任何结果;百分比为估算值,回滚不保证完整恢复。 请自行核实并承担后果。完整声明见 立项.md §0.1 与设置页「诊断」上方。