dsh-plugin-adapt · git:20261027.847e88d · 2026-10-27 · sha256 c8625debaca38933

dsh-plugin-adapt git:20261027.847e88dA

Immutable. This exact content never changes and is served at /api/v1/blob/c8625debaca38933.

---
name: dsh-plugin-adapt
description: plugin-fast-adapt 的升级适配车道。查 DeepSeek 官方最新版本,取该版本官方 upgrade-guide,扫描插件代码匹配破坏性变更锚点,报出踩了哪几条并给官方修法;seams 查包名或 API 符号在指南与本机宿主里的双源结论;改完秒验并做四维性能架构体检;官方指南覆盖不到的老版本转七阶段迁移 SOP。宿主环境本身有问题(覆盖层残留、悬空软链、插件装了没生效)不走这里,转 dsh-plugin-diagnose。触发词:适配 DSH 插件、升级插件、插件不兼容、dsh 升级后报错、插件用了废弃 API、接缝查询、这个 API 还在不在、扫描窗口、破坏性变更、老版本 DSH 适配、历史走廊迁移、插件性能审计、peer 修补。
---

# 适配升级车道

把「插件适配到新版 DSH」拆成四步,每步一个脚本,每步都能单独跑;另有两个可选步骤补全「修完即验、升级顺带优化」。以下命令都在工具根目录执行,即本文件真实路径往上两级、含 `scripts/` 的那一层。本目录常以软链别名加载,shell 的 `cd ../..` 会按软链走错地方,用 `cd "$(realpath <本文件所在目录>)/../.."` 进入。`<插件目录>` 指被查插件的仓库根。

环境本身有问题(覆盖层旧包、悬空软链、客户端半死链)先转 `../diagnose/SKILL.md`,别在坏环境上做适配判断。

## 第一步:看清本机环境

```sh
node scripts/env-doctor.mjs <插件目录>
```

本车道只取四段:

- `globalDsh`:全局安装的 DSH 版本和位置,后面定目标版本要用
- `target.peerWindows`:待查插件自己声明的 peer 版本窗口,决定扫描走廊起点
- `ghostHost`:磁盘代码已换新、宿主进程还在跑旧代码时判 `stale`(进程启动时间早于磁盘变更超过 120 秒)。先重启宿主再做适配判断,否则改完也验不到;`ok` / `no-running-host` / `unknown` 看 `reason`,不猜
- `falseGreen`:`tsconfig` 的 `paths` 指向不存在的 checkout(M1)。此时 TypeScript 静默回落到 `node_modules` 的已发布类型,本地 `typecheck` 可能全绿而实际类型是旧的;`findings` 非空先修路径或删 alias,再信 typecheck 结果

`overlayFindings`、`sharedFarmFindings`、`activation` 非空,或 `ghostHost.verdict` 为 `stale`,说明环境本身要修,转 `../diagnose/SKILL.md`。

## 第二步:定目标版本

```sh
node scripts/guide-sync.mjs <本机版本>
```

它读 npm 的 `dist-tags`(`latest` / `next` / `alpha`),判断本机在官方通道里的位置:

| 判定 | 含义 | 下一步 |
| --- | --- | --- |
| `upgrade-available` | 有通道版本高于本机 | 取 `target`(官方所有通道的最高版本)继续 |
| `up-to-date-on-channel` | 本机已是某通道最新版 | 无需适配,收工 |
| `ahead-of-upstream` | 本机比官方所有通道都新 | 无需适配,收工 |
| `unknown-local` | 没检测到本机版本 | 默认跟 `latest`,但要人工确认 |

**不要盲目取 `latest`。** 本机完全可能跑在 `next` 线上,比 `latest` 还新一版,这时报「建议升级到 latest」是倒退。

**上游盯哨(--check)**:`node scripts/guide-sync.mjs --check` 零参数可直接接 cron、launchd 或 agent 轮询,自动探测本机宿主版本,退出码语义化(1=上游有新版,0=无需动作,2=判定失败),有新版时顺手把该版官方指南预取进缓存,下次 `run.mjs` 零等待。工具不自带定时器。

有 `target` 时取该版本的官方升级指南:

```sh
node scripts/guide-sync.mjs <本机版本> --clone
```

优先复用本机已有的 harness clone(自动发现 `~/Documents/*/deepseek-harness`,或用 `DSH_HARNESS_CLONE` 指定),没有才 sparse clone 到 `~/.cache/dsh-plugin-fast-adapt/`。

**取版锚点(2026-10-25 实测复盘)**:本机已是官方通道最新版时 `resolution.target` 为 `null`,旧实现因此拿不到指南语料、`run.mjs` 的 scan 阶段静默跳过(扫描走廊 `(peerFloor, hostVersion]` 明明有内容却报「无指南根或无窗口」)。现在取版锚点退回本机版本(`target || localVersion || dist-tags.latest`),走廊照常扫。

