dsh-ppt-studio 1.2.1 → 1.2.2
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/agent-presets/ppt/agent.cordis.yml +16 -201
- package/cordis.patch.yml +1 -1
- package/docs/03-/346/233/264/346/226/260/346/227/245/345/277/227.md +31 -0
- package/docs/14-/345/217/221/345/270/203/346/270/205/345/215/225/357/274/210/350/215/211/347/250/277/357/274/211.md +62 -0
- package/docs/15-1.2.0 /345/217/221/345/270/203/346/212/245/345/221/212.md" +13 -0
- package/dsh-target.json +4 -0
- package/examples/smoke/imported/source.pptx +0 -0
- package/examples/smoke/out-smoke.pptx +0 -0
- package/package.json +26 -7
- package/scripts/check-dsh-target.mjs +51 -0
- package/scripts/check-workflows.mjs +50 -0
- package/scripts/smoke.mjs +8 -8
- package/skills/ppt-studio-diagram/pitfalls.md +10 -0
|
@@ -1,43 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
# standing scope; every session naming it joins by scope parentage, so the
|
|
5
|
-
# tools and prompt sections registered here cover each joined agent while a
|
|
6
|
-
# session's own state stays keyed per Session/Agent inside the plugins. The
|
|
7
|
-
# host composition (`base.cordis.yml` + `web.cordis.yml`) keeps everything a
|
|
8
|
-
# preset must not own: the registries themselves, the sandbox and approval
|
|
9
|
-
# stack, persistence, and the model route.
|
|
10
|
-
#
|
|
11
|
-
# A service row here MUST sit inside a group carrying an `isolate` realm.
|
|
12
|
-
# Without one it publishes into the root realm, where it is process-global —
|
|
13
|
-
# another preset publishing the same name collides, and a host reader would
|
|
14
|
-
# resolve one preset's instance for every session; the agent-preset registry
|
|
15
|
-
# rejects that at mount. `true` means an entry-local realm: this standing
|
|
16
|
-
# mount's own private instance, apart from every other preset's. (A shared
|
|
17
|
-
# label does NOT pool instances — `provide()` throws on the second registration
|
|
18
|
-
# under the same realm symbol; labels join REALMS, and are not what this file
|
|
19
|
-
# needs.)
|
|
20
|
-
#
|
|
21
|
-
# 【本文件在 DSH 0.1.7 上怎么被用(2026-09-24 适配)】
|
|
22
|
-
# 它是一份 **Loader 方言的条目数组**,由 `lib/preset-delivery.js` 读出来、作为
|
|
23
|
-
# `plugins` 交给 `ctx.agentPresets.register({id:'ppt', name, description, order, plugins})`。
|
|
24
|
-
# 0.1.6 及以前是靠把本文件**写到 `<dshHome>/.agent-presets/ppt/`** 让宿主扫到——那条路
|
|
25
|
-
# 在 0.1.7 已被上游删除(原文:"the harness discovers no preset on disk"),所以现在是**声明**。
|
|
26
|
-
# 两个必须守住的细节(都在 preset-delivery.js 里有源码级依据):
|
|
27
|
-
# · `!!js` 会被 Loader 求值(`{__jsExpr}`),自己读 YAML 时必须**保真**,否则平台开关会失真;
|
|
28
|
-
# · 相对模块名会被解析到**注册表所在层**(= dsh-web-app 包目录),不是本目录——本文件里没有相对名。
|
|
29
|
-
|
|
30
|
-
# ── identity ────────────────────────────────────────────────────────────────
|
|
31
|
-
|
|
32
|
-
# The preset's own persona, shadowing the deployment default for this agent.
|
|
33
|
-
# `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
|
|
34
|
-
#
|
|
35
|
-
# 【本预设唯一的**有意差异**(相对随包 standard)】2026-09-18:
|
|
36
|
-
# 这行原来与 standard 逐字相同(通用编码人格),于是本预设只是"改了名字的 standard"——
|
|
37
|
-
# 用户提问"我们的预设和标准模式有啥区别?"时,diff 给出的答案是"0 行差异"。
|
|
38
|
-
# 工具/技能按预设隔离在这版 DSH 上做不到(切换预设时 agent 已存在 ⇒ `agent/created` 钩子不触发;
|
|
39
|
-
# 技能注册表在父层读 ⇒ 子层注册看不见;见 docs/03 最新条目),所以**预设能可靠交付的只有人格**。
|
|
40
|
-
# 改这行必须同步更新 scripts/check-preset.mjs 顶部的 INTENTIONAL 白名单(否则门禁会报配置漂移)。
|
|
1
|
+
# Agent preset «PPT 工作室» —— 以 DSH 0.2.1-rc.2 的 standard 预设体为基准复制,
|
|
2
|
+
# 唯一有意差异 = persona(PPT 工作室的人格/开工状态)。
|
|
3
|
+
# 该文件由 scripts/check-preset.mjs 与随装 standard 预设逐行比对,漂移即失败。
|
|
41
4
|
- id: persona
|
|
42
5
|
name: '@deepseek-ai/dsh-persona'
|
|
43
6
|
config:
|
|
@@ -51,81 +14,36 @@
|
|
|
51
14
|
before showing it. The ppt_* tools, the /ppt commands and the PPT Studio skill manuals are your
|
|
52
15
|
working set — reach for them instead of hand-rolling artifacts.
|
|
53
16
|
If the human's request is clearly unrelated to decks, answer it normally without forcing this workflow.
|
|
54
|
-
|
|
55
17
|
- id: agent-instructions
|
|
56
18
|
name: '@deepseek-ai/dsh-agent-instructions'
|
|
57
19
|
config:
|
|
58
20
|
maxBytes: 65536
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
|
|
63
|
-
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
|
|
64
|
-
# the criterion for host-plane ownership — injection resolves before any session
|
|
65
|
-
# exists, so there is no agent to key by. Behind a preset realm those variables
|
|
66
|
-
# never reached the model's shell at all. Both shell tools consume the host
|
|
67
|
-
# registry from here; their executors (`bash-sandbox`/`pwsh-sandbox`) are
|
|
68
|
-
# host-plane too.
|
|
21
|
+
- id: time-context
|
|
22
|
+
name: '@deepseek-ai/dsh-time-context'
|
|
69
23
|
- id: tool-bash
|
|
70
24
|
name: '@deepseek-ai/dsh-tool-bash'
|
|
71
25
|
disabled: !!js process.platform === 'win32'
|
|
72
|
-
|
|
73
26
|
- id: tool-pwsh
|
|
74
27
|
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
75
28
|
disabled: !!js process.platform !== 'win32'
|
|
76
|
-
|
|
77
|
-
# ── filesystem ──────────────────────────────────────────────────────────────
|
|
78
|
-
|
|
79
|
-
# Both register into the host `tools` registry and provide nothing, so
|
|
80
|
-
# they need no realm. The `fs` service and its policy stay in the host.
|
|
81
29
|
- id: tool-fs
|
|
82
30
|
name: '@deepseek-ai/dsh-tool-fs'
|
|
83
|
-
|
|
84
31
|
- id: tool-fs-search
|
|
85
32
|
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
86
33
|
config:
|
|
87
34
|
sampleOverCapGlobResults: false
|
|
88
|
-
|
|
89
|
-
# ── background jobs ────────────────────────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
# Only the model-facing controls. The task REGISTRY stays on the host plane:
|
|
92
|
-
# its producers sit outside any realm this file could put it in — `tool-bash`
|
|
93
|
-
# above resolves it with `ctx.get`, and an entry-local realm here is invisible
|
|
94
|
-
# to every sibling row, so `run_in_background` would answer "background jobs
|
|
95
|
-
# unavailable" while these controls sat in the catalog. The registry is keyed by
|
|
96
|
-
# owning agent anyway, so one host instance serves every session. What a preset
|
|
97
|
-
# chooses is whether its agent can collect and stop background work at all.
|
|
98
35
|
- id: tool-jobs
|
|
99
36
|
name: '@deepseek-ai/dsh-tool-jobs'
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# The skill REGISTRY lives in the host composition and is layered per scope:
|
|
104
|
-
# these rows register into THIS preset's layer of it, so they need no realm.
|
|
105
|
-
# `skill-filesystem` contributes local-root discovery for agents on this preset, and
|
|
106
|
-
# `tool-skill` gives them the catalog and loader; the merged catalog also
|
|
107
|
-
# carries whatever the deployment registered globally (repository plugins).
|
|
37
|
+
- id: tool-schedule
|
|
38
|
+
name: '@deepseek-ai/dsh-tool-schedule'
|
|
108
39
|
- id: skill-filesystem
|
|
109
40
|
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
110
|
-
|
|
111
41
|
- id: tool-skill
|
|
112
42
|
name: '@deepseek-ai/dsh-tool-skill'
|
|
113
|
-
|
|
114
|
-
# ── goals ───────────────────────────────────────────────────────────────────
|
|
115
|
-
|
|
116
|
-
# The goal service and session driver stay on the host plane, where the Gateway
|
|
117
|
-
# can resolve them. The human command and model-facing tool register into this
|
|
118
|
-
# preset's scoped layers.
|
|
119
43
|
- id: command-goal
|
|
120
44
|
name: '@deepseek-ai/dsh-command-goal'
|
|
121
|
-
|
|
122
45
|
- id: tool-goal
|
|
123
46
|
name: '@deepseek-ai/dsh-tool-goal'
|
|
124
|
-
|
|
125
|
-
# ── plan mode ───────────────────────────────────────────────────────────────
|
|
126
|
-
|
|
127
|
-
# Plan state is per-agent by nature, so an entry-local realm is not a
|
|
128
|
-
# workaround here — it is the correct lifetime.
|
|
129
47
|
- id: planning
|
|
130
48
|
name: cordis:group
|
|
131
49
|
group: true
|
|
@@ -136,29 +54,17 @@
|
|
|
136
54
|
name: '@deepseek-ai/dsh-plan-mode'
|
|
137
55
|
config:
|
|
138
56
|
section: |
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
142
|
-
|
|
143
|
-
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
57
|
+
You are in plan mode. Stay in plan mode until the user approves your plan through exit_plan_mode or switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
144
58
|
|
|
145
|
-
|
|
59
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
146
60
|
|
|
147
|
-
|
|
61
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
148
62
|
|
|
149
|
-
|
|
63
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
150
64
|
|
|
151
|
-
|
|
65
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
152
66
|
|
|
153
|
-
#
|
|
154
|
-
# share this realm rather than sit outside it.
|
|
155
|
-
#
|
|
156
|
-
# `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
|
|
157
|
-
# plane, and the rows here resolve that one instance. It takes no configuration,
|
|
158
|
-
# keys every fold by Session, and owns the context-meter projection units the
|
|
159
|
-
# browser reads for every session — behind a realm those units would come and go
|
|
160
|
-
# with whichever presets happen to be mounted. What a preset chooses is whether
|
|
161
|
-
# its agent compacts at all, which is `compaction-basic` below.
|
|
67
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
162
68
|
- id: compaction
|
|
163
69
|
name: cordis:group
|
|
164
70
|
group: true
|
|
@@ -168,28 +74,14 @@
|
|
|
168
74
|
config:
|
|
169
75
|
- id: compaction-basic
|
|
170
76
|
name: '@deepseek-ai/dsh-compaction-basic'
|
|
171
|
-
|
|
172
77
|
- id: command-compact
|
|
173
78
|
name: '@deepseek-ai/dsh-command-compact'
|
|
174
|
-
|
|
175
79
|
- id: tool-result-pruner
|
|
176
80
|
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
177
81
|
config:
|
|
178
82
|
thresholdChars: 8192
|
|
179
83
|
headChars: 4096
|
|
180
84
|
tailChars: 1024
|
|
181
|
-
|
|
182
|
-
# ── delegation and workflows ────────────────────────────────────────────────
|
|
183
|
-
|
|
184
|
-
# The `subagents` registry and its spawn/fork backends live in the HOST
|
|
185
|
-
# composition: the registry is a process singleton whose cross-session queries
|
|
186
|
-
# the api-proxy serves to the browser, and a provider name may only be
|
|
187
|
-
# registered once. This preset contributes the delegation TOOLS, which resolve
|
|
188
|
-
# that host registry.
|
|
189
|
-
#
|
|
190
|
-
# `workflows` is different — nothing outside an agent reads it — so every row
|
|
191
|
-
# that reaches it shares one entry-local realm here, and a consumer left
|
|
192
|
-
# outside would resolve a host registry this preset does not populate.
|
|
193
85
|
- id: delegation
|
|
194
86
|
name: cordis:group
|
|
195
87
|
group: true
|
|
@@ -198,118 +90,41 @@
|
|
|
198
90
|
config:
|
|
199
91
|
- id: tool-subagent-control
|
|
200
92
|
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
201
|
-
|
|
202
93
|
- id: tool-subagent-list-agents
|
|
203
94
|
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
204
|
-
|
|
205
95
|
- id: tool-subagent
|
|
206
96
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
207
97
|
config:
|
|
208
98
|
provider: spawn
|
|
209
99
|
toolName: subagent
|
|
210
100
|
modelSelectionSettings: true
|
|
211
|
-
backgroundMode: continuable
|
|
212
|
-
|
|
213
|
-
# Fork omits model selection so provider/model stay equal to the parent and
|
|
214
|
-
# the inherited history remains eligible for KV Cache reuse. This preset
|
|
215
|
-
# keeps fork continuable; parent and child inherit the same messaging tool,
|
|
216
|
-
# while the parent id and return guidance follow the inherited history.
|
|
217
101
|
- id: tool-subagent-fork
|
|
218
102
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
219
103
|
config:
|
|
220
104
|
provider: fork
|
|
221
105
|
toolName: subagent_fork
|
|
222
|
-
backgroundMode: continuable
|
|
223
|
-
|
|
224
|
-
# Production dsh does not install these optional providers. Install the
|
|
225
|
-
# matching Bundle in this Profile and restart the Host, then copy this
|
|
226
|
-
# preset and remove `disabled` from the matching tool row. Host availability
|
|
227
|
-
# alone grants no tool.
|
|
228
|
-
- id: tool-subagent-codex
|
|
229
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
230
|
-
disabled: true
|
|
231
|
-
config:
|
|
232
|
-
provider: codex
|
|
233
|
-
toolName: subagent_codex
|
|
234
|
-
backgroundMode: one-shot
|
|
235
|
-
maxDepth: provider-managed
|
|
236
|
-
|
|
237
|
-
- id: tool-subagent-claude-code
|
|
238
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
239
|
-
disabled: true
|
|
240
|
-
config:
|
|
241
|
-
provider: claude-code
|
|
242
|
-
toolName: subagent_claude_code
|
|
243
|
-
backgroundMode: one-shot
|
|
244
|
-
maxDepth: provider-managed
|
|
245
|
-
|
|
246
106
|
- id: workflow-ptc
|
|
247
107
|
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
248
108
|
config:
|
|
249
109
|
provider: spawn
|
|
250
|
-
|
|
251
110
|
- id: tool-workflow
|
|
252
111
|
name: '@deepseek-ai/dsh-tool-workflow'
|
|
253
|
-
|
|
254
|
-
# Off by default with the base row it mirrors: the tool description
|
|
255
|
-
# restricts `ralph` to runs the human explicitly asked for, and completion
|
|
256
|
-
# is a worker self-report, not an independent evaluation. Preset files take
|
|
257
|
-
# no patches, so a session that wants it duplicates this preset under a new
|
|
258
|
-
# id and drops `disabled`; the shipped root shadows a copy that reuses this
|
|
259
|
-
# id.
|
|
260
|
-
- id: tool-ralph
|
|
261
|
-
name: '@deepseek-ai/dsh-tool-ralph'
|
|
262
|
-
disabled: true
|
|
263
|
-
config:
|
|
264
|
-
subagentProvider: spawn
|
|
265
|
-
maxRounds: 64
|
|
266
|
-
|
|
267
|
-
# ── remaining model-facing rows ─────────────────────────────────────────────
|
|
268
|
-
|
|
269
112
|
- id: tool-ask-user
|
|
270
113
|
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
271
|
-
|
|
272
114
|
- id: tool-todo
|
|
273
115
|
name: '@deepseek-ai/dsh-tool-todo'
|
|
274
116
|
config:
|
|
275
117
|
allowParallelInProgress: true
|
|
276
|
-
|
|
277
|
-
# The `web` service and its search provider stay in the host composition; only
|
|
278
|
-
# the model-facing tool is per-session.
|
|
279
118
|
- id: tool-web
|
|
280
119
|
name: '@deepseek-ai/dsh-tool-web'
|
|
281
120
|
config:
|
|
282
121
|
fetch: true
|
|
283
122
|
searchTimeoutMs: 60000
|
|
284
|
-
|
|
285
123
|
- id: present
|
|
286
124
|
name: '@deepseek-ai/dsh-tool-present'
|
|
287
|
-
|
|
288
125
|
- id: tool-plugin-manager
|
|
289
126
|
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
290
127
|
disabled: true
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
# 隔离靠**一行自定位行**实现,但那一行**由声明时程序化追加**(`preset-delivery.js` 的 `selfPresetRow()`):
|
|
295
|
-
# { id: 'ppt-studio-scope', name: 'file:///<本包绝对路径>/lib/index.js', config: { scope: 'preset', autoPreset: false } }
|
|
296
|
-
# 为什么不写死在 YAML 里:
|
|
297
|
-
# ① 写死就要在安装期把绝对路径填进来 ⇒ 换 profile/升级换代会陈旧;`import.meta.url` 现算永远是对的;
|
|
298
|
-
# ② 本文件的"不含插件行"是**被门禁断言**的(`scripts/verify-bundle-install.mjs` 断言预设组合里没有
|
|
299
|
-
# `ppt-studio` 插件行、`scripts/check-preset.mjs` 做逐行意图比对)——程序化追加让 YAML 保持干净,门禁全绿。
|
|
300
|
-
# 为什么必须是**绝对 file://**(而裸包名不行):
|
|
301
|
-
# 预设的行由注册表在**它自己 ctx 的 baseUrl** 下 mount(源码 `mountPreset(scope.ctx.extend({ baseUrl: record.context.baseUrl }), …)`,
|
|
302
|
-
# 而 `register()` 里 `context = this.ctx`)——那是 `dsh-web-app` 的包目录,**不是** profile、也不是预设目录
|
|
303
|
-
# ⇒ 裸包名 `dsh-ppt-studio` 解析不到 ⇒ 预设被判 `broken` ⇒ 前端选择器只渲染健康预设
|
|
304
|
-
# (`presetOptions() = presets.filter(p => p.broken === void 0)`)⇒ 用户根本选不到;
|
|
305
|
-
# 而 Loader 对**非 `.` 开头**的名字是**原样 `import(name)`** ⇒ `file://` 绝对名不查 baseUrl。
|
|
306
|
-
# (0.1.7-rc.2 上真宿主实测:工具 22/0、技能 4/0、提示段有/无、切换预设、负面对照,全过。)
|
|
307
|
-
#
|
|
308
|
-
# 历史沿革(两次"证伪"的正确结论,别再按旧结论行事):
|
|
309
|
-
# ① 2026-09-18 的"按 agent 动态门控"(`agent/created` 钩子 + 子层注册)**确实不可行** ——
|
|
310
|
-
# 空白会话切预设时 agent 已存在、钩子不会为它触发。但那只是"插件自己按 agent 门控"这一种写法;
|
|
311
|
-
# **预设行挂载**不经过 agent 生命周期:声明即挂载,切换预设时宿主把 agent 的 scope 父指针 `rebind`
|
|
312
|
-
# 到新代,可见性自动跟随。
|
|
313
|
-
# ② "技能分层 ⇒ 子层注册父层看不见"方向对、**但不是障碍**:技能读路径按"查看方 scope 的链"合并
|
|
314
|
-
# (含 agent 自己那一层)⇒ 注册进**预设作用域**正是技能能被该预设看到的**充要姿势**。
|
|
315
|
-
|
|
128
|
+
- id: tool-plugin-market
|
|
129
|
+
name: '@deepseek-ai/dsh-plugin-market/tools'
|
|
130
|
+
disabled: true
|
package/cordis.patch.yml
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# · 本行 = 把插件挂进 **profile 层**。`config.scope` 决定它的角色:
|
|
13
13
|
# - `auto`(缺省,= 本行现在写的值):声明「PPT 工作室」预设,并给它追加一行**自定位行**
|
|
14
14
|
# (本包入口的绝对 `file://`)⇒ 预设的常驻组合会再挂本包一次(`scope: 'preset'`),
|
|
15
|
-
# **那一次**把 22 个 `ppt_*` 工具 / `/ppt` 命令 / `ppt_state` /
|
|
15
|
+
# **那一次**把 22 个 `ppt_*` 工具 / `/ppt` 命令 / `ppt_state` / 17 本内嵌技能 / 语义路由 / 工作流提示段
|
|
16
16
|
# 全部注册在**预设作用域** ⇒ 只有「PPT 工作室」的会话看得到,**其他预设零影响**。
|
|
17
17
|
# 本档自己不注册任何能力面;只有判定隔离没生效时才**自动回落**到全局(功能不丢,只是没隔离)。
|
|
18
18
|
# - `profile`:传统档,一律全局(所有预设都能用)。想回到旧行为就改成这个。
|
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
> 格式:`## [版本号] 日期` → 目的(为什么改,引测试反馈编号)→ 改动 → 验证 → 影响/后续。
|
|
4
4
|
|
|
5
|
+
## [1.2.2] 2026-10-18 —— 适配 DSH 0.2.1-rc.2 + CI 稳定性
|
|
6
|
+
|
|
7
|
+
**环境事实(实测 2026-10-17)**
|
|
8
|
+
- 桌面 App 外壳 `app.asar/package.json` = `@deepseek-ai/dsh-desktop@0.2.1-rc.2`;运行宿主 `plugin_manager list_bundles` 的 `dsh-base` / `dsh-web-app` / agent-team 全部 = 0.2.1-rc.2 ⇒ **运行宿主 = 0.2.1-rc.2** ✓。
|
|
9
|
+
- npm 全局 CLI(`dsh --version`)原为 0.2.0-rc.2(**落后**)⇒ 按"先对齐后适配"用 `npm install -g @deepseek-ai/dsh@next` 升到 0.2.1-rc.2(守卫:`@next` = 0.2.1-rc.2 = 桌面端,未超前 ✓)。
|
|
10
|
+
- `~/.dsh/dsh-runtimes/dsh-primary-runtime/runtime.json` 的 `desktopVersion` 仍是 0.2.0-rc.2 —— **陈旧元数据**,不代表运行宿主;版本判据以 `app.asar/package.json` + 宿主 bundle 目录为准。
|
|
11
|
+
- profile 仍为 desktop / web / headless 三个(`tui` 只出现在检查脚本的搜索列表里,不是实际 profile)。
|
|
12
|
+
|
|
13
|
+
**兼容性复核(0.2.1-rc.2 上实测)**
|
|
14
|
+
- 服务与签名不变:`tools.register(definition)`、`commands.register(definition)`、`systemPrompt.section(section)`、`skills.register(skill)`(另有 `registerProvider`)、`agentPresets.register(definition) → Promise<() => Promise<void>>`。
|
|
15
|
+
- `dsh-skill` 同时保留 `register` 与 `registerProvider` ⇒ 17 本内嵌技能仍按原通道注册。
|
|
16
|
+
- `dsh.bundle.patch`(profile 层 patch)语义不变;`plugin_manager` 仍能识别本插件 bundle。
|
|
17
|
+
- **标准预设改了形状**:参照从 `dsh-agent-presets/presets/standard/agent.cordis.yml` 迁到 `dsh-web-app/presets/standard.patch.yml`(`- insert:` → `preset-standard` → `config.plugins`),standard 组合行 **19 → 22**(新增 `time-context`、`tool-schedule` 等),`planning` / `delegation` 两个分组的 config 也变了。
|
|
18
|
+
- **修法**:以 0.2.1 的 standard 组合为基准重写 `agent-presets/ppt/agent.cordis.yml`(**文本级拼接**,保留 `!!js` 标签与多行字符串,不靠 YAML 重序列化),只保留唯一有意差异 `persona`(PPT 工作室人格)。
|
|
19
|
+
- 同步更新 smoke §40 的三处行数期望(19/20 → 22/23)。
|
|
20
|
+
|
|
21
|
+
**验收证据(全部实测)**
|
|
22
|
+
- `npm test`:**全部通过**(`check-preset` 漂移 0 / 缺行 0 / 有意差异 1 ✓;`lib ⟷ src` 逐字节一致 ✓;痕迹扫描 0 ✓;生成器 9/9 ✓)
|
|
23
|
+
- `node scripts/verify-preset-scope.mjs --expect-skills 17`:**PASS 22 / 0** —— ppt 预设 **17 技能 / 22 个 ppt_* 工具**、standard 预设 **0**、全局视图 **0** ✓
|
|
24
|
+
- `npm run verify:package`:通过(249 文件、装到干净目录后 `require` 可用 ✓)
|
|
25
|
+
- `npm run verify:products`:通过(13 页 PNG 与基线一致,差异 0 ✓)
|
|
26
|
+
|
|
27
|
+
**CI 稳定性(同版一并交付)**
|
|
28
|
+
- **被测 DSH 版本单源化**:新增 `dsh-target.json`(唯一事实源);`ci.yml` 用 `node -p` 读取,不再手写死数字;`scripts/check-dsh-target.mjs` 并入 `npm test`,强制它 == `docs/14` 的 `dsh-target:` 行,且 `dsh.engines.dsh` 下限不得高于目标。
|
|
29
|
+
- **PR 不跑安装链**:`install-path` 加 `if: github.event_name != 'pull_request'` ⇒ PR 只跑干净检出的快测档,安装链在 main / 手动 时跑。
|
|
30
|
+
- **`@next` 周检**:新增 `dsh-next-probe.yml`(每周一 + 手动,作业级 `continue-on-error`)⇒ 上游 DSH 变化**只告警、不拦 PR**。
|
|
31
|
+
- **工作流语法守卫**:新增 `scripts/check-workflows.mjs`(并入 `npm test`),解析全部 workflow 并校验结构。起因:`dsh-next-probe.yml` 的 step 名含 `: ` 未加引号 ⇒ GitHub 判文件无效(显示文件路径 + "No jobs were run");该类错误现在**本地即被拦住**。
|
|
32
|
+
- **修复**:`ci.yml` 的 one-command install 钉版由 `0.1.6-alpha.2` 改为跟随 `dsh-target.json`(= 0.2.1-rc.2),消除"预设来自新版、CLI 是旧版"的安装自证假红。
|
|
33
|
+
|
|
34
|
+
**发布**:本条目即 **1.2.2**(2026-10-18);发版前检查全绿:`npm test` 322/0、`verify:package`、`verify:products` 13/13。安装方式:两端统一改为 npm 安装 `dsh-ppt-studio@1.2.2`。
|
|
35
|
+
|
|
5
36
|
## [1.2.1] 2026-09-30 —— 修复:复杂图 skills 未被注册(用户实测)
|
|
6
37
|
|
|
7
38
|
**问题**:新预设会话里,斜杠命令与助手的 skill 清单都找不到复杂图相关 skill(四份 `ppt-studio-*` 正常可见)。
|
|
@@ -55,3 +55,65 @@
|
|
|
55
55
|
3. 发布后必须**从 registry 复核**,并把结果写进 `docs/03` 与发布报告。
|
|
56
56
|
|
|
57
57
|
**标准发布序列**:`npm test` 全绿 → `verify:package` / `verify:products` → 版本号 + `docs/03` 定稿 → 提交并推 `main` → 打 `v<版本>` 标签并推 → **创建 Release(附 tgz)** → 轮询 registry 至目标版本 → 两端安装升级 → 复核。
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 8. 发布 RUNBOOK(npm OIDC 专用 · 照抄执行)
|
|
62
|
+
|
|
63
|
+
> **一句话**:本包**没有也不该有**本机 npm 凭据;发布**只能**通过"创建 GitHub Release → `publish.yml` 用 OIDC 发到 npm"这条路。
|
|
64
|
+
|
|
65
|
+
### 8.1 铁律(违反即算事故)
|
|
66
|
+
1. **没有用户明确指令,不得**:改 `package.json` 版本号 / 打 `v*` 标签 / 创建 GitHub Release。**创建 Release = 发布**。
|
|
67
|
+
2. **不得用本机 `npm publish`** —— 必然 `E401/E404`,这是**设计如此**,不是故障;**永远不要**把它的退出码当发布判据。
|
|
68
|
+
3. **发布判据只有一个**:`npm view dsh-ppt-studio version`(与 `dist-tags`)显示目标版本。
|
|
69
|
+
|
|
70
|
+
### 8.2 标准序列(逐条执行,任一不过即停)
|
|
71
|
+
```powershell
|
|
72
|
+
# 0) 发版前检查(全绿才能继续)
|
|
73
|
+
npm test # 期望 322 通过 / 0 失败
|
|
74
|
+
npm run verify:package # 打包与安装一致性(装到干净目录后 require 可用)
|
|
75
|
+
npm run verify:products # 本机专用:重出 → 导出 → 渲染 13 PNG → 与基线比对
|
|
76
|
+
|
|
77
|
+
# 1) 版本与日志
|
|
78
|
+
# package.json 的 version → <新版本>;docs/03 写 [<新版本>] <日期> 条目(含已知限制)
|
|
79
|
+
|
|
80
|
+
# 2) 提交并推主干
|
|
81
|
+
git add -A; git commit -m "release(<版本>): …"; git push origin main
|
|
82
|
+
|
|
83
|
+
# 3) 打标签(注意:**只推 tag 不会发布**)
|
|
84
|
+
git tag -a v<版本> -m "<一句话>"; git push origin v<版本>
|
|
85
|
+
|
|
86
|
+
# 4) 打包并**创建 Release** —— 这一步才触发发布
|
|
87
|
+
$tmp = Join-Path $env:TEMP ("rel" + (Get-Random)); New-Item -ItemType Directory -Force -Path $tmp | Out-Null
|
|
88
|
+
$tgz = (npm pack --pack-destination $tmp | Select-Object -Last 1).Trim()
|
|
89
|
+
gh release create v<版本> (Join-Path $tmp $tgz) -R zbsph/dsh-ppt-studio -t "dsh-ppt-studio <版本>" -n "<发布说明>"
|
|
90
|
+
|
|
91
|
+
# 5) 等发布并**从 registry 复核**(唯一判据)
|
|
92
|
+
gh run list -R zbsph/dsh-ppt-studio --limit 3 # 看 "Publish to npm" 是否 success
|
|
93
|
+
npm view dsh-ppt-studio version # 必须等于 <版本>
|
|
94
|
+
npm view dsh-ppt-studio dist-tags --json
|
|
95
|
+
|
|
96
|
+
# 6) 两端安装升级
|
|
97
|
+
# web: dsh plugin --profile web add dsh-ppt-studio@<版本>
|
|
98
|
+
# 桌面端:应用「插件管理」界面升级(CLI 对 desktop profile 会被拒:managed exclusively by the Electron application)
|
|
99
|
+
# ⇒ 桌面端升级后需**重启应用**才生效;重启后在插件管理里确认版本
|
|
100
|
+
|
|
101
|
+
# 7) 记录:docs/15 追加发布小节;docs/03 条目保持与实际一致
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 8.3 撤回(仅当尚未真正发布到 npm)
|
|
105
|
+
```powershell
|
|
106
|
+
gh release delete v<版本> -R zbsph/dsh-ppt-studio --yes # 移除触发源
|
|
107
|
+
gh run list -R zbsph/dsh-ppt-studio --limit 5 # 找到 queued/in_progress 的 Publish run
|
|
108
|
+
gh run cancel <数据库ID> -R zbsph/dsh-ppt-studio # 取消它
|
|
109
|
+
npm view dsh-ppt-studio version # 确认仍是上一版(= 未发布)
|
|
110
|
+
```
|
|
111
|
+
**一旦已发布**:**不要** unpublish(会伤消费者)⇒ 改用**发下一个补丁版本**修正。
|
|
112
|
+
|
|
113
|
+
### 8.4 补发/重发
|
|
114
|
+
用工作流自带的手动入口(`publish.yml` 支持 `workflow_dispatch` 并接受 tag 入参):
|
|
115
|
+
```powershell
|
|
116
|
+
gh workflow run publish.yml -R zbsph/dsh-ppt-studio -f tag=v<版本>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`dsh-target: 0.2.1-rc.2`(CI install-path 的被测 DSH 版本;与 `dsh-target.json` 同源,由 `scripts/check-dsh-target.mjs` 强制一致)
|
|
@@ -44,3 +44,16 @@
|
|
|
44
44
|
- **遗留 3**:装配语义(C)只核到"名字与结构仍在"层,未逐行验证行为等价。
|
|
45
45
|
- **回滚点**:`pre-release`、`pre-adapt-D/E/F`、`pre-release-1.2.0`;上一版 tag `v1.1.5`。
|
|
46
46
|
- **平台事实**:桌面 profile 由 Electron 独占管理(`dsh plugin --profile desktop …` 会被拒 ⇒ 桌面端插件变更只能走应用「插件管理」);`web` / `headless` profile 可用 CLI 管理。
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 7. 1.2.1 发布小节(2026-09-30)
|
|
51
|
+
|
|
52
|
+
| 项 | 值 |
|
|
53
|
+
|---|---|
|
|
54
|
+
| 版本 | **1.2.1**(`package.json`) |
|
|
55
|
+
| 内容 | 修复复杂图 skills 未被注册;13 份统一命名为 `ppt-studio-diagram` + 12 份 `ppt-studio-diagram-<类型>`;描述/正文长度与「优先级边界」合规;smoke §40/§52 期望技能数 4→17 |
|
|
56
|
+
| 发布通道 | **创建 GitHub Release `v1.2.1`** ⇒ `publish.yml` 以 **OIDC 受信发布** → npm(**未使用本机 `npm publish`**,本机无凭据属设计如此) |
|
|
57
|
+
| 发布证据 | `npm view dsh-ppt-studio version` → **1.2.1** ✓;Release 资产 `dsh-ppt-studio-1.2.1.tgz`(含 **17** 个 skill)✓ |
|
|
58
|
+
| 发布前检查 | `npm test` 322/0 ✓、`verify:package` ✓、`verify:products` 13/13 ✓、痕迹扫描 0 ✓、`lib ⟷ src` 39/39 ✓ |
|
|
59
|
+
| 回滚 | 上一版 `v1.2.0`;撤回流程见 `docs/14` §8.3(仅未发布时可用) |
|
package/dsh-target.json
ADDED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ppt-studio",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "在 DeepSeek Harness 里做 PPT:说需求 → 逐页制作 → 导出可编辑的 .pptx。含布局检查、Office 真渲染复核,以及可扩展的本地模板库",
|
|
5
5
|
"//publish": "2026-09-18 定:发 npm,**无 scope** 包名 `dsh-ppt-studio`。**状态(2026-09-26):已发布** —— `dsh-ppt-studio@1.1.0` 在 npm 上(`dist-tags.latest`、public,发布者 `zbsph`),registry 的 tarball 与 GitHub Release 资产**逐字节相同**(sha256 `9303cc5c…`)。首个版本是 `npm publish <发版 tgz>` 直发的;**从下一版起改走 Trusted Publishing(OIDC)全自动通道**:`.github/workflows/publish.yml` 由 release 事件触发 → 下载 Release 资产 → OIDC `npm publish` → 发后自证 integrity —— 零长期 token、零手工(npm 已公告:Bypass-2FA 直发 2027-01 移除)。发布纪律不变:**用发版时那个 tgz**(`.sync-state.json` 的 `localTgz`),**别在 main 前进后跑裸 `npm publish`**(会按当前工作区重新打包 ⇒ 破坏'两条通道同一份字节')。手动补发入口:Actions → Publish to npm → Run workflow。见 README §10 与 docs/05「发布-同步纪律」。",
|
|
6
|
-
"repository": {
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/zbsph/dsh-ppt-studio.git"
|
|
9
|
+
},
|
|
7
10
|
"homepage": "https://github.com/zbsph/dsh-ppt-studio#readme",
|
|
8
|
-
"bugs": {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/zbsph/dsh-ppt-studio/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"dsh",
|
|
16
|
+
"ppt",
|
|
17
|
+
"pptx",
|
|
18
|
+
"powerpoint",
|
|
19
|
+
"slides",
|
|
20
|
+
"deck",
|
|
21
|
+
"presentation",
|
|
22
|
+
"cordis",
|
|
23
|
+
"plugin",
|
|
24
|
+
"deepseek-harness"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
11
29
|
"type": "module",
|
|
12
30
|
"main": "./lib/index.js",
|
|
13
31
|
"//lib": "lib/ 是**提交**的构建产物(src 的纯拷贝):商店(创意工坊)对无 npm 包的条目生成的命令是 `dsh plugin add <repo URL>`,git 安装只拿得到已提交内容,lib 不入库则插件挂不上。守卫:npm run check:lib + smoke 断言。",
|
|
@@ -36,7 +54,8 @@
|
|
|
36
54
|
"cordis.patch.yml",
|
|
37
55
|
"examples/smoke",
|
|
38
56
|
"examples/fx",
|
|
39
|
-
"README.md"
|
|
57
|
+
"README.md",
|
|
58
|
+
"dsh-target.json"
|
|
40
59
|
],
|
|
41
60
|
"license": "MIT",
|
|
42
61
|
"dependencies": {
|
|
@@ -68,7 +87,7 @@
|
|
|
68
87
|
"build": "bash scripts/build.sh",
|
|
69
88
|
"build:node": "node scripts/build.mjs",
|
|
70
89
|
"sync": "node scripts/release-sync.mjs",
|
|
71
|
-
"test": "node scripts/build.mjs && node scripts/check-tracked-lf.mjs && node scripts/check-lib-sync.mjs && node scripts/check-unknown-keys.mjs && node scripts/check-removed-traces.mjs && node scripts/check-generators.mjs && node scripts/smoke.mjs && node scripts/check-preset.mjs && node scripts/verify-handdrawn.mjs && node scripts/red-probe.mjs && node scripts/check-line-text.mjs && node scripts/handdrawn-option-probe.mjs",
|
|
90
|
+
"test": "node scripts/build.mjs && node scripts/check-tracked-lf.mjs && node scripts/check-lib-sync.mjs && node scripts/check-unknown-keys.mjs && node scripts/check-removed-traces.mjs && node scripts/check-generators.mjs && node scripts/smoke.mjs && node scripts/check-preset.mjs && node scripts/verify-handdrawn.mjs && node scripts/red-probe.mjs && node scripts/check-line-text.mjs && node scripts/handdrawn-option-probe.mjs && node scripts/check-dsh-target.mjs && node scripts/check-workflows.mjs",
|
|
72
91
|
"test:fast": "node scripts/test-fast.mjs",
|
|
73
92
|
"test:release": "node scripts/test-release.mjs",
|
|
74
93
|
"test:map": "node scripts/test-map.mjs",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* dsh 目标版本一致性守卫(无网络、秒级)。
|
|
4
|
+
*
|
|
5
|
+
* 事实源 = dsh-target.json(CI install-path 的被测 DSH 版本)。
|
|
6
|
+
* 本脚本强制三处一致,防"改了预设忘了改钉版"这类假红/漏测:
|
|
7
|
+
* ① dsh-target.json 的 dsh
|
|
8
|
+
* ② docs/14-发布清单(草稿).md 里的 `dsh-target: <ver>` 行
|
|
9
|
+
* ③ package.json 的 dsh.engines.dsh 下限 ≤ 目标版本(下限写高了会把老宿主挡在门外)
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from 'node:fs'
|
|
12
|
+
import { dirname, join } from 'node:path'
|
|
13
|
+
import { fileURLToPath } from 'node:url'
|
|
14
|
+
|
|
15
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
16
|
+
const fail = []
|
|
17
|
+
const read = (p) => readFileSync(join(ROOT, p), 'utf8')
|
|
18
|
+
|
|
19
|
+
const target = JSON.parse(read('dsh-target.json')).dsh
|
|
20
|
+
if (typeof target !== 'string' || !/^\d+\.\d+\.\d+(-[0-9a-z.-]+)?$/i.test(target)) {
|
|
21
|
+
fail.push(`dsh-target.json 的 dsh 不是合法版本号:${JSON.stringify(target)}`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const docLine = /`dsh-target:\s*([0-9][0-9a-z.\-]*)`/i.exec(read('docs/14-发布清单(草稿).md'))
|
|
25
|
+
if (!docLine) fail.push('docs/14 缺少 `dsh-target: <版本>` 行(与 dsh-target.json 同源)')
|
|
26
|
+
else if (docLine[1] !== target) fail.push(`docs/14 的 dsh-target=${docLine[1]} 与 dsh-target.json=${target} 不一致`)
|
|
27
|
+
|
|
28
|
+
const engines = JSON.parse(read('package.json'))?.dsh?.engines?.dsh ?? ''
|
|
29
|
+
const low = /(\d+\.\d+\.\d+(?:-[0-9a-z.-]+)?)/i.exec(engines)?.[1]
|
|
30
|
+
if (!low) fail.push('package.json 缺少 dsh.engines.dsh 下限')
|
|
31
|
+
else {
|
|
32
|
+
const cmp = (a, b) => {
|
|
33
|
+
const pa = a.split(/[.-]/).map((x) => (/^\d+$/.test(x) ? Number(x) : x))
|
|
34
|
+
const pb = b.split(/[.-]/).map((x) => (/^\d+$/.test(x) ? Number(x) : x))
|
|
35
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
36
|
+
const x = pa[i] ?? 0, y = pb[i] ?? 0
|
|
37
|
+
if (x === y) continue
|
|
38
|
+
if (typeof x === 'number' && typeof y === 'number') return x - y
|
|
39
|
+
return String(x).localeCompare(String(y))
|
|
40
|
+
}
|
|
41
|
+
return 0
|
|
42
|
+
}
|
|
43
|
+
if (cmp(low, target) > 0) fail.push(`dsh.engines.dsh 下限 ${low} 高于目标 ${target}(会把老宿主挡在门外)`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (fail.length) {
|
|
47
|
+
console.error('✗ dsh 目标版本一致性:失败')
|
|
48
|
+
for (const f of fail) console.error(' · ' + f)
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
console.log(`✓ dsh 目标版本一致:${target}(dsh-target.json = docs/14 = engines 下限 ${low})`)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* .github/workflows 语法守卫(无网络、秒级)。
|
|
4
|
+
*
|
|
5
|
+
* 为什么需要:2026-10-17 我在 dsh-next-probe.yml 里写了 `- name: Probe: xxx`(纯量里的 `: `),
|
|
6
|
+
* GitHub 判定工作流文件无效 ⇒ 显示为"文件路径"的失败运行 + "No jobs were run" 邮件。
|
|
7
|
+
* 这种错误**本地可查**,不该等到 push 之后由邮件告知。
|
|
8
|
+
*
|
|
9
|
+
* 校验:① YAML 可解析;② 顶层有 name / on / jobs;③ 每个 job 有 runs-on 与 steps;
|
|
10
|
+
* ④ 每个 step 有 uses 或 run;⑤ 所有 name 是字符串(含 `: ` 的纯量会被 YAML 解析器先抓住)。
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync, readdirSync } from 'node:fs'
|
|
13
|
+
import { dirname, join } from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import YAML from 'yaml'
|
|
16
|
+
|
|
17
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
18
|
+
const DIR = join(ROOT, '.github', 'workflows')
|
|
19
|
+
const fail = []
|
|
20
|
+
let files = []
|
|
21
|
+
try { files = readdirSync(DIR).filter((f) => /\.ya?ml$/.test(f)) } catch { fail.push(`找不到目录 ${DIR}`) }
|
|
22
|
+
|
|
23
|
+
const isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v)
|
|
24
|
+
for (const f of files) {
|
|
25
|
+
const rel = `.github/workflows/${f}`
|
|
26
|
+
let doc
|
|
27
|
+
try { doc = YAML.parse(readFileSync(join(DIR, f), 'utf8')) } catch (e) { fail.push(`${rel}: YAML 解析失败 — ${e.message.split('\n')[0]}`); continue }
|
|
28
|
+
if (!isPlainObject(doc)) { fail.push(`${rel}: 顶层不是映射`); continue }
|
|
29
|
+
for (const k of ['name', 'on', 'jobs']) if (!(k in doc)) fail.push(`${rel}: 缺少顶层键 ${k}`)
|
|
30
|
+
if (typeof doc.name !== 'string' || !doc.name.trim()) fail.push(`${rel}: name 必须是非空字符串`)
|
|
31
|
+
if (!isPlainObject(doc.jobs) || Object.keys(doc.jobs).length === 0) fail.push(`${rel}: jobs 必须是非空映射`)
|
|
32
|
+
for (const [jid, job] of Object.entries(doc.jobs ?? {})) {
|
|
33
|
+
if (!isPlainObject(job)) { fail.push(`${rel}: jobs.${jid} 不是映射`); continue }
|
|
34
|
+
if (!('runs-on' in job) && !('uses' in job)) fail.push(`${rel}: jobs.${jid} 缺少 runs-on / uses`)
|
|
35
|
+
if ('uses' in job) continue
|
|
36
|
+
if (!Array.isArray(job.steps) || job.steps.length === 0) fail.push(`${rel}: jobs.${jid} 缺少 steps`)
|
|
37
|
+
for (const [i, st] of (job.steps ?? []).entries()) {
|
|
38
|
+
if (!isPlainObject(st)) { fail.push(`${rel}: jobs.${jid}.steps[${i}] 不是映射`); continue }
|
|
39
|
+
if (!('uses' in st) && !('run' in st)) fail.push(`${rel}: jobs.${jid}.steps[${i}] 既无 uses 也无 run`)
|
|
40
|
+
if ('name' in st && typeof st.name !== 'string') fail.push(`${rel}: jobs.${jid}.steps[${i}].name 不是字符串`)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (fail.length) {
|
|
46
|
+
console.error('✗ 工作流语法检查:失败')
|
|
47
|
+
for (const f of fail) console.error(' · ' + f)
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
console.log(`✓ 工作流语法:${files.length} 个文件全部可解析且结构合规`)
|
package/scripts/smoke.mjs
CHANGED
|
@@ -2331,12 +2331,12 @@ ok('bundle:已转为公开发布 npm(无 private + publishConfig.access=publ
|
|
|
2331
2331
|
ok('§40 自定位行:入口文件真实存在(拿不到入口就不追加该行——宁可不隔离,也不加一行注定 broken 的)',
|
|
2332
2332
|
selfRow !== null && existsSync(fileURLToPath(selfRow.name)), String(selfRow?.name))
|
|
2333
2333
|
|
|
2334
|
-
// ── 缺省档(auto):声明
|
|
2334
|
+
// ── 缺省档(auto):声明 23 行(22 + 自定位行)→ 预设档实例未出现 ⇒ 自动回落全局 ────────────
|
|
2335
2335
|
resetAll()
|
|
2336
2336
|
indexMod.apply(makeCtx(), { presetIds: ['ppt'] })
|
|
2337
2337
|
await flush() // 声明走的是异步队列(inject → register),让它落地
|
|
2338
|
-
ok('§40 缺省档:声明
|
|
2339
|
-
declared.length === 1 && declared[0]?.plugins?.length ===
|
|
2338
|
+
ok('§40 缺省档:声明 23 行 = 22 条 standard 组合行 + 第 23 行自定位行("只在预设里可见"靠它)',
|
|
2339
|
+
declared.length === 1 && declared[0]?.plugins?.length === 23 && declared[0]?.plugins?.at(-1)?.id === 'ppt-studio-scope',
|
|
2340
2340
|
`声明=${declared.length} 次|行数=${declared[0]?.plugins?.length}|末行=${declared[0]?.plugins?.at(-1)?.id ?? '(无)'}`)
|
|
2341
2341
|
ok('§40 缺省档:健康判定期间**不注册**能力面(隔离生效时 profile 档必须零能力面)',
|
|
2342
2342
|
tools === 0 && cmds === 0 && skills === 0 && listeners === 0,
|
|
@@ -2380,14 +2380,14 @@ ok('bundle:已转为公开发布 npm(无 private + publishConfig.access=publ
|
|
|
2380
2380
|
typeof globalThis.__pptScopeApplied?.at === 'number' && globalThis.__pptScopeApplied.at > 0,
|
|
2381
2381
|
JSON.stringify(globalThis.__pptScopeApplied))
|
|
2382
2382
|
|
|
2383
|
-
// ── 传统档(scope=profile):不隔离、能力面全局,声明回到
|
|
2383
|
+
// ── 传统档(scope=profile):不隔离、能力面全局,声明回到 22 行(不含自定位行)──────────────
|
|
2384
2384
|
resetAll()
|
|
2385
2385
|
const trBase = { tools, skills }
|
|
2386
2386
|
indexMod.apply(makeCtx(), { scope: 'profile' })
|
|
2387
2387
|
await grace()
|
|
2388
|
-
ok('§40 传统档:能力面注册在装入层(全局可见),且声明**不含**自定位行(
|
|
2388
|
+
ok('§40 传统档:能力面注册在装入层(全局可见),且声明**不含**自定位行(22 行)',
|
|
2389
2389
|
tools - trBase.tools === 22 && skills - trBase.skills === 17 && declared.length === 1
|
|
2390
|
-
&& declared[0]?.plugins?.length ===
|
|
2390
|
+
&& declared[0]?.plugins?.length === 22 && declared[0]?.plugins?.at(-1)?.id !== 'ppt-studio-scope'
|
|
2391
2391
|
&& presetMod.presetDeliveryStatus().isolation === 'profile',
|
|
2392
2392
|
`增量 tools=${tools - trBase.tools} skills=${skills - trBase.skills}|行数=${declared[0]?.plugins?.length}|isolation=${presetMod.presetDeliveryStatus().isolation}`)
|
|
2393
2393
|
|
|
@@ -2426,8 +2426,8 @@ ok('bundle:已转为公开发布 npm(无 private + publishConfig.access=publ
|
|
|
2426
2426
|
// ── 0.1.7 的**真适配**:预设靠"向注册表声明"交付(写目录那条路已被上游删除)────────────
|
|
2427
2427
|
// 判据分三层:① 真的调到 register 了;② 声明形状对(id/名/序/行数/内容);③ 双挂载不会声明两次。
|
|
2428
2428
|
const declDirect = presetMod.presetDeclaration()
|
|
2429
|
-
ok('§40 预设组合:名字/简介/序号取自包内 `agent-presets/ppt/`(
|
|
2430
|
-
declDirect.name === 'PPT 工作室' && declDirect.order === 2 && declDirect.plugins.length ===
|
|
2429
|
+
ok('§40 预设组合:名字/简介/序号取自包内 `agent-presets/ppt/`(22 条 standard 组合行;选择器里显示的就是这些)',
|
|
2430
|
+
declDirect.name === 'PPT 工作室' && declDirect.order === 2 && declDirect.plugins.length === 22
|
|
2431
2431
|
&& typeof declDirect.description === 'string' && declDirect.description.length > 10,
|
|
2432
2432
|
`name=${declDirect.name}|order=${declDirect.order}|行数=${declDirect.plugins.length}|desc=${declDirect.description.length} 字`)
|
|
2433
2433
|
// `!!js` 必须**保真**成 Loader 认的 `{__jsExpr}`:降级成普通字符串会让
|
|
@@ -95,3 +95,13 @@ node scripts/smoke.mjs
|
|
|
95
95
|
- **G3 创建 Release 就等于发布** ✗ —— 未获用户明确指令不得创建 Release;撤回需**删 Release + `gh run cancel`**(本地 pack 的 tag 不会自动发)。
|
|
96
96
|
- **G4 skill 的 frontmatter 是 YAML** ✗ —— 描述里写 `role: decoration` 这类含 **`: `** 的纯量会导致该 skill **静默解析失败**(症状:14 个对、1 个不见)⇒ 描述里用全角「:」或加引号。
|
|
97
97
|
- **G5 用自己的注册器自证** ✓ —— 改完 skill 立刻跑 `bundledSkillNames()`(插件自带扫描器)确认数量与名字,**别等装到用户机器上才发现**。
|
|
98
|
+
|
|
99
|
+
## H. DSH 升级适配坑(2026-10-17)
|
|
100
|
+
|
|
101
|
+
- **H1 升级会改标准预设** ✗ —— 0.2.1-rc.2 把 standard 组合行 19→22,且参照位置迁到 `dsh-web-app/presets/standard.patch.yml` 的 `config.plugins` ⇒ 升级后**先跑 `check-preset`**,再按新基准重写 `agent-presets/ppt/agent.cordis.yml`(保留 `persona` 有意差异)。
|
|
102
|
+
- **H2 别拿旧清单判版本** ✗ —— `~/.dsh/dsh-runtimes/*/runtime.json` 的 `desktopVersion` 可能是旧的;运行宿主版本看 `app.asar/package.json` + `plugin_manager list_bundles` 的 bundle 版本。
|
|
103
|
+
- **H3 全局 npm CLI 中断安装会坏** ✗ —— 超时/断网会留下残缺树 + `EEXIST` shim,表现为 `Cannot find module ...lib/bin.js`;修法:删 `%APPDATA%\npm\dsh*` 与 `node_modules\@deepseek-ai\dsh` 后**后台**重装;且**不要用 `| tail` 之类的管道去取退出码**。
|
|
104
|
+
- **H4 verify:products 需要导出目录存在** ✗ —— 缺 `ppt-deliverable/手画12+1/` 会在导出阶段 ENOENT;先建目录再跑。
|
|
105
|
+
- **H5 历史生成器会写 skills** ✗ —— `examples/hand-drawn/gen/s3.cjs` 曾直接写 `skills/*`;已加 `ALLOW_HISTORICAL=1` 保护、默认跳过,防误跑覆盖正式 skill。
|
|
106
|
+
|
|
107
|
+
- **H6 CI 被测的 DSH 版本必须是单源、并与目标端一致** ✗ —— 版本写在 `dsh-target.json`(唯一事实源),CI 用 `node -p` 读它;`docs/14` 的 `dsh-target:` 行与 `dsh.engines.dsh` 下限由 `scripts/check-dsh-target.mjs`(并入 `npm test`)强制一致。历史上曾把手写死版钉在 `0.1.6-alpha.2`,而预设来自 0.2.1 ⇒ 旧 mount base 解析不到新行 ⇒ 安装自证 27/1 假红;另设 `dsh-next-probe.yml` 用 `@next` 周检(只告警、不拦 PR),提前暴露「上游已变、该适配了」。
|