dsh-kit
Quality-of-life plugins for DeepSeek Harness: workspace rewind, desktop notifications, auto-format, live git context, secret redaction, and a done-means-green verify gate.
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:FoyonaCZY/dsh-kit说明文档
阅读完整 README ↗dsh-kit
English | 中文
Six plugins that close the gap between DeepSeek Harness and a polished desktop coding agent.
DSH's core is excellent and its plugin seams are unusually well designed — but the release ships no undo, no notifications, no formatting, no git awareness, no credential filter, and nothing that checks the agent's work before it declares victory. Those are exactly the things you stop noticing in a good desktop agent because they are always there. This is that layer.
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
That is the whole installation. There is no build step — the package ships plain ESM JavaScript, so a git install needs no prepare script and no allowBuilds entry in your profile's pnpm-workspace.yaml. Its only runtime dependency is @deepseek-ai/schemastery, which the harness already uses for plugin config.
Pin a commit if you want the usual supply-chain guarantee:
dsh plugin --profile web add github:FoyonaCZY/dsh-kit#
Verify the layer landed before booting:
dsh --profile web --dump-config # look for "# == dsh-kit"
What you get
| Plugin | The gap it closes | Extension point |
|---|---|---|
checkpoint | No way to take back a file the agent rewrote | tools/pre-execute + ctx.commands |
notify | No idea the run finished, or that it is blocked on you | agent/status, tools/pre-execute |
autoformat | Edits land in the model's style, not the project's | tools/post-execute |
git-context | Agent is blind to branch and working-tree state | ctx.systemPrompt.context() |
secret-guard | Tokens in tool output ride along in every later request | tools/pre-execute, tools/post-execute |
verify | "Done" can mean "done and broken" | agent/turn-stopping |
Each is an independent row. Disable or reconfigure any one from your profile's cordis.patch.yml without touching the rest:
- id: dsh-kit-notify
disabled: true
checkpoint
/rewind for your workspace.
DSH already has dsh-session-checkpoint-policy, but that makes the session log crash-durable — it is not an undo stack. If the agent rewrites six files down a path that turned out to be wrong, the files are simply rewritten.
Before every write, edit, or str_replace_editor call, this records the target file's current bytes. Then:
/rewind list checkpoints, newest first
/rewind 12 restore the workspace to the state just before checkpoint 12
/rewind last undo the most recent file-modifying call
/rewind clear drop this session's history
Restoring is itself checkpointed, so a rewind is undoable — /rewind last right after a rewind puts everything back. Files that did not exist before are deleted rather than left behind. Content is stored by hash outside the workspace, so repeated captures of an unchanged file cost nothing and a restore never fights the repository it is restoring.
Not covered: bash. Nothing can know which files a shell command will touch before it runs.
- id: dsh-kit-checkpoint
config:
stateDir: ~/.dsh-kit/checkpoints # default
tools: [write, edit, str_replace_editor]
maxCheckpoints: 200 # retained per session
maxFileBytes: 2097152 # larger files are recorded as skipped
commandName: rewind
notify
A desktop ping when the agent finishes, or when it needs you.
Two triggers:
- the agent returns to
idleafter working longer thanminDurationMs, so the short exchanges you were watching stay silent; - the tool pipeline settles on
ask, meaning the run is blocked until you answer — regardless of which policy plugin asked.
Native on all three platforms with no dependencies: osascript on macOS, notify-send on Linux, and a WinRT toast through the in-box PowerShell identity on Windows. A terminal bell goes out too, which reaches you over SSH where no desktop notifier exists.
- id: dsh-kit-notify
config:
onIdle: true
onApprovalRequest: true
minDurationMs: 15000
bell: true
title: DeepSeek Harness
command: '' # e.g. 'terminal-notifier -title {title} -message {body}'
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 this from being annoying: a formatter only runs if the project already has it. Every rule carries detect paths, and a repository with no Prettier config and no Prettier binary gets no Prettier run — no surprise reformatting, no npx reaching for the network, no cost at all 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 and the agent sees it on its next step.
- 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
git-context
Branch, working tree, and recent commits, in the prompt.
DSH ships context providers for the clock, tmux, referenced files, and AGENTS.md — but not for git. So the agent starts blind to the branch it is on and whether the tree is dirty, then burns tool calls rediscovering it, or does not bother and commits onto the wrong branch.
# Git
Branch: feat/rewind → origin/feat/rewind (2 ahead, 1 behind)
Working tree: 1 staged, 3 unstaged, 2 untracked
M src/checkpoint.js
M README.md
?? test/checkpoint.test.js
Recent commits:
a1b2c3d Add the restore planner
d4e5f6a Capture file bytes before a mutating call
Registered on each agent's own scoped context, so a deployment driving two workspaces gives each agent its own repository's state rather than a shared guess. The first request of a session waits for the snapshot; every later refresh runs in the background, off the request's critical path. A directory that is not a repository contributes nothing at all.
- id: dsh-kit-git-context
config:
order: 1000
refreshIntervalMs: 15000
maxFiles: 20
recentCommits: 5
secret-guard
Keep credentials out of the transcript.
The common leak is not the agent deliberately opening .env. It is env, cat .env, git log -p, a stack trace, or a curl transcript putting a token in a tool result — where it then rides along in every subsequent request to the model provider for the rest of the session.
Two defences:
- Redaction. Tool results are scanned on the way back to the model and credentials are replaced with labelled markers:
[redacted:github-token]. Prefixed formats (GitHub, AWS, Google, Slack, Stripe, npm, OpenAI, JWTs, private-key blocks, basic-auth URLs,Authorizationheaders) plus one general rule for assignments to secret-shaped names. - Protected paths. Reads and writes of credential-shaped files go to the approval gate instead of running unattended.
The rules are tuned for precision, because a false positive silently hides real output and wastes a turn. API_URL, CLIENT_NAME, publicKey, sessionId, and keyboardLayout are left alone; API_KEY, clientSecret, and accessToken are not. .env.example and friends stay readable, so documentation still works. Markers contain no part of the secret, so redaction is safe to log and idempotent.
PTC mode: the registry accepts a replacement for the model-facing content or the canonical value, not both. The default redacts content, which is the path to the provider. Deployments running
run_codeshould setredactValue: trueso programs cannot read the raw value.
- id: dsh-kit-secret-guard
config:
redactToolOutput: true
redactValue: false # set true in PTC deployments
onProtectedPath: ask # ask | deny | allow
guardWrites: true
disabledRules: [] # e.g. ['openai-key']
allowPaths: ['.env.example', '**/*.pub']
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. If it fails, the output goes back to the agent and it keeps working. If it passes, the turn ends as it would have.
By default it auto-detects a typecheck command — a typecheck/type-check/tsc script in package.json (with the right package manager inferred from the lockfile), else a local tsc --noEmit. Test suites are never auto-detected: they are too slow to run at every turn boundary. Configure them explicitly if you want them.
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 to the agent as a failure — resolved by walking PATH directly, because a shell reports a missing command in the machine's own language.
- 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
timeoutMs: 120000
maxOutputLines: 40
Design notes
No build step. The package is plain ESM JavaScript with JSDoc types. DSH's own docs warn that installing a TypeScript plugin from a git host requires a prepare script on the author's side and an allowBuilds grant on yours — which is permission to execute the package's code on your machine at install time. Shipping JavaScript removes that requirement entirely.
No coupling to harness internals. The only runtime import from the DSH ecosystem is @deepseek-ai/schemastery, required for config schemas. 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, a git call in a non-repository — each degrades to a log line. None of them 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 would otherwise orphan its children.
Tests
npm install
npm test
143 tests, no network, no harness required. Pure logic (redaction rules, glob matching, porcelain parsing, restore planning) is tested directly; the plugins are 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.
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 and expect to update. Every extension point used here is documented in docs/cookbook/extension-cookbook.md.
License
MIT