@bruc3van/dsh-doctor 0.5.5 → 0.5.6
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 +5 -5
- package/README.md +5 -5
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.2/packages.json +1 -3
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.2/symbols.json +8 -0
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3/behavior.md +14 -0
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3/config-rules.json +36 -0
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3/manifest.json +19 -0
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3/packages.json +74 -0
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3/services.json +28 -0
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3/symbols.json +134 -0
- package/package.json +1 -1
- package/skills/dsh-plugin-upgrade/SKILL.md +15 -7
- package/skills/dsh-plugin-upgrade/references/cli-bootstrap.md +6 -4
- package/skills/dsh-plugin-upgrade/references/migration-map.md +5 -1
- package/skills/dsh-plugin-upgrade/references/source-investigation.md +2 -0
- package/skills/dsh-plugin-upgrade/references/verification.md +6 -0
- package/src/cli.mjs +7 -3
- package/src/migrate-verify.mjs +3 -2
- package/src/migrate.mjs +53 -22
package/README.en.md
CHANGED
|
@@ -12,7 +12,7 @@ DSH 0.1.1 → DSH 0.1.2
|
|
|
12
12
|
|
|
13
13
|
The project also diagnoses DSH profiles and installed plugins, checks for compatible versions, and performs safety checks before quarantine or removal.
|
|
14
14
|
|
|
15
|
-
> This is a community-maintained third-party project, not an official DeepSeek project. The current catalog
|
|
15
|
+
> This is a community-maintained third-party project, not an official DeepSeek project. The migration scope remains DSH 0.1.1 → 0.1.2; the current known-latest catalog exactly covers `dsh-v0.1.1-rc.2` through `dsh-v0.1.2-alpha.3`. The older alpha.2 catalog remains as historical rules. When a newer 0.1.2 prerelease appears, inspect its delta before deliberately updating the catalog and skill.
|
|
16
16
|
|
|
17
17
|
## Upgrade a plugin with the skill
|
|
18
18
|
|
|
@@ -115,7 +115,7 @@ Node.js `^22.19.0` or `>=24.0.0` is required.
|
|
|
115
115
|
First confirm that the CLI contains the required migration:
|
|
116
116
|
|
|
117
117
|
```sh
|
|
118
|
-
|
|
118
|
+
npm exec --yes --package=@bruc3van/dsh-doctor@<selected-version> -- \
|
|
119
119
|
dsh-doctor migrations list
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -124,12 +124,12 @@ npx --yes --package=@bruc3van/dsh-doctor@0.5.5 \
|
|
|
124
124
|
```sh
|
|
125
125
|
dsh-doctor migrate analyze /path/to/plugin \
|
|
126
126
|
--from dsh-v0.1.1-rc.2 \
|
|
127
|
-
--to dsh-v0.1.2-alpha.
|
|
127
|
+
--to dsh-v0.1.2-alpha.3 \
|
|
128
128
|
--harness-root /path/to/deepseek-harness \
|
|
129
129
|
--json
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
Analysis checks source, dependencies, manifest, client graph, patch targets, and existing build output without executing plugin code.
|
|
132
|
+
Analysis checks source, dependencies, manifest, client graph, patch targets, and existing build output without executing plugin code. `--from`/`--to` select the exact catalog that supplies API rules. Alpha.3 is the current default, so it needs no separate `--target-version`. If a newer 0.1.2 build is explicitly targeted before the catalog is updated, inspect the source delta from alpha.3 first, then use `--target-version` to control dependency ranges, safe-plan pins, and the runtime version check. The report keeps the catalog and actual targets separate.
|
|
133
133
|
|
|
134
134
|
### 2. Apply
|
|
135
135
|
|
|
@@ -155,7 +155,7 @@ dsh-doctor migrate verify /path/to/plugin --level static \
|
|
|
155
155
|
dsh-doctor migrate verify /path/to/plugin --level build --yes --install \
|
|
156
156
|
--harness-root /path/to/deepseek-harness --json
|
|
157
157
|
dsh-doctor migrate verify /path/to/plugin --level runtime --yes --install \
|
|
158
|
-
--
|
|
158
|
+
--dsh-command /path/to/dsh --json
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
| Level | What it checks |
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ DSH 0.1.1 → DSH 0.1.2
|
|
|
12
12
|
|
|
13
13
|
项目同时提供 DSH profile 和插件的诊断、兼容版本检查、隔离与删除前检查。
|
|
14
14
|
|
|
15
|
-
> 这是社区维护的第三方工具,不属于 DeepSeek
|
|
15
|
+
> 这是社区维护的第三方工具,不属于 DeepSeek 官方项目。工具的迁移范围保持为 DSH 0.1.1 → 0.1.2;当前已知最新版 catalog 精确覆盖 `dsh-v0.1.1-rc.2` 到 `dsh-v0.1.2-alpha.3`。旧 alpha.2 catalog 作为历史规则保留;出现更新的 0.1.2 预发布版本时,需要先核对增量,再明确更新 catalog 和 Skill。
|
|
16
16
|
|
|
17
17
|
## 使用 Skill 升级插件
|
|
18
18
|
|
|
@@ -115,7 +115,7 @@ Migration catalog 保存 source/target tag 和 Git commit,并记录 package、
|
|
|
115
115
|
先确认 CLI 包含需要的迁移:
|
|
116
116
|
|
|
117
117
|
```sh
|
|
118
|
-
|
|
118
|
+
npm exec --yes --package=@bruc3van/dsh-doctor@<selected-version> -- \
|
|
119
119
|
dsh-doctor migrations list
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -124,12 +124,12 @@ npx --yes --package=@bruc3van/dsh-doctor@0.5.5 \
|
|
|
124
124
|
```sh
|
|
125
125
|
dsh-doctor migrate analyze /path/to/plugin \
|
|
126
126
|
--from dsh-v0.1.1-rc.2 \
|
|
127
|
-
--to dsh-v0.1.2-alpha.
|
|
127
|
+
--to dsh-v0.1.2-alpha.3 \
|
|
128
128
|
--harness-root /path/to/deepseek-harness \
|
|
129
129
|
--json
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
分析会检查源码、依赖、manifest、client graph、patch target
|
|
132
|
+
分析会检查源码、依赖、manifest、client graph、patch target 和已有构建产物,不执行插件代码。`--from`/`--to` 选择提供 API 规则的精确 catalog。当前默认目标就是 alpha.3,无需额外指定 `--target-version`;如果在 catalog 更新前明确迁移到更新的 0.1.2 版本,必须先调查 alpha.3 到该版本的源码差异,再用 `--target-version` 单独控制依赖范围、safe plan pin 和 runtime 版本核验。报告会保留 catalog 与实际目标两个值。
|
|
133
133
|
|
|
134
134
|
### 2. 修改
|
|
135
135
|
|
|
@@ -155,7 +155,7 @@ dsh-doctor migrate verify /path/to/plugin --level static \
|
|
|
155
155
|
dsh-doctor migrate verify /path/to/plugin --level build --yes --install \
|
|
156
156
|
--harness-root /path/to/deepseek-harness --json
|
|
157
157
|
dsh-doctor migrate verify /path/to/plugin --level runtime --yes --install \
|
|
158
|
-
--
|
|
158
|
+
--dsh-command /path/to/dsh --json
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
| 级别 | 验证内容 |
|
|
@@ -38,9 +38,7 @@
|
|
|
38
38
|
"@deepseek-ai/dsh-win32-process"
|
|
39
39
|
],
|
|
40
40
|
"targetVersions": {
|
|
41
|
-
"@deepseek-ai/cordis": "4.0.2"
|
|
42
|
-
"@deepseek-ai/dsh-client-store": "0.1.2-alpha.2",
|
|
43
|
-
"@deepseek-ai/dsh-session": "0.1.2-alpha.2"
|
|
41
|
+
"@deepseek-ai/cordis": "4.0.2"
|
|
44
42
|
},
|
|
45
43
|
"dependencyPolicies": {
|
|
46
44
|
"@deepseek-ai/cordis": {
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"modules": {
|
|
3
|
+
"@deepseek-ai/dsh-settings": {
|
|
4
|
+
"settingsNamespace": {
|
|
5
|
+
"toModule": "@deepseek-ai/dsh-settings",
|
|
6
|
+
"toSymbol": "SettingsProvider.register",
|
|
7
|
+
"confidence": "semantic",
|
|
8
|
+
"reason": "The settingsNamespace factory was removed. Inject the settings Service and register the namespace through ctx.settings.register(name, schema)."
|
|
9
|
+
}
|
|
10
|
+
},
|
|
3
11
|
"@deepseek-ai/dsh-client-runtime/client": {
|
|
4
12
|
"ClientContext": {
|
|
5
13
|
"toModule": "@deepseek-ai/cordis",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# DSH 0.1.1-rc.2 to 0.1.2-alpha.3 behavior migration
|
|
2
|
+
|
|
3
|
+
The old Client Runtime was split by ownership. There is no aggregate replacement package.
|
|
4
|
+
|
|
5
|
+
- Session control, list state, commands, projections, queue and event windows belong to `api-session-controller/client`.
|
|
6
|
+
- Workspace state and commands belong to `api-workspace-controller/client`; navigation policy belongs to `ui-workspace`.
|
|
7
|
+
- Conversation assembly belongs to `ui-conversation`; Chat and Trajectory own their respective projections.
|
|
8
|
+
- Approval and Question own their pending objects; `ui-session` only aggregates domain publications.
|
|
9
|
+
- The store engine belongs to `client-store`; React hook synthesis belongs to the renderer.
|
|
10
|
+
- The Host API Proxy was removed. Unary browser operations live on their natural Remote service owners.
|
|
11
|
+
- The demo-only `dsh-agent-spine-demo` package and the SQLite session persistence package were removed by alpha.3; neither has a catalog-confirmed drop-in replacement.
|
|
12
|
+
- Alpha.3 adds the `dsh-session-turn-outline` package and extends session submission/load-through APIs. These additions do not justify mechanical rewrites of existing plugin behavior.
|
|
13
|
+
|
|
14
|
+
An import move is safe only when the catalog marks it `exact`. A `semantic` mapping identifies the new owner but still requires the plugin developer to rewrite behavior and verify the real lifecycle.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platformModules": [
|
|
3
|
+
"react",
|
|
4
|
+
"react/jsx-runtime",
|
|
5
|
+
"react-dom",
|
|
6
|
+
"react-dom/client",
|
|
7
|
+
"@deepseek-ai/cordis",
|
|
8
|
+
"@deepseek-ai/dsh-client-store",
|
|
9
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
10
|
+
"@deepseek-ai/dsh-client-ui-primitives"
|
|
11
|
+
],
|
|
12
|
+
"profilePatchPaths": {
|
|
13
|
+
"web": [
|
|
14
|
+
"packages/bundle/base/cordis.patch.yml",
|
|
15
|
+
"packages/bundle/web-app/cordis.patch.yml"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"rules": [
|
|
19
|
+
{
|
|
20
|
+
"id": "client-inject-is-informational",
|
|
21
|
+
"note": "dsh.client.inject lists package dependency edges; Cordis service inject controls activation."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "baseline-external-is-implicit",
|
|
25
|
+
"note": "Platform modules are implicit and must not be repeated in dsh.client.external."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "feature-values-use-services-or-slots",
|
|
29
|
+
"note": "Feature plugins must not use dsh.client.external as a feature-to-feature value dependency mechanism."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "client-export-required",
|
|
33
|
+
"note": "A web dsh.client declaration requires a published exports[\"./client\"] artifact."
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3",
|
|
4
|
+
"from": {
|
|
5
|
+
"ref": "dsh-v0.1.1-rc.2",
|
|
6
|
+
"commit": "b150a551b8d465e31e418e1b2eaf5e79bbb7d28e"
|
|
7
|
+
},
|
|
8
|
+
"to": {
|
|
9
|
+
"ref": "dsh-v0.1.2-alpha.3",
|
|
10
|
+
"commit": "dd6322d604e00eec1ba5e0c8541159906a21094a",
|
|
11
|
+
"version": "0.1.2-alpha.3"
|
|
12
|
+
},
|
|
13
|
+
"description": "DeepSeek Harness 0.1.1-rc.2 to 0.1.2-alpha.3 plugin migration",
|
|
14
|
+
"references": {
|
|
15
|
+
"ownership": ".agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.md",
|
|
16
|
+
"remoteMigration": ".agents/notes/implemented/architecture/2026-08-10-unary-apiproxy-remote-migration.md",
|
|
17
|
+
"clientRules": "packages/client/AGENTS.md"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"removed": [
|
|
3
|
+
"@deepseek-ai/dsh-acp-demo",
|
|
4
|
+
"@deepseek-ai/dsh-acp-snapshot",
|
|
5
|
+
"@deepseek-ai/dsh-agent-spine-demo",
|
|
6
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
7
|
+
"@deepseek-ai/dsh-host-apiproxy",
|
|
8
|
+
"@deepseek-ai/dsh-sdk-jsonrpc-demo",
|
|
9
|
+
"@deepseek-ai/dsh-session-persistence-sqlite"
|
|
10
|
+
],
|
|
11
|
+
"added": [
|
|
12
|
+
"@deepseek-ai/dsh-acp-app",
|
|
13
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
14
|
+
"@deepseek-ai/dsh-api-settings-controller",
|
|
15
|
+
"@deepseek-ai/dsh-api-workspace-controller",
|
|
16
|
+
"@deepseek-ai/dsh-client-store",
|
|
17
|
+
"@deepseek-ai/dsh-client-ui-approval",
|
|
18
|
+
"@deepseek-ai/dsh-client-ui-chat",
|
|
19
|
+
"@deepseek-ai/dsh-client-ui-schedule",
|
|
20
|
+
"@deepseek-ai/dsh-client-ui-session",
|
|
21
|
+
"@deepseek-ai/dsh-deepseek-llm-api-extensions",
|
|
22
|
+
"@deepseek-ai/dsh-deque",
|
|
23
|
+
"@deepseek-ai/dsh-experimental-agent-team-profile",
|
|
24
|
+
"@deepseek-ai/dsh-experimental-agent-team-web-profile",
|
|
25
|
+
"@deepseek-ai/dsh-experimental-client-ui-agent-team",
|
|
26
|
+
"@deepseek-ai/dsh-experimental-inspector",
|
|
27
|
+
"@deepseek-ai/dsh-experimental-webworker-packer",
|
|
28
|
+
"@deepseek-ai/dsh-experimental-webworker-runtime",
|
|
29
|
+
"@deepseek-ai/dsh-plugin-package-inventory-deepseek",
|
|
30
|
+
"@deepseek-ai/dsh-sdk-app",
|
|
31
|
+
"@deepseek-ai/dsh-sdk-minimal",
|
|
32
|
+
"@deepseek-ai/dsh-session-log-deepseek",
|
|
33
|
+
"@deepseek-ai/dsh-session-snapshot",
|
|
34
|
+
"@deepseek-ai/dsh-session-turn-outline",
|
|
35
|
+
"@deepseek-ai/dsh-util-crypto",
|
|
36
|
+
"@deepseek-ai/dsh-util-time",
|
|
37
|
+
"@deepseek-ai/dsh-util-values",
|
|
38
|
+
"@deepseek-ai/dsh-util-workspace-path",
|
|
39
|
+
"@deepseek-ai/dsh-webhook",
|
|
40
|
+
"@deepseek-ai/dsh-webhook-github",
|
|
41
|
+
"@deepseek-ai/dsh-win32-process"
|
|
42
|
+
],
|
|
43
|
+
"targetVersions": {
|
|
44
|
+
"@deepseek-ai/cordis": "4.0.2"
|
|
45
|
+
},
|
|
46
|
+
"dependencyPolicies": {
|
|
47
|
+
"@deepseek-ai/cordis": {
|
|
48
|
+
"client": ["peerDependencies", "devDependencies"]
|
|
49
|
+
},
|
|
50
|
+
"@deepseek-ai/dsh-client-store": {
|
|
51
|
+
"client": ["devDependencies"]
|
|
52
|
+
},
|
|
53
|
+
"@deepseek-ai/dsh-session": {
|
|
54
|
+
"client": ["devDependencies"]
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"semanticReplacements": {
|
|
58
|
+
"@deepseek-ai/dsh-client-runtime": [
|
|
59
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
60
|
+
"@deepseek-ai/dsh-api-workspace-controller",
|
|
61
|
+
"@deepseek-ai/dsh-client-store",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-session",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-chat",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-trajectory",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-approval",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-user-questions"
|
|
68
|
+
],
|
|
69
|
+
"@deepseek-ai/dsh-host-apiproxy": [
|
|
70
|
+
"@deepseek-ai/dsh-api-gateway",
|
|
71
|
+
"@deepseek-ai/dsh-api-remotes"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"services": {
|
|
3
|
+
"sessions": {
|
|
4
|
+
"owner": "@deepseek-ai/dsh-api-session-controller/client",
|
|
5
|
+
"confidence": "semantic",
|
|
6
|
+
"note": "The service name survives, but Session snapshots no longer contain Conversation views or pending interactions."
|
|
7
|
+
},
|
|
8
|
+
"workspaces": {
|
|
9
|
+
"owner": "@deepseek-ai/dsh-api-workspace-controller/client",
|
|
10
|
+
"confidence": "semantic",
|
|
11
|
+
"note": "The service name survives, while cross-domain navigation belongs to ui-workspace."
|
|
12
|
+
},
|
|
13
|
+
"conversation": {
|
|
14
|
+
"owner": "@deepseek-ai/dsh-client-ui-conversation/client",
|
|
15
|
+
"confidence": "semantic",
|
|
16
|
+
"note": "Conversation assembly and input are independent of Session lifecycle snapshots."
|
|
17
|
+
},
|
|
18
|
+
"slots": {
|
|
19
|
+
"owner": "@deepseek-ai/dsh-client-ui-renderer/client",
|
|
20
|
+
"confidence": "semantic",
|
|
21
|
+
"note": "Slot declarations stay in ui-slots; runtime registration and binding belong to ui-renderer."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"removedHostFacade": {
|
|
25
|
+
"package": "@deepseek-ai/dsh-host-apiproxy",
|
|
26
|
+
"replacementPolicy": "Use the natural business Remote owner through generated api-remotes contributions; there is no replacement facade."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
{
|
|
2
|
+
"modules": {
|
|
3
|
+
"@deepseek-ai/dsh-settings": {
|
|
4
|
+
"settingsNamespace": {
|
|
5
|
+
"toModule": "@deepseek-ai/dsh-settings",
|
|
6
|
+
"toSymbol": "SettingsProvider.register",
|
|
7
|
+
"confidence": "semantic",
|
|
8
|
+
"reason": "The settingsNamespace factory was removed. Inject the settings Service and register the namespace through ctx.settings.register(name, schema)."
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"@deepseek-ai/dsh-client-runtime/client": {
|
|
12
|
+
"ClientContext": {
|
|
13
|
+
"toModule": "@deepseek-ai/cordis",
|
|
14
|
+
"toSymbol": "Context",
|
|
15
|
+
"confidence": "exact",
|
|
16
|
+
"reason": "ClientContext was a type alias of Cordis Context."
|
|
17
|
+
},
|
|
18
|
+
"createSnapshotStore": {
|
|
19
|
+
"toModule": "@deepseek-ai/dsh-client-store",
|
|
20
|
+
"toSymbol": "createSnapshotStore",
|
|
21
|
+
"confidence": "exact",
|
|
22
|
+
"reason": "The React-free store engine moved to client-store."
|
|
23
|
+
},
|
|
24
|
+
"defineStore": {
|
|
25
|
+
"toModule": "@deepseek-ai/dsh-client-store",
|
|
26
|
+
"toSymbol": "defineStore",
|
|
27
|
+
"confidence": "exact",
|
|
28
|
+
"reason": "The declarative store factory moved to client-store."
|
|
29
|
+
},
|
|
30
|
+
"ObservableSnapshot": {
|
|
31
|
+
"toModule": "@deepseek-ai/dsh-client-store",
|
|
32
|
+
"toSymbol": "ObservableSnapshot",
|
|
33
|
+
"confidence": "exact",
|
|
34
|
+
"reason": "The observable store contract moved to client-store."
|
|
35
|
+
},
|
|
36
|
+
"SnapshotStore": {
|
|
37
|
+
"toModule": "@deepseek-ai/dsh-client-store",
|
|
38
|
+
"toSymbol": "SnapshotStore",
|
|
39
|
+
"confidence": "exact",
|
|
40
|
+
"reason": "The writable store contract moved to client-store."
|
|
41
|
+
},
|
|
42
|
+
"shallowEqual": {
|
|
43
|
+
"toModule": "@deepseek-ai/dsh-client-store",
|
|
44
|
+
"toSymbol": "shallowEqual",
|
|
45
|
+
"confidence": "exact",
|
|
46
|
+
"reason": "The store equality helper moved to client-store."
|
|
47
|
+
},
|
|
48
|
+
"SessionId": {
|
|
49
|
+
"toModule": "@deepseek-ai/dsh-session/types",
|
|
50
|
+
"toSymbol": "SessionId",
|
|
51
|
+
"confidence": "exact",
|
|
52
|
+
"reason": "Session identity is owned by the Session domain."
|
|
53
|
+
},
|
|
54
|
+
"ISessions": {
|
|
55
|
+
"toModule": "@deepseek-ai/dsh-api-session-controller/client",
|
|
56
|
+
"toSymbol": "ISessions",
|
|
57
|
+
"confidence": "semantic",
|
|
58
|
+
"reason": "Session ownership moved to the Session Controller and its snapshots changed."
|
|
59
|
+
},
|
|
60
|
+
"Session": {
|
|
61
|
+
"toModule": "@deepseek-ai/dsh-api-session-controller/client",
|
|
62
|
+
"toSymbol": "Session",
|
|
63
|
+
"confidence": "semantic",
|
|
64
|
+
"reason": "The Client Session object and lifecycle moved to the Session Controller."
|
|
65
|
+
},
|
|
66
|
+
"SessionBinding": {
|
|
67
|
+
"toModule": "@deepseek-ai/dsh-api-session-controller/client",
|
|
68
|
+
"toSymbol": "SessionBinding",
|
|
69
|
+
"confidence": "semantic",
|
|
70
|
+
"reason": "Session bindings are now Controller-owned lifetimes."
|
|
71
|
+
},
|
|
72
|
+
"SessionListState": {
|
|
73
|
+
"toModule": "@deepseek-ai/dsh-api-session-controller/client",
|
|
74
|
+
"toSymbol": "SessionListState",
|
|
75
|
+
"confidence": "semantic",
|
|
76
|
+
"reason": "The list state moved and no longer owns Conversation or pending-interaction facts."
|
|
77
|
+
},
|
|
78
|
+
"SessionSummary": {
|
|
79
|
+
"toModule": "@deepseek-ai/dsh-api-session-controller/client",
|
|
80
|
+
"toSymbol": "SessionSummary",
|
|
81
|
+
"confidence": "semantic",
|
|
82
|
+
"reason": "The Session summary is now a Controller-owned control snapshot."
|
|
83
|
+
},
|
|
84
|
+
"UseProjection": {
|
|
85
|
+
"toModule": "@deepseek-ai/dsh-api-session-controller/client",
|
|
86
|
+
"toSymbol": "UseProjection",
|
|
87
|
+
"confidence": "semantic",
|
|
88
|
+
"reason": "Projection sources are now exposed by Session Controller bindings and adapted by ui-session."
|
|
89
|
+
},
|
|
90
|
+
"IWorkspaces": {
|
|
91
|
+
"toModule": "@deepseek-ai/dsh-api-workspace-controller/client",
|
|
92
|
+
"toSymbol": "IWorkspaces",
|
|
93
|
+
"confidence": "semantic",
|
|
94
|
+
"reason": "Workspace ownership moved to the Workspace Controller and navigation policy moved to ui-workspace."
|
|
95
|
+
},
|
|
96
|
+
"WorkspaceId": {
|
|
97
|
+
"toModule": "@deepseek-ai/dsh-api-workspace-controller/client",
|
|
98
|
+
"toSymbol": "WorkspaceId",
|
|
99
|
+
"confidence": "semantic",
|
|
100
|
+
"reason": "Workspace identity is re-exported by the Workspace Controller."
|
|
101
|
+
},
|
|
102
|
+
"WorkspaceView": {
|
|
103
|
+
"toModule": "@deepseek-ai/dsh-api-workspace-controller/client",
|
|
104
|
+
"toSymbol": "WorkspaceView",
|
|
105
|
+
"confidence": "semantic",
|
|
106
|
+
"reason": "Workspace snapshots are Controller-owned and their shape changed."
|
|
107
|
+
},
|
|
108
|
+
"ConversationNode": {
|
|
109
|
+
"toModule": "@deepseek-ai/dsh-client-ui-conversation/client",
|
|
110
|
+
"toSymbol": "ConversationNode",
|
|
111
|
+
"confidence": "semantic",
|
|
112
|
+
"reason": "Conversation assembly moved out of Session snapshots into ui-conversation."
|
|
113
|
+
},
|
|
114
|
+
"ConversationSnapshot": {
|
|
115
|
+
"toModule": "@deepseek-ai/dsh-client-ui-conversation/client",
|
|
116
|
+
"toSymbol": "ConversationSnapshot",
|
|
117
|
+
"confidence": "semantic",
|
|
118
|
+
"reason": "Conversation is now an independent target-neutral source."
|
|
119
|
+
},
|
|
120
|
+
"PendingInteraction": {
|
|
121
|
+
"toModule": "@deepseek-ai/dsh-client-ui-session/client",
|
|
122
|
+
"toSymbol": "SessionPendingInteraction",
|
|
123
|
+
"confidence": "semantic",
|
|
124
|
+
"reason": "Approval and Question now publish domain-owned pending interactions through ui-session."
|
|
125
|
+
},
|
|
126
|
+
"PendingWait": {
|
|
127
|
+
"toModule": "@deepseek-ai/dsh-client-ui-session/client",
|
|
128
|
+
"toSymbol": "SessionPendingInteraction",
|
|
129
|
+
"confidence": "semantic",
|
|
130
|
+
"reason": "The Runtime PendingWait aggregate was removed; pending domains own their payloads."
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dsh-plugin-upgrade
|
|
3
|
-
description: Help diagnose and upgrade a DeepSeek Harness plugin from DSH 0.1.1 to 0.1.2 with dsh-doctor, while deciding explicitly whether the upgraded release must remain compatible with DSH 0.1.1. Use when a plugin developer asks to assess compatibility, identify changed APIs, replace removed dsh-client-runtime or dsh-host-apiproxy usage, update DSH dependencies, modify plugin code, rebuild artifacts, or verify the plugin before releasing a new version. Apply only catalog-confirmed exact rewrites automatically, guide semantic code changes, and report what still needs developer verification. The current
|
|
3
|
+
description: Help diagnose and upgrade a DeepSeek Harness plugin from DSH 0.1.1 to 0.1.2 with dsh-doctor, while deciding explicitly whether the upgraded release must remain compatible with DSH 0.1.1. Use when a plugin developer asks to assess compatibility, identify changed APIs, replace removed dsh-client-runtime or dsh-host-apiproxy usage, update DSH dependencies, modify plugin code, rebuild artifacts, or verify the plugin before releasing a new version. Apply only catalog-confirmed exact rewrites automatically, guide semantic code changes, and report what still needs developer verification. The current known target reference is dsh-v0.1.2-alpha.3.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Upgrade a DSH 0.1.1 plugin to 0.1.2
|
|
7
7
|
|
|
8
8
|
Help the developer diagnose and modify one plugin at a time. Treat source migration, artifact verification, runtime activation, and business behavior as separate gates.
|
|
9
9
|
|
|
10
|
-
The
|
|
10
|
+
The migration knowledge covers the DSH 0.1.1 to 0.1.2 transition. The current known catalog records `dsh-v0.1.1-rc.2` and `dsh-v0.1.2-alpha.3` as the exact reference points used to derive and verify known changes. Keep the skill's product scope at the release-line level (`0.1.1` to `0.1.2`), while keeping the catalog target exact. First record the plugin's actual DSH ranges and requested target. When a newer patch or prerelease appears, use the catalog for known changes only, inspect the additional interval, and report that it is not catalog-confirmed until the catalog and this skill are deliberately updated. Do not claim that the catalog proves an unlisted version combination.
|
|
11
11
|
|
|
12
12
|
## Inputs
|
|
13
13
|
|
|
@@ -24,13 +24,15 @@ Determine:
|
|
|
24
24
|
Use these catalog reference points in the current CLI commands:
|
|
25
25
|
|
|
26
26
|
- source: `dsh-v0.1.1-rc.2`
|
|
27
|
-
- target: `dsh-v0.1.2-alpha.
|
|
27
|
+
- target: `dsh-v0.1.2-alpha.3`
|
|
28
|
+
|
|
29
|
+
The standard commands target the current known alpha.3 catalog and do not need `--target-version`. If the developer explicitly targets a newer 0.1.2 build before the catalog is updated, inspect the interval first, then pass its exact version to every migration phase as `--target-version <actual-0.1.2-version>`. This changes dependency pins and runtime expectations only; it does not extend the catalog's API claims.
|
|
28
30
|
|
|
29
31
|
Read [cli-bootstrap.md](references/cli-bootstrap.md) before running any migration command, [compatibility-strategy.md](references/compatibility-strategy.md) before proposing or writing changes, [source-investigation.md](references/source-investigation.md) before investigating an unlisted version or semantic task, [migration-map.md](references/migration-map.md) before making semantic changes, and [verification.md](references/verification.md) before dependency, build, or runtime verification.
|
|
30
32
|
|
|
31
|
-
Before touching the plugin, inspect the local CLI and perform the read-only registry update check described in `cli-bootstrap.md`. Select one exact DSH Doctor version, verify it exposes this migration catalog, and keep the same invocation for analyze, apply, and verify. Prefer
|
|
33
|
+
Before touching the plugin, inspect the local CLI and perform the read-only registry update check described in `cli-bootstrap.md`. Select one exact DSH Doctor version, verify it exposes this migration catalog, and keep the same invocation for analyze, apply, and verify. Prefer the exact-version `npm exec` fallback over changing the developer's global installation. Never globally install or update the CLI without explicit authorization.
|
|
32
34
|
|
|
33
|
-
The examples below use `dsh-doctor` for readability. When bootstrap selected
|
|
35
|
+
The examples below use `dsh-doctor` for readability. When bootstrap selected a package-runner invocation, substitute the complete pinned prefix, `npm exec --yes --package=@bruc3van/dsh-doctor@<selected-version> -- dsh-doctor`, in every phase.
|
|
34
36
|
|
|
35
37
|
## Compatibility decision gate
|
|
36
38
|
|
|
@@ -53,7 +55,7 @@ Run from any directory:
|
|
|
53
55
|
```sh
|
|
54
56
|
dsh-doctor migrate analyze <plugin-root> \
|
|
55
57
|
--from dsh-v0.1.1-rc.2 \
|
|
56
|
-
--to dsh-v0.1.2-alpha.
|
|
58
|
+
--to dsh-v0.1.2-alpha.3 \
|
|
57
59
|
--json
|
|
58
60
|
```
|
|
59
61
|
|
|
@@ -61,6 +63,10 @@ Append `--harness-root <deepseek-harness-root>` when the checkout is available.
|
|
|
61
63
|
|
|
62
64
|
Follow `source-investigation.md` to record the actual PATH or explicit DSH command, installed package, profile, plugin manifest and resolved dependency versions. When the actual source or target differs from the catalog refs, inspect that additional ref interval separately with read-only Git commands. For each semantic finding, use its target module and the catalog reference paths to inspect the exact exported API and plugin callers; do not stop at naming a likely new owner.
|
|
63
65
|
|
|
66
|
+
The catalog reports the retained-package removal of `@deepseek-ai/dsh-settings.settingsNamespace` as semantic work. Remove only that named import, inject the `settings` Service, register through `ctx.settings.register(name, schema)`, and migrate any reads to the provider's current API. Do not remove still-exported symbols such as `SettingsConflictError` merely because they share the same import declaration.
|
|
67
|
+
|
|
68
|
+
The static analyzer recognizes named imports and re-exports for retained-package symbol removals, but it does not resolve property access through namespace imports such as `import * as settings from '@deepseek-ai/dsh-settings'`. Search for and inspect those namespace bindings manually before declaring the semantic migration complete.
|
|
69
|
+
|
|
64
70
|
Group the result by:
|
|
65
71
|
|
|
66
72
|
1. errors blocking compatibility;
|
|
@@ -102,7 +108,7 @@ Run the gates in order:
|
|
|
102
108
|
```sh
|
|
103
109
|
dsh-doctor migrate verify <plugin-root> --level static --harness-root <deepseek-harness-root> --json
|
|
104
110
|
dsh-doctor migrate verify <plugin-root> --level build --yes --install --harness-root <deepseek-harness-root> --json
|
|
105
|
-
dsh-doctor migrate verify <plugin-root> --level runtime --yes --install --
|
|
111
|
+
dsh-doctor migrate verify <plugin-root> --level runtime --yes --install --dsh-command <actual-dsh-command> --json
|
|
106
112
|
```
|
|
107
113
|
|
|
108
114
|
Build and runtime levels require `--yes --install`: the CLI first runs the detected package manager with lifecycle scripts disabled, updates the lockfile when needed, and verifies the installed DSH/Cordis versions against runtime, development, peer, and optional dependency declarations before executing project scripts. A required peer must resolve and satisfy every declared range; a missing optional peer is recorded but does not fail the gate. Review lockfile changes as migration changes. Runtime verification then packs the real plugin, creates a temporary `DSH_HOME`, installs into a new web profile, dumps effective config, and performs an activation smoke. It does not modify the developer's normal `~/.dsh`.
|
|
@@ -119,6 +125,8 @@ For a dual-version result, verify the produced release against both the actual 0
|
|
|
119
125
|
|
|
120
126
|
Verification prepares the plugin for release but does not publish it. If the developer explicitly asks to release the upgraded plugin, first follow the repository's own release instructions and confirm that required semantic and behavior checks are complete. Then update the plugin version and changelog, inspect the packed artifact, and use the repository's existing commit, tag, publish, and registry-verification workflow. Do not commit, tag, or publish merely because the migration skill was installed or run.
|
|
121
127
|
|
|
128
|
+
For a `0.1.2-only` release, update the README or compatibility documentation to state the minimum actual 0.1.2 version, that the new release does not support 0.1.1, and which prior plugin release 0.1.1 users should retain when known. After changing the release version or any other packed metadata, rebuild, inspect the final-version tarball, and repeat the isolated runtime gate against that artifact before committing or tagging; evidence from a tarball carrying the previous plugin version is not final release evidence.
|
|
129
|
+
|
|
122
130
|
## Report the outcome
|
|
123
131
|
|
|
124
132
|
State the highest achieved gate exactly:
|
|
@@ -11,7 +11,7 @@ dsh-doctor --version
|
|
|
11
11
|
dsh-doctor migrations list --json
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Record the local version and whether the exact `dsh-v0.1.1-rc.2` to `dsh-v0.1.2-alpha.
|
|
14
|
+
Record the local version and whether the exact current `dsh-v0.1.1-rc.2` to `dsh-v0.1.2-alpha.3` catalog is present. A command that exists but lacks this catalog is not usable for the current known target.
|
|
15
15
|
|
|
16
16
|
## 2. Check the registry without changing the machine
|
|
17
17
|
|
|
@@ -26,12 +26,14 @@ This is a read-only update check. If it fails because the registry is unavailabl
|
|
|
26
26
|
## 3. Select and pin one invocation
|
|
27
27
|
|
|
28
28
|
- When the local version equals the registry version and exposes the catalog, use `dsh-doctor` directly.
|
|
29
|
-
- When the local CLI is missing, differs from the registry version, or lacks the catalog, prefer the registry version through exact-version
|
|
29
|
+
- When the local CLI is missing, differs from the registry version, or lacks the catalog, prefer the registry version through an exact-version package runner. First verify that version exposes the required catalog:
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
|
-
|
|
32
|
+
npm exec --yes --package=@bruc3van/dsh-doctor@<registry-version> -- dsh-doctor migrations list --json
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
This `npm exec ... -- dsh-doctor` form is the preferred cross-platform invocation. On Windows, `npx --package=... dsh-doctor` can fail to resolve the temporary binary when launched from an unrelated project root. A direct `npx --yes @bruc3van/dsh-doctor@<version> ...` smoke from a fresh temporary directory is also acceptable, but keep one verified invocation for the whole migration.
|
|
36
|
+
|
|
35
37
|
- When the registry is unavailable, use the local CLI only if its catalog check passed.
|
|
36
38
|
- When neither candidate exposes the catalog, stop and report the missing prerequisite instead of guessing a compatible CLI.
|
|
37
39
|
- Respect an explicit developer request to use a particular local or development CLI, but still record its version and verify the catalog.
|
|
@@ -54,6 +56,6 @@ Include these fields in the handoff:
|
|
|
54
56
|
|
|
55
57
|
- local CLI version or `missing`;
|
|
56
58
|
- registry version or `unknown`;
|
|
57
|
-
- selected exact version and invocation source (`local`, `
|
|
59
|
+
- selected exact version and invocation source (`local`, `npm-exec`, or authorized `global-install`);
|
|
58
60
|
- required catalog present or absent;
|
|
59
61
|
- update status: `current`, `outdated`, `missing`, or `unknown`.
|
|
@@ -19,6 +19,10 @@ Session snapshots no longer own Conversation views or all pending-interaction fa
|
|
|
19
19
|
|
|
20
20
|
`@deepseek-ai/dsh-host-apiproxy` was removed. Browser operations use their natural generated Remote owners through API Remotes/API Gateway contributions. There is no facade-compatible package substitution.
|
|
21
21
|
|
|
22
|
+
## Settings registration
|
|
23
|
+
|
|
24
|
+
`@deepseek-ai/dsh-settings` remains, but its `settingsNamespace` factory export was removed in 0.1.2-alpha.2. This is a semantic Service migration, not an import move: inject the `settings` Service and register the plugin namespace through `ctx.settings.register(name, schema)`. Keep `SettingsConflictError` or other exports that still exist, and verify every namespace read path against the provider API (for example `describe`) instead of assuming the former factory object's methods still exist.
|
|
25
|
+
|
|
22
26
|
## Client graph
|
|
23
27
|
|
|
24
28
|
- `dsh.client.inject` declares package dependency edges; Cordis service injection still controls activation.
|
|
@@ -39,4 +43,4 @@ Do not copy a replacement package into every dependency section that contained `
|
|
|
39
43
|
|
|
40
44
|
For `MIG_PATCH_TARGET_CHANGED`, first confirm the Harness checkout is exact and its tag scan succeeded. Then compare the old and target web profile bundle patches: remove an obsolete override only when the old row disappeared without a replacement, or update the id when upstream deliberately renamed/moved the row. Do not guess a replacement id from a similar name.
|
|
41
45
|
|
|
42
|
-
The machine-readable source of truth ships with DSH Doctor. Confirm the installed CLI exposes
|
|
46
|
+
The machine-readable source of truth ships with DSH Doctor. Confirm the installed CLI exposes the current exact version pair with `dsh-doctor migrations list`; the canonical catalog source is also available in the [DSH Doctor repository](https://github.com/bruc3van/dsh-doctor/tree/master/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.3). The older alpha.2 catalog remains historical evidence, not the current default.
|
|
@@ -70,6 +70,8 @@ Classify every additional difference as:
|
|
|
70
70
|
|
|
71
71
|
Only the catalog-confirmed pair is eligible for the built-in safe codemod. Treat additional exact-looking changes as proposed manual edits until independently reviewed and tested.
|
|
72
72
|
|
|
73
|
+
After the additional interval is reviewed, pass that exact version as `--target-version` in analyze, apply preview/apply, and verify. This is an explicit fallback for a newer 0.1.2 build that the current alpha.3 catalog does not yet cover. It changes DSH dependency-range validation, deterministic DSH development pins, and the runtime version expectation; it does not turn the unlisted interval into catalog-confirmed API knowledge. Use `--dsh-command` to name the executable that actually reports and runs that version, and update the catalog and skill deliberately when that build becomes the new known target.
|
|
74
|
+
|
|
73
75
|
## 4. Investigate each semantic task in the plugin
|
|
74
76
|
|
|
75
77
|
Start from the finding's `file`, `symbol`, `targetModule`, `targetSymbol`, and `reason`. Then:
|
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
Static analysis covers source/type imports, dependency ranges, client graph declarations, and generated artifacts. A pass means no known blocking finding; it does not execute project code.
|
|
6
6
|
|
|
7
|
+
The current known static target is alpha.3. When the actual target is later than that catalog ref on the same 0.1.2 release line, first inspect the additional interval, then use `--target-version <version>` at every phase. The report keeps `migration.to` as the catalog ref and records `migration.actualTarget` separately, so a successful dependency or runtime check cannot be mistaken for catalog coverage of the additional API delta.
|
|
8
|
+
|
|
7
9
|
## Build
|
|
8
10
|
|
|
9
11
|
Build and runtime verification require `--yes --install`. The CLI selects the package manager from lockfiles, synchronizes dependencies with dependency lifecycle scripts disabled, records the lockfile hash before and after, and verifies installed DSH/Cordis versions against `dependencies`, `devDependencies`, `peerDependencies`, and `optionalDependencies`. Required peers must resolve and satisfy every declared range. Missing optional dependencies or peers are recorded as `optional-missing`; when installed, they must satisfy their ranges. It then runs declared scripts in this order: `typecheck`, `build`, `test`, `pack:check`, stopping at the first failure. Artifact verification requires a successful `build` or `pack:check`; `typecheck`/`test` alone do not pass this gate.
|
|
10
12
|
|
|
11
13
|
If dependency installation or resolution verification fails, stop before running project scripts. Review and retain package manifest and lockfile changes with the source migration.
|
|
12
14
|
|
|
15
|
+
If pnpm's `minimumReleaseAge` blocks a newly published target prerelease, do not disable or weaken the repository-wide supply-chain policy. Confirm the selected target and registry provenance, then add only the exact `package@version` rows required under `minimumReleaseAgeExclude`, including transitive DSH packages reported by pnpm. Review this workspace-policy change alongside the lockfile and report it explicitly.
|
|
16
|
+
|
|
13
17
|
## Runtime
|
|
14
18
|
|
|
15
19
|
Runtime verification:
|
|
@@ -24,6 +28,8 @@ The gate also checks the DSH CLI's exact target version, the created profile man
|
|
|
24
28
|
|
|
25
29
|
Use a built target Harness CLI through `--harness-root`, or pass `--dsh-command` explicitly. The temporary home is deleted only after a successful run unless `--keep-temp` is set.
|
|
26
30
|
|
|
31
|
+
The runtime executable must report the exact actual target selected by `--target-version` (or the catalog target when the option is omitted). A later prerelease is not accepted implicitly because that would disconnect the runtime evidence from the reviewed dependency and source target.
|
|
32
|
+
|
|
27
33
|
## Behavior
|
|
28
34
|
|
|
29
35
|
Choose focused checks from the plugin contract: render the real UI, invoke each command, exercise settings, verify service availability, repeat activation/deactivation, check listener cleanup, and cover failure recovery. Record exact commands, screenshots, logs, or tests. This gate is deliberately not inferred by dsh-doctor.
|
package/src/cli.mjs
CHANGED
|
@@ -43,7 +43,8 @@ Options:
|
|
|
43
43
|
--fix, --repair legacy confirmed repairs (never removes a plugin)
|
|
44
44
|
--yes confirm an explicit write or command
|
|
45
45
|
--from <ref> source DSH ref (default: dsh-v0.1.1-rc.2)
|
|
46
|
-
--to <ref> target DSH ref (default: dsh-v0.1.2-alpha.
|
|
46
|
+
--to <ref> target DSH ref (default: dsh-v0.1.2-alpha.3)
|
|
47
|
+
--target-version <ver> actual DSH 0.1.2 version for dependency/runtime checks
|
|
47
48
|
--safe restrict migrate apply to catalog-confirmed exact rewrites
|
|
48
49
|
--plan-file <path> persist or consume the reviewed migrate apply plan
|
|
49
50
|
--level <level> static, build, or runtime verification
|
|
@@ -85,7 +86,8 @@ const HELP_ZH = `用法:
|
|
|
85
86
|
--fix, --repair 旧式确认修复(绝不移除插件)
|
|
86
87
|
--yes 确认一个明确的写入或命令动作
|
|
87
88
|
--from <ref> 源 DSH ref(默认:dsh-v0.1.1-rc.2)
|
|
88
|
-
--to <ref> 目标 DSH ref(默认:dsh-v0.1.2-alpha.
|
|
89
|
+
--to <ref> 目标 DSH ref(默认:dsh-v0.1.2-alpha.3)
|
|
90
|
+
--target-version <版本> 依赖与 runtime 验证使用的实际 DSH 0.1.2 版本
|
|
89
91
|
--safe migrate apply 仅执行 catalog 确认的精确改写
|
|
90
92
|
--plan-file <路径> 保存或读取已审阅的 migrate apply 计划
|
|
91
93
|
--level <级别> static、build 或 runtime
|
|
@@ -146,6 +148,8 @@ function parse(args) {
|
|
|
146
148
|
else if (arg.startsWith('--from=')) options.from = optionValue(arg, '--from')
|
|
147
149
|
else if (arg === '--to') options.to = valueAfter(args, index++, arg)
|
|
148
150
|
else if (arg.startsWith('--to=')) options.to = optionValue(arg, '--to')
|
|
151
|
+
else if (arg === '--target-version') options.targetVersion = valueAfter(args, index++, arg)
|
|
152
|
+
else if (arg.startsWith('--target-version=')) options.targetVersion = optionValue(arg, '--target-version')
|
|
149
153
|
else if (arg === '--safe') options.safe = true
|
|
150
154
|
else if (arg === '--plan-file') options.planFile = valueAfter(args, index++, arg)
|
|
151
155
|
else if (arg.startsWith('--plan-file=')) options.planFile = optionValue(arg, '--plan-file')
|
|
@@ -300,7 +304,7 @@ async function main() {
|
|
|
300
304
|
if (!['analyze', 'apply', 'verify'].includes(options.migrateAction)) throw new Error('migrate needs analyze, apply, or verify')
|
|
301
305
|
if (options.migrateAction === 'apply' && options.safe !== true) throw new Error('migrate apply requires --safe')
|
|
302
306
|
if (options.migrateAction === 'apply' && options.pluginRoot === undefined) throw new Error('migrate apply requires an explicit plugin root')
|
|
303
|
-
const migrationOptions = { from: options.from, to: options.to, harnessRoot: options.harnessRoot, dshCommand: options.dshCommand, level: options.level, yes: options.yes, safe: options.safe, keepTemp: options.keepTemp, planFile: options.planFile, install: options.install }
|
|
307
|
+
const migrationOptions = { from: options.from, to: options.to, targetVersion: options.targetVersion, harnessRoot: options.harnessRoot, dshCommand: options.dshCommand, level: options.level, yes: options.yes, safe: options.safe, keepTemp: options.keepTemp, planFile: options.planFile, install: options.install }
|
|
304
308
|
if (options.migrateAction === 'verify') {
|
|
305
309
|
const { formatVerification, verifyMigration } = await import('./migrate-verify.mjs')
|
|
306
310
|
const result = verifyMigration(options.pluginRoot, migrationOptions)
|
package/src/migrate-verify.mjs
CHANGED
|
@@ -214,7 +214,8 @@ export function verifyMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
214
214
|
const [versionCommand, versionArgs] = dshInvocation(dsh, ['--version'])
|
|
215
215
|
const versionRun = commandResult(versionCommand, versionArgs, { cwd: root, env })
|
|
216
216
|
const actualDshVersion = versionRun.stdout.trim()
|
|
217
|
-
const
|
|
217
|
+
const expectedDshVersion = analysis.migration.actualTarget.version
|
|
218
|
+
const versionPassed = versionRun.passed && actualDshVersion === expectedDshVersion
|
|
218
219
|
const pack = commandResult(manager.command, manager.pack(packDir), { cwd: root })
|
|
219
220
|
const tarball = tarballFromPack(pack, packDir)
|
|
220
221
|
const runtimeCommands = []
|
|
@@ -236,7 +237,7 @@ export function verifyMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
236
237
|
result.stages.push({
|
|
237
238
|
name: 'runtime',
|
|
238
239
|
passed: runtimePassed,
|
|
239
|
-
dshVersion: { expected: analysis.migration.to.version, actual: actualDshVersion, passed: versionPassed, command: commandEvidence(versionRun) },
|
|
240
|
+
dshVersion: { expected: expectedDshVersion, catalog: analysis.migration.to.version, actual: actualDshVersion, passed: versionPassed, command: commandEvidence(versionRun) },
|
|
240
241
|
pack: commandEvidence(pack),
|
|
241
242
|
tarball,
|
|
242
243
|
commands: runtimeCommands.map(commandEvidence),
|
package/src/migrate.mjs
CHANGED
|
@@ -80,12 +80,17 @@ function moduleLiteral(node) {
|
|
|
80
80
|
|
|
81
81
|
function namedImports(node) {
|
|
82
82
|
const bindings = node.importClause?.namedBindings
|
|
83
|
-
if (
|
|
84
|
-
return bindings.elements.map(item => ({
|
|
83
|
+
if (bindings && ts.isNamedImports(bindings)) return bindings.elements.map(item => ({
|
|
85
84
|
imported: item.propertyName?.text ?? item.name.text,
|
|
86
85
|
local: item.name.text,
|
|
87
86
|
typeOnly: node.importClause.isTypeOnly || item.isTypeOnly,
|
|
88
87
|
}))
|
|
88
|
+
if (ts.isExportDeclaration(node) && node.exportClause && ts.isNamedExports(node.exportClause)) return node.exportClause.elements.map(item => ({
|
|
89
|
+
imported: item.propertyName?.text ?? item.name.text,
|
|
90
|
+
local: item.name.text,
|
|
91
|
+
typeOnly: node.isTypeOnly || item.isTypeOnly,
|
|
92
|
+
}))
|
|
93
|
+
return []
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
function quoteModule(text) {
|
|
@@ -117,10 +122,11 @@ function analyzeFile(file, pluginRoot, catalog, origin) {
|
|
|
117
122
|
handledLiterals.add(literal)
|
|
118
123
|
const moduleName = literal.text
|
|
119
124
|
const rootName = packageRoot(moduleName)
|
|
120
|
-
|
|
125
|
+
const moduleRemoved = removed.has(rootName)
|
|
126
|
+
const symbolRules = catalog.symbols.modules[moduleName]
|
|
127
|
+
if (moduleRemoved || symbolRules !== undefined) {
|
|
121
128
|
const where = location(source, literal, pluginRoot)
|
|
122
|
-
const
|
|
123
|
-
const specs = ts.isImportDeclaration(node) ? namedImports(node) : []
|
|
129
|
+
const specs = namedImports(node)
|
|
124
130
|
const exact = []
|
|
125
131
|
const remaining = []
|
|
126
132
|
for (const spec of specs) {
|
|
@@ -128,7 +134,7 @@ function analyzeFile(file, pluginRoot, catalog, origin) {
|
|
|
128
134
|
if (rule?.confidence === 'exact') {
|
|
129
135
|
exact.push({ ...spec, imported: rule.toSymbol, toModule: rule.toModule, fromSymbol: spec.imported, reason: rule.reason })
|
|
130
136
|
findings.push({ code: 'MIG_MOVED_SYMBOL', severity: 'error', message: `${spec.imported} moved to ${rule.toModule}`, location: where, evidence: { module: moduleName, symbol: spec.imported, targetModule: rule.toModule, replacement: rule.toSymbol }, autoFix: 'safe' })
|
|
131
|
-
} else {
|
|
137
|
+
} else if (rule?.confidence === 'semantic' || moduleRemoved) {
|
|
132
138
|
remaining.push(spec)
|
|
133
139
|
const semantic = rule?.confidence === 'semantic'
|
|
134
140
|
findings.push({ code: semantic ? 'MIG_SEMANTIC_API_CHANGE' : 'MIG_REMOVED_PACKAGE_REFERENCE', severity: 'error', message: semantic ? `${spec.imported} requires a semantic migration: ${rule.reason}` : `${moduleName} was removed without a safe automatic replacement`, location: where, evidence: { module: moduleName, ...(spec.imported ? { symbol: spec.imported } : {}) }, autoFix: 'none' })
|
|
@@ -140,7 +146,7 @@ function analyzeFile(file, pluginRoot, catalog, origin) {
|
|
|
140
146
|
...(rule?.toSymbol ? { targetSymbol: rule.toSymbol } : {}),
|
|
141
147
|
reason: rule?.reason ?? 'No exact replacement is known.',
|
|
142
148
|
})
|
|
143
|
-
}
|
|
149
|
+
} else remaining.push(spec)
|
|
144
150
|
}
|
|
145
151
|
const declarationText = ts.isImportDeclaration(node) ? text.slice(node.getStart(source), node.getEnd()) : ''
|
|
146
152
|
const simpleNamedImport = exact.length > 0 && ts.isImportDeclaration(node) && node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings) && node.importClause.name === undefined && node.attributes === undefined && node.assertClause === undefined && !/\/(?:\/|\*)/.test(declarationText)
|
|
@@ -160,7 +166,7 @@ function analyzeFile(file, pluginRoot, catalog, origin) {
|
|
|
160
166
|
const generated = [...groups].map(([target, items]) => importText(target, items, node.importClause.isTypeOnly))
|
|
161
167
|
if (remaining.length > 0) generated.unshift(importText(moduleName, remaining, node.importClause.isTypeOnly))
|
|
162
168
|
replacements.push({ start: node.getStart(source), end: node.getEnd(), next: generated.join('\n'), exact })
|
|
163
|
-
} else if (specs.length === 0 || exact.length > 0) {
|
|
169
|
+
} else if (moduleRemoved && (specs.length === 0 || exact.length > 0)) {
|
|
164
170
|
for (const finding of findings) if (finding.code === 'MIG_MOVED_SYMBOL' && finding.location.line === where.line && exact.some(item => item.fromSymbol === finding.evidence.symbol)) finding.autoFix = 'none'
|
|
165
171
|
findings.push({ code: 'MIG_REMOVED_PACKAGE_REFERENCE', severity: 'error', message: `${moduleName} was removed and this reference cannot be rewritten safely`, location: where, evidence: { module: moduleName, kind: ts.SyntaxKind[node.kind] }, autoFix: 'none' })
|
|
166
172
|
unresolved.push({ file: where.file, package: rootName, module: moduleName, reason: 'Default, namespace, side-effect, export, require, dynamic import, or module augmentation reference.' })
|
|
@@ -189,12 +195,28 @@ function readManifest(pluginRoot) {
|
|
|
189
195
|
return { file, value, text }
|
|
190
196
|
}
|
|
191
197
|
|
|
192
|
-
function
|
|
198
|
+
function actualTargetVersion(catalog, requested) {
|
|
199
|
+
if (requested === undefined) return catalog.manifest.to.version
|
|
200
|
+
const version = semver.valid(requested)
|
|
201
|
+
if (version === null) throw new Error(`--target-version must be an exact semantic version, received ${requested}`)
|
|
202
|
+
const catalogVersion = semver.parse(catalog.manifest.to.version)
|
|
203
|
+
const target = semver.parse(version)
|
|
204
|
+
if (catalogVersion === null || target === null || target.major !== catalogVersion.major || target.minor !== catalogVersion.minor || target.patch !== catalogVersion.patch) {
|
|
205
|
+
throw new Error(`--target-version ${requested} is outside the catalog's ${catalogVersion?.major}.${catalogVersion?.minor}.${catalogVersion?.patch} release line`)
|
|
206
|
+
}
|
|
207
|
+
if (semver.lt(target, catalogVersion)) {
|
|
208
|
+
throw new Error(`--target-version ${requested} predates the catalog target ${catalog.manifest.to.version}`)
|
|
209
|
+
}
|
|
210
|
+
return version
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function targetVersion(name, catalog, actualTarget) {
|
|
214
|
+
if (name.startsWith('@deepseek-ai/dsh-')) return actualTarget
|
|
193
215
|
if (catalog.packages.targetVersions?.[name] !== undefined) return catalog.packages.targetVersions[name]
|
|
194
|
-
return
|
|
216
|
+
return undefined
|
|
195
217
|
}
|
|
196
218
|
|
|
197
|
-
function manifestFindings(manifest, catalog) {
|
|
219
|
+
function manifestFindings(manifest, catalog, actualTarget) {
|
|
198
220
|
const findings = []
|
|
199
221
|
for (const field of DEPENDENCY_FIELDS) {
|
|
200
222
|
const dependencyMap = manifest.value[field]
|
|
@@ -207,7 +229,7 @@ function manifestFindings(manifest, catalog) {
|
|
|
207
229
|
findings.push({ code: 'MIG_REMOVED_PACKAGE_REFERENCE', severity: 'error', message: `${field}.${name} targets a removed package`, location: { file: 'package.json' }, evidence: { field, package: name, range }, autoFix: 'conditional' })
|
|
208
230
|
continue
|
|
209
231
|
}
|
|
210
|
-
const target = targetVersion(name, catalog)
|
|
232
|
+
const target = targetVersion(name, catalog, actualTarget)
|
|
211
233
|
if (target === undefined) continue
|
|
212
234
|
const validRange = typeof range === 'string' ? semver.validRange(range) : null
|
|
213
235
|
if (validRange === null) findings.push({ code: 'MIG_INVALID_DEPENDENCY_RANGE', severity: 'error', message: `${field}.${name} has a non-registry range that cannot prove target compatibility`, location: { file: 'package.json' }, evidence: { field, package: name, range, target }, autoFix: field === 'devDependencies' ? 'safe' : 'none' })
|
|
@@ -307,7 +329,7 @@ function patchTargetFindings(pluginRoot, manifest, harness) {
|
|
|
307
329
|
return findings
|
|
308
330
|
}
|
|
309
331
|
|
|
310
|
-
function planManifest(manifest, sourceResults, artifactResults, opaqueReferences, catalog) {
|
|
332
|
+
function planManifest(manifest, sourceResults, artifactResults, opaqueReferences, catalog, actualTarget) {
|
|
311
333
|
const next = structuredClone(manifest.value)
|
|
312
334
|
const exactTargets = sourceResults.flatMap(result => result.exactTargets)
|
|
313
335
|
const sourceReferences = new Set(sourceResults.flatMap(result => result.remainingPackages))
|
|
@@ -323,14 +345,14 @@ function planManifest(manifest, sourceResults, artifactResults, opaqueReferences
|
|
|
323
345
|
}
|
|
324
346
|
if (field === 'devDependencies') {
|
|
325
347
|
for (const [name, range] of Object.entries(deps)) {
|
|
326
|
-
const target = targetVersion(name, catalog)
|
|
348
|
+
const target = targetVersion(name, catalog, actualTarget)
|
|
327
349
|
if (target !== undefined && !catalog.packages.removed.includes(name) && (semver.validRange(range) === null || !semver.satisfies(target, range))) deps[name] = target
|
|
328
350
|
}
|
|
329
351
|
}
|
|
330
352
|
}
|
|
331
353
|
for (const move of new Map(exactTargets.map(item => [`${item.relationship}\0${item.toPackage}`, item])).values()) {
|
|
332
354
|
const fields = catalog.packages.dependencyPolicies?.[move.toPackage]?.[move.relationship]
|
|
333
|
-
const version = targetVersion(move.toPackage, catalog)
|
|
355
|
+
const version = targetVersion(move.toPackage, catalog, actualTarget)
|
|
334
356
|
if (!Array.isArray(fields) || version === undefined) continue
|
|
335
357
|
for (const field of fields) {
|
|
336
358
|
if (!DEPENDENCY_FIELDS.includes(field)) throw new Error(`migration catalog has an invalid dependency field ${field}`)
|
|
@@ -369,7 +391,8 @@ function analysisInputs(root) {
|
|
|
369
391
|
export function analyzeMigration(pluginRoot = process.cwd(), options = {}) {
|
|
370
392
|
const root = resolve(pluginRoot)
|
|
371
393
|
if (!statSync(root).isDirectory()) throw new Error(`plugin root is not a directory: ${root}`)
|
|
372
|
-
const catalog = loadMigration(options.from ?? 'dsh-v0.1.1-rc.2', options.to ?? 'dsh-v0.1.2-alpha.
|
|
394
|
+
const catalog = loadMigration(options.from ?? 'dsh-v0.1.1-rc.2', options.to ?? 'dsh-v0.1.2-alpha.3')
|
|
395
|
+
const actualTarget = actualTargetVersion(catalog, options.targetVersion)
|
|
373
396
|
const harness = verifyHarnessCheckout(catalog, options.harnessRoot)
|
|
374
397
|
const manifest = readManifest(root)
|
|
375
398
|
const sourceFiles = walk(root, SOURCE_DIRS_TO_SKIP).filter(file => !isTopLevelArtifact(root, file))
|
|
@@ -378,10 +401,10 @@ export function analyzeMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
378
401
|
const opaqueReferences = opaqueReferenceFindings(root, sourceFiles, manifest, catalog)
|
|
379
402
|
const sourceRemoved = sources.some(item => item.findings.some(finding => ['MIG_REMOVED_PACKAGE_REFERENCE', 'MIG_SEMANTIC_API_CHANGE', 'MIG_MOVED_SYMBOL'].includes(finding.code)))
|
|
380
403
|
const artifactRemoved = artifacts.some(item => item.findings.some(finding => ['MIG_REMOVED_PACKAGE_REFERENCE', 'MIG_SEMANTIC_API_CHANGE', 'MIG_MOVED_SYMBOL'].includes(finding.code)))
|
|
381
|
-
const findings = [...manifestFindings(manifest, catalog), ...patchTargetFindings(root, manifest, harness), ...sources.flatMap(item => item.findings), ...opaqueReferences.findings, ...artifacts.flatMap(item => item.findings)]
|
|
404
|
+
const findings = [...manifestFindings(manifest, catalog, actualTarget), ...patchTargetFindings(root, manifest, harness), ...sources.flatMap(item => item.findings), ...opaqueReferences.findings, ...artifacts.flatMap(item => item.findings)]
|
|
382
405
|
if (!sourceRemoved && artifactRemoved) findings.push({ code: 'MIG_SOURCE_ARTIFACT_DRIFT', severity: 'error', message: 'built artifacts still reference removed APIs although source files do not', location: { file: '.' }, evidence: { artifactDirectories: ARTIFACT_DIRS }, autoFix: 'none' })
|
|
383
406
|
findings.push({ code: 'MIG_RUNTIME_VERIFICATION_REQUIRED', severity: 'info', message: 'static analysis cannot prove activation and lifecycle behavior; run migrate verify --level runtime', location: { file: '.' }, evidence: {}, autoFix: 'none' })
|
|
384
|
-
const manifestPlan = planManifest(manifest, sources, artifacts, opaqueReferences.packages, catalog)
|
|
407
|
+
const manifestPlan = planManifest(manifest, sources, artifacts, opaqueReferences.packages, catalog, actualTarget)
|
|
385
408
|
const changed = [...sources.filter(item => item.changed), ...(manifestPlan.changed ? [manifestPlan] : [])]
|
|
386
409
|
const safeEdits = changed.map(item => ({
|
|
387
410
|
file: relativePath(root, item.file),
|
|
@@ -393,14 +416,21 @@ export function analyzeMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
393
416
|
return {
|
|
394
417
|
schemaVersion: 1,
|
|
395
418
|
command: 'migrate analyze',
|
|
396
|
-
migration: {
|
|
419
|
+
migration: {
|
|
420
|
+
id: catalog.manifest.id,
|
|
421
|
+
from: catalog.manifest.from,
|
|
422
|
+
to: catalog.manifest.to,
|
|
423
|
+
actualTarget: { version: actualTarget, catalogVersion: catalog.manifest.to.version, catalogExact: actualTarget === catalog.manifest.to.version },
|
|
424
|
+
references: catalog.manifest.references ?? {},
|
|
425
|
+
harness,
|
|
426
|
+
},
|
|
397
427
|
plugin: { root, name: manifest.value.name ?? basename(root), version: manifest.value.version ?? 'unknown', manifestFile: manifest.file, packageManager: existsSync(join(root, 'pnpm-lock.yaml')) ? 'pnpm' : existsSync(join(root, 'yarn.lock')) ? 'yarn' : 'npm' },
|
|
398
428
|
summary: summarize(findings, safeEdits, unresolved),
|
|
399
429
|
findings,
|
|
400
430
|
safeEdits,
|
|
401
431
|
semanticTasks: unresolved,
|
|
402
432
|
sourceInvestigation: {
|
|
403
|
-
required: harness.exact !== true || unresolved.length > 0,
|
|
433
|
+
required: harness.exact !== true || unresolved.length > 0 || actualTarget !== catalog.manifest.to.version,
|
|
404
434
|
targetRef: catalog.manifest.to.ref,
|
|
405
435
|
catalogReferences: catalog.manifest.references ?? {},
|
|
406
436
|
semanticTargets: [...new Set(unresolved.map(item => item.targetModule).filter(Boolean))],
|
|
@@ -429,6 +459,7 @@ export function createMigrationPlan(report) {
|
|
|
429
459
|
id: report.migration.id,
|
|
430
460
|
from: report.migration.from.ref,
|
|
431
461
|
to: report.migration.to.ref,
|
|
462
|
+
targetVersion: report.migration.actualTarget.version,
|
|
432
463
|
harness: {
|
|
433
464
|
status: report.migration.harness.status,
|
|
434
465
|
fromCommit: report.migration.harness.fromCommit,
|
|
@@ -453,7 +484,7 @@ function readMigrationPlan(file) {
|
|
|
453
484
|
function assertMigrationPlan(report, plan) {
|
|
454
485
|
const current = createMigrationPlan(report)
|
|
455
486
|
if (plan.plugin.root !== current.plugin.root || plan.plugin.manifestFile !== current.plugin.manifestFile) throw new Error('migration plan targets a different plugin')
|
|
456
|
-
if (plan.migration.id !== current.migration.id || plan.migration.from !== current.migration.from || plan.migration.to !== current.migration.to) throw new Error('migration plan targets a different catalog')
|
|
487
|
+
if (plan.migration.id !== current.migration.id || plan.migration.from !== current.migration.from || plan.migration.to !== current.migration.to || plan.migration.targetVersion !== current.migration.targetVersion) throw new Error('migration plan targets a different catalog or actual target version')
|
|
457
488
|
if (plan.reportHash !== current.reportHash || JSON.stringify(plan.inputs) !== JSON.stringify(current.inputs) || JSON.stringify(plan.edits) !== JSON.stringify(current.edits)) throw new Error('plugin analysis changed after the preview; create and review a new migration plan')
|
|
458
489
|
return current
|
|
459
490
|
}
|
|
@@ -478,7 +509,7 @@ export function applyMigration(report, options = {}) {
|
|
|
478
509
|
if (currentExists !== item.snapshot.exists || sha256(current) !== item.snapshot.hash) throw new Error(`${item.snapshot.file} changed after the preview; diagnose again before applying`)
|
|
479
510
|
}
|
|
480
511
|
const writes = report._plan.map(item => ({ ...atomicWrite(item.snapshot, item.nextText), beforeHash: item.snapshot.hash, afterHash: sha256(item.nextText) }))
|
|
481
|
-
const verification = analyzeMigration(report.plugin.root, { from: report.migration.from.ref, to: report.migration.to.ref, harnessRoot: report.migration.harness.root })
|
|
512
|
+
const verification = analyzeMigration(report.plugin.root, { from: report.migration.from.ref, to: report.migration.to.ref, targetVersion: report.migration.actualTarget.version, harnessRoot: report.migration.harness.root })
|
|
482
513
|
return { mode: 'applied', plan: { file: planFile, id: verifiedPlan.planId }, writes, report: publicMigrationReport(verification) }
|
|
483
514
|
}
|
|
484
515
|
|