Skip to content
dsh.fish
Bundle

dsh-tui-session-manager

Session manager for dsh-TUI: a /sessions scene to browse, filter and truly delete saved sessions, including the state leftovers the host delete path leaves behind.

Source
BoweiDuan
License
MIT
Updated
Updated 3 hours ago

Readme

# dsh-tui-session-manager

[dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI) · 一个为 dsh-TUI 生态打造的插件

给 dsh-TUI 的会话管理器:一条 `/sessions` 命令打开全屏面板,按工作区分组浏览、
过滤、多选,然后**真正删除**保存的会话 —— 并把宿主删除路径留下的状态残留一并清掉。

A session manager for dsh-TUI: `/sessions` opens a full-screen browser grouped by
workspace, with filtering, multi-select and **real deletion** — including the state
leftovers the host's own delete path does not touch.

## 为什么需要它 / Why

dsh-TUI 0.11.x 的适配层其实**已经实现了真正的删除**(`channel.deleteSession(id)`:
递归删除会话日志目录、清 resume 标记、`last-used.json` 与 agent-view 记录),
但**没有任何 UI 调用它** —— 命令表、`/resume` 选择器、工作区菜单里都没有入口。
缺的是入口,不是能力。本插件不重写删除逻辑,只补上入口。

两件常被问到的事,这里一次说清:

- **列表里的空会话从哪来?** DSH 的「新建会话」是**立即落盘**的:TUI 进程启动、
  切换工作区(`switchWorkspace` → `newSession`)或 `/new` 之后,会话日志就已经存在。
  若随后 `/resume` 到别处或直接退出,它就留成一个只有 4 条元数据帧、0 条消息的
  **空壳**,标题回退成目录名。按 `e` 可一键筛出它们。
- **这些空壳占空间吗?** 不占。每个约 312~466 B,宿主列表还有 `revision` 缓存。
  真正占空间的是历史调试留下的 `*.bak-*` 备份,那是另一回事。

## 功能 / Features

- `/sessions` 全屏管理器:按工作区分组、`/` 实时过滤(标题 / 工作目录 / id)。
- `e` 三态过滤:全部 → 只看空壳 → 隐藏空壳。
- 空壳行标注体积(`empty·312B`),头部显示总数、空壳数与当前视图。
- 多选批量删除(`a` 全选当前可见集),删除前 `y`/`n` 确认。
- 删除后清扫宿主不碰的四处残留,杜绝 `/resume` 里的幽灵条目。
- 无构建、无运行时依赖、纯 ESM:装进 profile 即用。

## 安装 / Install

```sh
git clone https://github.com/BoweiDuan/dsh-tui-session-manager.git
dsh plugin --profile dsh-tui add ./dsh-tui-session-manager
```

装完重启 dsh-tui(profile 的 `patchReload: live` 也可能让它热加载)。卸载:

```sh
dsh plugin --profile dsh-tui remove dsh-tui-session-manager
```

## 用法 / Usage

在对话里输入:

```
/sessions
```

全屏管理器打开,对话保持干净(命令返回静默成功)。

| 按键 | 作用 |
| --- | --- |
| `↑` `↓` / `k` `j` | 移动光标 |
| `space` / `x` / `enter` | 勾选或取消当前会话 |
| `a` | 全选 / 全不选(仅当前可见集) |
| `d` | 删除已勾选(先弹 `y` / `n` 确认) |
| `/` | 输入过滤词(匹配标题、工作目录、id) |
| `e` | 循环过滤:全部 → 只看空壳 → 隐藏空壳 |
| `esc` | 退出过滤 / 取消确认 / 关闭面板 |

清理空壳的标准动作:`/sessions` → `e` → `a` → `d` → `y`。

删除结果在底部汇总:`deleted N, refused M`。**当前正在使用的会话会被宿主拒绝**
(与宿主语义一致,不是本插件的限制)。`refused` 也覆盖日志已丢失的情况。

## 空会话判定 / Empty artifacts

只有**三个条件全部满足**才算空壳(保守:宁可漏掉一个,也不隐藏或误删有内容的会话):

```js
kind !== 'subagent'                  // 子代理天然无人类消息,排除
&& hasPrompt === false               // 宿主证明没有人类消息;日志不可读时为 true
&& bytes < 4096                      // 4 条元数据帧约 312~466 B
&& title.source === 'fallback'       // 标题仍是目录名回退
```

`bytes` 缺失、标题来源非 `fallback`、kind 无法识别 —— 一律**不判为空**。
判定只读宿主 `channel.listSessions()` 已给出的摘要字段,**不读会话日志**。

## 安全 / Safety

- 会话 id 必须匹配 `^[A-Za-z0-9][A-Za-z0-9_-]*$` 才会被当作文件名或 map 键使用,
  其它一律跳过并报告。
- 每次清扫在首次改动某个 JSON 文件前备份为 `<file>.bak-sweep-<YYYYMMDD-HHMMSS>`。
- 无法解析的 JSON 原样保留,不做猜测性修改。
- 清扫过程不抛异常:单步失败计入结果行,不影响其它步骤。
- 删除**不可撤销**(没有回收站):勾选时请看清,确认时 `y` 是唯一入口。

## 兼容 / Compatibility

- dsh-TUI 0.11.x;接缝 `ctx.tuiScenes`(全屏场景)与 `ctx.commands`(斜杠命令)。
- 两者都做软探测:宿主不提供时插件保持 idle,不会让启动失败。
- 场景只使用宿主注入的 `props.React` 与 `props.ui`,不引入自带 React
  (TUI 的 reconciler 只认宿主自己的 React 19 元素)。
- Node `^22.19 || >=24`。

## 开发 / Development

```sh
npm test        # node --test,无依赖、无构建
```

测试覆盖:命令/场景注册与注销、无声成功返回、缺接缝时保持 idle、
空壳判定的四个条件与边界、`e` 三态过滤、`a` 只作用于可见集、
id 白名单与 `dryRun`、以及在临时 `$DSH_HOME` / `$DSH_TUI_HOME` 上真实落盘的清扫。

## 结构 / Layout

```text
index.js            插件契约(name / apply):注册 /sessions 命令与全屏场景
scene.js            全屏管理器界面 + 空壳判定(使用宿主注入的 React 与 ui kit)
sweep.js            删除后的残留清扫(支持 dryRun)
debug.js            诊断日志($DSH_TUI_HOME/session-manager.log,64 KiB 上限)
cordis.patch.yml    向 profile 插入插件行
test/               契约测试、判定测试与清扫落盘测试
docs/design/        设计说明
```

## 许可 / License

[MIT](LICENSE) © 2026 BoweiDuan

Install

dsh plugin --profile web add github:BoweiDuan/dsh-tui-session-manager

Profile: web

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