dsh-session-cleaner-cli π³π§Ή
August 14, 2026 Β· View on GitHub
An offline CLI that deep-cleans DeepSeek Harness (DSH) workspace sessions: list by workspace, delete with a trash bin, restore, prune ghost entries β keeping the workspace registry and projection cache in sync. Cross-platform (Windows / macOS / Linux), zero dependencies.
Why
DSH's session persistence is append-only by design β the GUI only offers archive (hide; data stays on disk) and exposes no delete at all (the SessionPersistence service has create/append/load/inspect/list, but no delete). Test conversations and abandoned chats pile up forever.
This tool deletes sessions at both layers β the workspace registry (storages/workspace.json) and the log directories (sessions/<scope>/<id>/) β so you can freely delete any conversation in any workspace.
Quick start
# 1. Stop the GUI first (the tool refuses to delete while the server is running)
# Windows: run stop-dsh.cmd in the deepseek-harness repo root
# macOS / Linux: press Ctrl+C in the terminal running `dsh web`, or kill the process
# 2. Interactive: pick a workspace β select rows (1,3 / 2-5 / all) β type DELETE to confirm
node dsh-session-cleaner.mjs
# 3. Restart the GUI (the sidebar reflects the new state after restart)
CLI mode:
node dsh-session-cleaner.mjs list # read-only listing of workspaces and sessions
node dsh-session-cleaner.mjs delete <id> [id...] # delete specific sessions (--yes skips the prompt)
node dsh-session-cleaner.mjs restore <batch-ts> # restore a previous deletion
node dsh-session-cleaner.mjs trash list # inspect the trash bin
node dsh-session-cleaner.mjs prune-ghosts # strip registry ids whose data dirs are gone
Install / Uninstall
Zero dependencies; Node.js β₯ 18:
# Option 1: run directly from GitHub via npx (no clone needed)
npx github:ChenChen913/dsh-session-cleaner-cli list
# Option 2: clone
git clone https://github.com/ChenChen913/dsh-session-cleaner-cli.git
cd dsh-session-cleaner-cli
node dsh-session-cleaner.mjs
Uninstall = delete the repository/script; if you no longer need recoverable data, also clear ~/.dsh/trash and ~/.dsh/storages/backups.
Commands
| Command | What it does |
|---|---|
| (no args) | Interactive: pick a workspace β select sessions to delete |
list | List all workspaces and sessions (read-only; safe while the server runs) |
list -w <title-or-path> | Show one workspace only |
delete <id> [id...] | Delete sessions (moves to trash by default; still requires typing DELETE) |
restore <batch-ts> | Restore a deletion batch (see trash list) |
trash list / trash empty | Inspect / empty the trash bin |
prune-ghosts | Strip registry ids whose session dirs are gone |
Global flags: --home <dir> (defaults to $DSH_HOME or ~/.dsh), --pid-file <path> (extra dsh.pid location for the liveness check), --dry-run, --purge (wipe immediately, no trash), --yes, --force (skip the running-server guard).
How it works
DSH's default JSONL backend on disk:
~/.dsh/
sessions/<encoded-workspace-path>/<session-id>/session.jsonl.zstd β session log
storages/workspace.json β workspace registry + archive set
storages/session_projcache.json β title/stats projection cache
Deleting a session is a four-step transaction:
- Backup: both registry files are copied to
storages/backups/<timestamp>/ - Move to trash: log dir β
.dsh/trash/<timestamp>/<id>/(--purgedeletes directly) - Sync registry: remove the id from
sessionIdsandarchivedSessionIdsinworkspace.json, stampupdatedAt - Clean cache: remove the
session_projcache.jsonentry (rebuilt by the harness when the session is next opened)
restore is the exact inverse: move the dir back, re-attach the id to its workspace, and restore the archived flag.
One key safety fact: the DSH workspace entity's sessionIds getter filters by the live header index (packages/workspace/workspace/src/entity.ts), so even if a deleted id lingers in the registry, it is invisible after restart and pruned on the next registry write β the tool and the harness converge by design.
Safety design
- Running-server guard: delete/restore/prune refuse to run while
127.0.0.1:3080is listening or adsh.pidprocess is alive. The process check uses a cross-platform signal-0 probe β no platform commands - Home validation: mutating commands verify that
--homeactually looks like a DSH_HOME (sessions/orstorages/present), so a mistyped path errors out instead of creating junk directories - Mutex lock: mutating commands hold
.dsh-session-cleaner.lock(records the pid) so two instances can never overwrite each other's registry writes; stale locks from dead holders are taken over automatically - Trash by default: delete = move, not wipe;
restorebrings data back, andtrash emptyclears it for good - Automatic backups: both registry JSONs are backed up before every write
- Confirmation prompt: interactive and CLI flows both require typing
DELETE(opt out with--yes) - Ghost self-healing: registry ids pointing at missing data dirs are shown as "ghosts";
prune-ghostsstrips them in one pass
Cross-platform support
| Platform | Default DSH_HOME | How to stop the GUI | Status |
|---|---|---|---|
| Windows | C:\Users\<you>\.dsh | stop-dsh.cmd (or end the process in Task Manager) | Development environment; CI-covered |
| macOS | ~/.dsh | Ctrl+C in the terminal running dsh web | CI-covered |
| Linux | ~/.dsh | Ctrl+C in the terminal running dsh web | CI-covered |
- The tool has zero platform dependencies: pure Node.js standard library, no native modules, no external commands
- Liveness detection = port probe + signal-0 process probe (replaced the earlier Windows-only
tasklistapproach) - Session-dir scanning does not depend on how workspace paths are encoded (works with both the Windows `--C-Users-...--$ \text{encoding} \text{and} \text{Unix} \text{encodings})
- \text{CI} (\text{GitHub} \text{Actions}) \text{matrix}: \text{ubuntu} / \text{macos} / \text{windows} \times \text{Node} 18 / 20 / 24
\text{Configuration}
| \text{Item} | \text{Description} |
|---|---|
$DSH_HOMEenv var /--home` | harness data directory; defaults to ~/.dsh |
--pid-file <path> | extra dsh.pid location for the liveness check (non-standard installs) |
--purge | delete without trash |
--dry-run | preview only |
--force | skip the running-server guard (only when the GUI is confirmed stopped) |
Permissions & data
- Reads/writes DSH_HOME only (
~/.dsh): never touches workspace project files or harness code - No network requests: the only network operation is a localhost port-3080 liveness probe
- No credentials:
settings.yaml,.anonymous-user-id, etc. are never read or written - Attachments untouched: images live in content-addressed shared storage (
attachments/v1) and do not belong to a single session
Compatibility
- Verified against deepseek-harness mainline
47f943859bef60e4160492346772ded9b24f765a(2026-08-13); storage formatsworkspace.jsonunit v2 andsession_projcache.jsonunit v3 (the tool keeps unit blocks untouched and only edits fields) - Developed and tested on Windows 11 + Node 24; CI covers ubuntu / macos / windows Γ Node 18 / 20 / 24
- Handles BOM-prefixed registry JSON; needs no compression tools (titles come from the projection cache, not from parsing zstd logs)
Troubleshooting
- Garbled Chinese output: run in Windows Terminal, or run
chcp 65001first in legacy cmd - "server is running" refusal: stop the GUI first (Windows: stop-dsh.cmd; macOS/Linux: stop
dsh web); use--forceonly when no server is up - "does not look like a DSH_HOME":
--homepoints at the wrong directory β check the path or theDSH_HOMEenv var - "another dsh-session-cleaner process": a second cleaner instance is running; after confirming none is, remove
~/.dsh/.dsh-session-cleaner.lock - Titles missing after restore: expected β the projection-cache entry is removed at delete time and rebuilt by the harness when the session is next opened
- Deleted a parent session: subagent sessions are not in workspace accounting; they appear under "Ungrouped" β clean those up too
Development
npm test # 13 end-to-end tests: list/delete/restore/purge/prune/interactive flow/running-server refusal/unknown id/lock/pid-file/home validation/incomplete batches
Tests drive the real CLI as a subprocess against scratch DSH_HOME fixtures and assert both filesystem and registry outcomes; CI lives in .github/workflows/test.yml.
Ecosystem
- Listed on the GitHub
dsh-plugintopic - Tracked by the awesome-dsh-plugins radar (topic auto-discovery, 8h scans)
- Curated list: awesome-deepseek-harness
- Related project: fountunt/dsh-session-cleaner β a plugin that deletes sessions inside the running web runtime (GUI delete button +
/api-ext/session.delete). Complementary to this tool (offline CLI + trash/restore/ghost pruning): use the former for one-click in-GUI deletion, this one for bulk cleanup with recovery and ghost-accounting repair
License & security
MIT Β© 2026 ChenChen913. This tool handles no credentials; report security concerns via GitHub Issues.