@zfdx123/dsh-superpowers 1.0.0

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.
Files changed (59) hide show
  1. package/LICENSE +27 -0
  2. package/LICENSE.superpowers +21 -0
  3. package/README.md +109 -0
  4. package/README.zh.md +109 -0
  5. package/cordis.patch.yml +16 -0
  6. package/index.js +395 -0
  7. package/package.json +68 -0
  8. package/skills/brainstorming/SKILL.md +250 -0
  9. package/skills/brainstorming/scripts/frame-template.html +213 -0
  10. package/skills/brainstorming/scripts/helper.js +179 -0
  11. package/skills/brainstorming/scripts/server.cjs +781 -0
  12. package/skills/brainstorming/scripts/start-server.sh +209 -0
  13. package/skills/brainstorming/scripts/stop-server.sh +120 -0
  14. package/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  15. package/skills/brainstorming/visual-companion.md +299 -0
  16. package/skills/dispatching-parallel-agents/SKILL.md +167 -0
  17. package/skills/executing-plans/SKILL.md +64 -0
  18. package/skills/finishing-a-development-branch/SKILL.md +225 -0
  19. package/skills/receiving-code-review/SKILL.md +205 -0
  20. package/skills/requesting-code-review/SKILL.md +95 -0
  21. package/skills/requesting-code-review/code-reviewer.md +181 -0
  22. package/skills/subagent-driven-development/SKILL.md +568 -0
  23. package/skills/subagent-driven-development/implementer-prompt.md +154 -0
  24. package/skills/subagent-driven-development/re-review-prompt.md +115 -0
  25. package/skills/subagent-driven-development/scripts/review-package +46 -0
  26. package/skills/subagent-driven-development/scripts/sdd-workspace +40 -0
  27. package/skills/subagent-driven-development/scripts/task-brief +41 -0
  28. package/skills/subagent-driven-development/task-reviewer-prompt.md +207 -0
  29. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  30. package/skills/systematic-debugging/SKILL.md +283 -0
  31. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  32. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  33. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  34. package/skills/systematic-debugging/find-polluter.sh +72 -0
  35. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  36. package/skills/systematic-debugging/test-academic.md +14 -0
  37. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  38. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  39. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  40. package/skills/test-driven-development/SKILL.md +320 -0
  41. package/skills/test-driven-development/writing-good-tests.md +198 -0
  42. package/skills/using-git-worktrees/SKILL.md +167 -0
  43. package/skills/using-superpowers/SKILL.md +63 -0
  44. package/skills/using-superpowers/references/antigravity-tools.md +23 -0
  45. package/skills/using-superpowers/references/codex-tools.md +108 -0
  46. package/skills/using-superpowers/references/gemini-tools.md +63 -0
  47. package/skills/using-superpowers/references/hermes-tools.md +56 -0
  48. package/skills/using-superpowers/references/pi-tools.md +16 -0
  49. package/skills/verification-before-completion/SKILL.md +120 -0
  50. package/skills/writing-plans/SKILL.md +171 -0
  51. package/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  52. package/skills/writing-skills/SKILL.md +679 -0
  53. package/skills/writing-skills/anthropic-best-practices.md +1150 -0
  54. package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  55. package/skills/writing-skills/graphviz-conventions.dot +172 -0
  56. package/skills/writing-skills/persuasion-principles.md +187 -0
  57. package/skills/writing-skills/render-graphs.js +172 -0
  58. package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  59. package/test/index.test.js +332 -0
