Bundle
dsh-project-workbench
Local project, requirement-group, and conversation workbench for DeepSeek Harness Web and Desktop
- Source
- 937862061
- stars
- 1 stars
- License
- MIT
- Updated
- Updated yesterday
Readme
# 项目工作台
<p>
<a href="README.md"><img src="https://img.shields.io/badge/%E4%B8%AD%E6%96%87-%E9%BB%98%E8%AE%A4-1677ff?style=for-the-badge" alt="中文文档"></a>
<a href="README.en.md"><img src="https://img.shields.io/badge/English-Documentation-30363d?style=for-the-badge" alt="English documentation"></a>
</p>
项目工作台是 DSH 侧边栏上的一个插件:把同一个需求相关的多个对话归到一组,统一管理进度和记忆,不用再在会话之间反复解释背景。
## 它解决什么问题
- 一个需求常常要开好几个对话分别做分析、实现和验证,每换一个对话都要重新交代一遍背景。
- 侧边栏按时间排会话,看不出哪些对话属于同一个需求、做到哪一步了。
## 怎么用
**打开工作台**:点击侧边栏底部的「项目工作台」按钮,展开或收起工作台。
**建组**:选一个项目,点「新建需求组」起个名字(比如“登录重构”),把同一需求的对话都放进来。
**加对话**:组内的「+」下拉框——
- 「新建对话」:像原生新建会话一样,在组里立刻开一个新对话;
- 其他列出的都是还没加入任何需求组的已有对话,点一下即可归入。
**管对话**:每个对话右侧的「⋯」可以重命名、分叉(在最后一步继续开新线)、归档,或移出当前组(方便换到别的组)。对话运行时,行首会有和原生侧边栏一致的动态指示。
**记记忆**:每个组维护「摘要」和「备注」。摘要只会在**实际推进需求**的对话(执行了工具或有实质性产出、且不是状态询问)结束后自动更新,并在组内每个会话的**第一次对话**时自动带入背景,之后不再重复引入;询问项目情况、闲聊、无关内容不会改动摘要。备注由你手动维护,不会自动带入对话。
**收尾**:需求做完直接归档需求组,组内对话会一并归档。
## 界面预览
### 项目与需求组

### 自动更新的组记忆

### 折叠状态

## 安装
插件需要使用包含 `@deepseek-ai/dsh-web-app` 的配置文件。
```powershell
pnpm dsh plugin --profile web add ./plugins/dsh-project-workbench
pnpm dsh --profile web
```
## 数据说明
- 所有数据只保存在当前设备的 DSH 数据目录中,不会上传,也不会跨设备同步。
- 界面文案会跟随 DSH 当前语言自动切换。
## 技术介绍
插件由浏览器半与宿主半组成,通过公开的 Cordis 插槽与宿主服务接入,不接管原生侧边栏、会话或输入框。
**接入面**
- 浏览器半(`client.js`,`window.__ModuleLoader__` bundle)注册两个插槽:
- `sidebar.footer.action`(order 20):侧边栏 footer 的“项目工作台”入口按钮,与设置、插件面板按钮样式统一;
- `shell.overlay`:工作台面板本体(全高 320px 列),通过测量 overlay 前的兄弟列定位在原生侧边栏右侧,并在中间对话列预留同宽空间。
- 宿主半(`index.js`)通过 `webServer` 注册本地路由、监听 `session/event` 在每次对话后自动更新组摘要,并在 `agent/pre-step` 挂载摘要注入。
**宿主路由**
- `GET/PUT /project-workbench/state`:工作台状态的读写,持久化到当前 profile 的 `project-workbench/state.json`。
- `POST /project-workbench/create-session`:经 `ctx.apiProxy.sessions.create` 真正铸造一个新会话(复用浏览器端的空白会话扫描,仅在没有可复用空白时调用)。
- `GET /project-workbench/session-summary?sessionId=`:读取会话首条用户消息(`firstUserMessageSummary`,纯函数,240 字符截断),用于组记忆播种。
**数据模型**
状态结构为 `projects[workspaceId].groups[]`,每个需求组包含:`id`、`name`、`status`(进行中/待验证/已完成)、`archivedAt`、`sessionIds`、`memory { summary, notes }`。浏览器本地存储仅作离线缓存与旧数据迁移来源。
**组记忆自动更新与注入**
宿主端监听 `session/event`,只在**实际推进需求的对话**(用户请求不是状态询问,且该轮执行了工具调用或产出了实质性回复)结束(`turn/end`)后,才在**原有摘要基础上**更新组摘要并持久化到 `state.json`:摘要保持结构化的 `需求:… / 进展:…` 格式(整个组始终只有一行需求 + 一行进展,紧凑易读);“进展”**就地刷新**为最近一次实际进展的**关键一句**(自动压缩为单行并完整保留,不罗列细节;整条摘要控制在 240 字内,超出时按完整句子丢弃,绝不从句子中间截断),状态询问、闲聊和无关对话不会改动摘要。`agent/pre-step` 只在组内每个会话的**第一次对话**(首个 turn)的模型请求前,把当前组摘要作为带来源标识的上下文消息(`[project-workbench-memory group=… revision=…]`)加入请求,之后的对话不再重复引入。备注仅保存在本地,不会进入任何模型请求。
**组记忆生成:现状与 LLM 模式开关**
当前摘要生成默认是**规则式、确定性**的(不调用模型):宿主端把会话事件按轮切分(`collectTurns`),只放行“实际推进需求”的轮(`turnCountsAsProgress`),再从每个会话取**关键一句**作为“需求/进展”(`condenseText`,跳过“好的/我先…”开头、不罗列细节),最后由 `supplementGroupSummary` 维护成单行 `需求:… / 进展:…` 并就地刷新。整条摘要控制在 240 字内,超出时**按完整句子丢弃**,绝不从句子中间截断、绝不出现 `…`。
每个需求组在记忆面板的“摘要”页有 **「自动更新 / 手动维护」开关**:手动维护时宿主不会在对话后自动改写摘要(也不会自动播种),摘要完全由你维护,但仍会在组内会话的第一次对话时注入。
插件配置里预留了 **LLM 摘要模式开关**(默认规则式,方便后续改善):
- `summaryMode: "rule" | "llm"`(默认 `"rule"`):`"llm"` 时,进展轮结束后由配置的模型按专用提示词(`llmSummaryPrompt`)生成 `需求:… / 进展:…` 两行摘要;
- `summaryModel: "provider/model"`(例如 `"deepseek-official/deepseek-chat"`):LLM 模式使用的模型;**未配置时自动读取该会话当前使用的模型**(经宿主 `apiProxy` 的会话模型选择,即“配置系统自身的对话模型”),调用失败或不可用时自动回退到规则式。
后续改善方向:① 真正让模型“改写/归纳”而非规则抽取;② LLM 调用改为异步队列 + 失败重试;③ 预算与提示词可配置;④ 增加“手动锁定某次摘要”避免被自动版本覆盖。
**会话行为**
- “新建对话”遵循原生 New Chat 语义:优先复用当前项目尚未发送过消息的空白会话(空白会话不显示时间、不累积),否则经宿主网关新建;
- “添加已有对话”仅列出未被任何需求组认领、未归档的会话;
- 组内会话行支持重命名、分叉(`ctx.sessions.fork`)、归档(`ctx.workspaces.archiveSession`)与移出需求组;运行状态指示与原生一致。
设计稿与产品边界见 [docs/design.md](docs/design.md)。
Install
dsh plugin --profile web add github:937862061/dsh-project-workbench
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-project-workbench from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.