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/CHANGELOG.md ADDED
@@ -0,0 +1,497 @@
1
+ # 更新日志
2
+
3
+ > **Wukong**(插件包 `dsh-plugin-wukong`;0.14.0 之前叫 Armory / 包名 `dsh-switchblade`,更早为 `@deepseek-ai/dsh-switchblade` + `@deepseek-ai/dsh-client-ui-switchblade`)的所有重要变更。
4
+
5
+ **中文** · [**English**](./CHANGELOG.en.md)
6
+
7
+ 格式基于 [Keep a Changelog](https://keepachangelog.com/),版本遵循 [Semantic Versioning](https://semver.org/)。
8
+
9
+ ## [0.22.0] - 2026-10
10
+
11
+ ### 新增 · Added
12
+ - **内置风景壁纸**:随插件附带五张原创风景(雪山日出、薄雾森林、沙漠黄昏、海岸星夜、高山湖泊,程序生成、无第三方素材,随包 MIT),壁纸页新增「内置风景」缩略图选择区,点一下即应用并启用壁纸。宿主新增 `GET /api/wukong-wallpaper/preset/<id>.jpg`(及 `<id>-thumb.jpg`),名字走固定白名单、同样流式并支持 `Range`;所选风景只是把路径写进现有的 `url` 字段,状态文档没有新字段。
13
+ **Bundled landscape wallpapers**: five original landscapes ship with the plugin (dawn peaks, misty forest, desert dusk, night coast, alpine lake — generated procedurally, no third-party imagery, MIT with the rest of the package), and the Wallpaper tab gains a thumbnail picker where one click applies one and turns the wallpaper on. The host gains `GET /api/wukong-wallpaper/preset/<id>.jpg` (and `<id>-thumb.jpg`), served from a fixed name whitelist, streamed, with `Range` support; picking one just stores its path in the existing `url` field, so the state document has no new field.
14
+ - **README 截图换成当前版本的真实截图**:旧截图是重构前的界面,已全部替换(深色 / 浅色主图 + 提示词、技能、MCP、壁纸四页)。
15
+ **README screenshots replaced with the current UI**: the old ones showed the pre-refactor panel; they are all replaced (dark and light hero shots plus the Prompts, Skills, MCP and Wallpaper pages).
16
+
17
+ ### 变更 · Changed
18
+ - **发布的包只含压缩并混淆后的产物,不带源码**:插件包的 `lib/`、`client/` 与安装器的 `dist/cli.cjs` 都先经 esbuild 压缩(标识符、空白、注释),再经 `javascript-obfuscator` 把字符串字面量放进编码查表并重命名局部标识符;**属性名不动**,因为宿主按属性名(`apply`、`inject`、服务成员)调用这份代码,改了插件就失效。控制流平坦化、死代码注入、自我防御与调试陷阱一概不开,它们会让体积成倍增长、拖慢启动,也是最常见的出错来源。固定了随机种子,产物逐字节可复现,`npm run verify` 的「构建可复现」检查照常工作。插件包不再带 `src/`(也去掉了 `./src/*` 导出),安装器包发布的入口改为 `dist/cli.cjs`(`bin` 与 `postinstall` 随之改指它);仓库里仍保留可读的 `cli.cjs` 与 `src/`。`verify-shipped` 随之更新:改为在沙箱里真正执行 client bundle,核对它向模块表请求的只有 `react` 与 `react/jsx-runtime`,不再依赖产物里的字符串,并新增「发布包不含源码」的检查。
19
+ **The published packages carry only the compressed, obfuscated build — no sources**: the plugin's `lib/` and `client/` and the installer's `dist/cli.cjs` are minified by esbuild (identifiers, whitespace, comments), then `javascript-obfuscator` moves string literals into an encoded lookup table and renames the remaining local identifiers; **property names are left alone**, because the harness calls this code by property name (`apply`, `inject`, service members) and renaming them would break the plugin. Control-flow flattening, dead-code injection, self-defending and debug traps are all off: they multiply the size, slow the start-up and are the usual source of breakage. The random seed is fixed, so the output is byte-for-byte reproducible and the build-reproducibility check in `npm run verify` keeps working. The plugin package no longer ships `src/` (and its `./src/*` export is gone), and the installer package publishes `dist/cli.cjs` as its entry (`bin` and `postinstall` point at it); the repo keeps the readable `cli.cjs` and `src/`. `verify-shipped` follows: it now runs the client bundle in a sandbox and checks that it asks the module table for `react` and `react/jsx-runtime` only, no longer depending on strings in the output, and gains a check that the published packages carry no sources.
20
+ - **提示词与技能的新增 / 编辑改成居中弹窗**:两个 tab 不再在列表上方铺一块内联表单,技能 tab 也不再让「添加」整页替换列表——点右上角「添加」或卡片上的「编辑」都会开一个最高 720px 宽、640px 高的弹窗,正文文本域吃掉剩余高度(不低于 240px,内部滚动),列表留在背景里可见。Esc / 点遮罩 / ✕ 都能取消,焦点进入首个字段、关闭后回到触发它的按钮,弹窗打开期间锁定背景滚动;保存失败保留弹窗与已填内容,成功后回到列表。
21
+ **Prompt and skill create/edit moved into a centered dialog**: neither tab pads the top with an inline form any more, and the Skills tab no longer swaps the whole list out for an add view — the top-right "Add" button and each row's "Edit" open a dialog (up to 720px wide, 640px tall) whose body textarea takes the remaining height (never below 240px, scrolling inside) while the list stays visible behind it. Escape, the scrim and ✕ all cancel, focus enters the first field and returns to the triggering button, background scrolling is locked while it is open, and a failed save keeps the dialog and what was typed.
22
+
23
+ - **壁纸的主题 token 改走官方 `ctx.theme.overrideTokens`**:背景层之上让应用外壳透明、输入卡片 / 气泡 / 代码块的毛玻璃填充,现在合成**一层**主题覆盖,每个值给 light / dark 一对,由主题服务按当前配色选取、在切主题时重新应用、释放时只移除这一层。不再手写内联变量再自己保存还原,也不再需要监听暗色属性的 MutationObserver;此前换主题会把壁纸洗掉。玻璃的亮度与高光改为纯 CSS 按配色切换。`--wk-*` 数值旋钮仍是插件命名空间内的内联变量,关闭时全部移除。
24
+ **Wallpaper theme tokens now go through the official `ctx.theme.overrideTokens`**: the transparent app chrome and the frosted-glass fill on the input card / bubbles / code blocks are one composed theme override layer, each value a light/dark pair that the theme service picks for the active scheme, re-applies on a theme change and removes alone on release. No more hand-written inline variables with a hand-rolled save/restore, and no MutationObserver on the dark attribute; switching theme used to wash the wallpaper out. Glass brightness and sheen switch per scheme in plain CSS. The `--wk-*` numeric knobs stay inline variables in the plugin's own namespace and are all removed when the wallpaper is turned off.
25
+ - **面板填满设置对话框,不再写死 600px 内容高度**:此前面板自己的页眉加 600px 内容区会超出宿主滚动区 16px,出现第二根滚动条;现在页眉与标签页固定,只有页面列滚动,矮窗口下同样只有一处滚动。滑块行的最小列宽从 230px 调到 280px,数值与 ↺ 不再被挤到下一行;字体改为继承宿主。
26
+ **The panel fills the settings dialog instead of a fixed 600px content area**: its header plus 600px overshot the host's scroll region by 16px and added a second scrollbar; now the header and tabs stay put and only the page column scrolls, on a short window too. Slider rows have a 280px minimum column (was 230px), so the value and ↺ no longer wrap under the slider; the font is inherited from the host.
27
+
28
+ ### 修复 · Fixed
29
+ - **换壁纸后界面里最暗的那档灰字看不清**:应用里「创造模式」「对话 / 轨迹」「工作区」标题、状态行、文件夹图标这类最淡的文字,是按主题自带的纯色背景调的;浅色壁纸压在深色主题上(或深色壁纸压在浅色主题上)时会被吃掉,而固定的 25% 遮罩无从得知是哪种情况。新增**「自动遮罩」**(默认开启,旧配置无需迁移):首次载入壁纸时在画布上缩小采样,测出它最暗 / 最亮部分的亮度,再按当前主题把遮罩加深或提亮到刚好让文字站得住(深色主题压到亮部不超过目标亮度,浅色主题抬到暗部不低于目标亮度,上限 70%);「遮罩」滑块变成它的最低值。深浅两个强度同时交给 CSS 按主题属性选取,切换主题不需要脚本。视频取第一帧;外部链接的图片因跨域读不到像素,仍只用滑块的值。
30
+ **The dimmest tier of interface text was unreadable after setting a wallpaper**: text such as "创造模式", the "对话 / 轨迹" tabs, the "工作区" heading, the status line and the folder icon is tuned for the theme's own flat background, so a pale wallpaper under the dark theme (or a dark one under the light theme) swallowed it, and a fixed 25% scrim cannot know which case it is in. New **"Auto scrim"** (on by default, no migration for existing settings): the wallpaper is sampled small on a canvas when it first loads to measure its darkest and brightest parts, and the scrim is deepened or lightened for the active theme just far enough for the text to stand (the bright parts no brighter than the target under the dark theme, the dark parts no darker under the light theme, capped at 70%); the Scrim slider becomes its floor. Both strengths are handed to CSS, which picks one by the theme attribute, so a theme switch needs no script. A video is sampled on its first frame; an image from another site cannot be read back across origins and keeps using the slider alone.
31
+ - **键盘可用性**:输入框、文本域与搜索框去掉内联 `outline: none`,统一补上与宿主 Switch 同源 token 的 `:focus-visible` 焦点环;四处文件上传按钮此前用 `display:none` 导致键盘根本够不到,现在留在 Tab 顺序里并显示焦点环;标签页补全 ARIA 标签页模式(`tabpanel`、`aria-controls`、roving tabindex、方向键 / Home / End);壁纸开关补 `aria-label`,空格键不再滚动页面;所有过渡动画尊重 `prefers-reduced-motion`。
32
+ **Keyboard use**: the inline `outline: none` is gone from the input, textarea and search fields, and one `:focus-visible` ring using the same tokens as the host's Switch covers every control; the four file-upload buttons used `display:none`, which put them out of keyboard reach, and now stay in the tab order with a visible ring; the tabs follow the ARIA tabs pattern (`tabpanel`, `aria-controls`, roving tabindex, arrow keys / Home / End); the wallpaper switch has an `aria-label` and Space no longer scrolls the page; every transition respects `prefers-reduced-motion`.
33
+ - **拖动壁纸滑块不再每个像素写一次整份状态文档**:绘制仍是即时的,保存改为停手 350ms 后一次(一次 30 步的拖动由 31 次写入降为 1 次),关闭对话框时会补写未保存的改动;写入遇到并发冲突(409)会在最新文档上重放一次;保存失败会提示,不再静默。副作用同时移出了 `setState` 的 updater。
34
+ **Dragging a wallpaper slider no longer rewrites the whole state document per pixel**: painting is still immediate, while the save waits 350ms after the drag settles (a 30-step drag went from 31 writes to 1), a change still waiting is flushed when the dialog closes, a write that hits a concurrency conflict (409) is replayed once on top of the newer document, and a failed save now says so instead of staying silent. The side effects also left the `setState` updater.
35
+ - **图片切换为视频(或反过来)后背景不显示**:背景元素只按首次的类型创建一次,之后换类型仍复用,视频地址被塞进 `<img>`。现在类型变化时重建元素。
36
+ **A wallpaper stopped showing after switching from an image to a video (or back)**: the backdrop element was created once for the first media kind and reused, so a video URL ended up in an `<img>`. It is now rebuilt when the kind changes.
37
+ - **视频壁纸**:创建后只 `setAttribute('muted')` 不会让元素真正静音,自动播放可能被拒,现在同时设置属性与 `muted` 属性;页面隐藏时暂停、恢复后继续,系统要求减少动态效果时不自动播放。
38
+ **Video wallpaper**: setting only the `muted` attribute after creation does not mute the element, so autoplay could be refused; the property is now set as well. The video pauses while the page is hidden and resumes after, and does not autoplay when the system asks for reduced motion.
39
+ - **壁纸媒体路由支持 Range 与流式读取**:`GET /api/wukong-wallpaper/image/<id>` 现在回应 `Accept-Ranges`,处理单段 `Range`(206 / 416),多段或畸形的 Range 回退为整文件;文件走流而不是整个读进内存(Safari 不会播放不支持 Range 的视频)。上传同样改为流式写入临时文件再重命名,主机在 `Content-Length` 超过 60MB 时直接 413,面板在发送前就检查同一上限并给出提示。
40
+ **The wallpaper media route supports Range and streams**: `GET /api/wukong-wallpaper/image/<id>` now answers `Accept-Ranges`, serves a single `Range` (206 / 416) and falls back to the whole file for a multi-range or malformed one; the file is streamed instead of read into memory whole (Safari plays no video from a server that ignores Range). Uploads stream to a temp file and are renamed on success too, the host answers 413 as soon as `Content-Length` is over 60MB, and the panel checks the same limit before sending and says so.
41
+ - **被替换或清除的壁纸文件不再永远留在磁盘上**:每次壁纸 section 写入和每次启动后,主机清理未被任何 section 引用的 `up-<24 位十六进制>.<扩展名>` 上传(及中断上传留下的 `.part`);刚上传的文件有 10 分钟宽限期,因为面板先上传、后保存引用;目录里其他文件一概不动。
42
+ **Replaced or cleared wallpaper files no longer stay on disk forever**: after every wallpaper-section write and after startup, the host removes `up-<24 hex>.<ext>` uploads (and the `.part` of an interrupted one) that no section references; a fresh upload has a 10-minute grace period because the panel uploads first and saves the reference after; nothing else in the directory is touched.
43
+ - **启动时不再先闪一下未应用壁纸的页面**:浏览器把最近一次的壁纸设置缓存在 localStorage,启动时先按缓存绘制,再由主机文档校正;缓存不可用时行为与以前一致。默认值也收敛到 `state-schema.ts` 一处,不再在主机与面板各写一份。
44
+ **No more flash of the unthemed page on start**: the browser caches the last wallpaper section in localStorage and paints from it first, then the host document corrects it; without storage the behaviour is as before. The defaults also live in one place, `state-schema.ts`, instead of one copy each in the host and the panel.
45
+ - **面板版本徽章的回退常量**停在 0.21.0(`WUKONG_VERSION`),README 徽章停在 0.15.0,均已同步到 0.22.0。
46
+ **The panel's fallback version constant** (`WUKONG_VERSION`) was stuck at 0.21.0 and the README badge at 0.15.0; both now read 0.22.0.
47
+
48
+ ### 移除 · Removed
49
+ - **`/wukong-*` 命令族只剩 `/wukong-skill-dir`**:列出、启用 / 停用、卸载、markdown 与 zip 安装、状态查看、bundle patch 层导出 / 导入,面板都已具备,不再各配一条命令。被删掉的这些都需要参数却没声明 `input`,于是在 `/` 菜单里点一下就会**立刻不带参数执行**并直接报错(`/wukong-enable` 点开就是一句用法提示);留下的 `/wukong-skill-dir` 声明了 `input`,菜单选中时落回输入框等参数,补上目录再回车才执行——它也是面板唯一做不到的事(浏览器读不到任意主机目录)。随之下线的是 `patch.ts`(bundle patch 层序列化)、它的 6 项测试与 `exportBundle` / `importBundle`:那条导入会把托管技能**整体替换**,而面板与安装器都不消费这种 patch 层。
50
+ **The `/wukong-*` family is down to `/wukong-skill-dir`**: listing, enable/disable, uninstall, markdown and zip installs, state inspection, and bundle-patch export/import all already exist in the panel and no longer have a command each. Every removed command took an argument without declaring `input`, so picking one from the `/` menu **ran it bare on the spot** and answered with an error (`/wukong-enable` opened straight into a usage line); the survivor declares `input`, so a menu pick stays in the composer until the directory is typed — and it is the one thing the panel cannot do, since a browser cannot read an arbitrary host directory. `patch.ts` (the bundle-patch serializer), its 6 tests and `exportBundle`/`importBundle` went with them: that import replaced the whole managed-skill list wholesale, and neither the panel nor the installer consumes such a patch layer.
51
+
52
+ ## [0.21.0] - 2026-10
53
+
54
+ ### 变更 · Changed
55
+ - **包名与仓库名统一改为 `dsh-plugin-wukong`(安装器 `dsh-plugin-wukong-cli`),插件自身数据目录仍为 `$DSH_HOME/wukong/`**:插件包改名 `dsh-plugin-wukong`,安装器包改名 `dsh-plugin-wukong-cli`(bin 仍是 `wukong` → `./cli.cjs`),仓库迁到 [`agassiz/dsh-plugin-wukong`](https://github.com/agassiz/dsh-plugin-wukong);插件自己的数据仍在 `$DSH_HOME/wukong/…`(状态文档、`market-cache`、`market-tmp`、`exports`、壁纸目录),技能仍在 `$DSH_HOME/skills/`——harness 的文件系统技能提供者扫的就是这个路径,我们没有改它。
56
+ **Package and repository names unified as `dsh-plugin-wukong` (installer `dsh-plugin-wukong-cli`), and the plugin's own data directory stays `$DSH_HOME/wukong/`**: the plugin package is renamed to `dsh-plugin-wukong`, the installer package to `dsh-plugin-wukong-cli` (bin still `wukong`), and the repository is now [`agassiz/dsh-plugin-wukong`](https://github.com/agassiz/dsh-plugin-wukong); the plugin's own data stays under `$DSH_HOME/wukong/…` (the state document, `market-cache`, `market-tmp`, `exports`, wallpaper directory), while skills stay in `$DSH_HOME/skills/` — the path the harness filesystem skill provider scans, which we do not control.
57
+ - **安装器只做一件事,而且走标准路径**:`cli.cjs` 在每个选中的 profile 里执行 `pnpm add <spec>`(默认 spec 是 npm 上已发布的 `dsh-plugin-wukong@<安装器版本>`;`--spec` 可换成 npm 包名 / 本地路径 / tarball / git 地址),把 `dsh-plugin-wukong` 追加进 profile 的 `dsh.profile.bundles`,再清理更早版本留下的安装痕迹(旧 bundle/依赖名、`node_modules/@deepseek-ai/` 作用域下的拷贝式安装、profile patch 里手写的面板行、旧的 `$DSH_HOME/{switchblade,wallpapers,armory-exports}` 目录)。profile 里的这条依赖记录就是插件管理器的「已安装」身份。pnpm 是必需项(找不到时安装器直接报错退出),安装器包的 `files` 里只有 `cli.cjs` 与文档,不带插件副本。
58
+ **The installer does one thing, the standard way**: `cli.cjs` runs `pnpm add <spec>` inside every selected profile (the default spec is the published `dsh-plugin-wukong@<installer version>`; `--spec` swaps in an npm name / local path / tarball / git address), appends `dsh-plugin-wukong` to the profile's `dsh.profile.bundles`, and cleans up what much older releases left behind (the old bundle/dependency names, the copy-style installs under `node_modules/@deepseek-ai/`, the hand-written panel row in the profile patch, the old `$DSH_HOME/{switchblade,wallpapers,armory-exports}` directories). That dependency record in the profile is the plugin manager's "installed" identity. pnpm is required (a missing pnpm stops the installer with an error), and the installer's `files` list is `cli.cjs` plus the docs, with no plugin copy.
59
+
60
+ ## [0.16.0] - 2026-10
61
+
62
+ ### 新增 · Added
63
+ - **扩展市场:MCP 与技能都可以从目录里搜索并安装**:新增 `GET /api/wukong/market?kind=mcp|skill&q=<关键词>` 与 `POST /api/wukong/market/install`。市场把多个上游目录聚合成同一种条目:MCP 来自**官方 MCP Registry** 与 **DSH MCP Market 快照**,技能来自 **ClawHub** 与 **SkillsMP**。每个条目都带来源名、来源等级(`bundled` / `official` / `curated` / `community`)、权限说明、热度(上游给 star 或下载量时)和**所需环境变量**;只有先搜索、再显式指定某个条目 id,才会真正安装——市场只是索引,不会自动装任何东西。
64
+ **Marketplace for MCP servers and skills**: `GET /api/wukong/market?kind=mcp|skill&q=<query>` and `POST /api/wukong/market/install` are new. The market normalizes several upstream directories into one item shape: MCP from the **official MCP Registry** and the **DSH MCP Market snapshot**, skills from **ClawHub** and **SkillsMP**. Every item carries its source name, a source-level trust label (`bundled` / `official` / `curated` / `community`), permission notes, popularity when the upstream reports stars or downloads, and the **environment variables it needs**; nothing installs until a search has listed it and an explicit item id is chosen.
65
+ - **离线可用**:某个源不可达或限流时,其余源照常显示并给出提示;全部源都失败时回退到上一次成功结果的磁盘缓存(`$DSH_HOME/wukong/market-cache`,读取时同样做 schema 校验,格式不对就当作没有缓存),再回退到随包精选(3 个 MCP 服务器 + 2 个技能模板),因此第一次打开市场、或整台机器离线时也有东西可装。
66
+ **Works offline**: an unreachable or rate-limited source only adds a warning while the others still answer; when every source fails the market falls back to the last successful result cached on disk (`$DSH_HOME/wukong/market-cache`, schema-checked on read, and a malformed file counts as no cache), and then to the bundled set (3 MCP servers + 2 skill templates), so there is something to install on a first run or on an offline machine.
67
+ - **MCP 安装写进插件自己的 MCP 行**:`POST /api/wukong/market/install` 生成 `serverName`(自动加短后缀避让重名)、`transport`、`command`/`args`/`env` 或 `url`/`headers`。需要环境变量的服务器**以禁用状态安装**并在响应里返回 `needsEnv`,不会在变量就绪之前启动子进程;环境变量在配置里写成 `${NAME}` 引用。
68
+ **MCP installs land in the plugin's own MCP rows**: `POST /api/wukong/market/install` produces `serverName` (with a short suffix to avoid a collision), `transport`, `command`/`args`/`env` or `url`/`headers`. A server that needs environment variables is installed **disabled** and the response lists `needsEnv`, so no child process starts before the variables exist; the variables are referenced as `${NAME}` in the config.
69
+ - **技能安装分两条路径**:单文件(随包模板)注册为**运行时技能**——面板立刻看得到、每个会话都能用、重启后仍在;带附加文件的仓库技能则写进 `$DSH_HOME/skills/<name>`,交给 harness 的文件系统技能提供者。
70
+ **Two skill install paths**: a single-file skill (a bundled template) is registered as a **managed runtime skill**, so the panel lists it immediately, every session can use it and it survives a restart; a repository skill that ships extra files is written to `$DSH_HOME/skills/<name>` for the harness filesystem skill provider.
71
+ - **面板里的市场浏览器**:技能与 MCP 两个页签都有可折叠的市场区块,支持关键词搜索(Enter 或按钮触发,不逐键请求)、刷新、来源与来源等级徽标、上游链接、权限与环境变量提示,以及一条一条的安装按钮;离线或使用了缓存时状态行会说清楚。
72
+ **A market browser in the panel**: both the Skills and the MCP tab get a collapsible market block with keyword search (Enter or button, never per keystroke), refresh, source and trust badges, a link to the upstream page, permission and environment-variable notes, and a per-item install button; the status line says when results are offline or served from cache.
73
+ - **24 项市场相关测试**:上游响应归一化(Registry / DSH 快照 / ClawHub / SkillsMP)、条目校验与拒绝、路径逃逸防护、去重、离线回退与磁盘缓存、`SKILL.md` 下载端点、以及"上游塞进畸形条目也不会被列出来"。
74
+ **24 new market tests**: upstream normalization (Registry / DSH snapshot / ClawHub / SkillsMP), item validation and rejection, path-escape defence, de-duplication, offline fallback and the disk cache, the `SKILL.md` download endpoint, and the case where a hostile upstream payload cannot inject an item.
75
+
76
+ ### 安全 · Security
77
+ - 市场内容一律**先校验再使用**:条目 schema 校验(来源等级、权限数组、payload 形状)、技能相对路径禁止绝对路径 / `..` / 反斜杠 / 控制字符、单文件 1MB、单目录 8MB、归档 16MB、每源 10 秒超时、一个技能最多 64 个文件,GitHub 归档解压后还要过一遍包含性检查(不许符号链接、不许逃出目录)。安装动作只写插件的状态文档与技能根目录,不改 profile、不执行下载来的代码,上游被收录或带 `official` 标签都不等于安全背书。
78
+ **Market content is validated before use**: item schema validation (trust label, permission array, payload shape), skill-relative path rules 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 any extracted GitHub archive (no symbolic links, nothing outside the target). An install only writes the plugin's own state document and the user skill root: it does not modify the profile, does not run downloaded code, and neither upstream listing nor an `official` label is a security endorsement.
79
+
80
+ ## [0.15.0] - 2026-10
81
+
82
+ ### 变更 · Changed
83
+ - **拆成两个包:安装器 `dsh-plugin-wukong-cli` 与插件包 `dsh-plugin-wukong`**:npm 安装器改名为 `dsh-plugin-wukong-cli`(bin 仍是 `wukong` → `./cli.cjs`);它**不声明 `dsh` 字段、因此不是 bundle**,`files` 里只有 `cli.cjs` 与文档。插件包仍是 `dsh-plugin-wukong`(`packages/wukong/`),声明 `dsh.bundle.patch` 与 `dsh.client`(platform web),必须装进 profile。两个名字分开是有意的:`pnpm add dsh-plugin-wukong`(或在 harness 插件管理器里选 `dsh-plugin-wukong`)本来就是标准路径,安装器命令不该和 bundle 抢名字。
84
+ **Split into two packages: installer `dsh-plugin-wukong-cli` + plugin package `dsh-plugin-wukong`**: the npm installer is renamed to `dsh-plugin-wukong-cli` (bin still `wukong` → `./cli.cjs`); it declares **no `dsh` field, so it is not a bundle**, and its `files` list is `cli.cjs` plus the docs. The plugin package is still `dsh-plugin-wukong` (`packages/wukong/`), declares `dsh.bundle.patch` and `dsh.client` (platform web), and must be installed into a profile. 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 should not fight the bundle over one name.
85
+ - **CLI 新增 `-h/--help` 与 `-v/--version`**:两者在任何安装动作之前处理,只打印用法或版本后退出,不碰任何 profile。`--spec <spec>`、`--profile <name>` 与卸载行为不变。
86
+ **New CLI flags `-h/--help` and `-v/--version`**: both are handled before any install work, printing usage or the version and exiting without touching a profile. `--spec <spec>`, `--profile <name>` and uninstall behaviour are unchanged.
87
+ - **状态写入契约:`{state, ifRevision}` + `revision` + HTTP 409 `stale`**:`GET /api/wukong/state` 返回 `{ok, revision, state}`;`POST /api/wukong/state` 接受 `{state, ifRevision}`(扁平文档仍兼容),每次接受的写入都会归一化整份文档并让 `revision` 加一;`ifRevision` 与当前值不符时返回 HTTP 409 与 `{ok:false, error:'stale', revision, state}`,调用方应采用返回的文档。读取同样先归一化整份文档。
88
+ **State write contract: `{state, ifRevision}` + `revision` + HTTP 409 `stale`**: `GET /api/wukong/state` answers `{ok, revision, state}`; `POST /api/wukong/state` takes `{state, ifRevision}` (a bare flat document is still accepted), normalizes the whole document on every accepted write and increments `revision`; a mismatched `ifRevision` gets HTTP 409 with `{ok:false, error:'stale', revision, state}`, and the caller is expected to adopt the returned document. Reads normalize the whole document first too.
89
+ - **新增 `GET /api/wukong/skills`**:返回 `{ok, revision, skills, commands}`,即面板需要的技能 + 托管命令目录,浏览器不再从整份状态文档里重建它。
90
+ **New `GET /api/wukong/skills` route**: it returns `{ok, revision, skills, commands}` — the skill + managed-command catalog the panel needs — so the browser no longer reconstructs it from the whole state document.
91
+ - **对话与统计重做**:改走 harness 的 session-query 服务,不再手工解析 store 文件;项目目录编码与持久化后端一致;列表带标题与占用大小;缓存命中率与首字时间取自真实用量;导入经持久化服务流式写盘;删除会拒绝仍在运行的会话,`/update` 需要显式确认。
92
+ **Conversations and stats reworked**: they read through the harness session-query service instead of hand-parsing store files; the project-directory encoding matches the persistence backend; the list carries title and size columns; cache-hit rate and first-token time come from real usage data; import streams through the persistence service; delete refuses a session that is still live and `/update` requires explicit confirmation.
93
+ - **版本查询与一键更新改走安装器包**:`GET /api/wukong/version` 返回 `{ok, latest, current}`——`current` 在运行时从插件自己的 `package.json`(`lib/` 旁边)读取,面板里编译期常量只作回退;`latest` 先问 `dsh-plugin-wukong-cli` 再问 `dsh-plugin-wukong`,registry 不可达或尚未发布时返回空串。`POST /api/wukong/update` 现在执行 `npm exec --yes --package=dsh-plugin-wukong-cli -- wukong`(安装并运行安装器包),仍要求请求体带 `{"confirm":true}`,否则 HTTP 400 `confirmation-required`。此前它试图从 `dsh-plugin-wukong` 这个 bundle 里跑 `wukong` bin——而 bundle 没有 bin,这是包拆分引出的真实缺陷。
94
+ **Version lookup and one-click update now go through the installer package**: `GET /api/wukong/version` returns `{ok, latest, current}` — `current` is read at runtime from the plugin's own `package.json` beside `lib/` (the compiled-in constant in the panel is only a fallback), and `latest` is asked of `dsh-plugin-wukong-cli` first and `dsh-plugin-wukong` second, returning an empty string when the registry is unreachable or the package is unpublished. `POST /api/wukong/update` now runs `npm exec --yes --package=dsh-plugin-wukong-cli -- wukong` (installs and runs the installer package) and still requires `{"confirm":true}`, otherwise HTTP 400 `confirmation-required`. Previously it tried to run the `wukong` bin out of the `dsh-plugin-wukong` bundle — which has no bin — a real defect caused by the package split.
95
+ - **导出归档自动清理**:每次导出后都会清理 `$DSH_HOME/wukong/exports`,保留最新 10 个归档,并删除超过 7 天的文件,目录不会随使用无限增长。
96
+ **Export archives are pruned automatically**: after every export `$DSH_HOME/wukong/exports` is cleaned up — the 10 newest archives are kept and anything older than 7 days is removed — so the directory cannot grow without bound.
97
+
98
+ ### 修复 · Fixed
99
+ - **同源围栏收紧**:外部来源与 opaque(`Origin: null`)来源、以及 `Sec-Fetch-Site: cross-site` 一律 HTTP 403;不带 `Origin` 的请求只在回环(loopback)主机上接受。
100
+ **Strict same-origin fence**: a foreign or opaque (`Origin: null`) origin, or `Sec-Fetch-Site: cross-site`, gets HTTP 403, and a request without an `Origin` is accepted only on a loopback host.
101
+ - **提示词正文不再插值**:正文以 `interpolate: false` 的 system-prompt section 注册,字面 `{{cwd}}`(或任何 `{{...}}`)原样送到模型。这修掉了一个真实 bug——插值开启时渲染器会在拼装系统提示词时抛错,让每个启用该提示词的会话的每一步模型调用都失败。
102
+ **Prompt bodies are no longer interpolated**: they register as system-prompt sections with `interpolate: false`, so a literal `{{cwd}}` (or any `{{...}}`) reaches the model verbatim. This fixes a real bug: with interpolation on, the renderer threw while assembling the system prompt and failed every model step of every session that had the prompt enabled.
103
+ - **zip 导入加封堵检查**:解压出的目录树在进入托管根之前先做包含性校验(zip slip 与符号链接一律拒绝);PowerShell 回退路径的引号写法也已修正。
104
+ **Zip imports are containment-checked**: an extracted tree is validated before it enters a managed root (zip slip and symlinks are refused), and the PowerShell fallback path now quotes correctly.
105
+ - **坏 zip 的报错更准确**:归档读不出来时区分「宿主没有解压工具」与「归档无法解压:没有任何工具接受该文件(它是不是合法 zip?)」,上传损坏文件时不再误导用户去装 unzip。
106
+ **A corrupt archive now gets an accurate error**: when a zip cannot be read, the host distinguishes "no unzip tool available" from "archive could not be extracted: no zip tool accepted the file (is it a valid zip?)", so a corrupt upload no longer tells the user to install unzip.
107
+ - **MCP 服务器状态不再假装成功**:新组合的 loader 条目在 10 秒宽限期内报 `starting`,发布出工具后报 `running`;10 秒内没有任何工具出现时带上 `lastError`,而不是静默显示「运行中、0 个工具」。
108
+ **MCP server status no longer pretends success**: a freshly composed loader entry reports `starting` during the 10-second grace window and `running` once it publishes a tool; when no tool appears within 10 seconds it carries a `lastError` instead of silently showing "running with 0 tools".
109
+ - **自定义斜杠命令没有进程内 handler 时明确报错**:从磁盘恢复的命令行拿不到进程内的闭包 handler,此时命令返回显式错误(说明它来自磁盘、本进程没有 handler),不再假装修好可用。
110
+ **A custom slash command with no process-local handler now reports an explicit error**: a command row restored from disk cannot carry its in-process closure, so it returns an explicit error saying exactly that instead of faking success.
111
+ - **面板侧修复**:壁纸读取改为有上限的重试(并正确 dispose,不再是无限重建循环);读取失败不再清空提示词列表;颜色改用主题 token 而不是硬编码色值;面板加了 error boundary,错误通知可手动关闭。
112
+ **Client-side fixes**: the wallpaper read is now a bounded retry with proper disposal (no more infinite rebuild loop); a failed read no longer blanks the prompt list; colors use theme tokens instead of hardcoded values; the panel gained an error boundary and dismissible error notices.
113
+
114
+ ### 文档与工具 · Docs & tooling
115
+ - **新增测试套件与自检脚本**:`npm test` 用 `node --test` 跑 `test/`;`npm run verify` 先跑 `node scripts/verify-shipped.mjs`(包身份、结构、语法、构建可复现性)再跑测试;`npm run link:dev` 把本机已安装的 harness 包软链进仓库的 `node_modules/@deepseek-ai/`,供差分用例使用(不链时这些用例自行跳过,其余照常通过)。测试需要 Node 24 或更高版本(直接用类型擦除跑 TypeScript 源码)。
116
+ **New test suite and self-check scripts**: `npm test` runs `node --test` over `test/`; `npm run verify` runs `node scripts/verify-shipped.mjs` (package identity, structure, syntax, build reproducibility) and then the tests; `npm run link:dev` symlinks the installed harness packages into the repo's `node_modules/@deepseek-ai/` for the differential cases (without it they skip themselves and the rest still passes). Tests need Node 24 or newer (they run the TypeScript sources directly through type stripping).
117
+ - **`packages/wukong/tsconfig.json` 修成自包含配置**:不再有失效的 `extends` 或过期的 project references,供编辑器与 `tsc` 使用。
118
+ **`packages/wukong/tsconfig.json` is now a self-contained config**: no broken `extends` and no stale project references, usable by editors and `tsc`.
119
+
120
+ ## [0.14.0] - 2026-10
121
+
122
+ ### 变更 · Changed
123
+ - **项目更名为 Wukong,插件包改为 `dsh-plugin-wukong`**:仓库迁到 [`agassiz/dsh-plugin-wukong`](https://github.com/agassiz/dsh-plugin-wukong);npm 安装器包名 `prompt-skill-armory` → `dsh-plugin-wukong`,bin 从 `armory` / `prompt-skill-armory` 两个名字收敛为**单个 `wukong`**(不保留别名)。包目录 `packages/switchblade/` → `packages/wukong/`。
124
+ **Renamed to Wukong, plugin package is now `dsh-plugin-wukong`**: the repository moved to [`agassiz/dsh-plugin-wukong`](https://github.com/agassiz/dsh-plugin-wukong); the npm installer package `prompt-skill-armory` is now `dsh-plugin-wukong`, and its two bin names (`armory`, `prompt-skill-armory`) collapse into the single **`wukong`** (no aliases). The package directory moved from `packages/switchblade/` to `packages/wukong/`.
125
+ - **内部标识一次性全部更名**(本项目处于测试期,**不提供任何旧名兼容或迁移**):cordis service `switchblade` → `wukong`;源码 `src/switchblade.ts` → `src/wukong.ts`,类 `Switchblade` → `Wukong`(`SwitchbladeState`/`SwitchbladePatch`/`SwitchbladeSection` 等同理);面板 cordis 插件名 `ui-switchblade` → `ui-wukong`;设置项 id `switchblade` → `wukong`,语言命名空间 `settings.switchblade` → `settingswukong`;同源路由 `/api/armory/*` → `/api/wukong/*`,壁纸路由 `/api/switchblade-wallpaper/*` → `/api/wukong-wallpaper/*`;斜杠命令族 `/armory[-*]` → `/wukong[-*]`(**去掉了 `/sw-*` 别名**);状态文档 `$DSH_HOME/switchblade/state.json` → `$DSH_HOME/wukong/state.json`,壁纸与导出目录收敛到 `$DSH_HOME/wukong/{wallpapers,exports}`;面板显示名 Armory → Wukong;浏览器样式标识 `data-ar-*` / `--ar-*` → `data-wk-*` / `--wk-*`。
126
+ **Every internal identifier was renamed in one pass** (this project is in testing and ships **no compatibility or migration for the old name**): cordis service `switchblade` → `wukong`; `src/switchblade.ts` → `src/wukong.ts` with class `Switchblade` → `Wukong` (likewise `WukongState`/`WukongPatch`/`WukongSection`); the panel's cordis plugin `ui-switchblade` → `ui-wukong`; settings entry id `switchblade` → `wukong` and locale namespace `settings.switchblade` → `settingswukong`; same-origin routes `/api/armory/*` → `/api/wukong/*` and wallpapers `/api/switchblade-wallpaper/*` → `/api/wukong-wallpaper/*`; the slash-command family `/armory[-*]` → `/wukong[-*]` (**the `/sw-*` aliases are gone**); the state document moved to `$DSH_HOME/wukong/state.json` with wallpapers and exports consolidated under `$DSH_HOME/wukong/{wallpapers,exports}`; the panel display name is now Wukong; browser style hooks `data-ar-*` / `--ar-*` became `data-wk-*` / `--wk-*`.
127
+ - **安装器清理旧名安装与旧数据**:安装时除了 `dsh-plugin-wukong` 本身,还会移除旧 bundle/依赖名(`dsh-switchblade`、`@deepseek-ai/dsh-switchblade`)、`node_modules/@deepseek-ai/` 下两份旧拷贝、profile patch 里手写的 `ui-switchblade` 行,以及旧数据目录 `$DSH_HOME/switchblade`、`$DSH_HOME/wallpapers`、`$DSH_HOME/armory-exports`。`uninstall` 现在连同 `$DSH_HOME/wukong/`(状态文档 + 壁纸 + 导出)一起删除——**配置不再保留**;`$DSH_HOME/skills` 仍保留。
128
+ **The installer cleans up both the old-name install and its data**: besides installing `dsh-plugin-wukong`, it removes the old bundle/dependency names (`dsh-switchblade`, `@deepseek-ai/dsh-switchblade`), the two old copies under `node_modules/@deepseek-ai/`, the hand-written `ui-switchblade` row in the profile patch, the old data directories `$DSH_HOME/switchblade`, `$DSH_HOME/wallpapers`, `$DSH_HOME/armory-exports`. `uninstall` now also deletes `$DSH_HOME/wukong/` (state document + wallpapers + exports) — **configuration is not kept**; `$DSH_HOME/skills` still is.
129
+ - **移除 `plugin-src/` 遗留镜像**:那是 0.13.0 之前两个旧包的快照,既不构建也不发布,随更名一并删除(需要时可从 git 历史取回)。
130
+ **Removed the legacy `plugin-src/` mirror**: it was a snapshot of the two pre-0.13 packages, neither built nor published, and went away with the rename (recoverable from git history if ever needed).
131
+
132
+ ## [0.13.1] - 2026-10
133
+
134
+ ### 修复 · Fixed
135
+ - **输入框下方统计条的提示样式在 0.2.1 世代重新生效**:统计条容器在 2.0.9 之后已由 `[data-composer-stats]` 改为 `[data-composer-dock]`,每条统计是 `[data-composer-stat]`(文字标签仍是哈希 `_label` 类)。旧选择器在当前 frontend 里匹配不到任何元素,因此提示样式的「统计条」部分实际是失效的。现在一条规则同时匹配 `[data-composer-dock]`、`[data-composer-stat]` 与遗留的 `[data-composer-stats]`;`[data-composer-placeholder]` 与 `[data-composer-card]` 两个标记在当前世代仍然存在,未改动。
136
+ **The composer stats-strip hint style works again on the 0.2.1 generation**: after 2.0.9 the harness renders the strip as `[data-composer-dock]` with one `[data-composer-stat]` pill per stat (labels keep a hashed `_label` class), so the old `[data-composer-stats]` selector matched nothing and that part of the hint style was dead. One rule now matches `[data-composer-dock]`, `[data-composer-stat]` and the legacy `[data-composer-stats]`; `[data-composer-placeholder]` and `[data-composer-card]` still exist in this generation and are unchanged.
137
+
138
+ ## [0.13.0] - 2026-10
139
+
140
+ ### 变更 · Changed
141
+ - **打包合并为单个插件包 `dsh-switchblade`**:原来的 `@deepseek-ai/dsh-switchblade`(Host)+ `@deepseek-ai/dsh-client-ui-switchblade`(面板)合并成一个可发布的包,位于 `packages/switchblade/`(不带 scope)。面板源码移到 `packages/switchblade/src/client/**`,浏览器产物改为 `packages/switchblade/client/client.js`。包内同时声明两面:`dsh.bundle.patch = ./cordis.patch.yml`(唯一的 Host 行 `id: switchblade`、`name: dsh-switchblade`)与 `dsh.client = { platform: 'web', inject: [...] }`(`exports["./client"]`)——与社区插件 `dsh-plugin` 同形。因此**不再需要第二个包、也不再需要第二条 loader 行**。
142
+ **Repackaged as one plugin package, `dsh-switchblade`**: the former `@deepseek-ai/dsh-switchblade` (host) + `@deepseek-ai/dsh-client-ui-switchblade` (panel) are now a single publishable, unscoped package under `packages/switchblade/`. Panel sources moved to `packages/switchblade/src/client/**` and the browser artifact is now `packages/switchblade/client/client.js`. The package declares both faces — `dsh.bundle.patch = ./cordis.patch.yml` (the single host row `id: switchblade`, `name: dsh-switchblade`) and `dsh.client = { platform: 'web', inject: [...] }` via `exports["./client"]` — the same shape the community `dsh-plugin` uses. **No second package and no second loader row are needed.**
143
+ - **安装器改走标准安装路径**:`cli.cjs` 在 profile 目录里执行 `pnpm add <spec>`,再把 `dsh-switchblade` 追加进 profile 的 `dsh.profile.bundles`,最后清理 0.13 之前拷贝式安装的遗留物:`node_modules/@deepseek-ai/dsh-switchblade` 与 `@deepseek-ai/dsh-client-ui-switchblade` 两份拷贝、旧 bundle 名 `@deepseek-ai/dsh-switchblade`,以及 profile `cordis.patch.yml` 里手写的 `ui-switchblade` 行(**只删这一条**,其余行逐字节保留,因为那份文件 DSH.app 也在写)。
144
+ **The installer now takes the standard install path**: `cli.cjs` runs `pnpm add <spec>` inside the profile, appends `dsh-switchblade` to the profile's `dsh.profile.bundles`, then cleans up the pre-0.13 copy-style install — the two copies under `node_modules/@deepseek-ai/`, the legacy bundle name `@deepseek-ai/dsh-switchblade`, and the hand-written `ui-switchblade` row in the profile's `cordis.patch.yml` (that entry only; every other line stays byte-identical because DSH.app writes that file too).
145
+ - **新增 `--spec`**:`--spec <npm 包名|本地路径|tarball|git 地址>` 可从任意来源安装。`--profile/-p` 行为不变(可重复 / 逗号分隔),`--postinstall` 不变。`uninstall` 改为 `pnpm remove dsh-switchblade` + 取消 bundle 选择 + 同样的遗留清理;状态文档与 `$DSH_HOME/skills` 照旧保留。
146
+ **New `--spec` flag**: `--spec <namespace|tarball|git url>` installs from any source. `--profile/-p` is unchanged (repeatable / comma lists), as is `--postinstall`. `uninstall` now runs `pnpm remove dsh-switchblade` + drops the bundle selection + the same legacy cleanup; the state document and `$DSH_HOME/skills` are still kept.
147
+ - **构建产物随打包变化**:`scripts/build-lib.mjs` 现在只产出 `packages/switchblade/lib/index.js`、`lib/invariant.js` 与 `packages/switchblade/client/client.js`。`scripts/build-shipped.mjs` 变成**可选**的上游同步:只拷 `<harness>/packages/switchblade/switchblade/src` → `packages/switchblade/src`、`<harness>/packages/client/ui-switchblade/src/client` → `packages/switchblade/src/client`、`lib/{index,invariant}.js`,并且**不会覆盖**仓库自己的 `package.json`、`cordis.patch.yml`、`client/client.js`;同步完仍需跑 `node scripts/build-lib.mjs`。
148
+ **Build outputs follow the repackaging**: `scripts/build-lib.mjs` now emits only `packages/switchblade/lib/index.js`, `lib/invariant.js` and `packages/switchblade/client/client.js`. `scripts/build-shipped.mjs` became an **optional** upstream sync: it copies only `<harness>/packages/switchblade/switchblade/src` → `packages/switchblade/src`, `<harness>/packages/client/ui-switchblade/src/client` → `packages/switchblade/src/client` and `lib/{index,invariant}.js`, and deliberately **never overwrites** this repo's `package.json`, `cordis.patch.yml` or `client/client.js`; you still run `node scripts/build-lib.mjs` afterwards.
149
+
150
+ ### 修复 · Fixed
151
+ - **`pnpm add` 不再因 `@deepseek-ai/*` 抓不到而 404 失败**:所有 `@deepseek-ai/*` 运行时包与 `react`/`react-dom` 都改在 `peerDependenciesMeta` 里标为 `optional`,运行时 `dependencies` 为空。这些包不在公共 npm 源上,写成必选 peer 会让 pnpm 直接 `ERR_PNPM_FETCH_404`;标成可选后 pnpm 能顺利装上插件,而模块仍由 harness 宿主在运行时提供。
152
+ **`pnpm add` no longer fails on `@deepseek-ai/*` fetch 404s**: every `@deepseek-ai/*` runtime package plus `react`/`react-dom` is now `optional` in `peerDependenciesMeta`, and runtime `dependencies` are empty. Those packages are not on the public npm registry, so required peers made pnpm abort with `ERR_PNPM_FETCH_404`; optional peers let pnpm install the plugin while the harness host supplies those modules at runtime.
153
+ - **插件现在会出现在 harness 的插件界面里**:它是带 bundle patch 的真实 pnpm profile 依赖,所以侧栏**插件**页与**设置 → 插件**的「已安装」列表都会列出并允许开关(旧的拷贝式安装没有依赖记录,因此从不出现)。面板本身仍在**设置 → Prompt-SkillArmory**(书图标)。
154
+ **The plugin now appears in the harness plugin surfaces**: being a real pnpm profile dependency with a bundle patch, it is listed and toggleable on the sidebar **Plugins** page and under **Settings → Plugins** → "Installed" (the old copy-style install left no dependency record, so it never showed up). The panel itself stays at **Settings → Prompt-SkillArmory** (book icon).
155
+
156
+ ### 移除 · Removed
157
+ - **`packages/client-ui-switchblade/` 目录**:面板已并入 `packages/switchblade/`(源码 `src/client/**`,产物 `client/client.js`)。
158
+ **The `packages/client-ui-switchblade/` directory**: the panel is now part of `packages/switchblade/` (sources `src/client/**`, artifact `client/client.js`).
159
+ - **`plugin-src/` 明确为遗留物**:它保存的是 0.13 之前的镜像(旧 `@deepseek-ai/...` 包名和第二个包),既不发布也不构建,可安全忽略。
160
+ **`plugin-src/` is now explicitly legacy**: it holds a pre-0.13 mirror (old `@deepseek-ai/...` names and the second package); it is neither shipped nor built and is safe to ignore.
161
+
162
+ ## [0.12.0] - 2026-10
163
+
164
+ ### 变更 · Changed
165
+ - **移植到 DSH 0.2.0-rc.2 接口世代**:harness 的 settings 服务改成了「每个 loader 条目一个命名空间」(`SettingsForms`),旧的 `settingsNamespace` / `settingsScope` 接口在该世代**已不存在**。0.12.0 按当前世代重写宿主与面板;**面向旧 2.0.9 世代的构建(0.11.x 及更早)不再受支持**,并且**有意不提供向后兼容**——两代接口只能二选一。
166
+ **Ported to the DSH 0.2.0-rc.2 internal-API generation**: the harness settings service became "one namespace per loader entry" (`SettingsForms`) and the old `settingsNamespace` / `settingsScope` interfaces are gone from that generation. 0.12.0 rewrites both halves against it; builds targeting the older 2.0.9-era generation (0.11.x and earlier) are no longer supported, with no backward compatibility on purpose.
167
+ - **持久化改为一个私有状态文档**:提示词 / 托管技能 / 自定义命令 / MCP 服务器 / 壁纸与特效设置全部写进 `$DSH_HOME/switchblade/state.json`(默认 `~/.dsh/switchblade/state.json`),**原子写入**(临时文件 + rename),不再占用 harness 的 settings 命名空间;壁纸字节在 `$DSH_HOME/wallpapers/`,zip/目录安装的技能落在 `$DSH_HOME/skills/`(官方 filesystem 技能 provider 扫描并监听该根目录),对话导出在 `$DSH_HOME/armory-exports/`。
168
+ **Persistence is now one private state document**: prompts, managed skills, custom commands, MCP servers and wallpaper/effect settings all live in `$DSH_HOME/switchblade/state.json` (default `~/.dsh/switchblade/state.json`), written **atomically** (temp file + rename); wallpaper bytes go to `$DSH_HOME/wallpapers/`, zip/directory-installed skills land in `$DSH_HOME/skills/` (the root the official filesystem skill provider scans and watches), and conversation exports go to `$DSH_HOME/armory-exports/`.
169
+ - **面板 ↔ Host 改走插件自有的同源路由**:`GET/POST /api/armory/state`、`POST /api/armory/mcp/test`、`POST /api/armory/skills/zip`、`POST /api/switchblade-wallpaper/upload`、`GET /api/switchblade-wallpaper/image/<id>`,加上原有的 `/api/armory/*` 对话/统计路由。旧的 `api.settings.mutate/describe` 与 `ctx.settingsScope` 路径全部移除;技能目录仍通过 remote RPC `api.skills.list` 读取。
170
+ **Panel ↔ Host now uses the plugin's own same-origin routes**: `GET/POST /api/armory/state`, `POST /api/armory/mcp/test`, `POST /api/armory/skills/zip`, `POST /api/switchblade-wallpaper/upload`, `GET /api/switchblade-wallpaper/image/<id>`, plus the existing `/api/armory/*` conversation/stats routes. The old `api.settings.mutate/describe` and `ctx.settingsScope` paths are gone; the skill catalog still comes over remote RPC (`api.skills.list`).
171
+ - **提示词细粒度作用域**:全局(默认)/ 仅指定项目(按会话工作区目录名匹配)/ 仅指定会话 / 除指定项目外 / 除指定会话外。只有作用域匹配的提示词才返回正文,不匹配的 section 返回空串并被渲染阶段丢弃——不会泄漏到其他项目或会话。
172
+ **Fine-grained prompt scoping**: global (default) / only-project (matched by the session workspace directory name) / only-session / all-except-project / all-except-session. A scoped prompt returns its body only for matching agents; non-matching sections resolve to an empty string that the render step drops, so nothing leaks into other projects or sessions.
173
+ - **构建改为 esbuild**:`node scripts/build-lib.mjs` 直接产出发布包,**不需要 deepseek-harness checkout**(打包只用 esbuild)。产物为 `packages/switchblade/lib/{index,invariant}.js` 与 `packages/client-ui-switchblade/lib/{index,invariant,client}.js`;浏览器面板打成 harness module-loader bundle(`window.__ModuleLoader__.load({ id, factory })`)。`scripts/build-shipped.mjs <harness-checkout>` 仍在,但职责缩小为「从 monorepo checkout 拷出源码/产物」。
174
+ **Build is now esbuild**: `node scripts/build-lib.mjs` produces the shipped artifacts with **no deepseek-harness checkout needed** (esbuild alone does the bundling). Outputs: `packages/switchblade/lib/{index,invariant}.js` and `packages/client-ui-switchblade/lib/{index,invariant,client}.js`; the browser panel is emitted as a harness module-loader bundle (`window.__ModuleLoader__.load({ id, factory })`). `scripts/build-shipped.mjs <harness-checkout>` still exists but is now only "copy sources/artifacts out of a monorepo checkout".
175
+ - **安装器支持只装指定 profile**:`npx prompt-skill-armory --profile web`(可重复、可逗号分隔,`-p` 为简写),`uninstall --profile web` 只从该 profile 卸载;不带该参数时行为不变(装到全部 profile)。名字写错会直接报错并列出当前可用的 profile。
176
+ **Installer can target specific profiles**: `npx prompt-skill-armory --profile web` (repeatable, comma lists accepted, `-p` is the short form), and `uninstall --profile web` removes it from that profile only; without the flag the behaviour is unchanged (every profile). A misspelled name fails immediately with the list of available profiles.
177
+
178
+ ### 修复 · Fixed
179
+ - **跨平台归档**:会话导出/导入依次尝试 `zip`/`unzip` → bsdtar(`tar`)→ PowerShell,不再是 Windows 专用的 `powershell.exe`。
180
+ **Cross-platform archives**: conversation export/import now tries `zip`/`unzip`, then bsdtar (`tar`), then PowerShell — no more Windows-only `powershell.exe`.
181
+ - **跨平台一键更新**:POSIX 上调 `npm`,仅在 Windows 上用 `npm.cmd`。
182
+ **Cross-platform one-click update**: calls `npm` on POSIX and `npm.cmd` only on Windows.
183
+ - **`dsh.client.inject` 列出真实存在的包**:`@deepseek-ai/dsh-api-remotes`、`@deepseek-ai/dsh-api-session-controller`、`@deepseek-ai/dsh-client-locale`、`@deepseek-ai/dsh-client-ui-settings`;面板的 cordis service inject 为 `['slots','locale','remote','remote.skills','sessions']`。
184
+ **`dsh.client.inject` now lists packages that actually exist**: `@deepseek-ai/dsh-api-remotes`, `@deepseek-ai/dsh-api-session-controller`, `@deepseek-ai/dsh-client-locale`, `@deepseek-ai/dsh-client-ui-settings`; the panel's cordis service inject is `['slots','locale','remote','remote.skills','sessions']`.
185
+
186
+ ### 移除 · Removed
187
+ - **Agent 预设 / prompt profile 管理**:`@deepseek-ai/dsh-agent-presets` 包在该世代已不存在,`/armory-profile` 命令一并移除(面板自 0.11.0 起就没有该 tab)。
188
+ **Agent-preset / prompt-profile management**: the `@deepseek-ai/dsh-agent-presets` package does not exist in this generation, so `/armory-profile` is gone too (the panel had no such tab since 0.11.0).
189
+ - **旧类型镜像与 `plugin-src/` 的定位**:`plugin-src/` 明确标记为**遗留镜像,不是发布真相**——以 `packages/`(`lib/` + `src/`)为准。
190
+ **Legacy type mirrors and the role of `plugin-src/`**: `plugin-src/` is now clearly marked as a **legacy mirror, not the shipped truth** — `packages/` (`lib/` + `src/`) is authoritative.
191
+
192
+ ### 命令 · Commands
193
+ - `/armory`、`/armory-enable`、`/armory-disable`、`/armory-install`、`/armory-uninstall`、`/armory-export`、`/armory-import`、`/armory-skill-dir`、`/armory-install-zip`、`/armory-state` —— 每个都保留 `/sw*` 旧别名。
194
+ `/armory`, `/armory-enable`, `/armory-disable`, `/armory-install`, `/armory-uninstall`, `/armory-export`, `/armory-import`, `/armory-skill-dir`, `/armory-install-zip`, `/armory-state` — each keeps a `/sw*` legacy alias.
195
+
196
+ ### 验证 · Verified
197
+ - 在隔离的 `DSH_HOME=/tmp/armory-test` 上端到端验证(`dsh web --no-open --port 3099`):`/api/armory/state` 的 GET/POST 正常、`state.json` 落盘、壁纸上传+回读字节一致、zip 技能解压进 `$DSH_HOME/skills`、MCP 启动/停止会组合出 `@deepseek-ai/dsh-mcp-client` loader 条目、面板 bundle 出现在 boot manifest 中并能驱动状态路由(新增提示词/开关、技能启用/卸载),五种作用域的匹配逐项断言。
198
+ **Verified end-to-end** on an isolated `DSH_HOME=/tmp/armory-test` (`dsh web --no-open --port 3099`): `/api/armory/state` GET/POST, `state.json` written, wallpaper upload + read-back byte-identical, zip skill install extracted into `$DSH_HOME/skills`, MCP start/stop composing a `@deepseek-ai/dsh-mcp-client` loader entry, the panel bundle discovered in the boot manifest and driving the state routes (prompt add/toggle, skill enable/uninstall), and scope matching asserted for all five scope types.
199
+
200
+ ## [0.11.0] - 2026-09
201
+
202
+ ### 变更 · Changed
203
+ - **提示词作用域更易用(下拉选择 + 排除模式)**:
204
+ - 作用域改为 5 选项下拉:全局 / 仅指定项目 / 仅指定会话 / **除指定项目外** / **除指定会话外**。
205
+ - 目标不再手填:项目从真实工作区(cwd 目录名)下拉,会话从真实会话列表下拉(显示「标题 · 项目 · 短 id」),无需记 session id。
206
+ - 新增**排除模式**——覆盖「全局生效、只有某个项目/会话不要」这一最常见诉求,一个开关搞定,不必逐个勾选。
207
+ - 列表徽章同步显示:`仅项目:qc` / `除会话 abc12345 外`。
208
+ **Prompt scoping is now easier (dropdowns + exclude modes)**: a 5-option scope dropdown (global / only-project / only-session / all-except-project / all-except-session); project and session targets are picked from real data (workspace cwd names, session titles) instead of typing ids; exclude modes cover the common "global but not here" case in one step; list badges reflect it.
209
+
210
+ ## [0.10.5] - 2026-09
211
+
212
+ ### 修复 · Fixed
213
+ - **输入框下方提示样式停用/启用异常**:此前的实现用 JS 直接改写元素 inline style——停用时只删样式标签、残留的 inline 样式没有还原(所以「关不掉」),启用时又给统计条容器加了字号与 `display:inline-block`(所以布局被撑动)。改为**单一 CSS 样式标签驱动**:停用即删除标签,DOM 零残留、完全还原;样式只作用于统计条 label 文本(font/color),不再触碰 flex 容器布局。
214
+ **Composer stats-strip style toggle was broken**: the old implementation wrote inline styles via JS — disabling removed only the style tag, leaving inline styles behind (so it never reverted), and enabling added font-size / `display:inline-block` to the strip container (so the layout shifted). Now driven by a single CSS tag: disabling deletes the tag with zero DOM residue (fully restores), and styles apply only to the strip label text (font/color), never touching the flex container layout.
215
+
216
+ ## [0.10.4] - 2026-09
217
+
218
+ ### 修复 · Fixed
219
+ - **输入框下方提示样式真正生效**:该样式针对的是输入框下方的**会话统计 + token 用量条**(2.0.9 渲染为 `[data-composer-stats]` 容器,内含 TimePill/UsagePill,分隔符为 `·`)。CSS 锚定 `[data-composer-stats]` 及其 `_label`/`_pill`(哈希类名用属性选择器匹配),JS 分隔符匹配从旧版 `|` 扩展为 `·`/ `|`。
220
+ **Composer stats-strip hint style now works**: this style targets the session-stats + token-usage strip below the composer, which 2.0.9 renders as `[data-composer-stats]` (TimePill/UsagePill, `·` separators). CSS anchors on `[data-composer-stats]` and its `_label`/`_pill` hashed classes via attribute selectors; JS separator matching now covers `·` and `|`.
221
+
222
+ ## [0.10.3] - 2026-09
223
+
224
+ ### 修复 · Fixed
225
+ - **输入框提示样式精准适配**:2.0.9 的提示/占位文本渲染为 `[data-composer-placeholder]` 独立 div(不是 `input::placeholder`)。提示样式直接作用于该元素,渐变色补 `display:inline-block` 确保 `background-clip:text` 生效。另确认 2.0.9 已移除旧 dock StatsLine(`span[aria-hidden="|"]`)。
226
+ **Hint style precisely retargeted**: 2.0.9 renders the composer hint/placeholder as a `[data-composer-placeholder]` div (not `input::placeholder`). The style now targets that element, with `display:inline-block` so gradient `background-clip:text` renders. Also confirmed the old StatsLine dock (`span[aria-hidden="|"]`) is gone in 2.0.9.
227
+
228
+ ## [0.10.2] - 2026-09
229
+
230
+ ### 修复 · Fixed
231
+ - **面板报错 `cannot get property "remote.skills" without inject`**:2.0.9 的 cordis 要求 client 插件在 `inject` 里显式声明 `remote.skills` 子命名空间(与官方 ui-skill 一致),且 `skills.list` 的签名是 `({ sessionId }, signal)` 而非旧的 `{ request: ... }`。补齐 inject + 修正调用后,面板正常加载,之前保存的提示词/技能/MCP 数据全部恢复显示(数据一直在 settings.yaml,从未丢失)。
232
+ **Panel error `cannot get property "remote.skills" without inject`**: 2.0.9's cordis requires the client plugin to declare the `remote.skills` sub-namespace in `inject` (matching official ui-skill), and `skills.list` takes `({ sessionId }, signal)` not the old `{ request: ... }`. With the inject added and the call fixed, the panel loads and previously saved prompts/skills/MCP data is restored (it was always in settings.yaml).
233
+ - **输入框提示样式适配 2.0.9**:2.0.9 移除了旧的 `[data-decoration="hint"]` 行,提示改在输入框内(placeholder + claim hint)。提示样式选择器改到稳定的 `[data-composer-card]`(placeholder 与内嵌提示文本),颜色/字号/渐变照常生效。
234
+ **Hint style adapted to 2.0.9**: the old `[data-decoration="hint"]` row is gone in 2.0.9; hints now live inside the composer card (placeholder + inline claim hint). The style now targets `[data-composer-card]` placeholder and hint text, keeping color/size/gradient working.
235
+
236
+ ## [0.10.1] - 2026-09
237
+
238
+ ### 修复 · Fixed
239
+ - **面板加载报错 `Cannot read properties of undefined (reading 'settings')`**:DSH 2.0.9 把 client 端 settings 访问从 `connection.api.settings` 改成 `ctx.settingsScope`(命名空间绑定)+ `ctx.remote`(RPC)——插件还走旧的 `api.settings`,api 为 undefined 直接抛错,导致提示词/技能/MCP/壁纸面板全部加载失败。已迁移:读走 `settingsScope.getSnapshot()`,写走 `scope.mutate(ops)`,技能走 `remote.skills.list({request})`。面板恢复后,之前安装的提示词/技能/MCP 数据依然在(settings.yaml 从未丢失)。
240
+ **Panel load error `Cannot read properties of undefined (reading 'settings')`**: DSH 2.0.9 moved client settings access from `connection.api.settings` to `ctx.settingsScope` (namespace-bound) + `ctx.remote` (RPC); the plugin still used the old `api.settings`, so api was undefined and the whole panel failed. Migrated: reads via `settingsScope.getSnapshot()`, writes via `scope.mutate(ops)`, skills via `remote.skills.list({request})`. Installed prompts/skills/MCP data was never lost (still in settings.yaml).
241
+ - **壁纸只覆盖对话区**:DSH 2.0.9 桌面端是 `.dshDesktopFrame` 三栏 grid(sidebar/conversation/rightbar),背景层挂在 body 下 z-index 为负被 surface 实色背景挡住,且 `data-dsh-desktop-material="off"` 的侧边栏用 `--dsw-alias-bg-layer-1` 实色。现在背景层 z-index 提到 0、`#root` 抬到 z-index 1、所有桌面 surface 背景强制透明(含 `--dsw-alias-bg-layer-1`),壁纸覆盖整个窗口。
242
+ **Wallpaper only covered the chat area**: 2.0.9 desktop renders a `.dshDesktopFrame` three-column grid; the negative-z body backdrop was hidden behind opaque surface fills (sidebar uses `--dsw-alias-bg-layer-1` at material=off). Backdrop is now z-index 0 with `#root` lifted above it and every desktop surface forced transparent, so the wallpaper covers the whole window.
243
+
244
+ ## [0.10.0] - 2026-09
245
+
246
+ ### 修复 · Fixed
247
+ - **DSH Desktop 2.0.9 恢复模式(第二次根治)**:修复 cordis 属性访问后,客户端面板仍报 `client-modules: require("@deepseek-ai/dsh-client-runtime/client") missed the module table`——2.0.9 的浏览器模块表不再把 `dsh-client-runtime/client` 作为可用种子,而插件的 snapshot store 从它导入 `createSnapshotStore`,构建产物残留运行时 require 导致加载失败。改为**内联本地 snapshot store** 并移除对 runtime 的运行时依赖(`ClientContext`/会话列表改用本地接口),2.0.9 下实测完整启动。
248
+ **DSH Desktop 2.0.9 recovery mode (second root cause)**: after the cordis fix, the panel still failed with `client-modules: require("@deepseek-ai/dsh-client-runtime/client") missed the module table` — 2.0.9's browser module table no longer seeds `dsh-client-runtime/client`, but the plugin imported `createSnapshotStore` from it, leaving a runtime require in the bundle. Now inlines a local snapshot store and drops the runtime dependency (local `ClientContext`/session interfaces); verified a full boot on 2.0.9.
249
+ - **提示词作用域(精细化差分)**:每个提示词可选 全局 / 指定项目(cwd 目录名)/ 指定会话(session id),Host 按 agent 的 cwd/sessionId 匹配注入,零全局污染。
250
+ **Prompt scoping**: each prompt can target global / a project / a session with zero leakage.
251
+
252
+ ## [0.9.9] - 2026-09
253
+
254
+ ### 修复 · Fixed
255
+ - **DSH Desktop 2.0.9 恢复模式(根治)**:2.0.9 的 cordis 禁止对未注入的服务做属性访问。插件里 `fiber.ctx.switchblade` 属性访问在加载时抛 `cannot get property "switchblade" without inject`,导致整个客户端启动失败进恢复模式。改为 `ctx.get('switchblade')` 惰性解析,并在 Switchblade 服务的 `static inject` 补上 `commands`;配合上一版对 `cordis.patch.yml` 的块级去重,2.0.9 下已通过 `dsh --profile desktop` 实测完整启动。
256
+ **DSH Desktop 2.0.9 recovery mode (root cause)**: 2.0.9's cordis forbids property access to non-injected services. The plugin's `fiber.ctx.switchblade` property access threw `cannot get property "switchblade" without inject` at load, failing startup into recovery mode. Switched to lazy `ctx.get('switchblade')` and added `commands` to `static inject`; verified a full boot via `dsh --profile desktop` on 2.0.9.
257
+ - **提示词作用域(精细化差分)**:每个提示词可选 全局 / 指定项目(cwd 目录名)/ 指定会话(session id)。Host 通过 systemPrompt section 的 text 函数按当前 agent 的 cwd/sessionId 匹配,不匹配返回空串(渲染时自动过滤)——零全局污染。
258
+ **Prompt scoping (fine-grained diff)**: each prompt can target global / a project (cwd basename) / a session (id); non-matching agents get an empty section that render filters out.
259
+
260
+ ## [0.9.8] - 2026-08
261
+
262
+ ### 修复 · Fixed
263
+ - **DSH Desktop 2.0.9 恢复模式**:安装器对 `cordis.patch.yml` 的行级去重会把注释行和裸 `- insert:` 当用户条目保留,每次安装/更新都叠加一段残缺 insert 块;2.0.9 的 patch 解析器拒绝无 body 的 insert 导致启动崩溃。改为**块级去重**(命中任一 Armory 管理行即整块清理),并重置已有 profile 的 patch 文件。
264
+ **DSH Desktop 2.0.9 recovery mode**: the installer's line-based dedup kept the comment/header and bare `- insert:` lines as "user entries", appending a truncated insert block on every install/update; 2.0.9's patch parser rejects body-less inserts and crashed at boot. Now block-level (any Armory-managed line clears the whole block), and existing profile patch files are reset.
265
+ - **技能名含点号无法注册**:DSH 技能名必须是 kebab-case(`gpt-5.6-sol` 被拒)。安装/注册时自动规范化名字(点号转连字符)。
266
+ Skills with dots failed to register: DSH requires kebab-case names. Names are now normalized on install/register.
267
+
268
+ ### 新增 · Added
269
+ - **提示词作用域(精细化差分)**:每个提示词可选 全局 / 指定项目(按 cwd 目录名)/ 指定会话(session id)。Host 通过 systemPrompt section 的 text 函数按当前 agent 的 cwd/sessionId 匹配,不匹配返回空串(渲染时自动过滤)——零全局污染,其他项目/会话完全不受影响。
270
+ **Prompt scoping (fine-grained diff)**: each prompt can target global / a specific project (cwd basename) / a specific session (id). The Host matches via a text provider against the assembling agent's cwd/sessionId and returns '' otherwise (filtered at render) — no global leakage.
271
+
272
+ ## [0.9.7] - 2026-08
273
+
274
+ ### 修复 · Fixed
275
+ - 对话删除与工作区下拉「没反应」:导出/导入/删除的异步处理缺少 try/finally,一旦下载抛错 `chatBusy` 卡死为 true,整个工具栏被禁用。现在所有处理统一 try/finally 复位,并逐会话容错删除、同步清理 `workspace.json` 里的 sessionId 引用(防止幽灵会话复活)。
276
+ Delete and the workspace selector appeared dead: the async handlers lacked try/finally, so a failed download left `chatBusy` stuck true and disabled the whole toolbar. Handlers now always reset via finally; deletion is per-session resilient and scrubs sessionIds from `workspace.json` (no ghost-session resurrection).
277
+ - 工作区名过长溢出面板:会话列表徽章加最大宽度 + 省略号,整体导出下拉限宽 220px。
278
+ Long workspace names no longer overflow: the session badge gets max-width + ellipsis and the export selector is capped at 220px.
279
+ - 折线图 Token 轴改为对数刻度:输入 Token 通常比输出大一个数量级,线性轴会把输出线压成贴地;对数轴让输入/输出/缓存的形状都清晰可读(步骤轴保持线性)。
280
+ The chart token axis now uses a log scale: input tokens are typically an order of magnitude above output, so a linear axis flattened the output line into the baseline; log scale keeps every series readable (the steps axis stays linear).
281
+
282
+ ## [0.9.6] - 2026-08
283
+
284
+ ### 修复 · Fixed
285
+ - 折线图输入 Token 线不可见:右轴此前只用输出 Token 缩放,输入 Token 通常大一个数量级,Y 坐标算出负值被画出视口顶部。现在输入/输出/缓存读/缓存写全部参与右轴缩放,并补上输入 Token 的数据点、数值标签与圆整刻度。
286
+ The input-token line was invisible: the right axis was scaled on output tokens alone, and input tokens (typically an order of magnitude larger) produced negative Y coordinates above the viewport. All token series now share the axis scale, and input-token dots, value labels and rounded ticks are rendered.
287
+
288
+ ## [0.9.5] - 2026-08
289
+
290
+ ### 新增 · Added
291
+ - 使用统计折线图实时刷新:统计标签页打开时每 30 秒自动轮询当前范围(今天 / 7天 / 30天 / 全部),并在标题旁显示「上次更新」时间;切换范围立即拉取最新数据。
292
+ Usage-stats chart now live-refreshes: while the stats tab is open it polls the active range (today / 7d / 30d / all) every 30 seconds, shows the last-updated time next to the title, and fetches instantly when switching ranges.
293
+ - 所有范围的按日折线都补全到今天(缺的日期补 0),7 天 / 30 天 / 全部的趋势线尾巴始终顶到最新使用状态;Host 端加 4 秒 TTL 缓存避免轮询反复读盘。
294
+ Daily trend lines for every range are now padded through today (missing days filled with 0), so the 7d / 30d / all lines always reach the latest usage; the Host adds a 4s TTL cache so polling does not re-read the projcache every tick.
295
+
296
+ ## [0.9.4] - 2026-08
297
+
298
+ ### 修复 · Fixed
299
+ - 「今天」统计改用会话的**最后活动时间**(`lastPromptAt`,回退到创建时间)做过滤与按小时分桶:此前按创建时间过滤会把「前几天创建、今天仍在使用」的会话整个跳过,导致今天折线图空白。同时按日统计改为本地时区日期(不再用 UTC `toISOString` 产生跨日错位)。
300
+ The "today" stats now use each session's **last activity time** (`lastPromptAt`, falling back to creation time) for filtering and hourly bucketing: previously sessions created earlier but still used today were skipped entirely, leaving the today chart blank. Daily bucketing also switched to local-timezone dates (no more UTC shift).
301
+
302
+ ## [0.9.3] - 2026-08
303
+
304
+ ### 新增 · Added
305
+ - 对话导出支持「整个项目工作区」:下拉选择项目后一键导出该项目下的全部对话。
306
+ Conversation export now supports whole-project workspaces: pick a project and export every conversation under it in one click.
307
+ - 导出包携带工作区注册表(title/path)与每个会话的 identity(createdAt/cwd/title),导入后合并写回 `workspace.json` 与 `session_projcache.json`,项目工作区名不再变成「未命名」,尽量与导出端一致。
308
+ The export zip now carries the workspace registry (title/path) plus per-session identity (createdAt/cwd/title); import merges them back into `workspace.json` and `session_projcache.json`, so project workspace names survive the move instead of falling back to "unnamed".
309
+
310
+ ## [0.9.2] - 2026-08
311
+
312
+ ### 修复 · Fixed
313
+ - 对话导出仅包含勾选的会话(不再打包全局附件/工作区,避免泄露其他内容)。
314
+ Conversation export now includes only the selected sessions (global attachments/workspace are no longer bundled, preventing leakage).
315
+ - 更新提示并入标题版本号旁(移除独立横幅)。
316
+ The update prompt now sits next to the version badge in the header (standalone banner removed).
317
+
318
+ ## [0.9.1] - 2026-08
319
+
320
+ ### 修复 · Fixed
321
+ - 一键更新失败:npm 包 bin 名实际为 `armory`,旧命令 `npx prompt-skill-armory` 找不到同名命令。新增 `prompt-skill-armory` bin 别名,Host 更新改用 `npm exec --package=prompt-skill-armory -- armory`(shell 模式 + 更长超时),并把具体错误信息回显到面板。
322
+ Fixed one-click update: the npm bin is `armory`, so `npx prompt-skill-armory` failed. Added a `prompt-skill-armory` bin alias, switched the Host update to `npm exec --package=prompt-skill-armory -- armory` (shell mode + longer timeout), and surface the real error in the panel.
323
+
324
+ ## [0.9.0] - 2026-08
325
+
326
+ ### 变更 · Changed
327
+ - **移除 Agent 预设 tab** 及相关 UI/状态/注入。
328
+ Removed the Agent Presets tab and related UI/state/injection.
329
+ - **使用统计全面升级**(对齐 cc-switch):时间范围筛选(全部/30天/7天/今天)、输入/输出/缓存读/缓存写 Token、缓存命中率、成本估算、请求日志(时间/项目/模型/输入输出/缓存/成本/用时/首字/状态)、Provider(项目)统计、模型统计、SVG 双轴趋势图,今天按 24 小时分段。
330
+ Usage stats overhaul (cc-switch style): range filter, input/output/cache tokens, cache-hit rate, cost estimate, request log, provider/project stats, model stats, SVG dual-axis trend chart (24h split for today).
331
+
332
+ ## [0.8.3] - 2026-08
333
+
334
+ ### 新增 · Added
335
+ - **一键更新**:面板自动检测 npm 最新版本,发现新版本时顶部横幅提示并可一键更新(Host 端运行官方安装器原地重装)。
336
+ **One-click update**: the panel auto-checks the npm registry; when a newer version exists it shows a banner with a one-click update (the Host re-runs the official installer in place).
337
+ - 删除/导入对话后同步刷新 DSH 官方会话列表。
338
+ The official DSH conversation list now refreshes after delete/import.
339
+
340
+ ## [0.8.2] - 2026-08
341
+
342
+ ### 新增 · Added
343
+ - **真实删除对话**:对话 tab 新增「删除选中」(带确认),删除会话目录并从 DSH 会话索引移除。
344
+ **Real conversation delete**: a "delete selected" action (with confirm) in the Chat tab that removes the session directory and its index entry.
345
+
346
+ ## [0.8.1] - 2026-08
347
+
348
+ ### 新增 · Added
349
+ - **卸载命令**:`npx prompt-skill-armory uninstall` 移除插件(包、bundle、面板挂载)并清理壁纸/导出目录。
350
+ **Uninstall command**: `npx prompt-skill-armory uninstall` removes the plugin (packages, bundle, panel mount) and cleans up wallpaper/export dirs.
351
+ - README 增加 LINUX DO 友情链接。
352
+ Added a LINUX DO friend link to the README.
353
+
354
+ ## [0.8.0] - 2026-08
355
+
356
+ ### 新增 · Added
357
+ - **对话导入 / 导出**:勾选多个会话导出为 zip(含附件与工作区),另一台机器导入即可还原;新增「对话」tab,显示对话标题、项目与时间。
358
+ **Conversation import/export**: export selected sessions as a zip (with attachments & workspace) and restore them on another machine; new "Chat" tab showing title, project and time.
359
+ - 会话标题回退:读取 DSH 会话缓存标题,缺失时从会话日志取第一条用户消息(与官方默认标题一致)。
360
+ Title fallback: use the DSH session-cache title, falling back to the first user message from the log (matching the official default-title rule).
361
+
362
+ ## [0.7.2] - 2026-08
363
+
364
+ ### 变更 · Changed
365
+ - 插件展示名更名 **Armory**(面板标题、设置导航、README 定位为 DeepSeek Harness 社区插件/管理中枢,覆盖提示词/技能/MCP/壁纸/预设);npm 与仓库名保持 `prompt-skill-armory`。
366
+ Renamed the display name to **Armory** (panel title, nav, README) and positioned it as a DeepSeek Harness community plugin / control center (prompts, skills, MCP, wallpaper, presets); npm & repo stay `prompt-skill-armory`.
367
+
368
+ ## [0.7.1] - 2026-08
369
+
370
+ ### 修复 · Fixed
371
+ - 渐变色方案:正确解析渐变 id → CSS(修复除「纯色」外渐变文字未生效)。
372
+ Gradient presets now resolve the preset id to its CSS (fixes gradient text not applying).
373
+
374
+ ## [0.7.0] - 2026-08
375
+
376
+ ### 新增 · Added
377
+ - 可配置全局壁纸:本地上传图片/视频(字节存盘 `~/.dsh/wallpapers`,settings 只存 id,不撑爆配置文档)或图片 URL;透明度 / 遮罩 / 玻璃透明度 / 玻璃模糊 / 壁纸模糊 / 铺法可调。
378
+ Configurable global wallpaper: local image/video upload (bytes on disk, id-only in settings) or an image URL; adjustable opacity / scrim / glass / blur / fit.
379
+ - 桌面客户端与网页端分别设置壁纸与样式(`backgroundDesktop` / `backgroundWeb` 分字段)。
380
+ Per-surface wallpaper & styling for desktop vs web.
381
+ - 输入栏下方提示行(含 dock 统计行)样式:启用 / 颜色 / 字号 / 渐变色预设(极光/火焰/晴空/霓虹/海洋/晚霞)。
382
+ Composer hint-line (incl. dock stats row) styling: enable / color / size / gradient presets.
383
+ - Wallpaper tab 移至第四列并更名 Wallpaper;启用改为醒目滑动开关。
384
+ Wallpaper tab moved to 4th column and renamed Wallpaper; a prominent enable switch.
385
+
386
+ ## [0.5.6] - 2026-08
387
+
388
+ ### 变更 · Changed
389
+ - 三个 tab 改为上下布局(操作区在上、列表在下、列表 flex 自适应),三个 tab 高度一致,消除底部留白与溢出。
390
+ Tabs switched to a vertical layout (form on top, list below, list flex-adaptive) so all three tabs share one height — no bottom whitespace or overflow.
391
+ - 版本徽章同步到 0.5.6。Version badge bumped to 0.5.6.
392
+
393
+ ## [0.5.5] - 2026-08
394
+
395
+ ### 修复
396
+ - 重新构建 client bundle,让发布的 `lib/client.js` 携带当前版本徽章(`v0.5.5`)——之前只改源码版本号导致已发布 bundle 过期。
397
+
398
+ ## [0.5.4] - 2026-08
399
+
400
+ ### 修复
401
+ - Profile 的 `cordis.patch.yml` 现在是纯顶层 `- insert:` 列表(之前的 `[]` + insert 形式是非法 YAML——“document separator expected”,会让 desktop 客户端进入恢复模式)。
402
+
403
+ ## [0.5.3] - 2026-08
404
+
405
+ ### 修复
406
+ - 安装器幂等地重写格式错误的 `cordis.patch.yml`(旧安装器产物)为干净格式。
407
+
408
+ ## [0.5.2] - 2026-08
409
+
410
+ ### 修复
411
+ - 安装器现在**始终确保 desktop profile 存在**(客户端默认用 `desktop` profile)并装好插件——新机器之前只有 `web` profile,客户端没有面板。
412
+
413
+ ## [0.5.1] - 2026-08
414
+
415
+ ### 修复
416
+ - 安装器在新机器上同时创建 `web` 和 `desktop` 两个 profile。
417
+
418
+ ## [0.5.0] - 2026-08
419
+
420
+ ### 新增
421
+ - **DSH Desktop 客户端支持**:面板同时出现在官方 `dsh web` 和 DSH Desktop 客户端。安装器把 `ui-switchblade` 挂进每个 profile 的 `cordis.patch.yml`(已验证的正确位置——不是 web-app patch,那会重复挂载崩溃),并把两个插件包装进所有 profile。
422
+
423
+ ### 修复
424
+ - Profile 的 `cordis.patch.yml` 以合法 YAML 写入(`[]`,绝不是带引号的 `"[]"`——那会破坏 desktop 客户端的恢复模式)。
425
+ - 安装器每个 profile 只挂一次客户端面板(去重)。
426
+
427
+ ## [0.4.7] - 2026-08
428
+
429
+ ### 修复
430
+ - 重新构建 client bundle,让发布的 `lib/client.js` 携带 Prompt•Skill-Armory 名称和当前版本徽章(只改源码导致已发布 bundle 过期)。
431
+
432
+ ## [0.4.6] - 2026-08
433
+
434
+ ### 变更
435
+ - 面板 + 侧边栏名称 **Prompt•Skill-Armory**(圆点分隔符),Prompt 与 Skill 共享同一强调色。版本徽章跟随发布版本。
436
+
437
+ ## [0.4.5] - 2026-08
438
+
439
+ ### 变更
440
+ - 面板和侧边栏名称现为 **Prompt•Skill-Armory**;Prompt 与 Skill 共享强调色。
441
+ - 安装器自动挂载客户端面板(cordis.patch.yml 的 ui-switchblade 行),面向 desktop / web profile。
442
+ - 安装器体检:settings.yaml 超过客户端 4MB 上限时告警(陈旧的 pendingZip 残留)。
443
+
444
+ ## [0.4.4] - 2026-08
445
+
446
+ ### 修复
447
+ - 安装器覆盖过期的符号链接/junction(先 rmSync 再复制),并安装进每个 profile(web + desktop)。
448
+ - settings 膨胀(5MB 陈旧的 zip base64)不再卡死 desktop 客户端。
449
+
450
+ ## [0.4.1] - 2026-08
451
+
452
+ ### 变更
453
+ - 统一命名为 **Prompt-SkillArmory**:CLI 命令现在是 `/armory-*`(`/armory`、`/armory-enable`、`/armory-skill-dir`、`/armory-install-zip`…),保留旧 `/sw-*` 别名以兼容。
454
+
455
+ ## [0.4.0] - 2026-08
456
+
457
+ ### 变更
458
+ - 技能 tab 现在是所有技能的唯一入口:面板安装的(managed)和本地扫描的合并进一个列表,每张卡片带调用提示(`/name`)和完整管理(增/改/开关/卸载/收养)。
459
+ - 移除单独的 "Local skills" tab 和 web zip 上传按钮(CLI 的 `/sw-install-zip` / `/sw-skill-dir` 命令仍是 zip/dir 安装路径)。
460
+ - 技能 tab 增加了 CLI 命令参考框。
461
+ - 面板头部版本徽章更清晰。
462
+
463
+ ## [0.3.1] - 2026-08
464
+
465
+ ### 修复
466
+ - 移除 web 面板的 zip-queue 路径(`pendingZip` settings 字段 + `handlePendingZip`),它死锁了 settings/api 通道、让整个 UI 空白。稳定的提示词 + 技能流程恢复;zip 技能经 `/sw-install-zip` CLI 命令安装(直接 Host 调用,不走 settings 往返)。
467
+
468
+ ## [0.3.0] - 2026-08
469
+
470
+ ### 修复
471
+ - **提示词现在真正生效。** 根因链:包 `main` 指向 `lib/types/index.js`(tsc 原始产物),loader 无法识别为插件——插件从未挂载。把 `main` 恢复为 `lib/index.js`(tsdown bundle),并在 `static inject` 声明 `skills` / `systemPrompt`,服务才能解析它们(`/sw` 之前报 "cannot get property skills without inject")。提示词经 `ctx.systemPrompt.section` 全局注入;技能经 `ctx.skills.register` 注册。
472
+
473
+ ## [0.2.0] - 2026-08
474
+
475
+ ### 新增
476
+ - 提示词编辑:改已存提示词的名称/描述/内容。
477
+ - 技能编辑:改已装技能的名称/描述/内容。
478
+ - 本地技能扫描:第 4 个 tab 列出本地扫描的技能,带"收养"操作。
479
+ - 设置侧边栏 Prompt-SkillArmory 导航的书图标。
480
+ - Tab 计数加载中显示 `…`(不再误导性显示 `0`)。
481
+
482
+ ### 变更
483
+ - 面板改名为 **Prompt-SkillArmory**,配打开的书形图标。
484
+ - 布局改为设置对话框固定宽度内的 4 个 tab:提示词 / 技能 / Agent 预设 / 本地技能。
485
+ - 技能 tab 只管理面板安装的技能;本地技能 tab 只列本地扫描的。
486
+ - Tab 和头部标签不再强制大写。
487
+ - Host 端提示词/技能对账全程受保护(settings 提交路径无死锁;修好 `signal timed out`)。
488
+
489
+ ## [0.1.0] - 2026-08
490
+
491
+ ### 新增
492
+ - 首个发布:CCswitch 风格的提示词、技能、预设管理器。
493
+ - 提示词:增 / 启用 / 停用 / 设默认 / 删除;全局注入系统提示词。
494
+ - 技能:从本地 `.md` 或手动输入安装;启用 / 停用 / 卸载。
495
+ - 预设:浏览 agent-preset 名册,设默认。
496
+ - 中英双语 UI。
497
+ - 每个 tab 内搜索 + 滚动。