xiciyang/dsh-publish-guard

dsh-publish-guard

Pre-flight checks for publishing a DSH plugin: one command answers ship / fix / block, and a fixer clears the mechanical blockers.

未分類clideepseek-harnesspublish

dsh-publish-guard

Pre-flight checks for publishing DeepSeek Harness (DSH) plugins. One command answers ship / fix / block, and a second command clears the mechanical blockers it finds.

It exists because the DSH ecosystem has ~11k plugins and the same failures repeat: a package that installs but never activates (no dsh.bundle.patch), a repository that cannot be indexed (private), a plugin that installs on Linux and dies on Android (native module), a README that answers nothing a user asks.

Install

# no install needed
npx @xiciyang/dsh-publish-guard <dir>

# or globally
npm i -g @xiciyang/dsh-publish-guard

As a DSH plugin (tool)

dsh plugin --profile <name> add @xiciyang/dsh-publish-guard   # or github:xiciyang/dsh-publish-guard

Registers one tool, publish_readiness({ dir, tests?, lint? }), so an agent can answer "can this package ship?" without knowing the script path. Same checks, one implementation.

Usage

publish-readiness ./my-plugin            # human report
publish-readiness ./my-plugin --json     # machine report
publish-readiness ./my-plugin --lint     # also run npx dsh-plugin-lint
publish-readiness ./my-plugin --tests    # also run npm test in the package

publish-fix ./my-plugin                  # dry-run: show the mechanical fixes
publish-fix ./my-plugin --apply          # write them
publish-fix ./my-plugin --apply --keep-private   # do not remove `private: true`

The verdict is data, not an exit code: publish-readiness always exits 0 and prints ready / needs-work / blocked. In CI, gate on fails.length from --json so the build fails for the reason recorded in the report.

What it checks

idseveritywhat it means
dsh-bundlefailno dsh.bundle.patch — the plugin installs but no layer activates
engines-dshfailno engines.dsh range — install/start cannot verify compatibility
license-field / license-filefailno SPDX id or no license text
private-flagfailprivate: true blocks publishing
repo-publicfailrepository not public — awesome lists and indexes skip it
deps-resolvablefaillink: / file: / ../ dependencies cannot be installed by users
abs-home-pathfail/home/<you>/… leaks into shipped files
secret-scanfailkey-shaped strings in the package
readme + 5 sectionsfail / warnREADME under 1500 chars, or missing install / usage / config / compatibility / license
keywordwarnno dsh-plugin keyword — npm discovery misses it
session-artifactswarnlogs, .playwright-mcp/, captured pages shipped by accident
test-scriptwarnno npm test entry
plugin-lintwarn / faildsh-plugin-lint exit code, when --lint is given

Configuration

enveffect
PUBLISH_GUARD_OFFLINE=1skip the GitHub visibility check (tests, CI without gh)
PUBLISH_GUARD_CANDIDATES=pathdefault candidate list for --all

publish-fix never touches engines.dsh, absolute paths or repository: those need a real decision (was it tested on that version? where does the path point?).

Compatibility

  • Node.js >= 22 (no runtime dependencies).
  • Works on any DSH version; the DSH-specific rules are read from package.json, not from a running harness. Verified against DSH 0.2.0-rc.2.
  • --lint delegates to dsh-plugin-lint via npx; without network it is skipped.

Why the checks are split into fail / warn

A failing check means the package cannot work for a user or cannot be found. A warning means it works but will be ignored or misread. Mixing the two is how people end up "fixing" a README while the plugin still never activates.

License

MIT