# Plugin Installation Spec > This document defines how `dsh-ezcommit-plugin` is 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/api` prefix routes on `webServer` (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 exporting `apply(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 ` 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: ```jsonc // 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 } } ``` ```yaml # 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.patch` points to a **loader patch list** (YAML array): each item `{id, name, config?}` inserts/overrides a composition row; `name` is 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 (`apply` function or an object with `apply`); - `dsh.client` (`platform: "web"` plus an `inject` array) declares a static client half; `exports["./client"]` must point at the client bundle (module-factory format, factory `id` = package name); - `cordis.patch.yml` and `src/**` must ship inside the npm package (explicitly listed in `files`), 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) ```bash dsh plugin --profile web add dsh-ezcommit-plugin ``` ### 3.2 From the git repository ```bash dsh plugin --profile web add git+https://github.com/PenguinAndy/dsh-ezcommit-plugin.git ``` > Packages with a `prepare` build script are blocked by pnpm until allowed in the profile's `pnpm-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) ```bash 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 ```bash # 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 installed (static dual-face…)` and `mounted /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 ```bash 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](https://semver.org/); `package.json.version` is the single source of truth. - Release flow: pushing a `v` tag triggers the GitHub Action [`.github/workflows/release.yml`](../.github/workflows/release.yml): 1. assert the tag matches `package.json.version`; 2. `pnpm verify` (package contract + HTTP-route integration on real git + client factory smoke); 3. `npm pack` produces the tarball; 4. create the GitHub Release (auto-generated release notes) and attach the tarball; 5. when the `NPM_TOKEN` secret is configured, publish to npm (`npm publish --access public`). - Local release commands: `npm version ` → `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 add ` | | `warning: 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 |