dsh-plugin-guide 0.3.7 → 0.3.8
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.es.md +2 -1
- package/README.hi.md +2 -1
- package/README.md +2 -1
- package/README.pt.md +2 -1
- package/README.zh.md +2 -1
- package/dist/dsh-plugin-dev.js +3 -3
- package/guide/plugin-dev-guide.md +1 -0
- package/guide/release-engineering.md +165 -0
- package/guide/release-engineering.zh-CN.md +165 -0
- package/package.json +4 -4
- package/templates/js/README.es.md +1 -1
- package/templates/js/README.hi.md +1 -1
- package/templates/js/README.md +1 -1
- package/templates/js/README.pt.md +1 -1
- package/templates/js/README.zh.md +1 -1
- package/templates/ts/README.es.md +1 -1
- package/templates/ts/README.hi.md +1 -1
- package/templates/ts/README.md +1 -1
- package/templates/ts/README.pt.md +1 -1
- package/templates/ts/README.zh.md +1 -1
package/README.es.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](https://github.com/topics/dsh-plugin)
|
|
12
|
+
[](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
|
|
12
13
|
[](#)
|
|
13
14
|
[](https://github.com/PerryLink/dsh-plugin-guide/actions)
|
|
14
15
|
[](https://github.com/PerryLink/dsh-plugin-guide/releases)
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
|
|
26
27
|
| Surface | Status |
|
|
27
28
|
|---|---|
|
|
28
|
-
| Harness | DeepSeek Harness `dsh-v0.1.5-
|
|
29
|
+
| Harness | DeepSeek Harness `dsh-v0.1.5-rc.1` (adaptado el 2026-09-09): el sobre de sesión conserva su campo ignorable solo para compatibilidad de lectura de logs almacenados - Session.append aún no puede estamparlo, por lo que el comportamiento de la puerta no cambia. Verificado el 2026-09-10 contra el checkout master dsh-v0.1.5-rc.1 (cadena completa de gates + smoke de instalación de perfil). |
|
|
29
30
|
| Node | `^22.19.0 || >=24.0.0` (runtime de DeepSeek Harness) |
|
|
30
31
|
| Platforms | Todas (bundle ESM plano; sin código nativo, sin red) |
|
|
31
32
|
| Model | Cualquiera (sin interacción con el modelo) |
|
package/README.hi.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](https://github.com/topics/dsh-plugin)
|
|
12
|
+
[](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
|
|
12
13
|
[](#)
|
|
13
14
|
[](https://github.com/PerryLink/dsh-plugin-guide/actions)
|
|
14
15
|
[](https://github.com/PerryLink/dsh-plugin-guide/releases)
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
|
|
26
27
|
| Surface | Status |
|
|
27
28
|
|---|---|
|
|
28
|
-
| Harness | DeepSeek Harness `dsh-v0.1.5-
|
|
29
|
+
| Harness | DeepSeek Harness `dsh-v0.1.5-rc.1` (2026-09-09 को अनुकूलित): सत्र लिफ़ाफ़ा अपना ignorable फ़ील्ड केवल संग्रहीत-लॉग पठन संगतता के लिए रखता है - Session.append अभी भी इसे स्टैम्प नहीं कर सकता, इसलिए गेट व्यवहार अपरिवर्तित है। 2026-09-10 को dsh-v0.1.5-rc.1 master checkout के विरुद्ध सत्यापित (पूर्ण gate chain + profile install smoke)। |
|
|
29
30
|
| Node | `^22.19.0 || >=24.0.0` (DeepSeek Harness रनटाइम) |
|
|
30
31
|
| Platforms | सभी (सादा ESM बंडल; कोई नेटिव कोड नहीं, कोई नेटवर्क नहीं) |
|
|
31
32
|
| Model | कोई भी (कोई मॉडल अंतःक्रिया नहीं) |
|
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
[](LICENSE)
|
|
14
14
|
[](https://github.com/topics/dsh-plugin)
|
|
15
|
+
[](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
|
|
15
16
|
[](#)
|
|
16
17
|
[](https://github.com/PerryLink/dsh-plugin-guide/actions)
|
|
17
18
|
[](https://github.com/PerryLink/dsh-plugin-guide/releases)
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
|
|
29
30
|
| Surface | Status |
|
|
30
31
|
|---|---|
|
|
31
|
-
| Harness | DeepSeek Harness `dsh-v0.1.5-
|
|
32
|
+
| Harness | DeepSeek Harness `dsh-v0.1.5-rc.1` (adapted 2026-09-09): the session envelope keeps its ignorable field for stored-log read compatibility only - Session.append still cannot stamp it, so audit-gate behavior is unchanged. Verified 2026-09-10 against the dsh-v0.1.5-rc.1 master checkout (full gate chain + profile install smoke). |
|
|
32
33
|
| Node | `^22.19.0 || >=24.0.0` (DeepSeek Harness runtime) |
|
|
33
34
|
| Platforms | All (plain ESM bundle; no native code, no network) |
|
|
34
35
|
| Model | Any (no model interaction) |
|
package/README.pt.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](https://github.com/topics/dsh-plugin)
|
|
12
|
+
[](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
|
|
12
13
|
[](#)
|
|
13
14
|
[](https://github.com/PerryLink/dsh-plugin-guide/actions)
|
|
14
15
|
[](https://github.com/PerryLink/dsh-plugin-guide/releases)
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
|
|
26
27
|
| Surface | Status |
|
|
27
28
|
|---|---|
|
|
28
|
-
| Harness | DeepSeek Harness `dsh-v0.1.5-
|
|
29
|
+
| Harness | DeepSeek Harness `dsh-v0.1.5-rc.1` (adaptado em 2026-09-09): o envelope de sessão mantém seu campo ignorable apenas para compatibilidade de leitura de logs armazenados - o Session.append ainda não consegue estampá-lo, então o comportamento da porta não muda. Verificado em 2026-09-10 contra o checkout master dsh-v0.1.5-rc.1 (cadeia completa de gates + smoke de instalação de perfil). |
|
|
29
30
|
| Node | `^22.19.0 || >=24.0.0` (runtime do DeepSeek Harness) |
|
|
30
31
|
| Platforms | Todas (bundle ESM puro; sem código nativo, sem rede) |
|
|
31
32
|
| Model | Qualquer (sem interação com o modelo) |
|
package/README.zh.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
[](https://github.com/topics/dsh-plugin)
|
|
14
|
+
[](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
|
|
14
15
|
[](#)
|
|
15
16
|
[](https://github.com/PerryLink/dsh-plugin-guide/actions)
|
|
16
17
|
[](https://github.com/PerryLink/dsh-plugin-guide/releases)
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
|
|
28
29
|
| Surface | Status |
|
|
29
30
|
|---|---|
|
|
30
|
-
| Harness | DeepSeek Harness `dsh-v0.1.5-
|
|
31
|
+
| Harness | DeepSeek Harness `dsh-v0.1.5-rc.1`(2026-09-09 已适配):会话信封保留 ignorable 字段但仅用于存量日志读取兼容——Session.append 仍无法盖章,门控行为不变。已于 2026-09-10 对照 dsh-v0.1.5-rc.1 master checkout 核验(完整门禁链 + profile 安装冒烟)。 |
|
|
31
32
|
| Node | `^22.19.0 || >=24.0.0`(DeepSeek Harness 运行时) |
|
|
32
33
|
| Platforms | 全部(纯 ESM bundle;无原生代码、无网络) |
|
|
33
34
|
| Model | 任意(无模型交互) |
|
package/dist/dsh-plugin-dev.js
CHANGED
|
@@ -1466,7 +1466,7 @@ function suggestionsFor(step, result) {
|
|
|
1466
1466
|
const base = [`tail of "${step}":\n${tailOf(result)}`];
|
|
1467
1467
|
if (result.timedOut) base.push(`command timed out; raise --timeout or --smoke-timeout`);
|
|
1468
1468
|
if (step === "install") {
|
|
1469
|
-
base.push("ensure the dsh CLI is @deepseek-ai/dsh@0.1.
|
|
1469
|
+
base.push("ensure the dsh CLI is @deepseek-ai/dsh@0.1.5-rc.1 (older builds such as the rc.6 line do not satisfy the compat pin)");
|
|
1470
1470
|
base.push("confirm the profile allowlist matches the repo compat workflow (native builds allowlisted)");
|
|
1471
1471
|
}
|
|
1472
1472
|
if (step === "headless") base.push("a hang usually means an injected service stayed pending; the smoke timeout surfaces exactly that");
|
|
@@ -1587,8 +1587,8 @@ async function main(argv, cwd = process.cwd()) {
|
|
|
1587
1587
|
dshBin: resolveDsh(flagString(flags, "dsh")),
|
|
1588
1588
|
pnpmBin: resolvePnpm(flagString(flags, "pnpm")),
|
|
1589
1589
|
profile: flagString(flags, "profile", "compat") ?? "compat",
|
|
1590
|
-
base: flagString(flags, "base", "@deepseek-ai/dsh-base@0.1.
|
|
1591
|
-
headless: flagString(flags, "headless", "@deepseek-ai/dsh-headless@0.1.
|
|
1590
|
+
base: flagString(flags, "base", "@deepseek-ai/dsh-base@0.1.5-rc.1") ?? "@deepseek-ai/dsh-base@0.1.5-rc.1",
|
|
1591
|
+
headless: flagString(flags, "headless", "@deepseek-ai/dsh-headless@0.1.5-rc.1") ?? "@deepseek-ai/dsh-headless@0.1.5-rc.1",
|
|
1592
1592
|
timeoutMs: timeoutMs(flags, "timeout", "DSH_PLUGIN_DEV_TIMEOUT", DEFAULT_TIMEOUT_MS),
|
|
1593
1593
|
smokeTimeoutMs: timeoutMs(flags, "smoke-timeout", "DSH_PLUGIN_DEV_SMOKE_TIMEOUT", DEFAULT_SMOKE_TIMEOUT_MS)
|
|
1594
1594
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
> 本文档是 `dsh-plugin-guide` 的核心成果之一:把官方文档、上游 Cordis 资料与社区经验汇总成一条完整的插件开发路径。
|
|
4
4
|
> 事实来源全部记录在 [sources.md](../references/sources.md);官方原文全文副本在 [references/official-docs/](../references/official-docs/)。
|
|
5
5
|
> 本文档为"指引",不是"替代品":开发中遇到精确签名/字段,永远以官方文档副本与生成式参考(subsystems 页、cordis-api 页)为准。
|
|
6
|
+
> 组合级运营(多仓版本编排、发布流水线、宿主破坏兼容时的迁移波)见 [release-engineering.md](release-engineering.md);中文稿:[release-engineering.zh-CN.md](release-engineering.zh-CN.md)。
|
|
6
7
|
|
|
7
8
|
---
|
|
8
9
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Release Engineering for a DSH Plugin Portfolio
|
|
2
|
+
|
|
3
|
+
> Chinese: [release-engineering.zh-CN.md](release-engineering.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
The official documentation teaches you how to build **one** plugin. It does not cover how to keep **dozens** of them installable, publishable, and upgradeable across a harness that ships compatibility-breaking changes by design. This chapter is that missing part, written from a live portfolio of 38 bundle plugins, 39 npm packages and 500+ published versions.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. The contract you are actually maintaining
|
|
10
|
+
|
|
11
|
+
A plugin is not "a TypeScript package". It is four contracts, and a portfolio breaks whenever one of them drifts:
|
|
12
|
+
|
|
13
|
+
| Contract | Where it lives | Breaks when |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Bundle manifest | `package.json` -> `dsh.bundle.patch` | the patch file is renamed or the key is dropped |
|
|
16
|
+
| Install surface | npm package name + `dist-tags` | a publish lands on the wrong tag, or never happens |
|
|
17
|
+
| Compatibility window | `peerDependencies` on `@deepseek-ai/*` | the harness moves an alpha/rc line and the range no longer admits it |
|
|
18
|
+
| Discoverability | GitHub topic `dsh-plugin` + package `keywords` | the repo is renamed, or a fork is created without topics |
|
|
19
|
+
|
|
20
|
+
Everything below is about keeping those four in sync at portfolio scale, cheaply.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Layout and naming that scale
|
|
25
|
+
|
|
26
|
+
Two habits separate 5 plugins from 50:
|
|
27
|
+
|
|
28
|
+
1. **One repository per plugin**, each independently releasable. A monorepo makes one failing gate block every plugin's release.
|
|
29
|
+
2. **A predictable name**: `dsh-<capability>` for the package, the repository named identically. Scoped names (`@you/dsh-x`) are fine; keep the repository name stable, because the discovery mechanism is a GitHub topic and a rename silently drops you out of search.
|
|
30
|
+
|
|
31
|
+
Keep a per-repo `AGENTS.md` stating that repository's own rules. As a portfolio grows, the repository file becomes the authority for that repo, and cross-repo conventions live in one shared document (this one).
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. The version-line matrix
|
|
36
|
+
|
|
37
|
+
During developer preview the harness publishes several lines at once, for example `0.1.2-rc.1`, `0.1.5-alpha.1`, `0.1.5-rc.1`. A portfolio pinned to one line breaks the moment a user installs from another.
|
|
38
|
+
|
|
39
|
+
The pattern that admits both windows:
|
|
40
|
+
|
|
41
|
+
```jsonc
|
|
42
|
+
{
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
45
|
+
"@deepseek-ai/dsh-tools": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@deepseek-ai/dsh-tools": "0.1.5-rc.1"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Rules that follow:
|
|
54
|
+
|
|
55
|
+
- **`devDependencies` pin the newest published line.** Your typecheck must fail before your users do.
|
|
56
|
+
- **`dependencies` pin the stable/rc line; `peerDependencies` admit both windows.** Never narrow a peer range just to make your own CI green - you will silently uninstall yourself from users on the other line.
|
|
57
|
+
- **Never re-pin 30 repositories by hand.** A scripted wave (patch bump, pin update, gate, publish) is the only sustainable path; run it as one atomic batch with a rollback note.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. The release pipeline
|
|
62
|
+
|
|
63
|
+
Per repository the release is a tag push that triggers a workflow:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
on:
|
|
67
|
+
push:
|
|
68
|
+
tags: ['v*']
|
|
69
|
+
jobs:
|
|
70
|
+
publish:
|
|
71
|
+
steps:
|
|
72
|
+
- run: pnpm install --frozen-lockfile
|
|
73
|
+
- run: pnpm run build
|
|
74
|
+
- run: npm publish --provenance --access public
|
|
75
|
+
env:
|
|
76
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Five things that cost real time to learn:
|
|
80
|
+
|
|
81
|
+
1. **`--frozen-lockfile` is a gate, and it is right.** A release commit that forgets `pnpm-lock.yaml` fails here. Never release from a dirty lockfile state.
|
|
82
|
+
2. **Publish with `--provenance`.** One flag produces a signed supply-chain attestation - the cheapest trust signal you can give a reviewer.
|
|
83
|
+
3. **Secrets are per repository.** `NPM_TOKEN` must exist in every repository that publishes. When a token is rotated, reseed all of them, or the failure appears much later, one repo at a time.
|
|
84
|
+
4. **A pushed tag cannot be re-published.** If the artifact was wrong: fix the commit, force-move the tag, re-run, and accept that the first attempt is public history.
|
|
85
|
+
5. **Know the registry replication delay.** A verification job running immediately after a publish can still resolve the previous version ("The latest release is X"). Re-run after `latest` settles instead of "fixing" a package that was never broken.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 4. One version, three surfaces
|
|
90
|
+
|
|
91
|
+
Publish once, mirror everywhere, make the mirror idempotent:
|
|
92
|
+
|
|
93
|
+
| Surface | Mechanism | Failure mode to watch |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| npm | tag-triggered workflow | dist-tag lands on the wrong line |
|
|
96
|
+
| GitHub Release | workflow or manual | release notes drift from CHANGELOG |
|
|
97
|
+
| Gitee (mirror) | scheduled sync workflow | mirror HEAD behind upstream after a force-move |
|
|
98
|
+
|
|
99
|
+
Verify all three before calling a release done. A release that exists on npm but not in the mirror is a half release, and mirror users will report a bug you already fixed.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 5. README parity is part of the build
|
|
104
|
+
|
|
105
|
+
If the portfolio ships READMEs in several languages, treat them as build artifacts:
|
|
106
|
+
|
|
107
|
+
- The language set is fixed and checked. Adding a section to one language and not the others must fail CI.
|
|
108
|
+
- Enforce encoding discipline before tagging: UTF-8 without BOM, no mojibake, no replacement characters. A published README cannot be repaired - npm metadata is immutable, and the registry may not even display your corrected file.
|
|
109
|
+
- When scripting anchors, remember word boundaries do not behave as expected after CJK characters.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 6. Migration waves when the harness breaks compatibility
|
|
114
|
+
|
|
115
|
+
The harness states plainly that compatibility-breaking changes will happen. Plan for waves, not for stability:
|
|
116
|
+
|
|
117
|
+
1. **Detect.** Run a typecheck matrix against the newest published line before users hit it. A removed event type or a changed signature surfaces as a compile error in one repository, which tells you the blast radius for all of them.
|
|
118
|
+
2. **Triage.** Separate pin updates, mechanical code changes, and semantic redesigns. Only the first two belong in a wave; the third gets its own release and its own changelog entry.
|
|
119
|
+
3. **Execute atomically.** One batch of patch bumps, one gate run, one publish wave, and a rollback note per repository (previous tag plus the exact pin to restore).
|
|
120
|
+
4. **Record the incident.** Two examples worth writing down, because both were self-inflicted and both cost a release cycle:
|
|
121
|
+
- A release commit without the lockfile turned CI red; fixed by amending the commit and force-moving the tag.
|
|
122
|
+
- A lockfile generated by a different package manager contained workspace-link entries that a clean install could not reproduce; fixed by deleting it, reinstalling in isolation, and regenerating.
|
|
123
|
+
|
|
124
|
+
**Rule of thumb**: if a wave touches more than about four repositories at once, the CI queue saturates and runs settle out of order. Batch accordingly.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 7. The pre-tag gate
|
|
129
|
+
|
|
130
|
+
Before every tag push:
|
|
131
|
+
|
|
132
|
+
- [ ] `pnpm install --frozen-lockfile` clean
|
|
133
|
+
- [ ] typecheck against the newest published line, not the one you developed on
|
|
134
|
+
- [ ] unit tests green
|
|
135
|
+
- [ ] package invariants: the `files` whitelist contains the built entry, and the entry exists
|
|
136
|
+
- [ ] README language parity plus encoding audit (no BOM, no mojibake, no replacement characters)
|
|
137
|
+
- [ ] `CHANGELOG.md` has a section for the version you are about to tag
|
|
138
|
+
- [ ] the version bump matches intent (patch = wave, minor = feature, major = breaking)
|
|
139
|
+
- [ ] the tag does not already exist on the remote
|
|
140
|
+
|
|
141
|
+
After the tag:
|
|
142
|
+
|
|
143
|
+
- [ ] npm `dist-tags.latest` equals the new version
|
|
144
|
+
- [ ] the GitHub Release exists with notes
|
|
145
|
+
- [ ] the mirror is synced
|
|
146
|
+
- [ ] every consumer repository that vendor-pins this package was bumped in the same batch
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 8. Registry behaviours that look like your bug but are not
|
|
151
|
+
|
|
152
|
+
Document these once so nobody debugs them twice:
|
|
153
|
+
|
|
154
|
+
- **`versions[].readme` can be empty** for every published version of a package even though the tarball README is byte-identical to your repository. The package page then shows a stale README. It is a registry-side presentation behaviour, reproducible through both the CLI and CI channels - do not "fix" it by re-publishing.
|
|
155
|
+
- **`description` is truncated** at roughly 255 characters in package metadata. Compare a truncated form, not the full string, when verifying a publish.
|
|
156
|
+
- **Scoped packages** may return placeholder values on some download-count endpoints. Use the range endpoint rather than the point endpoint.
|
|
157
|
+
- **Published does not mean visible.** See item 5 in section 3.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 9. What this buys you
|
|
162
|
+
|
|
163
|
+
A portfolio maintained this way behaves like a product: users on any harness line can install any plugin, every version is attested, mirrors agree, and a breaking upstream change costs one wave instead of an outage. It is also the strongest form of ecosystem contribution - the official project asks for exactly this, and explicitly does not rank official packages above community ones.
|
|
164
|
+
|
|
165
|
+
Corrections and additions are welcome; the practical entry points are listed in [links.md](links.md).
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# DSH 插件组合的发布工程
|
|
2
|
+
|
|
3
|
+
> English: [release-engineering.md](release-engineering.md)
|
|
4
|
+
|
|
5
|
+
官方文档教的是**一个**插件怎么写。它没有讲**几十个**插件怎么在一个"设计上就会破坏兼容"的宿主上持续可安装、可发布、可升级。本章补的就是这一段,素材来自一个真实在跑的插件组合:38 个 bundle 插件、39 个 npm 包、500+ 已发布版本。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. 你实际在维护的是四份契约
|
|
10
|
+
|
|
11
|
+
插件不是"一个 TypeScript 包",而是四份契约;组合出问题,一定是其中一份先漂移了:
|
|
12
|
+
|
|
13
|
+
| 契约 | 位置 | 断裂方式 |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Bundle 清单 | `package.json` -> `dsh.bundle.patch` | patch 文件改名,或该键被删掉 |
|
|
16
|
+
| 安装面 | npm 包名 + `dist-tags` | 发布落到错误的 tag,或根本没发出去 |
|
|
17
|
+
| 兼容窗口 | `@deepseek-ai/*` 的 `peerDependencies` | 宿主换了 alpha/rc 线,范围不再接受它 |
|
|
18
|
+
| 可发现性 | GitHub topic `dsh-plugin` + 包 `keywords` | 仓库改名,或 fork 出来时没带 topic |
|
|
19
|
+
|
|
20
|
+
下面全部围绕"如何低成本地让这四份契约在组合规模上保持一致"。
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. 能扩展的布局与命名
|
|
25
|
+
|
|
26
|
+
从 5 个插件走到 50 个,只差两个习惯:
|
|
27
|
+
|
|
28
|
+
1. **一个插件一个仓库**,各自独立发布。monorepo 会让一个失败的门禁卡住所有插件的发布。
|
|
29
|
+
2. **可预测的命名**:包名 `dsh-<能力>`,仓库同名。scoped 名(`@you/dsh-x`)也可以,但仓库名要保持稳定——发现机制靠 GitHub topic,改名会让你无声地掉出搜索结果。
|
|
30
|
+
|
|
31
|
+
每个仓库保留一份 `AGENTS.md` 写清该仓自己的规则。组合变大后,仓库内文件是该仓的权威,跨仓约定则集中在一份共享文档里(就是本文)。
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. 版本线矩阵
|
|
36
|
+
|
|
37
|
+
开发者预览期,宿主会同时存在多条线,例如 `0.1.2-rc.1`、`0.1.5-alpha.1`、`0.1.5-rc.1`。只钉一条线的组合,在用户从另一条线安装的那一刻就坏。
|
|
38
|
+
|
|
39
|
+
同时接受两个窗口的写法:
|
|
40
|
+
|
|
41
|
+
```jsonc
|
|
42
|
+
{
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
45
|
+
"@deepseek-ai/dsh-tools": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@deepseek-ai/dsh-tools": "0.1.5-rc.1"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
由此得到的规则:
|
|
54
|
+
|
|
55
|
+
- **`devDependencies` 钉最新已发布的线。** 类型检查要赶在你的用户之前报错。
|
|
56
|
+
- **`dependencies` 钉 stable/rc 线,`peerDependencies` 接受两个窗口。** 永远不要为了让自己的 CI 变绿而收窄 peer 范围——那等于把另一条线上的用户静默卸载掉。
|
|
57
|
+
- **绝不用手改 30 个仓库的钉号。** 脚本化的一波(patch bump、更新钉号、过门禁、发布)是唯一可持续的路径;把它当成一个原子批次执行,并为每个仓留回滚注记。
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. 发布流水线
|
|
62
|
+
|
|
63
|
+
每个仓的发布 = 推一个 tag,触发 workflow:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
on:
|
|
67
|
+
push:
|
|
68
|
+
tags: ['v*']
|
|
69
|
+
jobs:
|
|
70
|
+
publish:
|
|
71
|
+
steps:
|
|
72
|
+
- run: pnpm install --frozen-lockfile
|
|
73
|
+
- run: pnpm run build
|
|
74
|
+
- run: npm publish --provenance --access public
|
|
75
|
+
env:
|
|
76
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
五个用时间换来的经验:
|
|
80
|
+
|
|
81
|
+
1. **`--frozen-lockfile` 是一道门禁,而且它是对的。** 发布提交漏了 `pnpm-lock.yaml`,就会死在这里。绝不要在锁文件不干净的状态下发布。
|
|
82
|
+
2. **用 `--provenance` 发布。** 一个参数换来签名的供应链凭证,这是你能给审查者的最便宜的信任信号。
|
|
83
|
+
3. **secret 是按仓存的。** 每个会发布的仓都必须有 `NPM_TOKEN`。令牌轮换时要全量重播种,否则故障会晚很多、一个一个仓地冒出来。
|
|
84
|
+
4. **已推送的 tag 不能重发。** 产物错了就:修提交、强制移动 tag、重跑,并接受第一次尝试已成为公开历史。
|
|
85
|
+
5. **要知道 registry 的复制延迟。** 发布后立刻跑的校验任务,可能仍解析到上一个版本("The latest release is X")。等 `latest` 稳定后重跑,而不是去"修"一个本来没坏的包。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 4. 一个版本,三个面
|
|
90
|
+
|
|
91
|
+
发布一次,镜像到位,并让镜像幂等:
|
|
92
|
+
|
|
93
|
+
| 面 | 机制 | 要盯的失败模式 |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| npm | tag 触发 workflow | dist-tag 落错线 |
|
|
96
|
+
| GitHub Release | workflow 或手工 | release notes 与 CHANGELOG 漂移 |
|
|
97
|
+
| Gitee(镜像) | 定时同步 workflow | 强制移动 tag 后镜像 HEAD 落后于上游 |
|
|
98
|
+
|
|
99
|
+
三面都验过,才算发布完成。只上了 npm 而镜像没有,是半个发布——镜像上的用户会来报一个你早已修好的 bug。
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 5. README 一致性属于构建的一部分
|
|
104
|
+
|
|
105
|
+
如果组合提供多语言 README,就把它们当构建产物:
|
|
106
|
+
|
|
107
|
+
- 语言集合是固定的、且被检查。只给一种语言加章节而其他语言不加,必须让 CI 失败。
|
|
108
|
+
- tag 之前先跑编码门禁:UTF-8 无 BOM、无乱码、无替换字符。已发布的 README 修不回来——npm 元数据不可变,registry 甚至可能不展示你修正后的文件。
|
|
109
|
+
- 写锚点正则时注意:CJK 字符之后的词边界行为与英文不同。
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 6. 宿主破坏兼容时的迁移波
|
|
114
|
+
|
|
115
|
+
宿主明确写了会破坏兼容。所以要按"波"来做计划,而不是指望稳定:
|
|
116
|
+
|
|
117
|
+
1. **探测。** 在用户撞上之前,先对最新已发布的线跑类型检查矩阵。某个事件类型被移除、或签名变了,会在某一个仓里先炸成编译错误——这个错误就告诉你全部仓的影响面。
|
|
118
|
+
2. **分诊。** 把"钉号更新""机械改写""语义重设计"分开。只有前两类属于一波;第三类单独发布、单独写 changelog。
|
|
119
|
+
3. **原子执行。** 一批 patch bump、一次门禁、一波发布,并为每个仓留回滚注记(上一个 tag + 要还原的确切钉号)。
|
|
120
|
+
4. **记录事故。** 两个值得写下来的例子,都是自伤,都各吃掉一个发布周期:
|
|
121
|
+
- 发布提交漏了锁文件导致 CI 红;修法是补提交 + 强制移动 tag。
|
|
122
|
+
- 用另一个包管理器生成的锁文件里含 workspace link 条目,干净环境无法复现;修法是删掉锁文件、隔离重装、重新生成。
|
|
123
|
+
|
|
124
|
+
**经验值**:一波超过约四个仓,CI 队列就会饱和、各 run 结算乱序。按此分批。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 7. 打 tag 前的门禁清单
|
|
129
|
+
|
|
130
|
+
每次推 tag 之前:
|
|
131
|
+
|
|
132
|
+
- [ ] `pnpm install --frozen-lockfile` 干净
|
|
133
|
+
- [ ] 对**最新已发布的线**做类型检查,而不是你开发时用的那条
|
|
134
|
+
- [ ] 单元测试全绿
|
|
135
|
+
- [ ] 包不变量:`files` 白名单确实包含构建产物,且该产物存在
|
|
136
|
+
- [ ] 多语 README 一致 + 编码审计(无 BOM、无乱码、无替换字符)
|
|
137
|
+
- [ ] `CHANGELOG.md` 有即将打的这个版本的小节
|
|
138
|
+
- [ ] 版本号与意图一致(patch = 波,minor = 功能,major = 破坏性)
|
|
139
|
+
- [ ] 远端还没有这个 tag
|
|
140
|
+
|
|
141
|
+
推 tag 之后:
|
|
142
|
+
|
|
143
|
+
- [ ] npm `dist-tags.latest` 等于新版本
|
|
144
|
+
- [ ] GitHub Release 已建且带 notes
|
|
145
|
+
- [ ] 镜像已同步
|
|
146
|
+
- [ ] 所有 vendor 钉住该包的消费仓已在同一批里升级
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 8. 那些看起来像你的 bug、其实不是的 registry 行为
|
|
151
|
+
|
|
152
|
+
写下来一次,省得有人调两遍:
|
|
153
|
+
|
|
154
|
+
- **`versions[].readme` 可能为空**——即使 tarball 里的 README 与你的仓库逐字节一致,包页也会展示旧 README。这是 registry 侧的呈现行为,CLI 与 CI 两条通道都能复现,不要靠"重发一次"去修。
|
|
155
|
+
- **`description` 在包元数据里约 255 字符处被截断。** 校验发布时按截断形式比对,而不是全文。
|
|
156
|
+
- **scoped 包**在某些下载量端点上会返回占位值。用 range 端点,别用 point 端点。
|
|
157
|
+
- **发布成功不等于可见。** 见第 3 节第 5 条。
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 9. 这样做换来什么
|
|
162
|
+
|
|
163
|
+
按这套方式维护的组合,表现得像一个产品:任何宿主线上的用户都能装到任何插件,每个版本都有凭证,镜像一致,上游的破坏性变更只花掉一波而不是一次事故。这同时也是最强的生态贡献形式——官方要的正是这个,并且明确否认"官方仓的包比社区的包更重要"。
|
|
164
|
+
|
|
165
|
+
欢迎指出错误或补充;常用入口见 [links.md](links.md)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-guide",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "The dsh-plugin-guide knowledge base plus the dsh-plugin-dev CLI toolchain: official docs, Cordis primer, community deep-dives, and battle-tested pitfalls registered as an on-demand agent skill, with a scaffolder, static checker, and pack verifier for building DSH plugins.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -63,12 +63,12 @@
|
|
|
63
63
|
"typescript": "^5.9.3"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@deepseek-ai/dsh-attachment": "0.1.5-
|
|
66
|
+
"@deepseek-ai/dsh-attachment": "0.1.5-rc.1",
|
|
67
67
|
"@types/node": "^22.20.1",
|
|
68
68
|
"vitest": "^3.2.7"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@deepseek-ai/dsh": ">=0.1.2-rc.1 <0.2.0"
|
|
71
|
+
"@deepseek-ai/dsh": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
74
74
|
"@deepseek-ai/dsh": {
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"filesystem:read"
|
|
106
106
|
],
|
|
107
107
|
"compatibility": {
|
|
108
|
-
"dshVersions": ["0.1.2-rc.1","0.1.5-
|
|
108
|
+
"dshVersions": ["0.1.2-rc.1","0.1.5-rc.1"]
|
|
109
109
|
},
|
|
110
110
|
"capability": {
|
|
111
111
|
"id": "dsh-plugin-guide",
|
|
@@ -6,7 +6,7 @@ Un plugin de DeepSeek Harness (DSH) generado con [`dsh-plugin-dev new`](https://
|
|
|
6
6
|
|
|
7
7
|
| Superficie | Estado |
|
|
8
8
|
|---|---|
|
|
9
|
-
| Harness | DeepSeek Harness `0.1.
|
|
9
|
+
| Harness | DeepSeek Harness `0.1.5-rc.1` |
|
|
10
10
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
11
11
|
| Plataformas | Todas (ESM puro; sin código nativo, sin red) |
|
|
12
12
|
|
package/templates/js/README.md
CHANGED
|
@@ -6,7 +6,7 @@ A DeepSeek Harness (DSH) plugin scaffolded by [`dsh-plugin-dev new`](https://git
|
|
|
6
6
|
|
|
7
7
|
| Surface | Status |
|
|
8
8
|
|---|---|
|
|
9
|
-
| Harness | DeepSeek Harness `0.1.
|
|
9
|
+
| Harness | DeepSeek Harness `0.1.5-rc.1` |
|
|
10
10
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
11
11
|
| Platforms | All (plain ESM; no native code, no network) |
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ Um plugin do DeepSeek Harness (DSH) gerado com [`dsh-plugin-dev new`](https://gi
|
|
|
6
6
|
|
|
7
7
|
| Superfície | Estado |
|
|
8
8
|
|---|---|
|
|
9
|
-
| Harness | DeepSeek Harness `0.1.
|
|
9
|
+
| Harness | DeepSeek Harness `0.1.5-rc.1` |
|
|
10
10
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
11
11
|
| Plataformas | Todas (ESM puro; sem código nativo, sem rede) |
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ Un plugin de DeepSeek Harness (DSH) generado con [`dsh-plugin-dev new`](https://
|
|
|
6
6
|
|
|
7
7
|
| Superficie | Estado |
|
|
8
8
|
|---|---|
|
|
9
|
-
| Harness | DeepSeek Harness `0.1.
|
|
9
|
+
| Harness | DeepSeek Harness `0.1.5-rc.1` |
|
|
10
10
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
11
11
|
| Plataformas | Todas (ESM puro; sin código nativo, sin red) |
|
|
12
12
|
|
package/templates/ts/README.md
CHANGED
|
@@ -6,7 +6,7 @@ A DeepSeek Harness (DSH) plugin scaffolded by [`dsh-plugin-dev new`](https://git
|
|
|
6
6
|
|
|
7
7
|
| Surface | Status |
|
|
8
8
|
|---|---|
|
|
9
|
-
| Harness | DeepSeek Harness `0.1.
|
|
9
|
+
| Harness | DeepSeek Harness `0.1.5-rc.1` |
|
|
10
10
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
11
11
|
| Platforms | All (plain ESM; no native code, no network) |
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ Um plugin do DeepSeek Harness (DSH) gerado com [`dsh-plugin-dev new`](https://gi
|
|
|
6
6
|
|
|
7
7
|
| Superfície | Estado |
|
|
8
8
|
|---|---|
|
|
9
|
-
| Harness | DeepSeek Harness `0.1.
|
|
9
|
+
| Harness | DeepSeek Harness `0.1.5-rc.1` |
|
|
10
10
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
11
11
|
| Plataformas | Todas (ESM puro; sem código nativo, sem rede) |
|
|
12
12
|
|