@young1lin/dsh-ui-gitworkbench
September 4, 2026 Β· View on GitHub
An out-of-tree Web UI plugin for dsh (DeepSeek Harness) that adds a Git workbench to the dsh web interface β without touching dsh itself.
Every session header gets a small status card showing the current branch, ahead/behind counts, and added/deleted lines. Click it and a workbench panel slides in from the right with everything about the session worktree's changes:
- Changes β a collapsible file tree beside full-context, side-by-side per-file diffs with dual line numbers, word-level highlights, Shiki syntax coloring, and an editable right column. A magnifier filters the tree by space-separated keywords (smart case); hovering a file reveals IDEA-style Rollback behind a consequence-specific dialog. The diff header keeps the current hunk and
current / totalvisible: Unstaged offers Stage / Revert even in Edit mode, while Staged offers Unstage for the current hunk or the whole file. - Files β a searchable repository tree with editable CodeMirror file views, image previews, and per-line blame information. Ctrl/Cmd+F opens a find panel dressed in the drawer's own controls that reports
3/128β which match you are on, out of how many. - History β commits, file tree, and diff in three panes; scrolling to the bottom loads the next page automatically. Rows carry the author, the hover card the exact time; an IDEA-style filter (a
user:/path:/after:query grammar plus an authors / date / paths funnel) compiles intogit logarguments and matches over all history, with an All-branches option. - Compare β diff any two branches against each other.
- Commit & sync β ticking a file in the tree is a real
git add/git restore --staged; with the commit box and the fetch / pull / push bar, a full commit-and-push never leaves the panel. - Appearance β seven theme families in light and dark (following the OS by default), plus a blurred background image and custom CSS, stored per project and globally with the project scope winning.
It also ships worktree emulation: the model can call the worktree_enter / worktree_exit / worktree_status agent tools to create or leave an isolated worktree under .agents/worktrees/<name> and bind the session to it. A subagent session never writes a binding of its own β it borrows the nearest bound ancestor's worktree along its lineage: the standing prompt, the chip, and worktree_status all resolve that effective binding for a session without one, and the borrow disappears when the parent exits. The status card lights up its binding marker, the panel header gains a worktree switcher listing every worktree in the repository, and the stats follow the binding.
The Chinese README.md is the project's deep handoff document β including the pitfall catalog (Β§6) that every contributor should read before changing the build, the RPC layer, or the Windows-specific handling.
Install
Prerequisites: dsh installed and dsh web running; Node.js β₯ 20, pnpm β₯ 10.
Recommended β the official plugin channel, one command:
dsh plugin --profile web add @young1lin/dsh-ui-gitworkbench
Then restart dsh and hard-refresh the browser (Ctrl/Cmd + Shift + R). The package declares its dsh.bundle.patch, so the CLI registers the host half into the profile's dsh.profile.bundles and mounts it on the next start β you never hand-write a cordis.patch.yml entry. Without a dsh command on PATH, run it through npx:
npx -y --package @deepseek-ai/dsh dsh plugin --profile web add @young1lin/dsh-ui-gitworkbench
Alternative: one-line installer scripts (same official channel, two extra conveniences)
# macOS / Linux (or Windows with Git Bash / WSL)
curl -fsSL https://raw.githubusercontent.com/young1lin/dsh-ui-gitworkbench/main/scripts/install.sh | bash
# Windows (PowerShell 5.1+ / pwsh)
irm https://raw.githubusercontent.com/young1lin/dsh-ui-gitworkbench/main/scripts/install.ps1 | iex
Beyond the install command, the scripts pre-write pnpm 11's minimumReleaseAgeExclude so a release younger than 24 hours installs immediately, and idempotently remove legacy manual mount lines (a double mount shows two status cards on the page). They also take options β pinning a version, pm2 restart dsh-web afterwards, --dry-run β documented at the top of each script.
Developing from source
dsh plugin --profile web add <repo path> installs the checkout into the profile. After editing the client half, run npx tsdown and refresh the browser; host-half changes need a dsh restart. When switching a link: dependency back to the npm version, remove the manual mount line from cordis.patch.yml (the installer scripts do this automatically).
For maintainers
pnpm install # .npmrc keeps auto-install-peers off; the dsh peers come from the web profile at runtime
pnpm typecheck # both tsconfigs β the client half is otherwise never checked
pnpm test # vitest, full suite
pnpm bundle # tsc (host half: lib/index.js) + tsdown (client half: lib/client.js)
The plugin has two halves with different feedback loops: the host half (src/index.ts and friends) is a TypertRemoteService exposing the RPCs behind every view, and must be rebuilt and followed by a dsh restart; the client half (src/client/**) is a closure-factory bundle that the web server re-reads from disk on every request, so a rebuild plus a browser refresh is enough. Pure logic lives in React-free modules so vitest can load it directly.
Releases are tag-driven: pushing a vX.Y.Z tag runs the CI checks and publishes to npm through OIDC trusted publishing β no npm token anywhere. The full runbook (first manual publish, trusted-publisher setup, why the tarball carries no sourcemap) lives in the Chinese README's maintainer section.
Known limits
- The status card mounts in the
conversation.session.header.actionsslot, so it appears only once a session is actually open. In headless automation without a real session the card will not show β open a session in the UI to see it. - Person search in the history filter matches the author only (IDEA parity β git has no author-or-committer pushdown). The committer stays visible in the hover card.