@bruc3van/dsh-doctor 0.5.4 → 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 +14 -12
- package/README.md +14 -12
- package/migrations/dsh-v0.1.1-rc.2__dsh-v0.1.2-alpha.2/packages.json +12 -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 +29 -14
- package/skills/dsh-plugin-upgrade/references/cli-bootstrap.md +6 -4
- package/skills/dsh-plugin-upgrade/references/migration-map.md +15 -1
- package/skills/dsh-plugin-upgrade/references/source-investigation.md +99 -0
- package/skills/dsh-plugin-upgrade/references/verification.md +9 -1
- package/src/cli.mjs +14 -3
- package/src/migrate-verify.mjs +74 -6
- package/src/migrate.mjs +148 -31
- package/src/safe-write.mjs +28 -1
- package/skills/dsh-plugin-upgrade/evals/evals.json +0 -76
|
@@ -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
28
|
|
|
29
|
-
|
|
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.
|
|
30
30
|
|
|
31
|
-
|
|
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.
|
|
32
32
|
|
|
33
|
-
|
|
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.
|
|
34
|
+
|
|
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,12 +55,18 @@ 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
|
|
|
60
62
|
Append `--harness-root <deepseek-harness-root>` when the checkout is available. Prefer an exact Harness checkout so the CLI verifies both tag commits. Both tags must exist and resolve to the commits recorded by the catalog; fetch the repository tags first when a shallow checkout lacks them. Without `--harness-root`, analysis is catalog-only and must be reported as such.
|
|
61
63
|
|
|
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.
|
|
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
|
+
|
|
62
70
|
Group the result by:
|
|
63
71
|
|
|
64
72
|
1. errors blocking compatibility;
|
|
@@ -76,16 +84,20 @@ Enter this phase only after the compatibility decision gate is resolved. The com
|
|
|
76
84
|
Preview first:
|
|
77
85
|
|
|
78
86
|
```sh
|
|
79
|
-
dsh-doctor migrate apply <plugin-root> --safe
|
|
87
|
+
dsh-doctor migrate apply <plugin-root> --safe \
|
|
88
|
+
--plan-file <reviewed-plan-outside-plugin-root.json> \
|
|
89
|
+
--harness-root <deepseek-harness-root> --json
|
|
80
90
|
```
|
|
81
91
|
|
|
82
|
-
Review affected files and hashes. Apply only with explicit authorization:
|
|
92
|
+
Store the plan outside the plugin root so it is not treated as plugin input. Review the plan id, complete analysis, affected files, and before/after hashes. Apply that exact plan only with explicit authorization:
|
|
83
93
|
|
|
84
94
|
```sh
|
|
85
|
-
dsh-doctor migrate apply <plugin-root> --safe --yes
|
|
95
|
+
dsh-doctor migrate apply <plugin-root> --safe --yes \
|
|
96
|
+
--plan-file <same-reviewed-plan.json> \
|
|
97
|
+
--harness-root <deepseek-harness-root> --json
|
|
86
98
|
```
|
|
87
99
|
|
|
88
|
-
The CLI creates timestamped backups and refuses writes when
|
|
100
|
+
The CLI binds the apply to the persisted report and edit hashes, creates timestamped backups, and refuses writes when the plugin analysis changed after preview. It may split a mixed import: exact symbols move to their new owners while semantic symbols remain unresolved. Dependency additions follow catalog-owned Client/Host and peer/dev policies instead of copying the removed package's old dependency section. It may pin non-removed DSH development dependencies. It does not widen the ranges of existing published peers automatically. Never mechanically replace the removed Client Runtime with one aggregate package; no such replacement exists.
|
|
89
101
|
|
|
90
102
|
After apply, inspect every `MIG_SEMANTIC_API_CHANGE`. Rewrite behavior using the new domain owner and its current snapshot/lifecycle contract. Keep these edits separate enough to review and test.
|
|
91
103
|
|
|
@@ -95,11 +107,11 @@ Run the gates in order:
|
|
|
95
107
|
|
|
96
108
|
```sh
|
|
97
109
|
dsh-doctor migrate verify <plugin-root> --level static --harness-root <deepseek-harness-root> --json
|
|
98
|
-
dsh-doctor migrate verify <plugin-root> --level build --yes --harness-root <deepseek-harness-root> --json
|
|
99
|
-
dsh-doctor migrate verify <plugin-root> --level runtime --yes --
|
|
110
|
+
dsh-doctor migrate verify <plugin-root> --level build --yes --install --harness-root <deepseek-harness-root> --json
|
|
111
|
+
dsh-doctor migrate verify <plugin-root> --level runtime --yes --install --dsh-command <actual-dsh-command> --json
|
|
100
112
|
```
|
|
101
113
|
|
|
102
|
-
Build and runtime levels
|
|
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`.
|
|
103
115
|
|
|
104
116
|
Resolve every semantic task and rebuild stale artifacts before expecting static verification to pass. An `apply` exit code of 1 after successful writes means migration blockers remain in the follow-up analysis; inspect `mode`, `writes`, and `report` instead of treating it as a write failure.
|
|
105
117
|
|
|
@@ -113,6 +125,8 @@ For a dual-version result, verify the produced release against both the actual 0
|
|
|
113
125
|
|
|
114
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.
|
|
115
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
|
+
|
|
116
130
|
## Report the outcome
|
|
117
131
|
|
|
118
132
|
State the highest achieved gate exactly:
|
|
@@ -126,4 +140,5 @@ State the highest achieved gate exactly:
|
|
|
126
140
|
Do not call the plugin compatible while errors, semantic tasks, stale artifacts, or required behavior checks remain. Include backups and retained temporary directories in the handoff.
|
|
127
141
|
Also report the selected DSH Doctor version and source (`local`, exact-version `npx`, or explicitly authorized global install), the registry version observed at bootstrap, and whether update status was current, outdated, missing, or unknown.
|
|
128
142
|
Report the plugin's actual source/target version evidence separately from the catalog reference refs. State whether the plugin is ready for its normal release process; if a release was explicitly requested and completed, include the commit, tag, registry, and release verification evidence.
|
|
143
|
+
Report the migration plan file and plan id, dependency-install command, lockfile change, and resolved target dependency versions. For semantic or unlisted-version work, include the DSH executable/package evidence, checkout refs and commits, inspected source paths, and remaining unknowns described in `source-investigation.md`.
|
|
129
144
|
State the compatibility intent as one of `0.1.2-only`, `dual-version`, or `pending developer decision`. For `dual-version`, report build, artifact, runtime, and behavior evidence separately for 0.1.1 and 0.1.2; only call the release dual-compatible when every required row passes.
|
|
@@ -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,14 +19,28 @@ 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.
|
|
25
29
|
- React, Cordis, client-store, ui-slots, and ui-primitives baseline modules are implicit and should not be repeated in `dsh.client.external`.
|
|
26
30
|
- A declared web client requires a published `exports["./client"]` artifact.
|
|
27
31
|
|
|
32
|
+
## Dependency ownership
|
|
33
|
+
|
|
34
|
+
An exact symbol move and its npm dependency placement are separate catalog decisions. The current target policy records Client relationships explicitly:
|
|
35
|
+
|
|
36
|
+
- Cordis must be present in matching `peerDependencies` and `devDependencies` for a client plugin;
|
|
37
|
+
- `dsh-client-store` and `dsh-session` Client/type relationships are development-only;
|
|
38
|
+
- existing published peer ranges are not widened automatically merely because a development dependency is pinned to the target catalog version.
|
|
39
|
+
|
|
40
|
+
Do not copy a replacement package into every dependency section that contained `dsh-client-runtime`. After apply, use the dependency verification gate to synchronize the lockfile and confirm the versions actually resolved in `node_modules`.
|
|
41
|
+
|
|
28
42
|
## Patch targets
|
|
29
43
|
|
|
30
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.
|
|
31
45
|
|
|
32
|
-
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.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Investigate the actual DSH source and installed runtime
|
|
2
|
+
|
|
3
|
+
Use this workflow whenever the plugin's declared or requested DSH version differs from a catalog reference, a semantic migration remains, or the developer asks what changed in DSH. The catalog is a starting point, not a substitute for checking the actual runtime and source.
|
|
4
|
+
|
|
5
|
+
## 1. Record the runtime actually in use
|
|
6
|
+
|
|
7
|
+
Keep these as separate facts:
|
|
8
|
+
|
|
9
|
+
- the DSH executable selected by PATH or an explicit command;
|
|
10
|
+
- the version printed by that executable;
|
|
11
|
+
- the DSH package installation backing the executable;
|
|
12
|
+
- the plugin's dependency, peer, optional-peer, and development ranges;
|
|
13
|
+
- the DSH packages resolved in the plugin lockfile and `node_modules`;
|
|
14
|
+
- the requested target version;
|
|
15
|
+
- the source and target refs used by the migration catalog.
|
|
16
|
+
|
|
17
|
+
On Windows, use read-only commands such as:
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
Get-Command dsh -All
|
|
21
|
+
dsh --version
|
|
22
|
+
npm root --global
|
|
23
|
+
Get-Content "$env:APPDATA\npm\node_modules\@deepseek-ai\dsh\package.json"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
On macOS or Linux:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
command -v -a dsh
|
|
30
|
+
dsh --version
|
|
31
|
+
npm root --global
|
|
32
|
+
cat "$(npm root --global)/@deepseek-ai/dsh/package.json"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Also inspect `DSH_HOME`, the selected profile manifest and lockfile, the plugin's lockfile, and any project-local DSH package. Do not assume PATH, a running DSH process, the profile, and the plugin workspace all resolve the same version.
|
|
36
|
+
|
|
37
|
+
## 2. Locate or obtain a Harness checkout
|
|
38
|
+
|
|
39
|
+
Prefer, in order:
|
|
40
|
+
|
|
41
|
+
1. an explicit checkout supplied by the developer;
|
|
42
|
+
2. a nearby project checkout whose remote and refs can be verified;
|
|
43
|
+
3. source provenance recorded by the installed package or repository metadata;
|
|
44
|
+
4. an upstream checkout obtained only when network access and cloning/fetching are within the developer's authorization.
|
|
45
|
+
|
|
46
|
+
Record `git remote -v`, worktree status, `git rev-parse <ref>`, and whether the checkout contains both relevant refs. Do not switch, reset, clean, or update a developer's checkout merely to inspect another ref. Use `git show`, `git diff`, `git grep`, and `git ls-tree` against refs without changing the worktree. A dirty checkout can still be used for ref-based inspection; report that the worktree itself was not treated as target evidence.
|
|
47
|
+
|
|
48
|
+
## 3. Build the additional-version delta
|
|
49
|
+
|
|
50
|
+
When the plugin's actual source or requested target is not exactly the catalog pair, compare the missing interval separately. At minimum inspect:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
git diff --name-status <actual-source-ref>..<actual-target-ref> -- packages apps
|
|
54
|
+
git diff <actual-source-ref>..<actual-target-ref> -- '**/package.json'
|
|
55
|
+
git grep -n '<old-symbol-or-service>' <actual-source-ref> -- packages apps
|
|
56
|
+
git grep -n '<old-symbol-or-service>' <actual-target-ref> -- packages apps
|
|
57
|
+
git grep -n '<new-owner-or-symbol>' <actual-target-ref> -- packages apps
|
|
58
|
+
git show <actual-target-ref>:<candidate-package>/package.json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Check package existence and exports, declaration files or TypeScript source, Service names and Cordis injection, snapshot shapes, event and cleanup lifecycles, client manifest rules, profile patches, build presets, and upstream architecture or migration notes. Do not infer an API from a similarly named package or symbol.
|
|
62
|
+
|
|
63
|
+
Classify every additional difference as:
|
|
64
|
+
|
|
65
|
+
- confirmed exact move;
|
|
66
|
+
- semantic behavior change;
|
|
67
|
+
- removed with no replacement;
|
|
68
|
+
- configuration or packaging change;
|
|
69
|
+
- unknown because source or evidence is unavailable.
|
|
70
|
+
|
|
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
|
+
|
|
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
|
+
|
|
75
|
+
## 4. Investigate each semantic task in the plugin
|
|
76
|
+
|
|
77
|
+
Start from the finding's `file`, `symbol`, `targetModule`, `targetSymbol`, and `reason`. Then:
|
|
78
|
+
|
|
79
|
+
1. trace every caller and lifecycle owner in the plugin;
|
|
80
|
+
2. inspect the target package export and implementation at the exact target ref;
|
|
81
|
+
3. compare source and target snapshot/event shapes;
|
|
82
|
+
4. identify activation, subscription, cleanup, error, and recovery behavior;
|
|
83
|
+
5. decide whether one source tree can support both requested DSH versions;
|
|
84
|
+
6. propose the smallest behavior-preserving change and name the tests needed to prove it.
|
|
85
|
+
|
|
86
|
+
If the catalog reference paths in `migration.references` exist in the Harness checkout, read them at the target ref. They are supporting evidence, not a replacement for inspecting the actual exported API and the plugin's callers.
|
|
87
|
+
|
|
88
|
+
## 5. Report provenance and uncertainty
|
|
89
|
+
|
|
90
|
+
For each conclusion, report:
|
|
91
|
+
|
|
92
|
+
- actual installed/runtime version evidence;
|
|
93
|
+
- plugin manifest and resolved dependency evidence;
|
|
94
|
+
- checkout remote, refs, and commits;
|
|
95
|
+
- catalog-covered delta versus additionally inspected delta;
|
|
96
|
+
- exact source paths, symbols, or commands used;
|
|
97
|
+
- remaining unknowns and the developer decision they block.
|
|
98
|
+
|
|
99
|
+
Never call an unlisted version combination compatible merely because the catalog pair passed.
|
|
@@ -4,9 +4,15 @@
|
|
|
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
|
-
The CLI selects the package manager from lockfiles and 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.
|
|
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.
|
|
12
|
+
|
|
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.
|
|
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.
|
|
10
16
|
|
|
11
17
|
## Runtime
|
|
12
18
|
|
|
@@ -22,6 +28,8 @@ The gate also checks the DSH CLI's exact target version, the created profile man
|
|
|
22
28
|
|
|
23
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.
|
|
24
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
|
+
|
|
25
33
|
## Behavior
|
|
26
34
|
|
|
27
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,9 +43,12 @@ 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
|
|
49
|
+
--plan-file <path> persist or consume the reviewed migrate apply plan
|
|
48
50
|
--level <level> static, build, or runtime verification
|
|
51
|
+
--install synchronize dependencies before build/runtime verification
|
|
49
52
|
--keep-temp retain a successful runtime verification directory
|
|
50
53
|
-h, --help show help
|
|
51
54
|
-v, --version show version
|
|
@@ -83,9 +86,12 @@ const HELP_ZH = `用法:
|
|
|
83
86
|
--fix, --repair 旧式确认修复(绝不移除插件)
|
|
84
87
|
--yes 确认一个明确的写入或命令动作
|
|
85
88
|
--from <ref> 源 DSH ref(默认:dsh-v0.1.1-rc.2)
|
|
86
|
-
--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 版本
|
|
87
91
|
--safe migrate apply 仅执行 catalog 确认的精确改写
|
|
92
|
+
--plan-file <路径> 保存或读取已审阅的 migrate apply 计划
|
|
88
93
|
--level <级别> static、build 或 runtime
|
|
94
|
+
--install 在 build/runtime 验证前同步依赖
|
|
89
95
|
--keep-temp 成功后仍保留运行时验证目录
|
|
90
96
|
-h, --help 显示帮助
|
|
91
97
|
-v, --version 显示版本
|
|
@@ -142,9 +148,14 @@ function parse(args) {
|
|
|
142
148
|
else if (arg.startsWith('--from=')) options.from = optionValue(arg, '--from')
|
|
143
149
|
else if (arg === '--to') options.to = valueAfter(args, index++, arg)
|
|
144
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')
|
|
145
153
|
else if (arg === '--safe') options.safe = true
|
|
154
|
+
else if (arg === '--plan-file') options.planFile = valueAfter(args, index++, arg)
|
|
155
|
+
else if (arg.startsWith('--plan-file=')) options.planFile = optionValue(arg, '--plan-file')
|
|
146
156
|
else if (arg === '--level') options.level = valueAfter(args, index++, arg)
|
|
147
157
|
else if (arg.startsWith('--level=')) options.level = optionValue(arg, '--level')
|
|
158
|
+
else if (arg === '--install') options.install = true
|
|
148
159
|
else if (arg === '--keep-temp') options.keepTemp = true
|
|
149
160
|
else if (arg === '--help' || arg === '-h') options.help = true
|
|
150
161
|
else if (arg === '--version' || arg === '-v') options.version = true
|
|
@@ -293,7 +304,7 @@ async function main() {
|
|
|
293
304
|
if (!['analyze', 'apply', 'verify'].includes(options.migrateAction)) throw new Error('migrate needs analyze, apply, or verify')
|
|
294
305
|
if (options.migrateAction === 'apply' && options.safe !== true) throw new Error('migrate apply requires --safe')
|
|
295
306
|
if (options.migrateAction === 'apply' && options.pluginRoot === undefined) throw new Error('migrate apply requires an explicit plugin root')
|
|
296
|
-
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 }
|
|
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 }
|
|
297
308
|
if (options.migrateAction === 'verify') {
|
|
298
309
|
const { formatVerification, verifyMigration } = await import('./migrate-verify.mjs')
|
|
299
310
|
const result = verifyMigration(options.pluginRoot, migrationOptions)
|
package/src/migrate-verify.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
|
|
2
2
|
import { tmpdir } from 'node:os'
|
|
3
|
-
import { basename, join, resolve } from 'node:path'
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path'
|
|
4
4
|
import crossSpawn from 'cross-spawn'
|
|
5
|
+
import semver from 'semver'
|
|
5
6
|
import { analyzeMigration, publicMigrationReport } from './migrate.mjs'
|
|
6
7
|
import { sha256 } from './safe-write.mjs'
|
|
7
8
|
|
|
@@ -38,9 +39,55 @@ function commandEvidence(result) {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function packageManager(root) {
|
|
41
|
-
if (existsSync(join(root, 'pnpm-lock.yaml'))) return { command: 'pnpm', run: script => ['run', script], pack: destination => ['pack', '--pack-destination', destination] }
|
|
42
|
-
if (existsSync(join(root, 'yarn.lock'))) return { command: 'yarn', run: script => [script], pack: destination => ['pack', '--out', join(destination, 'plugin.tgz')] }
|
|
43
|
-
return { command: 'npm', run: script => ['run', script], pack: destination => ['pack', '--json', '--pack-destination', destination] }
|
|
42
|
+
if (existsSync(join(root, 'pnpm-lock.yaml'))) return { command: 'pnpm', install: ['install', '--no-frozen-lockfile', '--ignore-scripts'], lockfile: 'pnpm-lock.yaml', run: script => ['run', script], pack: destination => ['pack', '--pack-destination', destination] }
|
|
43
|
+
if (existsSync(join(root, 'yarn.lock'))) return { command: 'yarn', install: ['install', '--ignore-scripts'], lockfile: 'yarn.lock', run: script => [script], pack: destination => ['pack', '--out', join(destination, 'plugin.tgz')] }
|
|
44
|
+
return { command: 'npm', install: ['install', '--ignore-scripts'], lockfile: 'package-lock.json', run: script => ['run', script], pack: destination => ['pack', '--json', '--pack-destination', destination] }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function lockfileHash(root, manager) {
|
|
48
|
+
const file = join(root, manager.lockfile)
|
|
49
|
+
return existsSync(file) ? sha256(readFileSync(file)) : undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function installedManifestFile(root, name) {
|
|
53
|
+
let current = resolve(root)
|
|
54
|
+
while (true) {
|
|
55
|
+
const file = join(current, 'node_modules', ...name.split('/'), 'package.json')
|
|
56
|
+
if (existsSync(file)) return file
|
|
57
|
+
const parent = dirname(current)
|
|
58
|
+
if (parent === current) return undefined
|
|
59
|
+
current = parent
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function installedDependencyEvidence(root, manifest) {
|
|
64
|
+
const requested = new Map()
|
|
65
|
+
for (const field of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) {
|
|
66
|
+
for (const [name, range] of Object.entries(manifest[field] ?? {})) {
|
|
67
|
+
if (name !== '@deepseek-ai/cordis' && !name.startsWith('@deepseek-ai/dsh-')) continue
|
|
68
|
+
const optional = field === 'optionalDependencies' || (field === 'peerDependencies' && manifest.peerDependenciesMeta?.[name]?.optional === true)
|
|
69
|
+
const declarations = requested.get(name) ?? []
|
|
70
|
+
declarations.push({ field, range, optional })
|
|
71
|
+
requested.set(name, declarations)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [...requested].map(([name, declarations]) => {
|
|
75
|
+
const file = installedManifestFile(root, name)
|
|
76
|
+
if (file === undefined) {
|
|
77
|
+
const optional = declarations.every(item => item.optional)
|
|
78
|
+
return { name, declarations, installed: false, optional, passed: optional, ...(optional ? { status: 'optional-missing' } : { error: 'declared dependency is not installed' }) }
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const installed = JSON.parse(readFileSync(file, 'utf8'))
|
|
82
|
+
const checks = declarations.map(item => {
|
|
83
|
+
const validRange = typeof item.range === 'string' ? semver.validRange(item.range) : null
|
|
84
|
+
return { ...item, passed: validRange !== null && semver.satisfies(installed.version, validRange) }
|
|
85
|
+
})
|
|
86
|
+
return { name, declarations: checks, installed: true, manifestFile: file, installedVersion: installed.version, passed: installed.name === name && checks.every(item => item.passed) }
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return { name, declarations, installed: true, manifestFile: file, passed: false, error: error instanceof Error ? error.message : String(error) }
|
|
89
|
+
}
|
|
90
|
+
})
|
|
44
91
|
}
|
|
45
92
|
|
|
46
93
|
function discoverDsh(options) {
|
|
@@ -98,6 +145,7 @@ export function verifyMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
98
145
|
const level = options.level ?? 'static'
|
|
99
146
|
if (!['static', 'build', 'runtime'].includes(level)) throw new Error(`unsupported verification level ${level}`)
|
|
100
147
|
if (level !== 'static' && options.yes !== true) throw new Error(`migrate verify --level ${level} executes project commands and requires --yes`)
|
|
148
|
+
if (level !== 'static' && options.install !== true) throw new Error(`migrate verify --level ${level} requires --install to synchronize and verify target dependencies`)
|
|
101
149
|
const analysis = analyzeMigration(pluginRoot, options)
|
|
102
150
|
const result = {
|
|
103
151
|
schemaVersion: 1,
|
|
@@ -111,10 +159,29 @@ export function verifyMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
111
159
|
manualBehaviorVerificationRequired: true,
|
|
112
160
|
}
|
|
113
161
|
if (level === 'static') return result
|
|
162
|
+
if (!result.passed) return result
|
|
114
163
|
|
|
115
164
|
const root = analysis.plugin.root
|
|
116
165
|
const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
|
|
117
166
|
const manager = packageManager(root)
|
|
167
|
+
const lockfileBefore = lockfileHash(root, manager)
|
|
168
|
+
const installRun = commandResult(manager.command, manager.install, { cwd: root })
|
|
169
|
+
const dependencies = installedDependencyEvidence(root, manifest)
|
|
170
|
+
const dependenciesPassed = installRun.passed && dependencies.every(item => item.passed)
|
|
171
|
+
result.stages.push({
|
|
172
|
+
name: 'dependencies',
|
|
173
|
+
passed: dependenciesPassed,
|
|
174
|
+
packageManager: manager.command,
|
|
175
|
+
command: commandEvidence(installRun),
|
|
176
|
+
lockfile: {
|
|
177
|
+
file: manager.lockfile,
|
|
178
|
+
beforeHash: lockfileBefore,
|
|
179
|
+
afterHash: lockfileHash(root, manager),
|
|
180
|
+
},
|
|
181
|
+
resolved: dependencies,
|
|
182
|
+
})
|
|
183
|
+
result.passed = dependenciesPassed
|
|
184
|
+
if (!result.passed) return result
|
|
118
185
|
const scripts = ['typecheck', 'build', 'test', 'pack:check'].filter(name => typeof manifest.scripts?.[name] === 'string')
|
|
119
186
|
const commandRuns = []
|
|
120
187
|
for (const script of scripts) {
|
|
@@ -147,7 +214,8 @@ export function verifyMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
147
214
|
const [versionCommand, versionArgs] = dshInvocation(dsh, ['--version'])
|
|
148
215
|
const versionRun = commandResult(versionCommand, versionArgs, { cwd: root, env })
|
|
149
216
|
const actualDshVersion = versionRun.stdout.trim()
|
|
150
|
-
const
|
|
217
|
+
const expectedDshVersion = analysis.migration.actualTarget.version
|
|
218
|
+
const versionPassed = versionRun.passed && actualDshVersion === expectedDshVersion
|
|
151
219
|
const pack = commandResult(manager.command, manager.pack(packDir), { cwd: root })
|
|
152
220
|
const tarball = tarballFromPack(pack, packDir)
|
|
153
221
|
const runtimeCommands = []
|
|
@@ -169,7 +237,7 @@ export function verifyMigration(pluginRoot = process.cwd(), options = {}) {
|
|
|
169
237
|
result.stages.push({
|
|
170
238
|
name: 'runtime',
|
|
171
239
|
passed: runtimePassed,
|
|
172
|
-
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) },
|
|
173
241
|
pack: commandEvidence(pack),
|
|
174
242
|
tarball,
|
|
175
243
|
commands: runtimeCommands.map(commandEvidence),
|