package/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-superpowers contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ The contents of skills/ are vendored unmodified from https://github.com/obra/superpowers
26
+ (v6.3.0, commit b36e0829c6d0140e93cfef2ca599b1b07d4a7797) and remain under their own
27
+ MIT license, reproduced in LICENSE.superpowers.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jesse Vincent
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # dsh-superpowers
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ dsh-superpowers adds [obra/superpowers](https://github.com/obra/superpowers) support to [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness). It registers 14 skills for brainstorming, planning, TDD, debugging, and code review, and keeps the `using-superpowers` bootstrap active throughout the session.
6
+
7
+ ## Contents
8
+
9
+ - [Installation](#installation)
10
+ - [What it does](#what-it-does)
11
+ - [Verification](#verification)
12
+ - [Configuration](#configuration)
13
+ - [Limitations](#limitations)
14
+ - [Overhead](#overhead)
15
+ - [Testing](#testing)
16
+ - [Requirements](#requirements)
17
+ - [Upstream and license](#upstream-and-license)
18
+
19
+ ## Installation
20
+
21
+ Add the package to the profile as a local workspace dependency, then restart the surface:
22
+
23
+ ```sh
24
+ cd E:/work/ai/dsh-superpowers && npm install # the config schema is a runtime dependency
25
+ dsh plugin --profile web add link:E:/work/ai/dsh-superpowers
26
+ ```
27
+
28
+ Stop and restart `dsh web` afterwards. Replace `web` with `headless` or a custom profile name when installing for a different surface, then restart that surface instead.
29
+
30
+ ## What it does
31
+
32
+ - Registers all 14 Superpowers skills on `ctx.skills`. They appear in the skill catalog and load through the native `skill` tool. Nothing is copied into `~/.dsh/skills`.
33
+ - Adds the `using-superpowers` bootstrap as the `superpowers:bootstrap` prompt section at order 50. It is present on the first request and survives context compaction because it is part of the system prompt, not a one-off session message. A preset that owns the whole system prompt replaces it — see [Limitations](#limitations).
34
+ - Warns on the first agent of a workspace when a project or preset skill shadows one of the bundled names, naming the copy the model will load instead.
35
+ - Maps Claude Code-style tool names such as `Task`, `TodoWrite`, and `Bash`/`Read`/`Write`/`Edit` to their DeepSeek Harness equivalents. The mapping also notes that hooks and slash commands are not available.
36
+
37
+ ## Verification
38
+
39
+ Check that the plugin is present in the profile:
40
+
41
+ ```sh
42
+ dsh --profile web --dump-config
43
+ ```
44
+
45
+ The output should contain `id: superpowers` followed by `name: @zfdx123/dsh-superpowers`.
46
+
47
+ Then start a new session and ask for a feature. The agent should explore first and respond with questions or a design instead of writing code immediately. Its tool calls should include `skill`.
48
+
49
+ If the bootstrap never arrives, check the session's preset: a preset whose persona owns the complete system prompt is the one case where it is absent by design — see [Limitations](#limitations).
50
+
51
+ ## Configuration
52
+
53
+ Every field is optional. Override fields in the profile's own `cordis.patch.yml`:
54
+
55
+ ```yaml
56
+ - id: superpowers
57
+ config:
58
+ bootstrap: false
59
+ ```
60
+
61
+ | Field | Default | Description |
62
+ | --- | --- | --- |
63
+ | `skills` | `true` | Register the bundled skills on `ctx.skills`. |
64
+ | `bootstrap` | `true` | Register the `using-superpowers` prompt section. |
65
+ | `toolMapping` | `true` | Append the DeepSeek Harness tool mapping to the bootstrap section. |
66
+ | `order` | `50` | Place the bootstrap after the persona prefix (0), before the plan policy (500) and tool guidance (1000+). |
67
+
68
+ Every field is validated against the plugin's schema before it applies: a misspelled type fails the profile with the offending field named, instead of half-registering the plugin.
69
+
70
+ Setting `bootstrap: false` keeps the skills discoverable but stops them from self-triggering; the model will use them only when it decides to consult the catalog.
71
+
72
+ ## Limitations
73
+
74
+ A preset can own the entire system prompt. When the preset's persona declares itself the complete prompt — the bundled `minimal` preset does, with `complete: true` — the prompt registry restores that one section after assembly and drops every other section, including `superpowers:bootstrap`. The drop is silent, and dsh publishes no signal for it: a section's `complete` flag never leaves the registry, and a `system-prompt/assemble` listener cannot append prompt text to a scope that has one. The plugin therefore registers the section and documents the case rather than guessing; `verify/src-02-complete-persona-shadow.mjs` pins the mechanism against the installed dsh.
75
+
76
+ Under such a preset:
77
+
78
+ - The bootstrap is not delivered, so the skills never self-trigger. The 14 skills are still registered on `ctx.skills`, but whether the model can reach them is the preset's decision, because the preset also decides which tools exist — `minimal` exposes only the persistent shell, so no `skill` tool is available there either.
79
+ - `bootstrap: true` cannot make the section appear, and `bootstrap: false` reports nothing: the section was never going to be delivered.
80
+
81
+ Use a preset whose persona is not complete to get the bootstrap.
82
+
83
+ ## Overhead
84
+
85
+ The bootstrap adds roughly 1.1k tokens (4,465 characters) to the system prompt of each request. The section is static and stays within the cached prefix. It is not appended as a new chat message on every turn. Set `bootstrap: false` to keep the skills without the fixed prompt overhead.
86
+
87
+ ## Testing
88
+
89
+ `npm test` runs the suite through `node --test`. `test/` ships in the published tarball, so the command works from an installed copy as well as from a checkout.
90
+
91
+ `verify/` holds maintainer-only probes that mount the real dsh service classes. They are not published, and they need a local dsh installation (pass its `package.json` path as the first argument to point at another one):
92
+
93
+ - `verify/dsh-compat.mjs` — every runtime and packaging contract this plugin depends on.
94
+ - `verify/src-01-doc-order-drift.mjs` — the documented prompt-order band against the installed section orders.
95
+ - `verify/src-02-complete-persona-shadow.mjs` — the complete-persona drop recorded under [Limitations](#limitations).
96
+
97
+ ## Requirements
98
+
99
+ - DeepSeek Harness `0.1.0-rc.6` or newer
100
+ - Node.js 22.19+ or 24+
101
+ - One runtime dependency, `@deepseek-ai/schemastery`, which provides the config schema. A registry install pulls it in; a `link:` install needs `npm install` in the checkout first, or the plugin fails to load. Every other registry is reached through `ctx`.
102
+
103
+ ## Upstream and license
104
+
105
+ The skills under `skills/` are vendored unmodified from [obra/superpowers](https://github.com/obra/superpowers) v6.3.0 at commit [`b36e082`](https://github.com/obra/superpowers/commit/b36e0829c6d0140e93cfef2ca599b1b07d4a7797). The exact upstream version and commit are recorded in `package.json`.
106
+
107
+ The optional visual companion in `brainstorming` loads an upstream-hosted logo containing the Superpowers version. It sends no project or prompt content. Set `SUPERPOWERS_DISABLE_TELEMETRY` to a true value to disable it.
108
+
109
+ Two MIT license notices apply: the adapter is © its contributors under [LICENSE](LICENSE), while the bundled skills are © Jesse Vincent and the Superpowers contributors under [LICENSE.superpowers](LICENSE.superpowers).
package/README.zh.md ADDED
@@ -0,0 +1,109 @@
1
+ # dsh-superpowers
2
+
3
+ [English](README.md) | 中文
4
+
5
+ dsh-superpowers 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 提供 [obra/superpowers](https://github.com/obra/superpowers) 适配。它注册 14 个覆盖需求澄清、任务规划、TDD、调试与代码审查的技能,并让 `using-superpowers` bootstrap 在整个会话中保持生效。
6
+
7
+ ## 目录
8
+
9
+ - [安装](#安装)
10
+ - [它做了什么](#它做了什么)
11
+ - [验证](#验证)
12
+ - [配置](#配置)
13
+ - [已知限制](#已知限制)
14
+ - [开销](#开销)
15
+ - [测试](#测试)
16
+ - [环境要求](#环境要求)
17
+ - [上游与许可](#上游与许可)
18
+
19
+ ## 安装
20
+
21
+ 把本包作为本地工作区依赖加进 profile,然后重启对应形态:
22
+
23
+ ```sh
24
+ cd E:/work/ai/dsh-superpowers && npm install # 配置 schema 是运行时依赖
25
+ dsh plugin --profile web add link:E:/work/ai/dsh-superpowers
26
+ ```
27
+
28
+ 执行后先停止再重启 `dsh web`。如果要安装到其他形态,可把 `web` 换成 `headless` 或自定义 profile 名,并重启对应形态。
29
+
30
+ ## 它做了什么
31
+
32
+ - 在 `ctx.skills` 注册全部 14 个 Superpowers 技能。它们会出现在技能目录中,并通过原生 `skill` 工具加载;不会往 `~/.dsh/skills` 复制任何文件。
33
+ - 把 `using-superpowers` bootstrap 注册为 `superpowers:bootstrap` 提示词段落,order 为 50。它在第一条请求中生效,并能在上下文压缩后继续存在,因为它属于系统提示词,不是一次性会话消息。若预设独占整个系统提示词,它会被替换,见[已知限制](#已知限制)。
34
+ - 每个工作区首个 agent 创建时,若内置技能名被项目或预设技能遮蔽,会报出警告,并指明模型实际加载的那份副本。
35
+ - 将 `Task`、`TodoWrite`、`Bash`/`Read`/`Write`/`Edit` 等 Claude Code 风格工具名映射到 DeepSeek Harness 对应工具。映射中也会说明当前环境不提供 hooks 与斜杠命令。
36
+
37
+ ## 验证
38
+
39
+ 先确认插件已挂载到 profile:
40
+
41
+ ```sh
42
+ dsh --profile web --dump-config
43
+ ```
44
+
45
+ 输出中应包含 `id: superpowers`,其后是 `name: @zfdx123/dsh-superpowers`。
46
+
47
+ 然后新建会话并提出一个功能需求。Agent 应先探查,再给出问题或设计,不会立即开始写代码。工具调用中应出现 `skill`。
48
+
49
+ 如果 bootstrap 始终没有出现,请检查会话所用的预设:当预设的 persona 独占完整系统提示词时,它按设计就不会下发,见[已知限制](#已知限制)。
50
+
51
+ ## 配置
52
+
53
+ 所有字段均为可选项,可在 profile 自己的 `cordis.patch.yml` 中覆盖:
54
+
55
+ ```yaml
56
+ - id: superpowers
57
+ config:
58
+ bootstrap: false
59
+ ```
60
+
61
+ | 字段 | 默认值 | 说明 |
62
+ | --- | --- | --- |
63
+ | `skills` | `true` | 把内置技能注册到 `ctx.skills`。 |
64
+ | `bootstrap` | `true` | 注册 `using-superpowers` 提示词段落。 |
65
+ | `toolMapping` | `true` | 在 bootstrap 段落中追加 DeepSeek Harness 工具映射。 |
66
+ | `order` | `50` | 把 bootstrap 放在 persona 前缀(0)之后、计划策略(500)与工具指导(1000+)之前。 |
67
+
68
+ 所有字段在生效前都会先过插件自己的 schema:类型写错时 profile 会直接失败并指出出错字段,而不是把插件注册到一半。
69
+
70
+ 设置 `bootstrap: false` 后,技能仍可被发现,但不会再自动触发;模型只会在自己决定查询技能目录时使用它们。
71
+
72
+ ## 已知限制
73
+
74
+ 预设可以独占整个系统提示词。当预设的 persona 声明自己就是完整提示词时——内置的 `minimal` 预设正是如此(`complete: true`)——提示词注册表会在装配结束后只保留那一个段落,丢掉包括 `superpowers:bootstrap` 在内的其他所有段落。这一丢弃是静默的,而且 dsh 不发布任何相关信号:段落的 `complete` 标记不会离开注册表,`system-prompt/assemble` 监听器也无法向存在 complete 段落的 scope 追加提示词文本。因此本插件照常注册该段落、把这种情况写进文档,而不是去猜;`verify/src-02-complete-persona-shadow.mjs` 会针对已安装的 dsh 钉住这一机制。
75
+
76
+ 在该类预设下:
77
+
78
+ - bootstrap 不会下发,技能因此不会自动触发。14 个技能仍注册在 `ctx.skills` 上,但模型能否取到它们由预设决定——预设同时决定有哪些工具:`minimal` 只挂常驻 shell,所以那里也没有 `skill` 工具。
79
+ - `bootstrap: true` 不会让它出现,`bootstrap: false` 也不会有任何提示:这个段落本来就不会下发。
80
+
81
+ 要拿到 bootstrap,请使用 persona 不是 complete 的预设。
82
+
83
+ ## 开销
84
+
85
+ bootstrap 会给每次请求的系统提示词增加约 1.1k token(4,465 字符)。这段内容是静态的,位于缓存前缀内,不会在每一轮作为新的聊天消息追加。设置 `bootstrap: false` 可以保留技能,同时去掉这份固定提示词开销。
86
+
87
+ ## 测试
88
+
89
+ `npm test` 通过 `node --test` 运行测试。`test/` 会随发布产物一起发布,因此在安装后的副本里和源码检出里都能直接运行。
90
+
91
+ `verify/` 下是维护者专用的探针,会挂载真实的 dsh 服务类。它们不随包发布,且需要本机装有 dsh(第一个参数可传入另一份 dsh 的 `package.json` 路径):
92
+
93
+ - `verify/dsh-compat.mjs` —— 本插件依赖的全部运行时与打包契约。
94
+ - `verify/src-01-doc-order-drift.mjs` —— 文档中的 order 区间与已安装段落顺序表的对照。
95
+ - `verify/src-02-complete-persona-shadow.mjs` —— [已知限制](#已知限制) 中记录的 complete persona 覆盖机制。
96
+
97
+ ## 环境要求
98
+
99
+ - DeepSeek Harness `0.1.0-rc.6` 及以上
100
+ - Node.js 22.19+ 或 24+
101
+ - 一个运行时依赖 `@deepseek-ai/schemastery`,用于提供配置 schema。从 registry 安装会自动带上它;用 `link:` 安装时需要先在检出目录执行 `npm install`,否则插件无法加载。其余注册表都只通过 `ctx` 访问。
102
+
103
+ ## 上游与许可
104
+
105
+ `skills/` 下的技能原样取自 [obra/superpowers](https://github.com/obra/superpowers) v6.3.0,对应 commit [`b36e082`](https://github.com/obra/superpowers/commit/b36e0829c6d0140e93cfef2ca599b1b07d4a7797),未做修改;`package.json` 记录了确切的上游版本与 commit。
106
+
107
+ `brainstorming` 的可选视觉组件会从上游网站加载带 Superpowers 版本号的 logo,不包含项目或提示词内容。把 `SUPERPOWERS_DISABLE_TELEMETRY` 设为任一 true 值即可关闭。
108
+
109
+ 这里同时适用两份 MIT 许可声明:适配器版权归其贡献者所有,依据 [LICENSE](LICENSE) 许可;内置技能版权归 Jesse Vincent 与 Superpowers 贡献者所有,依据 [LICENSE.superpowers](LICENSE.superpowers) 许可。
@@ -0,0 +1,16 @@
1
+ # The dsh-superpowers bundle patch: one host-plane row.
2
+ #
3
+ # The row registers a `ctx.skills` provider for the bundled Superpowers skills
4
+ # and a `superpowers:bootstrap` prompt section. Both land in the global layer,
5
+ # which every agent's scope chain merges, so the plugin works on the Web surface
6
+ # (where the agent plane sits behind presets) without touching any preset.
7
+ #
8
+ # Turn a half off in the profile's own cordis.patch.yml, for example:
9
+ #
10
+ # - id: superpowers
11
+ # config:
12
+ # bootstrap: false
13
+ #
14
+ - insert:
15
+ - id: superpowers
16
+ name: "@zfdx123/dsh-superpowers"
package/index.js ADDED
@@ -0,0 +1,395 @@
1
+ /**
2
+ * Superpowers for DeepSeek Harness.
3
+ *
4
+ * Brings the Superpowers software-development methodology (obra/superpowers) to
5
+ * dsh: the bundled skills are registered as runtime skills on `ctx.skills`, and
6
+ * the `using-superpowers` bootstrap is registered as a prompt section so it is
7
+ * present from the first request and survives context compaction.
8
+ *
9
+ * Both registries are reached through `ctx` alone — a third-party plugin cannot
10
+ * rely on a `@deepseek-ai/*` service package resolving from its own directory,
11
+ * because they are nested inside the dsh installation. The one exception is the
12
+ * config-schema package, which is a declared dependency of this package and
13
+ * therefore resolves from this package's own tree.
14
+ *
15
+ * @module dsh-superpowers
16
+ */
17
+
18
+ import { readdirSync, readFileSync } from 'node:fs'
19
+ import { dirname, join, resolve } from 'node:path'
20
+ import { fileURLToPath } from 'node:url'
21
+
22
+ import Schema from '@deepseek-ai/schemastery'
23
+
24
+ /** Cordis plugin name used by loader diagnostics. */
25
+ export const name = 'superpowers'
26
+
27
+ /** The prompt and skill registries this plugin registers into. */
28
+ export const inject = ['systemPrompt', 'skills']
29
+
30
+ const packageRoot = dirname(fileURLToPath(import.meta.url))
31
+ const skillsDir = resolve(packageRoot, 'skills')
32
+ const BOOTSTRAP_SKILL = 'using-superpowers'
33
+ const BOOTSTRAP_SECTION = 'superpowers:bootstrap'
34
+
35
+ /**
36
+ * Prompt order of the bootstrap section: after the deployment persona prefix
37
+ * (0), and before the plan policy (500) and the tool guidance at 1000+.
38
+ * `verify/src-01-doc-order-drift.mjs` re-checks that placement against the
39
+ * installed dsh.
40
+ */
41
+ const DEFAULT_ORDER = 50
42
+
43
+ /**
44
+ * Plugin configuration. Every deployment-varying choice is a validated field:
45
+ * cordis resolves these defaults and rejects a malformed override before
46
+ * {@link apply} runs, instead of failing the fiber halfway through setup.
47
+ */
48
+ export const Config = Schema.object({
49
+ /** Register the bundled skills on `ctx.skills`. */
50
+ skills: Schema.boolean().default(true),
51
+ /** Register the `using-superpowers` bootstrap prompt section. */
52
+ bootstrap: Schema.boolean().default(true),
53
+ /** Append the DeepSeek Harness tool mapping to the bootstrap section. */
54
+ toolMapping: Schema.boolean().default(true),
55
+ /** Prompt order of the bootstrap section; the registry rejects a non-finite value. */
56
+ order: Schema.number().default(DEFAULT_ORDER),
57
+ })
58
+
59
+ /**
60
+ * Split a `SKILL.md` into its frontmatter block and body.
61
+ * @param content - the raw file text.
62
+ * @returns the frontmatter text (empty when absent) and the trimmed body.
63
+ */
64
+ function splitFrontmatter(content) {
65
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/)
66
+ if (match === null) return { frontmatter: '', body: content.trim() }
67
+ return { frontmatter: match[1], body: match[2].trim() }
68
+ }
69
+
70
+ /** Count the leading ASCII spaces on one YAML line. */
71
+ function leadingSpaces(line) {
72
+ return line.length - line.trimStart().length
73
+ }
74
+
75
+ /**
76
+ * Parse a YAML block-scalar header from a value such as `>-`, `|+`, or `>2-`.
77
+ * @returns the scalar controls, or undefined for an ordinary scalar.
78
+ */
79
+ function parseBlockHeader(value) {
80
+ const match = value.match(/^([|>])(?:(?:([1-9])([+-]?))|(?:([+-])([1-9]?)))?(?:\s+#.*)?$/)
81
+ if (match === null) return undefined
82
+ return {
83
+ style: match[1],
84
+ indentation: Number(match[2] || match[5] || 0),
85
+ chomping: match[3] || match[4] || '',
86
+ }
87
+ }
88
+
89
+ /** Fold non-empty YAML lines while preserving paragraph and indented breaks. */
90
+ function foldBlockLines(lines, moreIndented) {
91
+ let value = ''
92
+ for (let index = 0; index < lines.length;) {
93
+ if (lines[index] === '') {
94
+ let end = index
95
+ while (end < lines.length && lines[end] === '') end += 1
96
+ value += '\n'.repeat(end - index)
97
+ index = end
98
+ continue
99
+ }
100
+
101
+ value += lines[index]
102
+ const next = index + 1
103
+ if (next < lines.length && lines[next] !== '') {
104
+ value += moreIndented[index] || moreIndented[next] ? '\n' : ' '
105
+ }
106
+ index = next
107
+ }
108
+ return value
109
+ }
110
+
111
+ /**
112
+ * Read one top-level YAML block scalar and return the next unconsumed line.
113
+ * This deliberately implements only scalar semantics needed by skill metadata,
114
+ * while covering literal/folded styles, indentation indicators, and chomping.
115
+ */
116
+ function readBlockScalar(lines, start, header) {
117
+ let end = start
118
+ while (end < lines.length && (lines[end].trim() === '' || lines[end].startsWith(' '))) {
119
+ end += 1
120
+ }
121
+
122
+ const rawLines = lines.slice(start, end)
123
+ const firstContent = rawLines.find((line) => line.trim() !== '')
124
+ const indentation = header.indentation || (firstContent === undefined ? 1 : leadingSpaces(firstContent))
125
+
126
+ for (const line of rawLines) {
127
+ if (line.trim() !== '' && leadingSpaces(line) < indentation) {
128
+ throw new Error('invalid block-scalar indentation')
129
+ }
130
+ }
131
+
132
+ const contentLines = rawLines.map((line) => (line.trim() === '' ? '' : line.slice(indentation)))
133
+ const moreIndented = rawLines.map((line) => line.trim() !== '' && leadingSpaces(line) > indentation)
134
+ const hasContent = contentLines.some((line) => line !== '')
135
+ let value = header.style === '|' ? contentLines.join('\n') : foldBlockLines(contentLines, moreIndented)
136
+
137
+ if (rawLines.length > 0) value += '\n'
138
+ if (header.chomping === '-') {
139
+ value = value.replace(/\n+$/, '')
140
+ } else if (header.chomping !== '+') {
141
+ value = hasContent ? `${value.replace(/\n+$/, '')}\n` : ''
142
+ }
143
+
144
+ return { value, nextLine: end }
145
+ }
146
+
147
+ /** Parse one quoted or plain YAML scalar. */
148
+ function parseInlineScalar(value) {
149
+ if (value.startsWith('"')) {
150
+ if (!value.endsWith('"')) throw new Error('unterminated double-quoted scalar')
151
+ return JSON.parse(value)
152
+ }
153
+ if (value.startsWith("'")) {
154
+ if (!value.endsWith("'")) throw new Error('unterminated single-quoted scalar')
155
+ return value.slice(1, -1).replace(/''/g, "'")
156
+ }
157
+ return value.replace(/\s+#.*$/, '').trim()
158
+ }
159
+
160
+ /**
161
+ * Read the scalar frontmatter keys a Superpowers skill declares. Besides the
162
+ * current one-line metadata, support YAML literal and folded block scalars so
163
+ * a future vendored release cannot silently turn `description: >-` into `>-`.
164
+ * @param frontmatter - the frontmatter text.
165
+ * @returns the parsed string values by key.
166
+ */
167
+ function parseFrontmatter(frontmatter) {
168
+ const fields = {}
169
+ const lines = frontmatter.split(/\r?\n/)
170
+ for (let index = 0; index < lines.length; index += 1) {
171
+ const match = lines[index].match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/)
172
+ if (match === null) continue
173
+
174
+ const rawValue = match[2].trim()
175
+ const block = parseBlockHeader(rawValue)
176
+ if (block !== undefined) {
177
+ const parsed = readBlockScalar(lines, index + 1, block)
178
+ fields[match[1]] = parsed.value
179
+ index = parsed.nextLine - 1
180
+ continue
181
+ }
182
+
183
+ if (rawValue !== '') fields[match[1]] = parseInlineScalar(rawValue)
184
+ }
185
+ return fields
186
+ }
187
+
188
+ /**
189
+ * Load every bundled skill.
190
+ * @returns one entry per readable skill directory.
191
+ */
192
+ function loadBundledSkills() {
193
+ const skills = []
194
+ const issues = []
195
+ let entries
196
+ try {
197
+ entries = readdirSync(skillsDir, { withFileTypes: true })
198
+ } catch (error) {
199
+ issues.push(`cannot read skills directory ${skillsDir}: ${error instanceof Error ? error.message : String(error)}`)
200
+ return { skills, issues }
201
+ }
202
+
203
+ const seen = new Map()
204
+ entries.sort((left, right) => left.name.localeCompare(right.name))
205
+ for (const entry of entries) {
206
+ if (!entry.isDirectory()) continue
207
+ const skillDir = join(skillsDir, entry.name)
208
+ const skillPath = join(skillDir, 'SKILL.md')
209
+ let raw
210
+ try {
211
+ raw = readFileSync(skillPath, 'utf8')
212
+ } catch (error) {
213
+ issues.push(`cannot read ${skillPath}: ${error instanceof Error ? error.message : String(error)}`)
214
+ continue
215
+ }
216
+
217
+ const { frontmatter, body } = splitFrontmatter(raw)
218
+ if (frontmatter === '') {
219
+ issues.push(`${skillPath} has no valid frontmatter`)
220
+ continue
221
+ }
222
+
223
+ let fields
224
+ try {
225
+ fields = parseFrontmatter(frontmatter)
226
+ } catch (error) {
227
+ issues.push(`cannot parse ${skillPath}: ${error instanceof Error ? error.message : String(error)}`)
228
+ continue
229
+ }
230
+
231
+ const skillName = fields.name
232
+ const description = fields.description
233
+ if (skillName === undefined || description === undefined || description === '') {
234
+ issues.push(`${skillPath} requires non-empty name and description fields`)
235
+ continue
236
+ }
237
+ if (body === '') {
238
+ issues.push(`${skillPath} has an empty instruction body`)
239
+ continue
240
+ }
241
+ if (seen.has(skillName)) {
242
+ issues.push(`${skillPath} duplicates skill name "${skillName}" from ${seen.get(skillName)}`)
243
+ continue
244
+ }
245
+ seen.set(skillName, skillPath)
246
+
247
+ skills.push({
248
+ name: skillName,
249
+ description,
250
+ ...(fields.whenToUse === undefined ? {} : { whenToUse: fields.whenToUse }),
251
+ content: body,
252
+ source: 'bundled',
253
+ path: skillPath,
254
+ resourceBase: { kind: 'directory', path: skillDir },
255
+ })
256
+ }
257
+ return { skills, issues }
258
+ }
259
+
260
+ /**
261
+ * The dsh tool vocabulary Superpowers instructions map onto. Superpowers is
262
+ * written against several harnesses and names Claude Code's PascalCase tools;
263
+ * without this mapping a skill tells the model to call tools that do not exist
264
+ * here.
265
+ * @returns the mapping section text.
266
+ */
267
+ function dshToolMapping() {
268
+ return `## DeepSeek Harness tool mapping
269
+
270
+ Superpowers skills are written for several coding agents. In DeepSeek Harness, use these tools:
271
+
272
+ - Invoke a skill → the native \`skill\` tool; every Superpowers skill is already in your skill catalog, so never read a \`SKILL.md\` by hand
273
+ - Read a file → \`read\` (\`read_image\` for images)
274
+ - Create or overwrite a file → \`write\`
275
+ - Modify an existing file → \`edit\`, or \`str_replace_editor\` where it is offered
276
+ - Run a shell command → \`bash\`; for long work pass \`run_in_background\`, then collect it with \`job_output\`, \`job_list\`, and \`job_kill\`
277
+ - Find files by path pattern → \`glob\`
278
+ - Search file contents → \`grep\`
279
+ - Track tasks → \`todo_write\`
280
+ - Ask the human a question → \`ask_user_question\`
281
+ - Search the web → \`web_search\`
282
+ - Dispatch a subagent → \`subagent\` for a fresh child, \`subagent_fork\` to continue from this session; steer children with \`send_message\`, \`interrupt_agent\`, and \`list_agents\`
283
+ - Present a plan for approval → \`exit_plan_mode\`, while the session is in plan mode
284
+
285
+ Read Claude Code's \`Task\` as \`subagent\`, \`TodoWrite\` as \`todo_write\`, and \`Bash\`/\`Read\`/\`Write\`/\`Edit\`/\`Glob\`/\`Grep\` as their lowercase equivalents above. DeepSeek Harness exposes no hook or slash-command API to skills, so skip instructions that install hooks or register commands and do the work with these tools instead.`
286
+ }
287
+
288
+ /**
289
+ * Build the bootstrap text.
290
+ * @param bootstrapBody - the `using-superpowers` body, without frontmatter.
291
+ * @param includeToolMapping - whether to append the dsh tool mapping.
292
+ * @returns the prompt section text.
293
+ */
294
+ function buildBootstrap(bootstrapBody, includeToolMapping) {
295
+ const mapping = includeToolMapping ? `\n\n${dshToolMapping()}` : ''
296
+ return `<EXTREMELY_IMPORTANT>
297
+ You have superpowers.
298
+
299
+ The using-superpowers skill content is included below and is already loaded for this session. Follow it now. Do not load using-superpowers again through the skill tool.
300
+
301
+ ${bootstrapBody}${mapping}
302
+ </EXTREMELY_IMPORTANT>`
303
+ }
304
+
305
+ /**
306
+ * Report bundled skills whose name a higher-priority skill owns.
307
+ *
308
+ * The registry resolves precedence when it collects a catalog, and only there:
309
+ * a project skill outranks this plugin's runtime registration, and a preset's
310
+ * own skill outranks it outright. Neither is an error — a deployment may shadow
311
+ * on purpose — but a shadowed skill must not be invisible, which is what this
312
+ * report exists for. It runs once per workspace, because every subagent of a
313
+ * session shares its parent's composition and would repeat the same warning.
314
+ *
315
+ * @param ctx - plugin context; used for the catalog read and the warning.
316
+ * @param agent - the agent whose catalog view to check.
317
+ * @param bundled - the skills this plugin registered.
318
+ * @param reported - signatures already warned about, kept per plugin instance.
319
+ */
320
+ async function reportShadowedSkills(ctx, agent, bundled, reported) {
321
+ const cwd = agent?.session?.header?.cwd
322
+ const owned = new Map(bundled.map((skill) => [skill.name, skill.path]))
323
+ const visible = await ctx.skills.list({ cwd, scope: agent })
324
+ const shadowed = visible.filter((skill) => owned.has(skill.name) && skill.path !== owned.get(skill.name))
325
+ if (shadowed.length === 0) return
326
+
327
+ const signature = `${cwd ?? ''}\u0000${shadowed
328
+ .map((skill) => `${skill.name}:${skill.provider}:${skill.source}:${skill.path ?? ''}`)
329
+ .sort()
330
+ .join('|')}`
331
+ if (reported.has(signature)) return
332
+ reported.add(signature)
333
+
334
+ const winners = shadowed
335
+ .map((skill) => {
336
+ const at = skill.path === undefined ? '' : ` at ${skill.path}`
337
+ return `"${skill.name}" from ${skill.source} (provider "${skill.provider}")${at}`
338
+ })
339
+ .join(', ')
340
+ const where = cwd === undefined ? '' : ` in ${cwd}`
341
+ const count = shadowed.length
342
+ ctx.logger.warn(
343
+ `superpowers: ${count} bundled skill${count === 1 ? '' : 's'} shadowed${where}; the model loads ${winners} instead`,
344
+ )
345
+ }
346
+
347
+ /**
348
+ * Register the bundled skills, the per-agent shadow report, and the bootstrap
349
+ * prompt section.
350
+ * @param ctx - plugin context; every registration is disposed with it.
351
+ * @param config - plugin configuration; unset fields take their schema defaults.
352
+ */
353
+ export function apply(ctx, config) {
354
+ const options = Config(config)
355
+ if (!options.skills && !options.bootstrap) return
356
+
357
+ const { skills: bundled, issues } = loadBundledSkills()
358
+ for (const issue of issues) ctx.logger.warn(`superpowers: ${issue}`)
359
+
360
+ if (bundled.length === 0) {
361
+ ctx.logger.warn(`superpowers: no valid skills found under ${skillsDir}`)
362
+ }
363
+
364
+ if (options.skills) {
365
+ for (const skill of bundled) ctx.skills.register(skill)
366
+
367
+ const reported = new Set()
368
+ ctx.on('agent/created', async ({ agent }) => {
369
+ try {
370
+ await reportShadowedSkills(ctx, agent, bundled, reported)
371
+ } catch (error) {
372
+ const reason = error instanceof Error ? error.message : String(error)
373
+ ctx.logger.warn(`superpowers: cannot check the skill catalog of agent "${agent?.id}": ${reason}`)
374
+ }
375
+ })
376
+ }
377
+
378
+ if (!options.bootstrap) return
379
+
380
+ const bootstrap = bundled.find((skill) => skill.name === BOOTSTRAP_SKILL)
381
+ if (bootstrap === undefined) {
382
+ ctx.logger.warn(`superpowers: ${BOOTSTRAP_SKILL} is missing; the bootstrap section is not registered`)
383
+ return
384
+ }
385
+
386
+ // A preset whose persona declares itself the complete section owns the whole
387
+ // system prompt, and the registry drops this section in that scope without an
388
+ // error; no published API reports that decision, so the registration stands
389
+ // and the limitation is documented in the README instead.
390
+ ctx.systemPrompt.section({
391
+ name: BOOTSTRAP_SECTION,
392
+ order: options.order,
393
+ text: buildBootstrap(bootstrap.content, options.toolMapping),
394
+ })
395
+ }