dsh-git-graph-sidebar
English | 中文
A Git plugin for DeepSeek Harness (DSH): a branch chip in the composer, a Repository graph tab inside dsh-better-sidebar, and — opt-in — the same graph as a page in the conversation's main area. It lists the Git repositories found up to three levels below the workspace, draws the selected repository's commit graph as an SVG lane canvas, and turns that repository's commit conventions into something both you and the model actually follow.
Repository graph tab, dark theme
Why this one
Plenty of plugins draw a commit graph. This one is built around the part that decides whether a commit message comes out right, and the three guarantees below are not on offer elsewhere:
- The rules reach the model, not just the panel. The convention document is registered as a
systemPrompt section, so every model step is told what this repository wants before it writes anything, instead of learning it from a rejection. strict: true goes further: the block tells the assistant to rewrite an offending message before committing.
- The rules are read back, prose included. Machine-checkable rules (type prefixes, subject length, case, trailing period, mood, subject language) are parsed out of the document — and out of an
AGENTS.md, CLAUDE.md, .cursorrules or CONTRIBUTING.md sitting beside it — so a repository that never wrote a GIT_CONVENTIONS.md is still understood. A backward-compatible ai: required | optional | forbidden field then checks the trailers in commit bodies, which is where attribution actually lives.
- Two documents that disagree never get silently outvoted. Dropping a plugin into a running project is the usual way a second convention document appears. The tab compares the workspace document, the repository document and a parent repository's document field by field and rule by rule, the injected block asks the agent to check with you before picking a side, and the agent merges them into
<workspace>/.dsh/GIT_CONVENTIONS.md with timestamped backups and pointer files the plugin then follows.
- Three entry points, one body. The same tab renders in the better-sidebar tab, and — behind a switch, closable at any time — as a page beside 对话 / 轨迹 in the conversation area, so the graph can be widened without leaving the sidebar as the primary entry.
Everything runs locally against the loopback endpoint the host already provides: no network calls, no telemetry, and every Git command is a read except the switches you press yourself.
What it does
Repository graph tab (better-sidebar)
The tab works from the session's working directory and treats that directory as a container: Git repositories up to three levels below it are reachable without registering each repository as a separate DSH workspace.
-
Multi-repository discovery — the workspace root itself, plus repositories nested up to 3 levels below it. Each entry carries its current branch, short head, dirty-file count and conflict count.
-
A scan that costs what it should — one git for-each-ref per repository returns the branch time, the upstream and the ahead/behind distance in one call; while the refs have not moved the whole answer comes from the last scan, and only the status probe (dirty files, conflicts) is repeated. A workspace full of repositories no longer gets slower with every extra one.
-
Repository tree — the root, the intermediate directories and each repository, so the level relationship between them is visible rather than implied by a path.
-
Linked checkouts, read-only — a repository row expands to its linked worktrees (git worktree list), each with its branch and HEAD. Picking one switches the status strip to that checkout's branch, HEAD and dirty state and marks its HEAD row on the graph — one graph, not one per checkout: linked worktrees share refs and objects, so the commit list is the same everywhere. From there the checkout opens in a new session, opens as a workspace, or its path is copied; a checkout that is not registered as a workspace says so instead of failing silently, and a directory that was removed still shows in the list. The tab never creates, removes or switches a worktree — that stays in the chip's dialog.
-
SVG commit graph — one row per commit, one colored lane per branch line, curved edges where a commit's parent sits on another lane, merge commits drawn as diamonds, ref badges and relative timestamps; orphan roots and tips carry a short dash through the node centre, so unrelated histories read as deliberately separate rather than as broken edges.
-
One ref chip per branch — main and origin/main name the same branch, so they share a chip and mute together; symbolic refs such as origin/HEAD never become chips; tags are not filters either (a tag points at a single commit, so muting it would hide one row) and stay on the commit rows as badges; the order is the current branch, other local branches, then remote-only branches, anything past twelve folds into +N, and a Clear filters chip appears while something is muted. Fewer than four filterable branches and the row is not drawn at all. Row badges and the exported SVG use the same labels.
-
Zoom — rows are two lines by default; − / + scale the rows and their text from a compact single line up to a roomy list.
Branch chip (composer)
The chip that ships with the upstream plugin is unchanged: on a blank conversation it lets you switch or create a branch, and it opens the interactive commit-graph dialog and the worktree manager. Branch operations run in the host process; the chip hides itself on active sessions.
Requirements
| Item | Value |
|---|
| DSH | >=0.2.1-rc.2 <0.3.0-0 |
| Node (host runtime) | ^22.19.0 || >=24.0.0 |
dsh-better-sidebar | needed for the tab only — without it the plugin still loads and the chip keeps working |
Install
Grab the prebuilt bundle from the latest release — dsh-git-graph-sidebar.tgz, no build step needed — then install it as a plugin bundle:
-
Desktop app (the desktop profile is owned by the Electron app, so the CLI refuses it): install the .tgz from the app's plugin page.
-
CLI-managed profile:
dsh plugin --profile <profile> add github:Athosoo/dsh-git-graph-sidebar
# or from the downloaded tarball
dsh plugin --profile <profile> add ./dsh-git-graph-sidebar.tgz
Restart DSH after installing. The host half registers its /git/* HTTP routes at startup, so a page refresh alone is not enough for a first install, and an upgraded build is not picked up until the process restarts.
The tab needs dsh-better-sidebar installed; without it everything else — the branch chip, the graph dialog, worktrees — keeps working and only the sidebar tab is absent.
Repository discovery rules
| Rule | Value |
|---|
| Scan depth | 3 levels below the working directory (a repository found at a level is not descended into) |
| Upper bound | 200 repositories per scan, then the result is flagged as truncated |
| Skipped directory names | node_modules, dist, build, out, coverage, target, vendor — plus every dot-directory |
| Repository marker | a .git entry (directory, file or symlink — so worktrees and submodules are both detected) |
| Per-entry status budget | 5 s; an entry whose git status did not return in time is still listed, without branch metadata |
| Cache | 60 s per base path, shared between concurrent requests; the tab's refresh button bypasses it |
Git conventions
A commit graph shows what happened; a conventions document records what the team agreed to do next time. The tab can bootstrap that document for every repository it lists.
-
Bring your own document — the plugin never invents house rules. It writes an empty scaffold with six sections marked 待定 / to decide and status: draft, so the team fills in the decisions instead of inheriting someone else's defaults.
-
Triggered by a new repository — a repository that has no document and no commits yet (unborn HEAD) gets the empty scaffold automatically, and the framework picker opens right away. Repositories that already contain commits but no document are listed as missing it; one click writes the scaffold.
-
Seven proven frameworks — picking one writes its full template into the document and flips it to status: settled:
| Framework | Best for |
|---|
| Conventional Commits | changelogs and semantic versions generated from commit messages |
| GitHub Flow | small teams shipping from main with short-lived feature branches |
| Git Flow | versioned releases promoted across several environments |
| Trunk-based development | continuous delivery with very short-lived branches |
| Conventional Branch | branch names that encode type and issue reference |
| GitLab Flow | explicit environment branches between main and production |
| Worktree flow | several versions or hotfixes checked out at once |
-
Never overwrites — an existing document is only replaced when the request carries force, which the UI does not offer for settled documents. The document is plain Markdown in the repository root, so it is reviewed and revised like any other source file.
-
My templates — the gear at the right end of the toolbar opens a one-entry Settings menu, and Template management takes you to the manager (the picker's Manage templates button jumps straight there): write the team's own rules as a template (id, name, tagline, best-for, reference URL, body), and it appears in a My templates group at the end of the framework picker with a Custom badge; the seven built-in frameworks stay read-only and cannot be edited or deleted; the same page lists them above your templates with a show/hide switch, which only removes their card from the picker ( action , stored as ) and never touches a body, so a document already carrying that framework keeps resolving. Saving forces and a line into the front matter, rejects a body over 64 KB or without , refuses an id that collides with a built-in framework or , and caps the store at 50 templates. Templates live in , never in the profile config; deleting one leaves written documents untouched, their value simply dangles (the conflict check still reports it).
Initializing a repository
A workspace does not have to start as a repository. When the root of the workspace is not one, the tab's toolbar offers Initialize Git repository (and a workspace with no repositories at all shows the same button in the empty panel).
- Two steps, never one click — the button opens a bar that names the target directory and the initial branch before anything is written.
- Only
.git is written — git init creates the metadata directory; no file in the workspace is touched, and existing repositories below the workspace are unaffected.
- It refuses to nest — if the directory already sits inside a repository (including a parent outside the workspace), the host answers
already-repository instead of creating a second root inside it.
- It hands over to the conventions flow — because the new repository has no commits yet, the empty conventions document is scaffolded and the framework picker opens immediately, so the first commit already follows an agreed rule set.
HTTP routes
Registered by the host half under /git; all of them answer JSON ({ok:true,value} / {ok:false,error}), except /git/events, which is a server-sent event stream.
| Route | Purpose |
|---|
/git/repos | multi-repository discovery for a path ({path, refresh?}) |
/git/status | branch, head, dirty/untracked/conflict counts |
/git/branches | branch list with the current one marked |
/git/graph | commit list with parents and refs ({path, limit?}) |
/git/switch | switch branch |
/git/create-branch | create and switch |
/git/worktrees | worktree list |
/git/worktree-add | create a worktree |
/git/worktree-remove | remove a worktree |
/git/worktree/list | linked worktrees of a repository, read-only ({path}; the anchor must sit inside a registered workspace, the answer is git worktree list --porcelain with bare/locked/prunable/detached kept) |
/git/worktree/status | branch, HEAD and dirty state read in one linked checkout ({path, worktree}; worktree is matched against the list, never used as a cwd directly) |
/git/worktree/branches | branch list of one linked checkout ({path, worktree}) |
/git/worktree/graph | commit list read in one linked checkout ({path, worktree, limit?}) |
/git/conventions | convention document state for a repository plus the framework catalog ({path}) |
/git/convention | write the document ({path, framework?, force?}) |
/git/convention/templates | the user's own convention templates: list / starter / get / save / delete ({action, id?, template?}; stored in $DSH_HOME/git-graph/conventions-templates.json, never in the profile config) |
| /git/events | change notifications (polled by the UI) |
Every route resolves the requested path with realpath and rejects it unless it lands inside a registered workspace. Nested repository directories are accepted; anything outside a workspace gets workspace-unknown.
Known limitations
- The tab never changes an existing repository's Git state. It writes the conventions document, and it can run
git init for a directory that is not a repository yet — nothing else. No commits, no branch switching, no worktree creation or removal: those stay in the chip's dialog. Linked checkouts are listed read-only, and a jump only opens a session or a workspace — it never creates a worktree.
- Linked checkouts outside every registered workspace are listed but not readable. The four
/git/worktree/* routes gate the repository the checkout belongs to, so a checkout that is not registered as a workspace itself still gets its branch, HEAD and dirty state; the jump then registers it on request.
- Do not install this alongside
@linxin666/dsh-client-ui-git-graph. Both halves own the /git route prefix, and two registrations of the same prefix conflict. Install one or the other.
- Repositories deeper than 3 levels are not listed, and a repository inside a skipped directory name (
node_modules, dist, …) is not listed either, by design.
- The host half is a build output.
lib/index.js is edited directly (the added parts sit in marked //#region blocks); lib/client.js is generated by splicing src/client/graph/SidebarTab.js into it. See DEVELOPMENT.md.
Development
npm run build # splice src/client/graph/SidebarTab.js into lib/client.js
npm run check # node --check on both bundles
npm run validate # every t("…") key exists in both locale dictionaries
npm run preview # render the tab to preview/*.html without a running DSH
npm run preview writes standalone HTML files (dark and light theme) that show the tab with sample repositories and commits, so layout changes can be checked in a browser without installing anything.
Attribution and license
Apache-2.0 — see LICENSE and NOTICE.
This is a fork of @linxin666/dsh-client-ui-git-graph 0.4.8 by linxin666, also Apache-2.0. The branch chip, the commit-graph dialog, the worktree manager and the host git service come from that project. Changes in this fork: renamed package, workspace gate widened from exact-path match to inside-a-workspace, added the /git/repos discovery route, added the better-sidebar tab (repository tree, SVG canvas, zoom, pinned commit card) and its locale keys, and since 1.15.0 the tab lists linked checkouts read-only through four /git/worktree/* routes and can jump to one.