Shows the repository, branch, working-tree change count and short commit SHA on the composer row, with a searchable branch switcher and a per-file diff pane in the right sidebar.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:mjnhmd/dsh-git-bar
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
A DeepSeek Harness bundle that adds one ambient row above the composer showing the Session's git context:
⎇ main · dsh-git-bar · 3 处改动 · ↑2 ↓1 · a1b2c3d

- repository name and root
- the branch the Session runs on, or
游离 HEAD/detached HEADwhen the HEAD is not on a branch - working-tree change totals (staged / unstaged / untracked in the tooltip)
- ahead/behind counts against the upstream branch
- short commit id — clickable, opening that commit on the origin remote
(GitHub, GitLab and Bitbucket commit routes are all handled) — and a
链接工作树/linked worktreemarker when the directory is a linkedgit worktree, or aN 个工作树marker when the repository has more than one - every label is plain text: select it and copy it
The row refreshes every 8 seconds, on window focus, and when the Session changes. Reading it never touches the repository; two interactions deliberately do:
- click the branch — a searchable popover lists every local branch with its upstream, ahead/behind and dirty state, marks the current one, and can check one out or create a new branch from the current HEAD;
- click the change count — opens the working tree in the right-hand Sidebar
as a
工作区改动tab: the changed files, and the unified diff of the file you pick, untracked files included (diffed against/dev/null); - click the worktree marker — lists every worktree of the repository with its
branch and change count.
在此新建会话opens a new Session in that worktree's directory (a worktree is a directory, so a Session is never moved between them), and复制路径copies it. A branch already checked out in another worktree is greyed out in the branch popover with the worktree that holds it, instead of failing with git'salready checked outafter the click. - nothing here silently scrolls away: a drag that selects text does not also open a popover.


Install
From a Git checkout (pin the commit; add &path:/plugins/dsh-git-bar when the
package is not at the repository root):
dsh plugin --profile desktop add "git+https://github.com/mjnhmd/dsh-git-bar.git#2d3e0408376127196a4c18e8b1e1c79389a166f5"
From npm, or from a local checkout during development:
dsh plugin --profile desktop add dsh-git-bar@0.1.0
dsh plugin --profile desktop add link:/absolute/path/to/plugins/dsh-git-bar
The Host half loads at startup, so restart the app after the first install; the Client half is served on the next page load.
Publish
Market index (add under 插件 → 发现 → 添加索引源): https://mjnhmd.github.io/dsh-git-bar/market/index.json
DSH ships no index of its own: a plugin market is a JSON file (format 1) you host
yourself and add under 插件 → 发现 → 添加索引源. docs/market-index.example.json
is a ready template, scripts/validate-index.mjs checks it against DSH's own
validator, and docs/PUBLISH.zh.md walks through pushing the
repository, publishing to npm, and publishing the index.
Host API
GET /dsh-git-bar/info?sessionId=<id>[&dir=<absolute path>]
- admitted through the harness browser session (
ctx.connection.requestRejection), so it is unreachable without the GUI's own credentials; - the directory comes from the Session store first;
diris only a fallback for a Session the store does not know, and must be absolute; - replies with
{ dir, repository: null | { root, name, branch, detached, sha, commitUrl, remote, dirty: { changed, staged, unstaged, untracked }, upstream, ahead, behind, worktree: { linked, mainRoot, count } } }; commitUrlis derived from theoriginremote: the scp,ssh://,git://and http(s) forms all normalize tohttps://host/owner/repo, and the commit route follows the host (GitHub/commit/, GitLab/-/commit/, Bitbucket/commits/). A local-path remote yieldsnulland the id stays plain text.
GET /dsh-git-bar/branches?sessionId=<id> — { branches: [{ name, current, upstream, ahead, behind, subject, checkedOutIn }], dirty }, where
checkedOutIn is the worktree path holding that branch (a branch can only be
checked out in one worktree at a time).
GET /dsh-git-bar/worktrees?sessionId=<id> — { root, worktrees: [{ path, name, branch, detached, sha, bare, locked, prunable, main, current, dirty }] }, each
worktree costing one status call for its own change count.
POST /dsh-git-bar/branches/checkout { sessionId, branch } — { branch, created: false }; 409 when git refuses (local changes, unknown branch).
POST /dsh-git-bar/branches/create { sessionId, name, from? } — { branch, created: true }; the name is validated with git check-ref-format --branch.
GET /dsh-git-bar/changes?sessionId=<id> — { files: [{ path, status, staged, additions, deletions }], dirty, truncated }, capped at 500 files.
GET /dsh-git-bar/diff?sessionId=<id>&path=<repo-relative>&staged=0|1 — { path, staged, diff, truncated }, capped at 200 000 characters.
Every git call uses an argument array (no shell), a 4 s timeout, and a 256 KiB buffer cap. Nothing is watched and nothing is cached.
Layout
| File | Role |
|---|---|
index.js |
Host half: the read-only JSON route over the git executable |
client.js |
Client half: the conversation.input.dock row, the branch popover, and the Sidebar tab body |
cordis.patch.yml |
Bundle patch that inserts the git-bar row |
locale/*.json |
Display metadata for Plugin Manager / Settings |
icon.svg |
Plugin icon |
docs/PUBLISH.zh.md |
How to distribute the plugin (Git / npm / a market index) |
docs/market-index.example.json |
Format-1 market index template |
scripts/validate-index.mjs |
Rejects an index the way DSH's reader would |
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-git-graph★ 9321
Git branch selector and Git graph for the dsh web GUI: switch branches and explore branch-lane and commit history from the conversation header.
Akimiya-z/codex-guard#dsh★ 133
Pre-submit pull-request hygiene checks inside DeepSeek Harness: scans the current diff for TODO leftovers, hardcoded secrets, and non-conventional commit subjects.
Cerbur/clutch-dsh#clutch-dsh-worktree★ 34
Adds a Worktree view to DSH Web UI that groups Sessions by Git worktree while keeping DSH as the source of truth.
lehhair/dsh-diff-viewer★ 26
PiUI-style diff viewer replacing the stock DiffBlock for write/edit tool calls.
DamonKoy/dsh-web-ui#dsh-git-graph★ 25
Git branch selector and Git graph in the conversation header of the dsh web GUI.
PerryLink/dsh-github★ 24
Official-grade GitHub CI integration: a composite action.yml, a polling PR review bot with idempotent inline comments and a status-check gate, plus PR/issues tools with every write gated by human approval.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.