**窗口起点只认宿主包(2026-10-25 dsh-image-gen 实录)**:起点取插件在宿主包(`@deepseek-ai/dsh` 及 `dsh-*` 套件子包)上声明的 peer 下限;同 scope 的 `@deepseek-ai/cordis`(4.x)、`@deepseek-ai/schemastery`(3.x)是独立版本线的库,不算宿主包。混入会把起点顶到 3.18.4 一类高位,窗口倒挂后扫描静默跳过。现在倒挂(起点大于终点)会回退宿主版本并在 `window.fallback` 留痕、`window.source` 记起点来源(flag/peer/host),stderr 同步告警。

## 第三步:查插件踩了哪些变更

```sh
node scripts/code-scan.mjs <插件目录> <upgrade-guide 目录> <本机版本> <目标版本>
```

`<upgrade-guide 目录>` 是官方指南根,例如 `~/layerfs-lab/deepseek-harness/docs/upgrade-guide`,或上一步 clone 出来的 `~/.cache/dsh-plugin-fast-adapt/harness-dsh-v<version>/docs/upgrade-guide`。

输出三段:

- `findingsByGuide`:按官方变更条目分组的命中,每条带 `guide`、`title`、`migration`
- `migration`:官方给的修法原文(从该指南的 Migration 段抽出的相关步骤)
- `findingCount` / `guidesConsidered`:命中数与参考的指南条数

每条命中带出处锚点 `guideFile`/`guideLine`(指南内相对路径加标题行号),结论可回溯到官方原文位置,不用在指南目录里再搜标题。

命中分三类并带 `severity`:`package`(import 了已移除或已改名的包,critical,装载必炸)、`member`(调用了已移除的方法或字段,high,运行时炸)、`code`(字符串里出现已变更的错误码或事件名,medium,最可能撞同名)。findings 先按 severity 排再按文件行号,每条带 `evidence`(命中行原文)。包名锚点有语义门:只在与移除或改名动词同句时才成为锚点,语境引用不算。

**扫描根(2026-10-25 实测复盘)**:`pickScanRoots` 取源码根并集,`src`(client/web 常嵌在 src 内部)与同级的 `client`/`web`/`ui` 一并扫;`lib`/`dist` 是构建产物、与 src 同义,只在没有任何源码根时才退回,否则同一处命中会按两份文件重复报。

## 接缝查询

```sh
node scripts/seams.mjs <包名或符号> [--guide <guide-root>] [--host <版本>]
```

回答「这个接缝在我的目标宿主还在不在」,双源合成:指南源(锚点表全走廊,哪个版本声称移除或改名了它、原文在哪一行、官方修法)乘宿主源(包看 node_modules 实测,符号看宿主套件源码文本命中,弱证据如实标注)。裸 `dsh-*` 名自动归一到 `@deepseek-ai/` scope 再查。诚实三态:双源都说没有才下「已移除」;指南说没有但宿主里有,报分歧不替人拍板;双源都无记录时明说「指南未提及不代表 API 不存在」。不走网络,指南根走缓存。

## 一键编排与运行日志

```sh
node scripts/run.mjs <插件目录> [--target <版本>] [--from <版本>] [--no-clone] [--no-smoke] [--no-typecheck] [--no-log] [--real-smoke]
```

按上面四步依次跑环境、定版、指南、扫描、体检、冒烟、typecheck(执行插件自有的 typecheck script,与冒烟同信任级),输出一份统一 JSON 报告;扫描窗口自动取「插件在宿主包上声明的 peer 下限 → 宿主版本」的走廊(`--from`/`--target` 可覆盖,倒挂自动回退并留痕)。

`--real-smoke` 默认关,显式启用后桥接原仓库 plugin-upgrade 的 `verify-runtime.mjs`:干净 profile 安装→启动→四类归因(插件代码、依赖解析、profile 配置、宿主运行时),fail 计入退出码 2。脚本不在本机(探测兄弟仓 `dsh-plugin-upgrade-skill`,或 `PFA_VERIFY_RUNTIME` 指路径)时诚实跳过给补法,Windows 按 POSIX-only 声明跳过。退出码:0 干净,2 有 critical/high 命中、typecheck 红或真冷启动失败。

每次运行写本地日志(`~/.local/state/dsh-plugin-fast-adapt/runs/`,一次一个自包含 JSON 加 index.jsonl 一行摘要;旧 `~/.cache/dsh-plugin-fast-adapt/runs/` 有数据时首次运行自动整体搬迁,台账是要留存的运行史,放 `~/.cache` 会被本机清理工具整目录扫掉,可再生的 dist-tags 缓存与指南快照仍走 `~/.cache`)。记录工具版本与 commit:2026-10-25 实测教训是跑了一份滞后拷贝产出 4 个本已修复的误报,没有日志这类「跑的是哪版工具」完全不可观测。索引摘要带 `phases` 三态(scan/smoke/typecheck/realSmoke 各 ok|skipped|fail|error)与 `toolDirty` 星标(工具仓当时有未提交改动,版本号与 commit 可能拆对),扫描没跑不再与「体检满分」在同一行里混淆。

