DeepSeek Harness plugin: review Markdown documents in a local browser with annotations, replacements, and deletion suggestions. Supports single files and whole directories.
A DeepSeek Harness plugin for reviewing Markdown documents in a local browser — word by word.
Select text in the rendered document to add a comment, suggest a replacement, or suggest deletion. The browser never edits the Markdown source; annotations are stored in one central append-only store at ~/.dsh-document-review/annotations.jsonl, so annotations on files anywhere on the machine — inside or outside the workspace — are all visible to the agent with a single tool call.
Adapted from opencode-document-review — the same review core (HTTP server, security model), wrapped as DSH model-facing tools instead of OpenCode tools, with annotations consolidated into a central store.
Why
DSH agents author and edit Markdown a lot — architecture docs, ADRs, READMEs, plans. "Review this document word by word" is a natural request, but DSH had no plugin for it (confirmed: no document-review/annotation plugin in the market as of 2026-08). This plugin fills that gap.
The agent opens the document in a local browser; the human reviews and annotates; the agent reads the annotations back and applies changes through its normal file-editing tools.
Tools
All four tools are registered on ctx.tools and become model-facing:
Tool
Purpose
document_review_start
Start or reuse a local review session for a Markdown file or directory (directory mode reviews every .md file inside). Returns the browser URL.
document_review_list
Read review annotations (open/pending by default, or all). Without path, returns every annotation across all documents on the machine — the single call the agent needs after "审查完了你看下". Includes source-hash staleness. In directory mode, summarizes every document.
document_review_update
Mark a comment (resolved/open) or suggestion (accepted/rejected/pending) as handled.
document_review_stop
Stop the local review session. Annotations remain in the central store.
Settings
Configuration follows the official DSH plugin pattern: export a Schemastery
Config schema, configure from cordis.yml (per-entry block), and
additionally register the same schema as a DSH settings namespace
() for runtime overrides.
dsh-document-review · DSH Hub
config:
document-review
In the harness Web profile's cordis.patch.yml, add a config block to the
plugin's entry:
Changing the config block hot-replaces the plugin via HMR. A DSH settings
user document (when present) overrides individual keys on top of the
cordis.yml base; without one, the entry config applies as-is.
In-page settings panel (review page)
Open any review page and click the gear button (top-right) for a settings
dialog with two levels:
全局设置 — applies to every workspace. Persisted to
~/.dsh-document-review/config.json, layered above the cordis.yml base.
当前工作区设置 — applies only to the workspace the review page is
rooted at. Persisted to ~/.dsh-document-review/workspace-configs.json
(keyed by root path), layered above the global settings.
Effective value = defaults → cordis.yml → 全局设置 → 当前工作区设置.
Index limits apply immediately (next scan); port and idle-timeout values apply
on the next session start. 恢复默认 clears the active level's overrides.
Key
Default
Effect
preferredPort
15580
First port to try for the review server; auto-increments when taken
maxPortTries
20
Ports tried before giving up
idleTimeoutMinutes
30
Idle time before the singleton server auto-shuts down
openBrowserOnStart
true
Open the review page when a session starts
indexMaxRoots
64
Filename-index roots remembered (LRU)
indexMaxEntries
60000
Filename-index entry cap
indexScanCooldownMs
30000
Min interval between full rescans of one root
indexIgnore
node_modules, .git, dist, …
Directory names skipped by indexing, migration, and directory scans
Index limits apply immediately; port and idle-timeout values apply on the next
session start.
Install (published package)
dsh plugin --profile web add dsh-document-review
dsh --profile web
The plugin declares dsh.bundle (host tools + HTTP route) and dsh.client
(浏览器「审阅」面板): the review page, the model-facing tools, and the
per-Workspace「文档审阅」menu entries in the DSH sidebar all ship in one package.
Install (development)
From a DSH repository checkout:
dsh web --patch /absolute/path/to/dsh-document-review/cordis.yml
Open http://127.0.0.1:3080 and ask the agent: "Review the document at /path/to/file.md word by word."
The cordis.yml overlay inserts the plugin into the Web profile. The path in cordis.yml must be absolute; edit it to match your checkout location.
Browser review page
The review page renders the Markdown read-only. Select any text to annotate:
Comment — a note with no replacement.
Suggest replacement — propose new text for the selection.
Suggest deletion — mark the selection for removal.
Annotations appear in a side panel. Each one carries textQuote anchors and a
SHA-256 hash of the source, so the agent can verify the document hasn't changed
underneath the review (stale: true otherwise). The agent reads annotations
back via document_review_list, applies changes to the source, and marks them
resolved/accepted/rejected.
Directory mode
Pass a directory path to document_review_start (or the HTTP route) to review
many Markdown files in one session:
The page lists every .md/.markdown file (recursive, sorted; node_modules,
.git, and build-output directories are skipped; capped at 500 files).
Click a file to render it; navigation (back/forward/breadcrumb/path jump) is
the same file-manager UI as a single-file open.
Each document keeps its own .review.jsonl sidecar.
document_review_update accepts a documentPath argument (relative within the directory) to address a specific file's annotation.
HTTP routes (GUI launcher)
The client launcher calls host routes to start the review page without the model:
GET /api/v2/document-review/scan?path= (compat; the page browses via /api/fs/list)
POST /api/v2/document-review/start
{ "path": "/abs/or/relative/path", "openBrowser": false }
start returns { reused, mode, documentPath, sidecarPath, url, documents }.
Both routes are registered on the harness web server (ctx.webServer, with an
httpServer fallback for older deployments) and are re-registered
idempotently when the web-server service binds after this plugin's apply.
Inside the review page (port 15580), the settings dialog uses:
GET /api/config (merged snapshot for this root)
POST /api/config/global (write global overrides; needs review token)
POST /api/config/workspace (write this root's overrides; needs review token)
Browser launcher (client plugin)
Every Workspace row in the DSH sidebar gets a「文档审阅」entry inside its
ellipsis menu:
Open a Workspace row's ⋯ menu and choose 文档审阅 to open the review
page rooted at that workspace's directory (ctx.workspaces → workspace
path).
The sidebar exposes no per-row slot, so the entry is injected into the
portaled menu list via a MutationObserver keyed on the workspace row menu
button (工作区“{name}”的操作 / Workspace actions for {name}), which
exists only on real Workspace rows — the ungrouped bucket is skipped, and
session-row menus never get the entry.
The review server is a singleton — one server, one port (15580, with
fallbacks), reused across opens. It shuts down automatically after 30 minutes
without traffic, and the page keeps it alive with a lightweight heartbeat
while open, so no port is permanently occupied.
Styling rides the DSH --dsw-* design tokens, so the injected menu item
matches the host chrome in both light and dark themes.
Review page — file-manager browser + review
The review page is a lightweight two-mode UI:
File browser (default): toolbar with back / forward / up / refresh, a
history dropdown, and a quick-open search box (VS Code style — Ctrl+P to
focus). Breadcrumbs run from the filesystem root through a「此电脑」level (all
drives), so you can browse anywhere on the machine — including drives
outside the workspace (e.g. OneDrive on C: while the workspace lives on
E:). Directories and Markdown files are listed; clicking a directory enters
it, clicking a document opens the review view. The workspace is just the
starting point, not a boundary.
Review view: the rendered document with the full annotation UI — select
text to comment / suggest replacement / suggest deletion; the side panel
lists annotations with locate, resolve, accept, and reject actions.
Quick search is backed by an Everything-style filename index (names
only, never contents) whose scope is the directories you actually browse,
persisted to ~/.dsh-document-review/index-state.json. Typing filters the
in-memory name map (prefix beats substring); ↑/↓ navigate, Enter opens, Esc
closes. Common noise directories (node_modules, .git, dist, cache,
dot-entries, …) and .review.jsonl sidecars are skipped.
Back / forward history is a front-end stack (Alt+← / Alt+→, mouse side
buttons, and the 🕘 dropdown all work), so navigation never touches the server.
A status dot shows the singleton server's health; if it idles out, refresh
the page to restart it.
The page consumes these APIs on the singleton server:
GET /api/health singleton status (root, document count); heartbeat target
GET /api/fs/list?path= one directory: subdirectories + Markdown files, breadcrumb ancestry, parent;
`::computer` lists every drive (the「此电脑」level)
GET /api/fs/search?q= quick search over the persisted filename index (max 50 hits)
GET /api/document?path= render one Markdown document + its annotations
POST /api/annotations add an annotation (X-Review-Token required)
PATCH /api/annotations/:id update an annotation status (X-Review-Token required)
DELETE /api/annotations/:id permanently delete an annotation (X-Review-Token required)
Data contract
Any explicit absolute .md or .markdown path readable by the current OS user can be reviewed.
Relative paths resolve from the agent's current working directory.
Review records live in one central append-only JSONL store: ~/.dsh-document-review/annotations.jsonl.
Status updates append a new record with the same annotation ID; reads dedupe by ID, latest wins.
Deleting an annotation rewrites the store without it (physical removal).
Legacy .review.jsonl sidecars from older versions are auto-imported and removed on first start.
Records contain quote anchors, a source SHA-256 hash, status, timestamps, and optional replacement text.
Source changes are applied by the agent through normal DSH file-editing tools, not by the browser.
Security
The HTTP server binds only to 127.0.0.1, validates the loopback Host and same-origin requests, uses a random write token, disables CORS, and serves the selected Markdown rendering, one-level directory listings, and bundled static assets. The browser only lists directory names and reads Markdown files; every other filesystem surface is closed. Store writes use a cross-process lock and reject symbolic-link sidecars.
npm install # dev tooling (tsdown, lightningcss, react, typescript)
npm run build # host tsc → lib/, client tsc → lib/client/, tsdown → lib/client.js
npm run check # type-check both programs without emitting
The client bundle is emitted as a CJS closure-factory artifact:
window.__ModuleLoader__.load({ id: "dsh-document-review", factory: (require) => … })
with react, react-dom, and the other platform modules resolved through the
shell's frozen module table, exactly like DSH's own packages/client/ui-* bundles.