Skip to content
dsh.fish
Bundle

dsh-plugin-workspace-archive

DSH 插件:工作区目录被删/移走时自动归档其会话,目录回归时恢复(取消归档 + 把成员挂回分组);支持手动归档整个工作区(工具与斜杠命令);并把「无项目」里工作目录匹配的会话归位。只走官方 API、零运行时依赖。

Source
WindFromKadath
License
MIT
Updated
Updated 7 days ago

Readme

# dsh-plugin-workspace-archive

[DSH](https://github.com/deepseek-ai/deepseek-harness)(DeepSeek Harness)插件:工作区文件夹被**删除或移走**时,把它名下的会话**归档**;同一个文件夹**放回来**时,把这些会话**带回**该工作区 —— 只走官方 API。

English: [README.en.md](README.en.md)

## 它补的是哪条缝

DSH 有意把"目录缺失"和"归档"分开:

- 目录无法校验的工作区(被移动、被删除、从未记录)**无法加入**新会话,其中的会话会散成「无项目」;
- 「移除项目」保留文件夹与会话历史,但**重新添加同一目录会从空项目开始**:旧会话不会自动回来。

本插件补上这条缺失的联动。它不改 `app.asar`、不写官方 `storages/` 与 `sessions/` 文件,自己的记账放在 DSH 主目录下的 sidecar 里。

## 它做什么

| 触发 | 结果 |
|---|---|
| 已登记的工作区目录被删除或移走 | 把该路径下**由本插件登记过的**会话归档(侧栏默认视图不再显示,在"全部对话"里仍可见) |
| 工作区登记被移除(菜单里的「删除工作区」,文件夹还在) | 同样视为消失并归档 —— 否则那些对话会散成「无项目」。**这一条不等确认窗口**:官方删除登记是即时的,等窗口就是让你盯着那批对话散在侧栏里看几秒 |
| 目录或登记**回来** | 本插件归档过的那批会话**取消归档**;并且**台账里记过的全部成员**都会被挂回该路径的工作区 —— 包括**在消失之前就已经归档**的会话(典型是你自己手动归档的) |
| 一条会话待在「无项目」里,但它的工作目录**就是**某个已登记工作区的路径 | 启动后**自动挂回**该工作区(判据:不在任何工作区的成员表 + 目录**精确相等**;可用 `adoptUngrouped: false` 关闭) |
| **你说「归档这个工作区」**(模型调用 `workspace_archive` 工具) | 该工作区里**官方尚未归档**的会话被收进归档;`action='unarchive'` 恢复 —— **只放回本工具归档的那批**,你自己在官方界面里归档的原样不动。(注意:归档**不等于**隐藏工作区行 —— 官方默认每个分组都显示,行还在,只是下面空了) |
| **你在输入框打** `/archive-workspace <路径或标题>`(或 `/restore-workspace <…>`) | 和上一行**同一个动作**,但**由你自己触发、不经过大模型**。命令注册在宿主侧(官方 `dsh-commands`),输入框的命令弹窗里能选;恢复用的还是那批独立清单 —— 所以它**不会**放回你自己在官方界面里归档的会话 |

三条永不违反的规则:

- **你自己归档的会话永远不会被取消归档**:它们只是回到自己的分组里,仍然隐藏,直到你手动取消归档;
- **归档 / 恢复只处理它记过账的会话**:台账是插件运行期间、工作区健康时写下的;这条路径**绝不**按工作目录反查。唯一的例外是启动后那一次**归位**:只把**工作目录精确等于**某已登记工作区路径、且不属于任何工作区的会话挂回去,可用 `adoptUngrouped: false` 关闭。
- **手动归档与自动归档是两份清单,互不撤销**:你让它归档的工作区,不会因为那个目录"消失又回来"被自动流程放出来;反过来,自动归档的那批手动恢复也碰不到。

## 它为什么做得成(关键机制)

因为这条能力**不需要改任何文件**:DSH 里"会话属于哪个组"存在**工作区记录的一份成员表**里,而官方恰好提供了往这份表里补一笔的 API(`Workspace.attachSession`)。

1. **分组是"名册",不是"档案"里的字段。** 会话日志里写的是它自己的工作目录;"它显示在哪个工作区下"是工作区记录里的成员表 —— 改分组 = 改名册,官方允许。
2. **官方挂载只校验一个条件,而这个条件天然成立。** `attachSession` 会读会话 header 的 `cwd`,要求它**规范化后精确等于**该工作区路径,否则拒绝;而"无项目"里的孤会话,cwd 本来就等于那个路径 —— 官方自己会放行(它也顺手代我们复核了一遍,塞错组在机制上不可能)。
3. **"没在名册上"是官方自己留下的空洞。** 官方只在**第一次启动**时按 cwd 把已有会话归组一次(bootstrap 一次性,写完 initialized 标记就不再跑),此后新增的工作区不会再回头认领旧会话 —— 这正是本插件补的那一步。

而"把会话从一个工作区迁到另一个工作区"**做不到**(在只走官方 API 的前提下):那要求改 `cwd` 本身,可它是写死在会话日志里的,官方没有任何改 `cwd` 的 API。详见 [docs/recon/05-migration-and-multi-folder.md](docs/recon/05-migration-and-multi-folder.md) 与 [docs/plan-session-migration.md](docs/plan-session-migration.md)。

## 环境要求

- DSH(DeepSeek Harness)桌面版或 CLI;开发与验证基线是 `0.2.0-rc.2`。
- Node.js 22.5+(由宿主提供)。
- **零运行时依赖**:插件不 import 标准库以外的任何东西 —— 这是刻意的:通过目录联接装载的插件按真实路径解析嵌套 import,够不到宿主自带的包。

## 安装

三种方式,推荐第一种:

1. **从 npm 安装(推荐,2026-10-08 已发布 `0.1.0`)**

   ```
   dsh plugin add dsh-plugin-workspace-archive
   ```

   插件管理器走 npm registry(官方源,回退 `registry.npmmirror.com`;两侧实测都已同步)。想钉版本就写 `dsh-plugin-workspace-archive@<version>`。
2. **从本地检出安装**(本仓库真机验证用的就是这条):把目录联接进 profile 的 `node_modules`,并把包名写进 profile 清单。本机用 `.verify/install-desktop.mjs` 一次完成这四步,`--uninstall` 可回滚。
3. **从本仓库按提交安装**:官方插件管理器也接受 GitHub 规格,例如 `dsh plugin add github:WindFromKadath/dsh-plugin-workspace-archive#<commit>`。

**必须重启宿主/应用**:插件行与 bundle 在启动时固定,HMR 从不热装载。配置默认值在 [src/index.js](src/index.js) 的 `resolveConfig`;profile 的 patch 行可以覆盖 `dryRun`、`confirmDelayMs`、`pollIntervalMs`、`watch`、`adoptUngrouped`、`adoptDelayMs`。

## 验证

- `npm test` —— **157 项**离线断言(工具行为、决策层、台账、真 Cordis 装载、"无项目归位"的选择逻辑、消失判据的分档确认、**目录 watcher 对「改名」事件的唤醒与不放大无关 churn**、**手动归档工作区的决策与工具定义契约**、**斜杠命令的注册与 handler 行为**、**`tasks.csv` 自身的结构守卫**,以及"插件源码不得 import 宿主包"的回归)。
- `npm run rm-test` —— **69 项**针对**真** DSH 运行时(一次性 `DSH_HOME`)的断言:建工作区 → 建两个真会话 → 删目录 → 断言只归档该归档的那个 → 放回目录 → 断言它被恢复并挂回工作区,而"用户手动归档"的那个**保持归档** → 删除登记再重新添加 → 断言都回到分组 → 未分组的孤会话被自动归位 → **命令阶段**:真 `dsh-commands` 里 `/archive-workspace`、`/restore-workspace` 确实注册成功且真能归档/恢复(含 3 条对照,证明"查询路径 / 定义形状 / inject 接线"各自可用)→ **改名阶段**:改文件夹名后插件被叫醒并在同一窗口内归档。
- `npm run check` —— 语法。

> 上面两套数字是**本仓库整套测试套件**的总数。仓库里另有一条**探索分支**(不随本包发布),它的测试也计入其中;见 [docs/plan-session-migration.md](docs/plan-session-migration.md)。

两套都可在本仓库一键重跑,且**不碰真实 profile、不碰真实工作区目录**。设计约束、已采纳决策与证据线索见 [MAINTAINER.md](MAINTAINER.md)。

## 已知限制

- **「删除工作区」立即归档,「目录缺失」要等几秒**,这不是不一致:目录缺失是一次 `stat` 的观测,可能抖(网络盘、改名中途),所以要等确认窗口(默认 3 秒);而「删除工作区」是官方注册表的权威变更,且**本次运行中见过这条登记**,所以立即动手。插件刚启动、注册表还没铺完时不会走这条快路(那时"不在清单里"和"被删了"分不清),会退回确认窗口。
- **归档 / 恢复只认它记过账的会话**:若"删除 → 重新添加"发生在插件没运行的时候,它只认得台账里最后一次快照 —— 快照里有的成员会被挂回,更早散掉的不会(这类可以由启动时的**归位**补上,前提是 cwd 仍等于该工作区路径)。
- **挂回分组 ≠ 取消归档**:`attach` 只动工作区成员表,从不碰归档集合。
- **归位只跑一次**(启动后延迟 5 秒):之后新出现的孤会话要等下次重启。
- **跨工作区迁移做不到**:需要在离线改写会话日志的 header,官方没有这个通道;而且即使改写成功,官方也**没有公开入口**去重建它在内存里缓存的会话 header ⇒ 必须重启 DSH 才生效。⇒ 这是一条**上游能力缺口**,本插件**不提供**该功能。完整的探索记录(机制、实测证据、为什么做不到)见 [docs/plan-session-migration.md](docs/plan-session-migration.md) —— 该目录**不随 npm 包发布**。
- 不做会话删除、不删文件夹、不做跨机器同步。
- 目录被**改名**等于换了一个路径:挂回时官方会校验 cwd,对不上时插件只记日志、不改数据。

## 更新记录

每个已发布版本的内容见 [CHANGELOG.md](CHANGELOG.md)。当前版本 `0.2.0`。

## 许可证

MIT —— 见 [LICENSE](LICENSE)。

Install

dsh plugin --profile web add github:WindFromKadath/dsh-plugin-workspace-archive

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source