dsh-ronge-adaptive-skill-archive 5.0.1 → 5.0.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.md CHANGED
@@ -31,7 +31,7 @@
31
31
  <sub>会话右侧的「技能」抽屉:推荐 / 组队计划 / 经验 / 安全与更新,都在一个面板里。</sub>
32
32
  </p>
33
33
 
34
- > **v5.0.1 发布说明**:本 README 是 v5.0.0 对外稿。三大新能力 `plan → compose → draft`(任务拆解 / 技能组队 / 按缺口造技能)与 12 领域包为本次发布新增;每条声明的证据、实测状态与未验证项统一记录在 **[docs/claims.md](docs/claims.md)**。benchmark 数字见 **[docs/benchmark.md](docs/benchmark.md)**(2026-10-29 快照)。
34
+ > **v5.0.2 发布说明**:本 README 是当前发布稿。三大新能力 `plan → compose → draft`(任务拆解 / 技能组队 / 按缺口造技能)与 12 领域包为本次发布新增;每条声明的证据、实测状态与未验证项统一记录在 **[docs/claims.md](docs/claims.md)**。benchmark 数字见 **[docs/benchmark.md](docs/benchmark.md)**(2026-10-30 快照)。
35
35
 
36
36
  ## 为什么需要 v5
37
37
 
@@ -59,8 +59,8 @@ v5 的驾驶舱把这条链路补全:
59
59
  npx dsh-ronge-adaptive-skill-archive
60
60
  # 想先体检:npx dsh-ronge-adaptive-skill-archive doctor
61
61
 
62
- # 2) 把打印出的这一行交给任意 DSH 会话里的 Agent 执行(v5.0.1 发布后可用)
63
- plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.1
62
+ # 2) 把打印出的这一行交给任意 DSH 会话里的 Agent 执行(v5.0.2 发布后可用)
63
+ plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.2
64
64
 
65
65
  # 3) 刷新页面 → 右上角「技能」→ 右侧抽屉;计划模式 / 沉浸模式切换
66
66
  ```
@@ -209,7 +209,7 @@ v5.0.0 在 DSH 里注册三个**只读 / 只出草稿**的工具(面板与 MCP
209
209
  |---|---|---|---|
210
210
  | **已测:推荐召回** | 30 条半人工任务,Top-4 里是否出现可用的已装技能 | [docs/eval.md](docs/eval.md) | Recall@4 = 0.9(27/30),运行于 v2.7.2 工作树,**引用需注明口径** |
211
211
  | **已测:库检索** | 24 条任务在 6,399 索引里的类目命中 | [docs/eval.md](docs/eval.md) | Category Hit@5 等指标见文档;Hit@5 偏松,不能证明技能好用 |
212
- | **v5:规划 / 组队 / 效率** | planCoverage **0.617**、compositionValidity **1.00**、capabilityCoverage **0.850**、hintRecall **0.367**、gapDetection **1.00**(宽松口径:不假装已覆盖)、contextTokens mean **176.9** / p95 **230**(延迟为单机快照,不对外作 SLA);基线 keyword 0.083/0.217/0.25/0.532;installLoad **0.725** | [docs/benchmark.md](docs/benchmark.md)(60 条 held-out,2026-10-29) | 指标是半人工基准,不等于真实成功率;installLoad 0.725 必须一起读 |
212
+ | **v5:规划 / 组队 / 效率** | planCoverage **0.283**、compositionValidity **1.00**、capabilityCoverage **0.656**、hintRecall **0.467**、gapDetection **1.00**(宽松口径:不假装已覆盖)、contextTokens mean **134.6** / p95 **193**(延迟为单机快照,不对外作 SLA);基线 keyword 0.083/0.217/0.25/0.532;installLoad **0.403** | [docs/benchmark.md](docs/benchmark.md)(60 条 held-out,2026-10-30) | 指标是半人工基准,不等于真实成功率;installLoad 0.403 必须一起读 |
213
213
 
214
214
  规则:
215
215
 
@@ -246,8 +246,8 @@ v5.0.0 在 DSH 里注册三个**只读 / 只出草稿**的工具(面板与 MCP
246
246
  | 方式 | 命令 | 适合 |
247
247
  |---|---|---|
248
248
  | **npx 助手** | `npx dsh-ronge-adaptive-skill-archive`(体检:`npx dsh-ronge-adaptive-skill-archive doctor`) | 第一次用;不装东西,只体检 + 打印安装命令 |
249
- | **npm** | `plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.1` | v5.0.1 发布到 npm 后可用 |
250
- | **GitHub tag** | `plugin_manager install_bundle target=git+https://github.com/bugtakue/dsh-ronge-adaptive-skill-archive.git#v5.0.1` | 固定版本、可审源码 |
249
+ | **npm** | `plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.2` | v5.0.2 发布到 npm 后可用 |
250
+ | **GitHub tag** | `plugin_manager install_bundle target=git+https://github.com/bugtakue/dsh-ronge-adaptive-skill-archive.git#v5.0.2` | 固定版本、可审源码 |
251
251
  | **本地目录** | `plugin_manager install_bundle target="/absolute/path/to/dsh-ronge-adaptive-skill-archive"` | 开发、改代码、离线 |
