sillyspec 3.7.6 → 3.7.7

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/setup.js +40 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sillyspec",
3
- "version": "3.7.6",
3
+ "version": "3.7.7",
4
4
  "description": "SillySpec CLI — 流程状态机,让 AI 严格按步骤来",
5
5
  "type": "module",
6
6
  "bin": {
package/src/setup.js CHANGED
@@ -17,7 +17,7 @@ const SKILLS = [
17
17
  name: 'Playwright E2E 测试参考',
18
18
  description: 'E2E 测试编写最佳实践,AI 执行测试任务时自动读取',
19
19
  source: join(__dirname, '..', 'templates', 'skills', 'playwright-e2e'),
20
- target: '.sillyspec/skills/playwright-e2e',
20
+ target: 'playwright-e2e',
21
21
  },
22
22
  ];
23
23
 
@@ -283,11 +283,20 @@ export async function cmdSetup(dir, options = {}) {
283
283
  ...globalChoices.length > 0 ? [{ name: chalk.bold('── 全局工具 ──'), value: '_global_header', disabled: true }] : [],
284
284
  ...globalChoices,
285
285
  ...[{ name: chalk.bold('── AI Skills(编写参考)──'), value: '_skill_header', disabled: true }],
286
- ...SKILLS.filter(s => !existsSync(join(dir, s.target, 'SKILL.md'))).map(s => ({
287
- name: `${s.name} ${s.description}`,
288
- value: `skill:${s.id}`,
289
- checked: false,
290
- })),
286
+ ...(() => {
287
+ const installed = new Set();
288
+ for (const { path } of availableTools) {
289
+ const skillDir = join(dir, dirname(path), 'skills');
290
+ for (const s of SKILLS) {
291
+ if (existsSync(join(skillDir, s.target, 'SKILL.md'))) installed.add(s.id);
292
+ }
293
+ }
294
+ return SKILLS.filter(s => !installed.has(s.id)).map(s => ({
295
+ name: `${s.name} — ${s.description}`,
296
+ value: `skill:${s.id}`,
297
+ checked: false,
298
+ }));
299
+ })(),
291
300
  ];
292
301
 
293
302
  if (allChoices.length === 0) {
@@ -382,16 +391,32 @@ export async function cmdSetup(dir, options = {}) {
382
391
  const selectedSkills = SKILLS.filter(s => selected.includes(`skill:${s.id}`));
383
392
 
384
393
  if (selectedSkills.length > 0) {
394
+ // 跟 MCP 一样,选择安装到哪些 AI 工具
395
+ const skillTargets = availableTools.map(t => ({
396
+ name: t.tool,
397
+ value: t.key,
398
+ checked: true,
399
+ }));
400
+
401
+ const selectedTools = await checkbox({
402
+ message: 'Skill 安装到哪些 AI 工具?',
403
+ choices: skillTargets,
404
+ });
405
+
406
+ const targets = availableTools.filter(t => selectedTools.includes(t.key));
407
+
385
408
  console.log('');
386
- for (const skill of selectedSkills) {
387
- const spinner = ora(`安装 ${skill.name}...`).start();
388
- try {
389
- const targetDir = join(dir, skill.target);
390
- mkdirSync(targetDir, { recursive: true });
391
- cpSync(skill.source, targetDir, { recursive: true });
392
- spinner.succeed(`${skill.name} 安装完成 ${skill.target}/SKILL.md`);
393
- } catch (err) {
394
- spinner.fail(`${skill.name} 安装失败: ${err.message}`);
409
+ for (const { tool, path } of targets) {
410
+ const spinner = ora(`安装 Skills 到 ${tool}...`).start();
411
+ for (const skill of selectedSkills) {
412
+ try {
413
+ const targetDir = join(dir, dirname(path), 'skills', skill.target);
414
+ mkdirSync(targetDir, { recursive: true });
415
+ cpSync(skill.source, targetDir, { recursive: true });
416
+ spinner.succeed(`${tool} ${dirname(path)}/skills/${skill.target}/SKILL.md`);
417
+ } catch (err) {
418
+ spinner.fail(`${skill.name} 安装失败: ${err.message}`);
419
+ }
395
420
  }
396
421
  }
397
422
  }