🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

提交规划智能体 havoc-rao/dsh-git-commit-agent

DeepSeek Harness
havoc-rao27 天前有提交已提供中文文档扫描:含敏感能力

读取仓库真实状态,规划提交并等你批准后执行

29.3/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/11 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add havoc-rao/dsh-git-commit-agent
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · dev
装得上吗
实装验证未通过(unknown),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 27 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
⚠︎ 实装验证未通过(unknown · 2026/10/11) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 17 处,展示前 17 条)
  • 写入剪贴板
    src/client/plan-views.cjs:17
    navigator.clipboard.writeText(String(message || '')).then(function () { setCopyError('') }, function () { setCopyError('复制失败,请选择提交消息手动复制') })
  • 发起网络请求
    src/client/plan-views.cjs:94
    fetch(planUrl(identity), { credentials: 'same-origin', signal: controller.signal }).then(function (res) {
  • 发起网络请求
    src/client/plan-views.cjs:124
    fetch('/git-commit-agent/api/plan-index?' + query, { credentials: 'same-origin', signal: controller.signal })
  • 读取本地文件
    src/core/service.ts:432
    const buffer = await readFile(abs)
  • 发起网络请求
    src/host/approval.ts:67
    outcome = await approval.request({
  • 读取本地文件
    src/host/edit-capability.ts:47
    const stat = lstatSync(current)
  • 读取本地文件
    src/host/plan-http.ts:66
    const document = JSON.parse(await readFile(join(service.directory, 'store.json'), 'utf8')) as { schemaVersion?: number; tasks?: Record<string, StoredT
  • 写入本地文件
    src/host/plan-presentation.ts:45
    await mkdir(join(directory, 'presentations'), { recursive: true })
  • 写入本地文件
    src/host/plan-presentation.ts:47
    await writeFile(temporary, JSON.stringify({ schemaVersion: 1, sessionId: sessionId ?? null, payload: value }), { encoding: 'utf8', mode: 0o600 })
  • 写入本地文件
    src/host/plan-presentation.ts:48
    await rename(temporary, file)
  • 读取本地文件
    src/host/plan-presentation.ts:54
    const envelope = JSON.parse(await readFile(payloadPath(directory, identity), 'utf8')) as { schemaVersion?: number; sessionId?: unknown; payload?: unkn
  • 读取本地文件
    src/host/plan-presentation.ts:69
    const stored: unknown = JSON.parse(await readFile(payloadPath(directory, identity), 'utf8'))
  • 读取本地文件
    src/host/repository-links.ts:132
    const canonicalCwd = await realpath(cwd)
  • 读取本地文件
    src/host/repository-links.ts:133
    const entries = (await readdir(cwd, { withFileTypes: true }))
  • 读取本地文件
    src/host/repository-links.ts:138
    const candidate = await realpath(join(cwd, entry.name))
  • 读取本地文件
    src/host/repository-links.ts:163
    const requestedRoot = repoRoot === undefined ? undefined : await realpath(repoRoot).catch(() => repoRoot)
  • 读取本地文件
    src/host/repository-links.ts:168
    const requestedWorktree = await realpath(worktree).catch(() => worktree)
扫描于 2026/10/31(1 天前) · 范围 全包扫描(包内未声明入口) · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh-agent@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 读取仓库真实状态
  • 规划精确提交
  • 审批后执行提交

DSH 插件:专属智能体,依据仓库真实状态规划精确的 Git 提交,把审批绑定到不可变的计划修订,再通过受限且经过校验的 git 执行器完成提交。

README

由 DeepSeek 最新模型翻译生成
dsh-git-commit-agent 专为 DSH 打造的 Git 提交规划与执行智能体。 智能体读取仓库真实状态,提出一小组逻辑连贯的提交,向用户展示每个提交的精确预览,并且只有在用户批准某个具体计划修订后,才运行受限的 git 执行器,暂存已批准的内容并验证结果。 规划与执行权限不同。对话永远不会授予 git 写权限。 GitLens entry ──▶ dedicated native session ──▶ status/diff analysis │ plan revision + digest │ user previews & approves (one revision) │ restricted executor: exact stage → verify tree → git commit │ report commits / partial failure / reconcile 状态 | 阶段 | 状态 | | P0 — 宿主 API 验证 | 已完成,证据见 docs/P0-VERIFICATION.md | | P1 — 交互式规划(不真正提交) | 已完成:专用会话组合、status/diff/read 工具、整文件计划、版本管理、精确预览、旧修订失效 |
展开完整文档(README 剩余 1 节)
dsh-git-commit-agent

专为 DSH 打造的 Git 提交规划与执行智能体。

智能体读取仓库真实状态,提出一小组逻辑连贯的提交,向用户展示每个提交的精确预览,并且只有在用户批准某个具体计划修订后,才运行受限的 git 执行器,暂存已批准的内容并验证结果。

规划与执行权限不同。对话永远不会授予 git 写权限。

GitLens entry ──▶ dedicated native session ──▶ status/diff analysis
│
plan revision + digest
│
user previews & approves (one revision)
│
restricted executor: exact stage → verify tree → git commit
│
report commits / partial failure / reconcile

状态

| 阶段 | 状态 |
| P0 — 宿主 API 验证 | 已完成,证据见 docs/P0-VERIFICATION.md |
| P1 — 交互式规划(不真正提交) | 已完成:专用会话组合、status/diff/read 工具、整文件计划、版本管理、精确预览、旧修订失效 |
| P2 — 批准 + 执行循环 | 已完成:精确批准绑定、执行器、真实 add/commit、取消、部分失败、钩子、对账 |
| P3 — hunk 级拆分、聊天批准协议 | 未开始(有意为之) |
| GitLens 按钮 + DiffPane 计划预览 | 已实现,位于 client/client.js,对接交付的 better-sidebar 接缝;启动图携带入口,bundle 已提供 —— 浏览器渲染本身尚未验证(见 docs/P0-VERIFICATION.md §8.7) |
| 实时 DSH 宿主集成 | 2026-09-14 验证两次(headless 配置档、临时 DSH_HOME):仅用 inject: ['tools'] 的裸挂载、受限的 8 工具面、守卫拒绝、会话可见性、卸载清理以及一次真实提交 —— 见 docs/P0-VERIFICATION.md §7。两轮发现并修复了三个缺陷(缺少惰性 agents 查找;followup 载荷形状;默认 dataDir 忽略 DSH_HOME) |
| 智能体预设注册 | 已实现:插件通过宿主的 ctx.agentPresets.register() 把自己注册为 git-commit 智能体预设(声明式 @deepseek-ai/dsh-agent-preset 插件包装的正是同一 API),因此它会作为卡片出现在 Settings → Agent presets;专用会话绑定该预设(meta.agentPreset + agentPresets.mount,即 webhook 会话创建者的模式)。尚未在实时宿主上验证(见 预设注册) |

122 个自动化测试针对真实、隔离的 git 仓库全部通过(npm test)。

计划检查器(包括无活动会话)

借助 better-sidebar 的 inspectors 功能,插件注册了两种稳定的 Inspector 类型:dsh-gca-plan(发布快照)和 dsh-gca-plans(最近计划条目)。最近列表使用经过认证的只读路由 /git-commit-agent/api/plan-index;它支持按工作区路径过滤和分页,列表响应中不会返回历史补丁。当前存储保留的是工作树路径,而不是单独的已注册工作区标识,所以过滤器目前按工作树路径精确匹配。

转录卡片上的 明细 和 查看差异 会在同一个 Inspector 中跳转到确切的提交,而不是展开第二份详情副本。概览、提交索引、依赖链接、排除项和修订差异都在该页面内跳转。差异是保存下来的发布补丁(纯文本),绝不是重新计算的工作树差异。被截断和不可用的历史预览会明确标注。

源会话属于资源标识,而不属于它的显示位置。打开资源不会激活或恢复它的源会话。稳定的类型让侧栏能在任何转录挂载之前恢复引用。重新打开同一修订会更新它的位置;打开另一个修订会使用不同的资源 id。Inspector 模式不会轮询完整的历史负载,也不会自动打开后台发布;显式导航和列表刷新绝不会抢走焦点。查看是只读的,绝不构成批准。

没有 inspectors 的旧版侧栏安装会保留下方会话作用域的原生 Plan 标签页回退方案(它们无法提供无会话 Inspector)。

在现有右侧栏中查看完整计划(旧版回退方案)

prepare-plan 转录卡片现在有 在侧栏查看完整计划。页面显示提交分组、完整的多行消息、理由、依赖、文件状态/层级/重命名/二进制标记、统计、排除项、警告/阻塞项以及完整的修订/摘要标识。每个分组保留 查看差异。这是只读的发布快照,不是批准界面;在批准决定或执行之后它仍然可用。

当专用提交会话挂载到原生右侧栏时,客户端每两秒轮询插件经过认证的只读计划路由。初始加载、每次会话切换或重新挂载时的第一个成功响应都是历史基线:它注册页面但不显示它们。只有在同一挂载会话中随后看到的新标识才会显示页面。隐藏的浏览器文档不会显示新计划。不涉及私有会话事件源、对话分页钩子、批准监听器或 apply_plan 调用。

原生侧栏的 params/meta 是临时的,原生打开会忽略 seed 的 id。因此每个修订都会注册一个隐藏的、稳定的标签页类型,编码它的会话、任务、计划、修订和摘要;重复打开会聚焦同一个原生页面。
载荷以原子方式存放在插件私有的展示文件里,通过 /git-commit-agent/api/plan 取回;旧版发布内容则通过公开读取句柄,从会话中持久保存的成功 prepare-tool 结果里恢复。恢复过程绝不会用今天的工作区重新计算补丁。缺失的历史载荷会报告为不可用,而不是凭空编造。侧边栏布局本身由宿主管理:如果刷新后它折叠了,重新打开历史卡片即可。

运行 npm run build 生成宿主产物。本包手写的客户端模块本身就是它对外提供的产物;刷新浏览器就会加载它。正在运行的宿主必须重载/重启已有的插件实例,才能挂载新的宿主 HTTP 路由(只刷新浏览器无法替换宿主代码)。不要为了更新原来的 GUI 而再启动一个服务器。当本智能体没有浏览器会话/cookie 时,带认证的浏览器渲染仍需单独手动检查。

命令面板模式

命令面板提供两个相互独立的操作(搜索 commit / 提交):

- 在当前工作区启动 Commit Agent / Start Commit Agent in Current Workspace:保留原有行为——在当前已注册的工作区里创建专用会话,安装插件受限的工具,应用配置好的默认模型,并提交规划请求。
- 将提交规划 prompt 填入当前会话 / Fill Commit Planning Prompt in Current Session:只填入并聚焦当前会话的输入框。由你审阅/编辑提示词后手动发送。它不会创建或跳转到另一个会话,不更改模型/预设,不安装工具,也不自动提交。提示词遵循配置的提示词语言,要求智能体用自己已有的工具检查改动、提出提交分组,并在暂存/提交前等待明确批准。这是一次普通的智能体对话,不是插件那套绑定修订/摘要的审批与受限执行器工作流。

两个操作都要求当前主视图会话属于已注册的工作区。输入模式拒绝替换已有文本、行内引用或附件,也拒绝在非普通准入阶段的输入框里填入;请先发送或清空草稿。会话已有的能力会被保留,不会被新的工具面替换(即使当前会话本身就是专用提交智能体也一样)。

前置条件

- 有默认模型,或显式指定一个。 专用会话会驱动一次真实的智能体回合。在 cordis 行里配置 agentOptions,或者确保配置档已选中默认模型;否则第一回合会在提示词组装阶段就结束,报 prompt variable "{{model}}" has no value。
- 可写的数据目录。 默认是 $DSH_HOME/git-commit-agent(会遵循 DSH_HOME;回退到 ~/.dsh/git-commit-agent)。如果该路径不可写,就把 config.dataDir 指向别处——不可用的路径会在创建任何会话之前大声失败,报 DATA_DIR_UNAVAILABLE。
- 会话持久化,如果你希望重启后 resume 仍然可用。

安装

dsh plugin --profile <name> add dsh-git-commit-agent

这个 bundle 通过 package.json 和 cordis.patch.yml 里的 dsh.bundle.patch 自行挂载。可选的 config 块(dataDir、lockDir、agentOptions)见该文件。

插件挂载时带 inject: ['tools']。智能体注册表按需使用;在没有注册表的主机上调用专用会话 API 会报出清晰的 INTERNAL 错误,而不是静默降级。

智能体预设注册

在带智能体预设注册表(agentPresets 服务)的主机上,插件会在 apply() 里把自己注册为 git-commit 智能体预设:

ctx.agentPresets.register({
id: 'git-commit', // COMMIT_AGENT_PRESET_ID
name: 'Git Commit Agent',
order: 30,
plugins: [{ id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' }],
})

声明式的 @deepseek-ai/dsh-agent-preset 插件行(PresetDefinition)包装的就是同一套 API,只是这里从插件自身的挂载点调用,这样定义就不会和本插件的代码脱节。它能带来什么:

- 预设会作为卡片出现在 Settings → Agent presets(ui-agent-preset 的 AgentPresetSection)里:可查看(其 composition YAML),也可选为默认——客户端零改动;
- 专用会话会绑定该预设:create/resume 的 setup 调用 agentPresets.mount(agentCtx, 'git-commit'),会话头部记录 agentPreset: git-commit,与 DSH 的 webhook 会话创建器完全一致。

composition 只提供官方原生文件系统工具。Git 能力来自本插件在主机层面的安装(agent/created + session-git-commit- 前缀 + restrict/guard),所以预设不能把 dsh-git-commit-agent 声明为 composition 行——那会在预设作用域内挂载第二个插件实例(第二个服务、共享存储、竞态),并且无法通过注册表的根领域服务泄漏审计,卡片会被标记为损坏。

注册通过 Cordis 的 ctx.inject(['agentPresets'], callback) 等待注册表;patch 行的顺序并不保证服务已就绪。依赖子项负责注销,包括注册 promise 仍在等待时的清理。没有 inject 的最小嵌入方仍保留一次性查找的回退路径。注册成功会记录 dsh-git-commit-agent: preset git-commit registered。

当前限制: 为普通会话选择该预设,并不会安装 Git 专用工具或专用授权作用域:那项安装仍然要求专用会话 id 前缀。普通会话在正常主机策略下拿到的是该预设的原生文件系统工具。不要把这个条目当作完整、可独立使用的默认智能体预设。未来的 composition 条目必须按预设作用域提供工具和提示词,而不是按会话 id。

提示词语言偏好

两条会话入口路径(GitLens 按钮和宿主 startDedicatedSession API)都从同一个持久化偏好里选一种语言,用来预置提示词:

- 插件在自己的配置档条目上声明一个易变设置字段 promptLanguage(zh | en | follow-ui)——这是语言环境偏好模板;预设卡片的 Configure 按钮通过 configForms 写入,无需自定义持久化;
- follow-ui 交给当前 UI 语言环境决定;固定值优先;
- 宿主平面(自身没有 UI 语言环境,比如业务 API)默认 en;客户端平面只有在既没有偏好也没有 UI 语言环境时,才回退到历史上的中文默认值;
- 语言在会话准入时解析,并冻结进第一条提示词;改设置永远不会重写正在运行的会话文本,客户端 UI 文案、审批文案和提示词语言始终是三个独立的文本平面。
- 提示词本身还不是全部:系统提示词和第一条用户消息都要求智能体用这五个工具驱动整个工作流(绝不猜测状态,也绝不在没有 commit_agent_prepare_plan 的情况下描述计划),并且要求每一条面向用户的消息——分析、计划说明、建议的提交信息、进度报告——都用提示词的语言呈现(zh 用中文,en 用英文),这样会话读起来就是所选语言,尽管第三个平面(按钮标签、审批文案)保留各自的翻译。

实现:src/config.ts 负责共享解析器,src/host/session.ts 构建两种语言(默认 en),client/client.js 镜像同样的规则。两个字段都通过预设卡片的 Configure 按钮(settings.agentPreset.card.action 槽位)变为可编辑。

默认 LLM 模型偏好

两条会话入口路径(GitLens 按钮和宿主 startDedicatedSession API)都能固定专用会话跑在哪个 LLM 模型上,从宿主的模型列表里选(就是设置模型页面渲染的那份按服务商分组的目录):

- 插件在自己的配置档条目上声明一个易变设置字段 defaultModel({ provider, model });预设卡片的 Configure 对话框渲染一个选择器,选项来自 remote.session.modelCatalog(),并通过 configForms 写入精确路由;
- 没有默认模型也是一种有效偏好:选择器的第一个选项是「跟随宿主默认(不指定)」,选中即清空该字段——随后生效的是部署 agentOptions 行或宿主默认模型;
- 会话准入时的优先级:存储的 defaultModel(用户在 Configure 对话框里的显式选择)→ 部署 agentOptions 行 → 宿主默认。存储的路由只覆盖该行的 provider/model;其他行选项(reasoningEffort、maxTokens)保留;
- 按钮路径在会话创建后立即应用存储的路由通过 remote.session.selectModel —— 与 composer 模型席位安装的是同一份按会话持久化的选择 —— 因此首个提示词的请求头就用它构建;host API 路径把它作为 agentOptions 传入;
- 远程界面缺失,或某条路由从目录中消失时,静默降级到 host 默认值:模型固定绝不会阻塞会话启动。

实现:schema 字段与准入时的解析位于 src/index.ts(COMMIT_AGENT_DEFAULT_MODEL_FIELD、resolveAgentOptions),src/config.ts 负责容错校验器,client/client.js 负责目录选择器和 selectModel 的应用。

动态原生编辑(固定工具面)

Commit 会话从首次模型请求起就声明 Git 工具以及原生 read 和 edit。commit_agent_request_edit 改变的是 host 持有的授权,不是工具 schema 或系统提示词,从而为服务商提示词缓存保留不变的请求前缀(实际是否命中缓存取决于服务商)。

当规划暴露出必须修正的问题时:

1. 检查绑定的工作树。
2. 调用 commit_agent_request_edit,传入精确的、相对于仓库的现有文件 paths 和 reason;host 通过其审批服务询问用户。
3. 在 edit 之前先用原生 read,即使 commit_agent_read_files 已经读过该文件。只有明确获批的文件才能编辑。
4. 再次检查,准备新的规划修订版,并获取新的 commit 审批。

action=status 列出授权;action=revoke 清除授权。授权是临时的,作用域限于当前智能体会话,重启/销毁后绝不恢复。编辑授权绝不等于批准 Git commit。一次获授权的编辑尝试会使先前的 ready/draft 规划及其审批失效,保守起见,即使编辑失败或被取消也一样。原生操作与 Git 执行共享同一份进程内工作树租约,持有至异步完成。

本插件复用 host 的原生工具;它不实现编辑器,也不绕过文件系统策略。部署必须提供继承的原生 read 和 edit、受限文件系统服务商,以及 fs-observation-policy。原生编辑仍受这些 host 策略约束。工具缺失会导致安装失败关闭;缺少作用域内的执行拦截会阻止编辑授权。不新增任何 shell、write、网络或委派能力。

原生读取和编辑都会拒绝工作树之外的路径、Git 元数据、敏感凭据文件、符号链接以及非普通/缺失的文件。权限提升参数会被拒绝。校验在派发时重复执行;进程内租约不会锁定外部编辑器或其他进程,路径检查与 host 的沙箱隔离也不是内核级安全边界;对抗性的并发文件系统路径替换不在本威胁模型范围内。

暴露给专用智能体的工具

| 工具 | 用途 |
| commit_agent_inspect | 唯一的状态工具。mode=status(默认:HEAD、分支、索引状态、索引规则、进行中的操作、每一处待提交改动及其内容寻址的 changeId、已有的计划修订)/ mode=recent(提交主题,用于参考风格)/ mode=reconcile(崩溃或执行失败后,把某个计划修订与真实历史对上) |
| commit_agent_diff | 唯一的 diff 读取器:完整工作区 diff、单个改动的 diff,或某个计划修订将引入的逐提交精确 diff —— 可按单个提交(commitId)和单个文件(path)过滤,这样大文件能完整读取,而整份计划预览仍有上限 |
| commit_agent_read_files | 唯一的文件读取器:读取指定仓库路径的内容,有长度上限;密钥和二进制文件会被排除,并给出原因 |
| commit_agent_prepare_plan | 准备一份新的不可变计划修订;宿主负责校验、计算每个预期树、返回预览 |
| commit_agent_apply_plan | 把某份修订提交到人工的计划审核面板,批准后执行;宿主会核验批准、摘要、快照和暂存树 |
| commit_agent_request_edit | 为确切的已有文件申请用户授权、列出当前授权,或撤销授权 |
| 原生 read / edit | 宿主提供的文件观察与字面编辑;在存在匹配的用户授权之前,edit 处于锁定状态 |

没有 shell,不能任意调用 git,没有 cwd,不能写文件,不能联网,也没有委派工具。

审批如何运作

模型可以提议、预览、解释 —— 但它永远不能批准。审批走宿主的标准审批服务(ctx.approval.request()),所以用户通过宿主自己的协议作答。请求把简短问题作为 reason 传入,把本地化的问题文案作为 displayReason,把该计划修订的 Markdown 文档作为 detail。支持审批 Markdown 详情的宿主,会用共享的安全 Markdown 渲染器在 ApprovalPanel 中渲染它;较旧的宿主只显示问题,prepare 卡片仍留在会话记录里。文档按提交列出冻结的文件路径(状态 + 层)、--numstat 计数和 base→expected 树;在审核详情功能出现之前存储的修订,会回退为改动 id,并附上明确说明。commit_agent_apply_plan 提交的是一份确切的修订;只有 allowed-once 会授予批准,更新的修订会撤销较旧的批准。只有做出决定之后,宿主才执行 —— 同一次调用返回执行结果。approvedBy 记录为 user:approval —— 这是审计标签,不是密码学身份:DSH 没有经过认证的进程内用户身份,任何进程内插件都能直接调用业务 API 的 approvePlan。

试一试

从命令面板或 GitLens 启动。 安装了 commandPalette 后,搜索 在当前工作区启动 Commit Agent / Start Commit Agent in Current Workspace (命令 ID dsh-git-commit-agent.start-current-workspace)。该条目遵循 CMD 描述符协议:命令和它的 Git 提交助手 / Git Commit Agent 插件标签(稳定标签 ID dsh-git-commit-agent)各自拥有独立的 localizedTitles,一个 git 身份图标,以及一个用于创建规划会话的 create 操作图标。无论当前 locale 是什么,两种语言都可搜索;命令面板负责双语显示和模糊匹配。单语言的 title 仍作为旧版命令面板的动态回退。

它从公开的 session 和 workspace 存储中解析出拥有当前主视图会话的 workspace;未分组会话或没有当前会话时,命令会被禁用。该操作会重新检查目标,用存储的模型/语言偏好创建同一个专用会话,打开它,并注入/提交规划提示词。它不会从命令面板的调用上下文、DOM 焦点或 cwd 推断 workspace,也不会预先检查 Git 状态:专用智能体会检查仓库状态。注册通过 ctx.inject(['commandPalette']) 可选进行;子 effect 负责命令/订阅的释放,存储变更会刷新命令的可用性。创建前取消会阻止创建;创建后取消不会移除会话,但会尽可能停止后续的导航/提交。没有新增命令面板运行时导入、必需的加载依赖或新快捷键。此集成有自动化客户端测试,没有实时浏览器验证。

在 GitLens 中,有未提交变更时,点击提交行里的 commit-node 图标(在内置 Commit 按钮旁边;其 tooltip 显示 在新会话中规划并提交这些变更)。插件会在选中的 worktree 中打开新会话,注入规划请求并提交它。

只要 git status 显示任何变更——已暂存、未暂存或未跟踪——该条目就可用。索引为空时,规划会在提交前自行暂存工作区变更(按 PLAN §7,索引为空模式);你永远不必先 git add。

普通聊天会话看不到提交工具,也无法通过请求启动此工作流。这五个工具只安装到一个智能体作用域,即按钮创建的会话——绝不会安装到每个会话。

按会话注入工具

DSH 按智能体作用域解析工具面:在插件自身上下文上调用 register(),对每个会话可见;而在智能体的 agent.ctx 上调用 register(),只对该智能体可见。全局挂载这五个工具是最初的捷径,它把它们放进了每个会话的模型工具面。

取而代之,宿主侧在 agent/created 时安装它们,只针对会话 id 带有保留前缀 session-git-commit- 的智能体(src/host/tools.ts 中的 COMMIT_AGENT_SESSION_PREFIX)。由于 GitLens 按钮无法从浏览器调用 AgentRegistry.create,它通过公开的 ISessions.create({ sessionId }) 契约预分配这样一个 id;这样创建的会话通过业务 API 的 startDedicatedSession 会从同一个 newSessionId 默认值拿到一个。安装只在目标作用域内添加:

- 五个工具定义,
- restrict({ allow: [] }),它会隐藏整个继承来的表面(全局层加上每个预设/常驻祖先层),只留下作用域自己的五个可见,
- 末尾的 guard,它按名称把五个加入允许列表,并且失败时关闭。

agent/created 在注册期间触发——早于 agent/session-start 和第一次提示词组装——对 create 和 resume 都是如此,所以第一次模型请求已经运行在关闭的表面上。

原生 Git More 操作槽位

Git 命令的 More 菜单包含 规划并提交(plan and commit),右侧带一个 agent 副标题徽章。它的 MenuItemButton 子元素使用公开的标签布局:span[data-better-sidebar-git-action-label] 包含 span[data-better-sidebar-git-action-title](现有标题)和 span[data-better-sidebar-git-action-subtitle](agent)。better-sidebar 负责共享的截断标题 / 尾部 token 徽章样式;这个插件不添加任何布局 CSS 或运行时导入,也不误用 shortcut 字段。没有那套共享样式的旧宿主仍然会渲染两个文本 span。客户端使用 ctx.slots.inject('betterSidebar.git.actions', () => ctx.slots.register({ name: 'betterSidebar.git.actions', id: 'dsh-git-commit-agent:plan-and-commit', order: 50 }, Component))。这是 better-sidebar 的列表 / 根协议,不是旧的 registerGitCommitAction 注册表;同一个操作绝不会注册两次。

这项贡献使用共享的 MenuItemButton 和实时的 owner props(GitActionSlotProps,由 dsh-better-sidebar/client/service 公开导出)。它的目标是槽位 owner 的 scope / worktree / repoRoot,绝不是某个 header session id。当 owner 的 busy 为 true 或没有任何变更时,它会被禁用;已暂存、未暂存和未跟踪的变更都仍然支持。选中会先关闭 More 再启动,并通过 props.runAction 运行会话创建,后者负责互斥、错误报告和成功后的刷新,即使菜单项卸载也一样。这个操作不会打开任何对话框;预设配置对话框与之无关,仍然在 Git 菜单之外。

现有的保留会话前缀、工作区分组、存储的默认模型、提示词语言选择和自动提交规划提示词都保持不变。客户端仍然是一个手写的 lazy-CJS bundle:它不在运行时导入 better-sidebar(也不会仅仅为了它的 props 类型而添加一个仅 TypeScript 的依赖)。平台在运行时提供 @deepseek-ai/dsh-client-ui-primitives。槽位注入管理 owner 到达、拆除和重新注册。在没有这个槽位的旧 better-sidebar 版本上,Git 菜单项不可用;插件的工具、规划视图和预设配置仍然可用。There 这里刻意不做任何旧版回退,否则会重复生成新的菜单项。

会话分组(工作区归属)

DSH 的侧边栏按工作区归属给会话分组,而不是按工作目录:树会查找 sessionIds 中包含该会话的工作区,找不到就把它丢进未分组(Ungrouped)桶(ui-workspace/src/client/tree.ts)。会话只有在通过某个工作区创建时才会加入该工作区——宿主在 SessionCommandController.create 里只对 { workspaceId } 分支做挂载;只带 { cwd } 的会话永远不是成员,哪怕它的 cwd 就是已注册的工作区。

所以这个插件会拿目标目录去匹配宿主的 workspaces 列表,匹配上就传 workspaceId,这样规划会话就出现在原工作区下。如果目标是链接的 git worktree,而它本身不是已注册的工作区,cwd 就无法挂载(宿主要求会话 cwd 的 realpath 与工作区路径相等),会话按设计保持未分组;把那个 worktree 注册成工作区就能分组。宿主侧的 startDedicatedSession API 也是这么做的,走 ctx.workspaceRegistry / workspace.attachSession。

两种情况下流程都一样:

1. 智能体调用 commit_agent_inspect(mode=status),读取真实差异(commit_agent_diff)和文件内容(commit_agent_read_files),然后准备计划(commit_agent_prepare_plan);
2. 计划以记录卡片的形式出现:一份摘要(revision、提交数、文件数、每个提交的 +/-),可展开的逐提交文件列表,带 git/VSCode 状态符号(M/A/D/R/C/T/U/? + 已暂存/未暂存层),排除项和警告,一个revision 增量(「自 rev N 以来改了什么」:文件在提交间移动、消息改写、新增排除项),以及一个折叠的技术区(plan id、digest、trees)。每个提交在侧边栏的 DiffPane 里以带版本的提议差异打开(tab id 带 revision,所以两个 revision 永远不会共用一个 tab;seed 带 truncated 和一个 sourceRef 快照标签,侧边栏把它当只读元数据显示;文件行跳到侧边栏编辑器里的实时文件,差异行则在对应行打开它——补丁本身仍是只读快照;统计和文件列表始终来自未截断的 --numstat);
3. 智能体调用 commit_agent_apply_plan,你在宿主的 ApprovalPanel 里做决定——它的 Markdown 文档列出每个文件,带 porcelain-XY 状态符号(第一格已暂存,第二格未暂存;?? 未跟踪)、stat 和 trees,全部来自计划冻结的审查详情,绝不实时读取工作树;
4. 只有你批准之后宿主才执行——同一次调用随后返回执行结果(提交已落地,或说明为什么停下)。

客户端那一半是一个手写的 lazy-CJS 文件(client/client.js):没有打包器、没有 JSX、没有 CSS modules,也没有 typert Remote —— 按钮只用公开的客户端 API,所以不会给宿主内部的符号表增加任何依赖。

业务 API

import { apply as mount } from 'dsh-git-commit-agent'

const plugin = mount(ctx, { dataDir: '~/.dsh/git-commit-agent' })
plugin.api.toolNames // the five tool names
plugin.api.systemPrompt()
await plugin.api.startDedicatedSession({ workspacePath, sourceSessionId, userConstraints })

await plugin.api.openTask({ sourceSessionId, agentSessionId: null, workspaceRoot })
await plugin.api.approvePlan({ taskId, planId, revision, planDigest, requestId, approvedBy })
await plugin.api.executePlan({ taskId, planId, revision })
plugin.api.cancel(taskId)
await plugin.api.reconcile(taskId, planId, revision)

approvePlan must only be called from a user-initiated surface (the plan card),
never from the model. The model's tools cannot reach it.

Architecture

client/client.js hand-written lazy-CJS browser half (button + plan cards)
src/core/ host-agnostic, fully testable
errors.ts coded error taxonomy
types.ts task / snapshot / plan / execution domain model
git/runner.ts restricted git runner: fixed args, scrubbed env, literal pathspecs
git/snapshot.ts status parsing, per-change content digests, consistency re-reads
plan/validate.ts coverage, duplicates, cycles, v1 refusals
plan/digest.ts canonical approval digest
plan/materialize.ts temporary-index materialisation → exact expected trees
plan/lock.ts in-process + on-disk worktree lock
plan/executor.ts approved execution, tree verification, reconciliation
store/store.ts atomic, append-only task/plan/execution store
service.ts orchestration used by the tools
src/host/ structural mirrors of the verified DSH contract
types.ts host service faces (no compile-time host dependency)
tools.ts the five ToolDefinition objects + per-session scope install
session.ts dedicated normal session, prompts
src/index.ts Cordis plugin: apply(ctx, config) + business API

Nothing under src/core/ imports a host package, which is why the whole engine
is testable with plain Node.

Preview is exact, not approximate

For every planned commit the host computes the tree it must produce, starting
from HEAD (or from the user's own index when staged content is being reused) in a
temporary index. The preview diff is the real diff between the parent tree
and that expected tree. Execution re-computes the same trees immediately before
committing and refuses to proceed if they differ. This is why a plan can never
claim one thing and commit another.

Safety highlights

- approval is bound to (revision, content digest); publishing a new revision
revokes every earlier approval;
- the real index tree is compared to the approved tree before git commit;
- HEAD is re-read after every attempt, including failures and cancellations, so
a commit that landed is recorded, never retried;
- --no-verify, --amend, reset, push and history rewriting are never used;
- existing staged content is respected and never split;
- hooks, identity and signing behave exactly as the user configured them;
- secret-looking files are not sent to the model;
- all plugin state is written outside the target repository.

Full model: docs/SECURITY.md.

Development

npm install
npm run verify # typecheck + build + 89 tests against real isolated git repos
npm run build

Tests never touch a user repository: every fixture is a fresh temp repo with
pinned identity, signing disabled and its own hooks path.

Component → source locator (dev-only)

@havocrao/dsh-code-finder is wired in as a dev dependency so that holding
Opt+Shift and hovering a CommitAction / PlanCard / ApprovalCard
element in the DSH web UI shows client/client.js:line:col.

npm run inject:dev # NODE_ENV=development dcf instrument client --write
npm run inject:revert # restore the tracked file when you are done

This project has no bundler config, so — unlike the tsdown/vite plugins — the elements are located by the standalone instrument entry applied to the hand-written client bundle. The overlay and the /code-finder/api/ routes come from the host: cordis.patch.yml here is a plugin-type patch (insert-only), so dcf init intentionally adds no mount row (the host layer owns it).

Two things to keep in mind:

- client/client.js is tracked source, not a build artifact. Injection
reprints it (+1063/−219 lines) and prepack only runs tsc, so it will
not* be restored for you — run npm run inject:revert before committing or npm pack. Injection is idempotent and reverting is byte-exact.
- Name-level search needs a root. Precise coordinates come from the
injection; the declaration-name fallback additionally requires this repo's
client/ directory in the profile roots: dcf roots add web "$PWD/client"
(idempotent). Roots are read at boot, so restart the host after changing them
(dsh web stop && dsh web).

See docs/README.md in the DSH-code-finder repo
("零构建插件" and "C. cordis 纯 runtime") for the full contract.

Not in this version

No push, no history rewriting, no automatic rollback of completed commits, no
--no-verify, no automatic code fixes, no hunk-level splitting, no second chat
UI, and no modification of the official DSH checkout or its generated artifacts.
预览分享卡片长什么样 ↓havoc-rao/dsh-git-commit-agent
分享 · DeepSeek Harness Hub
havoc-rao/dsh-git-commit-agent

读取仓库真实状态,规划提交并等你批准后执行

回到顶部