dsh-plugin-doc-present
Doc Present for the DeepSeek Harness (dsh): let the agent show plans and designs instead of dumping long text into the chat.
When the agent has a proposal to present — an architecture, a flow, a state
machine, a message sequence, a numeric curve, a UI walkthrough — it publishes a
self-contained interactive HTML page (inline CSS/JS, FX animation
framework included) and you view it in an in-GUI panel or any browser,
including colleagues on the LAN.
方案展示插件:agent 把方案做成自包含 HTML 动态演示页(状态机/时序/曲线/UI 四类动图配方),你在 GUI 内嵌面板或浏览器里看,替代对话框长文。
Surfaces
| Surface | What you get |
|---|---|
| In-GUI panel | 📄 方案预览 button in the session header opens a drawer with the doc list and a live iframe preview (same-origin route /doc-present/... on the dsh web server — no CORS, no extra port needed for local viewing). |
| Tool card | Each doc_present tool call renders a card in the conversation with 在面板中打开 / 浏览器打开 / 复制内网链接 buttons. |
| LAN server | A standalone preview server on 0.0.0.0 (default port 8899, auto-scans upward if occupied) so colleagues open http://<your-LAN-IP>:<port>/<slug>/ directly. Started lazily on first publish; survives nothing but a dsh restart (docs persist on disk; it re-ensures itself on next use). |
Repository layout
package.json plugin manifest (dsh.client declaration, exports["./client"])
lib/index.js host half: doc_present tool, /doc-present command, prompt section, /doc-present web route
lib/server.js document server core (pure Node, no dsh imports — standalone-testable)
lib/client.js client half: header button + overlay drawer + doc_present tool card
assets/template.html self-contained doc template with the FX animation framework
references/ authoring guides the agent reads before writing pages
html-authoring.md FX framework usage + 4 copy-paste demo recipes
writing-guide.md inverted-pyramid writing guide for design docs
scripts/smoke.mjs standalone smoke test for lib/server.js
The doc template, the FX framework, and both guides are ported from the
design-preview agent skill; the preview server is a Node port of its
doc_server.py with the same lifecycle rules (unpublish → 410, delete →
trash, etc.).
Installation
Prerequisites: a working dsh install (the dsh CLI on PATH, with a web
profile). The plugin loads as a real package from the profile module fallback
directory, plus one row in a composition (host profile patch or agent
preset). Installing the package alone changes nothing — the row placement in
step 2 decides who gets Doc Present. This repository ships no install
scripts and never writes outside its own files: every mount is a manual row.
1. Install the package
Copy this repository into the profile module directory (create it if needed):
New-Item -ItemType Directory -Force "$HOME\.dsh\profiles\node_modules"
Copy-Item -Recurse . "$HOME\.dsh\profiles\node_modules\dsh-doc-present"
The bare specifier dsh-doc-present then resolves from every profile.
2. Mount the row
The row declares inject: ["tools", "systemPrompt", "commands", "webServer"],
so it needs the web profile (the webServer service only exists there).
Add an insert entry to the web profile's patch layer
$HOME\.dsh\profiles\web\cordis.patch.yml:
- insert:
- id: doc-present
name: dsh-doc-present
Profile boot watches this file (watchUserPatches), so the edit hot-reloads
into the running server — no restart needed. Every session gains the
doc_present tool, the /doc-present command, the prompt section, the
header button, the panel, and the tool card.
3. Verify the mount, then start
Verify against the live server before refreshing the page:
- the served web root embeds
window.__DSH_BOOT__; itsentrieslist must contain iddsh-doc-present(the entry id is the PACKAGE name, not the row id), and GET /plugins/dsh-doc-present/client.jsmust return 200, andGET /doc-present/__health__must return 200 with{"service":"doc-present","magic":"doc-present-ok",...}— this same-origin route works even while the standalone LAN server is off.
Then refresh the browser once: the 📄 方案预览 button appears in the session
header (top-right actions row), and Settings → Plugins lists
dsh-doc-present as active.
Usage
For the agent (model tool)
doc_present — one tool for the whole lifecycle:
| action | What it does |
|---|---|
new |
Create <root>/<slug>/index.html from the template (needs slug, optional title), ensure the LAN server, return the file path and all URLs. The agent then writes the content with its normal write/edit tools. |
status |
Server state, root, port, doc counts, URLs. |
ensure |
Start (or reuse/adopt) the LAN server; prints share URLs. |
list |
List published docs (all: true includes withdrawn). |
unpublish |
Take a doc offline (visitors see a 410 page); files kept, restorable. |
republish |
Re-publish a withdrawn doc. |
delete |
Move a doc to the trash (hard: true deletes permanently). |
trash |
List the trash (empty: true empties it). |
Authoring rules the agent follows (enforced by the prompt section): a doc is
one self-contained HTML file — inline CSS/JS, no CDN / web fonts /
external images (LAN readers may not reach the public internet), demos
paused by default with a static caption each, semantic colors (blue = active,
green = done, yellow = attention, red = risk, purple = self-call). The two
guides in references/ teach the FX framework and the writing structure; the
prompt section points the agent at their absolute paths.
For the user
📄 方案预览button (session header, top right) — toggle the preview drawer: doc chips on top, live preview below;浏览器打开/复制链接/↔ 全宽/⟳/✕in the drawer header;Esccloses.- Tool cards — after the agent publishes, its
doc_presentresult renders a card with在面板中打开(opens the drawer straight at that doc),浏览器打开, and复制内网链接. /doc-present status|start|stop|list— the service as a command. (/doc-present jsonis the machine-readable state the panel uses.)
How the two servers relate
- The same-origin route
/doc-present/...on the dsh web server (e.g.http://127.0.0.1:3080/doc-present/) serves the doc root directly from disk. It is always on while the plugin is mounted and powers the in-GUI panel. Only people who can reach your dsh web server can see it. - The standalone LAN server (default
0.0.0.0:8899) exists for sharing: it is what colleagues open. It starts lazily on the firstnew/ensure/start, stops on/doc-present stop, and is closed when the plugin unloads (docs persist on disk). If the configured port is taken by a foreign process it scans upward (up to +20) and persists the chosen port back to<root>/.server/config.json; if it finds a healthy doc-present instance already on the port, it adopts it instead of starting a second one.
Configuration
- Doc root:
DOC_PRESENT_ROOTenv var, default~/.dsh/doc-present(docs +.server/metadata:config.json,state.json,trash/). - Port:
DOC_PRESENT_PORTenv var ><root>/.server/config.json>8899. - Template and guides resolve relative to the installed package, so keep
assets/andreferences/next tolib/.
Development
lib/server.js is pure Node and covered by a standalone smoke test (new →
serve → unpublish/410 → republish → delete/trash → port persistence):
node scripts/smoke.mjs
Client-half edits hot-reload only while a watcher rebuilds plugin bundles
(pnpm run dev:web from the dsh checkout); otherwise refresh the page after
copying the package into the profile. Host-half changes load when the row
reloads (edit the patch file) — note that webServer.register returns a raw
disposer, and while the plugin captures and disposes its route on unload, a
route leaked by an older build is detected and reused (a warning is logged);
restart the profile for a completely clean slate.
License
MIT
No comments yet. Be the first to write one.