prompt-skill-armory 0.9.7 → 0.9.9

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
@@ -12,13 +12,13 @@
12
12
  |--|--|--|--|
13
13
  | <img src="docs/preview/preview-6.png" width="220"/> | <img src="docs/preview/preview-7.png" width="220"/> | | |
14
14
 
15
- ![version](https://img.shields.io/badge/version-0.9.7-00ff9c)
15
+ ![version](https://img.shields.io/badge/version-0.9.9-00ff9c)
16
16
  ![license](https://img.shields.io/badge/license-MIT-blue)
17
17
  ![platform](https://img.shields.io/badge/platform-web%20%2B%20desktop-00ff9c)
18
18
  ![CI](https://github.com/Qian-Ning/prompt-skill-armory/actions/workflows/ci.yml/badge.svg)
19
19
 
20
20
  ```
21
- ✦ Armory v0.9.7
21
+ ✦ Armory v0.9.9
22
22
  ┌─────────┬─────────┬─────┬────────────┬──────┬──────┐
23
23
  │ Prompts │ Skills │ MCP │ Wallpaper │ Chat │ Stats│
24
24
  │ global │ merged │ hub │ wallpaper │ sync │ usage│
@@ -30,6 +30,7 @@
30
30
  ## 📑 Table of contents
31
31
 
32
32
  - [🚀 Quick start](#-quick-start)
33
+ - [🎯 Supported upstreams](#-supported-upstreams)
33
34
  - [✨ Features](#-features)
34
35
  - [🖥️ Platform support](#️-platform-support)
35
36
  - [🔄 One-click update](#-one-click-update)
@@ -46,6 +47,19 @@
46
47
 
47
48
  ---
48
49
 
50
+ ## 🎯 Supported upstreams
51
+
52
+ Armory is a **DeepSeek Harness plugin**, built and tested against exactly these two official repositories:
53
+
54
+ | Upstream | Environment | Repo |
55
+ |---|---|---|
56
+ | **DeepSeek Harness** | `dsh web` web runtime | <https://github.com/deepseek-ai/deepseek-harness> |
57
+ | **DSH Desktop** | desktop client | <https://github.com/anywhere-labs/dsh-desktop> |
58
+
59
+ > ⚠️ **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, settings from applying, missing commands, 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.
60
+
61
+ ---
62
+
49
63
  ## 🚀 Quick start
50
64
 
51
65
  ### Install
@@ -102,8 +116,8 @@ Open **Settings → Armory** (book icon).
102
116
 
103
117
  | Environment | Panel | `/armory` commands |
104
118
  |---|---|---|
105
- | `dsh web` (official) | ✅ | ✅ |
106
- | DSH Desktop client | ✅ | ✅ |
119
+ | `dsh web` ([deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)) | ✅ | ✅ |
120
+ | DSH Desktop client ([anywhere-labs/dsh-desktop](https://github.com/anywhere-labs/dsh-desktop)) | ✅ | ✅ |
107
121
 
108
122
  Achieved via profile composition (the installer mounts the panel into each profile's `cordis.patch.yml`) — no client fork needed.
109
123
 
@@ -111,7 +125,7 @@ Achieved via profile composition (the installer mounts the panel into each profi
111
125
 
112
126
  Every time the panel opens it queries npm for the latest `prompt-skill-armory` version; when a newer version exists a banner appears:
113
127
 
114
- > New version vX.Y.Z (current v0.9.7) [Update now]
128
+ > New version vX.Y.Z (current v0.9.9) [Update now]
115
129
 
116
130
  Clicking "Update now" makes the Host re-run the official installer in place → "Update complete, please restart the client".
117
131
 
package/README.md CHANGED
@@ -12,13 +12,13 @@
12
12
  |--|--|--|--|
13
13
  | <img src="docs/preview/preview-6.png" width="220"/> | <img src="docs/preview/preview-7.png" width="220"/> | | |
14
14
 
15
- ![version](https://img.shields.io/badge/version-0.9.7-00ff9c)
15
+ ![version](https://img.shields.io/badge/version-0.9.9-00ff9c)
16
16
  ![license](https://img.shields.io/badge/license-MIT-blue)
17
17
  ![platform](https://img.shields.io/badge/platform-web%20%2B%20desktop-00ff9c)
18
18
  ![CI](https://github.com/Qian-Ning/prompt-skill-armory/actions/workflows/ci.yml/badge.svg)
19
19
 
20
20
  ```
21
- ✦ Armory v0.9.7
21
+ ✦ Armory v0.9.9
22
22
  ┌─────────┬─────────┬─────┬────────────┬──────┬──────┐
23
23
  │ 提示词 │ 技能 │ MCP │ Wallpaper │ 对话 │ 统计 │
24
24
  │ 全局注入 │ 合并管理 │ 连接 │ 壁纸·特效 │ 导入 │ 用量 │
@@ -30,6 +30,7 @@
30
30
  ## 📑 目录
31
31
 
32
32
  - [🚀 快速开始](#-快速开始)
33
+ - [🎯 适配目标 · Supported upstreams](#-适配目标--supported-upstreams)
33
34
  - [✨ 功能](#-功能)
34
35
  - [🖥️ 平台支持](#️-平台支持)
35
36
  - [🔄 一键更新](#-一键更新)
@@ -46,6 +47,19 @@
46
47
 
47
48
  ---
48
49
 
50
+ ## 🎯 适配目标 · Supported upstreams
51
+
52
+ Armory 是 **DeepSeek Harness 的插件**,专为以下两个官方仓库构建与测试:
53
+
54
+ | 上游 | 对应环境 | 仓库 |
55
+ |---|---|---|
56
+ | **DeepSeek Harness** | `dsh web` 网页端运行时 | <https://github.com/deepseek-ai/deepseek-harness> |
57
+ | **DSH Desktop** | 桌面客户端 | <https://github.com/anywhere-labs/dsh-desktop> |
58
+
59
+ > ⚠️ **请使用上述官方仓库的构建(或其保持兼容的派生版)。** 其他 fork / 魔改版本的内部接口可能不同,导致面板无法挂载、设置不生效、命令缺失,甚至让客户端进入恢复模式——那不是本插件的 bug。安装或反馈前请先确认你的运行时来自以上两个出处。
60
+
61
+ ---
62
+
49
63
  ## 🚀 快速开始
50
64
 
51
65
  ### 安装
@@ -105,8 +119,8 @@ pnpm dsh web
105
119
 
106
120
  | 环境 | 面板 | `/armory` 命令 |
107
121
  |---|---|---|
108
- | `dsh web`(官方) | ✅ | ✅ |
109
- | DSH Desktop 客户端 | ✅ | ✅ |
122
+ | `dsh web`([deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)) | ✅ | ✅ |
123
+ | DSH Desktop 客户端([anywhere-labs/dsh-desktop](https://github.com/anywhere-labs/dsh-desktop)) | ✅ | ✅ |
110
124
 
111
125
  通过 profile 组合实现(安装器把面板挂进每个 profile 的 `cordis.patch.yml`)——无需 fork 客户端。
112
126
 
@@ -114,7 +128,7 @@ pnpm dsh web
114
128
 
115
129
  插件面板每次打开会自动查询 npm 上 `prompt-skill-armory` 的最新版本;发现新版本时顶部显示横幅:
116
130
 
117
- > 有新版本 vX.Y.Z(当前 v0.9.7) [一键更新]
131
+ > 有新版本 vX.Y.Z(当前 v0.9.9) [一键更新]
118
132
 
119
133
  点击「一键更新」→ Host 端运行官方安装器原地重装 → 提示「更新完成,请重启客户端生效」。
120
134
 
package/cli.cjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * prompt-skill-armory — one-command installer.
4
4
  *
@@ -141,13 +141,24 @@ function mountClientPanel(profileDir) {
141
141
  console.log(` · ui-switchblade already mounted in ${patchPath}`)
142
142
  return
143
143
  }
144
- // Preserve any real user entries, dropping an old bare `[]` line.
144
+ // Block-level dedup: if ANY Armory-managed line (comment header, the id/name
145
+ // pair, or a bare stub we previously wrote) is present, drop every one of
146
+ // them. Line-based filtering used to keep the header + bare `- insert:`
147
+ // lines (they don't contain the literal "ui-switchblade"), re-appending a
148
+ // truncated insert block on every install/update — which the 2.0.9 patch
149
+ // parser rejects (recovery mode). This must remove the whole block at once.
150
+ const hasArmory = /Prompt-SkillArmory client panel|ui-switchblade/.test(existing)
145
151
  const meaningful = existing
146
152
  .split('\n')
147
153
  .map((l) => l.replace(/\r$/, ''))
148
154
  .filter((l) => {
149
155
  const t = l.trim()
150
- return t !== '' && t !== '[]' && !(l.includes('ui-switchblade') && (l.includes('id:') || l.includes('name:') || l.includes('# Prompt-SkillArmory')))
156
+ if (t === '' || t === '[]') return false
157
+ if (!hasArmory) return true
158
+ if (t.includes('Prompt-SkillArmory client panel')) return false
159
+ if (t === '- insert:') return false
160
+ if (t.includes('ui-switchblade')) return false
161
+ return true
151
162
  })
152
163
  const base = meaningful.length > 0 ? meaningful.join('\n') + '\n' : ''
153
164
  writeFileSync(patchPath, `${base}${canonical}`)
@@ -186,11 +197,18 @@ function removeBundle(profileDir) {
186
197
  function unmountClientPanel(profileDir) {
187
198
  const patchPath = join(profileDir, 'cordis.patch.yml')
188
199
  if (!existsSync(patchPath)) { console.log(' · no cordis.patch.yml'); return }
200
+ // Same block-level removal: strip every Armory-managed line, keep user rows.
201
+ const hasArmory = /Prompt-SkillArmory client panel|ui-switchblade/.test(readFileSync(patchPath, 'utf8'))
189
202
  const meaningful = readFileSync(patchPath, 'utf8')
190
203
  .split('\n').map((l) => l.replace(/\r$/, ''))
191
204
  .filter((l) => {
192
205
  const t = l.trim()
193
- return t !== '' && t !== '[]' && !(l.includes('ui-switchblade') && (l.includes('id:') || l.includes('name:') || l.includes('# Prompt-SkillArmory')))
206
+ if (t === '' || t === '[]') return false
207
+ if (!hasArmory) return true
208
+ if (t.includes('Prompt-SkillArmory client panel')) return false
209
+ if (t === '- insert:') return false
210
+ if (t.includes('ui-switchblade')) return false
211
+ return true
194
212
  })
195
213
  if (meaningful.length === 0) {
196
214
  rmSync(patchPath, { force: true })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prompt-skill-armory",
3
- "version": "0.9.7",
3
+ "version": "0.9.9",
4
4
  "description": "One-command installer for Prompt-SkillArmory (prompts + skills manager) into the DeepSeek Harness (web + desktop)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -958,7 +958,7 @@ window.__ModuleLoader__.load({
958
958
  });
959
959
  }
960
960
  /** Bump with every release; keep in sync with package.json version + CHANGELOG. */
961
- const ARMORY_VERSION = "0.9.7";
961
+ const ARMORY_VERSION = "0.9.9";
962
962
  /** Compact duration: 45.2s / 2m42s / 1h05m. */
963
963
  function fmtDuration(ms) {
964
964
  const s = ms / 1e3;
@@ -1247,6 +1247,8 @@ window.__ModuleLoader__.load({
1247
1247
  const [promptName, setPromptName] = (0, react.useState)("");
1248
1248
  const [promptDesc, setPromptDesc] = (0, react.useState)("");
1249
1249
  const [promptContent, setPromptContent] = (0, react.useState)("");
1250
+ const [promptScopeType, setPromptScopeType] = (0, react.useState)("global");
1251
+ const [promptScopeKey, setPromptScopeKey] = (0, react.useState)("");
1250
1252
  const [skillName, setSkillName] = (0, react.useState)("");
1251
1253
  const [skillDesc, setSkillDesc] = (0, react.useState)("");
1252
1254
  const [skillContent, setSkillContent] = (0, react.useState)("");
@@ -1554,19 +1556,30 @@ window.__ModuleLoader__.load({
1554
1556
  const submitPrompt = () => {
1555
1557
  if (promptName.trim() === "" || promptContent.trim() === "") return;
1556
1558
  setBusy(true);
1559
+ const scope = promptScopeType === "global" ? void 0 : promptScopeType === "project" ? {
1560
+ type: "project",
1561
+ key: promptScopeKey.trim()
1562
+ } : {
1563
+ type: "session",
1564
+ id: promptScopeKey.trim()
1565
+ };
1557
1566
  (editingPromptId !== void 0 ? updatePrompt(editingPromptId, {
1558
1567
  name: promptName,
1559
1568
  description: promptDesc,
1560
- content: promptContent
1569
+ content: promptContent,
1570
+ scope
1561
1571
  }) : addPrompt({
1562
1572
  name: promptName,
1563
1573
  description: promptDesc,
1564
- content: promptContent
1574
+ content: promptContent,
1575
+ scope
1565
1576
  })).catch((error) => console.error("[switchblade] prompt save failed", error)).finally(() => {
1566
1577
  setBusy(false);
1567
1578
  setPromptName("");
1568
1579
  setPromptDesc("");
1569
1580
  setPromptContent("");
1581
+ setPromptScopeType("global");
1582
+ setPromptScopeKey("");
1570
1583
  setEditingPromptId(void 0);
1571
1584
  });
1572
1585
  };
@@ -1575,6 +1588,14 @@ window.__ModuleLoader__.load({
1575
1588
  setPromptName(row.name);
1576
1589
  setPromptDesc(row.desc);
1577
1590
  setPromptContent(row.content ?? "");
1591
+ const s = row.scope;
1592
+ if (s !== void 0 && (s.type === "project" || s.type === "session")) {
1593
+ setPromptScopeType(s.type);
1594
+ setPromptScopeKey(s.type === "project" ? s.key ?? "" : s.id ?? "");
1595
+ } else {
1596
+ setPromptScopeType("global");
1597
+ setPromptScopeKey("");
1598
+ }
1578
1599
  };
1579
1600
  const togglePrompt = (id, enabled) => {
1580
1601
  setPromptEnabled(id, enabled).catch((error) => console.error("[switchblade] toggle failed", error));
@@ -1635,7 +1656,8 @@ window.__ModuleLoader__.load({
1635
1656
  promptId: p.id,
1636
1657
  isDefault: p.isDefault,
1637
1658
  content: p.content,
1638
- promptEnabled: p.enabled
1659
+ promptEnabled: p.enabled,
1660
+ scope: p.scope
1639
1661
  }));
1640
1662
  const managedNames = new Set(state.installedSkills.map((s) => s.name));
1641
1663
  const managedRows = state.installedSkills.map((s) => ({
@@ -1827,6 +1849,47 @@ window.__ModuleLoader__.load({
1827
1849
  value: promptContent,
1828
1850
  onChange: (e) => setPromptContent(e.target.value)
1829
1851
  }),
1852
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1853
+ style: {
1854
+ display: "flex",
1855
+ gap: "6px",
1856
+ alignItems: "center",
1857
+ flexWrap: "wrap"
1858
+ },
1859
+ children: [
1860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1861
+ style: {
1862
+ ...CSS.hint,
1863
+ width: "76px",
1864
+ flex: "none"
1865
+ },
1866
+ children: "作用域"
1867
+ }),
1868
+ [
1869
+ "global",
1870
+ "project",
1871
+ "session"
1872
+ ].map((st) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1873
+ style: {
1874
+ ...CSS.actionBtn,
1875
+ ...promptScopeType === st ? CSS.tabActive : {}
1876
+ },
1877
+ onClick: () => setPromptScopeType(st),
1878
+ children: st === "global" ? "全局" : st === "project" ? "指定项目" : "指定会话"
1879
+ }, st)),
1880
+ promptScopeType !== "global" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1881
+ style: {
1882
+ ...CSS.input,
1883
+ maxWidth: "240px",
1884
+ flex: 1,
1885
+ minWidth: "120px"
1886
+ },
1887
+ placeholder: promptScopeType === "project" ? "项目目录名(如 qc)" : "会话 id(session-…)",
1888
+ value: promptScopeKey,
1889
+ onChange: (e) => setPromptScopeKey(e.target.value)
1890
+ })
1891
+ ]
1892
+ }),
1830
1893
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1831
1894
  style: CSS.actions,
1832
1895
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -1841,6 +1904,8 @@ window.__ModuleLoader__.load({
1841
1904
  setPromptName("");
1842
1905
  setPromptDesc("");
1843
1906
  setPromptContent("");
1907
+ setPromptScopeType("global");
1908
+ setPromptScopeKey("");
1844
1909
  },
1845
1910
  children: t("cancel")
1846
1911
  })]
@@ -1863,16 +1928,26 @@ window.__ModuleLoader__.load({
1863
1928
  children: [
1864
1929
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1865
1930
  style: CSS.cardTop,
1866
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1867
- style: CSS.name,
1868
- children: [row.isDefault ? "★ " : "", row.name]
1869
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1870
- style: {
1871
- ...CSS.badge,
1872
- ...row.state === "enabled" ? CSS.badgeEnabled : CSS.badgeDisabled
1873
- },
1874
- children: row.state === "enabled" ? t("enabled") : t("disabled")
1875
- })]
1931
+ children: [
1932
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1933
+ style: CSS.name,
1934
+ children: [row.isDefault ? "★ " : "", row.name]
1935
+ }),
1936
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1937
+ style: {
1938
+ ...CSS.badge,
1939
+ ...row.state === "enabled" ? CSS.badgeEnabled : CSS.badgeDisabled
1940
+ },
1941
+ children: row.state === "enabled" ? t("enabled") : t("disabled")
1942
+ }),
1943
+ row.scope !== void 0 && row.scope.type !== "global" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1944
+ style: {
1945
+ ...CSS.badge,
1946
+ ...CSS.badgeInstalled
1947
+ },
1948
+ children: row.scope.type === "project" ? `项目:${row.scope.key}` : `会话:${row.scope.id.slice(0, 12)}…`
1949
+ })
1950
+ ]
1876
1951
  }),
1877
1952
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1878
1953
  style: CSS.desc,
@@ -3244,7 +3319,8 @@ window.__ModuleLoader__.load({
3244
3319
  content: input.content,
3245
3320
  order: this.currentPrompts().length,
3246
3321
  enabled: true,
3247
- isDefault: this.currentPrompts().length === 0
3322
+ isDefault: this.currentPrompts().length === 0,
3323
+ ...input.scope === void 0 || input.scope.type === "global" ? {} : { scope: input.scope }
3248
3324
  }]
3249
3325
  }]
3250
3326
  });
@@ -3272,13 +3348,14 @@ window.__ModuleLoader__.load({
3272
3348
  const next = this.currentPrompts().filter((p) => p.id !== id);
3273
3349
  await this.writePrompts(next);
3274
3350
  }
3275
- /** Update a prompt's name/description/content. */
3351
+ /** Update a prompt's name/description/content/scope. */
3276
3352
  async updatePrompt(id, patch) {
3277
3353
  const next = this.currentPrompts().map((p) => p.id === id ? {
3278
3354
  ...p,
3279
3355
  name: patch.name?.trim() || p.name,
3280
3356
  description: patch.description ?? p.description,
3281
- content: patch.content ?? p.content
3357
+ content: patch.content ?? p.content,
3358
+ ...patch.scope === void 0 ? {} : patch.scope.type === "global" ? { scope: void 0 } : { scope: patch.scope }
3282
3359
  } : p);
3283
3360
  await this.writePrompts(next);
3284
3361
  }