dsh-kit
English | 中文
Eight plugins for DeepSeek Harness, aimed at the quiet failures a coding agent produces when nobody is checking its work.
Four of them cover gaps I could not find filled by anything else. Four have good existing alternatives, named below — the DSH plugin ecosystem is enormous (13,000+ repos on the dsh-plugin topic; 8,100 in the plugin radar catalogue), and pretending otherwise would waste your time.
Everything here is a plugin row on a documented extension point. Nothing patches the harness.
Install
dsh plugin --profile web add github:FoyonaCZY/dsh-kit
dsh --profile web
No build step — plain ESM JavaScript, so a git install needs no prepare script and no allowBuilds grant in your profile's pnpm-workspace.yaml (which is, after all, permission to execute a package's code on your machine at install time). The only runtime dependency is @deepseek-ai/schemastery, which the harness already uses for plugin config.
Pin a commit for the usual supply-chain guarantee:
dsh plugin --profile web add github:FoyonaCZY/dsh-kit#<sha>
What you get
| Plugin | What it does | Extension point | Ecosystem |
|---|---|---|---|
autoformat |
Runs the project's own formatter on files the agent writes | tools/post-execute |
no alternative found |
artifact-guard |
Stops the agent hand-editing lockfiles, build output, generated code | tools/pre-execute |
no alternative found |
env-drift |
Reports env vars the agent added but never documented | tools/post-execute |
no alternative found |
verify |
Re-opens a turn whose project check fails, before it reports done | agent/turn-stopping |
no alternative found |
checkpoint |
/rewind the workspace to before any file-modifying call |
tools/pre-execute + ctx.commands |
alternatives ↓ |
secret-guard |
Redacts credentials from tool output; gates credential files | tools/pre-execute, tools/post-execute |
alternatives ↓ |
git-context |
Live branch, working tree, recent commits in the prompt | systemPrompt.context() |
alternatives ↓ |
notify |
Desktop ping on turn end and approval requests | agent/status, tools/pre-execute |
alternatives ↓ |
Each is an independent row. Disable any from your profile's cordis.patch.yml:
- id: dsh-kit-notify
disabled: true
The four that fill real gaps
autoformat
The project's formatter, on every file the agent writes. Otherwise the diff you review is half real change and half whitespace.
The rule that keeps it from being annoying: a formatter only runs if the project already has it. Every rule carries detect paths, so a repository with no Prettier config and no Prettier binary gets no Prettier run — no surprise reformatting, no npx reaching for the network, zero cost in projects that never opted in. Prettier, gofmt, rustfmt, ruff, and black are detected out of the box.
When a formatter rejects the file, that is usually the fastest possible signal that the agent just wrote a syntax error, so the output is attached to the tool result.
- id: dsh-kit-autoformat
config:
formatters:
- extensions: ['.ts', '.tsx']
command: npx --no-install prettier --write {file}
detect: ['.prettierrc', 'node_modules/.bin/prettier']
- extensions: ['.sql']
command: sqlfluff fix --force {file}
detect: [] # empty detect = always run
timeoutMs: 15000
reportFailures: true
artifact-guard
Stop the agent hand-editing files a machine wrote.
An agent has no reliable way to tell a source file from a derived one. So it edits pnpm-lock.yaml to "add" a dependency, patches something under dist/, or tweaks generated protobuf bindings — and the change is either silently reverted by the next install or codegen run, or it corrupts the artifact and breaks the build somewhere far from the edit.
Two signals, because neither is enough alone:
- Path rules catch the conventional artifacts — lockfiles (14 kinds), build output, vendored trees, generated bindings.
- A content marker catches everything else.
@generated,DO NOT EDIT, and Go'sCode generated by … DO NOT EDIT.are near-universal, so a project's own generated files announce themselves without anyone listing them. Only the first 20 lines count, so a source file that merely discusses codegen is unaffected.
The refusal says what to do instead — "change the manifest and let the package manager regenerate it" — because "you may not edit this" alone just makes an agent try again.
- id: dsh-kit-artifact-guard
config:
onArtifact: ask # ask | deny | allow
detectMarkers: true
allowPaths: [] # escape hatch for a hand-maintained dist/ file
env-drift
Catch environment variables the agent added but never documented.
The agent writes process.env.STRIPE_SECRET_KEY, it works on the machine that already exports it, and .env.example never learns about it. The next person to clone the repo fails at runtime with an error that says nothing about a missing template entry.
After a successful write the file is read back, its env accesses extracted, and anything missing from the template is handed to the agent as context — while it still remembers why the variable exists.
Covers JS/TS (including import.meta.env), Python, Go, Rust, Java/Kotlin, Ruby, PHP, and C#. Bare shell $FOO is deliberately ignored: it is indistinguishable from an ordinary local. Runtime-supplied variables (NODE_ENV, CI, PATH, …) are filtered, each variable is reported once per session, and a project with no template gets nothing — it has not adopted the convention, and inventing one for it would be presumptuous.
- id: dsh-kit-env-drift
config:
templates: ['.env.example', '.env.sample', '.env.template']
ignore: ['NODE_ENV', 'CI', 'PATH']
verify
Make "done" mean "still compiles".
An agent's most expensive failure is not a wrong edit — it is a wrong edit reported as finished, because the cost lands on the human who reads the summary and believes it.
agent/turn-stopping is awaited before the turn boundary commits, and a listener that objects can steer another step. So when a turn that edited files is about to end, the project's check runs. Fail → the output goes back and the agent keeps working. Pass → the turn ends as it would have.
There is a
dsh-test-runnerin the ecosystem, but it is a tool the model chooses to call. This is an automatic gate. The difference matters exactly when the model doesn't bother to call it — which is the failure being addressed.
Auto-detects a typecheck command by default: a typecheck/type-check/tsc script in package.json (package manager inferred from the lockfile), else a local tsc --noEmit. Test suites are never auto-detected — too slow for every turn boundary. maxRounds bounds the loop; past it the turn closes even while failing, and the last message tells the agent to report the failure honestly rather than claim success.
A check naming a binary that is not on PATH is skipped with a log line rather than reported as a failure — resolved by walking PATH directly, because a shell reports a missing command in the machine's own language (this bit me during development: a Chinese-locale cmd.exe emits GBK-encoded text that no English pattern matches).
- id: dsh-kit-verify
config:
checks:
- name: typecheck
command: pnpm typecheck
- name: unit tests
command: pnpm test -- --run
autoDetect: true # ignored when `checks` is non-empty
maxRounds: 2
The four with existing alternatives
Keep these if you want one install for the whole set; go to the alternative if you want the best-in-class version.
| Mine | Consider instead | Why |
|---|---|---|
checkpoint |
dsh-checkpoint-rewind | Honestly better: git stash create/commit-tree snapshots instead of a blob directory, covers bash/pwsh/terminal_send (mine can't), plus preview/diff subcommands and session+config state. |
secret-guard |
JohnXu22786/secret-guard, hol-guard | Same two extension points, more rules, plus HMAC-fingerprint inspection tools. Mine's one edge: redactValue for PTC deployments, where run_code programs read the canonical value that content-only redaction leaves exposed. |
git-context |
qtjg/dsh-plugin-git-context, dsh-gitflow | Thinly covered but covered. Mine registers per-agent on agent.ctx, so multi-workspace deployments get each agent's own repo rather than a shared guess. |
notify |
dsh-notification, dsh-notification-center | Heavily covered (150+ plugins touch notifications). Theirs integrate with the Web UI settings; mine is native-OS with no UI and also fires a terminal bell, which reaches you over SSH. |
/rewind and /cost-style features are the most crowded corners of this ecosystem. If you only want what is genuinely missing, disable those four rows and keep the first four.
Design notes
No coupling to harness internals. The only runtime import from the DSH ecosystem is @deepseek-ai/schemastery. User messages are built as the documented plain object (id, role, content, source) rather than through @deepseek-ai/dsh-llm, so a core version bump in a fast-moving preview does not break the kit.
Failures stay contained. A checkpoint that cannot be written, a notifier that does not exist, a formatter that is not installed, git in a non-repository — each degrades to a log line. None can cost you the tool call you actually asked for.
Everything external is bounded. One subprocess helper enforces a timeout, an output cap, and cancellation — and kills the whole process tree on Windows, where killing a shell otherwise orphans its children and the "timeout" never actually stops anything.
Tests
npm install && npm test
180 tests, no network, no harness required. Pure logic (redaction rules, glob matching, git porcelain parsing, restore planning, env extraction, artifact classification) is tested directly; each plugin is then driven end to end against a fake context and a real temporary filesystem — a checkpoint really restores a file, a failing check really steers the turn, a formatter really rewrites what was written, the guard really reads a generated-file header off disk.
Compatibility
Built against DeepSeek Harness at main, September 2026. DSH is in developer preview and its docs warn of compatibility-breaking changes, so pin a commit. Every extension point used here is documented in extension-cookbook.md.
License
MIT
No comments yet. Be the first to write one.