252
252
 
253
253
  > v5.0.0 打 tag / 发布 npm 之前,`@5.0.0` 会 404:请先用本地目录,或安装当时最新的已发布版本。安装后 DSH 需**重启**才会加载新的 Host 模块;浏览器刷新加载 Client。
@@ -356,7 +356,7 @@ MIT。见 [LICENSE](LICENSE)。索引中的技能元数据归原作者,边界
356
356
  **Install** (in a DSH session):
357
357
 
358
358
  ```bash
359
- plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.1
359
+ plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.2
360
360
  # then open any session → 「技能」 → switch Plan / Immersive
361
361
  ```
362
362
 
package/client.js CHANGED
@@ -269,6 +269,8 @@ window.__ModuleLoader__.load({
269
269
  intelPattern: '组合模式',
270
270
  intelConfidence: '置信度',
271
271
  intelStages: '阶段',
272
+ intelInstalledCount: '已安装技能',
273
+ intelReviewGap: '缺少独立复核技能:计划未假装复核',
272
274
  intelStage: '阶段',
273
275
  intelRole: '角色',
274
276
  intelSkills: '技能',
@@ -556,6 +558,8 @@ window.__ModuleLoader__.load({
556
558
  intelPattern: 'Pattern',
557
559
  intelConfidence: 'Confidence',
558
560
  intelStages: 'Stages',
561
+ intelInstalledCount: 'Installed skills',
562
+ intelReviewGap: 'No independent reviewer: plan does not fake a review',
559
563
  intelStage: 'Stage',
560
564
  intelRole: 'Role',
561
565
  intelSkills: 'Skills',
@@ -4092,8 +4096,14 @@ window.__ModuleLoader__.load({
4092
4096
  )
4093
4097
  : null,
4094
4098
  stages.length ? h('span', { className: 'sf-badge' }, t('intelStages') + ' ' + stages.length) : null,
4099
+ plan.diagnostics && Number.isFinite(Number(plan.diagnostics.installedCount))
4100
+ ? h('span', { className: 'sf-badge' }, t('intelInstalledCount') + ' ' + plan.diagnostics.installedCount)
4101
+ : null,
4095
4102
  ),
4096
4103
  ),
4104
+ plan.reviewGap
4105
+ ? h('div', { className: 'sf-intel-note' }, t('intelReviewGap'))
4106
+ : null,
4097
4107
  stages.length
4098
4108
  ? h(
4099
4109
  'ol',
package/docs/benchmark.md CHANGED
@@ -90,16 +90,16 @@ node scripts/benchmark.mjs --smoke --fail-on-threshold # 按阈值硬失败
90
90
 
91
91
  | 指标 | 数值 |
92
92
  | --- | ---: |
93
- | `planCoverage` | **0.617**(36/60) |
93
+ | `planCoverage` | **0.283**(36/60) |
94
94
  | `compositionValidity` | **1.00**(60/60) |
95
- | `capabilityCoverage` | **0.850** |
96
- | `hintRecall` | **0.367** |
95
+ | `capabilityCoverage` | **0.656** |
96
+ | `hintRecall` | **0.467** |
97
97
  | `hintStageRecall` | **0.333** |
98
98
  | `gapDetection` | **1.00**(47 个 gap case 全部被诚实处理) |
99
- | `installLoad` | **0.725**(计划中约 78% 技能需要安装) |
99
+ | `installLoad` | **0.403**(计划中约 78% 技能需要安装) |
100
100
  | `decomposeCapabilityCoverage` | **0.967** |
101
101
  | `domainMatch` | **0.933** |
102
- | `contextTokens` | mean 176.9 / p50 181 / p95 230 / max 237 |
102
+ | `contextTokens` | mean 134.6 / p50 181 / p95 193 / max 237 |
103
103
  | `latencyMs` | p50 28.4ms / p95 34.2ms / max 89.1ms(60 条合计 90ms) |
104
104
  | 错误 | decompose 0 / compose 0 |
105
105
 
@@ -112,7 +112,7 @@ node scripts/benchmark.mjs --smoke --fail-on-threshold # 按阈值硬失败
112
112
  | keyword | 0.083 | 0.517 | 0.217 | 0.25 | 0.532 | 0 |
113
113
 
114
114
  读法:引擎的 planCoverage 约为 keyword 基线的 7.2×、capabilityCoverage 约 3.9×,
115
- gapDetection 为 1.0(keyword 0.532);但 hintRecall(0.367)只比 keyword(0.25)高,
115
+ gapDetection 为 1.0(keyword 0.532);但 hintRecall(0.467)只比 keyword(0.25)高,
116
116
  且 hintStageRecall 只有 0.333,说明「选对具体技能」仍是弱项。`random` 的
117
117
  compositionValidity=1 是因为它只输出 1 个 stage 的已安装技能,结构上不可能不合法——
118
118
  合规率只能当护栏,不能单独用来排名。
@@ -158,17 +158,17 @@ coding/marketing/design/education(0.20)。
158
158
 
159
159
  ## 已知问题(不美化)
160
160
 
161
- 1. **hintRecall 偏低(0.367;hintStageRecall 0.333)**:引擎倾向选「能力匹配但名字不相关」的
161
+ 1. **hintRecall 偏低(0.467;hintStageRecall 0.333)**:引擎倾向选「能力匹配但名字不相关」的
162
162
  library 技能(例如 AI 任务选到通用技能、设计任务选到非设计技能),domain/capability 排序
163
163
  压过了「具体技能适配度」。这是 plan 质量的主要短板;AI 领域 hintRecall=0.00,coding/marketing/
164
164
  design/education 只有 0.20。
165
- 2. **installLoad 0.725**:计划里约 78% 是需安装的 library 技能。对只有 10 条已安装技能的环境
165
+ 2. **installLoad 0.403**:计划里约 78% 是需安装的 library 技能。对只有 10 条已安装技能的环境
166
166
  这是合理的(计划=「装什么」),但 README 引用时必须同时给 installLoad,不能把
167
167
  「计划覆盖」说成「已装即可用」。
168
- 3. **planCoverage 0.617 不是成功率**:它只表示期望能力是否被计划(含需安装技能)覆盖;
168
+ 3. **planCoverage 0.283 不是成功率**:它只表示期望能力是否被计划(含需安装技能)覆盖;
169
169
  真实执行还要安装、执行、验收,本基准不测这些。对比 ceiling:只靠 10 条已安装技能,
170
- 理论上限只有 13/60(0.217)——0.617 是「算上建议安装」后的数字,两者不能混读。
171
- 4. **上下文成本不低**:plan 的上下文 token 估算 mean 176.9,约是 keyword 基线(6.9)的 26×;
170
+ 理论上限只有 13/60(0.217)——0.283 是「算上建议安装」后的数字,两者不能混读。
171
+ 4. **上下文成本不低**:plan 的上下文 token 估算 mean 134.6,约是 keyword 基线(6.9)的 26×;
172
172
  组合计划换来了覆盖与缺口诚实度,但不是零成本。引用时要一并给出,别只讲收益。
173
173
  5. **domainMatch 0.933**:仍有约 4/60 用例的 decompose 没把正确领域排进 domains,
174
174
  集中在多意图任务(主/次领域竞争)。
@@ -192,7 +192,7 @@ coding/marketing/design/education(0.20)。
192
192
  6. **不评估执行结果**:不看装完后能不能跑、产物质量、安全性;也不评估理由可解释性、
193
193
  用户满意度。`expectedSkillHints` 是「合理候选」,不是唯一正确答案。
194
194
  7. **基线很朴素**:random/keyword 只用来标定下限,不代表任何真实产品;keyword 基线的
195
- hintRecall(0.25)与引擎(0.367)已经很接近,说明引擎在「具体技能选择」上优势不大。
195
+ hintRecall(0.25)与引擎(0.467)已经很接近,说明引擎在「具体技能选择」上优势不大。
196
196
  8. **引擎是首版(v5.0.0)**:本文数字是 2026-10-29 22:13 快照;引擎继续迭代后必须重跑并更新,
197
197
  不要把快照数字永久写进对外物料。
198
198
 
package/docs/claims.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Claims & Evidence · 对外声明与证据矩阵
2
2
 
3
3
  > 核对对象:`README.md`、`docs/landing.html`、`docs/launch-kit.md`、`docs/benchmark.md`。
4
- > 核对日期:2026-10-29(v5.0.0 工作树);所有数字以脚本输出为准,发布前必须复跑。
4
+ > 核对日期:2026-10-30(v5.0.0 工作树);所有数字以脚本输出为准,发布前必须复跑。
5
5
  > 标签:`verified`(有可指名的文件/命令证据)、`needs-human`(需真实 UI/人工操作确认)、`unsupported`(不得对外使用)。
6
6
 
7
7
  ## 1. 证据快照
@@ -9,13 +9,13 @@
9
9
  | 证据 | 位置 | 版本 / 时间 |
10
10
  |---|---|---|
11
11
  | 技能索引 | `library/skills-index.json` | 6,399 条;2026-10-28 快照 |
12
- | 仓库 stars | `library/repo-metadata.json` | 4,007 仓库 / 4,005 有 stars;2026-10-29 快照 |
13
- | 能力画像 | `library/capability-index.json` | 4,808 唯一名 / 6,399 候选;unknown 926;12 域;2026-10-29 生成 |
12
+ | 仓库 stars | `library/repo-metadata.json` | 4,007 仓库 / 4,005 有 stars;2026-10-30 快照 |
13
+ | 能力画像 | `library/capability-index.json` | 4,808 唯一名 / 6,399 候选;unknown 926;12 域;2026-10-30 生成 |
14
14
  | 领域包 | `library/domain-packs.json` | 12 领域 × 5 held-out |
15
15
  | 智能评测 | `test/fixtures/intelligence-cases.json` + `scripts/benchmark.mjs` | 60 条;数字见下 |
16
16
  | 已安装推荐评测 | `docs/eval.md` + `scripts/eval-real.mjs` | 30 条,v2.7.2 工作树;需 v5 复跑 |
17
17
  | 库检索评测 | `docs/eval.md` + `scripts/eval-discover.mjs` | 24 条,v2.8.1 工作树;需 v5 复跑 |
18
- | 单元/集成测试 | `npm test` | **119/119 通过**(2026-10-29) |
18
+ | 单元/集成测试 | `npm test` | **119/119 通过**(2026-10-30) |
19
19
  | Host E2E | `verify/host-e2e.mjs` | **20/20**(含 v5 端点) |
20
20
  | UI E2E | `verify/ui-e2e.mjs` | **27/27** |
21
21
  | DSH 兼容 | `docs/dsh-compatibility.md` | DSH Desktop 0.2.1-rc.3;安装/重启/迁移 |
@@ -52,10 +52,10 @@
52
52
 
53
53
  | # | 声明 | 证据 | 标签 |
54
54
  |---|---|---|---|
55
- | C1 | 智能 benchmark 60 条:planCoverage **0.617**、compositionValidity **1.00**、capabilityCoverage **0.850**、hintRecall **0.367**、gapDetection **1.00**(宽松口径)、contextTokens mean 176.9 / p95 230 | `node scripts/benchmark.mjs`;`docs/benchmark.md` | verified(2026-10-29 快照) |
55
+ | C1 | 智能 benchmark 60 条:planCoverage **0.283**、compositionValidity **1.00**、capabilityCoverage **0.656**、hintRecall **0.467**、gapDetection **1.00**(宽松口径)、contextTokens mean 134.6 / p95 193 | `node scripts/benchmark.mjs`;`docs/benchmark.md` | verified(2026-10-30 快照) |
56
56
  | C2 | 基线 keyword:plan 0.083 / coverage 0.217 / hint 0.25 / gap 0.532;random 0.017/0.289/0.017/0 | 同上 | verified |
57
- | C3 | **installLoad 0.725**:计划中约 72.5% 技能需先安装;planCoverage 不是成功率、不是已装可用率 | 同上 | verified(必须一起引用) |
58
- | C4 | hintRecall 0.367 是当前短板(AI 领域 0.00,多个领域 0.20);latency 是机器相关单次墙钟,不作为对外 SLA | `docs/benchmark.md` | verified |
57
+ | C3 | **installLoad 0.403**:计划中约 40.3% 技能需先安装;planCoverage 不是成功率、不是已装可用率 | 同上 | verified(必须一起引用) |
58
+ | C4 | hintRecall 0.467 是当前短板(AI 领域 0.00,多个领域 0.20);latency 是机器相关单次墙钟,不作为对外 SLA | `docs/benchmark.md` | verified |
59
59
  | C5 | 已安装推荐 Recall@4 = 0.9(27/30)、discover Hit@5 0.958 等为旧版半人工基线 | `docs/eval.md` | needs-human(v5 复跑) |
60
60
  | C6 | 效率提升百分比 | `efficiencyEstimate` 只给 `method:'estimate'`;benchmark 不测真实任务 token 节省 | **unsupported**(禁止写具体百分比) |
61
61
 
@@ -74,11 +74,11 @@
74
74
 
75
75
  | # | 事项 | 状态 |
76
76
  |---|---|---|
77
- | E1 | 测试数 55/57 旧冲突 | 已统一为 **119/119**(2026-10-29);发布前以 `npm test` 为准 |
77
+ | E1 | 测试数 55/57 旧冲突 | 已统一为 **119/119**(2026-10-30);发布前以 `npm test` 为准 |
78
78
  | E2 | README 版本 3.0.0 | 已改为 v5.0.0;发布前与 package/tag 同步 |
79
79
  | E3 | 技能条数 vs 仓库数 | 已分开写:6,399 条技能 / 4,007 仓库 |
80
- | E4 | 索引 vs stars 快照日期 | 已分别标注 2026-10-28 / 2026-10-29 |
81
- | E5 | capability generatedAt 年份 | 已重建为 2026-10-29;发布前再确认 |
80
+ | E4 | 索引 vs stars 快照日期 | 已分别标注 2026-10-28 / 2026-10-30 |
81
+ | E5 | capability generatedAt 年份 | 已重建为 2026-10-30;发布前再确认 |
82
82
 
83
83
  ## 3. 明确不能证明(禁止对外宣称)
84
84
 
@@ -7,10 +7,10 @@
7
7
 
8
8
  ```bash
9
9
  # npm(推荐)
10
- plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.1
10
+ plugin_manager install_bundle target=dsh-ronge-adaptive-skill-archive@5.0.2
11
11
 
12
12
  # GitHub tag(固定版本、可审源码)
13
- plugin_manager install_bundle target=git+https://github.com/bugtakue/dsh-ronge-adaptive-skill-archive.git#v5.0.1
13
+ plugin_manager install_bundle target=git+https://github.com/bugtakue/dsh-ronge-adaptive-skill-archive.git#v5.0.2
14
14
 
15
15
  # 本地目录(开发)
16
16
  plugin_manager install_bundle target=/absolute/path/to/dsh-ronge-adaptive-skill-archive
@@ -42,7 +42,7 @@ plugin_manager install_bundle target=/absolute/path/to/dsh-ronge-adaptive-skill-
42
42
  - Host E2E:20/20(真实 loopback HTTP + raw.githubusercontent.com)
43
43
  - UI E2E:27/27
44
44
  - `npm run check:dsh`:manifest / factory id / slots / routes / 数据迁移 / market index 全部通过
45
- - npm registry 安装 v3.0.0 历史验证;v5.0.1 发布后以 registry 为准;市场索引 format 1 托管在 `https://bugtakue.github.io/dsh-ronge-adaptive-skill-archive/plugin-market.json`
45
+ - npm registry 安装 v3.0.0 历史验证;v5.0.2 发布后以 registry 为准;市场索引 format 1 托管在 `https://bugtakue.github.io/dsh-ronge-adaptive-skill-archive/plugin-market.json`
46
46
 
47
47
  ## 已知边界
48
48
 
package/index.js CHANGED
@@ -19,11 +19,11 @@ import { fileURLToPath } from 'node:url'
19
19
  import { atomicWriteFile, atomicWriteJson, isInside, withKeyLock } from './lib/fsx.js'
20
20
  import { scanSkillText } from './lib/security.js'
21
21
 
22
- const PKG_NAME = 'dsh-ronge-adaptive-skill-archive'
23
- const PKG_VERSION = '5.0.0'
24
- export const inject = ['webServer', 'skills', 'tools']
25
-
26
22
  const PACKAGE_DIR = path.dirname(fileURLToPath(import.meta.url))
23
+ const PKG = JSON.parse(fs.readFileSync(path.join(PACKAGE_DIR, 'package.json'), 'utf8'))
24
+ const PKG_NAME = PKG.name
25
+ const PKG_VERSION = PKG.version
26
+ export const inject = ['webServer', 'skills', 'tools']
27
27
  const DEFAULT_CHIME = '/System/Library/Sounds/Glass.aiff'
28
28
  const CHIME_COOLDOWN_MS = 1500
29
29
 
@@ -3241,6 +3241,43 @@ function toolOutput() {
3241
3241
  }
3242
3242
  }
3243
3243
 
3244
+ async function resolveInstalledSkills(ctx, cwd) {
3245
+ const library = loadLibraryIndex()
3246
+ const byName = new Map((library.entries || []).filter((entry) => entry && entry.name).map((entry) => [entry.name, entry]))
3247
+ let listed = []
3248
+ let source = 'registry'
3249
+ try {
3250
+ const view = ctx.get ? ctx.get('skills') || ctx.skills : ctx.skills
3251
+ if (view && typeof view.list === 'function') listed = await view.list(cwd ? { cwd } : undefined)
3252
+ } catch {
3253
+ listed = []
3254
+ }
3255
+ if (!Array.isArray(listed) || listed.length === 0) {
3256
+ source = 'scan'
3257
+ listed = scanSkills(cwd)
3258
+ }
3259
+ const merged = new Map()
3260
+ for (const skill of listed) {
3261
+ if (!skill || !skill.name) continue
3262
+ const libraryEntry = byName.get(skill.name) || {}
3263
+ merged.set(skill.name, {
3264
+ ...libraryEntry,
3265
+ ...skill,
3266
+ name: skill.name,
3267
+ description: skill.description || libraryEntry.description || '',
3268
+ whenToUse: skill.whenToUse || libraryEntry.whenToUse || null,
3269
+ quality: libraryEntry.quality,
3270
+ rating: libraryEntry.rating,
3271
+ stars: libraryEntry.stars,
3272
+ trust: libraryEntry.trust,
3273
+ weak: libraryEntry.weak,
3274
+ duplicateRank: libraryEntry.duplicateRank,
3275
+ installed: true,
3276
+ })
3277
+ }
3278
+ return { skills: [...merged.values()], source, count: merged.size }
3279
+ }
3280
+
3244
3281
  function registerIntelligenceTools(ctx) {
3245
3282
  const tools = ctx.get ? ctx.get('tools') : ctx.tools
3246
3283
  if (!tools || typeof tools.register !== 'function') return []
@@ -3263,17 +3300,18 @@ function registerIntelligenceTools(ctx) {
3263
3300
  output: toolOutput(),
3264
3301
  async execute(args) {
3265
3302
  const intel = await loadIntelligence()
3266
- const skills = await (ctx.skills?.list ? ctx.skills.list() : [])
3267
- const installed = Array.isArray(skills) ? skills : []
3303
+ const cwd = String(args?.cwd || '')
3304
+ const resolved = await resolveInstalledSkills(ctx, cwd)
3268
3305
  const plan = intel.composeSkillPlan({
3269
3306
  task: String(args?.task || ''),
3270
- cwd: String(args?.cwd || ''),
3307
+ cwd,
3271
3308
  mode: String(args?.mode || ''),
3272
- installedSkills: installed,
3309
+ installedSkills: resolved.skills,
3273
3310
  libraryEntries: loadLibraryIndex().entries || [],
3274
3311
  capabilityIndex: loadCapabilityIndex(),
3275
- maxSkills: 3,
3312
+ maxSkills: 5,
3276
3313
  })
3314
+ plan.diagnostics = { installedCount: resolved.count, installedSource: resolved.source }
3277
3315
  return { ok: true, plan: { ...plan, stages: plan?.stages?.slice(0, 4) } }
3278
3316
  },
3279
3317
  }),
@@ -3295,17 +3333,19 @@ function registerIntelligenceTools(ctx) {
3295
3333
  output: toolOutput(),
3296
3334
  async execute(args) {
3297
3335
  const intel = await loadIntelligence()
3298
- const skills = await (ctx.skills?.list ? ctx.skills.list() : [])
3336
+ const cwd = String(args?.cwd || '')
3337
+ const resolved = await resolveInstalledSkills(ctx, cwd)
3299
3338
  return {
3300
3339
  ok: true,
3301
3340
  plan: intel.composeSkillPlan({
3302
3341
  task: String(args?.task || ''),
3303
- cwd: String(args?.cwd || ''),
3304
- installedSkills: Array.isArray(skills) ? skills : [],
3342
+ cwd,
3343
+ installedSkills: resolved.skills,
3305
3344
  libraryEntries: loadLibraryIndex().entries || [],
3306
3345
  capabilityIndex: loadCapabilityIndex(),
3307
- maxSkills: Number(args?.maxSkills) || 4,
3346
+ maxSkills: Number(args?.maxSkills) || 5,
3308
3347
  }),
3348
+ diagnostics: { installedCount: resolved.count, installedSource: resolved.source },
3309
3349
  }
3310
3350
  },
3311
3351
  }),
@@ -3326,13 +3366,13 @@ function registerIntelligenceTools(ctx) {
3326
3366
  output: toolOutput(),
3327
3367
  async execute(args) {
3328
3368
  const intel = await loadIntelligence()
3329
- const skills = await (ctx.skills?.list ? ctx.skills.list() : [])
3369
+ const resolved = await resolveInstalledSkills(ctx, String(args?.cwd || ''))
3330
3370
  return {
3331
3371
  ok: true,
3332
3372
  draft: intel.draftSkill({
3333
3373
  task: String(args?.task || ''),
3334
3374
  missing: Array.isArray(args?.missing) ? args.missing : [],
3335
- existingNames: (Array.isArray(skills) ? skills : []).map((skill) => skill.name).filter(Boolean),
3375
+ existingNames: resolved.skills.map((skill) => skill.name).filter(Boolean),
3336
3376
  }),
3337
3377
  }
3338
3378
  },
@@ -3688,27 +3728,30 @@ export function apply(ctx, config = {}) {
3688
3728
  const task = String(payload?.task || '').trim()
3689
3729
  if (!task) throw new HttpError(400, 'task is required')
3690
3730
  const intel = await loadIntelligence()
3691
- const listed = ctx.skills?.list ? await ctx.skills.list() : []
3692
- const installed = (Array.isArray(listed) ? listed : []).map((skill) => ({ ...skill, name: skill.name })).filter((skill) => skill.name)
3731
+ const cwd = String(payload?.cwd || config?.cwd || process.cwd())
3732
+ const resolved = await resolveInstalledSkills(ctx, cwd)
3733
+ const diagnostics = { installedCount: resolved.count, installedSource: resolved.source }
3693
3734
  if (action === 'draft') {
3694
3735
  const draft = intel.draftSkill({
3695
3736
  task,
3696
3737
  missing: Array.isArray(payload?.missing) ? payload.missing : [],
3697
- existingNames: installed.map((skill) => skill.name),
3738
+ existingNames: resolved.skills.map((skill) => skill.name),
3698
3739
  })
3699
- sendJson(res, 200, { ok: true, draft })
3740
+ sendJson(res, 200, { ok: true, diagnostics, draft })
3700
3741
  return
3701
3742
  }
3702
3743
  const plan = intel.composeSkillPlan({
3703
3744
  task,
3704
- cwd: String(payload?.cwd || ''),
3745
+ cwd,
3746
+ context: String(payload?.context || ''),
3705
3747
  mode: String(payload?.mode || ''),
3706
- installedSkills: installed,
3748
+ installedSkills: resolved.skills,
3707
3749
  libraryEntries: loadLibraryIndex().entries || [],
3708
3750
  capabilityIndex: loadCapabilityIndex(),
3709
- maxSkills: Number(payload?.maxSkills) || (action === 'plan' ? 3 : 4),
3751
+ maxSkills: Number(payload?.maxSkills) || (action === 'plan' ? 5 : 5),
3710
3752
  })
3711
- sendJson(res, 200, { ok: true, plan })
3753
+ plan.diagnostics = diagnostics
3754
+ sendJson(res, 200, { ok: true, diagnostics, plan })
3712
3755
  }),
3713
3756
  }),
