superpowers-zh 1.7.12 → 1.7.14
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +2 -2
- package/.opencode/INSTALL.md +82 -14
- package/.opencode/plugins/superpowers.js +319 -31
- package/GEMINI.md +3 -2
- package/README.md +50 -18
- package/README.zh-Hant.md +48 -19
- package/RELEASE-NOTES.zh.md +108 -0
- package/assets/sponsors/logo-fluxionai-icon.png +0 -0
- package/assets/sponsors/logo-volcengine-icon.png +0 -0
- package/assets/sponsors/volcengine.png +0 -0
- package/bin/superpowers-zh.js +21 -2
- package/docs/README.aider.md +2 -2
- package/docs/README.claw.md +1 -1
- package/docs/README.cline.md +3 -3
- package/docs/README.codearts.md +1 -1
- package/docs/README.codebuddy.md +1 -1
- package/docs/README.codex.md +26 -0
- package/docs/README.crush.md +1 -1
- package/docs/README.deepseek-harness.md +1 -1
- package/docs/README.deerflow.md +1 -1
- package/docs/README.hermes.md +1 -1
- package/docs/README.kilocode.md +3 -3
- package/docs/README.kiro.md +1 -1
- package/docs/README.opencode.md +128 -19
- package/docs/README.qoder.md +1 -1
- package/docs/README.qwen.md +1 -1
- package/docs/README.trae-cn.md +62 -0
- package/docs/README.vscode.md +1 -1
- package/docs/README.zcode.md +2 -2
- package/gemini-extension.json +1 -1
- package/hooks/session-start +6 -2
- package/index.js +9 -0
- package/package.json +2 -1
- package/skills/brainstorming/SKILL.md +19 -3
- package/skills/brainstorming/visual-companion.md +36 -33
- package/skills/diagnosing-superpowers/SKILL.md +102 -0
- package/skills/diagnosing-superpowers/prompts/analyst-common.md +32 -0
- package/skills/diagnosing-superpowers/prompts/cost-and-time.md +20 -0
- package/skills/diagnosing-superpowers/prompts/plan-adherence.md +21 -0
- package/skills/diagnosing-superpowers/prompts/quality-evidence.md +20 -0
- package/skills/diagnosing-superpowers/prompts/repeated-work.md +24 -0
- package/skills/diagnosing-superpowers/prompts/request-conflicts.md +14 -0
- package/skills/diagnosing-superpowers/prompts/scrub-audit.md +26 -0
- package/skills/diagnosing-superpowers/prompts/scrub.md +22 -0
- package/skills/diagnosing-superpowers/prompts/similar-session.md +34 -0
- package/skills/diagnosing-superpowers/prompts/skill-timeline.md +23 -0
- package/skills/diagnosing-superpowers/prompts/stumbles.md +20 -0
- package/skills/diagnosing-superpowers/references/context-safety.md +20 -0
- package/skills/diagnosing-superpowers/references/github-issues.md +46 -0
- package/skills/diagnosing-superpowers/references/redaction-policy.md +29 -0
- package/skills/diagnosing-superpowers/references/session-discovery.md +23 -0
- package/skills/diagnosing-superpowers/templates/bundle-README.md +65 -0
- package/skills/diagnosing-superpowers/templates/case.md +61 -0
- package/skills/diagnosing-superpowers/templates/issue.md +48 -0
- package/skills/diagnosing-superpowers/templates/report.md +78 -0
- package/skills/executing-plans/SKILL.md +176 -125
- package/skills/executing-plans/scripts/task-done +52 -0
- package/skills/executing-plans/scripts/task-start +28 -0
- package/skills/mcp-builder/SKILL.md +1 -1
- package/skills/requesting-code-review/SKILL.md +1 -1
- package/skills/requesting-code-review/code-reviewer.md +8 -0
- package/skills/subagent-driven-development/SKILL.md +17 -17
- package/skills/subagent-driven-development/re-review-prompt.md +1 -1
- package/skills/subagent-driven-development/scripts/review-package +8 -1
- package/skills/subagent-driven-development/scripts/sdd-workspace +44 -2
- package/skills/subagent-driven-development/scripts/task-brief +3 -1
- package/skills/subagent-driven-development/task-reviewer-prompt.md +2 -2
- package/skills/systematic-debugging/root-cause-tracing.md +1 -1
- package/skills/test-driven-development/SKILL.md +2 -0
- package/skills/using-superpowers/SKILL.md +19 -14
- package/skills/using-superpowers/references/claude-code-tools.md +11 -0
- package/skills/using-superpowers/references/muse-tools.md +35 -0
- package/skills/writing-plans/SKILL.md +34 -27
- package/skills/writing-skills/SKILL.md +4 -2
- package/skills/writing-plans/plan-document-reviewer-prompt.md +0 -49
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superpowers-zh",
|
|
3
|
-
"description": "AI 编程超能力中文增强版:
|
|
3
|
+
"description": "AI 编程超能力中文增强版:21 个 skills,支持 Claude Code / Hermes Agent / Cursor / Claw Code / Qoder 等 26 款工具",
|
|
4
4
|
"owner": {
|
|
5
5
|
"name": "jnMetaCode",
|
|
6
6
|
"url": "https://github.com/jnMetaCode"
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"plugins": [
|
|
9
9
|
{
|
|
10
10
|
"name": "superpowers-zh",
|
|
11
|
-
"description": "AI 编程超能力中文增强版:
|
|
12
|
-
"version": "1.7.
|
|
11
|
+
"description": "AI 编程超能力中文增强版:21 个 skills(15 翻译 + 4 中国原创 + 2 上游历史保留),支持 Claude Code / Hermes Agent / Cursor / Claw Code / Qoder 等 26 款工具",
|
|
12
|
+
"version": "1.7.14",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "jnMetaCode",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superpowers-zh",
|
|
3
|
-
"description": "AI 编程超能力中文增强版:
|
|
4
|
-
"version": "1.7.
|
|
3
|
+
"description": "AI 编程超能力中文增强版:21 个 skills(15 翻译 + 4 中国原创 + 2 上游历史保留),支持 Claude Code / Hermes Agent / Cursor / Claw Code / Qoder 等 26 款工具",
|
|
4
|
+
"version": "1.7.14",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "jnMetaCode",
|
|
7
7
|
"url": "https://github.com/jnMetaCode"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superpowers-zh",
|
|
3
3
|
"displayName": "Superpowers 中文版",
|
|
4
|
-
"description": "AI 编程超能力中文增强版:
|
|
5
|
-
"version": "1.7.
|
|
4
|
+
"description": "AI 编程超能力中文增强版:21 个 skills(15 翻译 + 4 中国原创 + 2 上游历史保留),支持 Cursor / Claude Code / Hermes Agent / Claw Code / Qoder 等 26 款工具",
|
|
5
|
+
"version": "1.7.14",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "jnMetaCode",
|
|
8
8
|
"url": "https://github.com/jnMetaCode"
|
package/.opencode/INSTALL.md
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
## 安装步骤
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
OpenCode V2 需要 2.0.4 或更高版本。
|
|
10
|
+
|
|
11
|
+
### OpenCode V1
|
|
12
|
+
|
|
13
|
+
使用原有的 V1 插件配置,在你的 `opencode.json`(全局或项目级别)中将 superpowers-zh 添加到 `plugin` 数组:
|
|
10
14
|
|
|
11
15
|
```json
|
|
12
16
|
{
|
|
@@ -14,10 +18,24 @@
|
|
|
14
18
|
}
|
|
15
19
|
```
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
### OpenCode V2(2.0.4 或更高版本)
|
|
22
|
+
|
|
23
|
+
使用 V2 插件配置(注意键名是 `plugins`):
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"plugins": ["superpowers@git+https://github.com/jnMetaCode/superpowers-zh.git"]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
如果是 V2 的本地安装,配置的应当是包含 `index.js` 的仓库目录。OpenCode 2.0.4 和 2.0.7 会拒绝直接配置成 JavaScript 文件路径。通过自动发现找到的插件符号链接仍然受支持。
|
|
32
|
+
|
|
33
|
+
重启 OpenCode。V2 使用 `opencode` 命令;`opencode2` 可能作为别名可用。插件会通过 OpenCode 的插件管理器安装,并注册所有 skills。
|
|
18
34
|
|
|
19
35
|
通过询问来验证:"告诉我你的超能力"
|
|
20
36
|
|
|
37
|
+
OpenCode 使用它自己的插件安装。如果你同时在用 Claude Code、Codex 或其他运行环境,需要为每一个分别安装 Superpowers。
|
|
38
|
+
|
|
21
39
|
## 从旧版符号链接安装方式迁移
|
|
22
40
|
|
|
23
41
|
如果你之前使用 `git clone` 和符号链接安装过 superpowers,请移除旧的配置:
|
|
@@ -41,28 +59,59 @@ rm -rf ~/.config/opencode/superpowers
|
|
|
41
59
|
|
|
42
60
|
```
|
|
43
61
|
use skill tool to list skills
|
|
44
|
-
use skill tool to load
|
|
62
|
+
use skill tool to load brainstorming
|
|
45
63
|
```
|
|
46
64
|
|
|
47
65
|
## 更新
|
|
48
66
|
|
|
49
|
-
Superpowers
|
|
67
|
+
OpenCode 通过一个基于 git 的包说明(package spec)安装 Superpowers。有些 OpenCode 和 Bun 版本会把解析出的 git 依赖锁定在 lockfile 或缓存里,所以重启不一定能拿到 Superpowers 的最新提交。如果更新没有生效,清掉 OpenCode 的包缓存,或重新安装插件。
|
|
50
68
|
|
|
51
|
-
|
|
69
|
+
要固定到特定版本,在包说明后面加上标签或提交(V1 的 `plugin` 键和 V2 的 `plugins` 键写法相同):
|
|
52
70
|
|
|
53
71
|
```json
|
|
54
72
|
{
|
|
55
|
-
"plugin": ["superpowers@git+https://github.com/jnMetaCode/superpowers-zh.git#v1.
|
|
73
|
+
"plugin": ["superpowers@git+https://github.com/jnMetaCode/superpowers-zh.git#v1.7.13"]
|
|
56
74
|
}
|
|
57
75
|
```
|
|
58
76
|
|
|
77
|
+
在 V2 上,要固定到包含 V2 插件的版本(v1.7.13 之后的版本);v1.7.13 及更早的发布只能在 V1 上加载。
|
|
78
|
+
|
|
59
79
|
## 故障排除
|
|
60
80
|
|
|
61
81
|
### 插件未加载
|
|
62
82
|
|
|
63
|
-
1.
|
|
83
|
+
1. 检查日志。V1:`opencode run --print-logs "hello" 2>&1 | grep -i superpowers`。
|
|
84
|
+
V2 在后台服务进程里加载插件,所以要加上 `--standalone`:
|
|
85
|
+
`opencode run --standalone --print-logs "hello" 2>&1 | grep -i superpowers`,
|
|
86
|
+
或者查看 `~/.local/share/opencode/log/opencode.log`,过滤 `role=server`。
|
|
64
87
|
2. 验证 `opencode.json` 中的插件配置
|
|
65
|
-
3.
|
|
88
|
+
3. 确保你运行的是较新版本的 OpenCode
|
|
89
|
+
|
|
90
|
+
### Windows 安装问题
|
|
91
|
+
|
|
92
|
+
部分 Windows 版 OpenCode 对基于 git 的插件说明存在上游安装器问题,包括 `git+https` URL 的缓存路径问题,以及 Bun 找不到 `git.exe`(即使它在普通终端里能用)。如果 OpenCode 装不上插件,可以试试用系统的 npm 安装,再让 OpenCode 指向本地包:
|
|
93
|
+
|
|
94
|
+
```powershell
|
|
95
|
+
npm install superpowers@git+https://github.com/jnMetaCode/superpowers-zh.git --prefix "$HOME\.config\opencode"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
然后按你的 OpenCode 版本,在 `opencode.json` 里使用已安装包的绝对路径。OpenCode 不展开 `~`;`~/...` 这样的条目会被当成包名,而不是本地目录。
|
|
99
|
+
|
|
100
|
+
**V1:**
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"plugin": ["C:\\Users\\<you>\\.config\\opencode\\node_modules\\superpowers"]
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**V2(2.0.4 或更高版本):**
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"plugins": ["C:\\Users\\<you>\\.config\\opencode\\node_modules\\superpowers"]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
66
115
|
|
|
67
116
|
### Skills 未找到
|
|
68
117
|
|
|
@@ -71,13 +120,32 @@ Superpowers 会在你重启 OpenCode 时自动更新。
|
|
|
71
120
|
|
|
72
121
|
### 工具映射
|
|
73
122
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
123
|
+
Skill 用动作来表述(「创建一条待办」「分派一个子智能体」「读取一个文件」)。插件会按 OpenCode 的版本注入对应的映射——先确认你的 OpenCode 版本:
|
|
124
|
+
|
|
125
|
+
**V1(`opencode` 1.x):**
|
|
126
|
+
|
|
127
|
+
- 「创建一条待办」/「在待办列表里标记完成」→ `todowrite`
|
|
128
|
+
- `Subagent (general-purpose):` 模板 → `task` 工具,传 `subagent_type: "general"`(探索代码库时用 `"explore"`)
|
|
129
|
+
- 「调用一个 skill」→ OpenCode 的原生 `skill` 工具
|
|
130
|
+
- 「读取文件」→ `read`
|
|
131
|
+
- 「创建文件」/「编辑文件」/「删除文件」→ `apply_patch`
|
|
132
|
+
- 「运行 shell 命令」→ `bash`
|
|
133
|
+
- 「搜索文件内容」/「按文件名查找」→ `grep`、`glob`
|
|
134
|
+
- 「抓取 URL」→ `webfetch`
|
|
135
|
+
|
|
136
|
+
**V2(`opencode` 2.0.4 或更高版本;`opencode2` 可能作为别名可用):**
|
|
137
|
+
|
|
138
|
+
- 「创建一条待办」→ V2 没有待办工具;改为在一个 markdown 文件里跟踪计划
|
|
139
|
+
- `Subagent (general-purpose):` 模板 → `subagent` 工具,传 `agent: "general"`(或 `"explore"`);传 `sessionID` 可以继续之前的子智能体
|
|
140
|
+
- 「调用一个 skill」→ OpenCode 的原生 `skill` 工具
|
|
141
|
+
- 「读取文件」→ `read`
|
|
142
|
+
- 「创建、编辑或删除文件」→ 有 `patch` 时用 `patch` 并传 `patchText`;否则用 `write` 创建或覆盖文件、`edit` 做定点修改、`shell` 删除文件
|
|
143
|
+
- 「运行 shell 命令」→ `shell`(`command`、`workdir`、`timeout`、`background`)
|
|
144
|
+
- 「搜索文件内容」/「按文件名查找」→ `grep`、`glob`
|
|
145
|
+
- 「抓取 URL」→ `webfetch`
|
|
146
|
+
- 「搜索网络」→ `websearch`
|
|
79
147
|
|
|
80
148
|
## 获取帮助
|
|
81
149
|
|
|
82
150
|
- 报告问题:https://github.com/jnMetaCode/superpowers-zh/issues
|
|
83
|
-
- 完整文档:https://github.com/jnMetaCode/superpowers-zh
|
|
151
|
+
- 完整文档:https://github.com/jnMetaCode/superpowers-zh/blob/main/docs/README.opencode.md
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Superpowers plugin for OpenCode.ai
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Dual-compatible with OpenCode V1 and V2.
|
|
5
|
+
*
|
|
6
|
+
* V1 (opencode): loaded via named export SuperpowersPlugin — provides config
|
|
7
|
+
* hook for skills registration and experimental.chat.messages.transform for
|
|
8
|
+
* bootstrap injection.
|
|
9
|
+
*
|
|
10
|
+
* V2 (opencode2): loaded via default export { id, setup } by PluginSupervisor.
|
|
11
|
+
* setup() registers skills natively via ctx.skill.transform(), and injects
|
|
12
|
+
* bootstrap context via ctx.session.hook("context").
|
|
13
|
+
*
|
|
14
|
+
* No external dependencies — pure JavaScript works in both V1 and V2 without
|
|
15
|
+
* installing @opencode-ai/plugin or effect.
|
|
6
16
|
*/
|
|
7
17
|
|
|
8
18
|
import path from 'path';
|
|
@@ -11,49 +21,113 @@ import { fileURLToPath } from 'url';
|
|
|
11
21
|
|
|
12
22
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
23
|
|
|
14
|
-
//
|
|
24
|
+
// Skills directory shared by V1 (config hook) and V2 (setup/ctx.skill.transform)
|
|
25
|
+
const superpowersSkillsDir = path.resolve(__dirname, '../../skills');
|
|
26
|
+
|
|
27
|
+
// Simple frontmatter extraction (avoid dependency on skills-core for
|
|
28
|
+
// bootstrap). Handles plain `key: value` lines, quoted values (including
|
|
29
|
+
// quotes that close on an indented continuation line), YAML block scalar
|
|
30
|
+
// markers (`>`, `|`) with indented continuation lines, and CRLF line
|
|
31
|
+
// endings. Not a full YAML parser — nested maps flatten into their parent
|
|
32
|
+
// key's value, which is fine for the name/description fields consumed here.
|
|
15
33
|
const extractAndStripFrontmatter = (content) => {
|
|
16
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
34
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
17
35
|
if (!match) return { frontmatter: {}, content };
|
|
18
36
|
|
|
19
37
|
const frontmatterStr = match[1];
|
|
20
38
|
const body = match[2];
|
|
21
39
|
const frontmatter = {};
|
|
40
|
+
let lastKey = null;
|
|
22
41
|
|
|
23
|
-
for (const
|
|
42
|
+
for (const rawLine of frontmatterStr.split('\n')) {
|
|
43
|
+
const line = rawLine.replace(/\r$/, '');
|
|
24
44
|
const colonIdx = line.indexOf(':');
|
|
25
|
-
if (colonIdx > 0) {
|
|
45
|
+
if (colonIdx > 0 && !/^\s/.test(line)) {
|
|
26
46
|
const key = line.slice(0, colonIdx).trim();
|
|
27
|
-
const value = line.slice(colonIdx + 1).trim()
|
|
28
|
-
|
|
47
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
48
|
+
// Block scalar markers (>, |, optionally with +/- chomping) carry no
|
|
49
|
+
// value themselves; the indented lines that follow do.
|
|
50
|
+
frontmatter[key] = /^(>[+-]?|\|[+-]?)$/.test(value) ? '' : value;
|
|
51
|
+
lastKey = key;
|
|
52
|
+
} else if (lastKey !== null && line.trim() !== '') {
|
|
53
|
+
// Continuation of a multi-line value: append rather than drop so long
|
|
54
|
+
// descriptions survive parsing. Newlines collapse to spaces — good
|
|
55
|
+
// enough for the single-line name/description fields consumed here.
|
|
56
|
+
frontmatter[lastKey] = `${frontmatter[lastKey]} ${line.trim()}`.trim();
|
|
29
57
|
}
|
|
30
58
|
}
|
|
31
59
|
|
|
60
|
+
// A quoted value may close on a continuation line, so unquote only once
|
|
61
|
+
// the value is fully assembled: strip exactly one matching surrounding
|
|
62
|
+
// pair and leave unbalanced quotes alone.
|
|
63
|
+
for (const key of Object.keys(frontmatter)) {
|
|
64
|
+
frontmatter[key] = frontmatter[key].replace(/^(["'])([\s\S]*)\1$/, '$2');
|
|
65
|
+
}
|
|
66
|
+
|
|
32
67
|
return { frontmatter, content: body };
|
|
33
68
|
};
|
|
34
69
|
|
|
35
|
-
|
|
36
|
-
|
|
70
|
+
// Tool mapping injected into the bootstrap, differentiated by host flavor.
|
|
71
|
+
// V1 (OpenCode 1.18.x) and V2 (OpenCode 2.0.4/2.0.7) expose different built-in
|
|
72
|
+
// tools, so each flavor's injection path picks its own constant below.
|
|
73
|
+
// Exported for tests (tests/opencode/test-bootstrap-caching.mjs).
|
|
37
74
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
75
|
+
// V1 built-ins: todowrite, task (subagent_type), skill, read, apply_patch,
|
|
76
|
+
// bash, grep, glob, webfetch.
|
|
77
|
+
export const V1_MAPPING = `**Tool Mapping for OpenCode:**
|
|
78
|
+
When skills request actions, substitute OpenCode equivalents:
|
|
79
|
+
- Create or update todos → \`todowrite\`
|
|
80
|
+
- \`Subagent (general-purpose):\` → \`task\` with \`subagent_type: "general"\`
|
|
81
|
+
- Invoke a skill → OpenCode's native \`skill\` tool
|
|
82
|
+
- Read files → \`read\`
|
|
83
|
+
- Create, edit, or delete files → \`apply_patch\`
|
|
84
|
+
- Run shell commands → \`bash\`
|
|
85
|
+
- Search files → \`grep\`, \`glob\`
|
|
86
|
+
- Fetch a URL → \`webfetch\`
|
|
43
87
|
|
|
44
|
-
|
|
45
|
-
const { content } = extractAndStripFrontmatter(fullContent);
|
|
88
|
+
Use OpenCode's native \`skill\` tool to list and load skills.`;
|
|
46
89
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
90
|
+
// V2 built-ins: no todo tool at all; task → subagent (agent name in 'agent',
|
|
91
|
+
// continuation via sessionID); apply_patch → patch (patchText, same patch
|
|
92
|
+
// format); bash → shell. read, write, edit, grep, glob, webfetch, websearch,
|
|
93
|
+
// and skill all exist under those names (verified against the 2.0.4 and 2.0.7
|
|
94
|
+
// host contracts).
|
|
95
|
+
export const V2_MAPPING = `**Tool Mapping for OpenCode:**
|
|
96
|
+
When skills request actions, substitute OpenCode equivalents:
|
|
97
|
+
- Create or update todos → OpenCode v2 has no todo tool; track the plan in a markdown file (or the harness's plan facility) instead
|
|
98
|
+
- \`Subagent (general-purpose):\` → \`subagent\` with \`agent: "general"\` (give it \`description\` and \`prompt\`, optionally \`background\`; pass \`sessionID\` to continue a previous subagent)
|
|
99
|
+
- Invoke a skill → OpenCode's native \`skill\` tool
|
|
100
|
+
- Read files → \`read\`
|
|
101
|
+
- Create, edit, or delete files → use \`patch\` with \`patchText\` when available; otherwise use \`write\` to create or overwrite files, \`edit\` for targeted changes, and \`shell\` for deletion
|
|
102
|
+
- Run shell commands → \`shell\` (\`command\`, \`workdir\`, \`timeout\`, \`background\`)
|
|
103
|
+
- Search files → \`grep\`, \`glob\`
|
|
104
|
+
- Fetch a URL → \`webfetch\`
|
|
105
|
+
- Search the web → \`websearch\`
|
|
53
106
|
|
|
54
107
|
Use OpenCode's native \`skill\` tool to list and load skills.`;
|
|
55
108
|
|
|
56
|
-
|
|
109
|
+
// Module-level cache for bootstrap content, keyed by tool mapping (host
|
|
110
|
+
// flavor). The SKILL.md file does not change during a session, so reading +
|
|
111
|
+
// parsing it once eliminates redundant fs.existsSync + fs.readFileSync +
|
|
112
|
+
// regex work on every agent step. See #1202 for the full analysis.
|
|
113
|
+
const _bootstrapCache = new Map(); // mapping -> bootstrap (null = file missing)
|
|
114
|
+
|
|
115
|
+
// Helper to generate bootstrap content (cached after first call per mapping)
|
|
116
|
+
const getBootstrapContent = (toolMapping) => {
|
|
117
|
+
// Return cached result on subsequent calls
|
|
118
|
+
if (_bootstrapCache.has(toolMapping)) return _bootstrapCache.get(toolMapping);
|
|
119
|
+
|
|
120
|
+
// Try to load using-superpowers skill
|
|
121
|
+
const skillPath = path.join(superpowersSkillsDir, 'using-superpowers', 'SKILL.md');
|
|
122
|
+
if (!fs.existsSync(skillPath)) {
|
|
123
|
+
_bootstrapCache.set(toolMapping, null);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const fullContent = fs.readFileSync(skillPath, 'utf8');
|
|
128
|
+
const { content } = extractAndStripFrontmatter(fullContent);
|
|
129
|
+
|
|
130
|
+
_bootstrapCache.set(toolMapping, `<EXTREMELY_IMPORTANT>
|
|
57
131
|
You have superpowers.
|
|
58
132
|
|
|
59
133
|
**IMPORTANT: The using-superpowers skill content is included below. It is ALREADY LOADED - you are currently following it. Do NOT use the skill tool to load "using-superpowers" again - that would be redundant.**
|
|
@@ -61,15 +135,96 @@ You have superpowers.
|
|
|
61
135
|
${content}
|
|
62
136
|
|
|
63
137
|
${toolMapping}
|
|
64
|
-
</EXTREMELY_IMPORTANT
|
|
65
|
-
|
|
138
|
+
</EXTREMELY_IMPORTANT>`);
|
|
139
|
+
|
|
140
|
+
return _bootstrapCache.get(toolMapping);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// --- Task-subagent (child session) detection --------------------------------
|
|
144
|
+
//
|
|
145
|
+
// #2160: the bootstrap drives controller workflows (brainstorming, planning,
|
|
146
|
+
// approval cycles). Injecting it into task subagent sessions makes workers
|
|
147
|
+
// restart design/approval cycles for work the parent already authorised; the
|
|
148
|
+
// <SUBAGENT-STOP> note inside the bootstrap relies on model compliance, which
|
|
149
|
+
// is not reliable. Detect child sessions structurally instead: a parentID on
|
|
150
|
+
// the session is the child signal on both flavors (task sessions are created
|
|
151
|
+
// with one; top-level sessions simply lack the field), so when the session
|
|
152
|
+
// carrying the message has a parentID we skip bootstrap injection. Skills
|
|
153
|
+
// stay registered for every session — workers keep explicit access to
|
|
154
|
+
// execution skills.
|
|
155
|
+
|
|
156
|
+
// sessionID -> is-child decision. parentID never changes for a session, so
|
|
157
|
+
// the result is cached until eviction and the injection hook (which fires on
|
|
158
|
+
// every agent step) pays only one client roundtrip per session. The V2
|
|
159
|
+
// service process is long-lived and sessions accumulate over weeks, so the
|
|
160
|
+
// cache is bounded: when full, drop the oldest quarter (Map iterates keys in
|
|
161
|
+
// insertion order). An evicted session merely pays one extra lookup if seen
|
|
162
|
+
// again.
|
|
163
|
+
const CHILD_SESSION_CACHE_MAX = 512;
|
|
164
|
+
const _childSessionCache = new Map();
|
|
165
|
+
|
|
166
|
+
const _cacheChildSession = (sessionID, isChild) => {
|
|
167
|
+
if (_childSessionCache.size >= CHILD_SESSION_CACHE_MAX) {
|
|
168
|
+
let toDrop = Math.ceil(CHILD_SESSION_CACHE_MAX / 4);
|
|
169
|
+
for (const key of _childSessionCache.keys()) {
|
|
170
|
+
if (toDrop-- <= 0) break;
|
|
171
|
+
_childSessionCache.delete(key);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
_childSessionCache.set(sessionID, isChild);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const isChildSession = async (fetchSession, sessionID) => {
|
|
178
|
+
if (!sessionID) return false; // unknown session: keep current behavior
|
|
179
|
+
if (_childSessionCache.has(sessionID)) return _childSessionCache.get(sessionID);
|
|
180
|
+
|
|
181
|
+
let isChild = false;
|
|
182
|
+
try {
|
|
183
|
+
const result = await fetchSession(sessionID);
|
|
184
|
+
// V1 returns a successful SDK envelope while V2 returns a direct session
|
|
185
|
+
// record. Validate both shapes before classifying or caching the result;
|
|
186
|
+
// resolved SDK errors must follow the same fail-open path as rejections.
|
|
187
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
|
188
|
+
throw new Error('Session lookup returned no usable record');
|
|
189
|
+
}
|
|
190
|
+
if (result.error != null || result.response?.ok === false) {
|
|
191
|
+
throw new Error('Session lookup was unsuccessful');
|
|
192
|
+
}
|
|
193
|
+
const session = 'data' in result ? result.data : result;
|
|
194
|
+
if (!session || typeof session !== 'object' || Array.isArray(session) || session.id !== sessionID) {
|
|
195
|
+
throw new Error('Session lookup returned an invalid session identity');
|
|
196
|
+
}
|
|
197
|
+
if (session.parentID !== undefined &&
|
|
198
|
+
(typeof session.parentID !== 'string' || session.parentID.length === 0)) {
|
|
199
|
+
throw new Error('Session lookup returned an invalid parent identity');
|
|
200
|
+
}
|
|
201
|
+
isChild = session.parentID !== undefined;
|
|
202
|
+
} catch (err) {
|
|
203
|
+
// Fail open: on lookup errors keep injecting (previous behavior) and do
|
|
204
|
+
// not cache, so a transient failure can recover on the next step.
|
|
205
|
+
console.error('[superpowers] session lookup failed, treating session as top-level:', err);
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
_cacheChildSession(sessionID, isChild);
|
|
209
|
+
return isChild;
|
|
210
|
+
};
|
|
66
211
|
|
|
212
|
+
/**
|
|
213
|
+
* V1 Plugin Function (named export + default.server)
|
|
214
|
+
*
|
|
215
|
+
* Used by V1 (OpenCode 1.x): discovered via named export scanning.
|
|
216
|
+
* Provides: config hook (V1 skills registration) + bootstrap injection
|
|
217
|
+
* (experimental.chat.messages.transform).
|
|
218
|
+
*/
|
|
219
|
+
export const SuperpowersPlugin = async ({ client, directory }) => {
|
|
67
220
|
return {
|
|
68
221
|
// Inject skills path into live config so OpenCode discovers superpowers skills
|
|
69
222
|
// without requiring manual symlinks or config file edits.
|
|
70
|
-
// This works because Config.get() returns a cached singleton — modifications
|
|
71
|
-
// here are visible when skills are lazily discovered later.
|
|
72
223
|
config: async (config) => {
|
|
224
|
+
// V2: skills is a flat array — skip, setup() handles V2 skill registration
|
|
225
|
+
if (Array.isArray(config.skills)) return;
|
|
226
|
+
|
|
227
|
+
// V1: skills is { paths: [...] }
|
|
73
228
|
config.skills = config.skills || {};
|
|
74
229
|
config.skills.paths = config.skills.paths || [];
|
|
75
230
|
if (!config.skills.paths.includes(superpowersSkillsDir)) {
|
|
@@ -77,19 +232,152 @@ ${toolMapping}
|
|
|
77
232
|
}
|
|
78
233
|
},
|
|
79
234
|
|
|
80
|
-
// Inject bootstrap into the first user message of each session.
|
|
235
|
+
// Inject bootstrap into the first user message of each top-level session.
|
|
81
236
|
// Using a user message instead of a system message avoids:
|
|
82
237
|
// 1. Token bloat from system messages repeated every turn (#750)
|
|
83
238
|
// 2. Multiple system messages breaking Qwen and other models (#894)
|
|
239
|
+
//
|
|
240
|
+
// The hook fires on every agent step (not just every turn) because
|
|
241
|
+
// opencode's prompt.ts reloads messages from DB each step. Fresh message
|
|
242
|
+
// arrays may need injection again, so getBootstrapContent() must not do
|
|
243
|
+
// repeated disk work.
|
|
84
244
|
'experimental.chat.messages.transform': async (_input, output) => {
|
|
85
|
-
const bootstrap = getBootstrapContent();
|
|
245
|
+
const bootstrap = getBootstrapContent(V1_MAPPING);
|
|
86
246
|
if (!bootstrap || !output.messages.length) return;
|
|
87
247
|
const firstUser = output.messages.find(m => m.info.role === 'user');
|
|
88
248
|
if (!firstUser || !firstUser.parts.length) return;
|
|
89
|
-
|
|
249
|
+
|
|
250
|
+
// Guard: skip if first user message already contains bootstrap.
|
|
90
251
|
if (firstUser.parts.some(p => p.type === 'text' && p.text.includes('EXTREMELY_IMPORTANT'))) return;
|
|
252
|
+
|
|
253
|
+
// #2160: never restart the controller workflow inside task subagent
|
|
254
|
+
// (child) sessions. V1 passes no input to this hook (verified in the
|
|
255
|
+
// 1.18.x bundle: trigger(..., {}, {messages})), so take the sessionID
|
|
256
|
+
// from the message record itself.
|
|
257
|
+
if (client && await isChildSession(
|
|
258
|
+
(id) => client.session.get({ path: { id } }),
|
|
259
|
+
firstUser.info.sessionID,
|
|
260
|
+
)) return;
|
|
261
|
+
|
|
91
262
|
const ref = firstUser.parts[0];
|
|
92
263
|
firstUser.parts.unshift({ ...ref, type: 'text', text: bootstrap });
|
|
93
264
|
}
|
|
94
265
|
};
|
|
95
266
|
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* V2 Setup Function (default.setup)
|
|
270
|
+
*
|
|
271
|
+
* Called by V2 PluginSupervisor (packages/core/src/plugin/supervisor.ts).
|
|
272
|
+
* Performs two things:
|
|
273
|
+
*
|
|
274
|
+
* 1. Registers every skills/<name>/SKILL.md as a native Skill.Info object
|
|
275
|
+
* via ctx.skill.transform((draft) => draft.add(info)).
|
|
276
|
+
* V2 removed the old draft.source() directory registration; the draft API
|
|
277
|
+
* is now { list, add, update, remove } where add() decodes plain objects
|
|
278
|
+
* against the host's Skill.Info schema (OpenCode 2.0.4 contract):
|
|
279
|
+
* { id, name, description?, autoinvoke?, path, content }. The file field
|
|
280
|
+
* is `path` — renamed from `location` in upstream commit 199aabe9e2,
|
|
281
|
+
* first released in v2.0.4.
|
|
282
|
+
* See packages/core/src/plugin/skill.ts and packages/schema/src/skill.ts.
|
|
283
|
+
* 2. Injects bootstrap context via ctx.session.hook("context"), the V2
|
|
284
|
+
* equivalent of V1's experimental.chat.messages.transform.
|
|
285
|
+
*/
|
|
286
|
+
async function setup(ctx) {
|
|
287
|
+
// V1 (observed on opencode 1.18.18) also invokes default.setup, but with a
|
|
288
|
+
// V1-shaped ctx that lacks the skill/session domains. Detect it and return
|
|
289
|
+
// quietly — V1 is served entirely by the SuperpowersPlugin named export.
|
|
290
|
+
if (!ctx || !ctx.skill || typeof ctx.skill.transform !== 'function' || !ctx.session || typeof ctx.session.hook !== 'function') {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 1. Register skills (one transform; one draft.add per skill)
|
|
295
|
+
try {
|
|
296
|
+
const skills = [];
|
|
297
|
+
if (fs.existsSync(superpowersSkillsDir)) {
|
|
298
|
+
for (const entry of fs.readdirSync(superpowersSkillsDir, { withFileTypes: true })) {
|
|
299
|
+
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
|
|
300
|
+
const skillPath = path.join(superpowersSkillsDir, entry.name, 'SKILL.md');
|
|
301
|
+
if (!fs.existsSync(skillPath)) continue;
|
|
302
|
+
const { frontmatter, content } = extractAndStripFrontmatter(fs.readFileSync(skillPath, 'utf8'));
|
|
303
|
+
skills.push({
|
|
304
|
+
id: entry.name,
|
|
305
|
+
name: frontmatter.name || entry.name,
|
|
306
|
+
...(frontmatter.description ? { description: frontmatter.description } : {}),
|
|
307
|
+
// Skill.Info renamed its required file field `location` -> `path`
|
|
308
|
+
// in OpenCode v2.0.4 (upstream commit 199aabe9e2).
|
|
309
|
+
path: skillPath,
|
|
310
|
+
content,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
await ctx.skill.transform((draft) => {
|
|
315
|
+
// draft.add() decodes against the host's Skill.Info schema and throws
|
|
316
|
+
// synchronously on a mismatch. A throw escaping this callback is what
|
|
317
|
+
// the host escalates into an asynchronous hard-disable of the entire
|
|
318
|
+
// plugin ("Plugin disabled after skill.transform failed") — the
|
|
319
|
+
// try/catch around ctx.skill.transform never sees it, and the
|
|
320
|
+
// bootstrap hook is torn down as collateral. Contain failures per
|
|
321
|
+
// skill so one rejected payload skips that skill instead of killing
|
|
322
|
+
// skills AND bootstrap.
|
|
323
|
+
for (const skill of skills) {
|
|
324
|
+
try {
|
|
325
|
+
draft.add(skill);
|
|
326
|
+
} catch (err) {
|
|
327
|
+
console.error(`[superpowers] skill "${skill.id}" rejected by host, skipping:`, err);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
} catch (err) {
|
|
332
|
+
// Never break plugin activation: one failing plugin takes down the whole
|
|
333
|
+
// V2 generation (including provider/catalog plugins => no models in TUI).
|
|
334
|
+
console.error('[superpowers] skill registration failed:', err);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 2. Inject bootstrap into first user message via V2 session context hook
|
|
338
|
+
try {
|
|
339
|
+
await ctx.session.hook('context', async (event) => {
|
|
340
|
+
try {
|
|
341
|
+
const bootstrap = getBootstrapContent(V2_MAPPING);
|
|
342
|
+
if (!bootstrap || !event.messages || !event.messages.length) return;
|
|
343
|
+
const firstUser = event.messages.find(m => m.role === 'user');
|
|
344
|
+
if (firstUser && (!firstUser.content || !firstUser.content.length)) return;
|
|
345
|
+
if (firstUser?.content.some(p => p.type === 'text' && p.text && p.text.includes('EXTREMELY_IMPORTANT'))) return;
|
|
346
|
+
|
|
347
|
+
// #2160: the context event carries the sessionID directly. Skip the
|
|
348
|
+
// controller bootstrap when this prompt belongs to a task subagent
|
|
349
|
+
// (child) session. Skills registered above stay available to workers.
|
|
350
|
+
if (typeof ctx.session.get === 'function' && await isChildSession(
|
|
351
|
+
(id) => ctx.session.get({ sessionID: id }),
|
|
352
|
+
event.sessionID,
|
|
353
|
+
)) return;
|
|
354
|
+
|
|
355
|
+
// Native compaction can leave only an opaque checkpoint. Keep it
|
|
356
|
+
// intact and append the transient bootstrap as a user message.
|
|
357
|
+
if (firstUser) {
|
|
358
|
+
firstUser.content.unshift({ type: 'text', text: bootstrap });
|
|
359
|
+
} else {
|
|
360
|
+
event.messages.push({ role: 'user', content: [{ type: 'text', text: bootstrap }] });
|
|
361
|
+
}
|
|
362
|
+
} catch (err) {
|
|
363
|
+
// Never let hook callback errors break the request pipeline.
|
|
364
|
+
console.error('[superpowers] context hook failed:', err);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.error('[superpowers] session hook registration failed:', err);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Default Export: { id, server, setup }
|
|
374
|
+
*
|
|
375
|
+
* V2 PluginSupervisor reads { id, setup }.
|
|
376
|
+
* V1 reads named export SuperpowersPlugin.
|
|
377
|
+
* server() is exported for V1 compatibility.
|
|
378
|
+
*/
|
|
379
|
+
export default {
|
|
380
|
+
id: 'superpowers',
|
|
381
|
+
server: SuperpowersPlugin,
|
|
382
|
+
setup,
|
|
383
|
+
};
|
package/GEMINI.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Superpowers-ZH 中文增强版
|
|
6
6
|
|
|
7
|
-
本项目已安装 superpowers-zh 技能框架(
|
|
7
|
+
本项目已安装 superpowers-zh 技能框架(21 个 skills)。
|
|
8
8
|
|
|
9
9
|
## 核心规则
|
|
10
10
|
|
|
@@ -22,8 +22,9 @@ Skills 位于 `.gemini/skills/` 目录,每个 skill 有独立的 `SKILL.md`
|
|
|
22
22
|
- **chinese-commit-conventions**: 中文 commit 与 changelog 配置参考——Conventional Commits 中文适配、commitlint/husky/commitizen 中文模板、conventional-changelog 中文配置。仅在用户显式 /chinese-commit-conventions 时调用,不要根据上下文自动触发。
|
|
23
23
|
- **chinese-documentation**: 中文文档排版参考——中英文空格、全半角标点、术语保留、链接格式、中文文案排版指北约定。仅在用户显式 /chinese-documentation 时调用,不要根据上下文自动触发。
|
|
24
24
|
- **chinese-git-workflow**: 国内 Git 平台配置参考——Gitee、Coding.net、极狐 GitLab、CNB 的 SSH/HTTPS/凭据/CI 接入差异与镜像同步配置。仅在用户显式 /chinese-git-workflow 时调用,不要根据上下文自动触发。
|
|
25
|
+
- **diagnosing-superpowers**: 当一次 superpowers 会话出了问题、你的人类伙伴想知道原因时使用——重复劳动、无视计划、磕磕绊绊、结果质量差、某个技能没触发、"太慢了"、"为什么这么贵"、"它到底在干什么"——或者想给 superpowers 维护者整理一份 bug 报告;适用于当前会话,或按 id / 路径指定的过往会话,任何工具均可
|
|
25
26
|
- **dispatching-parallel-agents**: 当面对 2 个以上可以独立进行、无共享状态或顺序依赖的任务时使用
|
|
26
|
-
- **executing-plans**:
|
|
27
|
+
- **executing-plans**: 当你要在当前会话里亲自担任实现者执行一份实现计划时使用——你的人类伙伴选择了内联执行,或者没有可用的子智能体工具
|
|
27
28
|
- **finishing-a-development-branch**: 当实现完成、所有测试通过、需要决定如何集成工作时使用——通过提供合并、PR 或清理等结构化选项来引导开发工作的收尾
|
|
28
29
|
- **mcp-builder**: MCP 服务器构建方法论 — 系统化构建生产级 MCP 工具,让 AI 助手连接外部能力
|
|
29
30
|
- **receiving-code-review**: 收到代码审查反馈后、实施建议之前使用,尤其当反馈不明确或技术上有疑问时——需要技术严谨性和验证,而非敷衍附和或盲目执行
|