dsh-plugin-compat-check 0.1.0 → 0.1.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.
@@ -1,25 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * `prepack` 钩子:把仓库内核(`../lib` + `../rules`)暂存进 `plugin/kernel/`,
4
- * 好让 `npm pack` / `npm publish` 产出的包**自足**。
5
- *
6
- * ## 为什么需要暂存,而不是直接把 kernel/ 提交进仓库
7
- *
8
- * 内核只有一份真源(仓库根的 `lib/` 与 `rules/`)。如果在 `plugin/kernel/` 里
9
- * **再放一份**并提交,那就是两份实现 —— 而"两份实现迟早漂移"正是这个项目
10
- * 从第一天起就在避免的事(见 Plan.md §2 第 6 题:否决双实现)。
11
- *
12
- * 所以内核**从不出现在 git 里**,只在 `npm pack` 的那几秒存在。
13
- * `plugin/kernel/` 已进 `.gitignore`,`postpack` 负责清掉。
14
- *
15
- * ## 为什么 `lib/` 只取三个文件
16
- *
17
- * 插件半边 import `extract` / `verdict` / `baseline` / `version`(见 index.js 的 `resolveKernelDir` 调用点)。
18
- * `version.mjs` 必须带上 —— 少了它,装好的包**报不出自己的版本**,
19
- * 而"结论可追溯"恰恰依赖它(Plan.md §2 第 9 题的「单版本」)。
20
- * `report.mjs` 是 CLI 的渲染层,插件半边不用它 —— 少打一个文件就少一处漂移面。
21
- * 但它**不影响正确性**(多打一个也无害),所以这里是"按实际 import 收窄",
22
- * 而不是"少打能省空间"。
3
+ * Stage the single-source lib/rules tree into the npm plugin. Include transitive
4
+ * AST, observation, state and worker modules; only opt-in process/browser runners
5
+ * are excluded. Root LICENSE is copied at pack time. Kernel copies are ignored
6
+ * and postpack removes them; independent artifact execution tests completeness.
23
7
  */
24
8
 
25
9
  import fs from 'node:fs';
@@ -32,13 +16,15 @@ const REPO = path.resolve(PLUGIN, '..');
32
16
  const DEST = path.join(PLUGIN, 'kernel');
33
17
 
34
18
  // 插件半边真正 import 的内核模块(改 index.js 的 import 时要同步这里)
35
- const KERNEL_LIBS = ['extract.mjs', 'verdict.mjs', 'baseline.mjs', 'version.mjs'];
19
+ const KERNEL_LIBS = fs.readdirSync(path.join(REPO,'lib')).filter(f=>f.endsWith('.mjs')&&!['probe-runner.mjs','debugger.mjs'].includes(f)).sort();
36
20
 
37
21
  function bail(message) {
38
22
  process.stderr.write(`[stage-kernel] ${message}\n`);
39
23
  process.exit(1);
40
24
  }
41
25
 
26
+ if(!KERNEL_LIBS.includes('version.mjs'))bail('缺少必需的 lib/version.mjs');
27
+
42
28
  if (!fs.existsSync(path.join(REPO, 'lib', 'extract.mjs'))) {
43
29
  bail(`在 ${REPO}/lib 下找不到内核 —— prepack 必须在仓库检出里跑`);
44
30
  }
@@ -47,6 +33,8 @@ fs.rmSync(DEST, { recursive: true, force: true });
47
33
  fs.mkdirSync(path.join(DEST, 'lib'), { recursive: true });
48
34
  fs.mkdirSync(path.join(DEST, 'rules'), { recursive: true });
49
35
 
