← Back to home@chzisnull

dsh-git-tree

DeepSeek Harness plugin: a Sourcetree-style Git workspace — branches, staged/unstaged changes and diffs in a full-height panel

Stars
0
Language
JavaScript
Created
Oct 21, 2026
Updated
Oct 21, 2026

Introduction

dsh-git-tree

A DeepSeek Harness plugin that gives the Web GUI a Sourcetree-style Git workspace: a full-height panel with the left rail Sourcetree has (Working Copy / branches / tags / remotes), a staged/unstaged file list, and a diff viewer — inside DSH, next to your conversation, instead of in another app.

中文文档

The Git workspace panel

What it does today

Version 0.1.0 is the read-only first slice. It is honest about that: the panel reads your working copy and renders it; it does not stage, commit or push yet.

  • A main panel, opened from the Git row in DSH's left icon rail — the same kind of seat the Plugins page uses, so it gets the full centre column, the window chrome inset, and DSH's theme for free.
  • Branch + sync state: current branch, upstream, ahead/behind, repository root.
  • Staged and unstaged lists, split the way Sourcetree splits them, driven by git status --porcelain=v2 --branch -z (so renames, untracked files and conflicts keep their real XY state instead of being flattened into "modified").
  • Diff for the selected file, with the same ---guarded, colour-free git diff DSH's own workspace-changes feature uses.
  • Read-only by construction: the host half only ever runs status, rev-parse and diff. There is no mutation route in this version at all.

Status

Area0.1.0
main panel + sidebar launcherdone
branch / ahead / behinddone
staged + unstaged listsdone
per-file diffdone
stage / unstage / commitplanned (P2)
history graphplanned (P3)
right-sidebar tab (compact mode)planned
agent-shared operationsplanned

The full plan, including the measured UI spec the layout is built from, is in docs/PLAN.md.

Design notes

The panel copies Sourcetree's information architecture, not its chrome. The layout numbers are measured, not guessed: they come from the running app's accessibility tree and from decoding its Interface Builder archives — see docs/sourcetree-ui.md and docs/ib-summary.md.

Two consequences worth knowing:

  • Sourcetree's diff has no syntax highlighting; it distinguishes lines with whole-row colour only. This panel does the same on purpose — in a diff, "which lines changed" beats "which token is a keyword".
  • Every colour here is a --dsw-alias-* theme token, so light and dark both work without a second design. npm test fails if a hardcoded colour ever ships.

How it is wired into DSH

HalfFileWhat it does
Hostindex.js (plain ESM, no build)registers two authenticated exact Fetch routes via ctx.connection.fetch.register, runs git through ctx.subprocess
Clientclient.js (built from src/client/index.tsx)registers the sidebar launcher into sidebar.panellist and the panel into main, both under the id git-tree
GET /api/git-tree/status          → { branch, upstream, ahead, behind, entries[] }
GET /api/git-tree/diff?path=…     → { path, staged, text }

Git runs without a shell, with GIT_TERMINAL_PROMPT=0 (never blocks on credentials), GIT_OPTIONAL_LOCKS=0 (never takes optional locks in your repo), GIT_CONFIG_COUNT=0 and bounded output and time. A path that is absolute, contains .., or starts with - is refused before it reaches argv, and a cwd outside the workspace is refused with 403.

The host mechanism is documented against the installed build in docs/dsh-plugin-api.md — slot ids, the Fetch-route channel, subprocess safety and theme tokens, all with file:line citations into app.asar.

Install

From this checkout:

dsh plugin add /Library/aiProject/dsh-git-tree

Or, inside a session that has the plugin manager, install the bundle and point it at the package directory:

plugin_manager action=install_bundle target=/Library/aiProject/dsh-git-tree

Restart or refresh the DSH page if the new client module does not appear immediately. The plugin only reads; nothing is written to your repository.

Development

node build.mjs     # src/client/index.tsx → client.js (committed artifact)
npm run check      # node --check on both halves
npm test           # 18 assertions: manifest, host routes, bundle contract

build.mjs resolves esbuild from this package's node_modules, then from an installed dsh-workbench; if neither is present it stops with the exact command to fix that (npm i -D esbuild).

The test suite needs no DSH, no git and no browser:

  • test/manifest.test.mjs — what the loader validates (platform, exports, patch row, icon, locales).
  • test/routes.test.mjs — apply() against a fake context: both routes registered, porcelain-v2 parsed, -- before paths, traversal/absolute/option-shaped paths refused, missing subprocess degrades instead of throwing.
  • test/ui-contract.test.mjs — the built bundle: module-loader wrapper, exactly two slot registrations under one id, both routes called, theme tokens only, no document.body.

Design mockups

docs/design/ holds the four artboards the implementation is being measured against:

FileFrame
overview.pngall four
frame1-docked.pngcompact panel next to a conversation
frame2-workspace.pngfull workspace: rail / files / diff / commit box
frame3-history.pnghistory lanes and commit detail
frame4-states.pngrow states, size grid, theme tokens, data flow

License

MIT