AGENTS.md
August 18, 2026 · View on GitHub
This file helps AI coding agents and LLM tooling understand and work with this repository quickly.
What this repo is
dsh-plugin-toggle is a packaged Cordis plugin for DeepSeek Harness (DSH) that adds an "Enable/Disable" tab to Settings → Plugins. The shipped inventory page is read-only; this plugin lets the user stop/start Loader entries live (via entry.update({disabled})) and persists the change to the profile's cordis.patch.yml. It is a real profile-bundled plugin: dsh.bundle (cordis.patch.yml) mounts the host half, and the dsh.client declaration + exports["./client"] register the browser half — install once with dsh plugin add, loads on every DSH boot, no cordis_define.
Repository layout
| Path | Role |
|---|---|
src/index.ts | Host half: `GET |
src/client/index.tsx | Client bundle: additive settings.plugins.tab registration (id toggle, order 20, label "Enable/Disable"), toggle switches, Enabled/Disabled tags + Cordis phase, toast, <style data-plugin> with --dsw-alias-* tokens. |
cordis.patch.yml | dsh.bundle.patch: inserts the plugin row {id: dsh-plugin-toggle, name: 'dsh-plugin-toggle'}. |
tsdown.config.ts | Builds host (node ESM → lib/index.js) + client (browser CJS ModuleLoader closure → lib/client.js, bundle id = package name). |
package.json | exports["./client"], dsh.bundle.patch, dsh.client (platform: 'web', inject edges), peers react + @deepseek-ai/cordis. |
README.md / README.zh.md | Human docs (en default, zh). |
llms.txt / llms-full.txt | LLM-friendly doc index / full text. |
Key behaviors (don't break these)
- Packaged, not dynamic: install via
dsh plugin add(or profilelink:dep + restart). Do NOT revert to a dynamiccordis_define-only shape. - Client talks to host over HTTP: the client fetches
GET|POST /plugin-toggle/api(hostwebServerroute). Do not reintroduce the dynamicharness.handle/host.callseam — it does not exist for packaged plugins. - Additive tab, never shadow: register
settings.plugins.tabwith a FRESH id (toggle) and order AFTER the shipped tab. Reusing the shipped id (all) would replace the shipped inventory page — never do that. - Live toggle via entry-level update only: call
entry.update({ disabled })directly on the resolved Entry. Do NOT go through the tree-levelEntryTree.update— that wrapper callstree.write()and would flatten bundle patches intocordis.yml. - Persistence is an append, not a rewrite: append
- id: <rawId>/disabled: <bool>to the profile'scordis.patch.yml(path derived from theincludeentry's config path). Never rewrite the file, never touchcordis.ymlor bundle patches. Patch rows are id-targeted; later rows win, so re-enabling appendsdisabled: false. - Raw id vs prefixed id:
entry.idis the prefixed form (include:<rawId>) and is what the client round-trips; patch rows must useentry.options.id(the raw config id). - Safety guard:
include,cordis:include, anddsh-plugin-toggleare locked (403); unknown ids404; POST bodies capped at 1 MB. - Never throw across the API:
/plugin-toggle/apialways returns{ok:false,error}JSON on failure, never a non-JSON 500. - Theme tokens only: client CSS uses
--dsw-alias-*tokens; no hardcoded colors. Font comes from inheritance (the seat wrappers supply the app font); do not set font-family. - ModuleLoader bundle shape: the client build must keep the exact CJS closure wrapper (
window.__ModuleLoader__.load({id: "dsh-plugin-toggle", factory})+module.exports = { inject, apply }) — seetsdown.config.ts.
Common tasks
- Change locked entries / route / tab order: edit
LOCKED/LOCKED_NAMES, the route path insrc/index.ts, and the taborder/labelinsrc/client/index.tsx, rebuild. - Change toast duration / labels: edit
src/client/index.tsx. - Rebuild:
pnpm install && pnpm build(outputslib/index.js+lib/client.js). - Update the live profile install: push to GitHub,
dsh pluginupdate or re-add in the profile, restart DSH, hard-refresh the browser tab (the DSH client HMR only hot-swaps already-loaded bundles — new bundles require a full page reload).
Environment facts (probed, do not re-probe)
- Packaged hosts are real Node modules:
node:fs/promises,node:path,node:urlwork; the DSH dynamic-plugin sandbox limits do not apply. webServer.registerroute shape:{kind: 'exact'|'prefix', path, handler(req, res)}with node:http semantics; duplicate (kind, path) throws. The handler receives the rawIncomingMessage(stream the body; do not assume it is pre-buffered).- The web profile's plugin tree = bundle patches + the user patch layer
<profile>/cordis.patch.yml, applied LAST → id-targeteddisabledrows win at boot. The patch file path is derived from the loader'sincludeentry (options.config.path→ dirname →cordis.patch.yml). - Loader
Entry.get id()returns the prefixed form (include:<rawId>);pluginInventory.list()returns that sameentryId;loader.resolve(entryId)round-trips it. Entry.update()at the entry level does NOT calltree.write();isNullablematches onlynull/undefined, sodisabled: false(re-enable) is kept.- The client bundle is plain browser JS (ModuleLoader CJS factory):
fetch,document,windoware available; React comes from the module table (external: react). - The client must export
inject = ['slots'](service key); the package.jsondsh.client.injectlists package names (informational edges). settings.plugins.tabis a list slot; a fresh id adds a tab beside the shipped one, reusing the shipped id replaces it.
Testing
- Before restart: verify the profile installed the bundle —
~/.dsh/profiles/web/package.jsondependenciesanddsh.profile.bundlesboth listdsh-plugin-toggle;lib/client.jshas the ModuleLoader wrapper;lib/index.jsexportsname+apply. - After restart (hard-refresh the tab): Settings → Plugins shows the "Enable/Disable" tab;
GET /plugin-toggle/apireturns the entry list; toggling a plugin updates its phase immediately and appends a row to<profile>/cordis.patch.yml; a further DSH restart keeps the state. - Failure path: toggle a locked id →
403toast; unknown id →404; persistence append failure → toast says "not persisted across restart" (session-only toggle still works). - No automated test framework; the manual matrix above is the verification contract.
Notes for LLM crawlers
- Listed under the GitHub topic
dsh-plugin; public at https://github.com/Zenjibad/dsh-plugin-toggle. - Distinguishing traits: packaged profile plugin (persists across restarts), additive settings tab that never shadows the shipped inventory, live
Entry.update({disabled})toggle withcordis.patch.ymlappend persistence, host HTTP route instead of dynamic RPC, real-Node host half, pure--dsw-alias-*theming.