DSH-EZ Commit Plugin
August 22, 2026 · View on GitHub
One-Click Commit, a static dual-face DSH (DeepSeek Harness) plugin: after install and a profile restart, every web session header automatically shows a One-Click Commit button. The current session model splits your workspace's git changes into multiple commits by business intent (Conventional Commits); once you review the plan, the batches are executed one by one. If the model rules the changes as "environment noise", it only notifies you — no git write operations are performed.
English | 简体中文
Table of Contents
- Introduction
- Features
- Quick Start
- How It Works
- API Contract
- Security & Privacy
- Development
- Contributing
- License
Introduction
DSH-EZ Commit is a static dual-face DSH plugin (the same mechanism used by third-party skin packages such as dsh-skin-market):
- One package, two planes: the Client half (
exports "./client"+ thedsh.clientdeclaration, a browser module factory) owns the UI (button + dialogs); the Host half (themainentry) mounts a same-origin HTTP API onwebServer, collects git state, asks the model for a verdict, and executes batches. - Install and it works:
dsh plugin --profile <name> add dsh-ezcommit-plugininstalls and upgrades it (thedsh.bundlecontract, see INSTALL.en.md); after a profile restart the session header automatically shows the button. No cordis preset and nocordis_define/cordis_runare needed. - Zero runtime dependencies: only Node built-ins plus browser-native capabilities (
fetch/React.createElement);Reactcomes from the web shell's seeded baseline. - Model first: splitting and the noise verdict are made entirely by the current session model — the plugin itself never performs speculative git write operations.
Features
| # | Requirement | Implementation |
|---|---|---|
| 1 | Non-git workspace → button disabled | Host shell runs git rev-parse --is-inside-work-tree |
| 2 | Branch name shown left of the button | git symbolic-ref --short HEAD (falls back to short hash when detached) |
| 3 | Click opens a confirmation dialog | Custom modal drawn in the Client shell.overlay slot |
| 4 | Model splits commits by business intent | Host llm.stream() analyzes change facts and outputs a JSON plan |
| 5 | No changes → disabled | Disabled when the git status --porcelain count is 0 |
| 6 | Model rules "environment noise" → notify, don't commit | Verdict verdict: "noise"; zero git write ops on the Host |
Quick Start
Installation
Install with the official DSH CLI for distribution and version management (full spec in INSTALL.en.md, 中文):
# from npm (once published) — registers a profile bundle layer, versioned by pnpm
dsh plugin --profile web add dsh-ezcommit-plugin
# or from the git repository / a local checkout
dsh plugin --profile web add git+https://github.com/PenguinAndy/dsh-ezcommit-plugin.git
dsh plugin --profile web add link:/path/to/dsh-ezcommit-plugin
Then restart the profile (e.g. dsh web): the startup log prints [dsh-ezcommit-plugin] v<x.y.z> installed plus the route-mounting result; after a page refresh, every web session header shows [branch] [One-Click Commit], grayed out outside git repositories or with no changes. No in-session steps are required.
Usage
- Click One-Click Commit → a confirmation dialog shows the branch and change counts (second confirmation).
- Start Analysis → the Host collects
status / diff / untracked samples(sensitive paths are filtered — never sampled, never included in the prompt) and asks the model currently routed for this session to rule:noise→ a yellow dialog shows the reason; no git write operations are performed;commit→ a plan-review dialog lists the batches (order, message, files), the filtered sensitive files, and warnings about uncovered files.
- Execute N Commits → batch by batch:
git add --pathspec-from-file=- --(the file list is piped through stdin, naturally avoiding whitespace/special-character path escaping) +git commit -m title [-m body], returning the short hash of each batch. - On completion the button refreshes to disabled immediately; if any batch fails, execution stops (no rollback) and successful batches are still reported.
How It Works
One package, two planes: the Client half is composed into the web boot graph by dsh-client-modules (/plugins/dsh-ezcommit-plugin/client.js) and mounted in the browser via the window.__ModuleLoader__ factory format; the Host half loads in the Node process and registers the /ezcommit/api prefix routes on webServer. The Client calls the Host over same-origin HTTP. The sequence below shows one complete operation:
sequenceDiagram
participant C as Client (browser)
participant H as Host (Node process)
participant M as Current session model
Note over C: setInterval: polls every 5s
C->>H: POST /ezcommit/api/git.state {sessionId}
H-->>C: { inRepo, branch, hasChanges, changedCount }
Note over C: not a git repo / no changes → button disabled
C->>C: click "One-Click Commit" → confirmation dialog
C->>H: POST /ezcommit/api/commit.analyze {sessionId}
H->>H: collect status / diff / untracked samples<br/>truncation caps + sensitive-path filtering
H->>M: llm.stream(change facts + system prompt)
M-->>H: JSON plan { verdict, commits }
alt verdict = "noise"
H-->>C: environment noise → notification dialog (zero git writes)
else verdict = "commit"
H-->>C: batch plan + file list (user review)
C->>H: POST /ezcommit/api/commit.execute {sessionId, commits}
H->>H: batch git add + git commit<br/>stop on first failure (no rollback)
H-->>C: { results: [{ title, hash }], leftoverCount }
end
API Contract
HTTP API Overview
The Host half mounts three same-origin JSON APIs on webServer (POST only, same-origin checked; arguments and return values are lossless JSON only):
| Method | Path | Arguments | Key results |
|---|---|---|---|
git.state | POST /ezcommit/api/git.state | {sessionId} | {ok, inRepo, branch, hasChanges, changedCount, untrackedCount} |
commit.analyze | POST /ezcommit/api/commit.analyze | {sessionId} | {ok, verdict:'noise'|'commit', reason, commits:[{title,body,files}], unplanned, problems, sensitive:[path], diffTruncated, target} |
commit.execute | POST /ezcommit/api/commit.execute | {sessionId, commits} | {ok, results:[{title,hash}], leftoverCount} |
All failures return {ok:false, error:{message, code}}; non-POST → 405, unknown method → 404, cross-origin → 403.
Model Output Contract
The system prompt asks the model to output only the following JSON (the parser strips markdown code fences from the response; on parse failure it retries once):
{
"verdict": "noise",
"reason": "one-line explanation (required for noise)",
"commits": [
{ "title": "feat(scope): ...", "body": "optional multi-line body", "files": ["relative paths..."] }
]
}
filesmust be taken verbatim from the change list; missing or duplicated files go intounplannedand are surfaced as UI warnings.- Model resolution order: session-routed config (
session.requestHeader().config) →agent.options→ default model (agentDefaultModel.currentSelection()).
Security & Privacy
- The noise verdict happens before any
git add/commit; the noise path performs zero git write operations. - Sensitive path filtering:
.env*,.npmrc,.pypirc,.netrc,.git-credentials,*.pem,*.key,id_rsa*,.ssh/,.aws/,.kube/,credentials*.jsonand similar — their paths and contents are never sent to the model, never enter a plan, and can never be committed; filtered files are listed in the plan/noise dialog (displayed locally only). - Privacy boundary: after clicking "Start Analysis", the workspace
status,diffand untracked text samples (first 8 KB per file, up to 32 files) are sent to the current session model provider — sensitive paths excluded. Use it only on workspaces you are allowed to share with the model provider. - The API only accepts same-origin POSTs (Origin must match Host); cross-site calls are rejected.
- Before
commit.execute, the known file set is rebuilt from the current porcelain output — stale or invented paths are always rejected. - No
git push, no--force, no history rewriting; if a batch fails, execution stops, nothing is rolled back, and everything is reported truthfully. - Analyze/execute per sessionId have an in-process in-flight lock; duplicate clicks are rejected.
- All side effects (routes, slots, polling, styles) hang off the plugin lifecycle and are cleaned up when the bundle layer unloads.
Development
Project Structure
.
├── .github/workflows/
│ └── release.yml release workflow
├── docs/
│ ├── README.en.md English README (this file)
│ ├── INSTALL.md Installation spec (Chinese)
│ ├── INSTALL.en.md Installation spec (English)
│ ├── dsh-ez-commit-design.md Design doc (requirements → contract mapping, decisions, risks)
│ └── VERIFICATION.md On-device verification walkthrough (six requirements)
├── src/
│ ├── index.js Host half: /ezcommit/api routes + git/model logic + notice/self-check
│ └── client.js Client half: button + dialogs + styles (__ModuleLoader__ factory)
├── scripts/
│ ├── verify.mjs Offline smoke verification (no DSH runtime; real-git integration)
│ └── stage-fixture.mjs Scenario fixture: demo | noise | clean
├── cordis.patch.yml Profile bundle layer patch (the ezcommit anchor row)
├── package.json Version + dsh.bundle & dsh.client dual-face contract; zero runtime deps
├── pnpm-lock.yaml
└── LICENSE MIT
Local Verification
pnpm verify # or: npm run verify — package contract + HTTP-route integration (real git) + client factory smoke
Runtime verification: after install and a profile restart, follow the walkthrough matrix in VERIFICATION.md to verify the six requirements one by one (using node scripts/stage-fixture.mjs demo|noise|clean to prepare scenarios). The model-verdict path can be exercised with a workspace containing noise such as .DS_Store or lock files.
Related Documents
- Installation spec: INSTALL.en.md / INSTALL.md (Chinese)
- Design: dsh-ez-commit-design.md (Chinese)
- Verification: VERIFICATION.md (Chinese)
Contributing
Issues and pull requests are welcome. For PRs, please:
- Write commit messages following Conventional Commits;
- Run
pnpm verifyafter touchingsrc/; - Update this README and the
docs/when behavior changes.