Bundle
dsh-git-seam
Structured git tools for DeepSeek Harness agents: deterministic porcelain parsing (Chinese/UTF-8 paths, Windows) plus a diff-before-commit policy gate that refuses blind commits.
- Source
- qt-11564
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-git-seam
Structured git tools for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) agents — with a **diff-before-commit gate** that refuses blind commits and **deterministic porcelain parsing** that handles Chinese/UTF-8 paths, spaces, and CRLF stably on Windows and Linux.
The agent gets six tools — `git_status`, `git_diff`, `git_log`, `git_show`, `git_branch`, `git_commit` — plus a policy layer that enforces review-before-commit discipline instead of relying on the model to remember it.
## Why not just `git` in bash?
An agent can already run `git` through the bash tool. What bash cannot give you:
- **A guarantee, not a suggestion.** `git_commit` is refused unless the changes were actually reviewed with `git_diff` first — same turn, and covering exactly the files the commit would include. Files staged after the review (e.g. via a shell `git add`) are caught and named. Bash cannot enforce this; it can only hope.
- **Deterministic parsing.** Every command runs with `LC_ALL=C`, `GIT_TERMINAL_PROMPT=0`, `core.quotepath=false`, and machine formats (`status --porcelain=v1 -z`, `diff --numstat`, `log --format=…`). Chinese filenames, BOM'd UTF-8 content, spaces, renames, and CRLF are parsed by code, not by the model's luck.
- **Approval you can actually judge.** `git_commit` asks for approval with a real per-file plan: `path (+N/-M)`, untracked paths listed as `(new file)`. Bash escalation shows a command string, not a change list.
- **Loud, actionable failures.** Not-a-repo, missing `user.email`, and `../` traversal return clear errors with the next step, not raw stderr.
## Install
```sh
# from GitHub (recommended)
dsh plugin --profile web add github:qt-11564/dsh-git-seam
# or from npm (once published)
dsh plugin --profile web add dsh-git-seam
```
Then restart the profile. Verify with `dsh --profile web --dump-config` (look for the `git-seam` row) or just start a conversation — the six `git_*` tools appear automatically.
## Configuration
```yaml
- id: git-seam
name: dsh-git-seam # or ./index.mjs when installed from a local checkout
config:
maxBytes: 32768 # output byte budget; truncation notice + spill path beyond it
requireDiffBeforeCommit: true # the diff-before-commit gate (set false to disable)
requireCommitApproval: true # route git_commit through the approval service
timeoutMs: 30000 # per-command timeout; longer commands are killed
```
## Tools
| Tool | Input | Output |
|---|---|---|
| `git_status` | `cwd?` | branch, ahead/behind, staged/unstaged/untracked lists, clean flag |
| `git_diff` | `spec?` (worktree / `--staged` / `A..B`), `path?` | per-file +N/−M stats + truncated unified diff |
| `git_log` | `range?`, `path?` | commits: hash, author, date, subject |
| `git_show` | `ref` (required), `path?` | commit metadata + stats + truncated diff |
| `git_branch` | `cwd?` | current branch + all local branches |
| `git_commit` | `message` (required), `paths?` | new hash + summary; gated and approval-routed |
## How the gate works
`git_commit` is checked on `tools/pre-execute`:
1. The last git action must be a **successful** `git_diff` in the **same turn** (a diff from a previous turn does not count; a failed diff does not count).
2. Every file the commit would include must be inside the reviewed diff's **covered set**: the staged set for path-less commits, the given paths for path commits.
3. Untracked paths are **exempt** — `git diff` structurally cannot show them; their review is read-based (fs tools), and the approval plan still lists them.
4. A successful commit invalidates the review; the next commit needs a fresh diff.
Denials name the offending files and tell the model what to do (`run git_diff again`). The gate is an ordinary `tools/pre-execute` listener — it unloads with the plugin and composes with the rest of the policy chain.
## Known Limitations and Deferred Work
- **No destructive history operations** — `reset`, `checkout`, `revert`, `merge`, `rebase` are out of scope; git_commit is the only mutating tool.
- **No** `stash`, remote operations (`push`/`pull`/`fetch`), or submodules.
- **No** `blame`, GPG signing, or LFS.
- **Remote providers** (GitHub/GitLab REST) are deferred — local git only.
- The gate verifies *file coverage*, not content freshness: editing a file after its diff and before committing is not detected (a deliberate trade-off to keep the gate cheap and predictable).
- A commit can still be made by bypassing the tools through bash; the gate protects the tool path, not the sandbox.
## Development
```sh
npm install # installs tsx for the selftest
npm test # 13-case selftest: gate semantics, approval plan, full commit flow
```
The selftest drives the real plugin module with a mocked ctx against a real git fixture repo (real porcelain output). For the test's `@deepseek-ai/dsh-tools` import to resolve without publishing, either `npm i @deepseek-ai/dsh-tools` or drop a dev shim under `node_modules/@deepseek-ai/dsh-tools` re-exporting your harness checkout's `packages/core/tools/src/schema.ts`.
## License
MIT
Install
dsh plugin --profile web add github:qt-11564/dsh-git-seam#d8cd4ea7d91fb8cdd6d53ed45555783cac1ab182
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-git-seam from the hub