Code-graph integration for the DSH Web GUI: a "Codegraph" card in Settings → Plugins with index status, symbol search, callers / callees / impact views, and one-click sync / index; hosts the codegraph MCP server and injects a CodeGraph usage guideline into the system prompt. Requires the codegraph CLI.
Install
# from npm (prebuilt)
dsh plugin --profile web add @hyzyn/dsh-codegraph
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:hyzyn/dsh-plugin-kit#path:/packages/codegraph
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
中文 | English
The “Codegraph” card under DSH Settings → Plugins: it brings code-graph status, symbol search, and call chains into the GUI, and aligns the project directory of the codegraph MCP server for you.
Features
status --jsonfields laid out per section:initialized/version/projectPath/fileCount/nodeCount/edgeCount/lastIndexed/pendingChanges/languages/dbSizeBytesrender as cells with the raw JSON behind a<details>; a symbol drill-down returns its line-numbered verbatim source pluscallers/callees/impactlists. The CLI's staleness signals (reindexRecommended/builtWithVersion/ extractor version gap /worktreeMismatch) surface as a warning line with "rebuild index" flagged as the suggested action.- Two index paths, two timeout buckets:
sync -- <path>incremental andindex [--force] -- <path>full rebuild run onindexTimeoutMs(600 s,0= unlimited);status/query/callers/callees/impact/noderun oncliTimeoutMs(60 s,0= unlimited). Timeout and cancellation both follow a "SIGTERM → 3 s grace → SIGKILL" process-group escalation on every platform; index-class operations get a Cancel button on the card, and closing the page aborts the CLI too. - Managed MCP server row: dsh-mcp-client declares no MCP roots, so
codegraph serve --mcpresolves.codegraph/upward fromprocess.cwd()only; the plugin maintains the@deepseek-ai/dsh-mcp-clientrow in~/.dsh/cordis.patch.ymlwithconfig.cwd, and the rewrite hot-loads through watchUserPatches to rebuild the MCP connection. One server mounts one project at a time; other projects are queried withprojectPath. - Index detection reads
.codegraph/*.dband walks upward like the CLI: from the target directory up (stopping at the git root) it looks for the first.codegraph/holding an index database; the hit root is the project root, so sessions in monorepo subdirectories are no longer misread as "unindexed". Directory existence alone mistakes the CLI's own install dir~/.codegraphfor a project index, dropping the managed row on an unindexed cwd — measured there,codegraph_explore's required grows from["query"]to["query","projectPath"]. Anything short of a real index leaves the existing cwd untouched, and the card reportsindexStateplus the reason. - Write discipline: every write to
~/.dsh/cordis.patch.ymlis re-verified against an mtime+size stamp (the same CAS as dsh-mcp, at most 3 re-reads); when reusing the dsh-mcp block only the codegraph row's owncwd:line is changed in place, leaving the block's comments, its other server rows and the loader's legitimate override entries byte-identical; if the block is corrupt it refuses to append a second one (a serverName collision would break codegraph MCP loading wholesale) and reports the reason on the card. - The managed row's cwd follows the active session:
followSession(on by default) aligns the row when a session switches to a project with a valid index, otherwise falls back to the pinned path; "Set as default project" writes that path into thecodegraphsettings namespace and setsfollowSessionfalse (an explicit pin beats session following). A failed session report retries with backoff instead of being silently lost forever. - Optional per-agent MCP isolation (
mcpScope: 'per-agent', default'managed'): in the default mode one MCP server time-shares across all projects (viaprojectPathand session following). With per-agent enabled, each agent mounts its owndsh-mcp-clientinside its own Cordis scope, withcwdpinned to the index root of that agent's session directory — parallel multi-project work no longer shares a single global cwd, and the whole "write patch → hot reload → rebuild connection" chain disappears; the global managed row is suspended (disabled: true, restored automatically when you switch back to managed). The cost is one child process per agent (measured ~40MB idle Node baseline). An agent whose session directory has no usable index is not mounted at all (rather than falling back to the default project) — otherwise it would inherit another project's context, the exact class of error that gets claimed-but-not-implemented. Mechanism measurements and tradeoffs: docs/p0-plan.md. - One-click initialisation: an uninitialised directory gets an "Initialise index" button on the card that runs
codegraph init(two-step confirm) —index/syncboth require the project to be initialised first, and this was the last step that still forced users back to a terminal. - Two systemPrompt sections, switches and gating independent:
plugin:dsh-codegraph(order 150) andplugin:dsh-codegraph:usage(order 151), both behind a<command> --versionprobe;announceToAgent/usageGuidanceadd or remove the sections live through the settings namespace.
Why the MCP working directory is managed
DSH's MCP client does not declare the MCP roots capability, so once codegraph serve --mcp starts it can only search upward for .codegraph/ from its process working directory. And dsh web is often started from the home directory — in that case model calls to mcp__codegraph__* always return:
No CodeGraph project is loaded for this session.
Searched for a .codegraph/ directory starting from: /Users/you
This plugin solves that: it manages one codegraph MCP server row in ~/.dsh/cordis.patch.yml (codegraph serve --mcp, cwd = the default project path). When the default project path changes (the card's “Set as default project” or a config edit), that row is rewritten, and DSH's watchUserPatches hot reload mounts the MCP server onto the new project automatically.
Behavior details:
- It prefers reusing an existing codegraph row in the
@hyzyn/dsh-mcpmanaged block (only the cwd is filled in; other fields, including the disabled state, are left untouched); only when there is none does it write this plugin's own block, avoiding a serverName collision. - Manual rows outside the block are only detected, never touched (to avoid conflicts).
- A target path is only managed when it holds a real index: a
.dbindex file must exist inside.codegraph/. A bare.codegraph/directory is not enough — the codegraph CLI keeps its own install data in~/.codegraph(current -> versions/<v>,bundles/,codegraph.lock, no index database), so the home directory used to be mistaken for an indexed project: the plugin pinned the MCP cwd to it and reported everything as fine, whilecodegraph status --json -- ~actually returnsinitialized:falseand the tools keep answering "No CodeGraph project is loaded". When that happens the plugin leaves the existing cwd alone, creates no row, and the card shows "default project X is not a valid index" plus the one-click fix. - Multiple projects: one codegraph MCP server mounts one default project at a time; other indexed projects can be queried by passing
projectPathon the tool call, or by switching with one click in the card. - Follow semantics: the managed cwd is the session directory when following is on and that directory holds a valid index, otherwise the pinned path. A valid index means an index database inside
.codegraph/(not merely the directory existing), so the CLI's own~/.codegraphinstall dir cannot drag the cwd off; aftercodegraph uninitthe cwd falls back automatically instead of going stale. - Following is reported by the browser half (it subscribes to the active session at page load, whether or not the settings panel is open): the host has no "current session" signal of its own, so following works while a GUI page is open and uses the pinned path otherwise.
- How to turn it off: the plugin config
mcpIntegration: false(this reverts the managed row written by this plugin).
CLI surface
The card used to cover only status/query/callers/callees/impact/node/sync/index/init; every other subcommand meant going to a terminal. The flagship and everyday ones are now covered too:
explore: the same output as the MCPcodegraph_exploretool (relevant symbol source plus call paths). The model reached this through MCP while a human on the card could not get at the same capability — a bit absurd, and now fixed.context: assembles context for a task (as opposed toexplore, which targets an area).files: the file structure inside the index (language / symbol count / size).affected: maps changed files back to the tests they affect (measured in this repo: touchingsrc/index.ts→ 5 test files). Useful precisely for "I changed this, which tests should I run". The plugin deliberately does not guess the change list for you (it does not readgit statusand does not guess editor state): where the list of files to test comes from is an upstream concern, and the card only hands the list you give it to the CLI.uninit: deletes.codegraph/— the inverse ofinit, and the card's second destructive action, likewise behind a two-step confirm. Measured:-fis mandatory — without it the CLI asksContinue? (y/N), and the runner has no TTY, so it reads EOF and aborts without deleting (safe but useless); the card therefore owns the confirmation.telemetry: a read-only relay of the upstream anonymous-usage telemetry status. Deliberately no toggle — that is the user's global preference (~/.codegraph/telemetry.json, owned by the CLI's owntelemetry on|off), and flipping it for them would mean overreaching into someone else's global setting.- Query-parameter panel:
query -k/--kind(a kind filter) andcallers|callees -l/--limit(the CLI defaults to 20, which truncates large symbols and the card had no way to know).
kind is free text rather than a dropdown enumeration: when upstream adds a new kind the plugin does not have to change along with it, and a wrong value simply gets an empty result back from the CLI.
Project list (one-click switch)
One codegraph MCP server mounts one project at a time, so switching projects is a frequent action — and the card used to require typing an absolute path by hand. GET /projects plus a row of pill buttons on the card solves it.
Candidates use in-scope data sources only: the active sessions' header.cwd from the host's sessions.list(), plus paths the plugin observed itself (/follow reports, directories whose /status was queried, the current default project). It deliberately does not read ~/.dsh/sessions/ — that does hold historical project paths, but in DSH's internal storage format (session bucket names are path-encoded, logs are multi-frame zstd), so parsing it would silently break the day DSH changes that format; measured, decoding 51 directories from bucket names got not a single one right. Better a few projects short of the user's memory than a list that is right only sometimes.
Every candidate recomputes locateIndex live: only a real index qualifies as a switch target; unindexed ones are still listed but disabled (a user wants to know "this project is not indexed yet") and clicking them does nothing. The list is sorted by most recently seen, capped at 50 entries, evicting the least recently seen. Switching reuses the "Set as default project" chain (persist defaultPath → turn following off → hot-switch the MCP).
API
| Route | Method | Description |
|---|---|---|
/api/dsh-codegraph/status?path= |
GET | Index status (JSON) |
/api/dsh-codegraph/query?q=&path=&limit= |
GET | Search symbols |
/api/dsh-codegraph/callers?symbol=&path= |
GET | Look up callers |
/api/dsh-codegraph/callees?symbol=&path= |
GET | Look up callees |
/api/dsh-codegraph/impact?symbol=&path=&depth= |
GET | Look up impact |
/api/dsh-codegraph/node?name=&path= |
GET | Look up symbol / file details |
/api/dsh-codegraph/sync |
POST | Incremental sync { path } |
/api/dsh-codegraph/index |
POST | Full rebuild { path } (requires the project to be already initialised, see below) |
/api/dsh-codegraph/init |
POST | Runs codegraph init in a not yet initialised directory (creates .codegraph/ plus the initial index) and then recomputes the managed MCP row; returns 409 for an already-indexed directory |
/api/dsh-codegraph/default-path |
GET | Pinned defaultPath + effectivePath + sessionPath + followSession + prompt toggles + cliAvailable / cliProbeError / cliProbeAt + MCP managed state (indexState describes the effective path) |
/api/dsh-codegraph/follow |
POST | Report the active session directory { path } (empty = no active session); the host aligns the managed cwd and falls back for unindexed directories |
/api/dsh-codegraph/settings |
POST | Write toggles { announceToAgent?, usageGuidance?, mcpIntegration?, followSession? } (booleans) and { mcpScope? } ('managed' / 'per-agent', 400 otherwise), effective immediately |
/api/dsh-codegraph/agents |
GET | Per-agent MCP mount ledger { mode, requested, effective, reason, fallback, mounted, live, agents } — which agents mounted, on which index root, and why not (read-only) |
/api/dsh-codegraph/default-path |
POST | Set as default project { path } (requires an index database inside .codegraph/), hot-switches the MCP at the same time |
/api/dsh-codegraph/reprobe |
POST | Re-runs the <command> --version probe, returns { cliAvailable, cliProbeError, cliProbeAt } and refreshes the systemPrompt gate |
/api/dsh-codegraph/files |
GET | File structure { path, files, raw } (codegraph files --json); knobs filter / pattern / maxDepth |
/api/dsh-codegraph/affected |
GET | Affected tests { path, affected, raw } (codegraph affected --json -- <files…>); files may repeat |
/api/dsh-codegraph/explore |
GET | Exploration { path, query, output } (codegraph explore, same output as the MCP codegraph_explore tool, markdown); knob maxFiles |
/api/dsh-codegraph/context |
GET | Task context { path, task, output } (codegraph context, markdown); knob maxNodes |
/api/dsh-codegraph/uninit |
POST | Deletes .codegraph/ { path } (codegraph uninit -f); 409 for an unindexed directory; undoes the index root |
/api/dsh-codegraph/telemetry |
GET | Read-only relay of upstream anonymous-usage telemetry status { enabled, output } |
/api/dsh-codegraph/projects |
GET | Known-projects list { projects, indexedCount, effectivePath } — candidates come from live sessions plus paths the plugin observed (/follow reports, directories whose /status was queried, the default project), each re-checked for index state; monorepo subdirectories collapse to the index root |
/api/dsh-codegraph/unlock |
POST | Clears stale lock files blocking indexing { path } (codegraph unlock; idempotent — exits 0 when there is no lock) |
/api/dsh-codegraph/cancel |
POST | Cancels in-flight CLI calls { path? } (omit = cancel all); a disconnecting page also aborts its call |
/api/dsh-codegraph/diagnose |
GET | Collects a diagnostics bundle { path?, report }: report is plain text covering versions/platform, the raw CLI probe failure, index state, the managed row plus the redacted patch blocks, the ~/.codegraph daemon registrations and log tail, the most recent CLI failure, and the adoption rate |
/api/dsh-codegraph/metrics |
GET | Adoption rate { path? }: without path returns every project as { summaries, since }; with path returns { project, summary, text, since } for that project |
All routes are loopback-only, to prevent remote access. diagnose is GET-only (read-only), but it may run a probe, so it is never reachable as a side effect of a write path.
Redaction policy for the diagnostics bundle: ~/.dsh/cordis.patch.yml is shared by every MCP server, so other entries may carry credentials under headers / env. The report therefore excerpts only the two codegraph-related blocks (this plugin's block and the dsh-mcp card block) and redacts by key name: id / name / serverName / transport / command / args / cwd / disabled keep their values (exactly what you need for diagnosis), every other key becomes <redacted>, and a sensitive key such as headers: / env: taints its whole subtree (the authorization: value is wiped while its name stays). Newly added fields are redacted by default (fail-closed).
Adoption metrics
The adoption row on the card answers the question that comes after configuration: once it is set up, does the model actually use it?
- Source: the host's existing
session/eventstream (countingtool/callonly — the same public subscription surfacedsh-agent-instructionsanddsh-acpuse).tool/resultis deliberately not counted: adoption asks whether the model wants to use it, and a failed call still counts as wanting to (whether it worked is a different question, answered by the diagnostics bundle). - Numerator/denominator (two denominators; the narrow one is primary):
codegraphmatchesmcp__codegraph__*; the narrow denominator is discovery calls (grep/glob/search/find/list_dir) and the wide denominator additionally includesread/view/open. Both are reported because in real historyreadaccounts for 1956 calls against only 100 forgrep, andreadis mostly "open the file I already know I need to change" — codegraph replaces finding things, so countingreadin the primary denominator would pin the number in the single digits forever (measured: 2.2% wide vs 28.8% narrow). bash/ media / network tools are excluded from the denominator: most of what a model does with bash (running tests, installing deps, git) has nothing to do with code exploration, andread_image/read_pdf/web_searchare the same (this bullet was added after measuring real history — see below).- Measured numbers: see docs/adoption-audit.md: traced back over 177 real historical sessions — 23% session-level, 28.8% narrow, 47% over the last three days — plus a quality sample of the
querycalls codegraph received (34/47 carried a concrete symbol name). - A zero denominator reads "no exploratory calls yet", not 0%: "never explored" and "explored but grepped everything" are different facts.
- The project key is the index root (
resolveIndexedRoot) — the same criterion as the managed row's cwd and the injection gate, so several sessions in one repository (new session, subagent, host restart) merge into one row instead of scattering into samples too small to mean anything. Unindexed projects are recorded too, but the wording states that the number says nothing about prompt effectiveness, and/metricsadditionally reportsgrouped.indexed/grouped.unindexedsubtotals — the model should not be using codegraph in an unindexed project at all, and mixing those into the overall figure would produce a meaningless number. - In-memory only; a host restart resets it, and
sincereports the starting point honestly. Not persisting is deliberate: this is an observation for deciding whether to tune the prompt, not an audit log, and persisting would leave tool names and project paths on disk indefinitely. - The same data also goes into the diagnostics bundle (
/diagnose), because the first thing to separate when someone reports "codegraph doesn't seem to help" is "the model never used it" from "it used it and the results were wrong".
Request semantics (since v0.4.2):
- POST routes answer 400 for malformed / oversized / non-object bodies — an unreadable body is never treated as "no path given" and silently executed against the default project.
- Paths must exist and be directories:
status/query/callers/callees/impact/nodereturn 400 for a nonexistent path (the CLI exits 0 with empty results there, which reads like "no matches");--limit/--depthaccept positive integers only (limitis capped at 10000;depth's upper bound is clamped by the CLI itself to 10). - Ancestor semantics:
POST /default-pathbinds the repository root that holds the index when invoked on a monorepo subdirectory;/initon such a subdirectory answers 409 (the ancestor already has an index, avoiding a nested one);/followrejects reports of nonexistent directories.
Compatibility (DSH / codegraph CLI)
- DSH: the host version matrix (every tier states how it was verified — do not dress a regression run up as a compatibility claim):
0.1.7-rc.1(the previous baseline, measured 2026-09-23):node scripts/verify-codegraph-host-contract.mjs --profile test --port 3087 --dsh-bin <separately installed rc.1 dsh> --runtime-store <that install's .pnpm/node_modules/@deepseek-ai>— 42/42 passed: all 25 routes present (including/projects,/metrics,/diagnose,/unlock,/files,/affected,/explore,/context,/uninit,/telemetry,/agents), the POST and loopback gates hold,/diagnoseoutput is complete in every section, the browser-half artifact is servable and carries the latest UI, the managed MCP row is written into the home patch according to the real index, and themcpScopeswitch ("per-agent → the global row is suspended → back to managed restores it") is fully reversible with the real~/.dsh/cordis.patch.ymlbyte-identical. Two new checks specifically cover the 0.1.7 settings migration: switchingmcpScopeback to the managed baseline through the plugin's own write path after startup, and switching the default project to a temporary one — both must answer HTTP 200 (under the old API these two steps only produced 500s).- How to verify: if the machine's global dsh is not rc.1, point
--dsh-binat a separately installed rc.1 and use--runtime-storeto re-point the copied temporary profile'snode_modules/@deepseek-ai/*at that install's runtime — otherwise those links still point at the global old install, and the new cohort's compatibility preflight disables the whole old-runtime row as incompatible (the host then never starts at all, which has nothing to do with this plugin). - Another piece of live evidence already collected: starting the test profile with an isolated
DSH_HOME(with the same runtime re-pointing) → all 10 plugins mounted, 0 lines ofdisabling profile plugin row, andPOST /api/dsh-codegraph/settingsanswering 200 in both directions betweenper-agentandmanaged.
- How to verify: if the machine's global dsh is not rc.1, point
0.1.7-rc.2(the current baseline, measured 2026-09-25): the cohort moved from rc.1 to rc.2, raising thepeerDependenciesanddsh.engines.dshlower bounds together.node scripts/verify-codegraph-host-contract.mjs --profile test --port 3087(this machine's global dsh already is rc.2, so no--dsh-bin/--runtime-storeneeded) — 42/42 passed: all 25 routes, the POST / loopback gates,/diagnosesections, the browser-half artifact being servable, the managed MCP row written into the isolated home patch, andmcpScopereversible betweenper-agentandmanagedwith the real~/.dsh/cordis.patch.ymlbyte-identical. Two more: a real host loading the test profile mounts all 10 plugins with 0 lines ofdisabling profile plugin row; andpnpm -r typecheckgreen across the workspace,vitest960/960,pnpm -r buildgreen. This round changed no plugin runtime behavior (only compatibility declarations, docs and the lockfile). Per-file comparison of the lib artifacts from rc.1 → rc.2:dsh-toolsonly adds an optionalPreToolDecision.displayReason(this repo constructs no such decision),dsh-session-queryonly gained JSDoc,dsh-client-ui-themeonly added design tokens, anddsh-mcp-client/dsh-host-webserver/dsh-subprocess-local/dsh-credentials/dsh-base/dsh-settings/dsh-web-app/dsh-headlessare byte-identical;dsh-app-bootremovedskippedProfileBundlesand changedgenerateConfigSchema's signature (dropping the leadingbinName), but that is an internal surface between DSH's own CLI and app-boot that this repo never references.⚠️ Note (left as-is) — the two test totals are not from the same run: this entry records the count at the rc.2 verification point (
960/960); after this round of project-level documentation work the current value is 56 files / 965 tests repo-wide (packages/codegraphitself: 14 files / 317 tests). Neither number is changed — the current value is the ROADMAP.md “2026-09-25 re-measurement” table.The same goes for the “25 routes” figure here: compute it with the command (see the ROADMAP re-measurement table); do not treat it as a contract.
0.2.0-rc.1(the second supported baseline, added in the same round; measured 2026-09-28): no runtime code changed — only the compatibility range became two segments (see the last two bullets). Evidence: ① this repo's 13 installable packages judged one by one with 0.2.0-rc.1's own evaluator (evaluatePluginCompatibilityindsh-app-boot): the single segment^0.1.7-rc.2gets 12/12 refused (^0.1.7-rc.2desugars to>=0.1.7-rc.2 <0.2.0-0, and0.2.0-rc.1sits exactly on that-0;includePrerelease: truedoes not rescue it), while the two-segment range admits every cohort. ② With an isolatedDSH_HOMEon a real 0.2.0-rc.1 host:dsh plugin addinstalls all 10 plugins without any exemption, all 10 mount at startup with noskipping profile bundle,verify-client-uiis 5/5 PASS andverify-rss-opmlis 9/9 PASS (real download + round-trip). ③ The same packages regress green on a 0.1.7-rc.2 host (client-ui 5/5, rss-opml 9/9). ④ Per-file comparison of the two cohorts' plugin API surface: the.d.tsofdsh-tools/dsh-client-ui-slots/dsh-client-locale/dsh-host-webserver/dsh-subprocess-local/dsh-client-modules/dsh-package-manifestare byte-identical, theplugins.row.configslot contract is identical, andcordis4.0.4 plusschemastery3.18.4 are the same versions.0.2.0-rc.2(the third supported baseline, appended in the same round; measured 2026-09-29): no runtime code changed — the compatibility range only gained a third segment (see the last two bullets). This tier differs from the previous two:0.2.0-rc.2was already covered by^0.2.0-rc.1(>=0.2.0-rc.1 <0.3.0-0;semver.satisfies('0.2.0-rc.2', …)is true withincludePrerelease: true), so the new segment is an explicit declaration rather than a boundary fix — but it still went through every step of "each added cohort must be really tested". Evidence: ① the evaluator from each of the three cohorts (evaluatePluginCompatibilityindsh-app-boot; the 0.1.7-rc.2 and 0.2.0-rc.1 copies installed from the registry with their dependencies) judging this repo's 13 installable packages one by one × cohort — 3 evaluators × 3 cohorts, every combination admitted (node scripts/check-dsh-peers.mjs --app-boot <that cohort's dsh-app-boot dir>). ② With an isolatedDSH_HOMEon a real 0.2.0-rc.2 host:dsh plugin addinstalls all 10 plugins without any exemption;--dump-configwrites 0 lines to stderr and all 10@hyzynbundles appear in the composed tree; at startup all 10/10 mount with noskipping profile bundle. ③ Three layers of live acceptance on that same host:verify-client-ui6/6 PASS (all 10@hyzynclient bundles really loaded in Chrome, 0 uncaught exceptions, 0 error-level console output, 0 failed sub-requests),verify-rss-opml9/9 PASS (real download + import round-trip + cleanup), andverify-codegraph-host-contract42/42 PASS (all 25 routes, the POST / loopback gates,/diagnosesections, the browser-half artifact being servable, the managed MCP row written into the isolated home patch, andmcpScopereversible betweenper-agentandmanagedwith the real home'scordis.patch.ymlbyte-identical). ④ Per-file comparison of the plugin API surface from rc.1 → rc.2: of all 166 dependency packages of the cli + web-app only 40 have real code changes, and the host packages this repo imports (dsh-base/dsh-tools/dsh-client-ui-slots/dsh-web-app/dsh-plugin-manager/dsh-mcp-client/dsh-settings/dsh-system-prompt/dsh-credentials/dsh-scope/dsh-session-query/dsh-launch-environment/dsh-agent/dsh-hmrand more) are byte-for-byte unchanged; the only plugin-facing change isdsh-cordis-client-runner's slot catalog, 97 → 98 entries, additions only (a newsidebar.right.tab.files.actions), while the 4 slots this repo registers (plugins.row.config/settings.kit.item/settings.plugin.item/sidebar.right.pane.tab) are byte-identical. ⑤ Workspace regression:pnpm -r build/pnpm -r typecheckgreen,pnpm test92 files / 1484 tests all passing; this round touched only compatibility declarations and docs, changing no plugin runtime behavior.0.2.1-alpha.1(the fourth supported baseline; measured 2026-10-03): no runtime code changed — the compatibility range only gained a fourth segment (see the last two bullets). Like the third tier, this is an explicit declaration rather than a boundary fix:0.2.1-alpha.1was already covered by^0.2.0-rc.2(>=0.2.0-rc.2 <0.3.0-0;semver.satisfies('0.2.1-alpha.1', …)is true withincludePrerelease: true), yet it still went through every step of "each added cohort must be really tested". Evidence: ① this repo's 13 entries (12 installable packages plustemplates/hello) judged with 0.2.1-alpha.1's own evaluator (thedsh-app-bootinside the local install, whosegetDshRuntimeVersion()reports0.2.1-alpha.1): 13/13 admitted, andnode scripts/check-dsh-peers.mjs --app-boot <that install's @deepseek-ai/dsh-app-boot dir>admits all four cohorts x 13 entries. ② A real host started with an isolatedDSH_HOME(the localtest2profile copied in,--patchpinning the port): 10/10@hyzynplugins mounted, 0 lines ofdisabling profile plugin row, 0 lines ofskipping profile bundle; on the real localtest2profile the install-time judgement was exercised too:dsh plugin --profile test2 add link:<each package>(10 of them) returnedAlready up to datefor every one, with noincompatible-versionand no exemption needed (judged once before install and once at startup). ③ Four layers of live acceptance on that host:verify-client-ui --mode boot5 PASS / 0 WARN / 0 FAIL and--mode full17 PASS / 1 WARN / 0 FAIL (all 10@hyzynclient bundles really loaded in Chrome, 0 uncaught exceptions, 0 error-level console output, 0 failed sub-requests;--mode fulladditionally proves that all 8 plugins' inline config cards render (plugins.bundle.configplus theplugins.row.configrow marks) and that the settings page lists 8 kit cards under its plugin-config row; the single WARN is the sandbox refusing a PTY withposix_openpt failed, not a regression),verify-mcp-http7 PASS / 0 FAIL (all three streamable-http response modes plus a readable failure when the URL is unreachable),verify-rss-opml9 PASS / 0 FAIL (real download + import round-trip + cleanup), andverify-codegraph-host-contract42/42 PASS (all 25 routes, the POST / loopback gates,/diagnosesections, the browser-half artifact being servable, the managed MCP row written into the isolated home patch, andmcpScopereversible betweenper-agentandmanaged, with the real~/.dsh/cordis.patch.ymlbyte-identical). ④ Per-file comparison of the plugin API surface from 0.2.0-rc.2 to 0.2.1-alpha.1: of the CLI package's 20 files onlypackage.jsonchanged; the host packages this repo imports (dsh-base/dsh-host-webserver/dsh-subprocess-local/dsh-client-locale/dsh-client-ui-slots/dsh-settings/dsh-session-query/dsh-mcp-client/dsh-system-prompt/dsh-launch-environment/dsh-client-ui-theme/dsh-headlessand more) are byte-for-byte unchanged;dsh-toolsonly reorders therun_codeparameters and rewrites their wording, and drops the./invariantsubpath export (unused here;index.d.tsis byte-identical), whileevaluatePluginCompatibilityis semantically equivalent;dsh-cordis-client-runner's slot catalog went 98 to 100 entries, additions only (plugins.add.actions/shell.bottom), and every slot this repo registers is still present. ⑤ Runtime dependency drift: this tier's host carriescordis4.0.5-alpha.1 andschemastery3.18.5-alpha.1 (the first three tiers all had 4.0.4 / 3.18.4), but both tarballs are byte-identical apart frompackage.json, and app-boot detects native schemas withSymbol.for('schemastery')rather thaninstanceof; this repo'spnpm -r typecheckwas run after re-linking to those two new versions and is green. ⑥ Workspace regression:pnpm -r build/pnpm -r typecheckgreen,pnpm test106 files / 1644 tests all passing,pnpm aggregatewith no diff; this round touched only compatibility declarations, the guard and docs, changing no plugin runtime behavior. Not run:pnpm live-smoke(it copies one-off profiles into~/.dsh/profiles/live-smoke-*, and this round's execution environment only permits writes inside the workspace, so~/.dshwrites were denied; run it from a normal local terminal when needed).0.2.1-alpha.2(the fifth supported baseline; measured 2026-10-09): no runtime code changed — the compatibility range only gained a fifth segment (see the last two bullets). Like the third and fourth tiers, this is an explicit declaration rather than a boundary fix:0.2.1-alpha.2was already covered by^0.2.1-alpha.1(the same0.2.1version tuple; prereleases do not rank against each other), yet it still went through every step of "each added cohort must be really tested". Evidence: ① this repo's 13 entries (12 installable packages plustemplates/hello) judged with 0.2.1-alpha.2's own evaluator (thedsh-app-bootinside the local install, whosegetDshRuntimeVersion()reports0.2.1-alpha.2): 13/13 admitted, andnode scripts/check-dsh-peers.mjs --with-app-boot(which locates that evaluator on its own) admits all five cohorts x 13 entries. ② The install-time judgement was exercised on the real localtestprofile:dsh plugin --profile test add link:<each package>(10 of them) returnedAlready up to datefor every one, with noincompatible-versionand no exemption needed; a real host then started from that profile (dsh --profile test --patch <port overlay>, runtime0.2.1-alpha.2): 10/10@hyzynplugins mounted, 0 lines ofdisabling profile plugin row, 0 lines ofskipping profile bundle. ③ Acceptance on that host:verify-client-ui --mode full18 PASS / 0 WARN / 0 FAIL (all 10@hyzynclient bundles really loaded in Chrome; all 8 plugins' inline config cards render (plugins.bundle.configplus theplugins.row.configrow marks); the settings page lists 8 kit cards under its plugin-config row; the global-search command palette really returns hits; the terminal pane's xterm initializes; 0 uncaught exceptions, 0 error-level console output, 0 failed sub-requests — this round refused no PTY, so the fourth tier's single WARN is gone as well), andpnpm live-smoke(=live-host-smoke --bootstrap --strict) 18/18 PASS (two host instances: the authorization ladder, route gating, tool inventory, probe wording, and theclient.jsthe host actually serves) — which closes the "not run" gap the fourth tier recorded. ④ File-by-file API-surface comparison, 0.2.1-alpha.1 → 0.2.1-alpha.2: among the host packages this repo imports,dsh-mcp-client(the MCP managed row) /dsh-settings/dsh-credentials/dsh-launch-environment/dsh-sandbox/dsh-scope/dsh-user-approval/dsh-agent/dsh-client-locale/dsh-subprocessare byte-identical except forpackage.json; indsh-toolsthedefineToolexport line this repo uses is byte-identical, and the only changes areToolPresentationModelosing'both'(zero use here) and the internal PTC bridgeRunCodeBridgeOptionsgaining a requiredresolveWorkingDirectory(this repo implements no such bridge); the client slot surface only grows (addsconversation.chat.flow/conversation.chat.reasoning.body; zero removals, zero renames, and no slot this repo registers was taken away); thedsh-host-webserveranddsh-web-appchanges all sit on the host configuration plane (the listen address now takes loopback or one concrete NIC only, new TLS config,webRuntime→webStartup), names this repo never references. ⑤ Runtime deps: this tier still shipscordis4.0.5-alpha.1 andschemastery3.18.5-alpha.1 (the same pair as the fourth tier). ⑥ Workspace regression:pnpm -r build/pnpm -r typecheckgreen — and that typecheck ran afternode scripts/link-dsh-runtime.mjshad pointed this repo'snode_modules/@deepseek-ai/*at the alpha.2 set (so the type surface really was alpha.2'sdsh-tools, not the older devDeps pin),pnpm test139 files / 2420 tests all green,pnpm aggregatewith no diff. ⑦ The same round re-checked the three facts and the picker criteria indocs/os-consent-plan.md§2.1 through the host's upgrade tripwire: this tier really did change those criteria (the loopback test moved toisLoopbackHost()fromdsh-host-webserver, andallowsRemoteAuthoritieswas added), so the tripwire went red by design and the re-check is recorded at the end of that document's §2.1 and in §4 item 2.0.1.6-alpha.2/0.1.5-rc.2/0.1.0-rc.7(historical baselines, no longer supported): those tiers still had the oldregister(ns, schema)API for thesettingsservice, whereas the 0.1.7 line replaced it withSettingsForms(describe/updateplus a volatile exported Config); this package'speerDependenciesfloor^0.1.7-rc.2also makes a new host refuse to load it onto an old one, both before install and at startup. The evidence collected for those tiers at the time (0.1.6-alpha.240/40,0.1.5-rc.2no script,0.1.0-rc.7audit baseline) stays in git history for reference only.package.jsondeclarespeerDependencies: { "@deepseek-ai/dsh": "^0.1.7-rc.2 || ^0.2.0-rc.1 || ^0.2.0-rc.2 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2" }(withoptionalinpeerDependenciesMeta, purely to quiet pnpm's unmet-peer noise). From DSH 0.1.7-rc.1 on, compatibility is judged from this before install and at startup: an incompatible pair throwsincompatible-versionon install, and at startup the whole row is markeddisabled(an incompatible bundle is skipped outright).dsh.engines.dsh: ">=0.1.7-rc.2"is kept as well, as the market display slot (the host does not read it — see the next bullet; the market resolver only accepts one segment, so it carries the lowest cohort), andscripts/check-dsh-peers.mjsasserts the peer floor agrees with it.Why peer rather than
engines.dsh:dsh-app-bootwalks onlypeerDependenciesentries named@deepseek-ai/dshor starting with@deepseek-ai/dsh-, judging them withsemver.satisfies(runtime, range, { includePrerelease: true })(prereleases take part in range matching).engines.dshis still declared, though — the host does not read it, but the plugin market / community entries display compatibility from it, so it is kept in the standard form>=0.1.7-rc.2(the market resolver only accepts>=X.Y.Z[-prerelease]; an||range is judged "unverifiable" and fails closed) and made to agree with the peer floor. Evidence:evaluatePluginCompatibilityindsh-app-boot/lib/index.js, and the app-boot README's own wording that these checks use the peer declaration rather thanengines.dsh.Why the range is five segments,
^0.1.7-rc.2 || ^0.2.0-rc.1 || ^0.2.0-rc.2 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2: with prereleases taking part in matching,^0.1.7-rc.2=>=0.1.7-rc.2 <0.2.0-0— a single segment kills the whole 0.2 line, which is exactly why this repo's plugins showed as "abnormal / contains no components" in the desktop app once 0.2.0-rc.1 shipped. The second segment admits the 0.2.0-rc.1 tier and, with it, the whole 0.2.0 rc line and 0.2.x finals; the third,^0.2.0-rc.2, the fourth,^0.2.1-alpha.1, and the fifth,^0.2.1-alpha.2, all explicitly declare the current newest tiers (zero behavioral difference for a host, since the second segment already covers them; the fourth and fifth tiers each have their own measurements and API-surface comparison, see the previous bullet) — they are kept because this repo's rule is "declare every cohort you adapt to and leave the measured evidence", not "a range that is just barely sufficient". Matching any branch admits the host, and all five tiers' API surfaces have been compared file by file (previous bullet).0.1.7final and later0.1.7-rc.N/0.1.8still ride the first segment, while0.1.7-rc.1,0.1.7-alpha.2and0.1.6-*still do not pass. To temporarily admit another version combination, write the exemption into the profile's owncompatibility.json(dsh plugin --profile <p> allow-version <pkg@ver> --dsh-version <ver> --accept-risk) rather than adding segments ad hoc — every added segment must be a cohort that was really tested and whose API surface was compared.
- Codegraph CLI: version matrix — the full chain has been verified on
1.5.0(macOS) and1.6.0(Windows / macOS); the subcommands used arestatus/query/callers/callees/impact/node/sync/index, with every flag checked one by one.codegraph serve --mcpstill works (it is not listed in the top-level help, butcodegraph serve --helphas it), so the managed row needs no change. Re-verify after upgrading the CLI: flags such as-yare version-dependent (this plugin deliberately omits it, see below). - URL shape of the browser half: DSH currently goes through client-modules' combo route, so the single-package direct link
/plugins/@hyzyn/dsh-codegraph/client.jsis no longer directly usable; the browser only uses/plugins/??<id>/client.js&rev=…handed down by the boot graph (window.__DSH_BOOT__), and the plugin side needs no change. - Operating systems: Windows / macOS / Linux run the same code path, and CI is a three-platform matrix (
pnpm -r build+typecheck+test).- Windows: the CLI goes through
%COMSPEC% /d /s /cwith cmd escaping (a global npm / pnpm install only ships a.cmdshim, whichexecFilecannot start directly —ENOENT); a timeout kills the whole tree withtaskkill /pid <pid> /T /F, including the grandchild CLI inside the shim (killing only cmd.exe lets a largeindexrun to completion); rewriting the patch file keeps the file's own line endings (a CRLF file is not turned into mixed endings, and a rewrite does not change the line count). indexwill not initialise for you:codegraph index --helpsays "same result as a fresh init", but that describes the result of a full rebuild being equivalent to a fresh init — not thatindexperforms initialisation. On a directory without.codegraph/, bothindexandsyncfail withCodeGraph not initialized in <path>plusRun "codegraph init" first. The card's "Initialise index" button therefore usesinit.- Arguments are fixed to
init -- <path>:-yis not passed — that flag only exists in CLI 1.6.0 and 1.5.0 fails witherror: unknown option '-y'; omitting it does not hang either (the runner has no TTY, and both 1.5.0 and 1.6.0 were verified to take the defaults and finish).-fis deliberately not passed (the CLI uses it to guard against accidentally initialising your home directory or a filesystem root, and the plugin should not bypass that for the user). - This is the plugin's only action that writes into the user's project: it creates
codegraph.dband a self-ignoring.gitignore(containing*and!.gitignore) under.codegraph/. It touches no source file and does not modify the project's root.gitignore;codegraph uninitremoves it entirely. The card uses a two-step confirm and it is never triggered automatically. init/indextrigger the codegraph CLI's own anonymous usage statistics (upstream prints a notice; disable withcodegraph telemetry offorCODEGRAPH_TELEMETRY=0). The plugin does not change that switch — it is the user's preference.
- Arguments are fixed to
- PATH: both the probe and the calls use the plugin's
commandconfig (defaultcodegraph, resolved through PATH). When the host is started from an entry point that does not inherit the shell environment (Dock / Start menu), the CLI may be missing from PATH — neither prompt block is injected and the card cannot resolve a working command.- PATH is read once, when the host process starts, so refreshing the page or reopening the card cannot change it, and the host will not pick it up on its own. Two fixes: (1) point
commandat the CLI's absolute path (editing the profile patch triggers a hot reload and re-probes); (2) restart the host from a freshly opened terminal so the new environment block applies. - Afterwards, click re-probe on the card (
POST /reprobe) to confirm in place — no host restart needed. The card shows the actual probe failure (spawn codegraph ENOENT, a non-zero exit's stderr, a timeout), which distinguishes "not installed" from "installed but off PATH". - On Chinese (and other non-UTF-8 code-page) Windows, the CLI and cmd.exe write stderr in the console code page; the plugin decodes it with the
@hyzyn/dsh-kittolerant decoder (UTF-8 first, falling back to the code page on invalid bytes), so the card shows the original Chinese instead of���.
- PATH is read once, when the host process starts, so refreshing the page or reopening the card cannot change it, and the host will not pick it up on its own. Two fixes: (1) point
- MCP row:
dsh-mcp-clientuses the official SDK'sStdioClientTransport(the SDK depends oncross-spawn), which resolves.cmdshims on Windows itself, so the managed row needs no platform branch. - The upstream CLI ships official builds for all three platforms × x64/arm64 (bundled Node runtime); the plugin's index detection only looks for a
*.dbunder.codegraph/rather than a fixed filename, so an upstream rename does not affect it.
- Windows: the CLI goes through
Development
pnpm --filter @hyzyn/dsh-codegraph build
pnpm --filter @hyzyn/dsh-codegraph typecheck
pnpm test # repository-wide vitest (or: vitest run packages/codegraph)
pnpm --filter @hyzyn/dsh-codegraph test # this package only (managed-row matrix + CLI knobs + route regressions)
node packages/codegraph/scripts/preview-card.mjs # render the card preview HTML into .preview/ (--png needs a normal terminal: Chrome cannot start under a restricted sandbox)
# also --per-agent / --fallback: view the per-agent "active" and "fell back to managed" states offline
node scripts/verify-codegraph-indexforce.mjs --profile test --port 3086 # live end-to-end: does indexForce really spawn with --force (requires a local DSH)
node scripts/verify-codegraph-host-contract.mjs --profile test --port 3087 # live end-to-end: host contract (25 routes + gates + served artifact + managed row + per-agent mode switch); needs a local DSH
per-agent: three layers of acceptance
Each layer proves one thing; missing a layer leaves a real gap uncovered — gap-closing, not padding:
| Script | Layer | What it proves |
|---|---|---|
verify-codegraph-agent-scope.mjs |
mechanism | Minimal Cordis root: scopes can mount mcp-client, the same serverName does not collide across scopes, the global layer stays clean, disposal reclaims only its own |
verify-codegraph-host-contract.mjs |
routes / switch | Real host: 25 routes, mcpScope switching, the global managed row getting suspended and restored, real patch byte-identical |
verify-codegraph-agent-integration.mjs |
integration | Mounts the real plugin, drives a real agent/created through the real AgentRegistry, and asserts the tool lands on that agent, that an unindexed agent is not mounted, and that agent/disposed reclaims it |
The third layer was added because the first two being green still could not prove "MCP actually mounts when a user opens a session": the mechanism script uses fake agents, and the host script's host never creates an agent (every /agents round reported mounted=0). That path is the entire value of per-agent.
All three build their own temp projects, recl
…
Links
More in this category
Tencent/WeKnora#dsh-weknora★ 33809
Four read-only tools over a WeKnora knowledge base: list knowledge bases, hybrid passage search, reassemble one document's chunks in order, and WeKnora's own cited RAG or ReAct-agent answer with a resumable session id.
superdesigndev/treg★ 5266
Tool catalog for agents: search ~2,600 external endpoints (SEO and SERP, backlinks, social, people and company enrichment, ad libraries, scraping) by the task you want done, read each one's parameters and per-call price, then call it with the credential injected server-side. Ships the skill plus an MCP row that stays disabled until TREG_TOKEN is set.
TencentCloudBase/CloudBase-AI-Toolkit#dsh-plugin★ 1136
Tencent CloudBase backend for DeepSeek Harness — scaffold and deploy full-stack apps from chat, render query results as table cards with paging, sorting and CSV export, preview a deployment on its domain, and call the CloudBase MCP toolset (`mcp__cloudbase__*`) with device-code login.
gitroomhq/postiz-agent#dsh-postiz★ 515
Connects DeepSeek Harness to Postiz over MCP: list connected social media channels, fetch per-platform posting rules, and schedule, draft, or publish posts to X, LinkedIn, Instagram, Facebook, Threads, TikTok, YouTube, Reddit, Bluesky, Mastodon, Discord, Slack, Telegram and more; adds a postiz workflow skill.
EthanYoQ/Invoice-Downloader#dsh-invoice-downloader★ 507
Local IMAP invoice download, OCR, archive, and Excel reimbursement summaries for DeepSeek Harness.
anysearch-team/anysearch-dsh★ 460
AnySearch-powered real-time web and vertical search provider for DeepSeek Harness.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.