Skip to main content

deepseek-harness-desktop/packages/dsh-git-graph

156Stars5Forks6Issues0Watchers

Adds a git branch selector and Git graph panel to the DSH Web interface, positioned next to the official workspace capsule. Branch switching executes in the host process. Also adds loopback and workspace guardian.

Evidence5/5methodologySourceInstallMaintenanceDSH versionSecurity scan
Machine-auditedInstall commandRepo verifieddsh-plugin topicLicenseREADMEAI wiki

ⓘ This plugin is a sub-package of the ningbainb/deepseek-harness-desktop monorepo — stars and activity count the whole repository.

Language
TypeScript
License
BSD-3-Clause
Branch
main
ai-agentai-coding-assistantcodexdeepseekdeepseek-harnessdesktop-appdshdsh-plugin

Install

cmdweb profile
$ dsh plugin --profile web add @linxin666/dsh-client-ui-git-graph

Run the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial

Install via your agent

Install the DeepSeek Harness plugin ningbainb/deepseek-harness-desktop/packages/dsh-git-graph for me: review the repository at https://github.com/ningbainb/deepseek-harness-desktop first, then run the install command and verify the plugin loads successfully.

Paste this instruction to the DSH Web GUI assistant — it will install and verify for you.

One-Line Description

Adds a git branch selector next to the official workspace chip in the DeepSeek Harness Web UI, with a "Create and Switch to New Branch" panel and "Git Graph" panel in the popover. Branch switching executes真实的 git commands in the host process, with the browser only responsible for display and interaction.

Core Features

  • Displays the current branch name on the chip right above the session input card ("分离 HEAD" shows "Detached HEAD" for detached HEAD state), automatically hides in non-git workspaces to avoid dead controls
  • After opening the popover, you can search local branches, see checkmarks on the current branch, and view an "Uncommitted Changes" summary at the bottom (dirty file count)
  • Automatically runs guard checks before branch switching (unresolved conflicts, ongoing merge/rebase/cherry-pick/revert/bisect, target branch checked out by another worktree); switching is rejected with readable bilingual error messages when guards fail, preventing disk working tree corruption
  • Supports "Create and Switch to New Branch": first provides real-time feedback by mirroring git check-ref-format --branch naming rules on the frontend, then performs authoritative validation and duplicate name checks on the host side, finally executing git switch -c <name>, and automatically refreshes the chip and graph upon success
  • Provides read-only Git Graph popover: displays branch/tag/remote commit list in topological order, including monospace lane characters, relative timestamps (刚刚/X minutes ago/X hours ago/X days前), and ref tags, with paginated loading (100 items per page, initial 200 items)
  • Host side pushes external git state changes via /git/events SSE (polls every 30 seconds when subscribed, with 15-second timeout per detection); chip also refreshes on window focus (5-second throttle), ensuring UI updates after switching branches in another terminal