36
+ fs.copyFileSync(path.join(REPO,'LICENSE'),path.join(PLUGIN,'LICENSE'));
37
+
50
38
  for (const f of KERNEL_LIBS) {
51
39
  const src = path.join(REPO, 'lib', f);
52
40
  if (!fs.existsSync(src)) bail(`缺少内核模块 lib/${f}`);
package/skill/SKILL.md CHANGED
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: dsh-plugin-compat-check
3
- description: 体检「已经装进某个 profile 的 DSH 插件」与当前 DSH 版本是否兼容,并给出每条不兼容的 file:line 与原因。当用户说「更新 DSH 之后插件不工作了」「这个插件在我这版上还行吗」「设置页/标签页/面板不见了」「工具点了没反应」「启动日志里有 did not activate」「装上了也没报错但功能是死的」,或者想核对插件声明的 peerDependencies、engines.dsh 兼容范围是否属实时,使用本 skill。Check whether the DSH plugins already installed in a profile work on the current DSH version, with file:line and reason; use when a plugin stopped working after a DSH update, when a settings page or feature silently disappeared, or when a plugin activates without error but its functionality is dead.
3
+ description: 当用户询问已安装DSH插件是否适配当前版本,或排查更新后插件不工作、设置页/面板消失、工具无响应、did not activate和兼容声明时使用。Use when checking installed DeepSeek Harness plugin compatibility, investigating post-update failures or missing features, or verifying peerDependencies and engines.dsh declarations.
4
4
  ---
5
5
 
6
6
  # dsh-plugin-compat-check
7
7
 
8
- 回答一个问题:**我这一版 DSH 上,我已经装了的插件到底能不能用**;不能用的,给位置(`file:line`)和原因。
8
+ 回答一个问题:**我这一版 DSH 上,我已经装了的插件有哪些兼容性证据与限制**。API源码发现有真实位置才给`file:line`;loader或声明事实用实际包身份、状态和依据说明,不编造源码行。
9
9
 
10
- 判定内核只有一处实现(`lib/verdict.mjs`,经 `bin/compat-check.mjs` 暴露),插件 / 脚本 / 本 skill 三个入口共用它。所以你调 CLI 得到的结论,与插件面板里看到的**必须**是同一份——如果对不上,那是缺陷,要报出来,不要替某一方圆场。
10
+ 判定内核只有一处实现(`lib/verdict.mjs`,经 `bin/compat-check.mjs` 暴露),插件 / 脚本 / 本 skill 三个入口共用它。只有相同目标、版本、规则与采样证据才应得到相同结果。默认 CLI 用静态证据,当前宿主插件使用进程内观测,两者结论和基线模式可以不同;先核对证据,不把差异直接当缺陷。
11
11
 
12
12
  ## 先看能不能直接调工具
13
13
 
@@ -15,10 +15,10 @@ description: 体检「已经装进某个 profile 的 DSH 插件」与当前 DSH
15
15
 
16
16
  ## 怎么跑
17
17
 
18
- 内核在一个 `dsh-plugin-compat-check` 检出/包目录里,必须同时有 `bin/compat-check.mjs`、`lib/`、`rules/`。先定位它,再跑:
18
+ CLI 只随仓库提供,npm 插件包没有 CLI bin。定位仓库检出,先在其根目录执行 `npm ci --ignore-scripts`;仓库必须同时有 `bin/compat-check.mjs`、`lib/`、`rules/`。先定位它,再跑:
19
19
 
20
20
  ```bash
21
- # 1) 找到内核(仓库检出,或 profile 里装好的包目录)
21
+ # 1) 找到内核(仓库检出,不是已安装的 npm 插件目录)
22
22
  ls <repo>/bin/compat-check.mjs
23
23
 
24
24
  # 2) 体检「已装插件」。--nm 接受 profile 目录,也接受 node_modules 目录
@@ -38,10 +38,10 @@ node <repo>/bin/compat-check.mjs --nm ~/.dsh/profiles/<profile> --no-baseline
38
38
  | 旗标 | 作用 |
39
39
  |---|---|
40
40
  | `--nm <dir>` | 必需。要体检的 `node_modules` 目录,或一个 profile 目录(会自动补 `node_modules`) |
41
- | `--probe <json>` | 宿主的**真实运行时面**(探针报告)。给了它,方法级结论才是「确定」 |
42
- | `--probe-boot` | **自己起一次宿主拿运行时面**(不必先有报告)。默认不开,约 10~20 秒。**关键是它把「疑似」变成「确定」** —— 用户要一个可信结论时用它 |
41
+ | `--probe <json>` | 宿主运行时观测;身份、时间、完整性和调用点归属满足要求时,才可支持 certain 方法结论 |
42
+ | `--probe-boot` | **自己起一次宿主拿运行时面**(不必先有报告)。默认不开,会安装并执行目标插件;只在用户授权启动检查时使用,不保证每条结论都是 certain |
43
43
  | `--probe-timeout <秒>` / `--probe-keep-dir <dir>` | `--probe-boot` 的等待上限(默认 40)/ 留一份探针报告到该目录(排查用) |
44
- | `--dsh-version <v>` | 当前 DSH 版本,用于「声明与实测不符」那类结论 |
44
+ | `--dsh-version <v>` | 目标DSH版本,用于声明/peer范围判断;显式值优先 |
45
45
  | `--json` / `--quiet` | 机器可读 / 只输出结论行 |
46
46
  | `--no-baseline` | 不读写本机基线。**一次性检查用这个**,见下面的「副作用」 |
47
47
  | `--rules` / `--catalog` | 覆盖规则表与官方 API 目录(默认在 `rules/`) |
@@ -49,38 +49,35 @@ node <repo>/bin/compat-check.mjs --nm ~/.dsh/profiles/<profile> --no-baseline
49
49
 
50
50
  `--nm` 指哪里:`$DSH_HOME/profiles/<name>`(默认 `~/.dsh/profiles/<name>`)。用户说「我的插件」时,先弄清楚是哪个 profile,别默认 `desktop`。
51
51
 
52
- 探针报告从哪来,三条路(**优先级从高到低**):
52
+ 运行时证据来源:
53
53
 
54
- 1. **`--probe-boot`**:让 CLI 自己起一次宿主去拿(推荐 —— 不需要任何前置准备)。
55
- 它只在你显式要求时才跑(约 10~20 秒),默认路径是只读静态的。
56
- 2. `--probe <json>`:你手上已经有一份探针报告。`./dsh-plugin-compat-check.sh` 跑完会落在
57
- `/tmp/dsh-plugin-compat-check/probe-report.json`;放在 `node_modules` 旁边时 CLI 也会自动找到。
58
- 3. 都没有 → 方法级结论一律降为「疑似」。**这时不要假装有**。
54
+ 1. 优先复用当前 `compat_check` 的进程内观测,或用户已有的 `--probe <json>` 文件;现代文件需匹配 profile/DSH版本,默认超过24小时失效,旧文件明确标为未绑定。
55
+ 2. 用户授权真实启动检查时,可显式使用 `--probe-boot`。它复制 profile、安装探针、启动自己的宿主并清理自己的现场;与 `--probe` 同时给时,文件优先。
56
+ 3. 没有探针则按目录提供疑似线索;省略的服务是未采样,不能推断为不存在。CLI 也会尝试读取目标 node_modules 旁的 probe-report.json,转述前检查输出中的采样来源。
59
57
 
60
- > 为什么值得主动提 `--probe-boot`:实测在真实 desktop profile 上,**没有运行时面时
61
- > 38 条发现全是「疑似」**,而跑一次之后可用数从 9 升到 14、不兼容从 4 降到 0
62
- > —— 换句话说,**不给面就只能给出"判不准",给面才能给出结论**。
58
+ 安装检查脚本 `--keep` 会返回本次唯一私有目录;CLI `--probe-keep-dir` 需显式给私有目录。报告与 boot.log 不再使用固定共享临时路径。`static.json` 来自 `node lib/extract.mjs <nm> <输出目录>`,不是每次安装检查都会自动产生的文件。
63
59
 
64
60
  ## 输出怎么读
65
61
 
66
62
  ```
67
63
  插件数: 4 有运行时面: 是
68
64
 
65
+ 采样说明: 旧格式探针未绑定 profile/DSH 版本;仅使用显式记录的服务,不推断缺项
66
+ 工具版本: 0.1.2 证据覆盖有限: 4 个插件
69
67
  结论: 可用 3 部分可用 1 不兼容 0 (其中 1 个「可用」是空洞的:它没有任何可检查的依赖)
70
68
 
71
69
  部分可用 dsh-keep-awake@0.2.0
72
70
  功能失效 / 宿主方法缺失 settings.register @ lib/index.js:466
73
71
  影响(推断,非实测): 插件的设置页/设置项会消失
74
- 依据: 运行时里没有这个方法;规则表给出影响面
72
+ 依据: 运行时方法集没有此方法;源码存在未被方法能力检查保护的宿主调用
75
73
  客户端半边(命令行测不到,需界面验证): locale, slots
76
74
  已过滤(判为噪音/有兜底): jobs.onJobsChanged
77
75
  无证据可判(只出现名字,没有调用点): timer
78
76
  可用 @omdsh-dev/dsh-plugin-check@0.1.0
79
77
  可用 dsh-pet@0.3.6
80
- 客户端半边(命令行测不到,需界面验证): commandUi, locale, remote, remote.commands, slots
81
- 已过滤(判为噪音/有兜底): llm.listConfigurableProviders, llm.listProviders
82
- 无证据可判(只出现名字,没有调用点): connection
83
- 可用 dsh-plugin-compat-check-probe@0.1.0 [空洞的结论:无可检查对象]
78
+ 客户端半边(命令行测不到,需界面验证): commandUi, connection, locale, remote, remote.commands, slots
79
+ 已过滤(判为噪音/有兜底): llm.listProviders, llm.listConfigurableProviders
80
+ 可用 dsh-plugin-compat-check-probe@0.1.1 [空洞的结论:无可检查对象]
84
81
  ```
85
82
 
86
83
  > ⚠️ 上面这段是**从真实运行里抄的**,而且**有一条测试盯着它**(`test/skill.test.mjs`:
@@ -91,11 +88,11 @@ node <repo>/bin/compat-check.mjs --nm ~/.dsh/profiles/<profile> --no-baseline
91
88
  > 教训:**示例输出也必须从运行里抄**,否则 skill 会教 agent 去期待一个不存在的结论。
92
89
 
93
90
  - **总结论**每插件一个:`可用` / `部分可用` / `不兼容` / `未知`。
94
- - **每条发现**四件事:严重度(`致命` / `功能失效` / `降级`)、类别(`宿主服务缺失` / `宿主方法缺失` / `无人提供的依赖` / `声明与实测不符`)、`服务.方法`、`@ 文件:行`。
91
+ - **API发现**通常包括位置;loader/声明事实可能没有源码行,不得编造位置。发现包括:严重度(`致命` / `功能失效` / `降级`)、类别(`宿主服务缺失` / `宿主方法缺失` / `无人提供的依赖` / `声明与实测不符` / `声明范围不兼容` / `宿主条目未激活`)、`服务.方法`、`@ 文件:行`。
95
92
  - `影响` 与 `依据` 是给人验证用的:告诉用户**凭什么**这么判,而不是只给一个红叉。
96
93
  - `[疑似]`:这一条没有运行时面支撑。转述时**必须**带上「疑似」,不能抹平成确定。
97
94
  - `[空洞的结论:无可检查对象]`:这个插件没有任何可检查的依赖,"可用"是空的。别把它当成通过。
98
- - `客户端半边(命令行测不到,需界面验证)`:列出来的服务**命令行永远测不到**,只能让用户去看界面上有没有该插件的元素。**不要替它下结论**。
95
+ - `客户端半边(命令行测不到,需界面验证)`:列出来的服务**默认 CLI 不验证**,只能让用户去看界面上有没有该插件的元素。**不要替它下结论**。
99
96
  - `已过滤(判为噪音/有兜底)`:报了但被判定为不构成问题(例如插件自己写了 `typeof x?.y === 'function'` 能力检测)。过滤与弃权**也是结论的一部分**,要如实转述,不要只报红叉。
100
97
  - `无证据可判`:只出现名字、没有调用点。这是弃权,不是清白。
101
98
  - **分清「事实」和「推断」**(`impactBasis` 字段):
@@ -105,18 +102,17 @@ node <repo>/bin/compat-check.mjs --nm ~/.dsh/profiles/<profile> --no-baseline
105
102
  **转述时必须保留这个限定**,不要说成"确定会消失"。
106
103
  实测有过一条 certain 级假阳性:工具说「设置页会消失」,而作者把能力检测写在了
107
104
  一个辅助函数里、早就兜住了,还 log 了降级行为。
108
- - 没标的 —— 有独立机器信号确认(目前只有「条目会停在 pending」这一类,
109
- 宿主自己报 `fiber.state`,`tools/labels.mjs` 用合成正负例量过它)。
105
+ - 只有显式`impactBasis=measured`且有对应观测,才按实测转述,例如明确记录的loader FAILED/PENDING。缺少字段或没有标注“推断”均不能当成实测;损坏报告应报工具错误。
110
106
  - **转述结论时带上 `--version` 报出的工具版本**(`--json` 里是 `toolVersion`)。
111
107
  同一个插件在「规则表还没覆盖这个方法」和「已经覆盖」的两个版本下,
112
- 结论会从「影响面未知」变成「设置页会消失」—— 不带版本,用户无法判断该不该信。
108
+ 结论可能从「影响面未知」变成「推断:可能影响设置页」—— 不带版本,用户无法判断该不该信。
113
109
 
114
110
  ## 退出码
115
111
 
116
112
  | 码 | 含义 |
117
113
  |---|---|
118
- | `0` | 可用(全体无问题) |
119
- | `1` | 部分可用(有 API 缺失,相关功能降级或失效) |
114
+ | `0` | 可用(未发现问题,仍需检查 coverage / vacuous) |
115
+ | `1` | 部分可用或未知;保留具体发现与证据限制 |
120
116
  | `2` | 不兼容(有插件连基本依赖都不满足) |
121
117
  | `3` | **无法判定**——工具/输入出了问题,**这不是插件的结论** |
122
118
 
@@ -126,24 +122,29 @@ CI 里可以直接用:`0` 过,`1`/`2` 按你的策略处理,`3` 是**工
126
122
 
127
123
  ## 判据:这个工具的三条硬规矩
128
124
 
129
- **1. 确定性是三档的,不是二值。**
130
- `确定`(服务名出现在可信声明里,或有运行时面实证)/ `疑似`(只靠官方目录推断——**那份目录是契约目录,不是运行时面**)/ `高概率假阳性`(已过滤)。没有运行时面时方法级结论一律降为「疑似」。**「疑似」是诚实,不是缺陷**——把不确定的位置呈现得像确定一样,比不给位置更糟。
125
+ **1. 确定性与去处分开。**
126
+ `certainty` 只有 `certain` 与 `suspected`;过滤、守卫、弃权是独立去处,不能当第三档确定性。声明能确定依赖关系,不单独证明方法不存在;目录不是运行时面。没有运行时面时方法级结论一律降为「疑似」。**「疑似」是诚实,不是缺陷**——把不确定的位置呈现得像确定一样,比不给位置更糟。
131
127
 
132
- **2. 假阳性比假阴性更被容忍,但假阴性的表现形式是「沉默」。**
128
+ **2. 误报和漏报都要关注,漏报可能表现为「沉默」。**
133
129
  判不准时一律**降级**,不升格成确定:假阳性会让用户去做不必要的处置(改配置、换版本),成本高且磨损信任。但反过来也要盯住——**假阴性不产出红字,它产出沉默**,而用户会把沉默当成「一切正常」。所以:
134
130
  - 「没报」不等于「没问题」。要区分**查过了、没问题**和**没查成**。
135
131
  - 先看 `有运行时面: 是/否`、`插件数: N`、退出码,再谈结论。
136
132
  - `插件数: 0` + 退出码 `0` 是**最危险**的输出:它意味着一个插件都没检到,不是"全都好"。看到这个先回头确认 `--nm` 指对了没有。
137
133
 
138
134
  **3. 「激活成功」不等于「功能可用」。**
139
- 这个生态的插件普遍用 `try/catch`、`typeof` 守卫做降级:API 没了它照样激活成功,只是相应功能是**死的**。所以「启动没报错」「日志干净」都不能当结论;同理,插件的 `peerDependencies` / `engines.dsh` 声明也不等于事实——DSH 自带的版本闸门只做**字符串范围比对**,不验证 API 是否存在。
135
+ 插件可能通过`try/catch`或能力守卫处理缺失API,因此激活成功、日志干净既不能证明功能完备,也不能证明相应功能一定失效。具体业务行为须另行验收;`peerDependencies`/`engines.dsh`声明也不是功能证明,DSH官方peer版本闸门只检查semver范围,不验证每个API。
140
136
 
141
137
  ## 边界(不要越界承诺)
142
138
 
143
- - **客户端半边命令行测不到。** 插件在浏览器里跑的部分(面板、标签页、设置卡片)需要人工在界面上确认;本工具只列出待确认项,不判定。
144
- - **方法级判定是正则启发式**,可能有误报。拿不准时建议用户加 `--keep`(脚本形态)查看现场:`static.json`(提取到的调用点)、`probe-report.json`(宿主真实 API 面)、`boot.log`(启动日志)。
145
- - **副作用只有一个,要主动说清**:默认会把这次结论写成本机基线
146
- (`$DSH_HOME/dsh-plugin-compat-check/baseline.<profile>.json`),好让下次对比出「相比上次变了什么」。
139
+ - **默认报告不验证客户端功能;可选开发工具能验证条目到货与浏览器错误。** 插件在浏览器里跑的部分(面板、标签页、设置卡片)需要人工在界面上确认;本工具只列出待确认项,不判定。
140
+ - **方法级判定使用 AST,但动态绑定与跨函数数据流仍有限**,可能有误报。拿不准时建议用户用脚本 `--keep` 保留探针与启动日志,并按需另运行源码提取输出 static.json。
141
+ - **默认静态检查的写入要主动说清**:默认会把这次结论写成本机基线
142
+ (`$DSH_HOME/dsh-plugin-compat-check/baseline.<profile>.<path-hash>.<mode>.json`),好让下次对比出「相比上次变了什么」。
147
143
  一次性检查(CI、替别人看一眼)加 `--no-baseline`;`--dry-run` 是只算差分不写。除此之外它不装包、
148
- 不改用户的 profile、不联网(除脚本形态的 npm 身份核对)。不要用它去"顺便修一下"。
149
- - 报结论时**带上位置和依据**。只说「不兼容」而不给 `file:line`,等于让用户无法验证我们凭什么判——那正是这个工具存在的理由。
144
+ 不改用户的 profile、不联网(显式 `--probe-boot` 和安装检查会安装并执行目标插件)。不要用它去"顺便修一下"。
145
+ - 报结论时**给出可核对的依据**。API源码发现引用真实`file:line`;没有源码位置的loader/声明事实说明包身份及观测或声明来源,不补造行号。
146
+
147
+
148
+ 0.1.1:`root` 缺项是未采样,不能称为不存在;`coverage.status=limited` 与 `vacuous` 必须转述。现代探针绑定 profile/DSH/采样时间;旧报告明确标为未绑定。解析、读取、资源上限等诊断会限制覆盖,完整性不足不更新基线。当前统计见仓库 `docs/current-state.json`;npm 包是插件,CLI 从仓库使用并先运行 `npm ci --ignore-scripts`。显式安装检查会执行目标插件,临时 profile 不是不可信代码隔离沙箱。
149
+
150
+ 官方 peer 范围冲突是 declared-incompatible:影响为推断,不能凭声明写成实际加载失败;当前实现沿用DSH0.2.0-rc.2的声明规则,将engines.dsh作建议处理;0.2.1-rc.1宿主已优先按engines.dsh判断,存在声明误报/漏报风险,须说明该限制并核对宿主实际结果,不能称新版已全面适配。当前契约、施工记录和后续计划统一见仓库 docs/Plan.md,原始审计位于 docs/GPT审计及重构方案文档.md。本地版本以仓库文件为准;发布受理不等于公开可安装,公开版本以npm registry实时结果为准;本地修订不能视为已在公开包生效。