```sh
node scripts/run-log.mjs list [--limit 20]                    # 最近运行一览(含工具版本@commit)
node scripts/run-log.mjs show <id|--latest>                   # 单次全文
node scripts/run-log.mjs sanitize <id|--latest> [--out 文件]  # 脱敏后才可外发
node scripts/run-log.mjs doctor [--logs <目录>]               # 运行日志 × 宿主启动日志交叉核对
```

`doctor` 的判定:宿主日志干净且最近一次 run 扫描跑过,则状态一致;宿主日志干净但扫描没跑(`host-log-clean-scan-unknown`),代码侧未核查,看 `scan.reason`;宿主报了它而最近一次扫描干净,嫌疑在装载/挂载/环境侧,转 `../diagnose/SKILL.md` 看 activation 双半;两边都有问题先按运行日志的命中清代码。日志不出本机,对外反馈前必须先 `sanitize`(home 前缀转 `~`、用户名转 `<user>`、机器名转 `<host>`、邮箱转 `<email>`,目录结构保留)。`--offline` 跳过 guide-sync 供自检零网络用。

## 第五步(可选):修完秒验

```sh
node scripts/quick-smoke.mjs <插件目录>
```

纯内存模拟 Cordis 上下文,动态载入插件入口,毫秒到秒级验证模块能加载、`apply` 契约能执行、工具能注册。改完代码先跑它,挂了立刻回改,不用等真宿主冷启动。

契约怎么判、Mock 缺口怎么读、边界在哪,见 `../build/SKILL.md` 的秒验段。一句话:冒烟通过不等于宿主兼容。

## 第六步(可选):升级时顺带做性能与架构体检

```sh
node scripts/plugin-audit.mjs <插件目录>                                # 纯四维审计
node scripts/plugin-audit.mjs <插件目录> --guide <guide-root> <本机版本> <目标版本>   # 叠加官方指南交叉
```

四个维度从 100 分扣:性能(同步 I/O、无上限读取)、弹性(强依赖已移除包)、上下文预算(工具 Schema 体积)、生命周期(监听器与定时器清理)。检测走 scanner 状态机,注释和字符串里的同名词不算命中。报告带 `targetForm`(plugin/tool):目标是不是 DSH 插件看生态信号(package.json 的 `dsh` 命名空间、`@deepseek-ai/*` 依赖),工具仓跳过生命周期维度,对普通 Node 脚本建议 `ctx.effect` 属形态错配的噪音(2026-10-25 自审实录)。

三条 INFO 级提示不扣分:import type 引用已移除包(编译期风险非运行时)、peer 窗口按严格 semver 不含目标宿主(本生态 bundle 安装不强制 peer,是声明性约定,建议先经 satisfiesRange 自验证再输出)、工具描述里的宿主路径约定在宿主代码零命中(可能旧版约定已消亡,如 `.dsh-filess`,需人工确认)。

带 `--guide` 时与 code-scan 同源交叉:package 命中先经宿主实测核查(包仍在目标宿主则降级 info 证伪;确实不存在则 HIGH 扣分附官方修法)。命中清零前不建议先做性能优化。新版本引入的接缝(弱依赖 `ctx.get()` 替代强 import、peer 范围写法)就是「按新依赖做架构调整」的依据。

**peer 修补建议**:对不含目标宿主的 peer 范围,`peerPatch` 给「与目标同三元组的 caret 线」替换写法,每条都先用 `satisfiesRange` 自验证覆盖目标,验证不过的写法不给。只对 `@deepseek-ai/dsh` 及其 `dsh-*` 套件子包生成;同 scope 的库(`@deepseek-ai/schemastery`、`@deepseek-ai/cordis`)有自己的版本线,拿宿主版本套它们的 peer 会给出错到离谱的建议。只生成不自动改包:改用户 `package.json` 是不可逆写,`--peer-patch` 打印后人工确认再落。

## 老插件:官方指南覆盖不到时

官方 upgrade-guide 最早只到 dsh-v0.1.7-rc.2。插件更老、目标版本更老,或跨大段历史走廊时,`code-scan` 会报 `guidesConsidered = 0`,这时转 [老插件迁移 SOP](../../references/legacy-migration-sop.md):用原仓库 [plugin-upgrade skill](https://github.com/oh-my-dsh/dsh-plugin-upgrade-skill/tree/main/skills/plugin-upgrade) 的人工卡片走廊(dsh-v0.1.0-rc.8 起 19 条边加跳版卡)当差异来源,按七阶段 checklist 走:定模式 → 只读准备 → 幽灵宿主检查 → 组装走廊 → 七类触点 → 计划确认 → 六层验证。两套工具的分工速查也在 SOP 里:有 guide 的段用本技能的 `code-scan`,没 guide 的段用原版的 `plan-migration.mjs` 和卡片,运行时验证用原版的 `verify-runtime.mjs`。

## 怎么读结果

- **命中为零不代表没问题**,只代表「在指南覆盖的范围内没踩到」。
- **命中先验误报**:同名符号会撞车,看命中位置上下文确认来源。
- **`member` 命中里若是指南的迁移目标就不会出现**,报了是噪音。
- **`guidesConsidered = 0` 说明这一段没有官方指南**,不是「查干净了」。