Plugin Installation Spec
August 22, 2026 · View on GitHub
This document defines how
dsh-ezcommit-pluginis installed, updated, removed, and released. Goal: every user can install and upgrade it through the standard DSH CLI.
1. Background: DSH plugin model and this package
A DSH profile (as of @deepseek-ai/dsh@0.1.1-rc.2) is assembled from bundle patch layers (cordis.patch.yml lists loaded by the Loader). A bundle package can be dual-face: the package's dsh.client declaration plus exports "./client" lets dsh-client-modules compose the client bundle into the web boot graph, and the browser mounts it as a client-side Cordis plugin via the window.__ModuleLoader__ factory format — the same mechanism used by third-party skin packages such as dsh-skin-market and maid-atelier.
This package is a static dual-face bundle:
- Host half (
main/exports "."→src/index.js): loads in the Node process, registers the/ezcommit/apiprefix routes onwebServer(git collection, model arbitration, batched commits), and prints an install notice plus a self-check at startup. - Client half (
exports "./client"→src/client.js): a browser module factory exportingapply(ctx)that registers the session-header button and confirmation dialog, talking to the Host half over same-origin HTTP.
It takes effect after install and a profile restart: the session header shows [branch] [One-Click Commit] automatically. No cordis preset and no cordis_define / cordis_run are needed.
2. Package structure spec (DSH bundle contract)
dsh plugin add is a thin pnpm forwarder: it runs pnpm add <pkg> inside the profile directory, then scans the dependencies — a dependency whose resolved package declares dsh.bundle.patch in package.json joins the profile's bundle layer list; otherwise it is installed as a plain dependency with a warning. This package declares accordingly:
// package.json (excerpt)
{
"name": "dsh-ezcommit-plugin",
"main": "./src/index.js", // Host half entry: default export must be a Cordis Plugin
"exports": {
".": "./src/index.js",
"./client": "./src/client.js", // Client half: browser module factory (consumed by dsh.client)
"./package.json": "./package.json"
},
"dsh": {
"engines": { "dsh": ">=0.1.1-rc.1" },
"bundle": { "patch": "./cordis.patch.yml" }, // the key field: the bundle layer patch
"client": { "inject": [], "platform": "web" } // the key field: static client-half declaration
}
}
# cordis.patch.yml (package root)
- insert:
- id: ezcommit
name: 'dsh-ezcommit-plugin' # module specifier, resolved from the profile's node_modules
Contract essentials (verified against the official in-box bundles and dsh-skin-market / maid-atelier):
dsh.bundle.patchpoints to a loader patch list (YAML array): each item{id, name, config?}inserts/overrides a composition row;nameis a module specifier resolved from two anchors (DSH install anchor, then the profile directory);- The entry module's default export must be a Cordis Plugin (
applyfunction or an object withapply); dsh.client(platform: "web"plus aninjectarray) declares a static client half;exports["./client"]must point at the client bundle (module-factory format, factoryid= package name);cordis.patch.ymlandsrc/**must ship inside the npm package (explicitly listed infiles), otherwise the bundle layer fails to resolve after install;- Bundle rows should stay fault-tolerant: this package's Host half checks optional services (
webServer/shell/llm, …) and can never break profile boot.
3. Installation
3.1 From the npm registry (recommended once published)
dsh plugin --profile web add dsh-ezcommit-plugin
3.2 From the git repository
dsh plugin --profile web add git+https://github.com/PenguinAndy/dsh-ezcommit-plugin.git
Packages with a
preparebuild script are blocked by pnpm until allowed in the profile'spnpm-workspace.yaml; this package is dependency-free with no build script, so that prompt never appears.
3.3 Local development (file:/link: the current checkout)
dsh plugin --profile web add file:/path/to/dsh-ezcommit-plugin
# or the link: form (changes visible immediately)
dsh plugin --profile web add link:/path/to/dsh-ezcommit-plugin
Relative paths (file:., link:../dsh-ezcommit-plugin) are anchored to the invoking directory by the CLI, equivalent to absolute paths.
3.4 Verifying the install
# the composed profile should contain the ezcommit row and this package's bundle layer:
dsh --profile web --dump-config | grep -A2 ezcommit
# the profile manifest should list the bundle and the dependency:
cat "$DSH_HOME/profiles/web/package.json"
After restarting the profile (dsh web):
- the startup log prints
[dsh-ezcommit-plugin] v<x.y.z> installed (static dual-face…)andmounted /ezcommit/api/* routes; - after a browser refresh, every web session header shows
[branch] [One-Click Commit](grayed out outside a git repo or with no changes); - the client bundle is served at
/plugins/dsh-ezcommit-plugin/client.js.
4. Update and removal
dsh plugin --profile web update dsh-ezcommit-plugin # upgrade to latest
dsh plugin --profile web remove dsh-ezcommit-plugin # uninstall (bundle layer removed automatically)
Updates and removals also require a profile restart to take effect in the running process.
5. Versioning and releases
- Versions follow SemVer;
package.json.versionis the single source of truth. - Release flow: pushing a
v<x.y.z>tag triggers the GitHub Action.github/workflows/release.yml:- assert the tag matches
package.json.version; pnpm verify(package contract + HTTP-route integration on real git + client factory smoke);npm packproduces the tarball;- create the GitHub Release (auto-generated release notes) and attach the tarball;
- when the
NPM_TOKENsecret is configured, publish to npm (npm publish --access public).
- assert the tag matches
- Local release commands:
npm version <patch|minor|major>→git push --tags(or tag manually and push).
6. Troubleshooting
| Symptom | Cause and fix |
|---|---|
| No button in the UI after install | Make sure the profile was restarted (a running process does not hot-load bundles); then check the startup log for the [dsh-ezcommit-plugin] notice and mounted /ezcommit/api/* routes |
| Startup log shows "route mount failed" | The profile is not web-shaped (no webServer service) or the route prefix collides; confirm the composed profile has a webserver row and no other package claims /ezcommit/api |
| Button appears but stays disabled | The workspace is not a git repo or has no changes (by design); point the session workspace at a dirty git repo |
"Start analysis" reports NO_MODEL / SERVICE_MISSING | The session has no routed model yet, or the profile lacks llm/agents; start a normal model turn in the session first |
| Browser console reports a client-bundle error | Confirm the profile restarted and the client bundle is reachable (/plugins/dsh-ezcommit-plugin/client.js should return JS); hard-refresh the page if needed |
dsh: cannot resolve profile bundle ... | The package is not installed in the profile (interrupted/offline install); re-run dsh plugin --profile <name> add <pkg> |
warning: <pkg> declares no dsh.bundle — installed as a plain dependency | A same-named package without dsh.bundle.patch, or an old version; upgrade to a bundle-declaring version |
pnpm blocks build scripts ... allowBuilds | A git-source install triggered a prepare script; follow the prompt to add allowBuilds in the profile's pnpm-workspace.yaml (not applicable to this package) |
| Startup log shows "self-check found N problems" | The installed src/client.js is corrupt or missing; reinstall or switch to the git source |
| npm publish 404 / E403 | First publish needs --access public and npm account permissions; make sure NPM_TOKEN is an Automation/CI token |