openclaw-superpowers-plugin 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WebLeOn
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.
@@ -0,0 +1,301 @@
1
+ # Superpowers for OpenClaw - 简化集成设计
2
+
3
+ ## 1. 目标
4
+
5
+ 在 OpenClaw 中集成 `obra/superpowers`,满足以下目标:
6
+
7
+ - 尽可能使用上游 `superpowers` 的全部 skill 能力
8
+ - 保持高集成度,避免插件 / skill / script 过度拆分
9
+ - 易于管理,日常只维护一个主目录
10
+ - 易于移植,迁移时可整体复制
11
+ - 支持同步上游最新代码
12
+ - 尽量符合 OpenClaw 的插件与工作流习惯
13
+
14
+ ---
15
+
16
+ ## 2. 非目标
17
+
18
+ 以下内容不作为第一阶段目标:
19
+
20
+ - 不追求 100% 原样复刻上游宿主行为
21
+ - 不修改 OpenClaw 核心加载逻辑
22
+ - 不引入额外独立 skill 包或外部桥接层
23
+ - 不依赖多个分散目录协同工作
24
+
25
+ ---
26
+
27
+ ## 3. 总体方案
28
+
29
+ 采用 **单插件一体化方案**,并固定为:
30
+
31
+ **静态内嵌 vendor + 显式 update**。
32
+
33
+ 所有集成逻辑集中在一个目录中:
34
+
35
+ ```text
36
+ workspace/plugins/superpowers/
37
+ ```
38
+
39
+ 这个插件目录内部负责:
40
+
41
+ 1. 内嵌保存一份上游 `obra/superpowers` 代码副本
42
+ 2. 扫描并索引上游 `skills/`
43
+ 3. 将上游 skills 适配为 OpenClaw 可用能力
44
+ 4. 提供统一的管理与调用入口
45
+
46
+ 外部不再拆分独立 `skills/`、`scripts/`、`vendor/` 顶层目录。
47
+
48
+ ---
49
+
50
+ ## 4. 目录结构
51
+
52
+ 建议结构如下:
53
+
54
+ ```text
55
+ workspace/plugins/superpowers/
56
+ README.md
57
+ README.design.md
58
+ plugin.json
59
+ index.js
60
+ vendor/
61
+ superpowers/
62
+ cache/
63
+ index.json
64
+ docs/
65
+ compat.md
66
+ ```
67
+
68
+ ### 目录说明
69
+
70
+ - `plugin.json`
71
+ - 插件元数据与配置入口
72
+ - `index.js`
73
+ - 插件主逻辑
74
+ - `vendor/superpowers/`
75
+ - 上游 `obra/superpowers` 的内嵌代码副本(不保留 `.git`)
76
+ - `cache/index.json`
77
+ - 上游 skills 扫描结果与索引缓存
78
+ - `docs/compat.md`
79
+ - 记录兼容差异与支持范围
80
+
81
+ ---
82
+
83
+ ## 5. 插件职责
84
+
85
+ ### 5.1 上游代码管理
86
+
87
+ 插件内部负责:
88
+
89
+ - 首次安装时拉取 `https://github.com/obra/superpowers`
90
+ - 后续更新时显式执行 `update`
91
+ - 将最新代码覆盖到 `vendor/superpowers/`
92
+ - 记录当前上游 commit hash
93
+ - 提供状态查询,明确当前使用的内嵌版本
94
+
95
+ ### 5.2 skill 扫描与索引
96
+
97
+ 插件扫描:
98
+
99
+ ```text
100
+ vendor/superpowers/skills/
101
+ ```
102
+
103
+ 读取每个 skill 的元信息,并生成统一索引,例如:
104
+
105
+ ```json
106
+ {
107
+ "upstreamCommit": "abc123",
108
+ "skills": [
109
+ {
110
+ "name": "brainstorming",
111
+ "path": "vendor/superpowers/skills/brainstorming",
112
+ "description": "...",
113
+ "enabled": true
114
+ }
115
+ ]
116
+ }
117
+ ```
118
+
119
+ ### 5.3 OpenClaw 适配
120
+
121
+ 插件负责把上游 skills 转换为 OpenClaw 可消费的能力,主要分两类:
122
+
123
+ #### A. 纯 workflow / prompt 型 skill
124
+ 直接读取内容并用于任务指导。
125
+
126
+ #### B. 依赖宿主执行机制的 skill
127
+ 映射到 OpenClaw 原生能力,例如:
128
+
129
+ - planning → 普通任务规划
130
+ - execution → 由当前会话或子会话执行
131
+ - subagent workflows → 映射到 `sessions_spawn`
132
+ - review workflows → 使用独立子会话做审查
133
+ - parallel workflows → 使用多个子会话并行执行
134
+
135
+ ### 5.4 统一管理入口
136
+
137
+ 插件统一提供如下能力:
138
+
139
+ - 安装上游代码
140
+ - 更新上游代码
141
+ - 查看状态
142
+ - 列出 skills
143
+ - 读取指定 skill
144
+ - 刷新索引
145
+ - 显式使用某个 skill
146
+ - 在适当场景下推荐某个 skill
147
+
148
+ ---
149
+
150
+ ## 6. 设计原则
151
+
152
+ ### 原则 1:单目录自包含
153
+
154
+ 整个集成应尽量收敛在:
155
+
156
+ ```text
157
+ workspace/plugins/superpowers/
158
+ ```
159
+
160
+ 方便备份、迁移和版本管理。
161
+
162
+ ### 原则 2:上游代码不魔改
163
+
164
+ `vendor/superpowers/` 保持尽量接近上游,只作为内嵌副本与读取来源。
165
+
166
+ 兼容逻辑放在插件自身实现,不直接改写上游 skill 内容。
167
+
168
+ ### 原则 3:更新与运行分离
169
+
170
+ - 更新上游代码时,显式执行 `update`
171
+ - 日常运行只读取当前内嵌版本
172
+ - 不在启动时自动联网检查或自动升级
173
+
174
+ 这样可以保持稳定、简单和可复现。
175
+
176
+ ### 原则 4:行为可观察
177
+
178
+ 用户应能明确看到:
179
+
180
+ - 当前上游 commit
181
+ - 当前已索引多少 skills
182
+ - 哪些 skills 可用
183
+ - 哪些是完全支持,哪些是兼容支持
184
+
185
+ ### 原则 5:尽量贴合 OpenClaw
186
+
187
+ 不修改 OpenClaw 核心。
188
+
189
+ 通过插件层完成:
190
+ - 上游同步
191
+ - skill 索引
192
+ - 能力适配
193
+ - 状态管理
194
+
195
+ ---
196
+
197
+ ## 7. 支持范围定义
198
+
199
+ “完整支持”不等于机械复刻,而定义为:
200
+
201
+ 1. 所有上游 skills 都能被发现和列出
202
+ 2. 大多数 workflow / prompt 型 skills 可直接使用
203
+ 3. 依赖宿主流程的 skills 可映射到 OpenClaw 原生能力
204
+ 4. 少数无法等价映射的能力会在兼容文档中明确标注
205
+
206
+ 这比“复制文件即算支持”更符合实际可维护性。
207
+
208
+ ---
209
+
210
+ ## 8. 与参考桥接项目的关系
211
+
212
+ 可以参考 `vruru/superpowers-bridge` 的思路,主要包括:
213
+
214
+ - 首次自动拉取上游代码
215
+ - 本地缓存 skills
216
+ - 提供更新机制
217
+
218
+ 但不直接照搬其结构。
219
+
220
+ 本方案的重点是:
221
+
222
+ - 一体化
223
+ - OpenClaw-first
224
+ - 可观察
225
+ - 易迁移
226
+
227
+ ---
228
+
229
+ ## 9. 推荐功能入口
230
+
231
+ 建议插件最终至少支持以下动作:
232
+
233
+ - `install`
234
+ - `update`
235
+ - `status`
236
+ - `list`
237
+ - `refresh-index`
238
+ - `use <skill>`
239
+ - `recommend <task-context>`
240
+
241
+ 这些入口由插件统一实现,不再分散到多个外部组件。
242
+
243
+ ---
244
+
245
+ ## 10. 实施阶段
246
+
247
+ ### Phase 1:最小可用
248
+
249
+ 目标:
250
+
251
+ - 插件目录初始化
252
+ - clone 上游 `superpowers`
253
+ - 扫描 `skills/`
254
+ - 生成 `cache/index.json`
255
+ - 能列出所有 skills
256
+ - 能查看当前上游 commit
257
+
258
+ ### Phase 2:显式调用
259
+
260
+ 目标:
261
+
262
+ - 支持 `use <skill>`
263
+ - 能读取并应用纯 workflow 型 skills
264
+ - 能在 OpenClaw 中显式使用 superpowers 技能
265
+
266
+ ### Phase 3:宿主适配
267
+
268
+ 目标:
269
+
270
+ - 将 plan / execute / review / subagent / parallel 等能力映射到 OpenClaw
271
+ - 增加兼容状态说明
272
+ - 优化自动推荐与调度
273
+
274
+ ---
275
+
276
+ ## 11. 最终建议
277
+
278
+ 最终采用:
279
+
280
+ **一个插件目录 + 一个上游镜像 + 一个索引缓存。**
281
+
282
+ 不再拆出额外顶层 script、skill bridge、vendor 根目录。
283
+
284
+ 这样更符合你的要求:
285
+
286
+ - 集成度高
287
+ - 管理简单
288
+ - 迁移方便
289
+ - 能持续跟上上游代码
290
+
291
+ ---
292
+
293
+ ## 12. 下一步
294
+
295
+ 下一步应直接开始 Phase 1:
296
+
297
+ 1. 创建 `workspace/plugins/superpowers/`
298
+ 2. 初始化插件骨架
299
+ 3. 设计 `plugin.json`
300
+ 4. 设计 `index.js` 的最小职责
301
+ 5. 实现上游 clone + status + list
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # openclaw-superpowers-plugin
2
+
3
+ Superpowers skills adapter for OpenClaw.
4
+
5
+ ## What is this?
6
+
7
+ This plugin brings [obra/superpowers](https://github.com/obra/superpowers) skills to OpenClaw. It provides:
8
+
9
+ - Vendored skills from upstream superpowers repository
10
+ - Skill recommendation based on task keywords
11
+ - Manual skill loading by name
12
+ - One-click install/update of upstream content
13
+
14
+ ## Installation
15
+
16
+ ### From local path
17
+ ```bash
18
+ openclaw plugins install /path/to/openclaw-superpowers-plugin
19
+ ```
20
+
21
+ ### From GitHub (future)
22
+ ```bash
23
+ openclaw plugins install openclaw-superpowers-plugin
24
+ ```
25
+
26
+ ### Enable the plugin
27
+ Add to your `~/.openclaw/openclaw.json`:
28
+
29
+ ```json
30
+ {
31
+ "plugins": {
32
+ "load": {
33
+ "paths": ["/path/to/openclaw-superpowers-plugin"]
34
+ },
35
+ "entries": {
36
+ "superpowers": {
37
+ "enabled": true
38
+ }
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Then restart the Gateway:
45
+ ```bash
46
+ openclaw gateway restart
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ### Command
52
+ ```text
53
+ /superpowers
54
+ /superpowers overview
55
+ /superpowers use <task description>
56
+ /superpowers use name <skill-name>
57
+ /superpowers install
58
+ /superpowers update
59
+ ```
60
+
61
+ ### Tools
62
+ - `superpowers_overview` — Show installation status and skill list
63
+ - `superpowers_use` — Recommend a skill for a task or load a skill by name
64
+ - `superpowers_install` — Install vendored superpowers repository
65
+ - `superpowers_update` — Update vendored superpowers from upstream
66
+
67
+ ### Examples
68
+
69
+ #### Get overview
70
+ ```text
71
+ /superpowers overview
72
+ ```
73
+
74
+ #### Recommend a skill for debugging
75
+ ```text
76
+ /superpowers use 帮我系统排查一个难复现的 bug
77
+ ```
78
+
79
+ #### Load a specific skill
80
+ ```text
81
+ /superpowers use name systematic-debugging
82
+ ```
83
+
84
+ #### Update upstream skills
85
+ ```text
86
+ /superpowers update
87
+ ```
88
+
89
+ ## Configuration
90
+
91
+ Optional config in `openclaw.json`:
92
+
93
+ ```json
94
+ {
95
+ "plugins": {
96
+ "entries": {
97
+ "superpowers": {
98
+ "enabled": true,
99
+ "config": {
100
+ "upstreamRepo": "https://github.com/obra/superpowers.git",
101
+ "defaultBranch": "main"
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Directory Structure
110
+
111
+ ```
112
+ openclaw-superpowers-plugin/
113
+ package.json # npm package manifest
114
+ openclaw.plugin.json # OpenClaw plugin manifest
115
+ index.js # Plugin entry point
116
+ lib/
117
+ vendor.js # Vendor management (install/update)
118
+ recommend.js # Keyword-based skill recommendation
119
+ adapter.js # OpenClaw integration helpers
120
+ vendor/ # Vendored superpowers (generated)
121
+ cache/ # Skill index cache (generated)
122
+ README.md
123
+ LICENSE
124
+ ```
125
+
126
+ ## How it works
127
+
128
+ 1. **Install/Update**: Clones `obra/superpowers` into `vendor/superpowers`
129
+ 2. **Index**: Scans `vendor/superpowers/skills/` and builds a local index
130
+ 3. **Recommend**: Matches task keywords against skill rules
131
+ 4. **Load**: Reads skill content (SKILL.md or README.md)
132
+
133
+ ## Mapped Execution Modes
134
+
135
+ The plugin maps superpowers skills to OpenClaw execution patterns:
136
+
137
+ | Skill | Mode |
138
+ |-------|------|
139
+ | brainstorming | local-guidance |
140
+ | writing-plans | local-guidance |
141
+ | executing-plans | local-execution |
142
+ | systematic-debugging | local-guidance |
143
+ | test-driven-development | local-execution |
144
+ | requesting-code-review | review |
145
+ | subagent-driven-development | subagent |
146
+ | dispatching-parallel-agents | parallel |
147
+
148
+ ## License
149
+
150
+ MIT
package/index.js ADDED
@@ -0,0 +1,215 @@
1
+ const path = require('path');
2
+ const { createVendorApi } = require('./lib/vendor');
3
+ const { createRecommendApi, RECOMMEND_RULES } = require('./lib/recommend');
4
+ const { createAdapterApi } = require('./lib/adapter');
5
+
6
+ const ROOT = __dirname;
7
+ const CONFIG = require(path.join(ROOT, 'openclaw.plugin.json')).config;
8
+ const VENDOR_DIR = path.join(ROOT, CONFIG.vendorDir);
9
+ const CACHE_FILE = path.join(ROOT, CONFIG.cacheFile);
10
+ const DEBUG_STATE_FILE = path.join(ROOT, CONFIG.debugStateFile);
11
+ const SKILLS_DIR = path.join(VENDOR_DIR, 'skills');
12
+
13
+ const vendorApi = createVendorApi({ ROOT, CONFIG, VENDOR_DIR, CACHE_FILE, SKILLS_DIR });
14
+ const recommendApi = createRecommendApi({ ROOT, vendorApi });
15
+ const adapterApi = createAdapterApi({ ROOT, CONFIG, DEBUG_STATE_FILE, recommendApi });
16
+
17
+ function textResult(text, details = undefined) {
18
+ return {
19
+ content: [{ type: 'text', text }],
20
+ details
21
+ };
22
+ }
23
+
24
+ function buildOverview() {
25
+ const status = vendorApi.status();
26
+ const index = vendorApi.list();
27
+ const skills = index.skills || [];
28
+ const lines = [
29
+ `installed: ${status.installed ? 'yes' : 'no'}`,
30
+ `vendorDir: ${status.vendorDir}`,
31
+ `skillsDir: ${status.skillsDir}`,
32
+ `cacheFile: ${status.cacheFile}`,
33
+ `upstreamCommit: ${status.upstreamCommit || 'unknown'}`,
34
+ `indexedSkills: ${skills.length}`,
35
+ '',
36
+ 'skills:'
37
+ ];
38
+
39
+ if (!skills.length) lines.push('- none');
40
+ else {
41
+ for (const skill of skills) {
42
+ lines.push(`- ${skill.name}${skill.description ? `: ${skill.description}` : ''}`);
43
+ }
44
+ }
45
+
46
+ return {
47
+ status,
48
+ index,
49
+ skills,
50
+ text: lines.join('\n')
51
+ };
52
+ }
53
+
54
+ function buildUseResult(params) {
55
+ if (params.name) {
56
+ const skillResult = recommendApi.useSkill(params.name);
57
+ if (!skillResult.ok) return {
58
+ ok: false,
59
+ mode: 'skill',
60
+ message: skillResult.message,
61
+ result: skillResult
62
+ };
63
+ return {
64
+ ok: true,
65
+ mode: 'skill',
66
+ skillName: params.name,
67
+ content: skillResult.content || skillResult.message,
68
+ result: skillResult
69
+ };
70
+ }
71
+
72
+ const runResult = adapterApi.run(params.task || '', { includeContent: true });
73
+ return {
74
+ ok: true,
75
+ mode: 'recommend',
76
+ task: params.task || '',
77
+ result: runResult,
78
+ content: runResult.content || null
79
+ };
80
+ }
81
+
82
+ function buildHelpText() {
83
+ return [
84
+ 'Superpowers 菜单:',
85
+ '',
86
+ '/superpowers overview',
87
+ '/superpowers use <任务描述>',
88
+ '/superpowers use name <skill名>',
89
+ '/superpowers install',
90
+ '/superpowers update'
91
+ ].join('\n');
92
+ }
93
+
94
+ module.exports = {
95
+ id: 'superpowers',
96
+ name: 'superpowers',
97
+ description: 'Native OpenClaw adapter for obra/superpowers skills and recommendations.',
98
+ register(api) {
99
+ api.registerCommand({
100
+ name: 'superpowers',
101
+ description: 'Superpowers menu and actions.',
102
+ acceptsArgs: true,
103
+ handler: async (ctx) => {
104
+ const tokens = (ctx.args?.trim() ?? '').split(/\s+/).filter(Boolean);
105
+ const action = (tokens[0] || '').toLowerCase();
106
+ const rest = tokens.slice(1);
107
+
108
+ if (!action || action === 'help' || action === 'menu') {
109
+ return { text: buildHelpText() };
110
+ }
111
+
112
+ if (action === 'overview' || action === 'status' || action === 'list') {
113
+ const overview = buildOverview();
114
+ return { text: overview.text };
115
+ }
116
+
117
+ if (action === 'use') {
118
+ if (!rest.length) return { text: buildHelpText() };
119
+ if ((rest[0] || '').toLowerCase() === 'name') {
120
+ const skillName = rest.slice(1).join(' ').trim();
121
+ if (!skillName) return { text: '用法: /superpowers use name <skill名>' };
122
+ const result = buildUseResult({ name: skillName });
123
+ return { text: result.ok ? (result.content || 'Skill loaded.') : (result.message || 'Skill load failed.') };
124
+ }
125
+ const task = rest.join(' ').trim();
126
+ const result = buildUseResult({ task });
127
+ return { text: JSON.stringify(result.result, null, 2) };
128
+ }
129
+
130
+ if (action === 'install') {
131
+ const result = vendorApi.install();
132
+ return { text: JSON.stringify(result, null, 2) };
133
+ }
134
+
135
+ if (action === 'update') {
136
+ const result = vendorApi.update();
137
+ return { text: JSON.stringify(result, null, 2) };
138
+ }
139
+
140
+ return { text: buildHelpText() };
141
+ }
142
+ });
143
+
144
+ api.registerTool({
145
+ name: 'superpowers_overview',
146
+ label: 'Superpowers Overview',
147
+ description: 'Show superpowers installation status and the current indexed skill list.',
148
+ parameters: {
149
+ type: 'object',
150
+ properties: {},
151
+ additionalProperties: false
152
+ },
153
+ async execute() {
154
+ const overview = buildOverview();
155
+ return textResult(overview.text, overview);
156
+ }
157
+ });
158
+
159
+ api.registerTool({
160
+ name: 'superpowers_use',
161
+ label: 'Superpowers Use',
162
+ description: 'Either recommend a superpowers skill for a task, or load a skill directly by name.',
163
+ parameters: {
164
+ type: 'object',
165
+ properties: {
166
+ task: { type: 'string', description: 'Task description to analyze and recommend against.' },
167
+ name: { type: 'string', description: 'Skill name to load directly, for example systematic-debugging.' }
168
+ },
169
+ additionalProperties: false,
170
+ anyOf: [
171
+ { required: ['task'] },
172
+ { required: ['name'] }
173
+ ]
174
+ },
175
+ async execute(_id, params) {
176
+ const result = buildUseResult(params);
177
+ if (!result.ok) return textResult(result.message || 'superpowers_use failed', { error: true, ...result });
178
+ if (result.mode === 'skill') return textResult(result.content || 'Skill loaded.', result);
179
+ return textResult(JSON.stringify(result.result, null, 2), result);
180
+ }
181
+ });
182
+
183
+ api.registerTool({
184
+ name: 'superpowers_install',
185
+ label: 'Superpowers Install',
186
+ description: 'Install the vendored superpowers repository for first-time setup.',
187
+ parameters: {
188
+ type: 'object',
189
+ properties: {},
190
+ additionalProperties: false
191
+ },
192
+ async execute() {
193
+ const result = vendorApi.install();
194
+ return textResult(JSON.stringify(result, null, 2), result);
195
+ }
196
+ });
197
+
198
+ api.registerTool({
199
+ name: 'superpowers_update',
200
+ label: 'Superpowers Update',
201
+ description: 'Update the vendored superpowers repository from upstream.',
202
+ parameters: {
203
+ type: 'object',
204
+ properties: {},
205
+ additionalProperties: false
206
+ },
207
+ async execute() {
208
+ const result = vendorApi.update();
209
+ return textResult(JSON.stringify(result, null, 2), result);
210
+ }
211
+ });
212
+
213
+ api.logger.info?.(`[superpowers] registered with ${RECOMMEND_RULES.length} recommendation rules`);
214
+ }
215
+ };
package/lib/adapter.js ADDED
@@ -0,0 +1,256 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function createAdapterApi({ ROOT, CONFIG, DEBUG_STATE_FILE, recommendApi }) {
5
+ function getDebugVisible() {
6
+ if (fs.existsSync(DEBUG_STATE_FILE)) {
7
+ try {
8
+ const state = JSON.parse(fs.readFileSync(DEBUG_STATE_FILE, 'utf8'));
9
+ if (typeof state.enabled === 'boolean') return state.enabled;
10
+ } catch {}
11
+ }
12
+ return Boolean(CONFIG.debugVisible);
13
+ }
14
+
15
+ function setDebugVisible(enabled) {
16
+ fs.mkdirSync(path.dirname(DEBUG_STATE_FILE), { recursive: true });
17
+ const payload = {
18
+ enabled: Boolean(enabled),
19
+ updatedAt: new Date().toISOString()
20
+ };
21
+ fs.writeFileSync(DEBUG_STATE_FILE, JSON.stringify(payload, null, 2));
22
+ return payload;
23
+ }
24
+
25
+ function getExecutionMode(skillName) {
26
+ const map = {
27
+ 'brainstorming': 'local-guidance',
28
+ 'writing-plans': 'local-guidance',
29
+ 'executing-plans': 'local-execution',
30
+ 'systematic-debugging': 'local-guidance',
31
+ 'test-driven-development': 'local-execution',
32
+ 'requesting-code-review': 'review',
33
+ 'receiving-code-review': 'review',
34
+ 'subagent-driven-development': 'subagent',
35
+ 'dispatching-parallel-agents': 'parallel',
36
+ 'using-git-worktrees': 'local-execution',
37
+ 'finishing-a-development-branch': 'local-execution'
38
+ };
39
+ return map[skillName] || 'local-guidance';
40
+ }
41
+
42
+ function buildNextStep(skillName) {
43
+ const map = {
44
+ 'brainstorming': '先阅读 skill 指导内容,再围绕目标列出方向、约束和备选方案。',
45
+ 'writing-plans': '先根据 skill 输出分阶段计划,再决定是否进入执行。',
46
+ 'executing-plans': '将已有计划拆成可执行步骤,逐项执行并验证。',
47
+ 'systematic-debugging': '先收集现象和证据,再按 skill 要求定位根因,不直接修。',
48
+ 'test-driven-development': '先定义测试,再写实现,最后验证通过。',
49
+ 'requesting-code-review': '整理变更背景、风险点和重点审查项,再发起 review。',
50
+ 'receiving-code-review': '先读取 review 反馈,再按优先级消化和修正。',
51
+ 'subagent-driven-development': '将任务拆分后映射到 OpenClaw 子会话执行。',
52
+ 'dispatching-parallel-agents': '识别可并行部分,再使用多个子会话分工执行。',
53
+ 'using-git-worktrees': '确认仓库策略后,为并行分支任务使用 git worktree。',
54
+ 'finishing-a-development-branch': '在合并前整理变更、验证状态并完成收尾。'
55
+ };
56
+ return map[skillName] || '读取 skill 内容后,按其流程在 OpenClaw 中执行。';
57
+ }
58
+
59
+ function buildOpenClawHint(skillName, executionMode) {
60
+ const hints = {
61
+ 'local-guidance': '建议在当前会话中直接按 skill 流程推进。',
62
+ 'local-execution': '建议在当前会话中执行,并结合 read/edit/exec 等工具逐步落实。',
63
+ 'review': '建议使用独立会话或子会话做审查,避免和主执行混在一起。',
64
+ 'subagent': '建议使用 sessions_spawn 创建子会话承接拆分任务。',
65
+ 'parallel': '建议将任务拆成互相独立的部分,并使用多个子会话并行处理。'
66
+ };
67
+ return hints[executionMode] || `按 ${skillName} 的流程在 OpenClaw 中执行。`;
68
+ }
69
+
70
+ function buildSuggestedActions(skillName, executionMode) {
71
+ const actions = {
72
+ 'local-guidance': [
73
+ '读取 skill 内容并提炼关键步骤',
74
+ '在当前会话中确认目标、约束和输入信息',
75
+ '按 skill 流程逐步推进'
76
+ ],
77
+ 'local-execution': [
78
+ '读取 skill 内容并拆成执行步骤',
79
+ '在当前会话中直接执行',
80
+ '每步后进行验证和汇报'
81
+ ],
82
+ 'review': [
83
+ '整理背景、变更范围和风险点',
84
+ '使用独立会话或子会话执行 review',
85
+ '汇总 review 结论并回到主会话处理'
86
+ ],
87
+ 'subagent': [
88
+ '识别可拆分子任务',
89
+ '用 sessions_spawn 创建子会话',
90
+ '汇总子会话结果并统一收口'
91
+ ],
92
+ 'parallel': [
93
+ '先拆分互相独立的任务块',
94
+ '为每个任务块创建独立子会话',
95
+ '并行执行后汇总结果并统一验证'
96
+ ]
97
+ };
98
+ return actions[executionMode] || [`按 ${skillName} 的流程在 OpenClaw 中执行`];
99
+ }
100
+
101
+ function buildExecutionPlan(skillName, executionMode) {
102
+ const plans = {
103
+ 'local-guidance': [
104
+ { step: 1, action: 'read-skill', description: '读取并提炼 skill 的关键流程' },
105
+ { step: 2, action: 'clarify-context', description: '确认目标、约束、输入和预期产出' },
106
+ { step: 3, action: 'guide-in-session', description: '在当前会话中按流程推进' }
107
+ ],
108
+ 'local-execution': [
109
+ { step: 1, action: 'read-skill', description: '读取 skill 并转成执行步骤' },
110
+ { step: 2, action: 'execute-in-session', description: '在当前会话中执行具体改动或命令' },
111
+ { step: 3, action: 'verify-result', description: '验证执行结果并汇报' }
112
+ ],
113
+ 'review': [
114
+ { step: 1, action: 'prepare-review-context', description: '整理背景、范围、重点与风险' },
115
+ { step: 2, action: 'spawn-review-session', description: '创建独立 review 会话或子会话' },
116
+ { step: 3, action: 'merge-review-feedback', description: '汇总 review 结论并回到主流程处理' }
117
+ ],
118
+ 'subagent': [
119
+ { step: 1, action: 'split-task', description: '将任务拆成清晰子任务' },
120
+ { step: 2, action: 'spawn-subagents', description: '用 sessions_spawn 创建子会话' },
121
+ { step: 3, action: 'collect-results', description: '汇总子会话结果并统一收口' }
122
+ ],
123
+ 'parallel': [
124
+ { step: 1, action: 'split-independent-work', description: '拆分互相独立的任务块' },
125
+ { step: 2, action: 'spawn-parallel-subagents', description: '为每个任务块创建独立子会话' },
126
+ { step: 3, action: 'merge-and-verify', description: '汇总并验证所有并行结果' }
127
+ ]
128
+ };
129
+
130
+ return {
131
+ skillName,
132
+ executionMode,
133
+ plan: plans[executionMode] || [
134
+ { step: 1, action: 'read-skill', description: `按 ${skillName} 的流程在 OpenClaw 中执行` }
135
+ ]
136
+ };
137
+ }
138
+
139
+ function buildOpenClawActions(skillName, executionMode, taskText = '') {
140
+ const baseTask = String(taskText || '').trim();
141
+ const templates = {
142
+ 'local-guidance': [
143
+ {
144
+ type: 'in-session-guidance',
145
+ purpose: 'guide-current-session',
146
+ messageTemplate: `在当前会话中按 ${skillName} 流程推进:${baseTask || '补充任务描述'}`
147
+ }
148
+ ],
149
+ 'local-execution': [
150
+ {
151
+ type: 'in-session-execution',
152
+ purpose: 'execute-current-session',
153
+ messageTemplate: `在当前会话中执行任务,并按 ${skillName} 流程验证:${baseTask || '补充任务描述'}`
154
+ }
155
+ ],
156
+ 'review': [
157
+ {
158
+ type: 'sessions_spawn',
159
+ purpose: 'review-session',
160
+ messageTemplate: `请基于 ${skillName} 对以下内容进行审查,并给出结构化结论:${baseTask || '补充审查对象和背景'}`
161
+ }
162
+ ],
163
+ 'subagent': [
164
+ {
165
+ type: 'sessions_spawn',
166
+ purpose: 'subtask-worker',
167
+ messageTemplate: `请作为子会话承接拆分后的任务,并按 ${skillName} 流程执行:${baseTask || '补充子任务描述'}`
168
+ }
169
+ ],
170
+ 'parallel': [
171
+ {
172
+ type: 'split-task',
173
+ purpose: 'identify-independent-work',
174
+ messageTemplate: `先把任务拆成可并行的独立部分:${baseTask || '补充任务描述'}`
175
+ },
176
+ {
177
+ type: 'sessions_spawn',
178
+ purpose: 'parallel-worker',
179
+ messageTemplate: `请作为并行子会话处理其中一个独立任务块,并回报结果:${baseTask || '补充任务描述'}`
180
+ },
181
+ {
182
+ type: 'merge-results',
183
+ purpose: 'collect-and-verify',
184
+ messageTemplate: '汇总所有并行子会话结果,并统一验证最终输出。'
185
+ }
186
+ ]
187
+ };
188
+
189
+ return templates[executionMode] || [
190
+ {
191
+ type: 'manual',
192
+ purpose: 'fallback',
193
+ messageTemplate: `按 ${skillName} 的流程在 OpenClaw 中手动执行。`
194
+ }
195
+ ];
196
+ }
197
+
198
+ function formatDebugTag(result) {
199
+ if (!result || !result.recommendation || !result.recommendation.matched) return null;
200
+ if (!getDebugVisible()) return null;
201
+ const skill = result.recommendation.matched;
202
+ const mode = result.executionMode || 'unknown';
203
+ const keywords = (result.recommendation.candidates?.[0]?.keywords || []).join(', ');
204
+ return keywords
205
+ ? `_⚡️[superpowers] ${skill} | ${mode} | ${keywords}_`
206
+ : `_⚡️[superpowers] ${skill} | ${mode}_`;
207
+ }
208
+
209
+ function run(taskText = '', options = {}) {
210
+ const recommendation = recommendApi.recommend(taskText);
211
+ if (!recommendation.matched) {
212
+ return {
213
+ ok: true,
214
+ taskText,
215
+ recommendation,
216
+ usedSkill: null,
217
+ suggestedActions: ['手动调用 list()', '手动指定 useSkill(name)', '补充更明确的任务描述'],
218
+ executionPlan: null,
219
+ openclawActions: [],
220
+ nextStep: '未匹配到明确 skill,建议手动 list 或指定 useSkill(name)。'
221
+ };
222
+ }
223
+
224
+ const used = recommendApi.useSkill(recommendation.matched);
225
+ const executionMode = getExecutionMode(recommendation.matched);
226
+
227
+ const result = {
228
+ ok: used.ok,
229
+ taskText,
230
+ recommendation,
231
+ usedSkill: used.skill || null,
232
+ executionMode,
233
+ openclawHint: buildOpenClawHint(recommendation.matched, executionMode),
234
+ suggestedActions: buildSuggestedActions(recommendation.matched, executionMode),
235
+ executionPlan: buildExecutionPlan(recommendation.matched, executionMode),
236
+ openclawActions: buildOpenClawActions(recommendation.matched, executionMode, taskText),
237
+ content: options.includeContent === false ? null : used.content,
238
+ nextStep: buildNextStep(recommendation.matched),
239
+ message: used.message
240
+ };
241
+ result.debugTag = formatDebugTag(result);
242
+ return result;
243
+ }
244
+
245
+ return {
246
+ getDebugVisible,
247
+ setDebugVisible,
248
+ getExecutionMode,
249
+ buildExecutionPlan,
250
+ buildOpenClawActions,
251
+ formatDebugTag,
252
+ run
253
+ };
254
+ }
255
+
256
+ module.exports = { createAdapterApi };
@@ -0,0 +1,95 @@
1
+ const RECOMMEND_RULES = [
2
+ { keywords: ['brainstorm', 'idea', '创意', '思路', '头脑风暴'], skill: 'brainstorming', score: 2 },
3
+ { keywords: ['plan', '规划', '方案', '拆解', '计划'], skill: 'writing-plans', score: 2 },
4
+ { keywords: ['execute', '执行', '落地', '实施', '开始做'], skill: 'executing-plans', score: 2 },
5
+ { keywords: ['debug', '排查', '故障', '问题定位', '根因'], skill: 'systematic-debugging', score: 3 },
6
+ { keywords: ['test', '测试', 'tdd', '先写测试'], skill: 'test-driven-development', score: 2 },
7
+ { keywords: ['review', '审查', '代码评审', '代码审查'], skill: 'requesting-code-review', score: 3 },
8
+ { keywords: ['subagent', '子代理', '子任务'], skill: 'subagent-driven-development', score: 3 },
9
+ { keywords: ['parallel', '并行', '并发'], skill: 'dispatching-parallel-agents', score: 4 },
10
+ { keywords: ['并行代理', '并行处理'], skill: 'dispatching-parallel-agents', score: 5 },
11
+ { keywords: ['worktree', '分支工作树'], skill: 'using-git-worktrees', score: 2 }
12
+ ];
13
+
14
+ function createRecommendApi({ ROOT, vendorApi }) {
15
+ function getSkill(name) {
16
+ const index = vendorApi.list();
17
+ return index.skills.find((skill) => skill.name === name) || null;
18
+ }
19
+
20
+ function useSkill(name) {
21
+ const skill = getSkill(name);
22
+ if (!skill) {
23
+ return { ok: false, message: `skill not found: ${name}` };
24
+ }
25
+
26
+ const sourcePath = require('path').join(ROOT, skill.sourceFile || '');
27
+ const content = skill.sourceFile && vendorApi.exists(sourcePath)
28
+ ? require('fs').readFileSync(sourcePath, 'utf8')
29
+ : null;
30
+
31
+ return {
32
+ ok: true,
33
+ skill,
34
+ content,
35
+ message: content ? `loaded skill: ${name}` : `skill found, but no readable source file: ${name}`
36
+ };
37
+ }
38
+
39
+ function recommend(taskText = '') {
40
+ const text = String(taskText).toLowerCase();
41
+ const scores = new Map();
42
+
43
+ for (const rule of RECOMMEND_RULES) {
44
+ const matchedKeywords = rule.keywords.filter((kw) => text.includes(kw));
45
+ if (!matchedKeywords.length) continue;
46
+
47
+ const current = scores.get(rule.skill) || { score: 0, keywords: [] };
48
+ current.score += matchedKeywords.length * rule.score;
49
+ current.keywords.push(...matchedKeywords);
50
+ scores.set(rule.skill, current);
51
+ }
52
+
53
+ if (!scores.size) {
54
+ return {
55
+ ok: true,
56
+ matched: null,
57
+ reason: 'no keyword match',
58
+ skill: null,
59
+ candidates: []
60
+ };
61
+ }
62
+
63
+ const ranked = Array.from(scores.entries())
64
+ .map(([skillName, data]) => ({
65
+ skillName,
66
+ score: data.score,
67
+ keywords: Array.from(new Set(data.keywords)),
68
+ skill: getSkill(skillName)
69
+ }))
70
+ .sort((a, b) => b.score - a.score);
71
+
72
+ const top = ranked[0];
73
+
74
+ return {
75
+ ok: true,
76
+ matched: top.skillName,
77
+ reason: `matched keywords: ${top.keywords.join(', ')}`,
78
+ skill: top.skill,
79
+ candidates: ranked.map((item) => ({
80
+ skill: item.skillName,
81
+ score: item.score,
82
+ keywords: item.keywords
83
+ }))
84
+ };
85
+ }
86
+
87
+ return {
88
+ RECOMMEND_RULES,
89
+ getSkill,
90
+ useSkill,
91
+ recommend
92
+ };
93
+ }
94
+
95
+ module.exports = { createRecommendApi, RECOMMEND_RULES };
package/lib/vendor.js ADDED
@@ -0,0 +1,163 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const { execSync } = require('child_process');
5
+
6
+ function createVendorApi({ ROOT, CONFIG, VENDOR_DIR, CACHE_FILE, SKILLS_DIR }) {
7
+ function ensureDir(dir) {
8
+ fs.mkdirSync(dir, { recursive: true });
9
+ }
10
+
11
+ function exists(p) {
12
+ return fs.existsSync(p);
13
+ }
14
+
15
+ function runCommand(command, cwd = ROOT) {
16
+ return execSync(command, { cwd, stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
17
+ }
18
+
19
+ function safeRemove(target) {
20
+ fs.rmSync(target, { recursive: true, force: true });
21
+ }
22
+
23
+ function copyDir(src, dest) {
24
+ safeRemove(dest);
25
+ ensureDir(path.dirname(dest));
26
+ fs.cpSync(src, dest, { recursive: true });
27
+ }
28
+
29
+ function loadIndex() {
30
+ if (!exists(CACHE_FILE)) return null;
31
+ return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
32
+ }
33
+
34
+ function getEmbeddedCommit() {
35
+ return loadIndex()?.upstreamCommit || null;
36
+ }
37
+
38
+ function withTempClone(callback) {
39
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'superpowers-'));
40
+ const cloneDir = path.join(tempRoot, 'repo');
41
+ try {
42
+ runCommand(`git clone --depth 1 --branch ${CONFIG.defaultBranch} ${CONFIG.upstreamRepo} ${cloneDir}`);
43
+ const commit = runCommand('git rev-parse HEAD', cloneDir);
44
+ return callback({ tempRoot, cloneDir, commit });
45
+ } finally {
46
+ safeRemove(tempRoot);
47
+ }
48
+ }
49
+
50
+ function listSkillDirs() {
51
+ if (!exists(SKILLS_DIR)) return [];
52
+ return fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
53
+ .filter((d) => d.isDirectory())
54
+ .map((d) => d.name)
55
+ .sort();
56
+ }
57
+
58
+ function readSkillMeta(skillName) {
59
+ const skillDir = path.join(SKILLS_DIR, skillName);
60
+ const readme = path.join(skillDir, 'README.md');
61
+ const skill = path.join(skillDir, 'SKILL.md');
62
+ const sourceFile = exists(skill) ? skill : readme;
63
+ let description = '';
64
+
65
+ if (exists(sourceFile)) {
66
+ const firstLines = fs.readFileSync(sourceFile, 'utf8').split('\n').slice(0, 12);
67
+ description = firstLines.find((line) => line.trim() && !line.startsWith('#'))?.trim() || '';
68
+ }
69
+
70
+ return {
71
+ name: skillName,
72
+ path: path.relative(ROOT, skillDir),
73
+ sourceFile: exists(sourceFile) ? path.relative(ROOT, sourceFile) : null,
74
+ description,
75
+ enabled: true,
76
+ source: 'obra/superpowers'
77
+ };
78
+ }
79
+
80
+ function refreshIndex(upstreamCommit = null) {
81
+ ensureDir(path.dirname(CACHE_FILE));
82
+ const skills = listSkillDirs().map(readSkillMeta);
83
+ const payload = {
84
+ updatedAt: new Date().toISOString(),
85
+ upstreamCommit: upstreamCommit || getEmbeddedCommit(),
86
+ skills
87
+ };
88
+ fs.writeFileSync(CACHE_FILE, JSON.stringify(payload, null, 2));
89
+ return payload;
90
+ }
91
+
92
+ function install() {
93
+ if (exists(VENDOR_DIR) && exists(SKILLS_DIR)) {
94
+ return {
95
+ ok: true,
96
+ message: 'superpowers vendor already installed',
97
+ vendorDir: VENDOR_DIR,
98
+ upstreamCommit: getEmbeddedCommit()
99
+ };
100
+ }
101
+
102
+ return withTempClone(({ cloneDir, commit }) => {
103
+ const gitDir = path.join(cloneDir, '.git');
104
+ if (exists(gitDir)) safeRemove(gitDir);
105
+ copyDir(cloneDir, VENDOR_DIR);
106
+ const index = refreshIndex(commit);
107
+ return {
108
+ ok: true,
109
+ message: 'superpowers vendor installed',
110
+ vendorDir: VENDOR_DIR,
111
+ upstreamCommit: commit,
112
+ indexedSkills: index.skills.length
113
+ };
114
+ });
115
+ }
116
+
117
+ function update() {
118
+ return withTempClone(({ cloneDir, commit }) => {
119
+ const gitDir = path.join(cloneDir, '.git');
120
+ if (exists(gitDir)) safeRemove(gitDir);
121
+ copyDir(cloneDir, VENDOR_DIR);
122
+ const index = refreshIndex(commit);
123
+ return {
124
+ ok: true,
125
+ message: 'superpowers vendor updated',
126
+ vendorDir: VENDOR_DIR,
127
+ upstreamCommit: commit,
128
+ indexedSkills: index.skills.length
129
+ };
130
+ });
131
+ }
132
+
133
+ function list() {
134
+ return loadIndex() || refreshIndex();
135
+ }
136
+
137
+ function status() {
138
+ return {
139
+ installed: exists(VENDOR_DIR) && exists(SKILLS_DIR),
140
+ vendorDir: VENDOR_DIR,
141
+ skillsDir: SKILLS_DIR,
142
+ cacheFile: CACHE_FILE,
143
+ upstreamCommit: getEmbeddedCommit(),
144
+ indexedSkills: (loadIndex()?.skills || []).length
145
+ };
146
+ }
147
+
148
+ return {
149
+ exists,
150
+ ensureDir,
151
+ loadIndex,
152
+ getEmbeddedCommit,
153
+ listSkillDirs,
154
+ readSkillMeta,
155
+ refreshIndex,
156
+ install,
157
+ update,
158
+ list,
159
+ status
160
+ };
161
+ }
162
+
163
+ module.exports = { createVendorApi };
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "superpowers",
3
+ "name": "superpowers",
4
+ "description": "Native OpenClaw adapter for obra/superpowers skills and recommendations.",
5
+ "skills": ["./vendor/superpowers/skills"],
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "upstreamRepo": {
11
+ "type": "string",
12
+ "description": "Upstream superpowers repository URL.",
13
+ "default": "https://github.com/obra/superpowers.git"
14
+ },
15
+ "defaultBranch": {
16
+ "type": "string",
17
+ "description": "Branch to clone from upstream.",
18
+ "default": "main"
19
+ }
20
+ }
21
+ },
22
+ "config": {
23
+ "upstreamRepo": "https://github.com/obra/superpowers.git",
24
+ "vendorDir": "vendor/superpowers",
25
+ "cacheFile": "cache/index.json",
26
+ "defaultBranch": "main",
27
+ "debugStateFile": "cache/debug.json"
28
+ }
29
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "openclaw-superpowers-plugin",
3
+ "version": "1.0.0",
4
+ "description": "Superpowers skills adapter for OpenClaw",
5
+ "main": "index.js",
6
+ "type": "commonjs",
7
+ "files": [
8
+ "index.js",
9
+ "lib/",
10
+ "openclaw.plugin.json",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/webleon/webleonclawdbot.git",
17
+ "directory": "packages/plugins/superpowers"
18
+ },
19
+ "author": "WebLeOn",
20
+ "license": "MIT",
21
+ "keywords": ["openclaw", "plugin", "superpowers", "skills"]
22
+ }