Bundle
dsh-plan-build-toggle
DSH web client plugin: a persistent OpenChamber-style Plan|Build toggle on the composer (with Tab-to-switch), driving the native /plan command channel over the host plan projection
- Source
- jdqingm
- License
- MIT
- Updated
- Updated 6 days ago
Readme
# 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 | [简体中文](README.zh.md) ## 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 `plan` projection 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 as `pending` and 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-plan` row, 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_mode` review, 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: ```bash dsh plugin --profile desktop add dsh-plan-build-toggle@0.1.0 ``` ### From source (no npm publish needed) ```bash 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 1. Remove `dsh-plan-build-toggle` from `dsh.profile.bundles` in `~/.dsh/profiles/<profile>/package.json` (or uninstall via Market/CLI if installed that way). 2. Delete `~/.dsh/profiles/<profile>/node_modules/dsh-plan-build-toggle`. 3. Restart the app — the stock "Plan ×" chip returns (the `ui-plan` row is no longer disabled). ## How it is built Two halves, no build step: - `lib/client.js` — the browser half: a hand-written `window.__ModuleLoader__.load` payload (the same module format the official client plugins ship). React comes from the shared module graph; CSS is injected once under a `data-plugin-css` guard. - `lib/index.js` — an empty node-side `apply()`, so the package appears as a host roster row; `dsh.client` in `package.json` declares the browser half and its load-order deps. - `cordis.patch.yml` — the bundle patch layer: inserts the roster row and disables the built-in `ui-plan` row (seat handover). Listed via `dsh.bundle.patch`, so adding the package to `dsh.profile.bundles` is 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
Install
dsh plugin --profile web add github:jdqingm/dsh-plan-build-toggle
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plan-build-toggle from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.