Workflow
September 3, 2026 · View on GitHub
English | 中文
This file is the procedure (how to do a job). Hard rules: AGENTS.md. Spec: conventions.md. Contributor entry: CONTRIBUTING.md. Which file to edit: README.md. Change rules in AGENTS.md; change the spec in conventions.md; change steps here.
Dev environment
Two homes. Spec: conventions.md § Homes. Test stays on test; official stays on official.
| Official / user | Sandbox | |
|---|---|---|
DSH_HOME | ~/.dsh | <repo>/.dsh-home (gitignored) |
| Command | xtz start | pnpm dev (xtz --sandbox) / link-plugin |
| Port | 3080 | 3081 |
| Job | Home |
|---|---|
Change plugin source, settings UI, link-plugin | Dedicated topic worktree. Run deterministic gates there without claiming 3081 in the normal path |
| Merged-main dogfood | Repository-root hub .dsh-home, pnpm dev, 3081 |
User product (xtz start) | Official ~/.dsh 3080 |
Inspect official home with xtz status / doctor | Official ~/.dsh 3080. Never .dsh-home / 3081 |
If 3080 is taken and xtz did not start it, do not steal it.
Before starting, pnpm dev stops a listener on 3081 only after process inspection proves it is this repository's marked sandbox dsh web --host 127.0.0.1 --port 3081 (spawned by xtz --sandbox); an unknown or unverifiable listener is a hard error and is never signalled. It never frees 3080. link-plugin always writes into .dsh-home. Do not link into ~/.dsh. Do not run dsh plugin add ./plugins/<slug> against the official default. Normally keep pnpm dev running in the repository-root hub as merged-main dogfood while edits stay in the dedicated topic worktree. A topic runs it only during an explicit bounded 3081 transfer and returns the port afterward. It rebuilds plugins/*/lib and restarts the xtz --sandbox child when host lib/index.js or cordis.patch.yml content changes, after those files exist again. Sandbox pnpm dev sets DSH_PLUGIN_TRACE=1 so every plugin host prints one-line traces (dsh-im, dsh-wecom-office, dsh-xtz-ui, dsh-sidebar, dsh-providers, dsh-market); official xtz start does not. Set DSH_PLUGIN_TRACE=0 to mute the sandbox. Unexpected sandbox exits retry with backoff; they are not treated as a host rebuild. Client lib/client.js uses host HMR (hard-refresh if the UI did not update). pnpm dev -- --once is the old build-once path. Clone, then run pnpm install before any build/check. pnpm check-home (or node scripts/doctor.mjs) is diagnosis only: it lists and fails on unsafe links from ~/.dsh; it never repairs a profile.
Repository gates: pnpm check covers version/docs/manifest policy plus type/tests; pnpm check:build additionally builds and inspects required lib/; pnpm check:path proves isolated Git path installs; pnpm check:cli checks the standalone CLI workspace (the user product). None of them publishes.
Need API keys in the sandbox: copy ~/.dsh/.credentials.yaml into .dsh-home/. Do not copy sessions/ or storages/.
Sandbox dogfood monitoring
Spec: conventions.md § Homes (sandbox dogfood).
When the user asks to start 沙箱监控 / 持续监控 / dogfood watch:
Keep-alive is mandatory. Journey-break grep is not a substitute. The hub monitor does not implement product fixes.
- Confirm this is the clean repository-root
mainhub, then startpnpm devhere (port 3081) as a background command withtimeout: 0. Same 3081 rules as above. Do not start a second sandbox. If the hub's marked sandbox is already healthy, attach the watches; do not bounce it.timeout: 0does not stop a wrapper ~10hmax_runtimekill — that kill is a hang, not “the task finished”. - Watch all of these for the session:
- Death: the
pnpm devcommand exiting,sandbox web exited, or 3081 not listening. A journey grep cannot see these. - Journey:
grep --line-bufferedjourney event=.*break=1on the hub'spnpm devlog, plus.dsh-home/traces/YYYY-MM-DD.jsonl. Not a generic error grep. origin/main: at least every 10 minutes,git fetch origin main. Notify only when fetch fails or the hub is behind. Do not chatter when already in sync.
- Death: the
pnpm devplus those watches are one set for the session. Finishing a code task or merging a PR does not stop them unless the user said stop.- If
pnpm devexits (crash, tool timeout, parent killed, wrappermax_runtime): restart it here in the same turn. Do not wait for the user to ask why the sandbox is down. A leftover 3081 listener that is the hub's marked sandbox may be reclaimed bypnpm dev. Unknown or other-checkout 3081 is a hard stop. Never touch 3080. - After a restart: confirm 3081 is LISTENing and
xtz --sandboxstayed up. Then retarget the journey watch to the newpnpm devlog. Looping exits (sandbox web exited) are not a running sandbox. If the boot failure is a product defect (wrong Node pin, staleapps/cli/libfrom a bad merge), open a GitHub issue; keep trying keep-alive; do not patch product code in the hub monitoring session. Watching a dead log is not monitoring. - When
origin/mainis ahead: if the hub is not onmain, or the working tree is dirty, stop and report. Do notcheckout, reset, or stash. If it is clean onmain,git pull --ff-only origin main. Then keep or restorepnpm dev, confirm 3081 LISTENs, retarget the watch if the process or log changed, and exercise the affected real journey.
Act on breaks. Do not wait to be asked to 发现问题 / 优化 / 帮我修. Do not implement the product fix in this hub session:
- On
journey event=… break=1or a JSONL line with"break": true, read that msgid/stream's events in.dsh-home/traces/YYYY-MM-DD.jsonl(inbound→stream_start/stream_fail→first_visible→tool→finish/abandon/ws_kick). - Classify. Separate fact / inference / guess.
- Ours (hidden stream body, overlay stacking, tool wiring, missing product we own): search open issues on this repository. If none match, open a GitHub issue (type Bug or Feature) with repro, commit sha, and plugin. Leave
pnpm devrunning. Report the issue URL. Another fixing session uses a dedicated topic worktree and green PR. A green unit suite is not that handoff. - Platform limit (for example WeCom ~5 min stream cap): say so. Open an issue only when a cheap user-visible mitigation exists that we own. Do not pretend we can lift the cap.
- Ops (official 3080 and sandbox 3081 sharing the same WeCom bot): tell the user to
xtz stopofficial; do not steal 3080; do not open an issue. - Do not open an issue for a session-wrapper process death with no product evidence. Do not duplicate an open issue; comment on it instead.
- Ours (hidden stream body, overlay stacking, tool wiring, missing product we own): search open issues on this repository. If none match, open a GitHub issue (type Bug or Feature) with repro, commit sha, and plugin. Leave
- Report the conclusion, the issue URL if any, and what you did not verify. Do not paste secrets or message bodies from traces.
- Do not commit or push unless asked. Do not implement, revert, or open a fix PR from the hub monitoring session. Do not touch official home. Irreversible, auth, or public-API changes still wait.
Parallel checkouts / worktrees
Spec: conventions.md § Git and § Homes.
One task, one short-lived topic branch in one dedicated Git worktree. The repository-root hub is reserved for clean main, pull/review/tags, and merged-main dogfood; never develop, test, or commit ordinary topic work there. Do not invent develop / release/* / hotfix/*.
Before pnpm dev or pnpm smoke:sandbox: 3081 is free, or it is this checkout's marked sandbox. Another checkout's sandbox is a hard stop — stop that sandbox in its own tree first. Unit tests, pnpm check, and CLI fake-home tests do not need 3081 and may run in parallel.
Each worktree needs its own pnpm install (root and, for CLI work, apps/cli). Do not link: any worktree into ~/.dsh.
Handoff across parallel sessions is git, not chat history: worktree path, branch (cut from origin/main), and commit / PR state. Uncommitted work stays in that worktree; do not copy it into a second tree.
Normal trunk-based main dogfood loop
The steady state. The hub is the repository-root checkout; the topic worktree is where the change lives until it merges.
- Confirm the repository-root hub is clean, on
main, current withorigin/main, and healthy on 3081. - Fetch
originand create one short-lived topic branch in a dedicated worktree fromorigin/main. - Develop and run area-specific gates in the task worktree without starting
pnpm dev. - Update with current
main, rerun required gates, and open a PR. - Merge only after required GitHub CI passes.
- Confirm the reviewed topic head is contained in
origin/main. - Fast-forward the hub with
git pull --ff-only; never reset or overwrite active work. - Keep or restore hub
pnpm dev, confirm 3081 LISTENs, and retarget journey monitoring if its process/log changed. - Exercise the affected real journey on merged
main. - A known post-merge
mainbreak is active work for a fixing session in a dedicated topic worktree, not for the hub monitor to implement in place. The hub monitor files a GitHub issue (spec: sandbox dogfood). The fixing session uses a green PR: fix forward only when the correction is small and known; revert through the same reviewed path for security, data-loss, startup, broad, or unclear regressions first.mainmust not remain knowingly broken while unrelated work continues. - Delete merged local/remote topic branches and remove only a clean task worktree. Never force cleanup; preserve and report a dirty worktree until its owner lands or moves the work.
Merge completion does not stop sandbox monitoring. Hub pnpm dev, the journey-break watch, and the 10-minute origin/main poll keep running until the user says stop; a dead wrapper or stale 3081 listener is restarted in the same turn, not parked for someone else to notice.
Bounded 3081 transfer
For pre-merge rendered UI QA, real-journey verification, irreversible migration, authentication, external side effects, or equivalent high risk: explicitly stop the hub sandbox, start the topic sandbox on 3081, verify without unrelated development, stop it, return to the hub main sandbox, confirm 3081 and monitoring, then continue. Never add another port or steal 3081. Do not make this the default plugin-development path.
CLI development
apps/cli/ is a standalone workspace; do not assume a root pnpm install installs it. Use a Node that matches DeepSeek Harness (^22.19.0 || >=24.0.0; floor is apps/cli/.node-version / versions.json node) and the pinned DSH 0.1.1-rc.2. After a CLI change, run:
cd apps/cli
pnpm install
pnpm check
node lib/cli.js --help
node lib/cli.js version --json
Prefer node lib/cli.js over a global pnpm link while developing. pnpm check uses a fake home. To inspect the real official environment, node lib/cli.js doctor; a red report on a dirty ~/.dsh is expected. Sandbox CLI debugging is rendered / real-environment verification: use the explicit bounded 3081 transfer, run pnpm dev in the topic worktree (it execs node apps/cli/lib/cli.js --sandbox start --foreground), then restore the clean-main hub sandbox. Do not link: this checkout into ~/.dsh.
Users install with apps/cli/scripts/install.sh, npm install -g xiaotaozi-dsh-cli, or bun add -g xiaotaozi-dsh-cli. Those commands require Node.js ^22.19.0 || >=24 already on PATH; they must not install or switch Node, and they must not start DSH.
Open commands match conventions.md § xtz CLI: help/version, start/web, stop, restart, open, status, config path, doctor. First xtz start seeds official web and every first-party plugin under plugins/. Extra (third-party) plugins: the in-app market (or dsh plugin --profile web add with an upstream spec). All official work is fixed to ~/.dsh; preferred port 3080. A busy or identity-unverified port is never a reason to use 3081.
start/stop/restart only manage $DSH_HOME/xiaotaozi-xtz-web.pid. Refuse an occupied 3080 that xtz did not start. init, plugin, run/ask, config dump/defaults, and update stay fail closed. Fake-home tests cover start/stop without touching the real official service.
Talking to agents
First sentence names product + environment + action. Environments are only: sandbox, official, CLI. Spec: conventions.md § Users. Do not ask for release Desktop as new work.
In <environment>, do <action> to <product>. [Do not touch <forbidden>.]
| Intent | Say |
|---|---|
| Change a plugin | In a dedicated topic worktree, change dsh-im settings and run its gates. Verify merged main in the hub, or use the explicit bounded 3081 transfer for required pre-merge QA. Do not touch ~/.dsh. |
| New plugin | Create dsh-foo (host) via dsh-plugin, install into sandbox dsh-dev, not the official home. |
| Ship to users | Sandbox already verified. Extra plugins: dsh plugin --profile web add. Do not link: official home. |
Revive Desktop / .dmg / pack | Refuse. Point at xtz. History is git show archive/desktop. |
| Test a user's first launch | xtz stop, move ~/.dsh/profiles/web aside, run xtz start. Do not rm -rf ~/.dsh. |
| See if official looks like a user machine | Supported Node (`^22.19.0 |
| Change the CLI | In a dedicated topic worktree, use apps/cli with .node-version and run pnpm check on a fake home. Use sandbox only through the explicit bounded 3081 transfer. Do not link: official home. |
Ship xtz | Follow Ship a product snapshot. Tag vX.Y.Z; GitHub Actions publishes xiaotaozi-dsh-cli. Do not npm publish from a laptop. |
| Ship the public website | Follow Deploy the public site. tcb app deploy, not tcb hosting deploy. Do not touch 3080. |
| Parallel checkout | One task, one topic branch in a dedicated worktree. Do not start pnpm dev if 3081 is another checkout. |
| Start sandbox monitoring | In the clean repository-root main hub, keep pnpm dev alive on 3081, watch journey breaks, poll origin/main every 10 minutes. Process death (including wrapper ~10h kill) is a hang: restart in the same turn and confirm 3081 LISTENs. Product / journey problems: open a GitHub issue; do not implement in the hub. Journey grep is not keep-alive. Do not touch ~/.dsh. |
Refuse or rewrite: install plugins into ~/.dsh from this repo; revive Desktop / pack / notarization; merge everyone onto ~/.dsh; delete all of ~/.dsh to test CLI install; add Git Flow standing branches (develop / release/* / hotfix/*); start a second sandbox on 3081.
Opening line for a new chat:
Per AGENTS / conventions: ordinary topic work uses a dedicated worktree without 3081; merged-main dogfood owns sandbox 3081; official ~/.dsh 3080 default seed is first xtz start. This task is: …
Rebuild official home
Do not rm -rf ~/.dsh (credentials and sessions live there).
xtz stop.- Optional: copy
~/.dsh/.credentials.yamlsomewhere outside~/.dsh. mv ~/.dsh/profiles/web ~/.dsh/profiles/web.bak-dirtyxtz start- With a supported Node (
^22.19.0 || >=24):dsh plugin --profile web listandnode lib/cli.js doctor.
Expect Git / npm deps from first xtz start (defaults) and dsh plugin --profile web add (extras). A missing plugin is an environment problem, not a reason to dsh plugin add from this repo.
Ship a product snapshot
User product is xtz (xiaotaozi-dsh-cli). Version rules: conventions.md § Versions. The live number is versions.json cliApp. bun/pnpm/install.sh only fetch that package. No Homebrew. First-party plugins stay Git-path / first xtz start; do not publish them to npm in the same step. Do not wait for a .dmg or signed pack.
.github/workflows/publish.yml publishes on push of tag vX.Y.Z using npm Trusted Publisher (OIDC). Do not npm publish from a laptop. Do not set NODE_AUTH_TOKEN.
Release commit
pnpm check,pnpm check:build,pnpm check:path,pnpm check:cli.pnpm check-homemust show official home unlinked from this repo.- Inspect official
~/.dshwithnode lib/cli.js doctor. A reddoctoron a stopped or unseeded home is environment dirt; do not weaken CLI checks. Do not reseed against#vNEWuntil that tag exists on GitHub. - One release commit: set
cliAppandapps/cli/package.jsonversionto the new number; pin everyDEFAULT_PLUGINSspec to#vX.Y.Z&path:plugins/<slug>; add theCHANGELOG.mdsection. Keepbin.xtzaslib/cli.jsandrepository.urlas this GitHub repo. - Merge to
main, tagvX.Y.Zon that commit, push the tag. npm trusts the workflow filenamepublish.ymlon that tagged commit. - After the tag job has published to npm, create the GitHub Release for the same tag and mark it Latest.
publish.ymlonly runsnpm publish(contents: read); it does not create a Release. Missing this step leaves the repo Releases page on an older tag (v0.2.1 and v0.2.2 shipped to npm with no Release page; GitHub still showed v0.2.0 as Latest). Example:gh release create vX.Y.Z --latest --title vX.Y.Z --notes-filefrom that version’sCHANGELOG.mdsection. Do not attach a laptop-built.tgzas the user install unit; npm is the artifact.
Trusted Publisher form
Spec: conventions.md § Versions. Filename is publish.yml only. Environment empty. Allow npm publish. npm does not verify the form when you save it.
Prove vs ship
workflow_dispatchwithdry_run=trueonly packs. It does not prove OIDC (no token exchange).- Version already on npm: do not republish. Bump PATCH or MINOR, then tag.
- Tag job failed before the version appeared on npm: rerun that tag's workflow. Do not bump.
- A matching Trusted Publisher plus a dummy
NODE_AUTH_TOKENor npm 10 still yieldsENEEDAUTH. OIDC exchange 404package not foundmeans the form does not match this run (wrong filename, extra environment, missing Allownpm publish). - Successful OIDC publish shows
_npmUserGitHub Actions <npm-oidc-no-reply@github.com>and a provenance attestation.
Publish job (do not regress)
permissions: id-token: writeon the workflow and the job.- Do not pass
registry-urltoactions/setup-node(it writes_authToken=${NODE_AUTH_TOKEN}and npm skips OIDC). unset NODE_AUTH_TOKENbeforenpm publish. An empty string is not unset.- After Node
22.19.0,npm install -g 'npm@^11.5.1'. Do notnpm@latest(npm 12 needs a newer Node than we pin).
Deploy the public site
Spec: conventions.md § Public website. This is CloudBase ops, not a DSH home. Do not start official 3080. Do not bounce a healthy hub 3081.
Documentation-only website work: pnpm --dir apps/website build (and local preview). Stop there.
Ship to production only when the user asked to deploy, and only from merged main (or the user explicitly named the commit).
- Topic worktree for source; merge the green PR into
main; hubgit pull --ff-only. tcb loginif needed. Use current@cloudbase/cli.- From
apps/websiteon that commit:pnpm deploy. That is localvitepress buildthentcb app deploy dshwith--deploy-path /dsh, empty install/build (VitePress Node pin stays local). Do nottcb hosting deploy. - Confirm CloudBase, not a guessed browser DNS:
tcb app info dsh -e xiaotaozi-5g279pi414331d52→ SUCCESS,appPath/dsh- Console: 静态网站托管 → 网站部署 shows app
dsh(not the empty 应用部署 list from file-only hosting uploads) tcb domains ls→dsh.xiaotaozi.ccSUCCESS, DNS OK, route/→STATIC_STORErewrite/dsh- Optional origin check:
https://xiaotaozi-5g279pi414331d52-1251794096.tcloudbaseapp.com/dsh/zh/returns 200
- Ask the user to open https://dsh.xiaotaozi.cc/zh/. Do not treat a proxied agent DNS lookup of that hostname as proof. Do not send them
*.webapps.tcloudbase.comas the public URL.
One-time domain bind (already done for dsh.xiaotaozi.cc; repeat only if the domain is missing):
- Issued SSL cert for
dsh.xiaotaozi.ccin Tencent SSL (ICP filing required). tcb domains add dsh.xiaotaozi.cc --certid <id> --access-type DIRECT -e xiaotaozi-5g279pi414331d52tcb routes addpath/→STATIC_STOREstaticstorewithpathRewrite.prefix/dsh. Never bind/to the bucket root without rewrite.- DNSPod CNAME
dsh→dsh.xiaotaozi.cc.tcbaccess.tencentcloudbase.com. - Wait until
tcb domains lsshows DNS OK.
--prune on the shared bucket is forbidden. --verify / --entry on tcb hosting deploy are irrelevant; this site uses tcb app deploy.
Create
Official “first plugin” / Cordis tutorials assume a harness checkout. Do not follow that path here. Delta: harness-plugin.md.
- Default
--kind host. Usemixedonly when the user asked for a settings page, slot, or theme. - Do not hand-create directories. Do not edit
templates/to make a new plugin. First-party work goes inplugins/. Third-party plugins are catalog rows inplugins/market, not a second source tree.
pnpm new <slug> # or: pnpm new <slug> --kind mixed
pnpm install
- Replace the
greetsample in the same turn. Logic that can run without Cordis stays in a separate file; tests import that file only. Do not fold models / IM / WeCom office / market / the right-hand files-Git-terminal panel intoxtz-ui; that plugin is chrome plus archive, task board, and git graph. The right panel isplugins/sidebar. DSHButtondefaults toghost; a danger hover must beat.ghost:hover(double the class).Inputfocus is:focus-withinon the wrap, not the inner control. - Tunable values go on the exported Schemastery
Config. - If the plugin binds / connects / adds an account and then creates a session, writes files, or otherwise does durable work: follow conventions.md § Onboarding and first work.
process.cwd()underpnpm devis this repo. Keep first work pending until the user confirms the target; the bind picker must not open at the plugin repo cwd; tests must cover that first-action race. - Then:
pnpm --filter dsh-<slug> test
pnpm --filter dsh-<slug> build
pnpm check
pnpm check:build # requires and inspects built lib/ (expands to pnpm build + check-manifest --require-lib; check:path proves the install)
- Link into the sandbox
dsh-devprofile (Install below). Creation is done only afterdump-configshows the layer.
New plugins ship with English README.md and Chinese README.zh.md. Keep both.
-
First-party README portrait. Spec: plugins/xtz-ui/docs/brand.zh.md §7. One 3D Xiaotaozi peach plus one prop that names the plugin’s job. Same set as the existing six; not the old flat glyphs, not the
/ip-as-logo32×32 peek. The same image is also the plugin’s in-app identity mark (inlined as a data URL by the client build). Do this for packages we own underplugins/. Market catalog rows do not get a peach.Write all of:
File What plugins/<slug>/docs/ip-3d.jpgSquare JPEG original plugins/<slug>/README.mdand `README.zh.md$160 \times 160 $<img src="docs/ip-3d.jpg" …>` under the H1 root README.mdand `README.zh.md$72 \times 72 \text{header} \text{cell} $<img src="plugins/ /docs/ip-3d.jpg" …> `apps/website/public/ip-<slug>.pngPNG from the same original ( sips -s format png)Replacing an existing
ip-3d.jpgin place can leave GitHub README on the old image (camo caches by path). Use a new filename if the portrait must refresh immediately.
List a third-party plugin
Spec: conventions.md § Market catalog. Third-party plugins are rows in plugins/market. plugins/ is first-party and is seeded. Do not add externals/.
Decide
List it in the market when it is a DeepSeek Harness plugin, the license is permissive, and we will offer optional install. Do not copy it into plugins/ unless we are taking ownership and seeding it.
Do not vendor deepseek-harness, a non-plugin, or a second implementation of a first-party job.
First listing
- Record the upstream URL, license, package name, and install spec.
- Add a row to
MARKET_PLUGINSinplugins/market/src/catalog.ts(id,name,version,summary,packageName,installSpec). installSpecis upstream Git or npm. Neverlink:,file:, or#path:externals/.- Do not clone the author's repo into this tree.
Later update
Bump version / summary / installSpec in the catalog when upstream ships.
Promote to first-party (rare)
Only when we will second-develop and seed it: pnpm new <slug>, port src, catalogize (four names, neverBundle, host rc pins, no dsh-tools value-import, NOTICE + upstream LICENSE, bilingual README), remove the market row, add it to DEFAULT_PLUGINS, and give it the first-party README portrait (Create step 8). link-plugin only plugins/<slug>.
Install
Build first. Profiles load lib/, not src/.
link-plugin writes the current checkout's gitignored .dsh-home without claiming a port. pnpm dev uses that sandbox home only in the clean-main hub or during the explicit bounded 3081 transfer. Do not link workspace plugins into ~/.dsh.
pnpm --filter dsh-<slug> build
node scripts/link-plugin.mjs --profile dsh-dev <slug>
- Load check only:
dsh-dev(still under.dsh-home). - Web UI or model-callable tools: verify merged
mainin the hub sandbox. For required pre-merge verification, follow the bounded 3081 transfer above, use--profile web, and leave official~/.dsh(3080) alone. - Stop if
link-pluginfails. Do not pretend it linked. - After source edits, build and run deterministic gates in the topic worktree while hub
pnpm devremains merged-main dogfood. During a bounded transfer, topicpnpm devwatches plugins, rebuildslib/, and restartsxtz --sandboxon 3081 when host output changes; stop it and return 3081 afterward. Usepnpm dev -- --onceto build once with no watch.pnpm dev -- --filter imwatches one plugin. - To skip a rebuild,
pnpm dev -- --once --patch <file>;namein that patch must be an absolute path. - Optional: copy
~/.dsh/.credentials.yamlinto.dsh-home/if the sandbox needs API keys. Do not copysessions/orstorages/.
Sandbox verification when the plugin binds then does durable work (spec: conventions.md § Onboarding and first work):
- Follow the bounded 3081 transfer, then keep topic
pnpm devrunning only for this verification. Sandbox traces stay on (DSH_PLUGIN_TRACE=1for every plugin host; officialxtz startstays silent). - Add / bind as a user would, then confirm the target. For
dsh-im, choose one project already present inworkspace.list; do not browse to a folder. The picker must not default to this repo, and cancel must not confirm cwd. - Do the first real action (first IM message, first write, first session).
- Check that work appeared only in the chosen target, not under this repository /
process.cwd(). A later action landing correctly does not excuse the first one. - A passing
pnpm --filter dsh-<slug> testis not this check. Do not call the plugin verified until that first-action path has been watched in the sandbox.
Several plugins:
for d in plugins/*/; do node scripts/link-plugin.mjs --profile dsh-dev "$(basename "$d")"; done
Developer ship (Node users): publish or pack each plugin on its own (pnpm --filter dsh-<slug> publish or pack). Git install is github:kedoupi/xiaotaozi-dsh#path:plugins/<slug> (floating) or #vX.Y.Z&path:plugins/<slug> (product tag). Never treat the repo root as one plugin package. Users take first xtz start for defaults, then the in-app market for extras.
Commit
pnpm check, the plugin in question has beenbuilded, andpnpm check-homepasses (~/.dshunlinked).git status/git diff/git log -5. If there is no.git,git initfirst. Do not addnode_modules,lib/,*.tgz,.dsh-home/, or$DSH_HOME. Do not add anexternals/tree.- One concern per commit. Split by plugin when you can. Do not bump
cliAppor pluginpackage.jsonversions on an ordinary commit. Land ordinary work on a topic branch in its dedicated worktree. Open a PR intomain; merge only after required CI passes. Do not add Git Flow standing branches. - Title:
<type>(<scope>): <imperative summary>
type: feat fix refactor docs chore test. scope is the plugin slug; repo-wide changes use repo.
5. Write the message with a HEREDOC. Do not git commit --no-verify. Do not git push unless the user asked.
6. After the commit, git status should be clean or only hold files left on purpose.
Simplify
Do this after the plugin works. Do not extract a shared layer while you are still adding features.
- Can this capability be
dsh plugin added on its own? If not, fold it into an existing plugin. Do not add a sharedpackages/workspace; path installs would not include it. - No Web UI: stay host-only and delete an empty
src/client. - Delete template leftovers (
greet, unusedConfigfields, unusedinject, unused deps). - Keep
lib/index.jssmall: no bundlednode_modules, no@deepseek-ai/dsh-tools. - Tests cover pure functions. Do not mock the whole harness for coverage.
Run pnpm check when you finish. To record it, use Commit.