dsh-session-workspace-organizer
A DeepSeek Harness (DSH) plugin: move a workspace folder to a new location, and give every new session its own output folder.
English | 简体中文
DSH binds each session to a workspace directory, and every session of that workspace shares it — so scripts, logs, downloads and screenshots from unrelated conversations pile up in the same place. This plugin adds three things on top of that model, without patching DSH itself:
- Relocate a workspace — move the folder to another path or disk and take its sessions with it. Every session log's header
cwdis rewritten to the new path, so sessions keep working and stay listed under the new workspace. - One output folder per session — each new or resumed session gets
<workspace>/sessions/<YYYY-MM-DD>_<title>/, the model is told (through a session-scoped system prompt) to write its artifacts there, and awritethat would create a file at the workspace root asks for confirmation first. - Session management — move idle sessions to another workspace, or delete them (with a dry-run preview), optionally together with the files they created.
Tested with DSH Desktop 0.2.1-rc.2 on Windows. Host code uses Node built-ins only; the client is a hand-written CommonJS bundle, so there is no build step.
Install
# from npm (prebuilt, no build approval needed)
dsh plugin --profile web add dsh-session-workspace-organizer
# from GitHub source
dsh plugin --profile web add github:dream-fish69/dsh-session-workspace-organizer
# local development checkout
dsh plugin --profile web add link:/absolute/path/to/the/checkout
On the Desktop app, use the Plugins page: remove the old entry, then install dsh-session-workspace-organizer or github:dream-fish69/dsh-session-workspace-organizer from the install field. Restart DSH after installing or upgrading — the Host half is only loaded at startup.
Uninstall:
dsh plugin --profile web remove dsh-session-workspace-organizer
Removing the plugin leaves the sessions/ folders and their files alone.
Where the UI lives
Settings → Workspaces (the section title follows the DSH language: 工作区 in Chinese). Three things live there:
Move workspace
Pick a registered workspace, choose a target parent folder and type the new folder name.
The plugin then performs a strict relocation:
- the whole directory —
sessions/included — is moved to the new path, and the old path is deleted; no junction or symlink is left behind; - each session log whose header
cwdequals the old path is rewritten: the first zstd frame carries the header, only itscwdis replaced, and every later frame is kept byte-identical; the log directory is then moved from--<old cwd>--to--<new cwd>--; - the workspace is re-registered from the new path and sessions are attached to it; sessions this running process cannot attach yet are recorded in
pending-attach.jsonand attached automatically after a DSH restart; - a cross-disk move copies first and verifies file by file, keeps the source until the copy is verified, and rolls back on failure;
- if the old directory is locked (Windows
EBUSY/EPERM/EACCES— e.g. DSH still holds a handle), the plugin falls back to a copy migration: copy and verify, rewrite the copied logs, switch the registry, then delete the old directory if it can and report the exact reason when it cannot.
Hard requirement: the workspace being moved must have no running session, because session logs are append-only and a live write handle cannot be rewritten safely. Open a conversation in another workspace and trigger the move from there. If sessions are still running, a dialog lists them (title + id) and offers to cancel their turns, wait for them to settle, and continue with the copy path — unless the session you are typing in is itself in the target workspace, which always has to be moved from elsewhere.
If the old path was DSH's launch directory, the plugin also switches the process working directory and the sandbox fallback root, but desktop shortcuts or launcher configuration still have to be updated by hand.
Organize structure
Creates sessions/ and sessions/README.md when missing. With move loose files ticked, ordinary files sitting at the workspace root are moved into sessions/<date>_整理/; dotfiles, README.md, AGENTS.md and common project manifests are left where they are.
Sessions
- Move to workspace… — moves idle sessions (single or batch) into another workspace: their logs, their
sessions/folder with everything inside it, and files the session wrote throughwrite/editelsewhere inside the source workspace. Name conflicts at the destination abort that session instead of overwriting. - Delete — permanent deletion of idle sessions, with a preview first that lists the log, the session folder and any external files that would go away. Files written by that session outside
sessions/are only removed when you tick that option, and only inside the session's source workspace.
Per-session output folders
For every new or resumed session whose cwd is inside a registered workspace:
<workspace>/sessions/<YYYY-MM-DD>_<title>/is created, and renamed when the session title changes (the date prefix stays; duplicates get-2);- a
.dsh-sessionmarker inside the folder recordssessionId,workspacePathandcreatedAt, so a resumed session finds its folder again — do not delete it; - the model receives a session-scoped system prompt saying that new scripts, logs, downloads, screenshots, temporary data and one-off artifacts belong in that folder, while existing project code stays where it is;
- creating a file at the workspace root with
writetriggers a confirmation dialog; existing files and common manifests (README.md,AGENTS.md,package.json, lockfiles,Makefile,Dockerfile, …) pass through untouched.
Configuration
Override the plugin row in the profile's cordis.patch.yml:
- id: workspace-organizer
config:
sessionFolders: true # create the per-session output folder
guardRootWrites: true # confirm before creating files at the workspace root
sessionsDirName: sessions # parent folder name for session folders
maxTitleChars: 40 # max title characters in the folder name (8-80)
# sessionsRoot: /path/to/dsh/home/sessions # only if DSH stores logs elsewhere
Safety notes
- Strict relocation deletes the old directory. It only runs after the blocker precheck, it writes the new location and the rewrites before deleting anything, and it rolls back when a step fails — but there is no "keep both folders" mode by design. Use a copy migration (the automatic fallback) if you want the old directory kept.
- Rewriting session logs is an offline operation. Restart DSH after moving, so the session list, search index and persistence caches re-read the new path.
- A workspace containing symlinks or junctions is refused for the copy fallback (to avoid duplicating cycles or losing links); same-disk rename is unaffected.
- Sessions that were already open before the plugin was installed get their folder on the next resume, not retroactively.
- All destructive commands require a confirmation token minted by the check/preview step; the client never sends a bare "delete".
Compatibility and limitations
- DSH
0.2.x(Web and Desktop profiles that includedsh-base+dsh-web-app). - Busy-handle handling keys off Windows error codes; on other platforms the copy fallback is simply the normal path.
- Movement and deletion run as commands recorded in an open conversation, so a session must be open; that session must not belong to the workspace being moved.
- The sidebar's workspace "…" menu has no extension slot in DSH, which is why the entry point lives in Settings.
How it works
| Layer | File | Responsibility |
|---|---|---|
| Host | index.js |
Registers the workspace-relocate-check / workspace-relocate-strict / workspace-organize / session-relocate / session-relocate-batch / session-delete-preview / session-delete / session-delete-batch commands; agent/created builds the session folder and the scoped prompt; session/event renames it on title change; tools/pre-execute guards root writes |
| Host | lib/fs-ops.js |
Directory move (same-disk rename, cross-disk copy + verify + rollback), name sanitizing, loose-file scan |
| Host | lib/session-logs.js |
zstd first-frame header parse, cwd-only rewrite, later frames untouched, log-directory re-homing |
| Client | client.js |
settings.section page with the move / organize / sessions dialogs, bilingual copy from locale/*.json |
Development
npm run check # node --check on index.js, client.js and both lib modules
No bundler: lib/ is the committed source, client.js is the bundle DSH loads through window.__ModuleLoader__. Host changes need a DSH restart; client changes need a page refresh.
License
MIT
No comments yet. Be the first to write one.