kilo-superpowers-compose 0.1.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 +21 -0
- package/NOTICE +49 -0
- package/README.md +150 -0
- package/agents/compose-dev.md +51 -0
- package/agents/compose-review.md +76 -0
- package/agents/compose.md +107 -0
- package/bin/cli.js +69 -0
- package/bin/install.js +11 -0
- package/bin/lib.js +484 -0
- package/bin/uninstall.js +11 -0
- package/bin/update.js +11 -0
- package/commands/superpowers.md +38 -0
- package/package.json +45 -0
- package/skills/brainstorming/SKILL.md +159 -0
- package/skills/brainstorming/scripts/frame-template.html +213 -0
- package/skills/brainstorming/scripts/helper.js +167 -0
- package/skills/brainstorming/scripts/server.cjs +723 -0
- package/skills/brainstorming/scripts/start-server.sh +209 -0
- package/skills/brainstorming/scripts/stop-server.sh +120 -0
- package/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/skills/brainstorming/visual-companion.md +291 -0
- package/skills/dispatching-parallel-agents/SKILL.md +185 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +241 -0
- package/skills/receiving-code-review/SKILL.md +213 -0
- package/skills/requesting-code-review/SKILL.md +103 -0
- package/skills/requesting-code-review/code-reviewer.md +172 -0
- package/skills/subagent-driven-development/SKILL.md +418 -0
- package/skills/subagent-driven-development/implementer-prompt.md +139 -0
- package/skills/subagent-driven-development/scripts/review-package +44 -0
- package/skills/subagent-driven-development/scripts/sdd-workspace +22 -0
- package/skills/subagent-driven-development/scripts/task-brief +40 -0
- package/skills/subagent-driven-development/task-reviewer-prompt.md +188 -0
- package/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/test-driven-development/SKILL.md +371 -0
- package/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/using-git-worktrees/SKILL.md +202 -0
- package/skills/using-superpowers/SKILL.md +62 -0
- package/skills/using-superpowers/references/antigravity-tools.md +23 -0
- package/skills/using-superpowers/references/codex-tools.md +39 -0
- package/skills/using-superpowers/references/pi-tools.md +16 -0
- package/skills/verification-before-completion/SKILL.md +139 -0
- package/skills/writing-plans/SKILL.md +174 -0
- package/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/skills/writing-skills/SKILL.md +689 -0
- package/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/writing-skills/persuasion-principles.md +187 -0
- package/skills/writing-skills/render-graphs.js +168 -0
- package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
package/bin/lib.js
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
// bin/lib.js
|
|
2
|
+
// kilo-superpowers-compose — 安装器共享逻辑(纯函数 + fs 辅助)。
|
|
3
|
+
// 零第三方依赖;仅使用 node:fs / node:path / node:os / node:url。
|
|
4
|
+
//
|
|
5
|
+
// 设计说明:
|
|
6
|
+
// - runInstall / runUninstall / runUpdate **返回退出码**(0–4),不调用 process.exit,
|
|
7
|
+
// 以便在 node:test 中直接断言。进程退出由各入口垫片(install.js / cli.js)负责。
|
|
8
|
+
// - 跨平台:所有路径用 path.join + os.homedir()/KILO_HOME,绝不手拼反斜杠。
|
|
9
|
+
// - skills.paths 幂等判定用规范化路径比较(path.resolve + toLowerCase)。
|
|
10
|
+
// - 卸载归属判定用清单法(manifest),替代内容嗅探。
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
// ─── 常量 ─────────────────────────────────────────────────────────────
|
|
18
|
+
// 退出码(INSTALLER.md §2.3)
|
|
19
|
+
export const EXIT = Object.freeze({
|
|
20
|
+
OK: 0,
|
|
21
|
+
ERROR: 1,
|
|
22
|
+
PARSE_ERROR: 2, // kilo.jsonc 解析失败(已从备份恢复)
|
|
23
|
+
NOT_WRITABLE: 3, // 目标目录不可写
|
|
24
|
+
LINK_FAILED: 4, // junction/symlink 创建失败(且递归复制兜底也失败)
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 内嵌技能锁定的上游 tag(见 NOTICE)。重新 vendor 时同步更新。
|
|
28
|
+
export const UPSTREAM_TAG = 'v6.1.1';
|
|
29
|
+
|
|
30
|
+
// 清单文件名(位于配置目录下,隐藏文件)
|
|
31
|
+
export const MANIFEST_NAME = '.kilo-superpowers-compose.json';
|
|
32
|
+
|
|
33
|
+
const noop = () => {};
|
|
34
|
+
|
|
35
|
+
// ─── 纯函数 / 配置解析 ─────────────────────────────────────────────────
|
|
36
|
+
// 读取环境变量(默认 process.env),便于测试注入。
|
|
37
|
+
export function readEnv(env = process.env) {
|
|
38
|
+
return {
|
|
39
|
+
HOME: env.KILO_HOME || os.homedir(),
|
|
40
|
+
DRY_RUN: env.KILO_SUPERPOWERS_DRY_RUN === '1',
|
|
41
|
+
VERBOSE: env.KILO_SUPERPOWERS_VERBOSE === '1',
|
|
42
|
+
USE_PREFIX: env.KILO_SUPERPOWERS_PREFIX === '1',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 由 home 推导所有目标路径(纯函数)。
|
|
47
|
+
export function resolvePaths(home) {
|
|
48
|
+
const configDir = path.join(home, '.config', 'kilo');
|
|
49
|
+
const skillsDir = path.join(home, '.kilo', 'skills');
|
|
50
|
+
return {
|
|
51
|
+
home,
|
|
52
|
+
configDir,
|
|
53
|
+
configFile: path.join(configDir, 'kilo.jsonc'),
|
|
54
|
+
agentsDir: path.join(configDir, 'agent'),
|
|
55
|
+
commandsDir: path.join(configDir, 'commands'),
|
|
56
|
+
skillsDir,
|
|
57
|
+
skillLink: path.join(skillsDir, 'superpowers'),
|
|
58
|
+
manifestFile: path.join(configDir, MANIFEST_NAME),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 组装完整运行上下文。
|
|
63
|
+
export function buildContext(env = process.env) {
|
|
64
|
+
return { ...readEnv(env), ...resolvePaths(readEnv(env).HOME) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// JSONC 行注释剥离:仅处理 `//`,且只在字符串外剥离。
|
|
68
|
+
// **不处理块注释 `/* *`/`/*`**——若 kilo.jsonc 含块注释导致解析失败,
|
|
69
|
+
// 调用方从备份恢复并以退出码 2 退出(INSTALLER.md §2.2 第 7 步 / 计划取舍)。
|
|
70
|
+
export function stripLineComments(raw) {
|
|
71
|
+
return raw.split('\n').map(stripLine).join('\n');
|
|
72
|
+
function stripLine(line) {
|
|
73
|
+
const idx = line.indexOf('//');
|
|
74
|
+
if (idx === -1) return line;
|
|
75
|
+
const before = line.slice(0, idx);
|
|
76
|
+
// 统计 `before` 中未转义双引号的数量;偶数表示不在字符串内。
|
|
77
|
+
const quotes = (before.match(/(?<!\\)"/g) || []).length;
|
|
78
|
+
return quotes % 2 === 0 ? before : line;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 规范化路径用于幂等比较(消除分隔符 / 大小写差异)。
|
|
83
|
+
export function normalizePath(p) {
|
|
84
|
+
return path.resolve(p).toLowerCase();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// skills.paths 数组是否已包含目标(规范化比较)。
|
|
88
|
+
export function skillsPathsContains(paths, target) {
|
|
89
|
+
const t = normalizePath(target);
|
|
90
|
+
return Array.isArray(paths) && paths.some((p) => normalizePath(p) === t);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 从给定目录定位包根:bin/ 的上一级。
|
|
94
|
+
export function findPackageRoot(startDir) {
|
|
95
|
+
return path.resolve(startDir, '..');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function readPackageJson(pkgRoot) {
|
|
99
|
+
const file = path.join(pkgRoot, 'package.json');
|
|
100
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ─── 日志 / 退出辅助 ───────────────────────────────────────────────────
|
|
104
|
+
export function makeLogger(verbose) {
|
|
105
|
+
const log = verbose
|
|
106
|
+
? (...a) => console.error('[kilo-sp-compose]', ...a)
|
|
107
|
+
: noop;
|
|
108
|
+
return log;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── fs 辅助 ──────────────────────────────────────────────────────────
|
|
112
|
+
export function ensureDir(d, { dryRun = false, log = noop } = {}) {
|
|
113
|
+
if (dryRun) {
|
|
114
|
+
log('would mkdir', d);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
fs.mkdirSync(d, { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function linkExists(p) {
|
|
121
|
+
return !!fs.lstatSync(p, { throwIfNoEntry: false });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 安全移除一个路径:符号链接/junction 用 unlinkSync(绝不深入目标);
|
|
125
|
+
// 真实目录用 rmSync 递归;普通文件用 rmSync。
|
|
126
|
+
export function safeRemove(p, { dryRun = false, log = noop } = {}) {
|
|
127
|
+
const st = fs.lstatSync(p, { throwIfNoEntry: false });
|
|
128
|
+
if (!st) return false;
|
|
129
|
+
log('removing', p);
|
|
130
|
+
if (dryRun) return true;
|
|
131
|
+
if (st.isSymbolicLink()) {
|
|
132
|
+
fs.unlinkSync(p); // junction/symlink:仅删除链接本身
|
|
133
|
+
} else if (st.isDirectory()) {
|
|
134
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
135
|
+
} else {
|
|
136
|
+
fs.rmSync(p, { force: true });
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 读取 JSONC(剥离行注释后解析)。失败返回 { __parseError, __raw }。
|
|
142
|
+
export function readJsonc(file) {
|
|
143
|
+
if (!fs.existsSync(file)) return {};
|
|
144
|
+
const raw = fs.readFileSync(file, 'utf8');
|
|
145
|
+
const stripped = stripLineComments(raw);
|
|
146
|
+
try {
|
|
147
|
+
return JSON.parse(stripped);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
return { __parseError: e.message, __raw: raw };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 以 JSON 写回(丢失注释——已知取舍)。
|
|
154
|
+
export function writeJson(file, obj, { dryRun = false } = {}) {
|
|
155
|
+
if (dryRun) return;
|
|
156
|
+
fs.writeFileSync(file, JSON.stringify(obj, null, 2) + '\n', 'utf8');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 备份 kilo.jsonc -> kilo.jsonc.bak.<timestamp>;不存在则返回 null。
|
|
160
|
+
export function backupConfig(configFile, { dryRun = false, log = noop } = {}) {
|
|
161
|
+
if (!fs.existsSync(configFile)) return null;
|
|
162
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
163
|
+
const bak = `${configFile}.bak.${ts}`;
|
|
164
|
+
log('backup', configFile, '->', bak);
|
|
165
|
+
if (dryRun) return bak;
|
|
166
|
+
fs.copyFileSync(configFile, bak);
|
|
167
|
+
return bak;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 清单读写 ─────────────────────────────────────────────────────────────
|
|
171
|
+
export function readManifest(manifestFile) {
|
|
172
|
+
if (!fs.existsSync(manifestFile)) return null;
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function writeManifest(manifestFile, data, { dryRun = false, log = noop } = {}) {
|
|
181
|
+
log('writing manifest', manifestFile);
|
|
182
|
+
if (dryRun) return;
|
|
183
|
+
fs.writeFileSync(manifestFile, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function removeManifest(manifestFile, { dryRun = false, log = noop } = {}) {
|
|
187
|
+
return safeRemove(manifestFile, { dryRun, log });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 创建技能链接(Windows junction / Unix symlink),失败则递归复制兜底。
|
|
191
|
+
// 返回 { type, ok, error?, fallback? }。
|
|
192
|
+
export function makeSkillsLink(src, link, { dryRun = false, log = noop } = {}) {
|
|
193
|
+
if (linkExists(link)) {
|
|
194
|
+
safeRemove(link, { dryRun, log });
|
|
195
|
+
}
|
|
196
|
+
const isWin = process.platform === 'win32';
|
|
197
|
+
const linkType = isWin ? 'junction' : 'symlink';
|
|
198
|
+
log(`creating ${linkType}`, src, '->', link);
|
|
199
|
+
if (dryRun) return { type: linkType, ok: true };
|
|
200
|
+
try {
|
|
201
|
+
fs.symlinkSync(src, link, isWin ? 'junction' : 'dir');
|
|
202
|
+
return { type: linkType, ok: true };
|
|
203
|
+
} catch (e) {
|
|
204
|
+
log(`${linkType} failed (${e.message}); falling back to recursive copy`);
|
|
205
|
+
try {
|
|
206
|
+
fs.cpSync(src, link, { recursive: true, force: true });
|
|
207
|
+
return { type: 'copy', ok: true, fallback: e.message };
|
|
208
|
+
} catch (e2) {
|
|
209
|
+
return {
|
|
210
|
+
type: linkType,
|
|
211
|
+
ok: false,
|
|
212
|
+
error: `${linkType}: ${e.message}; copy fallback: ${e2.message}`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 列出源目录下的 .md 文件(agents / commands)。
|
|
219
|
+
export function listMdFiles(dir) {
|
|
220
|
+
if (!fs.existsSync(dir)) return [];
|
|
221
|
+
return fs
|
|
222
|
+
.readdirSync(dir)
|
|
223
|
+
.filter((f) => f.endsWith('.md'))
|
|
224
|
+
.map((f) => ({ name: f, abs: path.join(dir, f) }));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ─── 编排:安装 ───────────────────────────────────────────────────────
|
|
228
|
+
// 返回退出码(0 成功)。失败路径返回对应码并已做清理(如恢复备份)。
|
|
229
|
+
export function runInstall(opts = {}) {
|
|
230
|
+
const ctx = opts.context || buildContext();
|
|
231
|
+
const log = makeLogger(ctx.VERBOSE);
|
|
232
|
+
const dryRun = ctx.DRY_RUN;
|
|
233
|
+
|
|
234
|
+
// bin/lib.js 的父目录即包根。
|
|
235
|
+
const binDir = path.dirname(fileURLToPath(import.meta.url));
|
|
236
|
+
const root = opts.pkgRoot || path.resolve(binDir, '..');
|
|
237
|
+
|
|
238
|
+
const srcSkills = path.join(root, 'skills');
|
|
239
|
+
const srcAgents = path.join(root, 'agents');
|
|
240
|
+
const srcCommands = path.join(root, 'commands');
|
|
241
|
+
|
|
242
|
+
let pkg;
|
|
243
|
+
try {
|
|
244
|
+
pkg = readPackageJson(root);
|
|
245
|
+
} catch (e) {
|
|
246
|
+
console.error(`✗ 无法读取 package.json: ${e.message}`);
|
|
247
|
+
return EXIT.ERROR;
|
|
248
|
+
}
|
|
249
|
+
const version = pkg.version || '0.0.0';
|
|
250
|
+
|
|
251
|
+
log('package root', root);
|
|
252
|
+
log('config dir', ctx.configDir);
|
|
253
|
+
|
|
254
|
+
// 1. 确保目标目录
|
|
255
|
+
for (const d of [ctx.configDir, ctx.agentsDir, ctx.commandsDir, ctx.skillsDir]) {
|
|
256
|
+
try {
|
|
257
|
+
ensureDir(d, { dryRun, log });
|
|
258
|
+
} catch (e) {
|
|
259
|
+
console.error(`✗ 无法创建目录 ${d}: ${e.message}`);
|
|
260
|
+
return EXIT.NOT_WRITABLE;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 2. 备份 kilo.jsonc
|
|
265
|
+
const bak = backupConfig(ctx.configFile, { dryRun, log });
|
|
266
|
+
|
|
267
|
+
// 3. 预读并校验 kilo.jsonc(解析失败则立即恢复备份并退出 2,不做任何文件写入)。
|
|
268
|
+
// 仅支持 // 行注释;块注释 /* */ 不处理,会导致解析失败。
|
|
269
|
+
log('reading', ctx.configFile);
|
|
270
|
+
const config = readJsonc(ctx.configFile);
|
|
271
|
+
if (config.__parseError) {
|
|
272
|
+
if (bak && !dryRun) {
|
|
273
|
+
try {
|
|
274
|
+
fs.copyFileSync(bak, ctx.configFile);
|
|
275
|
+
} catch (e) {
|
|
276
|
+
log('恢复备份失败:', e.message);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
console.error(`✗ kilo.jsonc 解析失败: ${config.__parseError}(已从备份恢复)`);
|
|
280
|
+
console.error(' 注意:本安装器仅支持 // 行注释;块注释 /* */ 不被处理。');
|
|
281
|
+
return EXIT.PARSE_ERROR;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 4. 创建技能链接
|
|
285
|
+
if (!fs.existsSync(srcSkills)) {
|
|
286
|
+
console.error(`✗ 找不到技能源目录: ${srcSkills}`);
|
|
287
|
+
return EXIT.ERROR;
|
|
288
|
+
}
|
|
289
|
+
const linkRes = makeSkillsLink(srcSkills, ctx.skillLink, { dryRun, log });
|
|
290
|
+
if (!linkRes.ok) {
|
|
291
|
+
console.error(`✗ ${linkRes.error}`);
|
|
292
|
+
return EXIT.LINK_FAILED;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 5. 复制 agents
|
|
296
|
+
const agentFiles = listMdFiles(srcAgents);
|
|
297
|
+
const installedAgentPaths = [];
|
|
298
|
+
for (const f of agentFiles) {
|
|
299
|
+
const dst = path.join(ctx.agentsDir, f.name);
|
|
300
|
+
log('copy agent', f.abs, '->', dst);
|
|
301
|
+
if (!dryRun) {
|
|
302
|
+
try {
|
|
303
|
+
fs.mkdirSync(ctx.agentsDir, { recursive: true });
|
|
304
|
+
fs.copyFileSync(f.abs, dst);
|
|
305
|
+
} catch (e) {
|
|
306
|
+
console.error(`✗ 复制 agent 失败 ${f.name}: ${e.message}`);
|
|
307
|
+
return EXIT.NOT_WRITABLE;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
installedAgentPaths.push(dst);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 6. 复制 commands
|
|
314
|
+
const cmdFiles = listMdFiles(srcCommands);
|
|
315
|
+
const installedCommandPaths = [];
|
|
316
|
+
for (const f of cmdFiles) {
|
|
317
|
+
const dst = path.join(ctx.commandsDir, f.name);
|
|
318
|
+
log('copy command', f.abs, '->', dst);
|
|
319
|
+
if (!dryRun) {
|
|
320
|
+
try {
|
|
321
|
+
fs.mkdirSync(ctx.commandsDir, { recursive: true });
|
|
322
|
+
fs.copyFileSync(f.abs, dst);
|
|
323
|
+
} catch (e) {
|
|
324
|
+
console.error(`✗ 复制 command 失败 ${f.name}: ${e.message}`);
|
|
325
|
+
return EXIT.NOT_WRITABLE;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
installedCommandPaths.push(dst);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 7. 追加 skills.paths(config 已在第 3 步解析校验通过)
|
|
332
|
+
config.skills = config.skills || {};
|
|
333
|
+
config.skills.paths = config.skills.paths || [];
|
|
334
|
+
let added = false;
|
|
335
|
+
if (!skillsPathsContains(config.skills.paths, srcSkills)) {
|
|
336
|
+
config.skills.paths.push(srcSkills);
|
|
337
|
+
added = true;
|
|
338
|
+
}
|
|
339
|
+
if (added && !dryRun) writeJson(ctx.configFile, config);
|
|
340
|
+
|
|
341
|
+
// 7. 写清单
|
|
342
|
+
writeManifest(
|
|
343
|
+
ctx.manifestFile,
|
|
344
|
+
{
|
|
345
|
+
name: 'kilo-superpowers-compose',
|
|
346
|
+
version,
|
|
347
|
+
upstreamTag: UPSTREAM_TAG,
|
|
348
|
+
pkgRoot: root,
|
|
349
|
+
skillsSrc: srcSkills,
|
|
350
|
+
skillsLink: ctx.skillLink,
|
|
351
|
+
skillsLinkType: linkRes.type,
|
|
352
|
+
skillsPathsEntry: srcSkills,
|
|
353
|
+
agents: installedAgentPaths,
|
|
354
|
+
commands: installedCommandPaths,
|
|
355
|
+
},
|
|
356
|
+
{ dryRun, log }
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// 8. 汇总
|
|
360
|
+
const skillCount = countSkills(srcSkills);
|
|
361
|
+
console.log('✓ kilo-superpowers-compose 已安装');
|
|
362
|
+
console.log(` 技能: ${skillCount} 个 -> ${ctx.skillLink} (${linkRes.type})`);
|
|
363
|
+
if (linkRes.fallback) {
|
|
364
|
+
console.log(` 注意: 链接创建失败,已回退为递归复制(${linkRes.fallback})`);
|
|
365
|
+
}
|
|
366
|
+
console.log(` 代理: ${agentFiles.map((f) => f.name).join(', ') || '(无)'}`);
|
|
367
|
+
console.log(` 命令: /${cmdFiles.map((f) => f.name.replace(/\.md$/, '')).join(', /') || '(无)'}`);
|
|
368
|
+
console.log(` kilo.jsonc: skills.paths ${added ? '已新增条目' : '已存在(幂等跳过)'}`);
|
|
369
|
+
console.log('');
|
|
370
|
+
console.log(' 请重启 Kilo CLI / VS Code 扩展以加载。');
|
|
371
|
+
return EXIT.OK;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function countSkills(srcSkills) {
|
|
375
|
+
try {
|
|
376
|
+
return fs
|
|
377
|
+
.readdirSync(srcSkills, { withFileTypes: true })
|
|
378
|
+
.filter((d) => d.isDirectory())
|
|
379
|
+
.length;
|
|
380
|
+
} catch {
|
|
381
|
+
return 0;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ─── 编排:卸载 ───────────────────────────────────────────────────────
|
|
386
|
+
export function runUninstall(opts = {}) {
|
|
387
|
+
const ctx = opts.context || buildContext();
|
|
388
|
+
const log = makeLogger(ctx.VERBOSE);
|
|
389
|
+
const dryRun = ctx.DRY_RUN;
|
|
390
|
+
|
|
391
|
+
log('uninstall from', ctx.configDir);
|
|
392
|
+
|
|
393
|
+
// 备份 kilo.jsonc(卸载也会改它)
|
|
394
|
+
const bak = backupConfig(ctx.configFile, { dryRun, log });
|
|
395
|
+
|
|
396
|
+
const manifest = readManifest(ctx.manifestFile);
|
|
397
|
+
|
|
398
|
+
// 1. 移除 agents
|
|
399
|
+
const agentPaths = (manifest && manifest.agents) || [];
|
|
400
|
+
if (agentPaths.length === 0) {
|
|
401
|
+
// 清单缺失时回退到已知文件名
|
|
402
|
+
log('清单缺失或无 agents 记录,回退到已知文件名');
|
|
403
|
+
for (const n of ['compose.md', 'compose-dev.md', 'compose-review.md']) {
|
|
404
|
+
agentPaths.push(path.join(ctx.agentsDir, n));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
for (const p of agentPaths) safeRemove(p, { dryRun, log });
|
|
408
|
+
|
|
409
|
+
// 2. 移除 commands
|
|
410
|
+
const cmdPaths = (manifest && manifest.commands) || [];
|
|
411
|
+
if (cmdPaths.length === 0) {
|
|
412
|
+
for (const n of ['superpowers.md']) {
|
|
413
|
+
cmdPaths.push(path.join(ctx.commandsDir, n));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
for (const p of cmdPaths) safeRemove(p, { dryRun, log });
|
|
417
|
+
|
|
418
|
+
// 3. 移除技能链接(清单有记录类型则用之,否则按链接处理)
|
|
419
|
+
if (linkExists(ctx.skillLink)) {
|
|
420
|
+
safeRemove(ctx.skillLink, { dryRun, log });
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// 4. 从 kilo.jsonc 移除本包的 skills.paths 条目
|
|
424
|
+
const config = readJsonc(ctx.configFile);
|
|
425
|
+
if (config.__parseError) {
|
|
426
|
+
if (bak && !dryRun) {
|
|
427
|
+
try {
|
|
428
|
+
fs.copyFileSync(bak, ctx.configFile);
|
|
429
|
+
} catch (e) {
|
|
430
|
+
log('恢复备份失败:', e.message);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
console.error(`✗ kilo.jsonc 解析失败: ${config.__parseError}(已从备份恢复)`);
|
|
434
|
+
return EXIT.PARSE_ERROR;
|
|
435
|
+
}
|
|
436
|
+
let removedEntries = 0;
|
|
437
|
+
if (config.skills && Array.isArray(config.skills.paths)) {
|
|
438
|
+
const entry = (manifest && manifest.skillsPathsEntry) || null;
|
|
439
|
+
const before = config.skills.paths.length;
|
|
440
|
+
config.skills.paths = config.skills.paths.filter((p) => {
|
|
441
|
+
if (entry && normalizePath(p) === normalizePath(entry)) return false;
|
|
442
|
+
// 也移除任何指向本包 skills 源的条目(容错)
|
|
443
|
+
return true;
|
|
444
|
+
});
|
|
445
|
+
// 若清单缺 entry,按已知包根启发式移除(路径片段含 kilo-superpowers-compose/skills)
|
|
446
|
+
if (!entry) {
|
|
447
|
+
config.skills.paths = config.skills.paths.filter(
|
|
448
|
+
(p) => !/kilo-superpowers-compose[\\/]+skills/i.test(p)
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
removedEntries = before - config.skills.paths.length;
|
|
452
|
+
if (!dryRun) writeJson(ctx.configFile, config);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// 5. 移除清单
|
|
456
|
+
removeManifest(ctx.manifestFile, { dryRun, log });
|
|
457
|
+
|
|
458
|
+
console.log('✓ kilo-superpowers-compose 已卸载');
|
|
459
|
+
console.log(` agents: ${agentPaths.length} 个文件`);
|
|
460
|
+
console.log(` commands: ${cmdPaths.length} 个文件`);
|
|
461
|
+
console.log(` 技能链接: ${ctx.skillLink}`);
|
|
462
|
+
console.log(` kilo.jsonc: skills.paths 移除 ${removedEntries} 条`);
|
|
463
|
+
console.log(' 未触碰用户自有的技能 / 代理 / 配置。');
|
|
464
|
+
return EXIT.OK;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ─── 编排:更新 ───────────────────────────────────────────────────────
|
|
468
|
+
// 重新运行安装(幂等)。若检测到版本变化则打印提示。
|
|
469
|
+
export function runUpdate(opts = {}) {
|
|
470
|
+
const ctx = opts.context || buildContext();
|
|
471
|
+
const prev = readManifest(ctx.manifestFile);
|
|
472
|
+
const result = runInstall(opts);
|
|
473
|
+
if (result === EXIT.OK && prev && prev.version) {
|
|
474
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
475
|
+
let cur = prev.version;
|
|
476
|
+
try {
|
|
477
|
+
cur = readPackageJson(root).version || prev.version;
|
|
478
|
+
} catch {}
|
|
479
|
+
if (prev.version !== cur) {
|
|
480
|
+
console.log(` 更新: kilo-superpowers-compose ${prev.version} -> ${cur}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return result;
|
|
484
|
+
}
|
package/bin/uninstall.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/uninstall.js — 卸载入口(清单法精确移除)。
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { runUninstall } from './lib.js';
|
|
6
|
+
|
|
7
|
+
const invokedDirectly =
|
|
8
|
+
process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
9
|
+
if (invokedDirectly) {
|
|
10
|
+
process.exit(runUninstall());
|
|
11
|
+
}
|
package/bin/update.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/update.js — 更新入口(重新运行安装,幂等)。
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { runUpdate } from './lib.js';
|
|
6
|
+
|
|
7
|
+
const invokedDirectly =
|
|
8
|
+
process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
9
|
+
if (invokedDirectly) {
|
|
10
|
+
process.exit(runUpdate());
|
|
11
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: |
|
|
3
|
+
Start the Superpowers workflow. Loads the using-superpowers discipline
|
|
4
|
+
rules, then hands the user's request to the compose orchestrator agent,
|
|
5
|
+
which classifies the task (simple / complex / bug) and drives the full
|
|
6
|
+
brainstorming → planning → subagent implementation → two-stage review
|
|
7
|
+
→ merge pipeline.
|
|
8
|
+
agent: compose
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# /superpowers
|
|
12
|
+
|
|
13
|
+
You are being launched via the `/superpowers` slash command. Follow these
|
|
14
|
+
steps in order:
|
|
15
|
+
|
|
16
|
+
1. **Bootstrap discipline (mandatory first step).** Load the
|
|
17
|
+
`using-superpowers` skill before doing anything else. This installs the
|
|
18
|
+
core rules that govern all subsequent work in this session.
|
|
19
|
+
|
|
20
|
+
2. **Hand off to the compose workflow.** Treat the user's request (the
|
|
21
|
+
text they supplied after `/superpowers`, or the most recent message if
|
|
22
|
+
none) as the task to process. Run the compose orchestrator's workflow
|
|
23
|
+
decision tree:
|
|
24
|
+
- Classify the task: **simple**, **complex**, or **bug fix**.
|
|
25
|
+
- For complex work: brainstorm → plan → get explicit user approval →
|
|
26
|
+
dispatch `compose-dev` per task → dispatch `compose-review` →
|
|
27
|
+
finish the branch.
|
|
28
|
+
- For bugs: `systematic-debugging` first, then a failing test, then
|
|
29
|
+
the simple or complex path based on fix size.
|
|
30
|
+
- For simple work: do it directly, then invoke
|
|
31
|
+
`verification-before-completion` before reporting done.
|
|
32
|
+
|
|
33
|
+
3. **Never skip the gates.** Brainstorming for new features, TDD for any
|
|
34
|
+
logic, systematic debugging for bugs, and review before merge on
|
|
35
|
+
complex work are mandatory — not suggestions.
|
|
36
|
+
|
|
37
|
+
Begin by loading `using-superpowers`, then emit the task classification
|
|
38
|
+
checkpoint so the user can see your reasoning.
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kilo-superpowers-compose",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "将 obra/superpowers 工作流打包为 Kilo 插件:一个 compose 编排器代理、/superpowers 斜杠命令与 14 个技能,一条命令安装。",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"kilo",
|
|
7
|
+
"kilo-code",
|
|
8
|
+
"kilo-cli",
|
|
9
|
+
"superpowers",
|
|
10
|
+
"compose",
|
|
11
|
+
"agent",
|
|
12
|
+
"subagent",
|
|
13
|
+
"skills",
|
|
14
|
+
"tdd",
|
|
15
|
+
"workflow",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "ChengZiiii",
|
|
20
|
+
"homepage": "https://github.com/ChengZiiii/kilo-superpowers-compose#readme",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/ChengZiiii/kilo-superpowers-compose.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/ChengZiiii/kilo-superpowers-compose/issues"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"bin": {
|
|
33
|
+
"kilo-superpowers-compose": "bin/cli.js"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"bin",
|
|
37
|
+
"skills",
|
|
38
|
+
"agents",
|
|
39
|
+
"commands",
|
|
40
|
+
"NOTICE"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"test": "node --test"
|
|
44
|
+
}
|
|
45
|
+
}
|