Development
August 16, 2026 · View on GitHub
Constraints that shape every change
- Zero npm dependencies. Plugin scripts run under whatever Node the user has (>= 20) with no install step; both reference plugins (Codex, Grok Build) made the same choice and it is why
/plugin installjust works. Hand-roll small things; if a change seems to need a dependency, it probably belongs in DSH itself. - DSH knowledge stays in two files.
scripts/lib/dsh.mjs(CLI composition) andscripts/dsh-broker.mjs(SDK wire). Any other file needing a DSH fact indicates a layering leak. - Every DSH assumption is a row in dsh-compat.md with a verification command. New assumption → new row, same commit.
- Third-party provenance goes in NOTICE. Mechanism ports, research citations, and architectural inspiration are named there with the correct license and URL in the same commit. Do not guess MIT. See CONTRIBUTING.md ground rule 4.
- stdout discipline. Bridge stdout is user-facing rendered text (or
--jsonpayloads); progress goes to stderr and the job log. Broker-runtime stdout is JSON-RPC only — never add a stdout logger to theccprofile. - POSIX only for v1 (unix sockets, pgrep). Gate any Windows work behind its own design pass.
Adding a slash command
- Add the subcommand handler in
scripts/dsh-bridge.mjs(parse withparseCommandInput, render vialib/render.mjs, wrap execution in a tracked job if it runs DSH). - Add
plugins/dsh/commands/<name>.md— frontmatterdescription+ the exact bridge invocation + presentation guidance for Claude. - Document the flags in commands.md and the one-line summary in the root README table.
- Add a test (fake-dsh fixture for anything that spawns DSH) and, if the command needs new presentation rules, extend the
dsh-run-outputskill.
Iteration guardrails
- User-visible wording lives in
commands/*.mdandlib/render.mjs; behavior lives in the bridge/libs. Change them in separate commits when possible — wording changes should be safe to ship alone. - The managed
ccprofile patch block is versioned by its marker comment; if the block's content must change, add migration handling inhandleSetup(detect the old block, replace it), because existing users already have the old text on disk. - Every pull request that changes
plugins/or.claude-plugin/bumps the version.mainis the distribution channel:/plugin installtakes whatevermainholds, unpacks it into~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/, and records that version ininstalled_plugins.json. Bumping only "on release" means every intermediate build claims to be the previous release — which is how1.0.0came to cover a dozen different trees,/pluginstopped being able to tell them apart, and a bug report could not name what it was filed against.tests/version.test.mjschecks that the four manifests agree and that the changelog leads with that version; theversionCI job fails a pull request that ships plugin code without raising the number. There is noUnreleasedchangelog section — write the entry under the version you are shipping. - Mount-time code in
lib/tool-bootstrap.mjsis load-bearing for two modes at once, and--dump-configcomposes rows without ever calling a plugin'sapply()— only a real run catches a mount failure there. See the boot smoke in testing.md item 15.
Release checklist
npm testgreen.- Manual acceptance checklist in testing.md against the pinned dsh version.
- Docs synced (README tables, commands.md, dsh-compat.md pin). NOTICE still names every third-party source with the correct license.
- English and Simplified Chinese user-entry pages synced; relative links and community templates checked.
CHANGELOG.mdentry finalized under the shipping version's heading, recording the pinned@deepseek-ai/dshnpm version used for acceptance.- Version bumps (plugin.json, marketplace.json ×2, package.json) land in the shipping pull request, not after it; tag
v<version>onmainonce merged.