@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.
Files changed (57) hide show
  1. package/README.md +5 -5
  2. package/lib/catalog.d.ts +98 -0
  3. package/lib/catalog.d.ts.map +1 -0
  4. package/lib/catalog.js +295 -0
  5. package/lib/catalog.js.map +1 -0
  6. package/lib/document.d.ts +61 -0
  7. package/lib/document.d.ts.map +1 -0
  8. package/lib/document.js +268 -0
  9. package/lib/document.js.map +1 -0
  10. package/lib/superpowers.d.ts +6 -0
  11. package/lib/superpowers.d.ts.map +1 -1
  12. package/lib/superpowers.js +27 -250
  13. package/lib/superpowers.js.map +1 -1
  14. package/package.json +2 -2
  15. package/skills/brainstorming/SKILL.md +1 -1
  16. package/skills/brainstorming/scripts/server.cjs +6 -14
  17. package/skills/brainstorming/scripts/start-server.sh +1 -6
  18. package/skills/brainstorming/spec-document-reviewer-prompt.md +1 -1
  19. package/skills/brainstorming/visual-companion.md +5 -31
  20. package/skills/diagnosing-superpowers/SKILL.md +1 -1
  21. package/skills/diagnosing-superpowers/prompts/request-conflicts.md +1 -1
  22. package/skills/diagnosing-superpowers/templates/issue.md +1 -1
  23. package/skills/dispatching-parallel-agents/SKILL.md +4 -4
  24. package/skills/executing-plans/SKILL.md +5 -5
  25. package/skills/finishing-a-development-branch/SKILL.md +5 -5
  26. package/skills/receiving-code-review/SKILL.md +1 -1
  27. package/skills/requesting-code-review/SKILL.md +1 -1
  28. package/skills/requesting-code-review/code-reviewer.md +1 -1
  29. package/skills/subagent-driven-development/SKILL.md +2 -2
  30. package/skills/subagent-driven-development/implementer-prompt.md +1 -1
  31. package/skills/subagent-driven-development/re-review-prompt.md +1 -1
  32. package/skills/subagent-driven-development/scripts/sdd-workspace +2 -2
  33. package/skills/subagent-driven-development/task-reviewer-prompt.md +1 -1
  34. package/skills/systematic-debugging/CREATION-LOG.md +2 -2
  35. package/skills/systematic-debugging/SKILL.md +2 -2
  36. package/skills/systematic-debugging/condition-based-waiting.md +3 -3
  37. package/skills/systematic-debugging/defense-in-depth.md +1 -1
  38. package/skills/systematic-debugging/root-cause-tracing.md +1 -1
  39. package/skills/systematic-debugging/test-pressure-3.md +1 -1
  40. package/skills/test-driven-development/SKILL.md +1 -1
  41. package/skills/test-driven-development/writing-good-tests.md +1 -1
  42. package/skills/using-git-worktrees/SKILL.md +1 -1
  43. package/skills/using-superpowers/SKILL.md +4 -10
  44. package/skills/using-superpowers/references/dsh-tools.md +3 -3
  45. package/skills/verification-before-completion/SKILL.md +1 -1
  46. package/skills/writing-plans/SKILL.md +1 -1
  47. package/skills/writing-skills/SKILL.md +6 -6
  48. package/skills/writing-skills/testing-skills-with-subagents.md +1 -1
  49. package/skills/using-superpowers/references/antigravity-tools.md +0 -23
  50. package/skills/using-superpowers/references/claude-code-tools.md +0 -11
  51. package/skills/using-superpowers/references/codex-tools.md +0 -108
  52. package/skills/using-superpowers/references/gemini-tools.md +0 -63
  53. package/skills/using-superpowers/references/hermes-tools.md +0 -56
  54. package/skills/using-superpowers/references/muse-tools.md +0 -35
  55. package/skills/using-superpowers/references/pi-tools.md +0 -16
  56. package/skills/writing-skills/anthropic-best-practices.md +0 -1146
  57. package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -188
@@ -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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"}
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"superpowers.d.ts","sourceRoot":"","sources":["../src/superpowers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AASlD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAM7C,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;AA4RzC,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAqBxD;;;;;QAjUC,kEAAkE;;QAElE,8CAA8C;;;QAF9C,kEAAkE;;QAElE,8CAA8C;;;;;AAiUhD,wBAA8C"}
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"}
@@ -5,12 +5,14 @@
5
5
  * 通过 ctx.skills.registerProvider 注入全局层,rank 10 使本插件技能优先级最高:
6
6
  * 小于 dsh-skill-filesystem 的项目/用户根(100–500)与官方 bundled(600)。
7
7
  */
8
- import { readdir, readFile, stat } from 'node:fs/promises';
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
- skillDir;
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
- this.skillDir = resolveDefaultSkillDir(config.skillDir);
61
+ const skillDir = resolveDefaultSkillDir(config.skillDir);
62
+ this.catalog = new SkillCatalog(skillDir);
146
63
  }
147
64
  async list(options) {
148
- options.signal?.throwIfAborted();
149
- const candidates = [];
150
- const seen = new Set();
151
- let entries;
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
- options.signal?.throwIfAborted();
236
- const locator = candidate.locator;
237
- if (!locator?.path || !locator?.directory)
238
- return undefined;
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
- return new SuperpowersProvider(ctx, control, config);
87
+ activeProvider = new SuperpowersProvider(ctx, control, config);
88
+ return activeProvider;
314
89
  });
315
- // skills/change 为 emit 模式(见 @deepseek-ai/dsh-skill Events 定义:@mode emit),非 waterfall,无需 next()
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