# dsh-toolbelt English | [中文](README.md) Eight [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugins that fill gaps the harness does not cover on its own: a general-purpose persona, a reply-language guard, a per-request vision fallback, two write/exec guards, a cross-agent memory bridge, image-generation tools, and shell-command substitution inside skills. Every plugin is an independent Cordis plugin with its own entry point, its own typed `Config`, and its own tests. Mount only what you want. > Built against `@deepseek-ai/dsh@0.1.0-rc.6`. The harness is in developer preview and **will** make breaking changes. ## Install From a local checkout. **Build it first** — `dsh plugin add ` links the directory and never runs your build, so an unbuilt checkout fails the whole plugin tree at boot with `loader entries failed to apply`: ```sh git clone https://github.com/cking000bigdemon/dsh-toolbelt cd dsh-toolbelt && npm install && npm run build && cd .. dsh plugin --profile add ./dsh-toolbelt ``` Directly from GitHub — **this takes two attempts, by design.** A git install fetches source, not build output, so this package builds itself through its `prepare` script, and pnpm ≥10 refuses to run that until you authorize it: ```sh dsh plugin --profile add github:cking000bigdemon/dsh-toolbelt # ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED — copy the exact key pnpm prints into # $DSH_HOME/profiles//pnpm-workspace.yaml: # # allowBuilds: # dsh-toolbelt@https://codeload.github.com/...: true # # then run the same command again. ``` Authorizing a build means **this package's code runs on your machine at install time, outside any agent sandbox**. Pin a commit (`github:cking000bigdemon/dsh-toolbelt#`) so a later push cannot silently change what runs. Then confirm the layer composed: ```sh dsh --profile --dump-config # look for "# == dsh-toolbelt" ``` ## Safe by default Only **`vision-fallback`** is enabled on install. It is the one row that cannot surprise you: it does nothing until you send an image to a model that cannot read images. The other seven change the agent's persona, block tool calls, read files outside the workspace, or execute shell commands. Enable each deliberately in your profile's `cordis.patch.yml`: ```yaml - id: language-guard disabled: false config: targetLanguage: zh ``` A patch replaces a row's whole `config` value rather than deep-merging, so restate every key you need. ## The plugins | Plugin | Extension point | What it does | |---|---|---| | `vision-fallback` | `agent/request` | A request carrying an image, on a model without image input, is served by an image-capable model **for that request only**. | | `general-agent-prompt` | `ctx.systemPrompt.section()` | Eight persona/discipline sections replacing the coding-assistant identity. Each is `true` / `false` / a replacement string. | | `language-guard` | `llm/stream` + `agent/turn-stopping` | Detects non-target-language drift while the reply streams; steers a correction at the turn boundary. | | `python-workdir-guard` | `tools/pre-execute` | Denies shell calls reaching for a global Python or pip instead of the project venv, with a model-actionable reason. | | `windows-encoding-guard` | `fs/write-intent`, `fs/edit-intent` | Blocks writes of PowerShell/Python files carrying Windows encoding traps; warns on four softer ones. | | `cross-agent-memory` | `ctx.systemPrompt.context()` | Injects Claude Code / Codex memory files as explicitly **untrusted** context. | | `image-generation` | `ctx.tools.register()` | `generate_image_gpt` and `generate_image_gemini`; returns a saved path, never base64. | | `skill-shell-injection` | `ctx.skills.registerProvider()` | Runs `` !`cmd` `` and ```` ```! ```` blocks inside a SKILL.md before the content reaches the model. | Each plugin's source file opens with a doc comment covering its design, its config, and its limitations. Read that before enabling. ### Notes worth knowing before you enable something - **`vision-fallback`** holds **zero** mutable state. Switching the model globally and restoring it afterwards is the obvious implementation and it is wrong: the image stays in durable history, so the next request goes to a blind model anyway. `agent/request` replaces the frozen call config for one request, which is both simpler and correct. - **`general-agent-prompt`**'s `runtime` section deliberately ships **no built-in text**. Only a deployment knows what it bundles, and a wrong claim ("Python is bundled") is worse than no claim, so that section materializes only when you pass a string. - **`windows-encoding-guard`** guards non-Windows hosts by default. The hazard belongs to the *destination* interpreter, so a `.ps1` authored on Linux is still dangerous. It also surfaces a dsh-specific hazard: `dsh-fs-local` decodes with a BOM-stripping `TextDecoder`, so an existing UTF-8 BOM never survives an edit. - **`cross-agent-memory`** reads files from your home directory and puts third-party content in front of the model. It is framed as untrusted data and XML-escaped so no closing tag is representable, and `{{…}}` runs are neutralized so memory content cannot interpolate prompt variables. There is no `isProjectTrusted()` equivalent in dsh — the `trustProject` flag is yours to wire to a real trust signal. - **`skill-shell-injection`** executes commands found in files. It only does so under trusted skill roots, memoizes per session so a side-effecting command runs once, and never rescans command output. `SkillRegistry` exposes no way to intercept an already-registered provider, so this registers its own provider and decorates a provider *instance* handed to it via config. ## Development ```sh npm install npm test # 342 tests npm run typecheck npm run build ``` Tests use the harness's own `@deepseek-ai/dsh-agent-loop-testkit` and stub adapters. They make **zero** network calls and write only under `os.tmpdir()`. ## Status and limitations - `image-generation` has **never been exercised against a live provider** — no image-generation credential was available during development. Request building and both response shapes are covered by tests against a stubbed `fetchImpl`; real `size`/`n` acceptance and provider error envelopes are unconfirmed. - `language-guard` keeps its correction state in memory, so it resets on resume; it registers no slash commands. - `python-workdir-guard` allows `python -c ''` and does not resolve variable indirection such as `P=pip; $P install x`. - Several plugins reach optional seams (`shell`, `credentials`, `subprocess`, `attachments`, `workspace`) structurally through `ctx.get()` rather than `inject`, so they degrade to a no-op when a deployment omits them. Declaring those packages as dependencies would let them use the real types instead. ## License [MIT](LICENSE)