@wenaixi/dsh-superpower 7.0.0 → 7.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/README.md +5 -5
- package/lib/catalog.d.ts +98 -0
- package/lib/catalog.d.ts.map +1 -0
- package/lib/catalog.js +295 -0
- package/lib/catalog.js.map +1 -0
- package/lib/document.d.ts +61 -0
- package/lib/document.d.ts.map +1 -0
- package/lib/document.js +268 -0
- package/lib/document.js.map +1 -0
- package/lib/superpowers.d.ts +6 -0
- package/lib/superpowers.d.ts.map +1 -1
- package/lib/superpowers.js +27 -250
- package/lib/superpowers.js.map +1 -1
- package/package.json +2 -2
- package/skills/brainstorming/SKILL.md +1 -1
- package/skills/brainstorming/scripts/server.cjs +6 -14
- package/skills/brainstorming/scripts/start-server.sh +1 -6
- package/skills/brainstorming/spec-document-reviewer-prompt.md +1 -1
- package/skills/brainstorming/visual-companion.md +5 -31
- package/skills/diagnosing-superpowers/SKILL.md +1 -1
- package/skills/diagnosing-superpowers/prompts/request-conflicts.md +1 -1
- package/skills/diagnosing-superpowers/templates/issue.md +1 -1
- package/skills/dispatching-parallel-agents/SKILL.md +4 -4
- package/skills/executing-plans/SKILL.md +5 -5
- package/skills/finishing-a-development-branch/SKILL.md +5 -5
- package/skills/receiving-code-review/SKILL.md +1 -1
- package/skills/requesting-code-review/SKILL.md +1 -1
- package/skills/requesting-code-review/code-reviewer.md +1 -1
- package/skills/subagent-driven-development/SKILL.md +2 -2
- package/skills/subagent-driven-development/implementer-prompt.md +1 -1
- package/skills/subagent-driven-development/re-review-prompt.md +1 -1
- package/skills/subagent-driven-development/scripts/sdd-workspace +2 -2
- package/skills/subagent-driven-development/task-reviewer-prompt.md +1 -1
- package/skills/systematic-debugging/CREATION-LOG.md +2 -2
- package/skills/systematic-debugging/SKILL.md +2 -2
- package/skills/systematic-debugging/condition-based-waiting.md +3 -3
- package/skills/systematic-debugging/defense-in-depth.md +1 -1
- package/skills/systematic-debugging/root-cause-tracing.md +1 -1
- package/skills/systematic-debugging/test-pressure-3.md +1 -1
- package/skills/test-driven-development/SKILL.md +1 -1
- package/skills/test-driven-development/writing-good-tests.md +1 -1
- package/skills/using-git-worktrees/SKILL.md +1 -1
- package/skills/using-superpowers/SKILL.md +4 -10
- package/skills/using-superpowers/references/dsh-tools.md +3 -3
- package/skills/verification-before-completion/SKILL.md +1 -1
- package/skills/writing-plans/SKILL.md +1 -1
- package/skills/writing-skills/SKILL.md +6 -6
- package/skills/writing-skills/testing-skills-with-subagents.md +1 -1
- package/skills/using-superpowers/references/antigravity-tools.md +0 -23
- package/skills/using-superpowers/references/claude-code-tools.md +0 -11
- package/skills/using-superpowers/references/codex-tools.md +0 -108
- package/skills/using-superpowers/references/gemini-tools.md +0 -63
- package/skills/using-superpowers/references/hermes-tools.md +0 -56
- package/skills/using-superpowers/references/muse-tools.md +0 -35
- package/skills/using-superpowers/references/pi-tools.md +0 -16
- package/skills/writing-skills/anthropic-best-practices.md +0 -1146
- package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -188
package/lib/document.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wenaixi/dsh-superpower — SkillDocument 深度模块
|
|
3
|
+
*
|
|
4
|
+
* 封装单个技能文档的读取、BOM/CRLF 归一化、YAML frontmatter 解析、
|
|
5
|
+
* 规范字段与调用策略校验,以及向 SkillCandidate 和 SkillDefinition 的契约映射。
|
|
6
|
+
*
|
|
7
|
+
* 核心原则:
|
|
8
|
+
* - Depth: 将大量文件 I/O、边界扫描、YAML 解析与类型收敛封装在紧凑的小接口后;
|
|
9
|
+
* - Locality: 所有 frontmatter 规则与校验逻辑内聚于此;
|
|
10
|
+
* - Test Surface: 对外暴露规范接口,成为运行时与验证脚本的共同测试表面。
|
|
11
|
+
*/
|
|
12
|
+
import { readFile } from 'node:fs/promises';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
import { parse } from 'yaml';
|
|
15
|
+
import { isSkillName } from '@deepseek-ai/dsh-skill';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// 内部纯工具函数(封装在模块内部,不泄漏给外部调用方)
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
function stringField(data, key) {
|
|
20
|
+
const v = data[key];
|
|
21
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
22
|
+
}
|
|
23
|
+
function optionalString(data, key) {
|
|
24
|
+
const v = data[key];
|
|
25
|
+
return typeof v === 'string' && v.length > 0 ? { [key]: v } : {};
|
|
26
|
+
}
|
|
27
|
+
function frontmatterBoolean(data, key) {
|
|
28
|
+
if (!Object.hasOwn(data, key))
|
|
29
|
+
return undefined;
|
|
30
|
+
const v = data[key];
|
|
31
|
+
if (typeof v === 'boolean')
|
|
32
|
+
return v;
|
|
33
|
+
if (v === 1 || v === '1')
|
|
34
|
+
return true;
|
|
35
|
+
if (v === 0 || v === '0')
|
|
36
|
+
return false;
|
|
37
|
+
if (typeof v === 'string') {
|
|
38
|
+
switch (v.toLowerCase()) {
|
|
39
|
+
case 'true':
|
|
40
|
+
case 'yes':
|
|
41
|
+
case 'on':
|
|
42
|
+
return true;
|
|
43
|
+
case 'false':
|
|
44
|
+
case 'no':
|
|
45
|
+
case 'off':
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw new TypeError(`frontmatter field "${key}" must be a boolean`);
|
|
50
|
+
}
|
|
51
|
+
function rejectLegacyKey(data, legacy, canonical) {
|
|
52
|
+
if (Object.hasOwn(data, legacy)) {
|
|
53
|
+
throw new Error(`frontmatter field "${legacy}" is unsupported; use "${canonical}"`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function parseInvocationPolicy(data) {
|
|
57
|
+
rejectLegacyKey(data, 'disableModelInvocation', 'disable-model-invocation');
|
|
58
|
+
rejectLegacyKey(data, 'modelInvocable', 'disable-model-invocation');
|
|
59
|
+
rejectLegacyKey(data, 'userInvocable', 'user-invocable');
|
|
60
|
+
const disableModelInvocation = frontmatterBoolean(data, 'disable-model-invocation');
|
|
61
|
+
const userInvocable = frontmatterBoolean(data, 'user-invocable');
|
|
62
|
+
return {
|
|
63
|
+
modelInvocable: disableModelInvocation !== true,
|
|
64
|
+
userInvocable: userInvocable !== false,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function findClosingFrontmatter(raw, start) {
|
|
68
|
+
let lineStart = start;
|
|
69
|
+
while (lineStart <= raw.length) {
|
|
70
|
+
const nl = raw.indexOf('\n', lineStart);
|
|
71
|
+
const lineEnd = nl < 0 ? raw.length : nl;
|
|
72
|
+
if (raw.slice(lineStart, lineEnd).replace(/\r$/, '') === '---') {
|
|
73
|
+
return { start: lineStart, bodyStart: nl < 0 ? raw.length : nl + 1 };
|
|
74
|
+
}
|
|
75
|
+
if (nl < 0)
|
|
76
|
+
return undefined;
|
|
77
|
+
lineStart = nl + 1;
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
function extractFrontmatter(raw) {
|
|
82
|
+
// 去除 Windows 编辑器易带的 UTF-8 BOM 头
|
|
83
|
+
let content = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
|
|
84
|
+
const firstNl = content.indexOf('\n');
|
|
85
|
+
if (firstNl < 0 || content.slice(0, firstNl).replace(/\r$/, '') !== '---') {
|
|
86
|
+
throw new Error('missing opening frontmatter delimiter "---"');
|
|
87
|
+
}
|
|
88
|
+
const start = firstNl + 1;
|
|
89
|
+
const closing = findClosingFrontmatter(content, start);
|
|
90
|
+
if (!closing) {
|
|
91
|
+
throw new Error('missing closing frontmatter delimiter "---"');
|
|
92
|
+
}
|
|
93
|
+
const parsed = parse(content.slice(start, closing.start));
|
|
94
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
95
|
+
throw new Error('frontmatter must be a YAML object mapping');
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
data: parsed,
|
|
99
|
+
body: content.slice(closing.bodyStart),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// SkillDocument 深度模块
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
export class SkillDocument {
|
|
106
|
+
path;
|
|
107
|
+
name;
|
|
108
|
+
description;
|
|
109
|
+
whenToUse;
|
|
110
|
+
invocation;
|
|
111
|
+
metadata;
|
|
112
|
+
body;
|
|
113
|
+
rawFrontmatterData;
|
|
114
|
+
constructor(filePath, extracted) {
|
|
115
|
+
this.path = filePath;
|
|
116
|
+
this.rawFrontmatterData = extracted.data;
|
|
117
|
+
this.body = extracted.body;
|
|
118
|
+
const skillName = stringField(extracted.data, 'name');
|
|
119
|
+
if (!skillName) {
|
|
120
|
+
throw new Error(`skill at "${filePath}" is missing required frontmatter field "name"`);
|
|
121
|
+
}
|
|
122
|
+
if (!isSkillName(skillName)) {
|
|
123
|
+
throw new Error(`invalid skill name "${skillName}" at "${filePath}" (must be kebab-case)`);
|
|
124
|
+
}
|
|
125
|
+
this.name = skillName;
|
|
126
|
+
const desc = stringField(extracted.data, 'description');
|
|
127
|
+
if (!desc) {
|
|
128
|
+
throw new Error(`skill "${skillName}" at "${filePath}" is missing required frontmatter field "description"`);
|
|
129
|
+
}
|
|
130
|
+
this.description = desc;
|
|
131
|
+
this.whenToUse = stringField(extracted.data, 'whenToUse');
|
|
132
|
+
this.invocation = parseInvocationPolicy(extracted.data);
|
|
133
|
+
const meta = extracted.data['metadata'];
|
|
134
|
+
if (typeof meta === 'object' && meta !== null && !Array.isArray(meta)) {
|
|
135
|
+
this.metadata = meta;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 从指定文件路径异步解析 SkillDocument。
|
|
140
|
+
*/
|
|
141
|
+
static async fromFile(filePath, signal) {
|
|
142
|
+
signal?.throwIfAborted();
|
|
143
|
+
const raw = await readFile(filePath, {
|
|
144
|
+
encoding: 'utf8',
|
|
145
|
+
signal,
|
|
146
|
+
});
|
|
147
|
+
signal?.throwIfAborted();
|
|
148
|
+
const extracted = extractFrontmatter(raw);
|
|
149
|
+
return new SkillDocument(filePath, extracted);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 从内存中的原始 markdown 字符串直接解析(用于测试或动态构建)。
|
|
153
|
+
*/
|
|
154
|
+
static fromString(raw, filePath = 'SKILL.md') {
|
|
155
|
+
const extracted = extractFrontmatter(raw);
|
|
156
|
+
return new SkillDocument(filePath, extracted);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 执行 SkillDocument 核心边界契约规范自检(接口即测试表面)。
|
|
160
|
+
*/
|
|
161
|
+
static async selfTest() {
|
|
162
|
+
const results = [];
|
|
163
|
+
const runCheck = async (name, fn) => {
|
|
164
|
+
try {
|
|
165
|
+
await fn();
|
|
166
|
+
results.push({ name, ok: true });
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
results.push({ name, ok: false, error: err?.message ?? String(err) });
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
await runCheck('BOM 剥离', () => {
|
|
173
|
+
const d = SkillDocument.fromString('\uFEFF---\nname: bom-test\ndescription: d\n---\nbody');
|
|
174
|
+
if (d.name !== 'bom-test')
|
|
175
|
+
throw new Error('BOM 未剥离: ' + JSON.stringify(d.name));
|
|
176
|
+
});
|
|
177
|
+
await runCheck('CRLF 归一', () => {
|
|
178
|
+
const d = SkillDocument.fromString('---\r\nname: crlf-test\r\ndescription: d\r\n---\r\nbody');
|
|
179
|
+
if (d.body.trim() !== 'body')
|
|
180
|
+
throw new Error('CRLF 归一失败: ' + JSON.stringify(d.body));
|
|
181
|
+
});
|
|
182
|
+
await runCheck('kebab 校验', () => {
|
|
183
|
+
let threw = null;
|
|
184
|
+
try {
|
|
185
|
+
SkillDocument.fromString('---\nname: Not_Kebab\ndescription: d\n---\n');
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
threw = e;
|
|
189
|
+
}
|
|
190
|
+
if (!threw || !/kebab/.test(String(threw?.message)))
|
|
191
|
+
throw new Error('未抛 kebab 错误: ' + String(threw));
|
|
192
|
+
});
|
|
193
|
+
await runCheck('缺 name 报错', () => {
|
|
194
|
+
let threw = null;
|
|
195
|
+
try {
|
|
196
|
+
SkillDocument.fromString('---\ndescription: d\n---\n');
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
threw = e;
|
|
200
|
+
}
|
|
201
|
+
if (!threw || !/name/.test(String(threw?.message)))
|
|
202
|
+
throw new Error('未抛缺 name 错误: ' + String(threw));
|
|
203
|
+
});
|
|
204
|
+
await runCheck('缺 description 报错', () => {
|
|
205
|
+
let threw = null;
|
|
206
|
+
try {
|
|
207
|
+
SkillDocument.fromString('---\nname: x-test\n---\n');
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
threw = e;
|
|
211
|
+
}
|
|
212
|
+
if (!threw || !/description/.test(String(threw?.message)))
|
|
213
|
+
throw new Error('未抛缺 description 错误: ' + String(threw));
|
|
214
|
+
});
|
|
215
|
+
await runCheck('abort 中止', async () => {
|
|
216
|
+
const ctrl = new AbortController();
|
|
217
|
+
ctrl.abort();
|
|
218
|
+
let threw = null;
|
|
219
|
+
try {
|
|
220
|
+
await SkillDocument.fromFile('non-existent-whatever.md', ctrl.signal);
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
threw = e;
|
|
224
|
+
}
|
|
225
|
+
if (!threw || threw.name !== 'AbortError')
|
|
226
|
+
throw new Error('未抛 AbortError: ' + (threw?.name ?? '无'));
|
|
227
|
+
});
|
|
228
|
+
return results;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 映射为 DSH SkillCandidate 契约对象。
|
|
232
|
+
*/
|
|
233
|
+
toCandidate(providerName, rank) {
|
|
234
|
+
const dir = dirname(this.path);
|
|
235
|
+
return {
|
|
236
|
+
name: this.name,
|
|
237
|
+
description: this.description,
|
|
238
|
+
...(this.whenToUse ? { whenToUse: this.whenToUse } : {}),
|
|
239
|
+
invocation: this.invocation,
|
|
240
|
+
source: 'bundled',
|
|
241
|
+
provider: providerName,
|
|
242
|
+
rank,
|
|
243
|
+
locator: { path: this.path, directory: dir },
|
|
244
|
+
resourceBase: { kind: 'directory', path: dir },
|
|
245
|
+
path: this.path,
|
|
246
|
+
...(this.metadata ? { metadata: this.metadata } : {}),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 映射为 DSH SkillDefinition 契约对象。
|
|
251
|
+
*/
|
|
252
|
+
toDefinition(providerName) {
|
|
253
|
+
const dir = dirname(this.path);
|
|
254
|
+
return {
|
|
255
|
+
name: this.name,
|
|
256
|
+
description: this.description,
|
|
257
|
+
...(this.whenToUse ? { whenToUse: this.whenToUse } : {}),
|
|
258
|
+
invocation: this.invocation,
|
|
259
|
+
source: 'bundled',
|
|
260
|
+
provider: providerName,
|
|
261
|
+
resourceBase: { kind: 'directory', path: dir },
|
|
262
|
+
path: this.path,
|
|
263
|
+
...(this.metadata ? { metadata: this.metadata } : {}),
|
|
264
|
+
content: this.body.trim(),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
|
package/lib/superpowers.d.ts
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { Context } from '@deepseek-ai/cordis';
|
|
9
9
|
import Schema from '@deepseek-ai/schemastery';
|
|
10
|
+
import { SkillCatalog } from './catalog.js';
|
|
11
|
+
import { SkillDocument } from './document.js';
|
|
12
|
+
export { SkillCatalog, type SpecificationReport } from './catalog.js';
|
|
13
|
+
export { SkillDocument, type SpecificationTestResult } from './document.js';
|
|
10
14
|
export declare const Config: Schema<Schemastery.ObjectS<{
|
|
11
15
|
/** 注册到 ctx.skills 的 provider 名称,默认为 superpowers;不可为保留名 runtime */
|
|
12
16
|
providerName: Schema<string, string>;
|
|
@@ -40,6 +44,8 @@ declare const _default: {
|
|
|
40
44
|
skillDir: Schema<string, string>;
|
|
41
45
|
}>>;
|
|
42
46
|
apply: typeof apply;
|
|
47
|
+
SkillDocument: typeof SkillDocument;
|
|
48
|
+
SkillCatalog: typeof SkillCatalog;
|
|
43
49
|
};
|
|
44
50
|
export default _default;
|
|
45
51
|
//# sourceMappingURL=superpowers.d.ts.map
|
package/lib/superpowers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"superpowers.d.ts","sourceRoot":"","sources":["../src/superpowers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"superpowers.d.ts","sourceRoot":"","sources":["../src/superpowers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAQlD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAG7C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAM3E,eAAO,MAAM,MAAM;IACjB,kEAAkE;;IAElE,8CAA8C;;;IAF9C,kEAAkE;;IAElE,8CAA8C;;GAEF,CAAA;AAE9C,MAAM,WAAW,MAAM;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAMD,eAAO,MAAM,IAAI,gBAAgB,CAAA;AACjC,eAAO,MAAM,MAAM,qBAAsB,CAAA;AA6DzC,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA0BxD;;;;;QAvGC,kEAAkE;;QAElE,8CAA8C;;;QAF9C,kEAAkE;;QAElE,8CAA8C;;;;;;;AAuGhD,wBAA2E"}
|
package/lib/superpowers.js
CHANGED
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
* 通过 ctx.skills.registerProvider 注入全局层,rank 10 使本插件技能优先级最高:
|
|
6
6
|
* 小于 dsh-skill-filesystem 的项目/用户根(100–500)与官方 bundled(600)。
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import { dirname, join, resolve } from 'node:path';
|
|
8
|
+
import { dirname, resolve } from 'node:path';
|
|
10
9
|
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { parse } from 'yaml';
|
|
12
|
-
import { isSkillName } from '@deepseek-ai/dsh-skill';
|
|
13
10
|
import Schema from '@deepseek-ai/schemastery';
|
|
11
|
+
import { SkillCatalog } from './catalog.js';
|
|
12
|
+
import { SkillDocument } from './document.js';
|
|
13
|
+
// 具名导出深度模块,为验证治理与测试表面提供统一深度接口
|
|
14
|
+
export { SkillCatalog } from './catalog.js';
|
|
15
|
+
export { SkillDocument } from './document.js';
|
|
14
16
|
// ---------------------------------------------------------------------------
|
|
15
17
|
// Config — 默认值写在 schema 里;可选字段用可选属性声明
|
|
16
18
|
// ---------------------------------------------------------------------------
|
|
@@ -26,7 +28,7 @@ export const Config = Schema.object({
|
|
|
26
28
|
export const name = 'superpowers';
|
|
27
29
|
export const inject = ['skills'];
|
|
28
30
|
// ---------------------------------------------------------------------------
|
|
29
|
-
//
|
|
31
|
+
// 常量与校验
|
|
30
32
|
// ---------------------------------------------------------------------------
|
|
31
33
|
// 本插件技能优先级最高:rank 值越小优先级越高(官方注册表升序裁决),
|
|
32
34
|
// 10 低于 filesystem 项目/用户根(100–500) 与官方 bundled(600)。
|
|
@@ -37,92 +39,6 @@ function assertNotRuntimeProvider(providerName) {
|
|
|
37
39
|
throw new Error(`[superpowers] providerName "${RUNTIME_PROVIDER}" 为保留名,不可用`);
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
|
-
function stringField(data, key) {
|
|
41
|
-
const v = data[key];
|
|
42
|
-
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
43
|
-
}
|
|
44
|
-
function optionalString(data, key) {
|
|
45
|
-
const v = data[key];
|
|
46
|
-
return typeof v === 'string' && v.length > 0 ? { [key]: v } : {};
|
|
47
|
-
}
|
|
48
|
-
function frontmatterBoolean(data, key) {
|
|
49
|
-
if (!Object.hasOwn(data, key))
|
|
50
|
-
return undefined;
|
|
51
|
-
const v = data[key];
|
|
52
|
-
if (typeof v === 'boolean')
|
|
53
|
-
return v;
|
|
54
|
-
if (v === 1 || v === '1')
|
|
55
|
-
return true;
|
|
56
|
-
if (v === 0 || v === '0')
|
|
57
|
-
return false;
|
|
58
|
-
if (typeof v === 'string') {
|
|
59
|
-
switch (v.toLowerCase()) {
|
|
60
|
-
case 'true':
|
|
61
|
-
case 'yes':
|
|
62
|
-
case 'on':
|
|
63
|
-
return true;
|
|
64
|
-
case 'false':
|
|
65
|
-
case 'no':
|
|
66
|
-
case 'off':
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
throw new TypeError(`frontmatter field "${key}" must be a boolean`);
|
|
71
|
-
}
|
|
72
|
-
function rejectLegacyKey(data, legacy, canonical) {
|
|
73
|
-
if (Object.hasOwn(data, legacy)) {
|
|
74
|
-
throw new Error(`frontmatter field "${legacy}" is unsupported; use "${canonical}"`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function parseInvocationPolicy(data) {
|
|
78
|
-
rejectLegacyKey(data, 'disableModelInvocation', 'disable-model-invocation');
|
|
79
|
-
rejectLegacyKey(data, 'modelInvocable', 'disable-model-invocation');
|
|
80
|
-
rejectLegacyKey(data, 'userInvocable', 'user-invocable');
|
|
81
|
-
const disableModelInvocation = frontmatterBoolean(data, 'disable-model-invocation');
|
|
82
|
-
const userInvocable = frontmatterBoolean(data, 'user-invocable');
|
|
83
|
-
return {
|
|
84
|
-
modelInvocable: disableModelInvocation !== true,
|
|
85
|
-
userInvocable: userInvocable !== false,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
function optionalMetadata(data) {
|
|
89
|
-
const v = data['metadata'];
|
|
90
|
-
if (typeof v === 'object' && v !== null && !Array.isArray(v))
|
|
91
|
-
return { metadata: v };
|
|
92
|
-
return {};
|
|
93
|
-
}
|
|
94
|
-
function findClosingFrontmatter(raw, start) {
|
|
95
|
-
let lineStart = start;
|
|
96
|
-
while (lineStart <= raw.length) {
|
|
97
|
-
const nl = raw.indexOf('\n', lineStart);
|
|
98
|
-
const lineEnd = nl < 0 ? raw.length : nl;
|
|
99
|
-
if (raw.slice(lineStart, lineEnd).replace(/\r$/, '') === '---') {
|
|
100
|
-
return { start: lineStart, bodyStart: nl < 0 ? raw.length : nl + 1 };
|
|
101
|
-
}
|
|
102
|
-
if (nl < 0)
|
|
103
|
-
return undefined;
|
|
104
|
-
lineStart = nl + 1;
|
|
105
|
-
}
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
function parseFrontmatter(raw) {
|
|
109
|
-
// 去 BOM:Windows 编辑器易带 \uFEFF,导致首行非 --- 而被静默跳过
|
|
110
|
-
if (raw.charCodeAt(0) === 0xfeff)
|
|
111
|
-
raw = raw.slice(1);
|
|
112
|
-
const firstNl = raw.indexOf('\n');
|
|
113
|
-
if (firstNl < 0)
|
|
114
|
-
return undefined;
|
|
115
|
-
if (raw.slice(0, firstNl).replace(/\r$/, '') !== '---')
|
|
116
|
-
return undefined;
|
|
117
|
-
const start = firstNl + 1;
|
|
118
|
-
const closing = findClosingFrontmatter(raw, start);
|
|
119
|
-
if (!closing)
|
|
120
|
-
return undefined;
|
|
121
|
-
const parsed = parse(raw.slice(start, closing.start));
|
|
122
|
-
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
123
|
-
return undefined;
|
|
124
|
-
return { data: parsed, body: raw.slice(closing.bodyStart) };
|
|
125
|
-
}
|
|
126
42
|
function resolveDefaultSkillDir(configSkillDir) {
|
|
127
43
|
if (configSkillDir)
|
|
128
44
|
return resolve(configSkillDir);
|
|
@@ -132,175 +48,32 @@ function resolveDefaultSkillDir(configSkillDir) {
|
|
|
132
48
|
return resolve(dirname(here), '..', 'skills');
|
|
133
49
|
}
|
|
134
50
|
// ---------------------------------------------------------------------------
|
|
135
|
-
// Provider
|
|
51
|
+
// Provider 实现 — 纯调度与生命周期治理,具体编目、快照与文档解析委托给 SkillCatalog 深度模块
|
|
136
52
|
// ---------------------------------------------------------------------------
|
|
137
53
|
class SuperpowersProvider {
|
|
138
54
|
name;
|
|
139
|
-
|
|
55
|
+
catalog;
|
|
140
56
|
ctx;
|
|
141
57
|
constructor(ctx, _control, config) {
|
|
142
58
|
assertNotRuntimeProvider(config.providerName);
|
|
143
59
|
this.ctx = ctx;
|
|
144
60
|
this.name = config.providerName;
|
|
145
|
-
|
|
61
|
+
const skillDir = resolveDefaultSkillDir(config.skillDir);
|
|
62
|
+
this.catalog = new SkillCatalog(skillDir);
|
|
146
63
|
}
|
|
147
64
|
async list(options) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
entries = await readdir(this.skillDir, { withFileTypes: true, signal: options.signal });
|
|
154
|
-
}
|
|
155
|
-
catch (err) {
|
|
156
|
-
const code = err?.code;
|
|
157
|
-
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
158
|
-
this.ctx.logger.warn(`[superpowers] skillDir not found: ${this.skillDir}`);
|
|
159
|
-
return [];
|
|
160
|
-
}
|
|
161
|
-
throw err;
|
|
162
|
-
}
|
|
163
|
-
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
164
|
-
options.signal?.throwIfAborted();
|
|
165
|
-
if (!entry.isDirectory())
|
|
166
|
-
continue;
|
|
167
|
-
if (entry.name.startsWith('.'))
|
|
168
|
-
continue;
|
|
169
|
-
const skillPath = join(this.skillDir, entry.name, 'SKILL.md');
|
|
170
|
-
try {
|
|
171
|
-
await stat(skillPath);
|
|
172
|
-
}
|
|
173
|
-
catch (err) {
|
|
174
|
-
const code = err?.code;
|
|
175
|
-
this.ctx.logger.debug(`[superpowers] skip ${entry.name}: no SKILL.md (${code ?? String(err)})`);
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
let parsed;
|
|
179
|
-
try {
|
|
180
|
-
parsed = await parseSkillFile(skillPath);
|
|
181
|
-
}
|
|
182
|
-
catch (err) {
|
|
183
|
-
this.ctx.logger.warn(`[superpowers] skip ${skillPath}: YAML 解析失败 — ${String(err)}`);
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
if (!parsed) {
|
|
187
|
-
this.ctx.logger.warn(`[superpowers] skip ${entry.name}: missing or invalid frontmatter`);
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
const { data } = parsed;
|
|
191
|
-
const skillName = stringField(data, 'name');
|
|
192
|
-
const description = stringField(data, 'description');
|
|
193
|
-
if (!skillName || !description) {
|
|
194
|
-
this.ctx.logger.warn(`[superpowers] skip ${skillPath}: frontmatter requires name and description`);
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
if (!isSkillName(skillName)) {
|
|
198
|
-
this.ctx.logger.warn(`[superpowers] skip ${skillPath}: invalid skill name "${skillName}"`);
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
if (seen.has(skillName)) {
|
|
202
|
-
this.ctx.logger.warn(`[superpowers] skip ${skillPath}: duplicate skill name "${skillName}"`);
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
// 目录名与 skill name 不一致时以 frontmatter 为准,但打印提示
|
|
206
|
-
if (skillName !== entry.name) {
|
|
207
|
-
this.ctx.logger.warn(`[superpowers] skill name "${skillName}" != directory "${entry.name}" (using frontmatter)`);
|
|
208
|
-
}
|
|
209
|
-
let invocation;
|
|
210
|
-
try {
|
|
211
|
-
invocation = parseInvocationPolicy(data);
|
|
212
|
-
}
|
|
213
|
-
catch (e) {
|
|
214
|
-
this.ctx.logger.warn(`[superpowers] skip ${skillPath}: ${String(e)}`);
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
seen.add(skillName);
|
|
218
|
-
candidates.push({
|
|
219
|
-
name: skillName,
|
|
220
|
-
description,
|
|
221
|
-
...optionalString(data, 'whenToUse'),
|
|
222
|
-
invocation,
|
|
223
|
-
source: 'bundled',
|
|
224
|
-
provider: this.name,
|
|
225
|
-
rank: SUPERPOWERS_RANK,
|
|
226
|
-
locator: { path: skillPath, directory: dirname(skillPath) },
|
|
227
|
-
resourceBase: { kind: 'directory', path: dirname(skillPath) },
|
|
228
|
-
path: skillPath,
|
|
229
|
-
...optionalMetadata(data),
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
return candidates;
|
|
65
|
+
return this.catalog.listCandidates(this.name, SUPERPOWERS_RANK, {
|
|
66
|
+
signal: options.signal,
|
|
67
|
+
logger: this.ctx.logger,
|
|
68
|
+
});
|
|
233
69
|
}
|
|
234
70
|
async get(candidate, options) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
let raw;
|
|
240
|
-
try {
|
|
241
|
-
raw = await readFile(locator.path, {
|
|
242
|
-
encoding: 'utf8',
|
|
243
|
-
signal: options.signal,
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
catch (err) {
|
|
247
|
-
if (err?.name === 'AbortError')
|
|
248
|
-
throw err;
|
|
249
|
-
const code = err?.code;
|
|
250
|
-
if (code === 'ENOENT')
|
|
251
|
-
return undefined;
|
|
252
|
-
this.ctx.logger.warn(`[superpowers] get ${candidate.name}: read failed (${code ?? String(err)})`);
|
|
253
|
-
return undefined;
|
|
254
|
-
}
|
|
255
|
-
if (raw === undefined)
|
|
256
|
-
return undefined;
|
|
257
|
-
options.signal?.throwIfAborted();
|
|
258
|
-
const parsed = parseFrontmatter(raw);
|
|
259
|
-
if (!parsed) {
|
|
260
|
-
this.ctx.logger.warn(`[superpowers] get ${candidate.name}: missing or invalid frontmatter`);
|
|
261
|
-
return undefined;
|
|
262
|
-
}
|
|
263
|
-
const data = parsed.data;
|
|
264
|
-
const skillName = stringField(data, 'name');
|
|
265
|
-
const description = stringField(data, 'description');
|
|
266
|
-
if (!skillName || !description) {
|
|
267
|
-
this.ctx.logger.warn(`[superpowers] get ${candidate.name}: frontmatter requires name and description`);
|
|
268
|
-
return undefined;
|
|
269
|
-
}
|
|
270
|
-
if (skillName !== candidate.name) {
|
|
271
|
-
// 名称漂移视为失效,触发上层 invalidate
|
|
272
|
-
this.ctx.logger.warn(`[superpowers] get ${candidate.name}: name drift "${skillName}" != "${candidate.name}"`);
|
|
273
|
-
return undefined;
|
|
274
|
-
}
|
|
275
|
-
let invocation;
|
|
276
|
-
try {
|
|
277
|
-
invocation = parseInvocationPolicy(data);
|
|
278
|
-
}
|
|
279
|
-
catch (e) {
|
|
280
|
-
this.ctx.logger.warn(`[superpowers] get ${candidate.name}: ${String(e)}`);
|
|
281
|
-
return undefined;
|
|
282
|
-
}
|
|
283
|
-
return {
|
|
284
|
-
name: skillName,
|
|
285
|
-
description,
|
|
286
|
-
...optionalString(data, 'whenToUse'),
|
|
287
|
-
invocation,
|
|
288
|
-
source: 'bundled',
|
|
289
|
-
provider: this.name,
|
|
290
|
-
resourceBase: { kind: 'directory', path: locator.directory },
|
|
291
|
-
path: locator.path,
|
|
292
|
-
...optionalMetadata(data),
|
|
293
|
-
content: parsed.body.trim(),
|
|
294
|
-
};
|
|
71
|
+
return this.catalog.getDefinition(candidate, this.name, {
|
|
72
|
+
signal: options.signal,
|
|
73
|
+
logger: this.ctx.logger,
|
|
74
|
+
});
|
|
295
75
|
}
|
|
296
76
|
}
|
|
297
|
-
async function parseSkillFile(path) {
|
|
298
|
-
const raw = await readFile(path, 'utf8');
|
|
299
|
-
const parsed = parseFrontmatter(raw);
|
|
300
|
-
if (!parsed)
|
|
301
|
-
return undefined;
|
|
302
|
-
return parsed;
|
|
303
|
-
}
|
|
304
77
|
// ---------------------------------------------------------------------------
|
|
305
78
|
// 插件入口 — 所有副作用走 ctx 注册,随 fiber 卸载自动清理
|
|
306
79
|
// ---------------------------------------------------------------------------
|
|
@@ -309,18 +82,22 @@ export function apply(ctx, config) {
|
|
|
309
82
|
ctx.logger.info(`[superpowers] registering provider "${config.providerName}"`);
|
|
310
83
|
// 将 provider 注册与事件监听放入同一个 effect,保证卸载时的清理顺序可控
|
|
311
84
|
ctx.effect(() => {
|
|
85
|
+
let activeProvider;
|
|
312
86
|
const disposeProvider = ctx.skills.registerProvider((control) => {
|
|
313
|
-
|
|
87
|
+
activeProvider = new SuperpowersProvider(ctx, control, config);
|
|
88
|
+
return activeProvider;
|
|
314
89
|
});
|
|
315
|
-
// skills/change 为 emit
|
|
90
|
+
// skills/change 为 emit 模式,感知外部变更并精准失效内存快照
|
|
316
91
|
const disposeListener = ctx.on('skills/change', () => {
|
|
317
|
-
ctx.logger.debug('[superpowers] skills catalog changed');
|
|
92
|
+
ctx.logger.debug('[superpowers] skills catalog changed, invalidating snapshot');
|
|
93
|
+
activeProvider?.catalog.invalidate();
|
|
318
94
|
});
|
|
319
95
|
return () => {
|
|
320
96
|
disposeListener();
|
|
321
97
|
disposeProvider();
|
|
98
|
+
activeProvider?.catalog.invalidate();
|
|
322
99
|
};
|
|
323
100
|
});
|
|
324
101
|
}
|
|
325
|
-
export default { name, inject, Config, apply };
|
|
102
|
+
export default { name, inject, Config, apply, SkillDocument, SkillCatalog };
|
|
326
103
|
//# sourceMappingURL=superpowers.js.map
|