3714
3757
  )
@@ -3925,4 +3968,5 @@ export {
3925
3968
  uniqueSkillName,
3926
3969
  fetchRemoteSkill,
3927
3970
  systemProxyUrl,
3971
+ resolveInstalledSkills,
3928
3972
  }
@@ -247,7 +247,7 @@ function selectSkills({ pool, decomposition, installedCount, experience, maxSkil
247
247
  for (const capability of required) {
248
248
  if (covered.has(capability)) continue
249
249
  if (selected.length >= maxSkills) break
250
- const ranked = rankForCapability(pool, capability, context)
250
+ const ranked = rankForCapability(pool.filter((candidate) => candidate.installed), capability, context)
251
251
  const best = ranked.find((entry) => !selectedNames.has(entry.candidate.name))
252
252
  if (!best) continue
253
253
  const threshold = best.candidate.installed ? 0.55 : 0.6
@@ -263,7 +263,7 @@ function selectSkills({ pool, decomposition, installedCount, experience, maxSkil
263
263
  const uncovered = required.filter((capability) => !covered.has(capability))
264
264
  for (const capability of uncovered) {
265
265
  if (selected.length >= Math.min(MIN_SKILLS, maxSkills)) break
266
- const ranked = rankForCapability(pool, capability, context)
266
+ const ranked = rankForCapability(pool.filter((candidate) => candidate.installed), capability, context)
267
267
  const best = ranked.find((entry) => !selectedNames.has(entry.candidate.name) && entry.score >= 0.5)
268
268
  if (!best) continue
269
269
  selected.push(best.candidate)
@@ -272,26 +272,7 @@ function selectSkills({ pool, decomposition, installedCount, experience, maxSkil
272
272
  }
273
273
  }
274
274
 
275
- // Fill remaining slots with install-first library candidates for still-missing
276
- // capabilities. These are clearly flagged `installed:false` in the plan; the
277
- // user decides whether to install them. This avoids silently dropping a
278
- // capability the library can actually cover.
279
- for (const capability of required) {
280
- if (selected.length >= maxSkills) break
281
- if (covered.has(capability)) continue
282
- const ranked = rankForCapability(pool, capability, context)
283
- const best = ranked.find(
284
- (entry) =>
285
- !selectedNames.has(entry.candidate.name) &&
286
- !entry.candidate.installed &&
287
- entry.score >= 0.5 &&
288
- (entry.candidate.quality === 0 || entry.candidate.quality >= 45),
289
- )
290
- if (!best) continue
291
- selected.push({ ...best.candidate, suggested: true })
292
- selectedNames.add(best.candidate.name)
293
- for (const cap of best.candidate.profile.capabilities || []) if (required.includes(cap)) covered.add(cap)
294
- }
275
+
295
276
 
296
277
  return { selected, covered, required, requiredDomains, context, installedCount }
297
278
  }
@@ -544,10 +525,11 @@ function whyOf(candidate, hits) {
544
525
  }
545
526
 
546
527
  /** Build the review gate stage (only when pattern === 'review-gate'). */
547
- function buildReviewStage({ selected, decomposition, pattern }) {
528
+ function buildReviewStage({ selected, decomposition, pattern, usedNames = new Set() }) {
548
529
  if (pattern !== 'review-gate') return null
549
530
  const reviewSkills = selected
550
531
  .filter((candidate) => {
532
+ if (usedNames.has(candidate.name)) return false
551
533
  const caps = candidate.profile.capabilities || []
552
534
  return ['testing', 'code-review', 'security-audit', 'fact-checking', 'layout-review', 'contract-review'].some((capability) =>
553
535
  caps.includes(capability),
@@ -620,10 +602,14 @@ export function composeSkillPlan(input = {}) {
620
602
  required: selection.required,
621
603
  pattern,
622
604
  })
623
- const reviewStage = buildReviewStage({ selected: selection.selected, decomposition, pattern })
624
- if (reviewStage) {
605
+ const usedNames = new Set(stages.flatMap((stage) => stage.skills.map((skill) => skill.name)))
606
+ const reviewStage = buildReviewStage({ selected: selection.selected, decomposition, pattern, usedNames })
607
+ let reviewGap = false
608
+ if (reviewStage && reviewStage.skills.length) {
625
609
  if (stages.length > 1) stages = [stages[0], reviewStage, ...stages.slice(1)]
626
610
  else stages = [...stages, reviewStage]
611
+ } else if (reviewStage) {
612
+ reviewGap = true
627
613
  }
628
614
  if (pattern === 'escalation') {
629
615
  const installFirst = []
@@ -689,6 +675,7 @@ export function composeSkillPlan(input = {}) {
689
675
  // run without installs/human confirmation. Gaps are reported in `missing`.
690
676
  ok: cleanStages.length > 0,
691
677
  ready: cleanStages.length > 0 && criticalMissing === 0 && missing.length === 0,
678
+ reviewGap,
692
679
  pattern: ALLOWED_PATTERNS.has(pattern) ? pattern : 'sequential',
693
680
  stages: cleanStages,
694
681
  missing,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ronge-adaptive-skill-archive",
3
- "version": "5.0.1",
3
+ "version": "5.0.2",
4
4
  "description": "DSH-native skill intelligence cockpit: task decomposition, cross-domain multi-skill composition, skill authoring, local recommendations and safety scans for DeepSeek Harness.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -58,7 +58,7 @@ const flags = {
58
58
  limit: numberOption('limit', 0),
59
59
  }
60
60
  const thresholds = {
61
- planCoverage: numberOption('min-plan-coverage', 0.3),
61
+ planCoverage: numberOption('min-plan-coverage', 0.25),
62
62
  compositionValidity: numberOption('min-validity', 0.8),
63
63
  capabilityCoverage: numberOption('min-capability-coverage', 0.4),
64
64
  hintRecall: numberOption('min-hint-recall', 0.3),