dsh-plugin
DayuanJiang/next-ai-draw-iodsh 的 draw.io 绘图插件:通过 next-ai-drawio MCP 服务器作图,点回复里的链接在右侧栏打开实时预览,可以手动修改图,AI 的每次修改都是可以恢复的版本。需 Node.js 18+,首次使用由 npx 下载 MCP 服务器。
DeepSeek Harness 的 Windows bash 工具:用 Git for Windows 或 MSYS2(UCRT64/MINGW64/CLANG64…)的 bash 替换预设里的 pwsh,保留官方终端契约(workdir、后台任务与超时转后台、结果标记、终端卡片),并提供一个可自动发现、校验 Git/MSYS2 安装的设置行。
English | 简体中文
A Windows terminal bridge for DeepSeek Harness (DSH). It replaces the
model-facing pwsh tool with a real bash tool, with selectable Git for
Windows and MSYS2 profiles, while keeping the official terminal-tool
behaviour: working directory, background jobs, timeout promotion, permission
escalation, terminal cards, PTC and meta.cwd.
This is the complete user manual and reference. It covers installation, daily usage, the security model, settings, troubleshooting and development.
Minimum DSH: 0.2.1. Windows 10/11 only. The replacement applies to new sessions; existing sessions keep the tools they were created with.
bash, no pwsh in the model tool catalogue: tools.restrict hides
pwsh, the tool:pwsh prompt section is shadowed, and tool:bash is added.
The replacement applies to new sessions; existing ones are not retrofitted.UCRT64, MINGW64, CLANG64, CLANGARM64, MINGW32, MSYS).
Discovery reads the registry, where.exe, PATH and the usual install roots,
validates the candidate with bash --version, and never proposes WSL's
bash.exe.description, command, timeoutMs, workdir,
run_in_background, sandbox_permissions / justification, POSIX and
Windows-form workdirs, the same [exit code: N], [stderr] and truncation
spill markers, and meta.cwd for the terminal card.run_in_background returns a job id; collect with
job_output, stop with job_kill, and a foreground call past its deadline is
promoted to a job instead of being killed.MSYSTEM matches the selected profile, and Git's
bin\bash.exe launcher is canonicalized to usr\bin\bash.exe, so the
environment the plugin composes is the one that actually runs.signalNumber << 8 exit encoding
becomes [killed by signal: SIGTERM], and inherited Windows PWD /
OLDPWD are tombstoned so pwd stays POSIX.1 background task /
1 subagent header badges, job cards and subagent cards are rendered by DSH
itself — the plugin registers a bash tool with meta.cwd, it does not draw
its own chat UI.workspace-write
runs bash unconfined and labels the result with [compat: ...], read-only
refuses, and strict allows only danger-full-access. See
Security model.| Item | Requirement |
|---|---|
| DSH | >= 0.2.1 (tested on desktop runtime 0.2.1-rc.2) |
| Windows | Windows 10/11, x64; MSYS2 arm64 environments are also supported |
| bash provider | Git for Windows and/or MSYS2 |
| Node.js | ^22.19.0 or >=24, development/build only |
| Optional | Cygwin, best-effort and not recommended |
bash.exe is intentionally excluded. WSL is not Git Bash and does not
fit this profile/environment model.Do not use an old global npm dsh (for example 0.1.7); it does not know
the 0.2.1 plugin/profile contract. Use the CLI under the desktop install:
set "DSH_CLI=%LOCALAPPDATA%\Programs\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd"
%DSH_CLI% --version
:: expect 0.2.1-rc.2 or newer
Fully quit DSH Desktop before changing a profile.
%DSH_CLI% plugin --profile desktop add link:D:/GitHub/dsh-gitbash-new
Reopen DSH, then hard-refresh the Web UI (Ctrl+Shift+R).
%DSH_CLI% plugin --profile desktop add github:riverzhou/dsh-gitbash-new
%DSH_CLI% plugin --profile desktop add @riverzh/dsh-gitbash@0.2.2
Use @riverzh/dsh-gitbash@latest for the newest release.
Use the same CLI, restart DSH, hard-refresh:
%DSH_CLI% plugin --profile desktop remove @riverzh/dsh-gitbash
%DSH_CLI% plugin --profile desktop add link:D:/GitHub/dsh-gitbash-new
For a released version, replace link:... with @riverzh/dsh-gitbash@<version>.
%DSH_CLI% plugin --profile desktop remove @riverzh/dsh-gitbash
Restart DSH. The settings row disappears with the plugin. A dsh-gitbash
entry left in the profile's cordis.patch.yml is inert and can be deleted
manually.
dsh-tweaks' git-bash-terminal-tool also hides pwsh and registers bash.
If it is already installed, remove or disable it first:
%DSH_CLI% plugin --profile desktop remove dsh-tweaks-git-bash-terminal-tool
When both are present, the first one to replace wins; the later one usually
skips because pwsh is no longer restrictable. The log contains
skipped ... pwsh is not restrictable.
bash (not pwsh) and the result carries the
expected cwd.The settings row looks like this:

