JayYuen666/dsh-ocr-review ↗★ 0
@jayyuen66/dsh-ocr-review
dsh 工作区代码审查插件:把 open-code-review(ocr CLI)封装为 5 个模型工具 + 设置卡片(从 settings.yaml 选 provider/model 应用到 OCR 配置)。
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:JayYuen666/dsh-ocr-review說明文件
閱讀完整 README ↗@jayyuen66/dsh-ocr-review
English · 中文
What it does
- Wraps the external
ocr(open-code-review) CLI into 5 model tools plus 1 web settings card for dsh; the host validates tool arguments not at all, so every shape and type gate lives in this package. - The host half (
host.ts) declares 6.volatile()settings fields — as of 0.1.7 the namespace is implicit: the host projects the form from the profile entry idocr-reviewand the plugin calls nosettings.register. - It also registers 5 tools, one systemPrompt routing section (
ocr-review-routing, order 1555) and 4/_dsh/ocr-review/*endpoints. - The client half (
src/client-entry.ts→client.js) is the settings card, taking provider/model from thellm-pi-ainamespace of the settings service. - It writes provider/model into the external ocr CLI's own
~/.opencodereview/config.json— as anapi_key_cmdcommand, never as a plaintext key.
Prerequisites (external CLI)
- This package imports no OCR library, it only spawns processes:
ocr review/ocr scan/ocr delegate preview/ocr delegate rule/ocr session/ocr llm test. - Commands are built against the contract verified line-by-line on the v1.12.x source (header comments of
lib/cli.tsandlib/parse.ts), shaped likeocr review --audience agent --format json --effort medium --output. - Multi-value
--exclude/--pathare merged into one comma-separated flag, and--formatonly accepts json/text/sarif. - Command resolution order: the launcher bundled with the package first -
@alibaba-group/open-code-review(anoptionalDependenciesentry, whose own optionalDependencies pick the platform binary) resolved to the absolute path of itsbin/ocr.js- and only when that does not resolve does it fall back to a bareocron PATH. Both installation shapes keep working, so installing this plugin is enough to get a working tool without a separate brew / npm i -g step. Resolution is lazy and memoized once per process (the same shape dsh core uses to resolve@vscode/ripgrep); a failed resolve never throws at load time, which would take the whole plugin offline over one optional binary. - With neither path available the child exits 127 and the tool reports "ocr command not found", offering
brew install open-code-reviewornpm i -g @alibaba-group/open-code-review. - Platforms: macOS and Linux are the targets. Process reaping (the reaper guard around
ocr_review/ocr_scan) depends on a POSIX toolchain - bash traps and job control plus the process-group semantics ofps/pgrep/kill. None of that exists on Windows, so the package runs the bare command there (guarded byprocess.platform): no reaping, but it still works. The cost is that a hard host exit can leave an orphaned OCR process. - The provider list and the key status come from the official host channels
ctx.settings.describe()(thevalue.providersof thellm-pi-aientry) andctx.credentials. - When either is missing the card shows the degradation reason while tools and settings keep working.
- The host must be
>=0.2.0-rc.2: declared inpeerDependencies(the host checks it on plugin install from 0.1.7-rc; alpha.1 has no such gate yet) and inengines.dsh(same value, read by nothing).
Installation
dsh plugin --profile web add @jayyuen66/dsh-ocr-review
- The packages are on the public npm registry, so installation needs no credentials.
- The published files are only
host.js,client.js,cordis.patch.ymlandscripts(prepackrebuilds both bundles); source repository: seerepository.urlin package.json. - Runtime value dependencies are
@jayyuen66/dsh-plugin-shared,@deepseek-ai/schemastery(the host's fork of schemastery — only it implements the 0.1.7.volatile()resolution) andjs-yaml(the last one used only byscripts/get-cred.mjs).
pnpm blocks the dependency script on install (ERR_PNPM_IGNORED_BUILDS)
This package keeps @alibaba-group/open-code-review in optionalDependencies, and upstream ships a postinstall (scripts/install.js). pnpm 10+ does not run dependency build scripts unless allowlisted, so the install ends like this:
Error: ERR_PNPM_IGNORED_BUILDS
× installing dependencies
╰─▶ Ignored build scripts: @alibaba-group/open-code-review@1.12.11
Allowlisting is a one-time step, and only this package triggers it — no other plugin in this family depends on anything with a build script.
The straightforward fix is to allowlist it in the profile's pnpm-workspace.yaml (under /profiles/ /):
allowBuilds:
'@alibaba-group/open-code-review': true
Then re-run pnpm i. The approval persists in that profile under the exact package name and survives later failed installs.
The host also ships an approval flow of its own: a failed install surfaces "allow these scripts and retry" on the web plugin page, and an Agent can grant it for you through install_bundle's approvedBuilds once you have said so in the conversation. The host validates only the pending package names, not the conversational consent, so you must approve it explicitly.
About that postinstall: on the normal path it does nothing. The platform binary comes from upstream's optionalDependencies (@alibaba-group/ocr--, each carrying os / cpu fields so pnpm installs only the one matching your platform); install.js detects that, prints Binary provided by platform package, skipping download. and returns. Measured, ocr runs identically whether or not the script executes — the only difference is whether that code runs during install, and it only downloads when the platform package is missing, which under pnpm never happens.
If you would rather run nothing at install time, write '@alibaba-group/open-code-review': false instead: ocr still works (the launcher reads the binary straight out of the platform package directory), but the host's readPendingBuilds() only recognises entries whose value is set this to true or false, so once it is false the approval flow can no longer grant it and you maintain that entry by hand.
Enabling it in dsh
- The in-package
cordis.patch.ymldeclares- id: ocr-review/name: "@jayyuen66/dsh-ocr-review"and is pointed at bydsh.bundle.patchinpackage.json. dsh plugin --profile web add/removeregisters and removes it, then restart dsh.- The card needs a web profile (
dsh.client.platform: web,immediately: true). - The four
/_dsh/ocr-review/*endpoints hang off aninject(["webServer"])child fiber: with no webServer (a TUI host) the child never activates, so the endpoints simply do not exist while tools and settings stay available.- On the real host webServer arrives about a second after this entry, which is why it has to be a dependency rather than a one-off
ctx.getin apply.
- On the real host webServer arrives about a second after this entry, which is why it has to be a dependency rather than a one-off
- Deployment defaults can go on the profile's
config:line (cordis validates and fills defaults through the exportedConfigschema); precedence: runtime settings value > line config > built-in default.
Tools exposed to the model
| Tool | Arguments and constraints |
|---|---|
ocr_review | repo (absolute path, default = current session workspace), scope workspace/commit/branch (branch needs both from+to and is mutually exclusive with commit), effort, background, exclude (at most 50 entries), provider/model/resume (resume only with commit/branch — workspace reviews cannot be resumed), wait |
Numeric group (arguments of ocr_review) | concurrency/timeoutMinutes/maxTools/maxTokens/maxTokensBudget (mapped to --concurrency --timeout --max-tools --max-tokens --max-tokens-budget; non-integers or values below the floor are rejected; note that --max-tools values 1-49 are raised to 50 by OCR — its help text says min 50) |
ocr_scan | repo, path (at most 100 entries), exclude (50), batch none/by-language/by-directory, provider/model (native shared flags of scan_cmd, matching review), the same numeric group, wait; no git diff required |
ocr_delegate_preview / ocr_delegate_rule | The former: repo, scope/commit/from/to, exclude 50, background. The latter: repo, paths required, 1–200 entries, positional arguments after --, each escaped. Zero LLM cost on the OCR side, they return only the file list and the rule groups |
ocr_session | repo, action list/show/comments (whitelist, anything else errors), id, limit (clamped to 1–100, default 10); show/comments without an id falls back to list |
| Background and reaper | wait: false (background start, no host deadline, reclaimed by a 2-hour backstop, no --output, poll results with ocr_session) and the host reaper guard apply only to these two minute-scale commands, review and scan - that subprocess is also an official job (see the job-surface section below); that change left the tool's JSON untouched (a later output-contract rework switched it to the direct canonical object, see below) |
Settings
| Field | Values, defaults and use |
|---|---|
effort | low/medium/high, built-in default medium; used when ocr_review is called without an explicit effort |
language | 中文/English, default 中文; written as the top-level language key of the OCR config when a selection is applied |
autoVerify | default true; when true, a successful /select also runs ocr llm test --color never (capped at 90 seconds) |
maxComments | default 12, the number of comments kept in the summary; 0 = no truncation, non-integers and negatives fall back to 12 |
timeoutMinutes | default 0 = request the host cap (the host clamps to min(request, shell.maxTimeoutMs)), >0 is a number of minutes |
ocrConfigPath | where the external ocr CLI config lives; left empty it derives os.homedir()/.opencodereview/config.json, ~ and ~/ prefixes are accepted, relative paths error out. A custom value must follow the /.opencodereview/config.json layout: OCR has no file-level path override (it only ever looks at /.opencodereview/config.json, config_cmd.go:92-99), so the plugin makes it effective by injecting HOME= into the ocr subprocess (config and sessions are redirected together, and api_key_cmd carries the host-resolved data directory as get-cred's explicit first-tier locator); any path outside that layout is rejected outright at the setting layer rather than silently mismatching (the HOME injection is a POSIX assignment prefix; Windows has no such channel, so custom paths only take effect on macOS/Linux) |
Three deployment-level tuning keys are deliberately not on the settings card — they are plain (non-volatile) fields of the exported Config, settable only on the profile's config: line and taking effect on restart:
| Key | Default | Use |
|---|---|---|
llmTestTimeoutMs | 90000 | Wall clock for ocr llm test (shared by autoVerify and the card's "test connection"); raise it on slow machines |
ocrBackgroundMaxMs | 7200000 | The 2-hour backstop window for background reviews (jobs.wait to the deadline, then kill) |
stdoutMaxBytes | 400000 | Executor stdout buffer cap, shared by foreground and background runs |
Credentials and config files
| Aspect | Fact |
|---|