DeepSeek Harness Hub
← 返回列表

xia-sc/dsh-git

DeepSeek 客户端兼容 / 相关生态spec-screened在 GitHub 查看 ↗
⚠ 装前注意

DeepSeek Harness Web GUI 的完整 Git…

基本兼容但装前注意:未发布到 npm registry,仅可从源码安装 · 最近上游提交 2026/9/15 · 已提供中文文档

DeepSeek Harness Web GUI 的完整 Git 管理插件:分支切换/fetch/pull/commit/push,可折叠悬浮面板 + 输入框胶囊

综合分
35.5
GitHub 分
35.5
用户评分
★ Stars
1
周下载量
兼容 / 相关生态插件(非 dsh 原生,请按其对应运行时安装)
git clone https://github.com/xia-sc/dsh-git.git
数据截至 2026/9/18(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查⚠ 装前注意

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

npm 包@dsh-plugins/dsh-git(未发布到 npm,仅可源码安装)
Node 引擎未声明 engines.node
dsh CLI 依赖未声明 dsh 版本约束
入口文件main/exports/bin 已声明

未发布到 npm registry,仅可从源码安装

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/18 02:57:24

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

@dsh-plugins/dsh-git

DeepSeek Harness Web GUI 的完整 Git 管理插件,形态为一个可折叠的悬浮面板,
实时跟随当前会话的工作区——在侧边栏点击不同的会话/工作区,面板会瞬间
重新绑定到对应仓库。

支持的工作流: 分支切换 · 拉取更新(fetch) · 拉取合并(pull,仅快进) ·
暂存全部 · 提交(commit,可用 AI 起草提交信息) · 推送(push) · 状态(status) ·
最近提交 · 未提交文件列表 · 点击变更看差异 · 基于某分支新建分支。

界面

- 悬浮面板(shell.overlay):折叠时不渲染任何元素(不会遮挡输入框);
展开后是完整的 Git 工作台(状态行、带 脏树预检 的分支切换器——在有
未提交修改时选择分支,会先显示受影响文件列表警告而不是直接切换,并提供
"仍要切换"按钮;fetch/pull 操作;提交区——"暂存全部"按钮、AI 生成依据
选择器、"✨ AI 生成"按钮、提交信息输入框(多行文本框,回车换行、
Ctrl/Cmd+Enter 提交)与提交按钮;可折叠的变更列表与
最近提交列表;上次操作输出)。面板可通过顶栏拖动(按住带 Git 标题的
那一行,拖到哪里就停在哪里,不会拖出视口;顶栏上的按钮/输入框不会触发
拖动;双击顶栏回到居中位置)。
分支切换器旁边的"+ 新建分支"按钮会展开一个内联表单:新分支名 + 基分支
选择器(本地分支或 origin/feature/x 这样的完整远端引用)——确认后从
该基分支创建新分支并切换过去。
- 点击变更看差异:点变更列表里的任意一行,面板会从 400px 的单栏展开成
两栏——左边照旧是完整工作台,右边是该文件的 unified diff(见下文
「差异查看」)。再点同一行、或点差异标题栏的 ×,就收回单栏。
- 输入框胶囊(conversation.input.dock):输入框左上角的紧凑型左对齐
状态胶囊(分支摘要,或"当前工作区不是 Git 仓库");点击它展开/收起悬浮面板。
- 两处界面共享同一个 store,状态永远一致,并都会随当前会话(及其 cwd)
切换而重新绑定。切换工作区会清空已选中的文件——绝不让面板显示别的仓库的内容。

差异查看

点变更列表里的一行,就在右侧显示该文件的 git diff。左侧列表是它的导航:
行高亮表示"正在看这一个",首次点击会自动把变更列表展开成完整列表。

- 可拖拽调宽:拖动面板最右侧那条边即改差异栏宽度——左栏(工作台)宽度不变,
面板整体变宽、差异栏跟着长;双击这条边复位。面板始终留在视口内,窗口变窄时
会自动收回;拖动时面板会就地锚定左边缘,所以指针与这条边是 1:1 跟手的。
- 未暂存 / 已暂存:默认自动跟随数据——未暂存有内容就显示未暂存,否则显示
已暂存;顶部两个 chip 可手动切换,空的一侧置灰。同一个文件两边都有改动时,
一次点击看到的是最关心的那一侧,另一侧一键可达。
- 自动刷新:暂存、提交、切分支、刷新之后,打开的差异会自己重读——未暂存
变空时会自动落到已暂存。
- 行号与配色:左右两列行号(旧/新)取自每个 hunk 头,+/- 行走主题的
成功/错误色,hunk 头单独一行,\ No newline at end of file 灰显。
- 边界都有明确提示:这一侧没有改动 / 二进制文件没有文本差异 / 差异过大只
显示开头一段 / 未跟踪目录里还有 N 个文件未展开 / 读取失败。超过 1500 行的
差异先渲染前 1500 行,按钮可展开全部(避免一次渲染几万个节点)。
- 未跟踪文件:与空文件对比,显示成 new file mode 的新增 diff;未跟踪
目录会把里面的文件逐个展开(上限 50 个,其余计入提示)。
- 重命名:同时把旧名与新名作为 pathspec 交给 git,否则 git 无法配对,会把
一次重命名报成整文件新增。

提交区与 AI 起草

提交区按真实操作顺序排列:暂存全部 → AI 生成 → 提交。

- 暂存全部:git add --all(含删除与未跟踪文件)。工作区干净时置灰。
这是"只有未暂存改动、点提交却报错"的正解——提交本身依然不会隐式暂存。
- AI 生成依据:三选一,决定把哪一部分改动交给模型:
已暂存(默认)、未暂存、全部。默认 已暂存,因为*只有它是本次
提交真正会记录的内容——用其他依据生成的描述可能与实际提交不符。
- ✨ AI 生成:把选中的改动(diffstat + diff,截断后)交给当前会话所选
模型,生成的提交信息直接填入输入框;不满意可改,也可以直接手写。

生成用的模型路由取当前会话的 modelSelection 投影(待生效的选择优先,
其次是上次实际使用),取不到时回落到宿主注册的第一条路由。失败会以
本地化文案显示在"上次操作输出"里(无可用改动 / 未配置模型 / 生成失败等)。

架构

一个双面 npm 包:

| 半边 | 文件 | 职责 |
| --- | --- | --- |
| 宿主 | lib/index.js | Cordis 插件(bundle 行 dsh-git),在自己的 ctx.webServer 上注册 /dsh-git-rpc 前缀路由,收发浏览器 connection.rpc.call 的同一套 Connection RPC 信封,并复用 connection 服务的 Host/Origin + 浏览器会话围栏(connection.requestRejection)。端点:status、branches、checkout、createBranch、fetch、pull、stage、diff、commit、push、log、generateMessage。所有 git 调用都走 execFile(无 shell)、带超时(本地 30s / 网络 120s)、严格入参校验。AI 生成走注入的 llm 服务。 |
| 浏览器 | lib/client.js | dsh.client bundle(服务于 /plugins/@dsh-plugins/dsh-git/client.js):悬浮面板 + dock 行 + 共享 store,对照模块表手写(仅依赖 react)。 |

为什么自持 HTTP 路由(dsh ≥ 0.1.5-rc.1)
从 dsh 0.1.5-rc.1 起,外部插件不能再调用 ctx.connection.rpc.handle():
HostConnectionService.rpc 闭包持有的是 connection 插件自己的 Context(inject
只有 ["credentials"]),注册时执行
owner.effect(() => owner.webServer.register(route)),而该插件只在内部的
ctx.inject(["webServer"], …) 作用域里取得到 webServer。于是无论调用方 inject 了
什么,这一行都会以 cannot get property "webServer" without inject 挂载失败
(0.3.0 正是如此,插件在 0.1.5-rc.1 上装不起来)。本插件因此改为自己注册
/dsh-git-rpc 路由、自己实现同一套 RPC 信封;请求围栏仍交给 connection 服务的
requestRejection,安全等级与 /api 完全一致。test/host-mount.mjs 在真实 Cordis +
真实 Connection 服务上守护这一点。

安装

dsh plugin --profile web add https://github.com/xia-sc/dsh-git

然后重启 dsh web(bundle 行与浏览器 roster 在启动时组合)。刷新后,
当前会话工作区是 git 仓库时,输入框上方会出现 dock 胶囊,点击即可展开面板。

要求 dsh ≥ 0.1.5-rc.1(宿主半自持 /dsh-git-rpc 路由,见上文架构说明)。

卸载:

dsh plugin --profile web remove @dsh-plugins/dsh-git

RPC 约定(/dsh-git-rpc)

浏览器侧通过 ctx.connection.rpc.call("/dsh-git-rpc", endpoint, { args }) 调用;宿主侧
是本插件自持的 /dsh-git-rpc/ 前缀路由,收发与 /api 相同的 Connection 信封:

- 请求:POST /dsh-git-rpc/,content-type: application/json,
{ type: "client-request", rpcId, method: , payload: { args } }
- 响应:{ type: "server-response", rpcId, result: { ok: true, value } | { ok: false, error } }
- 围栏:connection.requestRejection(Host/Origin + 浏览器会话 Cookie);非 POST → 405,
非 JSON → 415,请求体超限 → 413,路径不属于本通道 → 404。

载荷使用 { args } 约定。cwd 必须是绝对路径;branch 匹配
^[A-Za-z0-9][A-Za-z0-9._/-]$(不允许前导 -、..、@{、\、空白、
控制字符);remote 为普通单段。提交信息是真正的提交信息:一行主题 +
可选的多行正文。CRLF 会归一为 LF,逐行去掉行尾空白,去掉首尾空行并把连续
空行折叠为一行(因此主题与正文之间那一行空行会保留);空、超长(>10000
字符)、含控制字符的信息报 invalid-message。信息经 stdin 以
git commit --cleanup=whitespace --file=- 传入,因此空格、引号、换行、shell
元字符、前导 - 都会被原样记录。

| 端点 | 参数 | 结果(value) |
| --- | --- | --- |
| status | { cwd } | { repo, branch, detached, oid, upstream, ahead, behind, dirty, changes: [{status, path, index, worktree, file, origFile}] }。path 是展示串(重命名读作 old → new),file/origFile 是交给 diff 的 pathspec,index/worktree 是 porcelain-v2 的两个字母。 |
| branches | { cwd } | { repo, current, local: [{name, current, upstream, sha}], remote: [{name, short}] } |
| checkout | { cwd, branch } | { branch, detached, oid, message? },经 git switch --guess;浏览器会预检脏树并提前警告;因"本地修改会被覆盖"被拒绝时会带上可读前缀。 |
| createBranch | { cwd, branch, base? } | { branch, detached, oid, message? },经 git switch --create  (缺省 base 即 HEAD);从基分支创建新分支并切换过去。 |
| fetch | { cwd, remote? } | { message }(120s 超时) |
| pull | { cwd } | { message },经 git pull --ff-only(绝不隐式合并) |
| stage | { cwd } | { message },经 git add --all |
| diff | { cwd, path, origPath? } | { repo, path, origPath, untracked, skipped, worktree: {diff, binary, truncated}, index: {…} }。两侧一次读回(git diff [--cached] --no-ext-diff --no-color --  []);path 必须是仓库内相对路径(拒绝绝对路径、..、前导 -、控制字符、首尾空白),非法时报 invalid-path。未跟踪路径用 git diff --no-index -- /dev/null (容忍退出码 1),未跟踪目录用 git ls-files --others --exclude-standard 展开(上限 50 个,其余计入 skipped)。单侧超过 40 万字符在行边界截断并置 truncated;二进制置 binary。只读,不碰 index / 工作区 / 配置。 |
| commit | { cwd, message } | { message };未配置 user.name/email 时报 missing-author 错误 |
| push | { cwd } | { message }(120s 超时) |
| log | { cwd, count? } | { repo, commits: [{sha, author, subject, refs}] }(钳制 1..50) |
| generateMessage | { cwd, mode?, provider?, model? } | { message, mode, provider, model }。mode 为 staged(默认)/unstaged/all,非法值报 invalid-mode;失败码见 error.details.code:no-changes、no-provider、no-model、llm-empty、cancelled、llm-failed。 |

失败结果的 error.code 在线路上固定为 "internal"(Connection 信封只要求它是字符串),
插件自己的诊断码放在 error.details.code;客户端按该码做本地化文案。

设计决策与边界

- pull 固定 --ff-only:不产生意外的合并提交;冲突以错误形式呈现,
由用户在自己的工具里解决。
- commit 不暂存:只提交已暂存的内容。想一次提交全部改动,用提交区的
"暂存全部"按钮(git add --all),而不是让提交隐式暂存。
- AI 生成会把改动的 diff 发给你配置的模型提供方——可能是第三方网关。
这是显式点击"✨ AI 生成"才会发生的联网行为;插件本身不联网。diff 截断到
12000 字符后发送,且不发送任何仓库外的内容。
- push/pull 凭据来自系统(Git Credential Manager / SSH agent);插件
绝不碰凭据存储。AI 生成同样不接触凭据——API key 由模型适配器自己解析。
- 插件绝不修改 git config;缺 author 时给出明确错误而不是悄悄补写。
- 差异查看是纯只读的:diff 端点只跑 git diff / git ls-files,不写
index、不动工作区、不改配置;它不依赖宿主右侧 Sidebar 那套标签页 API
(那部分还在快速迭代),而是面板内自带两栏——左侧工作台照旧,右侧差异栏。
差异渲染也是自己写的(本 bundle 只依赖 react):解析统一 diff、双行号、
+/- 配色,不引任何语法高亮依赖。
- 重命名必须同时传旧名与新名:git 只在旧名也在 pathspec 里时才配对,只给
新名会把一次重命名报成整文件新增(test/diff.mjs 守护这一点)。
- status 的 porcelain-v2 解析:2(rename/copy)记录的路径在第 10 个字段、
与旧名以 TAB 分隔,u(冲突)记录的路径在第 11 个字段且状态恒为冲突——这
两处曾按 slice(8) / slice(9) 取值而错位,现在由 changeEntry() 统一构造。
- 插件不导入任何 @deepseek-ai/ 运行时包(只用 node: 内置模块和
@deepseek-ai/cordis)。以 pnpm link: 方式安装时,宿主包无法从插件的真实
源码路径解析,声明这类导入会让插件在加载期就崩溃;生成所需的请求构造与流
式拼装因此就近实现。test/generate.mjs 直接测这些单元。
- 面板操作是普通 UI 行为(和 Cordis 面板一样),不会写入会话日志 /
模型提示词。AI 生成只填输入框,不会自动提交。

开发说明

- 浏览器 bundle 为手写(无构建步骤);改 lib/client.js 刷新即生效
(no-cache),改宿主半需要重启 dsh web。
- 测试:
- node test/smoke.mjs —— 路由/信封/端点分发/入参校验(不 spawn git:会话沙箱
拦截子进程管道 stdio);
- node test/host-mount.mjs —— 在真实 Cordis + 真实 dsh-client-connection
上挂载插件行(从 DSH_HOME 的 profile 解析 DSH 包,找不到则 SKIP);
- node test/generate.mjs —— AI 生成单元测试:路由解析、prompt 组装、截断、
流式拼装(block-end 与纯 delta 两条路径)、终止失败/取消/空输出;
- node test/render.mjs —— 双界面真实 React SSR 渲染,含提交区三个控件、
diff 解析器(行号 / 分类 / -- 开头的删除行)、行渲染、差异面板标题栏与
act() 的结果回传/本地化(需要一份 react/react-dom,可用
DSH_GIT_REACT_ROOT 指定,找不到则 SKIP)。
- 也提供 npm test(依次跑四个)。
- npm run test:commit —— 端到端:在临时仓库里真起 git,走插件的
/dsh-git-rpc/commit 路由提交,再用 git log --format=%B 逐字节比对提交
信息(多行、CRLF、中文、前导 -、shell 元字符等),并确认非法信息被拒且
不产生提交。
- npm run test:diff —— 端到端:临时仓库里逐个验证 diff 端点:工作区
/ 已暂存两侧、未跟踪文件与未跟踪目录、重命名配对、删除、二进制、
40 万字符截断、以及路径校验与非仓库目录。
- 后两个都必须 spawn git 的管道 stdio,故不在 npm test 内——请在没有
该限制的环境(普通终端)单独运行。
git 命令集对照运行中的服务端做端到端验证。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

同作者(xia-sc)的其他插件

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群