The Shell, Compatibility and MSYS2-environment dropdowns use the DSH light/dark surface tokens, so both the closed control and the open list stay readable:

The MSYS2 runtime cannot start under DSH's Windows ACL sandbox: restricted
tokens break its named shared memory, signal pipe and BaseNamedObjects. Adding
the user SID defeats write confinement; lowering to Low integrity breaks
BaseNamedObjects again. There is no configuration in which Git Bash both runs
and stays inside that sandbox.
The plugin therefore makes the trade-off explicit:
| Session permission | compat: auto (default) | compat: strict |
|---|---|---|
danger-full-access | bash runs normally | bash runs normally |
workspace-write | bash runs outside the file sandbox, result carries [compat: ...] | refused |
read-only | refused | refused |
workspace-write + auto, bash can write outside the workspace. Treat
paths outside the workspace as read-only unless the user explicitly asks
otherwise.read-only deliberately refuses rather than breaking the read-only promise.strict is for security-sensitive users: bash is available only under
danger-full-access.sandbox_permissions plus a one-sentence
justification; DSH asks the user for approval, and the wider mode applies to
that call only.| Parameter | Type | Required | Notes |
|---|---|---|---|
description | string | yes | Clear 5–10 word description shown in the UI |
command | string | yes | Executed as bash -c '<command>'; use POSIX syntax |
timeoutMs | number | no | Positive; default 120000, capped at 600000 |
workdir | string | no | Working directory; relative paths resolve against the session cwd |
run_in_background | boolean | no | Return a job id immediately; no timeout applies |
sandbox_permissions | workspace-write | danger-full-access | no | Request a wider mode when the session policy blocks the command |
justification | string | with escalation | One sentence for the user explaining why this command needs wider access |
run_in_background and sandbox_permissions are feature-detected. The standard
web preset composes the job service and sandboxing executor, so both are
available; a minimal composition may expose only the basic parameters.
The tool runs a fresh shell for every call; it is bash -c, not a persistent
PTY. Use workdir rather than cd.
[stderr] header.(no output).[exit code: N] as the last line; a signal adds
[killed by signal: X]. A successful foreground result usually has no exit
marker; the terminal card shows the exit status pill.[timed out after Nms].[still running after Nms; moved to background job <id>].[output truncated; full output: <file>]; the full output is spilled to
a file (up to 64 MiB).[compat: bash runs outside the DSH file sandbox (session mode workspace-write)].
It is a deployment notice, not command stderr.started background job <id> and no stdout; read
it later with job_output.tool/result.meta.cwd is the Windows absolute path actually used. Inside
bash, pwd is the MSYS POSIX form (for example /d/project).
workdir: uses DSH's current session working directory.workdir: resolved against the session cwd.C:\...) and POSIX drive paths (/c/..., /d/...)
are accepted; /tmp maps to the Windows temp directory./home/...) are refused rather than
guessed, because the Windows mapping is ambiguous.$VAR and ${VAR:?} instead of
PowerShell syntax.run_in_background: true returns a job id and continues the turn.job_output (with wait: true to block) and job_kill to collect or
stop it. These are DSH's built-in job tools; the plugin registers the job as
kind bash.timeoutMs (default 120 s, cap 10 min).If the session policy blocks a command, retry with the narrowest wider mode and a justification:
sandbox_permissions: "workspace-write" (or "danger-full-access")
justification: "Need to write the build cache under <path> because ..."
DSH asks the user. Approval applies to that one call. In compat: strict,
workspace-write is refused, so escalation must target danger-full-access.
pwsh in the parent, a new child has bash and no pwsh; this was verified
for Git and MSYS2.1 subagent. Use View subagent
to inspect the child conversation.run_code) sees the same bash tool; the plugin supplies presenters and
presentationMeta.cwd. The Web UI derives terminal cards from the tool name,
arguments, result markers and meta.cwd.A profile is more than a bash.exe path; it carries the flavour, install root,
MSYS2 environment and PATH rules.
| Flavour | Selector label | MSYSTEM | bash path |
|---|---|---|---|
| Git for Windows | Git for Windows | always MINGW64 | <root>\usr\bin\bash.exe |
| MSYS2 | MSYS2 UCRT64, MSYS2 MINGW64, … | the selected environment | <root>\usr\bin\bash.exe |
| Cygwin | Cygwin (experimental) | unset | <root>\bin\bash.exe |
UCRT64 first, then MINGW64, CLANG64, MSYS).usr\bin\bash.exe, so
bashPath alone cannot distinguish them — msystem is persisted too.MSYS2 environments must exist (<root>\ucrt64\bin, etc.). A missing
environment makes that profile invalid rather than silently falling back.bin\bash.exe is canonicalizedGit for Windows ships two entry points:
bin\bash.exe is a launcher. It resets MSYSTEM and PATH from the
installation (Git 2.56 uses UCRT64), overriding the requested environment.usr\bin\bash.exe is the runtime and honours the environment we compose.If you enter <root>\bin\bash.exe, the plugin normalizes the profile to
<root>\usr\bin\bash.exe. The effective path is shown in the settings row.
This was a real-machine bug: before normalization, a Git profile reported
MSYSTEM=UCRT64; after it, MSYSTEM=MINGW64.
| Profile | MSYSTEM | PATH prefix (before inherited PATH) |
|---|---|---|
| Git | MINGW64 | <root>\bin, <root>\usr\bin, existing <root>\ucrt64\bin / mingw64\bin / mingw32\bin |
| MSYS2 | selected | <environmentRoot>\bin, <root>\usr\bin, <root>\usr\local\bin |
| Cygwin | unset | <root>\bin |
NO_COLOR=1, TERM=dumb, PAGER=cat, GIT_PAGER=cat,
CHERE_INVOKING=1.MSYS2_PATH_TYPE=inherit.C:\msys64\usr\bin; an MSYS2 session does not inherit Git's usr\bin.DSH_* facts are merged last, so they win. HOME, TMP, TEMP and LANG
are not overridden.bashPath + msystem (highest priority; invalid means a
clear error, not a silent fallback).InstallPath (HKLM / HKCU).C:\Git, D:\Git), MSYS2
(C:\msys64, D:\msys64, LocalAppData, C:\msys32), Cygwin
(C:\cygwin64, C:\cygwin).where git (derive the root from git.exe) and where bash.bash.exe / git.exe on PATH.WSL is excluded: System32\bash.exe, Sysnative, WindowsApps, \\wsl$
and wsl.exe are never candidates. Version probing runs only on Refresh or
Test, not when the settings page opens; the selector uses cached profiles.
Open Settings → General → Terminal tool (Git Bash / MSYS2). The default view is intentionally small:
Auto (recommended), Git for Windows, or a specific MSYS2
environment. Selecting an entry writes both the path and msystem.bash --version for versions/badges.The badge at the top right shows the effective profile (which may differ from a manually entered path after Git canonicalization).
Configuration is stored in the profile's cordis.patch.yml:
- id: dsh-gitbash
config:
bashPath: 'C:\Program Files\Git\usr\bin\bash.exe'
msystem: MINGW64
compat: auto
bashPath: '' means auto.msystem is auto or a concrete MSYS2 environment; Git ignores it and uses
MINGW64.compat is auto or strict.Ask:
Use bash to print
$MSYSTEM,command -v bash,bash --version,pwdandgit status.
Expected: MSYSTEM=MINGW64 for Git, or the selected environment for MSYS2;
bash resolves under the chosen install; pwd is POSIX and the terminal card
shows the Windows cwd.
Ask the model to start it with run_in_background: true, then collect logs
with job_output and stop it with job_kill. The session header shows the
background-task badge.
pacman -S --needed mingw-w64-ucrt-x86_64-gcc).command -v gcc; it should resolve under
C:\msys64\ucrt64\bin.Ask the parent to use subagent with a child prompt that runs a bash command.
The child has its own bash; the parent receives the child report and the
session shows a subagent card. Use View subagent to inspect the child.
Set Shell back to Auto (or pick Git for Windows) and refresh. A manually
entered bin\bash.exe is canonicalized to usr\bin\bash.exe; if you still see
UCRT64, you are probably running an older plugin build or a stale session.
| Symptom | What to do |
|---|---|
| Settings says no bash found | Install Git for Windows or MSYS2; click Refresh; or enter <root>\usr\bin\bash.exe manually (no quotes) |
New session still has pwsh | Confirm DSH >= 0.2.1, the plugin is in the profile bundles, and DSH was restarted; inspect %TEMP%\dsh-gitbash.log for replace: |
| Only old sessions have no bash | Replacement is per-agent; start a new session. Subagents inherit from their parent |
Git session reports MSYSTEM=UCRT64 | Old bin\bash.exe path/build; use Auto/Refresh; current builds canonicalize to usr\bin\bash.exe |
| MSYS2 environment not installed | Install it in MSYS2; the profile only lists environments whose bin directory exists |
CreateFileMapping ... Win32 error 5 / couldn't create signal pipe | The process was started inside the Windows ACL sandbox. This plugin runs bash unconfined in compat: auto; remove custom confinement or use a supported preset |
read-only refuses bash | By design; switch the session to workspace-write or danger-full-access (or escalate) |
Strict mode refuses workspace-write | Set compatibility to auto, switch the session to danger-full-access, or escalate to danger-full-access |
| Both pwsh and bash are gone after installing another plugin | Two plugins are competing; keep only one bash replacement (see Install) |
| Background jobs are unavailable | The preset did not compose ctx.jobs; use the standard web preset or run in the foreground with a timeout |
job_kill / job_output missing | They are separate DSH tools, not part of bash; ensure the jobs tool group is in the preset |
| Settings row is missing | DSH too old, plugin not loaded, or the Web client was not rebuilt/refreshed; restart DSH and hard-refresh |
| Cannot find the Git/MSYS2 selector, or changing it does not affect this session | The selector is only in Settings → General → Terminal tool (Git Bash / MSYS2); the built-in Terminal card is the separate UI-terminal plugin. Changes persist immediately but apply to new sessions only |
| Path with spaces fails | Enter the path without surrounding quotes; the settings form stores it verbatim |
| WSL bash is not listed | Intentional; WSL cannot be driven through this profile/environment model |
| Output seems cut off | Long output is tail-truncated; use the reported spill path for the full file |
%TEMP%\dsh-gitbash.log (typically
C:\Users\<you>\AppData\Local\Temp\dsh-gitbash.log). One line per event:
apply, capability probing, replace: applied ... or
skipped ... pwsh is not restrictable./api/dsh-gitbash/state (authenticated), used by the
row; /discover and /validate are also available to the client. They are
not a public unauthenticated API.pnpm compatcheck compares the deployed official
bundles with the strings this plugin depends on.For a full real-machine transcript, see docs/SMOKE.md.
ctx.subprocess, ctx.jobs, ctx.sandboxPolicy, ctx.shellEnv,
ctx.workingDirectory, ctx.approval, ctx.configForms, etc.) and does not
import private DSH packages.agent/created, tools.restrict, tool:bash / tool:pwsh section names,
result markers and meta.cwd are the critical integration points. A wrong
event name would be a silent no-op, so the test suite asserts the strings.pnpm compatcheckpnpm testpnpm build) if src/ changed; the committed lib/ must match
src/cordis.patch.yml disables the
entry when process.platform !== 'win32').Verified on 2026-10-18: DSH 0.2.1-rc.2, Git for Windows 2.56 and MSYS2
5.3.20 (UCRT64); direct bash, background jobs, subagents, the settings switch and
the native UI cards were exercised. Raw events: docs/SMOKE.md.
pnpm install
pnpm build # lib/index.js + lib/client.js
pnpm check # tsc --noEmit
pnpm test # node:test; 75 tests, including real Git/MSYS2 when present
pnpm selftest # pure-logic checks, no DSH needed
pnpm compatcheck # contract-string check against the installed DSH
Real integration tests discover local Git/MSYS2 installations and skip when
absent. They exercise MSYSTEM, PATH isolation, exit codes, stderr, real child
bash, timeout and the expected unconfined write behaviour.
src/index.ts host entry: apply, Config, routes, replacement
src/shared/protocol.ts host/client wire ty