From 928c99876e8e3a66a730f759236525f7944a4a0f Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 5 Aug 2026 21:50:44 +0800 Subject: [PATCH] feat: add optional dsh badge skill provider --- ...26-08-06-bundled-dsh-badge-skill.i18n.yaml | 6 + .../2026-08-06-bundled-dsh-badge-skill.md | 25 +++ .../2026-08-06-bundled-dsh-badge-skill.zh.md | 25 +++ apps/cli/composition.md | 3 + apps/cli/config/base.cordis.yml | 4 + apps/cli/package.json | 1 + apps/cli/tests/dsh-badge.snapshot.ts | 173 ++++++++++++++++++ apps/cli/tests/fixtures/dsh-badge/cordis.yml | 9 + .../fixtures/dsh-badge/default.cordis.yml | 6 + apps/cli/tests/fixtures/dsh-badge/snapshot.ts | 53 ++++++ docs/capability-seams.md | 4 +- docs/config-catalog.md | 1 + docs/module-graph.md | 4 + knip.json | 3 +- packages/skill/README.i18n.yaml | 4 +- packages/skill/README.md | 1 + packages/skill/README.zh.md | 1 + packages/skill/skill-badge/README.i18n.yaml | 6 + packages/skill/skill-badge/README.md | 22 +++ packages/skill/skill-badge/README.zh.md | 22 +++ .../skill/skill-badge/assets/dsh-badge.md | 31 ++++ .../skill/skill-badge/assets/dsh-badge.png | Bin 0 -> 12339 bytes packages/skill/skill-badge/package.json | 37 ++++ packages/skill/skill-badge/src/index.ts | 60 ++++++ packages/skill/skill-badge/src/invariant.ts | 30 +++ .../skill-badge/tests/skill-badge.spec.ts | 40 ++++ packages/skill/skill-badge/tsconfig.json | 14 ++ pnpm-lock.yaml | 15 ++ scripts/check-workspace-constraints.ts | 1 + scripts/gen-doc-graphs.ts | 2 +- .../verify-package-readme-model-experience.ts | 1 + tsconfig.host.json | 1 + vitest.snapshot.config.ts | 1 + 33 files changed, 601 insertions(+), 5 deletions(-) create mode 100644 .agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.i18n.yaml create mode 100644 .agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.md create mode 100644 .agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.zh.md create mode 100644 apps/cli/tests/dsh-badge.snapshot.ts create mode 100644 apps/cli/tests/fixtures/dsh-badge/cordis.yml create mode 100644 apps/cli/tests/fixtures/dsh-badge/default.cordis.yml create mode 100644 apps/cli/tests/fixtures/dsh-badge/snapshot.ts create mode 100644 packages/skill/skill-badge/README.i18n.yaml create mode 100644 packages/skill/skill-badge/README.md create mode 100644 packages/skill/skill-badge/README.zh.md create mode 100644 packages/skill/skill-badge/assets/dsh-badge.md create mode 100644 packages/skill/skill-badge/assets/dsh-badge.png create mode 100644 packages/skill/skill-badge/package.json create mode 100644 packages/skill/skill-badge/src/index.ts create mode 100644 packages/skill/skill-badge/src/invariant.ts create mode 100644 packages/skill/skill-badge/tests/skill-badge.spec.ts create mode 100644 packages/skill/skill-badge/tsconfig.json diff --git a/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.i18n.yaml b/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.i18n.yaml new file mode 100644 index 0000000000..bdc103ef46 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.md +2026-08-06-bundled-dsh-badge-skill.md: afe0b21d64a414a9e78ef55459a42c0d3817e3fd +2026-08-06-bundled-dsh-badge-skill.zh.md: de1ec989570b07987b12f0a291c84643aa5531fd diff --git a/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.md b/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.md new file mode 100644 index 0000000000..afe0b21d64 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.md @@ -0,0 +1,25 @@ +# Agent Note: Bundled dsh badge skill + +Status: implemented + +English | [中文](2026-08-06-bundled-dsh-badge-skill.zh.md) + +## Problem + +DeepSeek Harness has an official attribution badge skill, but keeping it only in a developer's personal skill directory makes it unavailable to other DSH installations and gives the shipped application no explicit opt-in point. + +## Decision + +`@deepseek-ai/dsh-skill-badge` is a native Cordis plugin that registers one immutable bundled provider on `ctx.skills`. The provider owns the `dsh-badge` summary, instruction body, and PNG resource base; `dsh-tool-skill` remains the sole owner of model-facing catalog and loader rendering. + +The shipped CLI composition declares `skill-badge` as disabled. Enabling that existing row is the explicit opt-in; disabled installations advertise no badge skill and gain no model-visible content. + +The provider uses the bundled rank after project, custom, and user filesystem sources, so a user-owned `dsh-badge` definition can override it through the ordinary registry precedence contract. Provider disposal removes the contribution through the registry-owned effect. + +## Alternatives considered + +A Codex marketplace plugin was rejected because it would install into a different runtime and would not participate in DSH's `ctx.skills` seam. Mounting `dsh-skill-local` over the packaged files was rejected because filesystem discovery, parsing, and watching add lifecycle machinery that an immutable single-skill provider does not need. + +## Consequences + +The badge instructions and source PNG are versioned with DSH and resolve through a packaged directory resource base. The provider has no configuration surface. Package tests pin provider lifecycle and the official PNG bytes, while a keyless assembled-application snapshot pins the enabled catalog and loaded skill body. diff --git a/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.zh.md b/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.zh.md new file mode 100644 index 0000000000..de1ec98957 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-08-06-bundled-dsh-badge-skill.zh.md @@ -0,0 +1,25 @@ +# Agent Note: 内置 dsh 徽章 skill + +Status: implemented + +[English](2026-08-06-bundled-dsh-badge-skill.md) | 中文 + +## 问题 + +DeepSeek Harness 已有官方署名徽章 skill(技能),但如果它只保存在某位开发者的个人 skill 目录中,其他 DSH 安装实例便无法使用,交付的应用也没有显式的选择加入点。 + +## 决策 + +`@deepseek-ai/dsh-skill-badge` 是一个原生 Cordis 插件,会在 `ctx.skills` 上注册一个不可变的内置提供方。该提供方负责 `dsh-badge` 的摘要、指令正文和 PNG 资源基底;`dsh-tool-skill` 仍是面向模型的目录与 loader 渲染的唯一归属方。 + +交付的 CLI(命令行界面)组合将 `skill-badge` 声明为禁用。启用这个现有配置行就是显式选择加入;禁用它的安装实例不会公开任何徽章 skill,也不会获得任何模型可见内容。 + +该提供方使用排在项目、自定义及用户文件系统来源之后的内置 rank,因此用户自有的 `dsh-badge` 定义可通过注册表的常规优先级契约覆盖它。提供方释放时,注册表拥有的 effect 会移除该贡献。 + +## 曾考虑的替代方案 + +未采用 Codex marketplace 插件,因为它会安装到不同的运行时,无法参与 DSH 的 `ctx.skills` seam。未采用使用 `dsh-skill-local` 挂载随包文件的方案,因为文件系统发现、解析和监视会引入不必要的生命周期机制,而不可变的单一 skill 提供方并不需要这些机制。 + +## 后果 + +徽章指令和源 PNG 随 DSH 一同纳入版本管理,并通过以随包目录为基础的资源基底解析。该提供方没有配置面。包测试固定提供方生命周期和官方 PNG 的字节内容;无密钥的组装应用快照则固定启用后的目录和已加载的 skill 正文。 diff --git a/apps/cli/composition.md b/apps/cli/composition.md index 28f58bcf4d..8edf39a07a 100644 --- a/apps/cli/composition.md +++ b/apps/cli/composition.md @@ -72,6 +72,8 @@ flowchart LR cfg --> plugin_dsh_base_skill plugin_dsh_base_skill_local["skill-local
@deepseek-ai/dsh-skill-local"] cfg --> plugin_dsh_base_skill_local + plugin_dsh_base_skill_badge["skill-badge
@deepseek-ai/dsh-skill-badge"] + cfg --> plugin_dsh_base_skill_badge plugin_dsh_base_tool_skill["tool-skill
@deepseek-ai/dsh-tool-skill"] cfg --> plugin_dsh_base_tool_skill plugin_dsh_base_commands["commands
@deepseek-ai/dsh-commands"] @@ -182,6 +184,7 @@ flowchart LR | `workspace-context` | `@deepseek-ai/dsh-workspace-context` | | `skill` | `@deepseek-ai/dsh-skill` | | `skill-local` | `@deepseek-ai/dsh-skill-local` | +| `skill-badge` | `@deepseek-ai/dsh-skill-badge` | | `tool-skill` | `@deepseek-ai/dsh-tool-skill` | | `commands` | `@deepseek-ai/dsh-commands` | | `goal` | `@deepseek-ai/dsh-goal` | diff --git a/apps/cli/config/base.cordis.yml b/apps/cli/config/base.cordis.yml index dddf2fc1b5..f5f39a6ae5 100644 --- a/apps/cli/config/base.cordis.yml +++ b/apps/cli/config/base.cordis.yml @@ -204,6 +204,10 @@ - id: skill-local name: '@deepseek-ai/dsh-skill-local' +- id: skill-badge + name: '@deepseek-ai/dsh-skill-badge' + disabled: true + - id: tool-skill name: '@deepseek-ai/dsh-tool-skill' diff --git a/apps/cli/package.json b/apps/cli/package.json index 4ba1da7e86..dfe35e6159 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -92,6 +92,7 @@ "@deepseek-ai/dsh-session-title-first-message-llm": "workspace:^", "@deepseek-ai/dsh-settings-local": "workspace:^", "@deepseek-ai/dsh-skill": "workspace:^", + "@deepseek-ai/dsh-skill-badge": "workspace:^", "@deepseek-ai/dsh-skill-local": "workspace:^", "@deepseek-ai/dsh-spill-local": "workspace:^", "@deepseek-ai/dsh-spill-policy": "workspace:^", diff --git a/apps/cli/tests/dsh-badge.snapshot.ts b/apps/cli/tests/dsh-badge.snapshot.ts new file mode 100644 index 0000000000..d78f4c743d --- /dev/null +++ b/apps/cli/tests/dsh-badge.snapshot.ts @@ -0,0 +1,173 @@ +import { fileURLToPath } from 'node:url' +import { describe, expect, it } from 'vitest' +import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke' +const binScript = fileURLToPath(new URL('./fixtures/dsh-badge/snapshot.ts', import.meta.url)) +const configPath = fileURLToPath(new URL('./fixtures/dsh-badge/cordis.yml', import.meta.url)) +const defaultConfigPath = fileURLToPath(new URL('./fixtures/dsh-badge/default.cordis.yml', import.meta.url)) +const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url)) +const badgeAssetsPath = fileURLToPath(new URL('../../../packages/skill/skill-badge/assets/', import.meta.url)) + +describe('dsh badge assembled snapshot', () => { + it('advertises and loads the opt-in bundled skill through the shipped app', async () => { + const disabled = await runLoaderSmoke({ + label: 'disabled dsh badge skill snapshot', + tempDirPrefix: 'headless-snapshot-dsh-badge-disabled-', + binScript, + libBinScript: binScript, + configPath: defaultConfigPath, + tsconfigPath, + }) + const enabled = await runLoaderSmoke({ + label: 'dsh badge skill snapshot', + tempDirPrefix: 'headless-snapshot-dsh-badge-', + binScript, + libBinScript: binScript, + configPath, + tsconfigPath, + }) + const disabledSnapshot = JSON.parse(disabled.stdout) as unknown + const enabledSnapshot = JSON.parse( + enabled.stdout.replaceAll(badgeAssetsPath, '{{badgeAssetsPath}}'), + ) as unknown + + expect(disabled.stderr).toBe('') + expect(enabled.stderr).toBe('') + expect(disabledSnapshot).toMatchInlineSnapshot(` + { + "result": { + "content": [ + { + "text": "Error: skill "dsh-badge" is unknown or no longer available", + "type": "text", + }, + ], + "error": { + "message": "skill "dsh-badge" is unknown or no longer available", + }, + "isError": true, + }, + } + `) + expect(enabledSnapshot).toMatchInlineSnapshot(` + { + "catalog": [ + { + "text": " + A skill is a reusable set of task-specific instructions. The following skills are available in this session: + + + - \`dsh-badge\`: Add the official “powered by dsh” badge to documents, pull requests, merge requests, and other content produced with DeepSeek Harness. Use whenever creating a pull request or merge request. Also use when the user asks for a dsh badge, powered-by-dsh attribution, or a reusable dsh badge asset or snippet. + + + If the user names a skill, or the task clearly matches a skill's description, call the \`skill\` tool with the exact skill name before taking task actions. Load all applicable skills, then follow their full instructions. This catalog contains summaries only; do not infer or follow a skill's instructions until it has been loaded. + ", + "type": "text", + }, + ], + "result": { + "content": [ + { + "text": " + + Base directory for this skill: {{badgeAssetsPath}} + Resolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed. + + + + # dsh Badge + + Add the official “powered by dsh” badge without recreating or restyling it. + + ## Assets + + - Local PNG: [\`dsh-badge.png\`](dsh-badge.png), 726×120 source image; render at 121×20 + - Shields.io image URL: \`https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white\` + - Project URL: \`https://github.com/deepseek-harness/deepseek-harness\` + + ## Markdown + + Use this linked badge in Markdown: + + \`\`\`markdown + [![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-harness/deepseek-harness) + \`\`\` + + If attribution should not be linked, use: + + \`\`\`markdown + ![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white) + \`\`\` + + ## Usage rules + + - For GitHub or GitLab Markdown, use the Shields.io URL and link it to the project URL unless the user asks for an unlinked image. + - For Feishu and other systems that import remote images unreliably, upload \`dsh-badge.png\` from this skill directory instead of generating another badge. + - Preserve the badge's 121×20 dimensions and aspect ratio. + - Place the badge at the end of the attributed document or section unless the user specifies another position. + - Do not substitute another color, logo, label, or project URL. + + + ", + "type": "text", + }, + ], + "isError": false, + "value": { + "content": "# dsh Badge + + Add the official “powered by dsh” badge without recreating or restyling it. + + ## Assets + + - Local PNG: [\`dsh-badge.png\`](dsh-badge.png), 726×120 source image; render at 121×20 + - Shields.io image URL: \`https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white\` + - Project URL: \`https://github.com/deepseek-harness/deepseek-harness\` + + ## Markdown + + Use this linked badge in Markdown: + + \`\`\`markdown + [![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-harness/deepseek-harness) + \`\`\` + + If attribution should not be linked, use: + + \`\`\`markdown + ![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white) + \`\`\` + + ## Usage rules + + - For GitHub or GitLab Markdown, use the Shields.io URL and link it to the project URL unless the user asks for an unlinked image. + - For Feishu and other systems that import remote images unreliably, upload \`dsh-badge.png\` from this skill directory instead of generating another badge. + - Preserve the badge's 121×20 dimensions and aspect ratio. + - Place the badge at the end of the attributed document or section unless the user specifies another position. + - Do not substitute another color, logo, label, or project URL. + ", + "name": "dsh-badge", + "provider": "dsh-badge", + "resourceBase": { + "kind": "directory", + "path": "{{badgeAssetsPath}}", + }, + }, + }, + "summary": { + "description": "Add the official “powered by dsh” badge to documents, pull requests, merge requests, and other content produced with DeepSeek Harness. Use whenever creating a pull request or merge request. Also use when the user asks for a dsh badge, powered-by-dsh attribution, or a reusable dsh badge asset or snippet.", + "invocation": { + "modelInvocable": true, + "userInvocable": true, + }, + "name": "dsh-badge", + "provider": "dsh-badge", + "resourceBase": { + "kind": "directory", + "path": "{{badgeAssetsPath}}", + }, + "source": "bundled", + }, + } + `) + }, LOADER_SMOKE_TEST_TIMEOUT_MS) +}) diff --git a/apps/cli/tests/fixtures/dsh-badge/cordis.yml b/apps/cli/tests/fixtures/dsh-badge/cordis.yml new file mode 100644 index 0000000000..b3bfdb1b04 --- /dev/null +++ b/apps/cli/tests/fixtures/dsh-badge/cordis.yml @@ -0,0 +1,9 @@ +- id: skill-badge + disabled: false + +- id: skill-local + config: + watch: false + +- id: telemetry-otel + disabled: true diff --git a/apps/cli/tests/fixtures/dsh-badge/default.cordis.yml b/apps/cli/tests/fixtures/dsh-badge/default.cordis.yml new file mode 100644 index 0000000000..ac3e48441a --- /dev/null +++ b/apps/cli/tests/fixtures/dsh-badge/default.cordis.yml @@ -0,0 +1,6 @@ +- id: skill-local + config: + watch: false + +- id: telemetry-otel + disabled: true diff --git a/apps/cli/tests/fixtures/dsh-badge/snapshot.ts b/apps/cli/tests/fixtures/dsh-badge/snapshot.ts new file mode 100644 index 0000000000..99379b4fe4 --- /dev/null +++ b/apps/cli/tests/fixtures/dsh-badge/snapshot.ts @@ -0,0 +1,53 @@ +import { fileURLToPath } from 'node:url' +import { Context } from 'cordis' +import { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent' +import { CallId } from '@deepseek-ai/dsh-llm' +import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot' +import { SessionId } from '@deepseek-ai/dsh-session' +import type {} from '@deepseek-ai/dsh-skill' +import type {} from '@deepseek-ai/dsh-tools' + +const overlayPath = process.argv[2] +if (overlayPath === undefined) throw new Error('dsh-badge snapshot requires an overlay path') +const baseConfigPath = fileURLToPath(new URL('../../../config/base.cordis.yml', import.meta.url)) +const ctx = await boot('dsh-badge-snapshot', baseConfigPath, loadOverlayPatches('dsh-badge-snapshot', overlayPath)) + +try { + const agentId = SessionId('dsh-badge-snapshot') + const session = ctx.sessions.create(agentId, { meta: { cwd: process.cwd() } }) + const agent: Agent = { + ctx: new Context(), + id: agentId, + options: {}, + session, + inbox: new Inbox(session, { inserted: () => {}, discarded: () => {}, claimed: () => {} }), + status: 'idle', + send: () => {}, + followup: () => {}, + steer: () => {}, + inject: () => { throw new Error('dsh-badge snapshot must receive the catalog at the step boundary') }, + cancel: () => {}, + runMaintenance: task => task(new AbortController().signal), + whenIdle: () => Promise.resolve(), + } + const decision = await agentEvents(ctx, agent).waterfall( + 'agent/pre-step', + [], + { turn: 1, step: 1, signal: new AbortController().signal }, + () => Promise.resolve({ kind: 'enter' as const, messages: [] }), + ) + const catalog = decision.kind === 'enter' + ? decision.messages.find(message => message.role === 'user' + && message.source.kind === 'skill-catalog')?.content + : undefined + const summary = (await ctx.skills.list()).find(skill => skill.name === 'dsh-badge') + const result = await ctx.tools.execute({ + callId: CallId('dsh-badge-snapshot'), + name: 'skill', + arguments: { name: 'dsh-badge' }, + signal: new AbortController().signal, + }) + process.stdout.write(`${JSON.stringify({ catalog, summary, result })}\n`) +} finally { + await ctx.fiber.dispose() +} diff --git a/docs/capability-seams.md b/docs/capability-seams.md index cb24cee7d5..a5f20b5349 100644 --- a/docs/capability-seams.md +++ b/docs/capability-seams.md @@ -89,6 +89,7 @@ flowchart LR svc_sessionProjectionCache["ctx.sessionProjectionCache
Persisted projection cache"] pkg_skill["skill"] svc_skills["ctx.skills
Skill provider registry"] + pkg_skill_badge["skill-badge"] pkg_skill_local["skill-local"] svc_agents["ctx.agents
Agent service"] pkg_acp["acp"] @@ -219,6 +220,7 @@ flowchart LR pkg_settings --> svc_settings pkg_settings_local --> svc_settings pkg_skill --> svc_skills + pkg_skill_badge --> svc_skills pkg_skill_local --> svc_skills pkg_spill --> svc_spillStore pkg_spill_local --> svc_spillStore @@ -373,7 +375,7 @@ flowchart LR | `ctx.commands` | `core` | [`commands`](../packages/ui/commands) | - | - | - | Plugins register direct human commands without sending invocations to the model. | | `ctx.sessionProjections` | `core` | [`session-projection`](../packages/session-projection/session-projection) | - | [`tool-todo`](../packages/todo/tool-todo), [`session-title`](../packages/session-title/session-title), [`host-apiproxy`](../packages/host/apiproxy) | - | Domains register state-driven fold units; the eager drive keeps per-session watermark states and api-proxy serves baselines and pushes changed values. | | `ctx.sessionProjectionCache` | `core` | [`session-projection-cache`](../packages/session-projection/session-projection-cache) | - | [`host-apiproxy`](../packages/host/apiproxy) | - | Durably checkpoints projection unit states per session (throttled + turn/end/detach mandatory points) and serves the cold-read ladder: cache row + persistence tail replay, so listings never load full logs. | -| `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`skill-local`](../packages/skill/skill-local) | [`tool-skill`](../packages/skill/tool-skill) | - | Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies. | +| `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`skill-badge`](../packages/skill/skill-badge), [`skill-local`](../packages/skill/skill-local) | [`tool-skill`](../packages/skill/tool-skill) | - | Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies. | | `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), [`cli-demo`](../packages/examples/cli-demo), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. | | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-spine-demo`](../packages/examples/agent-spine-demo) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. | | `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | Folds revisioned objective state from the session log and keeps live continuation activation process-local. | diff --git a/docs/config-catalog.md b/docs/config-catalog.md index ffa3d5bdd4..449addad8a 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -2362,6 +2362,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co - `@deepseek-ai/dsh-session` ([`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts)) - `@deepseek-ai/dsh-session-checkpoint-policy` — requires `llm` · `sessionPersistence` · `sessions` · `tools` ([`packages/session-persistence/session-checkpoint-policy/src/index.ts`](../packages/session-persistence/session-checkpoint-policy/src/index.ts)) - `@deepseek-ai/dsh-session-projection` ([`packages/session-projection/session-projection/src/index.ts`](../packages/session-projection/session-projection/src/index.ts)) +- `@deepseek-ai/dsh-skill-badge` — requires `skills` ([`packages/skill/skill-badge/src/index.ts`](../packages/skill/skill-badge/src/index.ts)) - `@deepseek-ai/dsh-storage` ([`packages/storage/storage/src/index.ts`](../packages/storage/storage/src/index.ts)) - `@deepseek-ai/dsh-subagent` ([`packages/subagent/subagent/src/index.ts`](../packages/subagent/subagent/src/index.ts)) - `@deepseek-ai/dsh-subprocess-local` ([`packages/subprocess/subprocess-local/src/index.ts`](../packages/subprocess/subprocess-local/src/index.ts)) diff --git a/docs/module-graph.md b/docs/module-graph.md index 0e2e7e0c37..7f3f68603f 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -56,6 +56,7 @@ flowchart TD end subgraph group_skill["packages/skill"] pkg_skill["skill"] + pkg_skill_badge["skill-badge"] pkg_skill_local["skill-local"] pkg_tool_skill["tool-skill"] end @@ -303,6 +304,8 @@ flowchart TD pkg_llm --> pkg_brand pkg_llm --> pkg_invariants pkg_llm --> pkg_timeout + pkg_skill_badge --> pkg_invariants + pkg_skill_badge --> pkg_skill pkg_client_connection --> pkg_host_webserver pkg_client_connection --> pkg_invariants pkg_client_hmr --> pkg_client_modules @@ -1106,6 +1109,7 @@ flowchart TD | [`typert-generator`](../packages/typert/generator) | `typert` | [`invariants`](../packages/support/invariants) | | [`typert-registry`](../packages/typert/registry) | `typert` | [`invariants`](../packages/support/invariants) | | [`llm`](../packages/llm/llm) | `llm` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`timeout`](../packages/util/timeout) | +| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/support/invariants), [`skill`](../packages/skill/skill) | | [`client-connection`](../packages/client/connection) | `client` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) | | [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) | | [`client-locale`](../packages/client/locale) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) | diff --git a/knip.json b/knip.json index dfb8058d7c..8d10010644 100644 --- a/knip.json +++ b/knip.json @@ -622,7 +622,8 @@ "apps/cli": { "entry": [ "tests/**/*.spec.ts", - "tests/**/*.e2e.ts" + "tests/**/*.e2e.ts", + "tests/**/*.snapshot.ts" ], "project": [ "src/**/*.ts", diff --git a/packages/skill/README.i18n.yaml b/packages/skill/README.i18n.yaml index 2d424c61dd..74875f2aa3 100644 --- a/packages/skill/README.i18n.yaml +++ b/packages/skill/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/skill/README.md -README.md: d10049ac3e741350fddb42f430b70f063da1d12d -README.zh.md: 67f2da6f75edecd180ff861122c6392684ed9bdb +README.md: 533904859ad998de4f371a073fde98b68660097b +README.zh.md: 1fad581cc61a05251f671577dcb7edab37281283 diff --git a/packages/skill/README.md b/packages/skill/README.md index d10049ac3e..533904859a 100644 --- a/packages/skill/README.md +++ b/packages/skill/README.md @@ -7,6 +7,7 @@ This family discovers reusable agent instructions and exposes them to the model | Package | Role | ctx key | |---|---|---| | [`skill/`](skill/README.md) | Defines skill provider registration and lookup | `ctx.skills` | +| [`skill-badge/`](skill-badge/README.md) | Contributes the optional bundled dsh badge skill | registers on `ctx.skills` | | [`skill-local/`](skill-local/README.md) | Discovers skills from local filesystems | registers on `ctx.skills` | | [`tool-skill/`](tool-skill/README.md) | Publishes the skill catalog and model-facing loader | registers on `ctx.tools` | diff --git a/packages/skill/README.zh.md b/packages/skill/README.zh.md index 67f2da6f75..1fad581cc6 100644 --- a/packages/skill/README.zh.md +++ b/packages/skill/README.zh.md @@ -7,6 +7,7 @@ | 包 | 职责 | ctx 键 | |---|---|---| | [`skill/`](skill/README.md) | 定义 skill 提供方注册和查找 | `ctx.skills` | +| [`skill-badge/`](skill-badge/README.md) | 贡献可选的内置 dsh 徽章 skill | 注册到 `ctx.skills` | | [`skill-local/`](skill-local/README.md) | 从本地文件系统发现 skill | 注册到 `ctx.skills` | | [`tool-skill/`](tool-skill/README.md) | 发布 skill 目录和面向模型的 loader | 注册到 `ctx.tools` | diff --git a/packages/skill/skill-badge/README.i18n.yaml b/packages/skill/skill-badge/README.i18n.yaml new file mode 100644 index 0000000000..4dda53481c --- /dev/null +++ b/packages/skill/skill-badge/README.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write packages/skill/skill-badge/README.md +README.md: 49b38023a7c110bb52bb351668905c702e251216 +README.zh.md: bf7eb0d7d4c0552c07f9cdf5665f8a20df829483 diff --git a/packages/skill/skill-badge/README.md b/packages/skill/skill-badge/README.md new file mode 100644 index 0000000000..49b38023a7 --- /dev/null +++ b/packages/skill/skill-badge/README.md @@ -0,0 +1,22 @@ +# @deepseek-ai/dsh-skill-badge + +English | [中文](README.zh.md) + +Optional bundled skill provider that contributes `dsh-badge` to `ctx.skills`. The skill supplies the official “powered by dsh” Markdown snippets and the packaged PNG for systems that cannot import a remote image reliably. + +Mount the plugin to enable the provider. It has no configuration. The shipped CLI composition includes the plugin as `disabled: true`; users must explicitly enable its `skill-badge` row before the skill enters a catalog. + +The provider exposes its packaged `assets/` directory as the skill resource base. `dsh-badge.png` is the 726×120 source asset, and consumers render it at 121×20. + +## Model Experience + +Indirectly, through `@deepseek-ai/dsh-tool-skill`, which renders the catalog entry and selected skill body. + +#### KV Cache effect + +Disabled by default, the plugin changes no request. When enabled, its catalog entry and any loaded body change the provider KV prefix at their insertion points. + +## Known Limitations and Deferred Work + +- The provider contributes one fixed skill and has no runtime customization. +- Remote Markdown uses Shields.io; use the packaged PNG when the target cannot fetch remote images reliably. diff --git a/packages/skill/skill-badge/README.zh.md b/packages/skill/skill-badge/README.zh.md new file mode 100644 index 0000000000..bf7eb0d7d4 --- /dev/null +++ b/packages/skill/skill-badge/README.zh.md @@ -0,0 +1,22 @@ +# @deepseek-ai/dsh-skill-badge + +[English](README.md) | 中文 + +可选的内置 skill(技能)提供方,向 `ctx.skills` 贡献 `dsh-badge`。该 skill 提供官方「powered by dsh」Markdown 片段和随包分发的 PNG,供无法可靠导入远程图片的系统使用。 + +挂载该插件即可启用提供方。它没有配置。交付的 CLI(命令行界面)组合以 `disabled: true` 包含该插件;用户必须显式启用其 `skill-badge` 配置行,该 skill 才会进入目录。 + +该提供方将随包分发的 `assets/` 目录作为 skill 资源基底公开。`dsh-badge.png` 是尺寸为 726×120 的源图资源,消费方以 121×20 的尺寸渲染。 + +## 模型体验 + +通过 `@deepseek-ai/dsh-tool-skill` 间接影响模型;该包会渲染目录条目和所选 skill 的正文。 + +#### KV Cache 影响 + +该插件默认禁用,不会改变任何请求。启用后,其目录条目和任何已加载正文都会在各自插入点改变提供方的 KV 前缀。 + +## 已知限制与暂缓事项 + +- 该提供方只贡献一个固定 skill,不提供运行时自定义。 +- 远程 Markdown 使用 Shields.io;当目标环境无法可靠获取远程图片时,请使用随包分发的 PNG。 diff --git a/packages/skill/skill-badge/assets/dsh-badge.md b/packages/skill/skill-badge/assets/dsh-badge.md new file mode 100644 index 0000000000..9905de1ed9 --- /dev/null +++ b/packages/skill/skill-badge/assets/dsh-badge.md @@ -0,0 +1,31 @@ +# dsh Badge + +Add the official “powered by dsh” badge without recreating or restyling it. + +## Assets + +- Local PNG: [`dsh-badge.png`](dsh-badge.png), 726×120 source image; render at 121×20 +- Shields.io image URL: `https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white` +- Project URL: `https://github.com/deepseek-harness/deepseek-harness` + +## Markdown + +Use this linked badge in Markdown: + +```markdown +[![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-harness/deepseek-harness) +``` + +If attribution should not be linked, use: + +```markdown +![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white) +``` + +## Usage rules + +- For GitHub or GitLab Markdown, use the Shields.io URL and link it to the project URL unless the user asks for an unlinked image. +- For Feishu and other systems that import remote images unreliably, upload `dsh-badge.png` from this skill directory instead of generating another badge. +- Preserve the badge's 121×20 dimensions and aspect ratio. +- Place the badge at the end of the attributed document or section unless the user specifies another position. +- Do not substitute another color, logo, label, or project URL. diff --git a/packages/skill/skill-badge/assets/dsh-badge.png b/packages/skill/skill-badge/assets/dsh-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..bf91ecca9790971072e946b56c01bbf99e26abd4 GIT binary patch literal 12339 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 Q1kOOnN`8?j7dH(0e_HocYybcN literal 0 HcmV?d00001 diff --git a/packages/skill/skill-badge/package.json b/packages/skill/skill-badge/package.json new file mode 100644 index 0000000000..b9dc53d9a5 --- /dev/null +++ b/packages/skill/skill-badge/package.json @@ -0,0 +1,37 @@ +{ + "name": "@deepseek-ai/dsh-skill-badge", + "description": "Bundled dsh badge skill provider for DeepSeek Harness", + "version": "0.0.1", + "private": true, + "type": "module", + "main": "lib/index.js", + "types": "lib/types/index.d.ts", + "exports": { + ".": { + "types": "./lib/types/index.d.ts", + "default": "./lib/index.js" + }, + "./invariant": { + "types": "./lib/types/invariant.d.ts", + "default": "./lib/invariant.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib/index.js", + "lib/invariant.js", + "assets", + "lib/types/**/*.d.ts" + ], + "license": "BSD-3-Clause", + "peerDependencies": { + "@deepseek-ai/dsh-invariants": "^0.0.1", + "@deepseek-ai/dsh-skill": "^0.0.1", + "cordis": "^4.0.0-rc.7" + }, + "devDependencies": { + "@deepseek-ai/dsh-invariants": "workspace:^", + "@deepseek-ai/dsh-skill": "workspace:^", + "cordis": "^4.0.0-rc.7" + } +} diff --git a/packages/skill/skill-badge/src/index.ts b/packages/skill/skill-badge/src/index.ts new file mode 100644 index 0000000000..27cfd29354 --- /dev/null +++ b/packages/skill/skill-badge/src/index.ts @@ -0,0 +1,60 @@ +/** + * Bundled `dsh-badge` skill provider. + * + * @module @deepseek-ai/dsh-skill-badge + */ + +import { readFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' +import type { Context } from 'cordis' +import type { + SkillCandidate, + SkillDefinition, + SkillProvider, +} from '@deepseek-ai/dsh-skill' + +const PROVIDER_NAME = 'dsh-badge' +const BUNDLED_RANK = 600 +const SKILL_BODY_URL = new URL('../assets/dsh-badge.md', import.meta.url) +const RESOURCE_BASE = { + kind: 'directory', + path: fileURLToPath(new URL('../assets/', import.meta.url)), +} as const +const INVOCATION = { modelInvocable: true, userInvocable: true } as const +const DESCRIPTION = 'Add the official “powered by dsh” badge to documents, pull requests, merge requests, and other content produced with DeepSeek Harness. Use whenever creating a pull request or merge request. Also use when the user asks for a dsh badge, powered-by-dsh attribution, or a reusable dsh badge asset or snippet.' +const CANDIDATE: SkillCandidate = { + name: 'dsh-badge', + description: DESCRIPTION, + invocation: INVOCATION, + provider: PROVIDER_NAME, + source: 'bundled', + resourceBase: RESOURCE_BASE, + rank: BUNDLED_RANK, + locator: SKILL_BODY_URL, +} + +const provider: SkillProvider = { + name: PROVIDER_NAME, + list: () => Promise.resolve([CANDIDATE]), + async get(_candidate): Promise { + return { + name: CANDIDATE.name, + description: CANDIDATE.description, + invocation: CANDIDATE.invocation, + provider: CANDIDATE.provider, + source: CANDIDATE.source, + resourceBase: RESOURCE_BASE, + content: await readFile(SKILL_BODY_URL, 'utf8'), + } + }, +} + +/** Cordis plugin name. */ +export const name = 'skill-badge' +/** Service required by the bundled provider. */ +export const inject = ['skills'] + +/** Register the bundled `dsh-badge` provider on `ctx.skills`. */ +export function apply(ctx: Context): void { + ctx.skills.registerProvider(() => provider) +} diff --git a/packages/skill/skill-badge/src/invariant.ts b/packages/skill/skill-badge/src/invariant.ts new file mode 100644 index 0000000000..c087d5917f --- /dev/null +++ b/packages/skill/skill-badge/src/invariant.ts @@ -0,0 +1,30 @@ +/** + * Package-owned invariant companion for `@deepseek-ai/dsh-skill-badge`. + * @module @deepseek-ai/dsh-skill-badge/invariant + */ + +/* jscpd:ignore-start */ +import type { Context } from 'cordis' +import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants' + +const PACKAGE_NAME = '@deepseek-ai/dsh-skill-badge' + +/** Cordis companion plugin name. */ +export const name = 'skill-badge-invariant' +/** Service required before the companion can reserve package ownership. */ +export const inject = ['invariants'] + +/** + * No runtime invariant: the package owns one immutable provider registration, + * while the skill registry owns registration uniqueness and lifecycle checks. + */ +const install: InvariantInstaller = () => {} + +/** + * Register this package's invariant companion. + * @param ctx - Cordis context carrying the invariant service. + * @returns the installed registration's disposer after setup succeeds. + */ +export const apply = (ctx: Context): Promise<() => void> => + Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install)) +/* jscpd:ignore-end */ diff --git a/packages/skill/skill-badge/tests/skill-badge.spec.ts b/packages/skill/skill-badge/tests/skill-badge.spec.ts new file mode 100644 index 0000000000..e4d62f1c89 --- /dev/null +++ b/packages/skill/skill-badge/tests/skill-badge.spec.ts @@ -0,0 +1,40 @@ +import { createHash } from 'node:crypto' +import { readFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' +import { Context } from 'cordis' +import { describe, expect, it } from 'vitest' +import SkillService from '@deepseek-ai/dsh-skill' +import * as SkillBadge from '@deepseek-ai/dsh-skill-badge' + +describe('dsh-skill-badge', () => { + it('registers and disposes the bundled badge skill', async () => { + const ctx = new Context() + await ctx.plugin(SkillService) + const fiber = await ctx.plugin(SkillBadge) + const resourcePath = fileURLToPath(new URL('../assets/', import.meta.url)) + + expect(await ctx.skills.list()).toEqual([{ + name: 'dsh-badge', + description: 'Add the official “powered by dsh” badge to documents, pull requests, merge requests, and other content produced with DeepSeek Harness. Use whenever creating a pull request or merge request. Also use when the user asks for a dsh badge, powered-by-dsh attribution, or a reusable dsh badge asset or snippet.', + invocation: { modelInvocable: true, userInvocable: true }, + provider: 'dsh-badge', + source: 'bundled', + resourceBase: { kind: 'directory', path: resourcePath }, + }]) + const loaded = await ctx.skills.get('dsh-badge') + expect(loaded?.content).toContain('Preserve the badge\'s 121×20 dimensions') + expect(loaded?.resourceBase).toEqual({ kind: 'directory', path: resourcePath }) + + await fiber.dispose() + expect(await ctx.skills.list()).toEqual([]) + }) + + it('ships the official 726×120 PNG unchanged', async () => { + const image = await readFile(new URL('../assets/dsh-badge.png', import.meta.url)) + expect(image.readUInt32BE(16)).toBe(726) + expect(image.readUInt32BE(20)).toBe(120) + expect(createHash('sha256').update(image).digest('hex')).toBe( + 'f2c4f5ec9cbe847c0c763545c4d839efa8485bc74203733d0a0e8259f233c653', + ) + }) +}) diff --git a/packages/skill/skill-badge/tsconfig.json b/packages/skill/skill-badge/tsconfig.json new file mode 100644 index 0000000000..cf6642f69e --- /dev/null +++ b/packages/skill/skill-badge/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/types" + }, + "include": ["src"], + "references": [ + { "path": "../../../vendor/cosmokit" }, + { "path": "../../../vendor/cordis" }, + { "path": "../skill" }, + { "path": "../../support/invariants" } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 208e43aa5f..ad6bd2250b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -365,6 +365,9 @@ importers: '@deepseek-ai/dsh-skill': specifier: workspace:^ version: link:../../packages/skill/skill + '@deepseek-ai/dsh-skill-badge': + specifier: workspace:^ + version: link:../../packages/skill/skill-badge '@deepseek-ai/dsh-skill-local': specifier: workspace:^ version: link:../../packages/skill/skill-local @@ -4846,6 +4849,18 @@ importers: specifier: ^4.0.0-rc.7 version: link:../../../vendor/cordis + packages/skill/skill-badge: + devDependencies: + '@deepseek-ai/dsh-invariants': + specifier: workspace:^ + version: link:../../support/invariants + '@deepseek-ai/dsh-skill': + specifier: workspace:^ + version: link:../skill + cordis: + specifier: ^4.0.0-rc.7 + version: link:../../../vendor/cordis + packages/skill/skill-local: dependencies: chokidar: diff --git a/scripts/check-workspace-constraints.ts b/scripts/check-workspace-constraints.ts index 6bd613c2ea..750abbc902 100644 --- a/scripts/check-workspace-constraints.ts +++ b/scripts/check-workspace-constraints.ts @@ -105,6 +105,7 @@ const packageFileExtras: Readonly> = { '@deepseek-ai/dsh-client-ui-theme': ['lib/styles'], '@deepseek-ai/dsh-helper': ['lib/assets'], '@deepseek-ai/dsh-pty-local': ['scripts/ensure-spawn-helper.mjs'], + '@deepseek-ai/dsh-skill-badge': ['assets'], '@deepseek-ai/dsh-scripts': [ 'lib/dev/tsdown-config.js', 'lib/local-plugin-loader-hooks.js', diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index 1dd7973fd5..98ef82bb8e 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -287,7 +287,7 @@ const SERVICE_ROLES: ServiceRole[] = [ pkg: 'skill', title: 'Skill provider registry', mode: 'seam', - implementations: ['skill-local'], + implementations: ['skill-badge', 'skill-local'], consumers: ['tool-skill'], note: 'Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies.', }, diff --git a/scripts/verify-package-readme-model-experience.ts b/scripts/verify-package-readme-model-experience.ts index 041972cb9f..e66e9a73d7 100644 --- a/scripts/verify-package-readme-model-experience.ts +++ b/scripts/verify-package-readme-model-experience.ts @@ -111,6 +111,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly> = { 'packages/telemetry/session-telemetry': { kind: 'none', reason: 'The seam observes the session stream and hands redacted copies outward; it registers no model surface.' }, 'packages/telemetry/session-telemetry-otel': { kind: 'none', reason: 'The backend forwards seam records into the OTel SDK pipeline and registers no model surface.' }, 'packages/skill/skill': { kind: 'indirect', reason: 'The provider registry delegates model rendering to dsh-tool-skill.' }, + 'packages/skill/skill-badge': { kind: 'indirect', reason: 'The bundled provider delegates model rendering to dsh-tool-skill.' }, 'packages/skill/skill-local': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-tool-skill.' }, 'packages/spill/spill': { kind: 'indirect', reason: 'The storage seam delegates model rendering to spill consumers.' }, 'packages/spill/spill-local': { kind: 'indirect', reason: 'The storage backend delegates model rendering to spill consumers.' }, diff --git a/tsconfig.host.json b/tsconfig.host.json index 4fcf71b680..8c11b48a46 100644 --- a/tsconfig.host.json +++ b/tsconfig.host.json @@ -135,6 +135,7 @@ { "path": "./packages/ui/permission" }, { "path": "./packages/core/tools" }, { "path": "./packages/skill/skill" }, + { "path": "./packages/skill/skill-badge" }, { "path": "./packages/skill/skill-local" }, { "path": "./packages/skill/tool-skill" }, { "path": "./packages/ui/tool-ask-user" }, diff --git a/vitest.snapshot.config.ts b/vitest.snapshot.config.ts index 455ecfb4d4..426ebedd8e 100644 --- a/vitest.snapshot.config.ts +++ b/vitest.snapshot.config.ts @@ -49,6 +49,7 @@ export default defineConfig({ // The assembled Web snapshot executes generated client bundles; source // mode remains the zero-build path, while lib mode requires a prior build. ...(process.env.DSH_EXAMPLE_MODE === 'lib' ? ['apps/web/tests/**/*.snapshot.ts'] : []), + 'apps/cli/tests/**/*.snapshot.ts', 'examples/*/tests/**/*.snapshot.ts', 'packages/sdk/*/tests/**/*.snapshot.ts', ],