steve-magne/dsh-plugins--packages-worktree-launcher ↗★ 0
@dsh-plugins/worktree-launcher
DSH web plugin: one isolated git worktree (branch dsh-word-word-word, based on an up-to-date main) materialized for every new chat session
AI Analysis
核心用途是防止多会话并行开发时文件冲突。适合同时进行多个独立代码修改任务、需要严格环境隔离的开发者。
Install
This plugin has no verified bundle, or compatibility checks failed. Read the repository notes first. Read the full README ↗
README
Read the full README ↗@dsh-plugins/worktree-launcher
A DeepSeek Harness plugin (inspired by Codex, the Claude Code app and worktree-mgr): every new chat session gets its own isolated git worktree, so parallel sessions never step on each other's files.
composer tool row (inside the card) under the card
┌─────────────────────────────────────────────┐ ┌──────────────────────────────┐
│ [access] [plan] (🟢 Worktree) [model] [↑] │ │ ● dsh-azure-cinder-calm ⧉ │
└─────────────────────────────────────────────┘ └──────────────────────────────┘
toggle — ON by default branch + copyable path
Behavior
- The browser half adds a Worktree toggle to the chat window (
conversation.input.left), active by default, persisted inlocalStorageand synced to a host-side preference. - When a brand-new session publishes (
agent/session-start, sourcestartuponly), it is marked eligible. Resumed, forked, compacted sessions and subagents never are. - On that session's first real message (
agent/inbox/inserted, turn 1) the host materializes the worktree:- location
/.dsh/worktrees/dsh-word-word-word; - branch
dsh-+ three random words joined by-, minted collision-free; - base = up-to-date main:
git fetch origin main, then — "git pull au besoin" — a realgit pull --ff-onlywhen main is checked out (the usual case: your workspace), else a non-forced refspec fast-forward. Divergence, conflicts and offline all fail harmlessly: nothing is ever rebased or overwritten, and the worktree still bases on the last known remote tip; .dsh/worktrees/is added to.git/info/exclude(local-only) sogit statusstays clean.
- location
- A scoped system-prompt section tells the model to run everything inside the worktree for that session; a chip under the composer shows branch and path.
Removing the plugin (or flipping the toggle off) restores the stock composer exactly; existing worktrees are left on disk for you to keep or prune via the API.
How it fits DSH
Dual-face cordis plugin, the same shape as @dsh-plugins/command-deck:
| Half | File | Role |
|---|---|---|
| Host | lib/index.js | Registers a prefix route /worktree-launcher/api on ctx.webServer; runs git through ctx.subprocess; listens to agent/session-start / agent/inbox/inserted; contributes a scoped systemPrompt.section |
| Browser | lib/client.js | Lazy-CJS factory bundle; injects into the runtime-owned conversation.input.left and conversation.composer.dock slots; zero deps beyond shell-seeded React |
The browser discovers the client bundle automatically: the node half of
dsh-client-modules scans Loader entries for packages declaring
dsh.client: { platform: "web" }, hashes exports["./client"] into
window.__DSH_BOOT__, and serves it at
/plugins/@dsh-plugins/worktree-launcher/client.js.
Install
# 1. Link the package into the web-surface profile's plugin tree
mkdir -p ~/.dsh/profiles/web/node_modules/@dsh-plugins
ln -s /Users/stevemagne/workspace/dsh-plugins/packages/worktree-launcher \
~/.dsh/profiles/web/node_modules/@dsh-plugins/worktree-launcher
# 2. Mount it as a Loader entry (~/.dsh/profiles/web/cordis.patch.yml)
# - insert:
# - id: worktree-launcher
# name: '@dsh-plugins/worktree-launcher'
# 3. Restart the harness (quit dsh and relaunch) — the web surface has no
# config hot-reload; new entries mount at boot.
Config (row config)
| Key | Default | Meaning |
|---|---|---|
cwd | harness process cwd | Default repo for manual creates without an explicit root (auto mode always uses the session cwd) |
enabled | true | Initial value of the auto-worktree preference (the toggle overrides at runtime) |
baseBranch | auto (main → master → origin/HEAD) | Force the base branch instead of detecting it |
fetchTimeoutMs | 20000 | Budget for git fetch / git pull --ff-only before degrading gracefully |
debug | false | Log best-effort failures (auto-create errors, exclude-file issues) with console.warn |
HTTP API (loopback only)
All endpoints answer JSON under the prefix route:
GET /worktree-launcher/api/pref→{enabled}PUT /worktree-launcher/api/pref{enabled:boolean}→ saved preferencePOST /worktree-launcher/api/worktrees{root?, sessionId?}→201record ({branch, path, root, baseBranch, baseSha, mainUpdated, note, sessionId, createdAt}),200withcreated:falsewhen the session is already boundGET /worktree-launcher/api/worktrees→ registry snapshot, newest firstGET /worktree-launcher/api/worktrees/:branch→ one recordDELETE /worktree-launcher/api/worktrees/:branch[?force=1]→ remove a worktree (git worktree remove; dirty trees need?force=1). The minteddsh-*branch is deliberately kept on removal — it may carry commits you still want; drop it yourself withgit branch -Dwhen done.GET /worktree-launcher/api/by-session/:sessionId→ record bound to a session
Trust posture matches the harness web server itself: loopback bind, no auth,
plus a Host allowlist (localhost/127.0.0.1/[::1]) against DNS rebinding
and a capped request body. Git commands execute with the full privileges of
the harness process — this surface can update and create branches in any
repository the process can reach.
Tests
node test/run.mjs # host half over REAL temp git repos (local bare origin)
node test/client-check.mjs # client-bundle contract (factory form, slot entries)