dsh-plugin-forge

August 15, 2026 · View on GitHub

CI

An AI-native DSH plugin generator. The AI calls forge_plugin while executing tasks: a subagent builds the plugin → it is migrated to an independent git repo under project-root/dsh-plugins/<name> (English Conventional Commit on feature completion) → hot-mounted into the current session.

中文: README.md

Tool forge_plugin

ParamTypeDefaultDescription
requirementstringrequiredPlugin requirement (Chinese preferred)
namestringautoRepo dir name (kebab-case, derived from requirement)
targetRootstringproject-root/dsh-pluginsMigration root (arg > config > default)
migratebooleantrueMigrate to an independent repo and commit
updatebooleanfalseUpdate an existing repo (guards overwrites)
installbooleanauto by defaultInstall into profile: auto-detects the current profile by default; false disables; installProfile overrides
hotbooleantrueHot-mount into the current runtime

Output JSON:

FieldDescription
ok pluginName committed build filesRequired; build = passed / skipped / failure reason
migratedToAbsolute repo path (forward slashes); absent when migrate=false
commitSubjectCommit subject; absent when nothing committed
childReportSubagent REPORT notes (truncated to 500 chars)
installed installDetail hotMounted hotDetailProfile install (installDetail explains when installed=false) / hot-mount result; neither failure blocks delivery
duplicated existingNameSubagent judged the requirement a duplicate: no new repo, existing name returned

Failure (subagent not completed / post-migration build failed / load smoke failed) throws; staging is kept.

Command /forge status

Per repo:

- <name>(<path>)
  HEAD:<short-hash> <subject>;worktree:clean|dirty|missing;last commit:<local time>,count <n>

Empty registry prints "plugin-forge 尚未创建任何插件仓库。"

Pipeline

staging → subagent develops (typecheck/test/build) → duplicate check → migrate → rebuild in target → load smoke → git init/commit → register → profile install (auto by default) → hot-mount.

  • staging: <stagingRoot>/<name>, default workspace/.forge-staging/<name>; the subagent may only write here
  • Same-name runs are mutexed in-process; stale staging cleaned up before a run when stagingTtlDays > 0
  • Migration: deepseek-harness paths in link deps and CI rewritten to target depth, registry-versioned @deepseek-ai/* deps rejected, .gitignore written (excludes node_modules/.git/.pnpm-store/lib/dist)
  • Update mode additionally runs syncRemoveStale: deletes source files in the target no longer present in staging
  • Commit: <type>: <name>[: <summaryEn>], header ≤ 72 chars, summary clipped to budget; invalid type falls back to feat
  • Registration: $DSH_HOME/plugin-forge.json, shape {version: 1, repos: [{name, path, createdAt, lastCommitAt?, commitCount, summaryZh?}]}, atomic tmp+rename write
  • Push only when explicitly configured (off by default, per AGENTS.md)

Avoiding duplicates

The guidance section does not inject a plugin list (saves context). The subagent self-checks before developing: it reads the registry and the dsh-plugins/ directory; a duplicate requirement is reported as duplicate_of → the host refuses to create a new repo and returns duplicated: true + existingName; iterate with update=true.

Config (cordis.patch.yml)

KeyDefaultDescription
targetRoot''Migration root; empty = workspace parent/dsh-plugins
stagingRoot''Staging root; empty = workspace/.forge-staging
harnessRoot''deepseek-harness checkout root; empty = auto-discovered
subagentProviderspawnSubagent provider
maxChildDepth2Subagent delegation depth cap (1–5)
childTimeoutMs2700000Subagent/build timeout (≥ 60000)
commitTypefeatConventional Commit type
pushfalsePush after commit
gitAuthorNamecsiroqaCommit author name
gitAuthorEmailjustinwangyj@163.comCommit author email
keepStagingtrueKeep staging for debugging
stagingTtlDays0Staging retention days; 0 = never clean
installProfile''Profile installed into; empty = auto-detect the current profile
autoInstalltrueAuto-install into the current profile on success (install: false disables one call)

Quality gates

typecheck/test/build in staging → pnpm install && pnpm build in the target after migration → main/types existence check → reject registry-versioned @deepseek-ai/* deps → load smoke (run apply on a real cordis Context; catches apply-time crashes — real incident: an empty command input.hint crashed DSH at boot) → host tool-name conflict guard (confirmed mechanism: dsh-tools allows cross-scope same-name registration without error, but the model-side view shadows; enforced at both load smoke and hot-mount) → diff checked before commit.

Install

pnpm install && pnpm build
dsh plugin --profile web add link:D:\2-OGP\dsh-plugin-forge

Restart dsh web. Prerequisites: Node ≥ 22, pnpm, a local deepseek-harness checkout (deps are link:ed to ../deepseek-harness).

Delivery methods

  1. Hot-mount (default): mounted into the current runtime after generation, usable this session
  2. Profile install (auto by default): current profile auto-detected (installProfile overrides), effective after restart
  3. repository source (home-level plugin-console): repository-plugins.repositories in $DSH_HOME/cordis.patch.yml, managed as panel lines — add = install, update = lock to the latest remote commit, delete = uninstall, edits take effect immediately; line format github:owner/repo#ref (&path:/packages/<subpkg> for monorepos). Forge products are single-package independent repos — push to GitHub and add directly as a source; later update=true iterations are picked up by the panel's update

Real-LLM testing (no GUI restart)

node scripts/llm-e2e-host.mjs prompt <name> <stagingDir> <targetRoot> <harnessRoot> "<requirement>"
node scripts/llm-e2e-host.mjs run <name> <stagingDir> <targetRoot> <harnessRoot> "feat: <name>: <summary>"

prompt: generates the real subagent prompt; run: executes the host-side pipeline (migration/link+CI rewrite/build/commit/register); --update exercises the update path.

Security

  • forge spawns subagents, runs networked pnpm install, builds in the target, and executes git commit — only pass requirements you trust
  • Diff checked before commit, .gitignore excludes build artifacts and deps, no automatic push/tag/release
  • The subagent may only write inside staging (within the workspace)

Demo

Session captures of the agent spontaneously requesting a plugin:

demo-1: agent spontaneously asks to archive conversation points as Markdown at each session end

demo-2: agent spontaneously proposes a process-hardening task after review