dsh-plugin-wukong-cli 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-plugin-wukong contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,423 @@
1
+ # Wukong
2
+
3
+ > **A community plugin / control center for DeepSeek Harness** — prompts, skills, MCP tool servers, and a global wallpaper with effects, all in one management panel.
4
+
5
+ **Plugin package / installer**: plugin bundle `dsh-plugin-wukong` · installer package `dsh-plugin-wukong-cli` (command `wukong`) · [GitHub](https://github.com/agassiz/dsh-plugin-wukong)
6
+
7
+ [**中文**](./README.md) · **English**
8
+
9
+ ![Wukong: wallpaper, frosted glass and the management panel (dark)](docs/preview/hero-dark.jpg)
10
+
11
+ | <img src="docs/preview/hero-light.jpg" width="190"/> | <img src="docs/preview/prompts.png" width="190"/> | <img src="docs/preview/skills.png" width="190"/> | <img src="docs/preview/mcp.png" width="190"/> | <img src="docs/preview/wallpaper.png" width="190"/> |
12
+ |--|--|--|--|--|
13
+ | Light theme | Prompts | Skills | MCP | Wallpaper |
14
+
15
+ ![version](https://img.shields.io/badge/version-0.22.0-00ff9c)
16
+ ![license](https://img.shields.io/badge/license-MIT-blue)
17
+ ![platform](https://img.shields.io/badge/platform-web%20%2B%20desktop-00ff9c)
18
+ ![runtime](https://img.shields.io/badge/runtime-DSH%200.2.0--rc.2-8957e5)
19
+ ![CI](https://github.com/agassiz/dsh-plugin-wukong/actions/workflows/ci.yml/badge.svg)
20
+
21
+ ```
22
+ ✦ Wukong v0.21.0
23
+ ┌─────────┬─────────┬─────┬────────────┬──────┬──────┐
24
+ │ Prompts │ Skills │ MCP │ Wallpaper │ Chat │ Stats│
25
+ │ scoped │ merged │ hub │ wallpaper │ sync │ usage│
26
+ └─────────┴─────────┴─────┴────────────┴──────┴──────┘
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 📑 Table of contents
32
+
33
+ - [🚀 Quick start](#-quick-start)
34
+ - [🎯 Supported upstreams](#-supported-upstreams)
35
+ - [✨ Features](#-features)
36
+ - [🗄️ State and data](#️-state-and-data)
37
+ - [⌨️ Slash commands](#️-slash-commands)
38
+ - [🖥️ Platform support](#️-platform-support)
39
+ - [🔄 One-click update](#-one-click-update)
40
+ - [🗑️ Uninstall](#️-uninstall)
41
+ - [🧰 Using a skill in chat](#-using-a-skill-in-chat)
42
+ - [🏗️ Architecture](#️-architecture)
43
+ - [📦 Packages](#-packages)
44
+ - [🗂️ Repository layout](#️-repository-layout)
45
+ - [🛠️ Development](#️-development)
46
+ - [❓ FAQ](#-faq)
47
+ - [📚 Docs](#-docs)
48
+ - [🤝 Contributing / Upstream](#-contributing--upstream)
49
+ - [📄 License](#-license)
50
+ - [🔗 Friends](#-friends)
51
+
52
+ ---
53
+
54
+ ## 🎯 Supported upstreams
55
+
56
+ Wukong is a **DeepSeek Harness plugin**, ported to and tested against the **0.2.0-rc.2 internal-API generation** of exactly these two official repositories:
57
+
58
+ | Upstream | Environment | Repo |
59
+ |---|---|---|
60
+ | **DeepSeek Harness** | `dsh web` web runtime | <https://github.com/deepseek-ai/deepseek-harness> |
61
+ | **DSH Desktop** | desktop client | <https://github.com/anywhere-labs/dsh-desktop> |
62
+
63
+ > ⚠️ **Requires DSH 0.2.0-rc.2 (or a build on the same internal-API generation).** 0.12.0 ports the plugin to the current generation: the harness settings service became "one namespace per loader entry" (`SettingsForms`), and the old `settingsNamespace` / `settingsScope` interfaces **no longer exist there**. Builds targeting the older 2.0.9-era generation (0.11.x and earlier) are therefore **no longer supported**, and this plugin deliberately ships **no backward compatibility** — you pick one generation or the other. On top of that port, 0.13.0 standardises packaging and installation: the former two packages (`@deepseek-ai/dsh-switchblade` + `@deepseek-ai/dsh-client-ui-switchblade`) became one publishable plugin package, `dsh-switchblade`; 0.14.0 then renamed the project to **Wukong** (the plugin package was renamed with it and moved to `packages/wukong/`); and 0.15.0 split the installer out of the plugin package, so the two have carried separate names since. **0.21.0 unified them as the plugin package `dsh-plugin-wukong` and the installer package `dsh-plugin-wukong-cli` (bin still `wukong`), and the plugin's own data directory stays `$DSH_HOME/wukong/`.**
64
+ >
65
+ > ⚠️ **Use builds from the official repositories above (or compatible derivatives).** Other forks / modified builds may change internal interfaces, which can prevent the panel from mounting, state from applying, commands from appearing, or even put the client into recovery mode — that is not a bug in this plugin. Before installing or reporting issues, confirm your runtime comes from these two sources.
66
+
67
+ ---
68
+
69
+ ## 🚀 Quick start
70
+
71
+ ### Install
72
+
73
+ **Two packages, two different names**: what you run is the installer **`dsh-plugin-wukong-cli`** (bin `wukong`), and what it installs is the plugin package **`dsh-plugin-wukong`**. The split is deliberate: `pnpm add dsh-plugin-wukong` (or picking `dsh-plugin-wukong` in the harness plugin manager) is already the standard path, so the installer command and the bundle should not fight over one name.
74
+
75
+ **Running it from the repository** (to install your working copy, point `--spec` at the local path):
76
+
77
+ ```bash
78
+ node cli.cjs --profile web --spec link:<repo>/packages/wukong # web profile only, installing the working copy
79
+ node cli.cjs # every profile (defaults to the published dsh-plugin-wukong@<same version>)
80
+ ```
81
+
82
+ **Once the installer is published to npm**:
83
+
84
+ ```bash
85
+ npx dsh-plugin-wukong-cli # no checkout needed; npx resolves package names, so this must be the package `dsh-plugin-wukong-cli`
86
+ npm i -g dsh-plugin-wukong-cli # or install it globally, then use the bin name
87
+ wukong # the command name after a global install (the bin)
88
+ ```
89
+
90
+ > ⚠️ Do not mix the names up: `dsh-plugin-wukong` is the **plugin package** and has no bin — run `npx dsh-plugin-wukong-cli` (or `npm i -g dsh-plugin-wukong-cli` and then plain `wukong`) for the installer. `wukong` is a bin name, not a package name, so **it must not be handed to npx directly** (npx would look for a different package called `wukong`). The pre-rename `prompt-skill-armory` stopped at **0.11.0** — the old 2.0.9-era version, which is broken on the current runtime and no longer used.
91
+
92
+ ```bash
93
+ node cli.cjs -h # print usage and exit without installing anything (same as --help)
94
+ node cli.cjs -v # print the installer + plugin versions and exit (same as --version)
95
+ ```
96
+
97
+ The installer takes the harness's **standard plugin path**:
98
+
99
+ 1. runs `pnpm add <spec>` inside the profile: the default spec is the published `dsh-plugin-wukong@<installer version>`, and `--spec <spec>` swaps it for any ordinary pnpm source (npm name, local path, tarball, git address), so `dsh-plugin-wukong` becomes a real profile dependency
100
+ 2. appends `dsh-plugin-wukong` to the profile's `dsh.profile.bundles`
101
+ 3. cleans up what much older releases left behind: the old bundle/dependency names `dsh-switchblade` and `@deepseek-ai/dsh-switchblade`, the two copy-style installs under `node_modules/@deepseek-ai/` (`dsh-switchblade` and `dsh-client-ui-switchblade`), the hand-written `ui-switchblade` row in the profile's `cordis.patch.yml` (that one entry only — every other line, blank lines included, stays byte-for-byte intact because DSH.app writes that file too), and the old `$DSH_HOME/{switchblade,wallpapers,armory-exports}` data directories. The `dsh-plugin-wukong` dependency name is **not** cleaned up and must not be: it is the current plugin package name
102
+ 4. health-checks the size of `settings.yaml` (guards the 4MB client limit) — the plugin itself never writes to that file
103
+
104
+ The installer needs **pnpm**: a profile install *is* a pnpm dependency, so a missing pnpm stops it with an error instead of falling back to another method.
105
+
106
+ Because `dsh-plugin-wukong` is a **real profile dependency with a bundle patch**, the harness plugin manager lists it: it shows up on the sidebar **Plugins** page and under **Settings → Plugins** → "Installed", where it can be toggled. The panel itself stays at **Settings → Wukong** (book icon).
107
+
108
+ Common flags:
109
+
110
+ ```bash
111
+ node cli.cjs --profile web # web profile only
112
+ node cli.cjs --profile web,desktop # pick several (repeatable, comma lists, -p is the short form)
113
+ node cli.cjs --spec dsh-plugin-wukong@0.21.0 # choose the source: npm name / local path / tarball / git URL
114
+ node cli.cjs --spec link:/abs/path/to/packages/wukong --profile web # install the working copy into a profile
115
+ node cli.cjs -h # print usage and exit (same as --help)
116
+ node cli.cjs -v # print the installer + plugin versions and exit (same as --version)
117
+ node cli.cjs uninstall --profile web # uninstall from web only
118
+ ```
119
+
120
+ `--profile` may repeat and accepts comma lists; **only without it does the installer touch every profile**. A selected standard profile is created when missing, and a misspelled name fails immediately with the list of available profiles.
121
+
122
+ `--spec` is the one source override: when given it wins, and without it the default is the published `dsh-plugin-wukong@<installer version>`. Local paths, tarballs and git addresses are ordinary pnpm specs and install exactly the same way as an npm name. `--postinstall` is the npm hook and installs nothing by itself.
123
+
124
+ ### Launch
125
+
126
+ ```bash
127
+ dsh web # or simply relaunch the DSH Desktop client
128
+ ```
129
+
130
+ Open **Settings → Wukong** (book icon).
131
+
132
+ > **Requirement**: a DeepSeek Harness runtime (0.2.0-rc.2 generation; `dsh web` or the desktop client) must already be installed — this is a DSH plugin and does not bundle the runtime. It also needs **no deepseek-harness source checkout**: the plugin package ships prebuilt esbuild artifacts (`lib/` + `client/`).
133
+
134
+ ---
135
+
136
+ ## ✨ Features
137
+
138
+ - **Prompts (scopable)** — CRUD, enable/disable, set default. Scope is a five-way choice: **global** (default) / **only this project** (matched by the session workspace directory name) / **only this session** / **all except this project** / **all except this session**. Enabled prompts are injected via `ctx.systemPrompt.section`: a scoped prompt **returns its body only for matching agents**, a non-matching section returns an empty string that the render step drops — so nothing leaks into other projects or sessions. Prompt bodies are registered with **interpolation switched off** (`interpolate: false`), so a literal `{{cwd}}` (or any `{{...}}`) in the body reaches the model verbatim instead of being treated as a template token; without that flag the renderer throws while assembling the system prompt and fails every step of every session that has the prompt enabled.
139
+ - **Skills (merged)** — one list: panel-installed (edit/toggle/uninstall) + locally scanned (managed), with an invoke hint (`/name`).
140
+ - **Skill installation**
141
+ - manual entry
142
+ - `.md` file picker
143
+ - panel `.zip` bundle import (uploaded into `$DSH_HOME/skills`)
144
+ - in-chat command `/wukong-skill-dir <dir>` (directory — a browser cannot read a host directory, so this one is chat-only)
145
+ - **MCP tool servers** — add/edit/remove stdio or HTTP(S) servers; enabling auto-connects them with per-server status, a live tool list, and a one-click connect test.
146
+ - **Wallpaper (global background & effects)** — five **original landscapes bundled** with the plugin (one click applies it), a local image/video upload, or an image URL; the wallpaper's theme tokens are one layer composed through the official `ctx.theme.overrideTokens`, so a light/dark switch just follows; tune opacity/scrim/glass/blur/fit live; **set separately on web vs desktop**. Uploaded bytes go to disk and only an id lives in the state document, so binaries never enter your config.
147
+ - **Composer hint-line style** — enable/color/size/gradient presets for the below-input hint and stats line.
148
+ - **Conversation import / export** — export selected sessions as a zip (attachments + workspace included) and restore them on another machine; the list shows title, project, time and size; **real delete** (removes the session directory + index entry, and refuses a session that is still live); import streams through the session-persistence service. Exported zips live in `$DSH_HOME/wukong/exports/` and are pruned on every export: the 10 newest are kept and anything older than 7 days is removed.
149
+ - **Usage stats (cc-switch style)** — range filter (all / 30d / 7d / today); input / output / cache-read / cache-write tokens, cache-hit rate, cost estimate; request log (time / project / in-out / cache / cost / latency / first-token / status); provider (project) stats and model stats; SVG dual-axis trend chart (24h split for today). Both the stats and the session list read through the harness `sessionQuery` service (the project-directory encoding matches the persistence backend), and the cache-hit rate and first-token time come from real usage data.
150
+ - **One state document** — all managed state lives in one private JSON document (next section), written **atomically** (temp file + rename) so it never competes with the harness settings document.
151
+ - **Standard plugin packaging** — one package, `dsh-plugin-wukong`, declares both the host bundle patch and the browser panel (`dsh.bundle.patch` + `dsh.client`); once installed it appears on the sidebar **Plugins** page and under **Settings → Plugins** → "Installed", where the plugin manager can toggle it.
152
+ - **Bilingual UI** (简体中文 / English).
153
+ - **Settings sidebar book icon**.
154
+
155
+ ---
156
+
157
+ ## 🗄️ State and data
158
+
159
+ Since 0.12.0 Wukong no longer uses a harness settings namespace: all managed state lives in the plugin's own document and directories.
160
+
161
+ | Path | Contents |
162
+ |---|---|
163
+ | `$DSH_HOME/wukong/state.json` (default `~/.dsh/wukong/state.json`) | prompts, managed skills, custom commands, MCP servers, wallpaper/effect settings (wallpapers store ids only) |
164
+ | `$DSH_HOME/wukong/wallpapers/` | uploaded wallpaper / video bytes |
165
+ | `$DSH_HOME/skills/` | skills installed from a zip or a directory (the official filesystem skill provider scans and watches this root) |
166
+ | `$DSH_HOME/wukong/exports/` | conversation export zips |
167
+
168
+ The panel talks to the Host only through the plugin's own **same-origin HTTP routes** (the old `api.settings.mutate/describe` and `ctx.settingsScope` paths are gone):
169
+
170
+ | Route | Purpose |
171
+ |---|---|
172
+ | `GET` / `POST /api/wukong/state` | read / write the state document (all panel CRUD goes here): `GET` answers `{ok, revision, state}`; `POST` takes `{state, ifRevision}` and normalizes the document on every accepted write, incrementing `revision` |
173
+ | `GET /api/wukong/skills` | the skill + managed-command catalog the panel needs: `{ok, revision, skills, commands}`, so the browser no longer reconstructs it from the whole state document |
174
+ | `POST /api/wukong/mcp/test` | one-shot MCP connect test |
175
+ | `GET /api/wukong/market` · `POST /api/wukong/market/install` | marketplace: search the aggregated catalogs (`kind=mcp\|skill` plus `q`) — official MCP Registry, DSH MCP Market, ClawHub, SkillsMP and the bundled set — and install one explicit item id; an MCP server that needs env vars is installed disabled and the answer lists `needsEnv` |
176
+ | `POST /api/wukong/skills/zip` | upload a zip skill bundle and extract it into `$DSH_HOME/skills` |
177
+ | `POST /api/wukong-wallpaper/upload` · `GET /api/wukong-wallpaper/image/<id>` | wallpaper upload (streamed to disk, 60MB cap) / read-back (streamed, `Range` supported) |
178
+ | `GET /api/wukong-wallpaper/preset/<id>.jpg` · `<id>-thumb.jpg` | the five bundled landscapes and their thumbnails (`dawn-peaks` / `misty-forest` / `desert-dusk` / `night-coast` / `alpine-lake`) |
179
+ | `GET /api/wukong/version` | version check: `{ok, latest, current}` (`current` is read at runtime from the plugin's own `package.json`; `latest` is the newest published version, or an empty string when unreachable) |
180
+ | `GET /api/wukong/stats` · `/sessions`, `POST /api/wukong/export` · `/import` · `/delete` · `/update` | stats, session list, export/import/delete, one-click update (the update runs `dsh-plugin-wukong-cli` and needs explicit confirmation) |
181
+
182
+ Write concurrency: when `ifRevision` does not match the current `revision`, `POST /api/wukong/state` answers **HTTP 409** with `{ok:false, error:'stale', revision, state}`, and the caller is expected to adopt the returned document (a bare flat state document is still accepted for compatibility). Every read also normalizes the document first.
183
+
184
+ ### Marketplace (MCP and skills)
185
+
186
+ Both the Skills and the MCP tab carry a collapsible market block, and the same data is available from `GET /api/wukong/market?kind=mcp|skill&q=<query>`. The market is an **aggregation and installation layer**, not the owner of any catalog, and an upstream listing is never treated as a security review:
187
+
188
+ | Source | Use |
189
+ |---|---|
190
+ | Official MCP Registry (`registry.modelcontextprotocol.io`) | MCP server discovery: npm package with a stdio transport, or a streamable-http endpoint, plus version and environment-variable declarations |
191
+ | DSH MCP Market snapshot | a curated, DSH-shaped MCP catalog that maps directly onto the plugin's MCP rows |
192
+ | ClawHub | skill search (suspicious entries hidden); installs fetch `SKILL.md` through the public file API and can pin a version |
193
+ | SkillsMP | skill metadata plus GitHub stars; installs go back to the entry's GitHub source and download the whole skill directory |
194
+ | Bundled set | offline fallback: 3 MCP servers and 2 skill templates, installable with no network |
195
+
196
+ - Every item carries its source name, a trust label (`bundled` / `official` / `curated` / `community`), permission notes, popularity, and the environment variables it needs; **nothing installs until a search has listed it and an explicit item id is chosen**.
197
+ - An unreachable source only affects itself (the status line says so); when every source fails the market falls back to the last successful result under `$DSH_HOME/wukong/market-cache` and then to the bundled set.
198
+ - An MCP install writes the plugin's own MCP row: `serverName` avoids collisions, and a server that needs environment variables is installed **disabled** with `needsEnv` in the answer, referencing them as `${NAME}`.
199
+ - Skills install along two paths: a single-file template becomes a managed runtime skill (visible in the panel at once, usable in every session, kept across restarts); a repository skill with extra files is written to `$DSH_HOME/skills/<name>` for the harness filesystem skill provider.
200
+ - Security boundary: item schema validation, skill-relative paths that refuse absolute paths, `..`, backslashes and control characters, 1MB per file, 8MB per catalog, 16MB per archive, a 10-second timeout per source, at most 64 files per skill, and a containment pass over extracted GitHub archives; an install never modifies the profile and never runs downloaded code.
201
+
202
+ The skill catalog (the Skills tab list) still comes over remote RPC (`api.skills.list`). Every wukong route rejects cross-site callers: a foreign or opaque (`Origin: null`) origin, or `Sec-Fetch-Site: cross-site`, gets **HTTP 403**; a request without an `Origin` is accepted only on a loopback host.
203
+
204
+ ---
205
+
206
+ ## ⌨️ Slash commands
207
+
208
+ | Command | Purpose |
209
+ |---|---|
210
+ | `/wukong-skill-dir <dir>` | install a skill directory (`SKILL.md` + references) into the user skill root |
211
+
212
+ This is the only command left, because it is the one thing the panel cannot do — a browser cannot read an arbitrary host directory. Listing, enable/disable, uninstall, markdown and zip installs, state inspection, and the export/import of a bundle patch layer all live in the panel and no longer have a command each: they either duplicate the panel or error out the moment the `/` menu picks them without their argument. The `input` this command declares keeps a menu pick in the composer, so the path can be typed instead.
213
+
214
+ The old `/armory-*` aliases went away in 0.14.0 — only the `/wukong*` names exist now.
215
+
216
+ > Agent-preset (prompt profile) management was removed in 0.12.0: `/wukong-profile` no longer exists, and the panel never exposed such a tab (the `@deepseek-ai/dsh-agent-presets` package does not exist in this generation).
217
+
218
+ ---
219
+
220
+ ## 🖥️ Platform support
221
+
222
+ | Environment | Panel | `/wukong` commands |
223
+ |---|---|---|
224
+ | `dsh web` ([deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness), 0.2.0-rc.2) | ✅ | ✅ |
225
+ | DSH Desktop client ([anywhere-labs/dsh-desktop](https://github.com/anywhere-labs/dsh-desktop)) | ✅ | ✅ |
226
+
227
+ Achieved via profile composition (the installer installs `dsh-plugin-wukong` as a profile dependency and selects it in `dsh.profile.bundles`) — no client fork and no hand-edited `cordis.patch.yml`.
228
+
229
+ Archives and updates are cross-platform too: conversation export/import tries `zip`/`unzip`, then bsdtar (`tar`), then PowerShell, and the one-click updater calls `npm` on POSIX and `npm.cmd` only on Windows.
230
+
231
+ ## 🔄 One-click update
232
+
233
+ Every time the panel opens it asks `GET /api/wukong/version`, which the host answers with `{ok, latest, current}`: `current` is the version read at **runtime** from the plugin's own `package.json` beside `lib/` (the compiled-in constant in the panel is only a fallback, so the two can no longer drift apart), and `latest` is the newest published version — asked of the installer package `dsh-plugin-wukong-cli` first and of `dsh-plugin-wukong` second, returning an empty string when the registry is unreachable or the package is unpublished. The panel shows `current` in its version badge and compares it with `latest`; when a newer version exists a banner appears:
234
+
235
+ > New version vX.Y.Z (current v0.21.0) [Update now]
236
+
237
+ Clicking "Update now" makes the host run, through the platform `npm` (Windows: `npm.cmd`), `npm exec --yes --package=dsh-plugin-wukong-cli -- wukong`: it installs and runs the **installer package** in place → "Update complete, please restart the client". The route requires `{"confirm":true}` in the body and otherwise answers HTTP 400 `confirmation-required`.
238
+
239
+ ## 🗑️ Uninstall
240
+
241
+ ```bash
242
+ node cli.cjs uninstall --profile web # or node cli.cjs uninstall (every profile)
243
+ ```
244
+
245
+ Removal mirrors installation: `pnpm remove dsh-plugin-wukong` (with no pnpm it only warns and drops the bundle selection, leaving the dependency in the profile), drops the bundle from `dsh.profile.bundles`, and runs the same legacy cleanup as the installer; finally it deletes the plugin's own directory `$DSH_HOME/wukong/` (state document + wallpapers + conversation exports).
246
+
247
+ Skills already extracted under `$DSH_HOME/skills/` are kept on purpose, but the managed configuration (prompts / skills / MCP / wallpaper) is **deleted with `$DSH_HOME/wukong/`** — since 0.14.0 uninstalling is a full wipe.
248
+
249
+ ## 🧰 Using a skill in chat
250
+
251
+ Type `/` + skill name in the input:
252
+
253
+ ```
254
+ /code-review
255
+ /mission-keeper
256
+ ```
257
+
258
+ The skill body is injected and the agent executes it; `modelInvocable` skills can also be called autonomously by the model.
259
+
260
+ ## 🏗️ Architecture
261
+
262
+ ```
263
+ DSH runtime (0.2.0-rc.2)
264
+ │
265
+ ├─ Web panel: the client face of dsh-plugin-wukong (dsh.client)
266
+ │ GET/POST /api/wukong/state
267
+ │ GET /api/wukong/skills
268
+ │ POST /api/wukong/mcp/test
269
+ │ POST /api/wukong/skills/zip
270
+ │ POST /api/wukong-wallpaper/upload
271
+ │ GET /api/wukong-wallpaper/image/<id>
272
+ │ GET /api/wukong/stats | /sessions | /version
273
+ │ POST /api/wukong/export | /import | /delete | /update
274
+ │
275
+ ├─ remote RPC: api.skills.list
276
+ │
277
+ └─► Host row: id=wukong, name=dsh-plugin-wukong (bundle)
278
+ • $DSH_HOME/wukong/state.json (atomic writes)
279
+ • ctx.systemPrompt.section (scope-matched injection)
280
+ • ctx.skills.register
281
+ ```
282
+
283
+ - **One plugin package, two faces**: `dsh-plugin-wukong`'s `package.json` points `dsh.bundle.patch` at its own `cordis.patch.yml` (the single host row, `id: wukong`) and declares `dsh.client` (`platform: web` + `inject`) pointing at `exports["./client"]` → `client/client.js`. The panel is discovered from the **same entry's** `dsh.client` field, so **no second plugin package and no second loader row** are needed; the installer is a separate package (`dsh-plugin-wukong-cli`, not a bundle).
284
+ - **Host side** owns prompt/skill/command/MCP/wallpaper CRUD and persistence (`$DSH_HOME/wukong/state.json`), injects prompts through `ctx.systemPrompt.section` (scope-matched, interpolation off) and registers skills via `ctx.skills.register`; it also serves the `/api/wukong/*` and `/api/wukong-wallpaper/*` routes (state read/write, skill catalog, MCP test, zip skills, conversation import/export, session delete, usage stats, version check, one-click update).
285
+ - **Panel side** reads and writes the state document over the plugin's own same-origin routes and reads the skill catalog from the plugin's own `GET /api/wukong/skills` — it **never touches a settings namespace**, never talks to DSH internals directly, and does not depend on remote APIs such as `api-remotes` or `sessions`. Its load-time declarations list only packages that exist: `dsh.client.inject` is `@deepseek-ai/dsh-client-locale`, `@deepseek-ai/dsh-client-ui-settings`; its cordis service inject is `['slots','locale']`.
286
+ - **Optional peers**: every `@deepseek-ai/*` runtime package plus `react` is `optional` in `peerDependenciesMeta`, and there are no runtime `dependencies`. The reason is that those runtime packages are not on the public npm registry, so declaring them as required peers made `pnpm add` fail with `ERR_PNPM_FETCH_404`; optional peers let pnpm install the plugin while the host still provides the modules at runtime.
287
+ - **Installer** (`dsh-plugin-wukong-cli`) does exactly three standard things — `pnpm add`, select the bundle, clean up the old copy-style install — and web and desktop share the mechanism.
288
+
289
+ ## 📦 Packages
290
+
291
+ | Package | Purpose |
292
+ |---|---|
293
+ | `dsh-plugin-wukong-cli` | installer CLI (bin `wukong` → `./cli.cjs`, with uninstall): profile discovery, bundle registration, legacy cleanup. It declares **no `dsh` field, so it is not a bundle**; its `files` list is `cli.cjs` plus the docs — it ships no plugin copy, and installs exactly the source its spec names |
294
+ | `dsh-plugin-wukong` | the one plugin package (`packages/wukong/`): host service (state document + prompts/skills/commands/MCP/wallpaper/conversations/stats + routes) **and** the web panel (six tabs: prompts/skills/MCP/wallpaper/chat/stats); it declares `dsh.bundle.patch` and `dsh.client` (platform web) and must be installed into a profile to take effect |
295
+
296
+ The plugin package `dsh-plugin-wukong` declares both faces in one package (`dsh.bundle.patch` + `dsh.client`) — the same shape the community `dsh-plugin` uses — while the installer `dsh-plugin-wukong-cli` only puts it into a profile. Two names on purpose: `pnpm add dsh-plugin-wukong` (or picking `dsh-plugin-wukong` in the harness plugin manager) is already the standard path, so the installer command and the bundle should not fight over one name.
297
+
298
+ ## 🗂️ Repository layout
299
+
300
+ ```
301
+ dsh-plugin-wukong/
302
+ ├── cli.cjs # one-command install/uninstall CLI (pnpm add + bundle + legacy cleanup)
303
+ ├── package.json # installer package `dsh-plugin-wukong-cli` (bin: wukong → ./cli.cjs)
304
+ ├── packages/ # shipped truth: the one plugin package (lib + client + src + manifest)
305
+ │ └── wukong/ # plugin package `dsh-plugin-wukong`
306
+ │ ├── package.json # dsh.bundle.patch + dsh.client + optional peers
307
+ │ ├── cordis.patch.yml # the single host row (id: wukong)
308
+ │ ├── lib/ # build output: index.js + invariant.js
309
+ │ ├── client/client.js # build output: browser panel (module-loader bundle)
310
+ │ ├── wallpapers/ # the five original bundled landscapes (JPG + thumbnail)
311
+ │ └── src/ # host sources + src/client/ panel sources
312
+ ├── scripts/build-lib.mjs # esbuild build of the shipped artifacts (devDependencies: esbuild + typescript and @types/*)
313
+ ├── scripts/verify-shipped.mjs # artifact structure/syntax checks (used by CI)
314
+ ├── scripts/typecheck.mjs # tsc type check (reports a skip and exits 0 without link:dev)
315
+ ├── scripts/build-shipped.mjs # optional: sync sources/artifacts from a DSH monorepo checkout (never overwrites manifest, patch or panel bundle)
316
+ ├── scripts/link-dev.mjs # symlinks the installed harness packages into node_modules/@deepseek-ai/ (for the differential tests and the type check)
317
+ ├── test/ # node --test cases (state / same-origin / archive / patch / invariants / conversations+stats / prompt sections / wallpaper Range + bundled landscapes)
318
+ ├── docs/preview/ # real panel screenshots
319
+ ├── demo/demo.html # UI preview (mock data)
320
+ ├── README.md # this file (zh) · README.en.md (en)
321
+ ├── UPSTREAM.md # upstream integration guide
322
+ ├── CHANGELOG.md # version history
323
+ ├── PUBLISHING.md # release guide
324
+ ├── TESTING.md # testing guide
325
+ ├── LICENSE # MIT
326
+ └── .github/workflows/ci.yml # CI quality gate
327
+ ```
328
+
329
+ > ⚠️ The two pre-0.13 packages (`@deepseek-ai/dsh-switchblade` + `@deepseek-ai/dsh-client-ui-switchblade`) and their source mirror `plugin-src/` are both gone, and 0.14.0 renamed the package directory from `packages/switchblade/` to `packages/wukong/`. The shipped truth is `packages/wukong` (`lib/` + `client/` + `src/`) only; recover the old directories from git history if ever needed.
330
+
331
+ ## 🛠️ Development
332
+
333
+ After editing source, rebuild the shipped artifacts (**no deepseek-harness checkout needed**):
334
+
335
+ ```bash
336
+ npm install # devDependencies: esbuild (bundling), typescript + @types/node / @types/react (type check)
337
+ npm run link:dev # symlink the installed harness @deepseek-ai/* into node_modules/ (needed by the type check and the differential cases)
338
+ node scripts/build-lib.mjs # or: npm run build — rebuild the shipped artifacts
339
+ ```
340
+
341
+ Outputs, all inside the plugin package `dsh-plugin-wukong`:
342
+
343
+ - `packages/wukong/lib/index.js` + `lib/invariant.js` (ESM; every `@deepseek-ai/*` and `node:*` import stays external so the profile's own copies resolve)
344
+ - `packages/wukong/client/client.js` — the browser panel as a harness module-loader bundle: `window.__ModuleLoader__.load({ id, factory })`, with `react` / `react/jsx-runtime` left external for the loader's module table
345
+
346
+ Self-check before publishing:
347
+
348
+ ```bash
349
+ npm run verify # = node scripts/verify-shipped.mjs && node scripts/typecheck.mjs && node --test
350
+ npm test # node --test only
351
+ npm run typecheck # the type check only (tsc -p packages/wukong/tsconfig.json --noEmit)
352
+ ```
353
+
354
+ `node scripts/verify-shipped.mjs` checks package identity (installer vs plugin package), structure, syntax and build reproducibility, and `npm run typecheck` runs the type check with the pinned `typescript` devDependency; `npm test` runs `node --test` over `test/`, covering state normalization, the same-origin guard, archive containment (zip slip / symlinks), the bundle-patch round trip, the validation invariants, project-key encoding (differentially compared against the installed persistence backend), first-token timing (compared against the harness), and prompt-section scoping plus the no-interpolation guarantee (differentially checked against the harness renderer).
355
+
356
+ The differential cases — and the type check — need the locally installed harness linked into the repo: `npm run link:dev` (`scripts/link-dev.mjs`) creates the symlinks under the gitignored `node_modules/@deepseek-ai/`; without them those cases skip themselves and the rest of the suite still passes. You can also point it at an explicit path: `node scripts/link-dev.mjs /path/to/@deepseek-ai/dsh`.
357
+
358
+ Tests require **Node 24 or newer** (they run the TypeScript sources directly through Node's type stripping). `packages/wukong/tsconfig.json` is a self-contained config (no broken `extends`, no stale project references), and `npm run typecheck` uses it (`tsc -p packages/wukong/tsconfig.json --noEmit`, with `typescript` as a pinned devDependency). On a checkout that has not run `npm run link:dev` the `@deepseek-ai/*` imports cannot resolve, so the script reports the skip and exits 0 instead of failing — exactly like the tests that need the DSH runtime; with the dev links in place the check is strict and any diagnostic fails it.
359
+
360
+ `scripts/build-shipped.mjs <harness-checkout>` (`npm run build:shipped`) is an **optional** upstream sync: it copies only `<harness>/packages/wukong/wukong/src` → `packages/wukong/src`, `<harness>/packages/client/ui-wukong/src/client` → `packages/wukong/src/client`, and `lib/{index,invariant}.js` — it deliberately **never overwrites** this repo's `package.json`, `cordis.patch.yml` or `client/client.js`, and you still run `node scripts/build-lib.mjs` afterwards. Day-to-day work on this repo does not need it.
361
+
362
+ Keep four places in sync on version bumps:
363
+
364
+ 1. root `package.json` `version` (installer `dsh-plugin-wukong-cli`)
365
+ 2. `packages/wukong/package.json` `version` (plugin package `dsh-plugin-wukong`)
366
+ 3. `WUKONG_VERSION` (in `packages/wukong/src/client/WukongSection.tsx`)
367
+ 4. `CHANGELOG.md`
368
+
369
+ Then rebuild the panel bundle so the version badge updates.
370
+
371
+ ## ❓ FAQ
372
+
373
+ **Q: Why is my client sidebar icon a gear instead of a book?**
374
+ A: The desktop client's packaged `SettingsRoot` hard-codes its icon map. The npm installer's profile composition works fine, but the book icon needs an upstream merge (see [`UPSTREAM.md`](./UPSTREAM.md)).
375
+
376
+ **Q: Do you support the older DSH (2.0.9-era) generation?**
377
+ A: No, deliberately. 0.12.0 is rewritten against the 0.2.0-rc.2 internal APIs (`SettingsForms`, official remote namespaces) and 0.13.x / 0.14.x keep that target; the old `settingsNamespace` / `settingsScope` interfaces do not exist in this generation. You pick one generation: 0.11.x for the old runtime, 0.12.x–0.14.x for this one.
378
+
379
+ **Q: Imported conversations don't show on the other machine?**
380
+ A: Restart the DSH client after import so it rescans the session list. If the target project path differs, fill in the "target project key" (e.g. `--C-Users-xxx--`) before importing so sessions land in the right project.
381
+
382
+ **Q: Is the cost in stats accurate?**
383
+ A: The cost is an **estimate** (built-in per-token pricing: input $0.3/M, output $1.2/M, cache-read $0.03/M, cache-write $0.6/M), not a real bill.
384
+
385
+ **Q: Does this affect my existing config / sessions?**
386
+ A: No. The plugin only writes its own `$DSH_HOME/wukong/{state.json,wallpapers,exports}/` and `$DSH_HOME/skills/` and **never writes `settings.yaml`**; nothing else is touched. The installer still health-checks `settings.yaml` size so it cannot creep past the client's 4MB limit.
387
+
388
+ **Q: What if the state document gets corrupted?**
389
+ A: Writes are atomic (temp file + rename), so a crash never leaves a half-written document. If it ever goes bad, just delete `$DSH_HOME/wukong/state.json` — the plugin rebuilds an empty state on next start.
390
+
391
+ **Q: Offline install?**
392
+ A: Yes: `node cli.cjs --spec <local path or tarball>` installs from a local source through the standard pnpm path. Local paths, tarballs and git addresses are ordinary pnpm specs and install exactly the same way as an npm name.
393
+
394
+ **Q: Why does installation require pnpm?**
395
+ A: Because that is how the harness plugin manager tracks plugins: a plugin's "installed" identity is a pnpm dependency record in the profile. The installer runs `pnpm add` inside the profile, after which the sidebar **Plugins** page and **Settings → Plugins** → "Installed" both list and can toggle it; with no pnpm the installer stops with an error, because a profile install *is* a pnpm dependency.
396
+
397
+ **Q: Why doesn't `pnpm add` try to download a pile of `@deepseek-ai/*` packages?**
398
+ A: Those runtime packages are not published on the public npm registry, so the plugin declares all of them (plus `react`/`react-dom`) as **optional peers** (`peerDependenciesMeta.optional`) and keeps runtime `dependencies` empty. pnpm can therefore install the plugin cleanly, while the harness host supplies those modules at runtime.
399
+
400
+ **Q: Where do skills come from?**
401
+ A: Four ways: manual entry, `.md` file picker, panel `.zip` import, or the in-chat `/wukong-skill-dir` (directory). Directory and zip installs land in `$DSH_HOME/skills/`; once installed they all appear in the Skills tab.
402
+
403
+ ## 📚 Docs
404
+
405
+ | Doc | Purpose |
406
+ |---|---|
407
+ | [`README.en.md`](./README.en.md) | English readme |
408
+ | [`TESTING.md`](./TESTING.md) | local functional tests (incl. an isolated-`DSH_HOME` end-to-end flow) |
409
+ | [`PUBLISHING.md`](./PUBLISHING.md) | release guide |
410
+ | [`UPSTREAM.md`](./UPSTREAM.md) | upstream integration (book-icon PR) |
411
+ | [`CHANGELOG.md`](./CHANGELOG.md) | full version history |
412
+
413
+ ## 🤝 Contributing / Upstream
414
+
415
+ See [`UPSTREAM.md`](./UPSTREAM.md) — how to get the panel natively into `deepseek-ai/deepseek-harness` and `anywhere-labs/dsh-desktop` (book icon in the client sidebar).
416
+
417
+ ## 📄 License
418
+
419
+ MIT
420
+
421
+ ## 🔗 Friends
422
+
423
+ - [**LINUX DO**](https://linux.do/) — tech community · LINUX DO 社区