Technical Implementation

  • Language: TypeScript (host and browser halves share src/core pure logic; TypeScript modular build, css-modules via lightningcss)
  • Key Dependencies: @deepseek-ai/dsh-client-ui-conversation, @deepseek-ai/dsh-client-ui-slots, @deepseek-ai/dsh-client-runtime, @deepseek-ai/dsh-client-locale (peer injection surface, all ^0.1.0-rc.7, package.json:60-70); host side depends on @deepseek-ai/dsh-host-webserver, @deepseek-ai/dsh-subprocess, @deepseek-ai/dsh-workspace (package.json:61-70); @deepseek-ai/cordis 4.x for function plugin assembly
  • Architecture Pattern: Dual-sided cordis plugin. Host half (src/index.ts) mounts GitService and /git/* routes via inject = ['webServer','subprocess','workspaceRegistry']; client half (src/client/index.ts) injects git verbs (repoStatus / branches / switchBranch / createBranch / graph / subscribeChanges) via inject = ['slots','sessions','connection','locale','conversation']. Activation uses "dsh": { "bundle": { "patch": "./cordis.patch.yml" } } to inject ui-git-graph row into profile (cordis.patch.yml:1-12)
  • Entry Files: packages/dsh-git-graph/src/index.ts (host half apply entry) + packages/dsh-git-graph/src/client/index.ts (browser half apply entry). Git command argv concentrated in src/core/git-command.ts, pure branch name validation mirror in the same file

Use Cases

When you work with multiple git branches in DeepSeek Harness Web, frequently need to switch directly to other branches from the conversation interface to view history/compare, and don't want to open a terminal; also want conflict protection before switching to avoid "switched and found unresolved merge conflicts" causing working tree dirty. Also suitable as a lightweight alternative to read-only Git Graph—you can view the full branch/tag/remote topological structure with ref tags without leaving dsh Web.

Prerequisites and Compatibility

DependencyMinimum VersionDescription
DeepSeek Harness (web profile)^0.1.0-rc.7All peerDependencies locked to this version (package.json:60-70); bundled patch via cordis.patch.yml (cordis.patch.yml:1-12)
Node.js^22.19.0 || >=24.0.0package.json:7-9 engines.node
PlatformmacOS / Windows / LinuxCross-platform, no native modules (package.json only declares react ^18.2.0 peer, no node-gyp dependency)
System git executableRequiredHost half executes git commands via ctx.subprocess.spawn(['git',...]) (Windows forces git.exe) (src/host/git-service.ts:53-94); without git, all operations return internal errors
Registered workspaceRequired/git/* path guard: requested path must hit a workspace in ctx.workspaceRegistry after realpath (src/index.ts:35-48); browser requests to unregistered directories receive workspace-unknown
Loopback clientRequiredBoth /git/* and /git/events SSE reject non-loopback socket/Host requests (src/host/routes.ts:83-103); LAN-exposed dsh web returns 403 for external clients
DSH_HOME environment variableOptionalFalls back to ~/.dsh when unset; host half places worktree directories under $DSH_HOME/worktrees/<repoHash>/<runId> (src/index.ts:82 / src/host/worktree-service.ts:212)

Installation

dsh plugin --profile web add github:ningbainb/deepseek-harness-desktop/packages/dsh-git-graph

Configuration Options

This plugin requires no additional configuration. All guards, SSE timing, and context mount timeouts are hardcoded in the source, with no user-level schema exposed.

ConfigTypeDescriptionDefault
DSH_HOMEEnvironment variable (optional)Determines worktree subdirectory location; defaults to ~/.dsh when unset (src/index.ts:82)~/.dsh

Other adjustable parameters in source code (for secondary development reference only, not exposed via plugin config at runtime):

  • CONTEXT_FALLBACK_MS = 2000: Timeout for waiting for conversation.input.selector.context slot declaration; chip falls back to conversation.input.dock after timeout (src/client/index.ts:108)
  • POLL_INTERVAL_MS = 30_000: Interval for host SSE to poll workspace status when subscribers exist (src/host/routes.ts:45)
  • STATUS_TIMEOUT_MS = 15_000: Hard timeout for single status detection, preventing hung git subprocesses from blocking push flow (src/host/routes.ts:57)
  • FOCUS_REFRESH_MIN_MS = 5_000: Throttle for chip refetch triggered by window focus (src/client/chips/BranchChip.tsx:37)

FAQ

Q: Does this plugin modify DeepSeek Harness official source code?

A: No. AGENTS.md:5-6 explicitly states "Main repo (sibling checkout) zero modifications; this repo is a self-contained cordis plugin package"; all types come from @deepseek-ai/* peerDependencies in node_modules (package.json:60-70), no tsconfig references to DSH source code checkout introduced. Uninstalling reverts to official native behavior.

Q: Is branch switching real git switch? Will it affect other sessions?

A: Yes, it's real. src/host/git-service.ts:176-197 calls git switch --no-guess <branch> on the real working tree at repoRoot, and affects all sessions under that workspace (not single-session cwd override). This means if you switch branches in one session, another session opening the same workspace will already see the new branch; this is "workspace-level" semantics, not "session-level".

Q: Can the browser use this interface to run git on arbitrary directories?

A: No. The workspaceGate in src/index.ts:35-48 requires the requested path after realpath to hit a registered path in ctx.workspaceRegistry, otherwise returns workspace-unknown; routes.ts:83-103 also rejects all non-loopback clients (LAN-exposed dsh web returns 403 for external clients), and /git/* also enforces POST + application/json (routes.ts:215-229). Worktree routes are stricter: only accept opaque IDs, rejecting path/base ref/argv (README.md:74 / worktree-routes.ts:46-48).

Q: When will the branch chip hide?

A: The entire chip doesn't render when it can't find cwd (empty cwd for that sessionId in sessions list) or status returns null (non-git workspace). src/client/chips/BranchChip.tsx:253 writes "repo === undefined || repo === null return null"; this hiding is better than disabling to avoid dead controls, and the chip will automatically appear on next refresh after the workspace becomes a repository.

Q: What kind of errors are given for switching failures?

A: Errors fall into two categories: guard-level (conflicts-present, operation-in-progress, branch-in-other-worktree, invalid-branch-name, branch-already-exists, target-branch-not-found, workspace-unknown) and Git-thrown overwrite conflicts during switching (tracked/untracked-changes-would-be-overwritten, with first 2 file paths + overflow count). The guardBlock in src/host/git-service.ts:294-311 runs the first type, and classifySwitchFailure in src/core/git-command.ts:178-196 categorizes stderr into stable codes; the client translates codes into bilingual readable sentences in src/client/chips/error-copy.ts:27-50.

Q: Does it conflict with official workspace management? Will it be overwritten by duplicate workspace selectors?

A: Workspace selection is not in this plugin—the official workspace chip is the only entry point (src/client/index.ts:23-24 / ADR-001:36). The plugin only supplements "branch" and "graph" two actions, corresponding to a 28px transparent chip next to the official chip in the UI. Branch state is not written to session log and doesn't enter the model's visible surface (src/index.ts:6-8), so it doesn't affect the model's conversation history.

Q: Does Git need to be in the system PATH?

A: Yes required. GitService on the host side uses the subprocess service to directly spawn git (macOS/Linux) or git.exe (Windows, src/host/git-service.ts:53-55 forces native executable name to avoid .cmd shim parsing issues); the running machine must have git installed, otherwise all /git/* interfaces return internal errors. Worktree operations use the same spawn seam.

Q: How to uninstall?

A: dsh plugin --profile web remove @linxin666/dsh-client-ui-git-graph (README.md:67). The package name is npm-published @linxin666/dsh-client-ui-git-graph, and the activated plugin mounts the cordis row name ui-git-graph (cordis.patch.yml:11). After uninstalling, the next dsh web startup won't register any /git/* routes or browser chip.

Getting Started Difficulty

Beginner — One-line install command, takes effect after restarting dsh web; no configuration needed; users only need to click the chip, select branches, and view error messages. All advanced behaviors (guards, SSE timing, loopback restrictions) are built into the plugin.

Known Issues and Limitations

  • Local branches only: Branch list only uses git for-each-ref refs/heads (src/host/git-service.ts:151 / src/core/git-command.ts:19-23), doesn't list remote branches. If you want to see origin/* branches after git fetch, need to let chip refetch after terminal fetch (mount/popover open/focus all trigger refresh)
  • Switching constrained by workspace state: Three cases are blocked by guards (unresolved merge conflicts, ongoing merge/rebase/cherry-pick/revert/bisect, target branch checked out by another worktree) (src/host/git-service.ts:294-311), you need to complete or abort these operations in the terminal first
  • /git/* not served when LAN-exposed: All /git/* interfaces enforce loopback + Host validation (src/host/routes.ts:83-103); if you reverse-proxy dsh web to public network, corresponding interfaces return 403; the plugin doesn't intend to relax this restriction for external access (ADR-001:38)
  • Worktree functionality only exposed on host half (src/index.ts:62-87), browser-side chip doesn't carry worktree entry; this part is consumed by desktop-side plugins like Task Board via /git-worktree/* routes
  • Worktree subdirectory fixed under $DSH_HOME/worktrees/<repoHash>/<runId> (src/host/worktree-service.ts:212 / src/index.ts:82), and path escaping is prevented by TypeError (src/host/worktree-service.ts:214); if you move DSH_HOME, ensure old path worktrees are cleaned up or actively removed, otherwise orphan records will appear
  • Browser chip and official workspace chip are "session-level vs workspace-level" separated: Session switching doesn't clear the chip, but chip auto-hides when switching to non-git cwd (src/client/chips/BranchChip.tsx:253)
  • package.json:7-9 locks Node to ^22.19.0 || >=24.0.0, earlier Node versions are not in the test matrix

Read the usage guide →

Install steps, key points, FAQ and compatibility for this plugin — auto-derived from indexed fields.

Listing badge

Listed on deepseek-plugin.org
[![Listed on deepseek-plugin.org](https://img.shields.io/badge/listed_on-deepseek--plugin.org-007EC6)](https://deepseek-plugin.org/plugins/ningbainb/deepseek-harness-desktop/packages/dsh-git-graph)

Paste this markdown into your GitHub README to link back to this listing. The badge only states the listing — not a security endorsement.

← Back to plugin directory