dsh-plugin-wukong-cli 0.26.0 → 0.26.2

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/README.en.md CHANGED
@@ -1,69 +1,51 @@
1
1
  # Wukong
2
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.
3
+ > **A plugin / management hub for DeepSeek Harness** — prompts, skills, MCP tool servers, image generation, and a global wallpaper with effects, all in one panel.
4
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)
5
+ **Plugin / installer**: plugin package `dsh-plugin-wukong` · installer package `dsh-plugin-wukong-cli` (command `wukong`)
6
6
 
7
- [**中文**](./README.md) · **English**
7
+ ![Wukong: wallpaper, frosted glass and the management panel (dark)](https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/hero-dark.jpg)
8
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"/> |
9
+ | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/hero-light.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/prompts.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/skills.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/mcp.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/wallpaper.jpg" width="190"/> |
12
10
  |--|--|--|--|--|
13
11
  | Light theme | Prompts | Skills | MCP | Wallpaper |
14
12
 
15
- ![version](https://img.shields.io/badge/version-0.26.0-00ff9c)
16
- ![license](https://img.shields.io/badge/license-MIT-blue)
13
+ | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/imagegen.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/stats.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/archive.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/enhance.jpg" width="190"/> | <img src="https://cdn.jsdelivr.net/npm/dsh-plugin-wukong-assets@1.1.0/preview/edit-resend.jpg" width="190"/> |
14
+ |--|--|--|--|--|
15
+ | Images | Stats | Archive | Prompt enhancement | Edit & resend |
16
+
17
+ ![version](https://img.shields.io/badge/version-0.26.2-00ff9c)
17
18
  ![platform](https://img.shields.io/badge/platform-web%20%2B%20desktop-00ff9c)
18
19
  ![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
20
 
29
21
  ---
30
22
 
31
- ## 📑 Table of contents
23
+ ## 📑 Contents
32
24
 
25
+ - [🎯 Where it runs](#-where-it-runs)
33
26
  - [🚀 Quick start](#-quick-start)
34
- - [🎯 Supported upstreams](#-supported-upstreams)
35
27
  - [✨ Features](#-features)
36
- - [🖼️ Image generation (`generate_image`)](#️-image-generation-generate_image)
37
- - [🗄️ State and data](#️-state-and-data)
28
+ - [🖼️ Image generation (generate_image)](#️-image-generation-generate_image)
29
+ - [🛒 Marketplace (MCP and skills)](#-marketplace-mcp-and-skills)
38
30
  - [⌨️ Slash commands](#️-slash-commands)
39
- - [🖥️ Platform support](#️-platform-support)
31
+ - [🧰 Using a skill in chat](#-using-a-skill-in-chat)
32
+ - [🗄️ Where your data lives](#️-where-your-data-lives)
40
33
  - [🔄 One-click update](#-one-click-update)
41
34
  - [🗑️ Uninstall](#️-uninstall)
42
- - [🧰 Using a skill in chat](#-using-a-skill-in-chat)
43
- - [🏗️ Architecture](#️-architecture)
44
- - [📦 Packages](#-packages)
45
- - [🗂️ Repository layout](#️-repository-layout)
46
- - [🛠️ Development](#️-development)
47
35
  - [❓ FAQ](#-faq)
48
- - [📚 Docs](#-docs)
49
- - [🤝 Contributing / Upstream](#-contributing--upstream)
50
- - [📄 License](#-license)
51
- - [🔗 Friends](#-friends)
52
36
 
53
37
  ---
54
38
 
55
- ## 🎯 Supported upstreams
39
+ ## 🎯 Where it runs
56
40
 
57
- 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:
41
+ Wukong is a **DeepSeek Harness plugin**, built and tested against the official **0.2.0-rc.2** release:
58
42
 
59
- | Upstream | Environment | Repo |
43
+ | Environment | Panel | `/wukong-skill-dir` command |
60
44
  |---|---|---|
61
- | **DeepSeek Harness** | `dsh web` web runtime | <https://github.com/deepseek-ai/deepseek-harness> |
62
- | **DSH Desktop** | desktop client | <https://github.com/anywhere-labs/dsh-desktop> |
45
+ | `dsh web` ([deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)) | ✅ | ✅ |
46
+ | The official DeepSeek Harness desktop client | ✅ | ✅ |
63
47
 
64
- > ⚠️ **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/`.**
65
- >
66
- > ⚠️ **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.
48
+ > ⚠️ **Supports only the official DeepSeek Harness 0.2.0-rc.2 release** (`dsh web` and the official desktop client).
67
49
 
68
50
  ---
69
51
 
@@ -71,97 +53,74 @@ Wukong is a **DeepSeek Harness plugin**, ported to and tested against the **0.2.
71
53
 
72
54
  ### Install
73
55
 
74
- **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.
75
-
76
- **Running it from the repository** (to install your working copy, point `--spec` at the local path):
77
-
78
- ```bash
79
- node cli.cjs --profile web --spec link:<repo>/packages/wukong # web profile only, installing the working copy
80
- node cli.cjs # every profile (defaults to the published dsh-plugin-wukong@<same version>)
81
- ```
82
-
83
- **Once the installer is published to npm**:
56
+ You need a DeepSeek Harness runtime (`dsh web` or the desktop client) and **pnpm**. Then run the installer:
84
57
 
85
58
  ```bash
86
- npx dsh-plugin-wukong-cli # no checkout needed; npx resolves package names, so this must be the package `dsh-plugin-wukong-cli`
87
- npm i -g dsh-plugin-wukong-cli # or install it globally, then use the bin name
88
- wukong # the command name after a global install (the bin)
59
+ npx dsh-plugin-wukong-cli
89
60
  ```
90
61
 
91
- > ⚠️ 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.
62
+ Or install it globally and use the `wukong` command:
92
63
 
93
64
  ```bash
94
- node cli.cjs -h # print usage and exit without installing anything (same as --help)
95
- node cli.cjs -v # print the installer + plugin versions and exit (same as --version)
65
+ npm i -g dsh-plugin-wukong-cli
66
+ wukong
96
67
  ```
97
68
 
98
- The installer takes the harness's **standard plugin path**:
99
-
100
- 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
101
- 2. appends `dsh-plugin-wukong` to the profile's `dsh.profile.bundles`
102
- 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
103
- 4. health-checks the size of `settings.yaml` (guards the 4MB client limit) — the plugin itself never writes to that file
104
-
105
- 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.
106
-
107
- 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).
69
+ > ⚠️ **Two packages, two names**: what you run is the installer **`dsh-plugin-wukong-cli`** (command `wukong`), and what it installs is the plugin package **`dsh-plugin-wukong`**. `dsh-plugin-wukong` has no command to run; `wukong` is a command name, not a package name, so `npx wukong` does not work.
108
70
 
109
71
  Common flags:
110
72
 
111
73
  ```bash
112
- node cli.cjs --profile web # web profile only
113
- node cli.cjs --profile web,desktop # pick several (repeatable, comma lists, -p is the short form)
114
- node cli.cjs --spec dsh-plugin-wukong@0.21.0 # choose the source: npm name / local path / tarball / git URL
115
- node cli.cjs --spec link:/abs/path/to/packages/wukong --profile web # install the working copy into a profile
116
- node cli.cjs -h # print usage and exit (same as --help)
117
- node cli.cjs -v # print the installer + plugin versions and exit (same as --version)
118
- node cli.cjs uninstall --profile web # uninstall from web only
74
+ npx dsh-plugin-wukong-cli --profile web # the web profile only
75
+ npx dsh-plugin-wukong-cli --profile web,desktop # several (repeatable, comma lists, -p is the short form)
76
+ npx dsh-plugin-wukong-cli --spec <source> # choose the source: npm name / local path / tarball / git URL
77
+ npx dsh-plugin-wukong-cli -h # print usage only, install nothing
78
+ npx dsh-plugin-wukong-cli -v # print the installer and plugin versions only
119
79
  ```
120
80
 
121
- `--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.
122
-
123
- `--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.
81
+ - **Without `--profile` it installs into every profile**; a misspelled name fails right away with the list of available profiles.
82
+ - The installer takes the harness's standard plugin path: `pnpm add dsh-plugin-wukong` inside the profile, then adds it to the profile's bundle list; it also cleans up installs left by much older releases. Without pnpm it stops with an error.
83
+ - Once installed, the plugin shows up on the sidebar **Plugins** page and under **Settings → Plugins** → "Installed", where it can be toggled.
124
84
 
125
85
  ### Launch
126
86
 
127
87
  ```bash
128
- dsh web # or simply relaunch the DSH Desktop client
88
+ dsh web # or restart the DeepSeek Harness desktop client
129
89
  ```
130
90
 
131
- Open **Settings → Wukong** (book icon).
132
-
133
- > **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/`).
91
+ Open **Settings → Wukong**.
134
92
 
135
93
  ---
136
94
 
137
95
  ## ✨ Features
138
96
 
139
- - **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.
140
- - **Skills (merged)** — one list: panel-installed (edit/toggle/uninstall) + locally scanned (managed), with an invoke hint (`/name`).
141
- - **Skill installation**
142
- - manual entry
143
- - `.md` file picker
144
- - panel `.zip` bundle import (uploaded into `$DSH_HOME/skills`)
145
- - in-chat command `/wukong-skill-dir <dir>` (directory — a browser cannot read a host directory, so this one is chat-only)
146
- - **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.
147
- - **Image generation (`generate_image`)** — registers an image tool in the session: the model calls an OpenAI-compatible Images API with a prompt and the image is saved into the workspace; the Provider and model are picked on the panel's Images page from the host's model catalog, with the endpoint and key following the Provider; the chat shows a placeholder animation while it runs. See the next section, [🖼️ Image generation](#️-image-generation-generate_image).
148
- - **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.
149
- - **Initialize models** — on the Prompts page, above Prompt enhancement: type a provider name (default `cpa`) and one click declares image input (multimodal) on every model under it and sets the standard reasoning levels (`off→none, low, medium, high, xhigh, max`); the provider's `defaultInput` becomes `[text, image]` too, so models added later get image support by default. It edits the `cordis.patch.yml` of **the profile that is running** (recognised from the launch arguments; if it cannot be told, it refuses rather than guess, and no other profile is touched; comments and other entries are kept), backs it up first (the latest three are kept), reloads live with no restart, and shows right under the button which environment and file it changed and what changed, or that it was already set up. Same rules as DSH.app's "Tools → Initialize cpa models".
150
- - **Composer hint-line style** — enable/color/size/gradient presets for the below-input hint and stats line.
151
- - **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.
152
- - **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.
153
- - **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.
154
- - **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.
97
+ - **Prompts (scopable)** — create, edit, delete, enable / disable, set a default. Scope is one of five: **global** (default) / **only this project** / **only this session** / **all except this project** / **all except this session**; projects and sessions outside the scope never receive the prompt. The body reaches the model verbatim — a `{{...}}` in it is not treated as a template variable.
98
+ - **Prompt enhancement** — the enhance button on the right of the composer has a model rewrite the draft you typed; it only replaces the composer text, sending nothing and writing nothing to the session. It follows the current conversation's model by default, or can be pinned to a Provider and model on the Prompts page (a cheaper small model, say).
99
+ - **Edit & resend, and fork** — your sent messages get "Edit" and "Fork" buttons: edit one and send, and the conversation continues in a new session branched off just before that message, with the original moved to the recycle bin (restorable in Archive management); "Fork" only branches a new session before that message and switches to it, sending nothing and leaving the original alone.
100
+ - **Archive management** (Settings → Archive management) — archived and unarchived sessions by workspace, with bulk archive, unarchive and move-to-recycle-bin; sessions in the recycle bin can be restored or deleted for good. Only "Delete permanently" / "Empty recycle bin" actually removes data, and both ask first.
101
+ - **Initialize models** — on the Prompts page, type a provider name (default `cpa`) and one click declares image input (multimodal) on every model under it and sets the standard reasoning levels (`off→none, low, medium, high, xhigh, max`), so models added later get image support by default too. It only edits **the environment that is running**, backs it up first (the latest three are kept), needs no restart, and shows under the button which environment and file it changed and what changed.
102
+ - **Skills (merged)** — one list of panel-installed skills (edit, toggle, uninstall) and locally scanned ones, each with its `/name` hint. Install by typing one in, picking an `.md` file, importing a `.zip` bundle, from the marketplace, or with the in-chat command `/wukong-skill-dir <dir>`.
103
+ - **MCP tool servers** — add / edit / remove stdio or HTTP(S) servers; enabling one connects it, and each shows its status and tool list, with a one-click connection test.
104
+ - **Image generation and editing** — once a Provider and model are picked, the assistant can generate images from a prompt and edit existing ones; open an image in the chat to annotate, comment, erase, remove the background or resize it. See [🖼️ Image generation](#️-image-generation-generate_image).
105
+ - **Wallpaper and frosted glass** — five original landscapes bundled with the plugin (one click applies one), or upload an image / video, or enter an image URL.
106
+ - The wallpaper shows through the main interface only (sidebar, chat, right bar, bottom bar); menus and dialogs keep their usual opaque surfaces.
107
+ - Tune live: image opacity, scrim (with "Auto scrim" adjusting to the wallpaper's brightness), panel opacity, **overlay opacity** (how solid the frosted glass is on sticky headers, the right bar hanging over the chat in a narrow window, the settings dialog, the changed-files card, question cards and the like; default 50), glass blur, wallpaper blur and fit.
108
+ - While a wallpaper shows, secondary text (descriptions, times, unselected tabs and so on) is one step stronger so it stays legible over it.
109
+ - Follows the light / dark theme; **set separately for the web and the desktop client**.
110
+ - **Composer hint-line style** — enable / colour / size / gradient for the hint and stats line under the input.
111
+ - **Conversation import / export** — export selected sessions as a zip (attachments and workspace included) and restore them on another machine; sessions can also be truly deleted (a running one is refused). Exports are pruned automatically: the 10 newest are kept and anything older than 7 days is removed.
112
+ - **Usage stats** — filter by all / 30 days / 7 days / today; input / output / cache-read / cache-write tokens, cache-hit rate, cost estimate; a request log, per-project and per-model stats, and a trend chart.
155
113
  - **Bilingual UI** (简体中文 / English).
156
- - **Settings sidebar book icon**.
157
114
 
158
115
  ---
159
116
 
160
- ## 🖼️ Image generation (`generate_image`)
117
+ ## 🖼️ Image generation (generate_image)
161
118
 
162
- The plugin registers a **`generate_image`** tool in the session: the model calls an OpenAI-compatible Images API once with a text prompt, and the image is saved into the session workspace. The tool sits on the harness `tools` service rather than on a web route, so a headless profile can use it too; the in-chat placeholder animation exists only in the web panel.
119
+ **Using it**: Settings → Wukong → **Images**, pick a Provider and model (the same catalog as the composer's model picker), adjust the default size, quality, format and save folder if you like, and save; then just ask the assistant for "an image of …".
163
120
 
164
- **Using it**: Settings → Wukong → **Images**, pick a Provider and a model (the same catalog as Prompt enhancement and the composer's model picker), adjust the default size / quality / format and the save folder if you like, and save; then just ask the assistant for "an image of …". **No model name is built in**: until a Provider and a model are both chosen, **`generate_image` is not in the assistant's tool list at all** (it appears as soon as you save and disappears as soon as you clear, no restart); if it is somehow called anyway it fails clearly (`imagegen-unset`) rather than guessing a model for you. Settings are read per call, so a model changed in the panel applies to the next call.
121
+ - **No model is built in**: until a Provider and model are picked, the assistant does not see the image tool at all; it is available as soon as you save and gone as soon as you clear, no restart.
122
+ - **The endpoint and key** follow the Provider, using the host's own configuration for it; nothing is stored twice. The request is the OpenAI-compatible `POST {baseURL}/images/generations`.
123
+ - **Where images go** is decided only by the panel's "Save folder" (default `output/imagegen`, relative to the current session's project, or an absolute path); an existing file is never overwritten — `-2`, `-3` is appended.
165
124
 
166
125
  **Tool parameters** (all but `prompt` are optional and default to the panel's values):
167
126
 
@@ -171,85 +130,43 @@ The plugin registers a **`generate_image`** tool in the session: the model calls
171
130
  | `size` | `auto` / `1024x1024` / `1024x1536` / `1536x1024` / `2048x2048` / `2048x1152` / `3840x2160` / `2160x3840` |
172
131
  | `quality` | `low` / `medium` / `high` / `auto` |
173
132
  | `output_format` | `png` / `jpeg` / `webp`; also decides the extension (`jpeg` is saved as `.jpg`) |
174
- | `file_name` | the file name (a name only, never a directory; the extension follows the output format). The save folder is decided **only by the panel's "Save folder"** (default `output/imagegen`, relative to the current session project, or an absolute path) and the model cannot change it; a directory or `../` in the value is cut down to its last segment. An existing file is never overwritten — `-2`, `-3` is appended. Defaults to `<prompt slug>-<timestamp>` |
175
-
176
- **Endpoint and credentials**: neither is stored in the plugin's state. They follow the Provider and are resolved per call — the `baseURL` and `apiKeyEnv` configured for that Provider in the host's settings, with the key resolved through the host's `credentials` service (an empty `apiKeyEnv` means the endpoint needs no key). The request is `POST {baseURL}/images/generations` with `model` / `prompt` / `size` / `quality` / `output_format`; the response is read as `b64_json`, or `url` (downloaded once more). One call produces one image; the per-call timeout defaults to 5 minutes.
177
-
178
- **How it shows in the chat**: the plugin registers its own view for the tool name `generate_image` in `tool.call.toolview`, replacing the host's default tool card.
179
- - While the call runs (the model writing arguments, waiting on the API): a skeleton placeholder in the image's aspect ratio, a dot grid with one faint sweep (visible in light and dark themes; still when the system asks for reduced motion).
180
- - On success: the placeholder collapses to one "Generated … at <path>" line. **The image itself is embedded by the assistant in its reply with Markdown** — the tool description asks for that, so the same image does not appear once in the tool card and again in the reply. This relies on the model complying; if it does not embed the image, the file is still on disk and its path is in that line.
181
- - On failure: one quiet line of error text.
182
-
183
- **Editing (`edit_image`)**: once a Provider and a model are chosen, the model gets `edit_image` next to `generate_image`: it sends an image already in the project (`image`, which must be inside the project or the save folder) with a prompt and an optional mask (`mask`, a PNG whose transparent pixels may change) to `POST {baseURL}/images/edits`, and the result is saved into the save folder named after the original plus `-edited`, **never overwriting** the original. The constraints follow the Image API notes in the Codex `imagegen` skill: no `input_fidelity` and no `background` (`gpt-image-2` accepts neither), masks are guidance rather than pixel-exact, and the image and the mask are each under 50MB.
184
-
185
- **The image viewer**: the result row has a thumbnail and an "Edit image" button; it opens a full-screen viewer with a drawing canvas over the image and a row of tools at the bottom (the interaction follows Codex):
186
-
187
- | Tool | What it does | How it becomes an edit |
188
- |---|---|---|
189
- | Annotate | the bottom bar becomes: select (click / drag / Delete), pen, text, shape (line, arrow, rectangle, ellipse, triangle, diamond, star, heart), colour (13 presets, and the rainbow cell opens a custom picker: saturation/brightness square, hue bar, an eyedropper where the browser has one, and RGB / HEX / HSL fields), eraser (a pixel eraser: rub out parts of what you drew, with a ring showing its size that follows the thickness slider; delete a whole mark with Select + Delete), undo / redo, exit, and the handle on the left drags the bar elsewhere; a vertical slider on the left sets thickness and font size. Marks are vector objects, not pixels painted on a bitmap | the marked-up image, the typed text and a note go to the assistant, which calls `edit_image` |
190
- | Comment | drag a region and write what to change, numbered | same, and the boxed regions become a mask so the model changes only what is inside |
191
- | Erase | paint over what to remove | the panel has the Host edit with the mask directly, no model in between |
192
- | Remove background | asks for confirmation first (it calls the image model and costs one call) | the model is first asked for a transparent background and a result that already has transparent areas is used as it is; otherwise the model puts the subject on a flat pure green `#00ff00` background and the browser turns that green transparent (`gpt-image-2` returns no real alpha; same approach as Codex's `remove_chroma_key.py`), saved as `-cutout.png` |
193
- | Resize | width and height (ratio can be locked) | scaled in the browser, saved as `-<w>x<h>.png`, no model call |
133
+ | `file_name` | the file name (a name only, no directory); defaults to `<prompt>-<timestamp>` |
194
134
 
195
- Every result is saved beside the original and never overwrites a file, and "Previous version" steps back. "Send to assistant" posts a new message in the **current session**: the annotated image (shrunk to at most 1536px on its long edge) as an image part, and the original's path, the mask's path and each comment as text; the model follows the `wukong-imagegen` skill (original as `image`, red marks not kept). Limits: for a picture with no clear subject (a whole room, say) the model often does not turn the background green; the viewer then reports the failure and the colour it actually found along the edge. The chroma-key cut-out does not suit a subject with large green areas (leaves and grass are partly cut away), and a `gpt-image-2` mask is not pixel-exact.
135
+ **In the chat**: while it runs, a placeholder in the image's aspect ratio is shown; when done, the tool card collapses to one line with a thumbnail and an "Edit image" button, and the assistant embeds the full image in its reply.
196
136
 
197
- **The skill (`wukong-imagegen`)**: while the two tools are registered the plugin injects a skill for the model only, covering how to write image prompts, "change only X; keep Y unchanged" for edits, what to do with an annotated image, how to get a transparent background, and how to pick size and quality. It is adapted from OpenAI's Codex `imagegen` skill (Apache-2.0). It is not injected when the tools are not (no model chosen), so it never takes up context for nothing.
137
+ **Editing (`edit_image`)**: with a model picked, the assistant can also edit an image already in the project (optionally with a mask so only one area changes); the result is saved as `<name>-edited`, never overwriting the original.
198
138
 
199
- **Error codes** (`code` is a stable machine key the panel and the tool pick copy from): `imagegen-unset` (no Provider / model chosen) · `imagegen-route` (the Provider has no resolvable endpoint or credential in this deployment) · `imagegen-prompt` (empty prompt) · `imagegen-http` (non-2xx) · `imagegen-network` · `imagegen-timeout` · `imagegen-aborted` · `imagegen-format` (response is not JSON) · `imagegen-empty` (no image in the response) · `imagegen-download` (downloading a `url` failed) · `imagegen-image` (the image to edit cannot be read / is too large / is not PNG, JPEG or WebP) · `imagegen-path` (the image is outside the project and the save folder).
139
+ **The image viewer**: click the thumbnail or "Edit image" to open a full-screen viewer:
200
140
 
201
- > A missing or invalid preference (`size`, `quality`, …) falls back to its default; only the Provider and model must be chosen by you. **Restart dsh after updating the plugin** (the tool's registration sits on the tools service and follows the settings by itself); a pure styling change to the panel view only needs a page refresh.
202
-
203
- ---
204
-
205
- ## 🗄️ State and data
206
-
207
- Since 0.12.0 Wukong no longer uses a harness settings namespace: all managed state lives in the plugin's own document and directories.
208
-
209
- | Path | Contents |
141
+ | Tool | What it does |
210
142
  |---|---|
211
- | `$DSH_HOME/wukong/state.json` (default `~/.dsh/wukong/state.json`) | prompts, managed skills, custom commands, MCP servers, wallpaper/effect settings (wallpapers store ids only), image-generation settings (Provider / model / default size, …; the endpoint and key are not stored here) |
212
- | `$DSH_HOME/wukong/wallpapers/` | uploaded wallpaper / video bytes |
213
- | `$DSH_HOME/skills/` | skills installed from a zip or a directory (the official filesystem skill provider scans and watches this root) |
214
- | `$DSH_HOME/wukong/exports/` | conversation export zips |
143
+ | Annotate | pen, text, shapes (line, arrow, rectangle, ellipse, triangle, diamond, star, heart), colours, eraser, undo / redo; then "Send to assistant" and the assistant edits the image from your marks |
144
+ | Comment | box a region and write what to change (numbered); the boxes become a mask, so only what is inside changes |
145
+ | Erase | paint over what to remove; the image is edited directly |
146
+ | Remove background | cut out a transparent background, saved as `-cutout.png` (it calls the image model once, so it asks first) |
147
+ | Resize | width and height (ratio can be locked), saved as `-<w>x<h>.png`, no model call |
215
148
 
216
- 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):
149
+ Every result is saved beside the original and never overwrites a file, and "Previous version" steps back.
217
150
 
218
- | Route | Purpose |
219
- |---|---|
220
- | `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` |
221
- | `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 |
222
- | `GET /api/wukong/models` | the models a Prompt-enhancement route can be pinned to: the same catalog the host's model picker uses (`sessionController.modelCatalog()`), grouped by provider; answers 503 when the host has no such service, and the panel falls back to typing them in |
223
- | `GET /api/wukong/imagegen/file?path=` · `POST /api/wukong/imagegen/edit` · `POST /api/wukong/imagegen/save` | for the image viewer: read an image for `<img>` / edit directly without the model (erase, remove background; nothing is written with `persist:false`) / save an image the panel computed (resize, cut-out, mask) beside the original. Absolute paths only, PNG / JPEG / WebP judged by magic bytes, and a result never overwrites an existing file |
224
- | `POST /api/wukong/mcp/test` | one-shot MCP connect test |
225
- | `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` |
226
- | `POST /api/wukong/skills/zip` | upload a zip skill bundle and extract it into `$DSH_HOME/skills` |
227
- | `POST /api/wukong-wallpaper/upload` · `GET /api/wukong-wallpaper/image/<id>` | wallpaper upload (streamed to disk, 60MB cap) / read-back (streamed, `Range` supported) |
228
- | `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`) |
229
- | `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) |
230
- | `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) |
151
+ > **Restart dsh after updating the plugin** so the image tools register with the new version; a styling-only change to the panel needs just a page refresh.
231
152
 
232
- 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.
153
+ ---
233
154
 
234
- ### Marketplace (MCP and skills)
155
+ ## 🛒 Marketplace (MCP and skills)
235
156
 
236
- 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:
157
+ The Skills and the MCP pages each carry a collapsible market block where you can search and install with one click:
237
158
 
238
- | Source | Use |
159
+ | Source | Contents |
239
160
  |---|---|
240
- | 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 |
241
- | DSH MCP Market snapshot | a curated, DSH-shaped MCP catalog that maps directly onto the plugin's MCP rows |
242
- | ClawHub | skill search (suspicious entries hidden); installs fetch `SKILL.md` through the public file API and can pin a version |
243
- | SkillsMP | skill metadata plus GitHub stars; installs go back to the entry's GitHub source and download the whole skill directory |
244
- | Bundled set | offline fallback: 3 MCP servers and 2 skill templates, installable with no network |
161
+ | Official MCP Registry | MCP servers |
162
+ | DSH MCP Market | curated MCP servers |
163
+ | ClawHub | skills |
164
+ | SkillsMP | skills (downloaded from their GitHub source) |
165
+ | Bundled set | MCP servers and skill templates installable offline |
245
166
 
246
- - 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**.
247
- - 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.
248
- - 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}`.
249
- - 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.
250
- - 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.
251
-
252
- 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.
167
+ - Every item shows its source, a trust label (bundled / official / curated / community), permission notes and the environment variables it needs. **The market only aggregates — a listing is not a security review**, so judge before installing.
168
+ - An MCP server that needs environment variables is installed **disabled**; fill them in, then enable it.
169
+ - An unreachable source only affects itself; when every source fails, the last successful result is used, then the bundled set.
253
170
 
254
171
  ---
255
172
 
@@ -257,274 +174,73 @@ The skill catalog (the Skills tab list) still comes over remote RPC (`api.skills
257
174
 
258
175
  | Command | Purpose |
259
176
  |---|---|
260
- | `/wukong-skill-dir <dir>` | install a skill directory (`SKILL.md` + references) into the user skill root |
261
-
262
- 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.
263
-
264
- The old `/armory-*` aliases went away in 0.14.0 — only the `/wukong*` names exist now.
265
-
266
- > 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).
267
-
268
- ---
269
-
270
- ## 🖥️ Platform support
271
-
272
- | Environment | Panel | `/wukong` commands |
273
- |---|---|---|
274
- | `dsh web` ([deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness), 0.2.0-rc.2) | ✅ | ✅ |
275
- | DSH Desktop client ([anywhere-labs/dsh-desktop](https://github.com/anywhere-labs/dsh-desktop)) | ✅ | ✅ |
276
-
277
- 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`.
278
-
279
- 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.
280
-
281
- ## 🔄 One-click update
177
+ | `/wukong-skill-dir <dir>` | install a skill directory (`SKILL.md` plus its files) into the user skill folder |
282
178
 
283
- 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:
284
-
285
- > New version vX.Y.Z (current v0.21.0) [Update now]
286
-
287
- 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`.
288
-
289
- ## 🗑️ Uninstall
290
-
291
- ```bash
292
- node cli.cjs uninstall --profile web # or node cli.cjs uninstall (every profile)
293
- ```
294
-
295
- 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).
296
-
297
- 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.
179
+ Everything else is managed in the panel.
298
180
 
299
181
  ## 🧰 Using a skill in chat
300
182
 
301
- Type `/` + skill name in the input:
183
+ Type `/` + the skill name in the input:
302
184
 
303
185
  ```
304
186
  /code-review
305
187
  /mission-keeper
306
188
  ```
307
189
 
308
- The skill body is injected and the agent executes it; `modelInvocable` skills can also be called autonomously by the model.
309
-
310
- ## 🏗️ Architecture
190
+ The skill is handed to the assistant, which follows it; skills the model is allowed to invoke are also called by the model on its own when needed.
311
191
 
312
- ```
313
- DSH runtime (0.2.0-rc.2)
314
- │
315
- ├─ Web panel: the client face of dsh-plugin-wukong (dsh.client)
316
- │ GET/POST /api/wukong/state
317
- │ GET /api/wukong/skills
318
- │ POST /api/wukong/mcp/test
319
- │ POST /api/wukong/skills/zip
320
- │ POST /api/wukong-wallpaper/upload
321
- │ GET /api/wukong-wallpaper/image/<id>
322
- │ GET /api/wukong/stats | /sessions | /version
323
- │ POST /api/wukong/export | /import | /delete | /update
324
- │
325
- ├─ remote RPC: api.skills.list
326
- │
327
- └─► Host row: id=wukong, name=dsh-plugin-wukong (bundle)
328
- • $DSH_HOME/wukong/state.json (atomic writes)
329
- • ctx.systemPrompt.section (scope-matched injection)
330
- • ctx.skills.register
331
- ```
192
+ ---
332
193
 
333
- - **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).
334
- - **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).
335
- - **Image tool**: the Host registers `generate_image` (`ctx.tools.register`) in its own `ctx.inject(['tools'])` fiber — only once a Provider and a model are chosen, registering or unregistering the moment the settings are saved or cleared — reading the settings, the Provider route and the attachment service per call; the panel side registers the chat view for it by tool name in `tool.call.toolview`.
336
- - **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']`.
337
- - **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.
338
- - **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.
194
+ ## 🗄️ Where your data lives
339
195
 
340
- ## 📦 Packages
196
+ The plugin writes only its own files and **never changes `settings.yaml`** or other host configuration:
341
197
 
342
- | Package | Purpose |
198
+ | Path | Contents |
343
199
  |---|---|
344
- | `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 |
345
- | `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 |
346
-
347
- 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.
348
-
349
- ## 🗂️ Repository layout
350
-
351
- ```
352
- dsh-plugin-wukong/
353
- ├── cli.cjs # one-command install/uninstall CLI (pnpm add + bundle + legacy cleanup)
354
- ├── package.json # installer package `dsh-plugin-wukong-cli` (bin: wukong → ./cli.cjs)
355
- ├── packages/ # shipped truth: the one plugin package (lib + client + src + manifest)
356
- │ └── wukong/ # plugin package `dsh-plugin-wukong`
357
- │ ├── package.json # dsh.bundle.patch + dsh.client + optional peers
358
- │ ├── cordis.patch.yml # the single host row (id: wukong)
359
- │ ├── lib/ # build output: index.js + invariant.js
360
- │ ├── client/client.js # build output: browser panel (module-loader bundle)
361
- │ ├── wallpapers/ # the five original bundled landscapes (JPG + thumbnail)
362
- │ └── src/ # host sources + src/client/ panel sources
363
- ├── scripts/build-lib.mjs # esbuild build of the shipped artifacts (devDependencies: esbuild + typescript and @types/*)
364
- ├── scripts/verify-shipped.mjs # artifact structure/syntax checks (used by CI)
365
- ├── scripts/typecheck.mjs # tsc type check (reports a skip and exits 0 without link:dev)
366
- ├── scripts/build-shipped.mjs # optional: sync sources/artifacts from a DSH monorepo checkout (never overwrites manifest, patch or panel bundle)
367
- ├── scripts/link-dev.mjs # symlinks the installed harness packages into node_modules/@deepseek-ai/ (for the differential tests and the type check)
368
- ├── test/ # node --test cases (state / same-origin / archive / patch / invariants / conversations+stats / prompt sections / wallpaper Range + bundled landscapes)
369
- ├── docs/preview/ # real panel screenshots
370
- ├── demo/demo.html # UI preview (mock data)
371
- ├── README.md # this file (zh) · README.en.md (en)
372
- ├── UPSTREAM.md # upstream integration guide
373
- ├── CHANGELOG.md # version history
374
- ├── PUBLISHING.md # release guide
375
- ├── TESTING.md # testing guide
376
- ├── LICENSE # MIT
377
- └── .github/workflows/ci.yml # CI quality gate
378
- ```
379
-
380
- > ⚠️ 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.
381
-
382
- ## 🛠️ Development
383
-
384
- After editing source, rebuild the shipped artifacts (**no deepseek-harness checkout needed**):
385
-
386
- ```bash
387
- npm install # devDependencies: esbuild (bundling), typescript + @types/node / @types/react (type check)
388
- npm run link:dev # symlink the installed harness @deepseek-ai/* into node_modules/ (needed by the type check and the differential cases)
389
- node scripts/build-lib.mjs # or: npm run build — rebuild the shipped artifacts
390
- ```
391
-
392
- Build outputs (the first two inside the plugin package `dsh-plugin-wukong`, the third in the installer package):
393
-
394
- - `packages/wukong/lib/index.js` + `lib/invariant.js` (ESM; every `@deepseek-ai/*` and `node:*` import stays external so the profile's own copies resolve)
395
- - `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
396
- - `dist/cli.cjs` — the published installer, built from the readable `cli.cjs` at the repo root (the package's `bin` and `postinstall` point at it)
397
-
398
- Everything published is minified and obfuscated, **not readable code**; the readable sources live only in the repository (`packages/wukong/src/` and the root `cli.cjs`) and the published packages do not carry them. These artifacts are not in git (see `.gitignore`): run `npm run build` after a clone; on publish, the `prepublishOnly` hook of both packages rebuilds them. The build is deterministic (the obfuscator's random seed is fixed), so `npm run verify` can check that the artifacts on disk match the sources.
399
-
400
- Self-check before publishing:
401
-
402
- ```bash
403
- npm run verify # = node scripts/verify-shipped.mjs && node scripts/typecheck.mjs && node --test
404
- npm test # node --test only
405
- npm run typecheck # the type check only (tsc -p packages/wukong/tsconfig.json --noEmit)
406
- ```
407
-
408
- `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).
409
-
410
- 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`.
411
-
412
- 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.
413
-
414
- `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.
415
-
416
- Keep four places in sync on version bumps:
417
-
418
- 1. root `package.json` `version` (installer `dsh-plugin-wukong-cli`)
419
- 2. `packages/wukong/package.json` `version` (plugin package `dsh-plugin-wukong`)
420
- 3. `WUKONG_VERSION` (in `packages/wukong/src/client/WukongSection.tsx`)
421
- 4. `CHANGELOG.md`
422
-
423
- Then rebuild the panel bundle so the version badge updates.
424
- ### Connecting to the local dev build / switching back to the published one
425
-
426
- While developing, point one of DSH's profiles at the working copy in this repo so a rebuild is all it takes to see a change, and switch back when you want the release. Both directions use the same installer; only `--spec` differs.
427
-
428
- **Switch to the local dev build** (from the repo root; `--profile` picks the profile, comma-separate several):
429
-
430
- ```bash
431
- node cli.cjs --profile web --spec link:$PWD/packages/wukong
432
- ```
433
-
434
- It replaces `node_modules/dsh-plugin-wukong` in the profile with a symlink to `packages/wukong`, and the dependency record becomes `link:…`. Then restart `dsh web` (or DSH.app for the desktop client).
435
-
436
- **Make an edit take effect**: the symlink points at the built output (`lib/`, `client/`), not at `src/`, so rebuild after every change, then restart or reload the page:
437
-
438
- ```bash
439
- npm run build
440
- ```
441
-
442
- **Debug with the dev build**: the output of `npm run build` is minified and obfuscated, so stack traces carry no function names. While debugging, run:
443
-
444
- ```bash
445
- npm run build:dev
446
- ```
447
-
448
- It writes **the same files to the same places** (so a profile that links to the working copy just needs a restart), but neither minified nor obfuscated: the code reads like the source and stack traces show real function names. The first line of every dev artifact is a `DEV BUILD` comment. **Do not publish it**: `npm run verify` and `node scripts/build-lib.mjs --check` fail on that marker and tell you to run `npm run build` first; the `prepublishOnly` hook of both packages (run automatically before `npm publish`) does an official rebuild first, so a dev build is never published by accident. Run `npm run build` again when you are done.
200
+ | `$DSH_HOME/wukong/state.json` (default `~/.dsh/wukong/state.json`) | prompts, skills, MCP servers, wallpaper settings, image-generation settings (no endpoint or key) |
201
+ | `$DSH_HOME/wukong/wallpapers/` | uploaded wallpaper images / videos |
202
+ | `$DSH_HOME/wukong/exports/` | conversation export zips |
203
+ | `$DSH_HOME/skills/` | skills installed from a zip or a directory |
449
204
 
450
- **Check which one a profile is on**:
205
+ ---
451
206
 
452
- ```bash
453
- ls -l ~/.dsh/profiles/web/node_modules/dsh-plugin-wukong # an `->` means the symlink = local dev build; a plain directory = the published one
454
- ```
207
+ ## 🔄 One-click update
455
208
 
456
- You can also read `~/.dsh/profiles/web/package.json`: a `link:…` dependency is the dev build, `^x.y.z` / `x.y.z` is the npm one. Both may carry the same version number, so the panel's version badge cannot tell them apart.
209
+ The panel checks for a new version whenever it opens; when there is one, a banner appears at the top — click "Update now" to reinstall in place, then **restart the client**.
457
210
 
458
- **Switch back to the published build** (no `--spec`: it installs the npm package at the installer's own version):
211
+ ## 🗑️ Uninstall
459
212
 
460
213
  ```bash
461
- npx dsh-plugin-wukong-cli --profile web
214
+ npx dsh-plugin-wukong-cli uninstall --profile web # uninstall from web only
215
+ npx dsh-plugin-wukong-cli uninstall # uninstall from every profile
462
216
  ```
463
217
 
464
- Notes:
465
-
466
- - A profile has exactly one source at a time; the symlink and the npm install replace each other and never coexist.
467
- - Both read and write the **same data** (`$DSH_HOME/wukong/`: prompts, skills, MCP, wallpaper), so breaking the data format while developing affects the copy you use every day. To experiment safely use an isolated `DSH_HOME` (step 2 of [`TESTING.en.md`](./TESTING.en.md)):
468
-
469
- ```bash
470
- DSH_HOME=/tmp/wukong-dev node cli.cjs --profile web --spec link:$PWD/packages/wukong
471
- DSH_HOME=/tmp/wukong-dev dsh --profile web --no-open --port 3917 # pick a port that is free
472
- ```
473
-
474
- - The official build is obfuscated, so stack traces in the browser console and the host log **do not show the original function names**. Use the **dev build** while debugging; see the previous section.
475
- - `npm install` removes the `node_modules/@deepseek-ai/` symlinks that `npm run link:dev` created; run `npm run link:dev` again afterwards, or the type check stops being strict.
218
+ Uninstalling removes the plugin from the profile and deletes its own data folder `$DSH_HOME/wukong/` (prompt, skill, MCP and wallpaper settings and conversation exports go with it). Skills already extracted under `$DSH_HOME/skills/` are kept.
476
219
 
220
+ ---
477
221
 
478
222
  ## ❓ FAQ
479
223
 
480
- **Q: Why is my client sidebar icon a gear instead of a book?**
481
- 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)).
482
-
483
- **Q: Do you support the older DSH (2.0.9-era) generation?**
484
- 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.
485
-
486
- **Q: Imported conversations don't show on the other machine?**
487
- 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.
488
-
489
- **Q: Is the cost in stats accurate?**
490
- 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.
491
-
492
- **Q: Does this affect my existing config / sessions?**
493
- 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.
224
+ **Q: Why is Wukong's icon in the settings sidebar a gear instead of a book?**
225
+ A: The packaged desktop client hard-codes its settings sidebar icons, which a plugin cannot change; the panel works the same either way.
494
226
 
495
- **Q: What if the state document gets corrupted?**
496
- 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.
497
-
498
- **Q: Offline install?**
499
- 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.
500
-
501
- **Q: Why does installation require pnpm?**
502
- 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.
503
-
504
- **Q: Why doesn't `pnpm add` try to download a pile of `@deepseek-ai/*` packages?**
505
- 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.
506
-
507
- **Q: Where do skills come from?**
508
- 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.
509
-
510
- ## 📚 Docs
511
-
512
- | Doc | Purpose |
513
- |---|---|
514
- | [`README.en.md`](./README.en.md) | English readme |
515
- | [`TESTING.md`](./TESTING.md) | local functional tests (incl. an isolated-`DSH_HOME` end-to-end flow) |
516
- | [`PUBLISHING.md`](./PUBLISHING.md) | release guide |
517
- | [`UPSTREAM.md`](./UPSTREAM.md) | upstream integration (book-icon PR) |
518
- | [`CHANGELOG.md`](./CHANGELOG.md) | full version history |
227
+ **Q: Imported sessions do not show up on the other machine?**
228
+ A: **Restart the DSH client** after a successful import so it rescans the session list; if the target project path differs, fill in "Target project key" (shaped like `--C-Users-xxx--`) before importing so the sessions land in the right project.
519
229
 
520
- ## 🤝 Contributing / Upstream
230
+ **Q: Is the cost in the stats accurate?**
231
+ A: It is an **estimate** (built-in prices × tokens: input $0.3/M, output $1.2/M, cache read $0.03/M, cache write $0.6/M), not your actual bill.
521
232
 
522
- 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).
233
+ **Q: Will it touch my existing settings or sessions?**
234
+ A: No. The plugin writes only `$DSH_HOME/wukong/` and `$DSH_HOME/skills/`, never `settings.yaml`.
523
235
 
524
- ## 📄 License
236
+ **Q: What if the settings file gets corrupted?**
237
+ A: Writes are atomic, so a crash never leaves a half-written file. If something does go wrong, delete `$DSH_HOME/wukong/state.json` and the plugin starts with empty settings next time.
525
238
 
526
- MIT
239
+ **Q: Can I install offline?**
240
+ A: Yes — point `--spec` at a local path or tarball: `npx dsh-plugin-wukong-cli --spec <local path or tarball>`.
527
241
 
528
- ## 🔗 Friends
242
+ **Q: Why does installing need pnpm?**
243
+ A: The harness plugin manager recognises installed plugins by the pnpm dependency in the profile, so the installer has to add the plugin to the profile with pnpm.
529
244
 
530
- - [**LINUX DO**](https://linux.do/) — tech community · LINUX DO 社区
245
+ **Q: Some text is still hard to read over my wallpaper?**
246
+ A: Raise "Scrim" or turn on "Auto scrim" on the panel's wallpaper page; if the overlays are too see-through, raise "Overlay opacity".