dsh-plan-build-toggle
OpenChamber-style persistent Plan|Build composer toggle for DeepSeek Harness, with Tab-to-switch. Drives the native /plan channel over the host plan projection.
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 31, 2026
- Updated
- Aug 31, 2026
Introduction
dsh-plan-build-toggle
A DeepSeek Harness web-client plugin: a persistent, OpenChamber-style Plan|Build toggle on the composer, driving DSH's native /plan channel over the host plan projection.
English | 简体中文
Why
DSH already ships plan mode end-to-end (@deepseek-ai/dsh-plan-mode: the /plan / /plan off commands, the per-session plan projection, the exit_plan_mode review). Its built-in composer control (dsh-client-ui-plan), however, only reveals a "Plan ×" chip after plan mode is on — there is no visible mode control in the default Build state.
OpenChamber's form factor is different: a persistent mode control on the input bar, with bare Tab switching between Build and Plan. This plugin fills exactly that gap on DSH — same seat, same command channel, no second vocabulary of modes.
What you get
- Persistent segmented pill (
Plan|Build) in the composer's mode-control area (the built-in chip's own seat,conversation.input.plan). Active segment highlighted: Plan in warn colors, Build in neutral. - Two-way switching through
command.execute— clicking Plan runs/plan, clicking Build runs/plan off, exactly as if you typed them. - Tab / Shift+Tab to switch (the OpenChamber/opencode convention) while the caret is in this session's composer editor. Strictly scoped:
- bare Tab only — any ⌘/Ctrl/Alt chord passes through untouched;
- yields to any earlier handler that already consumed the event (
defaultPrevented), so a future autocomplete-over-Tab keeps priority; - fires only when focus is inside the session's own editor (popups, settings, other sessions are unaffected);
- does nothing when the
planprojection is absent (drafts, presets without plan mode) or while a switch is in flight.
- Host projection is the source of truth: the shown target is
pending ? !active : active— a folded host value, not client optimism. Switches made while the agent runs queue aspendingand self-correct as projection frames arrive. - Inline error state when the command channel rejects a switch.
- Replaces the built-in chip cleanly: the bundle patch disables the stock
ui-planrow, so the single-instance seat never collides. Uninstall to restore stock behavior.
Boundaries (same as DSH plan mode)
- Plan mode is soft guidance (a policy prompt section), not an enforced sandbox. For hard read-only planning, compose separate sandbox / approval presets — this plugin only drives the mode vocabulary.
- No control on draft sessions (plan state is per-session).
- Exiting still happens via the model's
exit_plan_modereview, rendered by the Web plan-review flow.
Install
Via the in-app Community Market
Once published, the plugin appears in Market → Installable on your selected catalog source (npm-backed, repository-backlink verified). Install there, or with the official CLI:
dsh plugin --profile desktop add dsh-plan-build-toggle@0.1.0
From source (no npm publish needed)
git clone https://github.com/jdqingm/dsh-plan-build-toggle.git
cd dsh-plan-build-toggle
bash scripts/install.sh desktop # copies into the profile + registers the bundle
Either way: restart the DSH app afterwards — the browser roster is computed at host boot.
Uninstall
- Remove
dsh-plan-build-togglefromdsh.profile.bundlesin~/.dsh/profiles/<profile>/package.json(or uninstall via Market/CLI if installed that way). - Delete
~/.dsh/profiles/<profile>/node_modules/dsh-plan-build-toggle. - Restart the app — the stock "Plan ×" chip returns (the
ui-planrow is no longer disabled).
How it is built
Two halves, no build step:
lib/client.js— the browser half: a hand-writtenwindow.__ModuleLoader__.loadpayload (the same module format the official client plugins ship). React comes from the shared module graph; CSS is injected once under adata-plugin-cssguard.lib/index.js— an empty node-sideapply(), so the package appears as a host roster row;dsh.clientinpackage.jsondeclares the browser half and its load-order deps.cordis.patch.yml— the bundle patch layer: inserts the roster row and disables the built-inui-planrow (seat handover). Listed viadsh.bundle.patch, so adding the package todsh.profile.bundlesis all the mounting it needs.
Iteration: edit lib/client.js, re-run scripts/install.sh, refresh the page. The webserver serves plugin assets with cache-control: no-cache, so no app restart is needed unless the roster itself changes.
License
MIT