@young1lin/dsh-ui-gitworkbench

September 4, 2026 Β· View on GitHub

🌏 δΈ­ζ–‡ Β· English

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 / total visible: 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 into git log arguments 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.

Demo (2:24) β€” status card β†’ Changes (ticking to stage, per-file diffs, committing) β†’ Appearance (light/dark, seven palettes, background image) β†’ History (commit graph, worktree switching)

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.actions slot, 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.