dsh-plugins π§©
August 19, 2026 Β· View on GitHub
Community plugin collection for the DeepSeek Harness (DSH) Web GUI.
DSH's runtime is built on Cordis: every capability is
a plugin row in a composition. This repo collects reusable DSH plugins β they
can be installed as client modules (persistent, loaded by the dsh.client
scan on dsh web start) or mounted as dynamic plugins in a single session
(zero install, zero restart).
π¦ Plugins at a glance
| Plugin | Version | Type | One-liner |
|---|---|---|---|
graph Β· flagship | 1.0.0 β | host tool + browser UI | LangGraph-style graph model tool: state-machine orchestration with cycles, unbounded loops, interactive editor, persistent library. |
goal-tracker | 0.4.x | browser UI | OpenCode-style Goal tracking bar + cross-session Goals history tab. |
workflow-visualizer | 0.2.x | browser UI | Claude-style workflow runs: status pills, per-phase progress, agent drill-down, "open child session". |
Flagship =
@dsh-plugins/graph. It ships a model-facing tool (host-plane row)
- a browser visualizer (client module) + a persistent FS library. It's the most complete example of the two-mounting-mechanisms workflow.
π§ What @dsh-plugins/graph does
You call one tool with a spec like:
{
"name": "revise-loop",
"entry": "writer",
"maxSteps": 0,
"persist": true,
"nodes": [
{ "id": "writer", "type": "agent", "prompt": "Write a draft." },
{ "id": "counter", "type": "js", "code": "return {n: (state.n ?? 0) + 1}" },
{ "id": "reviewer", "type": "agent", "outputSchema": { "type": "object", "properties": { "pass": { "type": "boolean" } } } }
],
"edges": [
{ "from": "writer", "to": "counter" },
{ "from": "counter", "to": "reviewer" },
{ "from": "reviewer", "router": "return state.reviewer.pass ? 'END' : 'writer'" }
]
}
β¦and the tool runs the whole draft β review β revise β review β ship loop as a single call. The user sees the loop visualized in the tool card and in the Graphs tab beside the conversation, with a drag-to-edit canvas that persists the definition to disk.
βββ chat βββββββββββββββ βββ Graphs tab βββββββββββββββββββββββ
β βΈ graph: revise-loop β β graphs 3 live 17 saved β
β β running 1m12s β β β revise β demo β
β β writer β 412ms β β β loop β arch β
β β bump β 18ms β β + live β¦ β β¦ β
β β reviewer β ... β β ββββββββββββ ββββββββββββββββββββ
β β³ step scrubber β¬ββ β β β β edit Β· revise ββ
β βΈ output: {β¦} β β β β β writer β revβ¦ββ
βββββββββββββββββββββββ βββββββββββββοΏ½βββ΄ββββββββββββββββββ
Full install + usage in plugins/graph/README.md.
ποΈ Layout
dsh-plugins/
βββ plugins/
β βββ graph/ # πΈοΈ flagship β LangGraph-style tool + visualizer + library
β βββ goal-tracker/ # π― OpenCode-style Goal tracking + history tab
β βββ workflow-visualizer/ # π Claude-style workflow run visualization
βββ scripts/
β βββ restart-web.sh # one-shot dsh web restart helper
βββ package.json # pnpm workspace root (workspaces: plugins/*)
βββ README.md # this file
βββ LICENSE # MIT
π Installing a plugin
Each plugin supports two mounting strategies β pick by use case.
Way A β Dynamic plugin (fastest, single-session, no restart)
In any DSH session, have the agent call the Cordis toolset:
- Read the plugin's
src/dynamic-client.js(andsrc/dynamic-host.jsif it has one). - Pass them as
code.client/code.hosttocordis_define(plugin.kind: "new"). cordis_runthe returned package and authorize the client half.
Dynamic plugins live only in the current process; gone after
dsh webrestart. Use them for trying things out.
Way B β Client module + host row (persistent, persistent across restarts)
The plugin package declares dsh.client: { platform: "web" } and exports a
./client bundle. After install, dsh web's client-modules scanner picks it up.
Quickest path: symlink the workspace into the profile
# 1. Make the package discoverable
ln -sfn "$PWD/plugins/graph" \
~/.dsh/profiles/node_modules/@dsh-plugins/graph
# (also into the specific profile, for scanner visibility)
mkdir -p ~/.dsh/profiles/web/node_modules/@dsh-plugins
ln -sfn "$PWD/plugins/graph" \
~/.dsh/profiles/web/node_modules/@dsh-plugins/graph
Add the host row to your profile's patch layer
Edit ~/.dsh/profiles/web/cordis.patch.yml:
# LangGraph-style `graph` tool β host-plane, visible to every session.
- insert:
- id: tool-graph
name: '@dsh-plugins/graph'
config:
provider: spawn
maxParallel: 6
Restart
bash scripts/restart-web.sh
After restart:
- Every session's agent can call the
graphtool. - The tool card (browser-side,
client.js) auto-renders with topology + step scrubber + per-node outputs. - The Graphs tab (browser-side, dynamic
src/dynamic-client.js) is not auto-mounted by the static row β to get it, either republish with the dynamic half wired in, or mount it dynamically in a session (Way A).
Why two halves?
client.jsis the always-on, restart-safe part of the package. The dynamic half (src/dynamic-client.js) is the heavier, session-scoped part that includes the interactive editor β DSH loads it when explicitly mounted.
π§ Quick tour of the flagship plugin
Read plugins/graph/README.md for the full
reference. The at-a-glance:
- Concept: one tool that runs a whole super-step state machine. Nodes run in a topological frontier, writes merge at step end, routing can branch back β cycles are first-class.
- Node kinds:
agent(delegates to a subagent with prompt + optional output schema) andjs(pure(state, info) => value). - Edge kinds: static (
a β borβ "END") or router ((state, info) => nodeId | "END" | [nodeIds]). - Persistence:
persist: truewrites<id>.jsonunder$DSH_HOME/graphs/. The host registers agraphLibraryCordis Service (list/get/save/update/remove). - Editor: click a saved graph in the Graphs tab β drag nodes, drag-port-to-port
to create edges, click to edit static target / router code, Save changes
writes back via
graphLibrary.update. - Live: while a run is in flight, the card pulses; the Graphs tab streams agent chips (running/done/failed with elapsed + output previews); clicking a live node's "open agent session" jumps to that child session.
π Writing your own plugin
The repo has three plugins you can copy from. The pattern:
| File in your plugin | What it is |
|---|---|
package.json | Must declare dsh.client: { platform: "web" } and exports["./client"]. |
client.js | Browser half: classic script wrapping window.__ModuleLoader__.load({ id, factory(require) }) returning { apply, inject }. |
index.js | Host half: usually a no-op apply; just exists so Cordis Loader can mount the row. |
src/dynamic-client.js / src/dynamic-host.js | Single-source variants of the same UI/logic, pasted directly into cordis_define's code.* fields for instant trials. |
test/*.test.mjs | Headless test suite (node test/engine.test.mjs β npm test runs all). |
Style: inject a <style> tag with data-plugin-css for HMR bookkeeping;
prefer DSW theme variables (--dsw-alias-*, --dsh-composer-*) over inline colors;
write React code with React.createElement β never JSX.
The plugins/graph/ plugin demonstrates the full two-mounting-mechanism
workflow (host row + dynamic visualizer + persistent library) β read its
README before writing a new one.
π οΈ Development workflow
Common tasks
-
Switch profile:
dsh --profile <name>β README and scripts both honour theDSH_PROFILEenv var. -
Restart:
bash scripts/restart-web.sh [port]β graceful stop β wait for the port to free β nohup relaunch in the background β readiness probe within 30 s. -
Run plugin tests:
npm test # or per plugin node plugins/graph/test/engine.test.mjs node plugins/graph/test/client.test.mjs node plugins/graph/test/dynamic-host.test.mjs node plugins/graph/test/library.test.mjs node plugins/graph/test/editor.test.mjs -
Try the dynamic variant of any plugin: ask the agent "mount
<plugin>'s dynamic variant in this session" β it reads thesrc/files and wires them up.
π¦ Publishing to npm
Every plugin in this repo is set up for public npm publishing out of the box:
cd plugins/graph # or goal-tracker / workflow-visualizer
npm publish # needs npm login on your machine
After publishing, users install with one command:
dsh plugin --profile web add @dsh-plugins/graph
The release workflow per plugin:
- Bump
versioninplugins/<name>/package.json(we follow semver). - Update
plugins/<name>/CHANGELOG.md(we follow Keep a Changelog). - Commit + push +
git tag @dsh-plugins/<name>@<version>. npm publishfrom the package directory.
Each plugin has
publishConfig.access: "public", arepositoryblock, and (for@dsh-plugins/graph) abugs/homepagefield β once younpm login, the package is onenpm publishaway.
π Dependencies & requirements
- DSH
>= 0.1.0-rc.7(needs thedsh-baseanddsh-web-appbundles). - Node
>= 18(DSH runtime). - React
^18.2.0(peer dependency for the client modules). - Browser: modern Chromium / Firefox / Safari (uses
color-mix(), CSS variables).
ποΈ Related links
π License
MIT.