@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.
@@ -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 catalog uses dsh-v0.1.1-rc.2 and dsh-v0.1.2-alpha.2 as its reference points.
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 current migration knowledge covers the DSH 0.1.1 to 0.1.2 transition. Its CLI catalog records `dsh-v0.1.1-rc.2` and `dsh-v0.1.2-alpha.2` as the exact reference points used to derive and verify known changes. Use those refs in dsh-doctor commands, but first record the plugin's actual DSH ranges and requested target. When another patch or prerelease is involved, use the catalog for known changes only and report that the additional version difference still needs review. Do not claim that the catalog proves an unlisted version combination.
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.2`
27
+ - target: `dsh-v0.1.2-alpha.3`
28
28
 
29
- 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, [migration-map.md](references/migration-map.md) before making semantic changes, and [verification.md](references/verification.md) before build or runtime verification.
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
- 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 an exact-version `npx` fallback over changing the developer's global installation. Never globally install or update the CLI without explicit authorization.
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
- The examples below use `dsh-doctor` for readability. When bootstrap selected an npx invocation, substitute the complete pinned prefix, `npx --yes --package=@bruc3van/dsh-doctor@<selected-version> dsh-doctor`, in every phase.
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.2 \
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 --harness-root <deepseek-harness-root> --json
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 --harness-root <deepseek-harness-root> --json
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 a file changed after preview. It may split a mixed import: exact symbols move to their new owners while semantic symbols remain unresolved. It may pin non-removed DSH development dependencies and add dependencies required by exact symbol moves. It does not change the ranges of existing published peers automatically. Never mechanically replace the removed Client Runtime with one aggregate package; no such replacement exists.
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 --harness-root <deepseek-harness-root> --json
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 execute plugin scripts and therefore require `--yes`. Runtime verification 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`.
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.2` catalog is present. A command that exists but lacks this catalog is not usable for this skill.
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 npx. First verify that version exposes the required catalog:
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
- npx --yes --package=@bruc3van/dsh-doctor@<registry-version> dsh-doctor migrations list --json
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`, `npx`, or authorized `global-install`);
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 this 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.2).
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.2)
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.2
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)
@@ -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 versionPassed = versionRun.passed && actualDshVersion === analysis.migration.to.version
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),