mdk-skills 2.4.20 → 2.4.22

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdk-skills",
3
- "version": "2.4.20",
3
+ "version": "2.4.22",
4
4
  "description": "mdk-engineer - 沉稳靠谱的前端开发助手 Claude Skills 配置包,一键注入 .claude/ 技能目录和 CLAUDE.md 人设配置",
5
5
  "author": "XiaoMa",
6
6
  "license": "MIT",
package/scripts/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
- const { getPackageSkills, getUserSkills, getSkillsSource, backupDir, listSkillDirs, copyDirSync } = require("./core");
5
+ const { getPackageSkills, getUserSkills, getSkillsSource, backupDir, listSkillDirs, copyDir } = require("./core");
6
6
 
7
7
  // npx 运行时:process.cwd() 是用户项目目录
8
8
  // __dirname 是包内 scripts/ 目录
@@ -14,31 +14,44 @@ const projectRoot = (() => {
14
14
  return process.cwd();
15
15
  }
16
16
  })();
17
- let skillsSource = getSkillsSource(projectRoot);
18
- const claudeDest = path.join(projectRoot, ".claude");
19
- const skillsDest = path.join(claudeDest, "skills");
20
- const settingsPath = path.join(claudeDest, "settings.json");
21
- const pkgSkillsSource = skillsSource
22
- ? path.join(skillsSource, ".claude", "skills")
23
- : null;
17
+
18
+ let skillsSource;
19
+ let claudeDest;
20
+ let skillsDest;
21
+ let settingsPath;
22
+ let pkgSkillsSource;
23
+
24
+ async function pathExists(p) {
25
+ try { await fs.promises.access(p); return true; } catch { return false; }
26
+ }
27
+
28
+ async function initPaths() {
29
+ skillsSource = await getSkillsSource(projectRoot);
30
+ claudeDest = path.join(projectRoot, ".claude");
31
+ skillsDest = path.join(claudeDest, "skills");
32
+ settingsPath = path.join(claudeDest, "settings.json");
33
+ pkgSkillsSource = skillsSource
34
+ ? path.join(skillsSource, ".claude", "skills")
35
+ : null;
36
+ }
24
37
 
25
38
  // ---------- 文件写入 ----------
26
39
 
27
- function readSettings() {
28
- if (!fs.existsSync(settingsPath))
40
+ async function readSettings() {
41
+ if (!(await pathExists(settingsPath)))
29
42
  return { skills: {}, always_apply_skills: [] };
30
43
  try {
31
- return JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
44
+ return JSON.parse(await fs.promises.readFile(settingsPath, "utf-8"));
32
45
  } catch {
33
46
  return { skills: {}, always_apply_skills: [] };
34
47
  }
35
48
  }
36
49
 
37
- function writeSettings(settings) {
38
- if (!fs.existsSync(claudeDest)) {
39
- fs.mkdirSync(claudeDest, { recursive: true });
50
+ async function writeSettings(settings) {
51
+ if (!(await pathExists(claudeDest))) {
52
+ await fs.promises.mkdir(claudeDest, { recursive: true });
40
53
  }
41
- fs.writeFileSync(
54
+ await fs.promises.writeFile(
42
55
  settingsPath,
43
56
  JSON.stringify(settings, null, 2) + "\n",
44
57
  "utf-8",
@@ -47,38 +60,35 @@ function writeSettings(settings) {
47
60
 
48
61
  // ---------- 安装勾选的技能 ----------
49
62
 
50
- function installSelectedSkills(selectedNames) {
51
- // 创建 .claude/ 和 skills/ 目录
52
- if (!fs.existsSync(skillsDest)) {
53
- fs.mkdirSync(skillsDest, { recursive: true });
63
+ async function installSelectedSkills(selectedNames) {
64
+ if (!(await pathExists(skillsDest))) {
65
+ await fs.promises.mkdir(skillsDest, { recursive: true });
54
66
  }
55
67
 
56
- const allPkgSkills = listSkillDirs(pkgSkillsSource); let installedCount = 0;
68
+ const allPkgSkills = await listSkillDirs(pkgSkillsSource);
69
+ let installedCount = 0;
57
70
 
58
71
  for (const name of allPkgSkills) {
59
72
  const src = path.join(pkgSkillsSource, name);
60
73
  const dest = path.join(skillsDest, name);
61
74
 
62
75
  if (selectedNames.includes(name)) {
63
- // 勾选了的 安装(不存在才装)
64
- if (!fs.existsSync(dest)) {
76
+ if (!(await pathExists(dest))) {
65
77
  try {
66
- copyDirSync(src, dest);
78
+ await copyDir(src, dest);
67
79
  installedCount++;
68
80
  } catch (err) {
69
81
  console.error(` ❌ 安装技能 "${name}" 失败:`, err.message);
70
82
  }
71
83
  }
72
84
  } else {
73
- // 没勾选的 如果之前装了,删掉
74
- if (fs.existsSync(dest)) {
75
- fs.rmSync(dest, { recursive: true, force: true });
85
+ if (await pathExists(dest)) {
86
+ await fs.promises.rm(dest, { recursive: true, force: true });
76
87
  }
77
88
  }
78
89
  }
79
90
 
80
- // 生成 settings.json
81
- const settings = readSettings();
91
+ const settings = await readSettings();
82
92
  if (!settings.skills) settings.skills = {};
83
93
 
84
94
  for (const name of allPkgSkills) {
@@ -89,21 +99,19 @@ function installSelectedSkills(selectedNames) {
89
99
  settings.skills[name].enabled = enabled;
90
100
  }
91
101
 
92
- writeSettings(settings);
102
+ await writeSettings(settings);
93
103
 
94
- // 安装 CLAUDE.md(没有才装)
95
104
  const mdSource = path.join(skillsSource, "CLAUDE.md");
96
105
  const mdDest = path.join(projectRoot, "CLAUDE.md");
97
- if (fs.existsSync(mdSource) && !fs.existsSync(mdDest)) {
98
- fs.copyFileSync(mdSource, mdDest);
106
+ if (await pathExists(mdSource) && !(await pathExists(mdDest))) {
107
+ await fs.promises.copyFile(mdSource, mdDest);
99
108
  console.log(" 📝 CLAUDE.md 已创建\n");
100
109
  }
101
110
 
102
- // 复制 profiles.json 到项目
103
111
  const profilesSource = path.join(skillsSource, ".claude", "profiles.json");
104
112
  const profilesDest = path.join(claudeDest, "profiles.json");
105
- if (fs.existsSync(profilesSource) && !fs.existsSync(profilesDest)) {
106
- fs.copyFileSync(profilesSource, profilesDest);
113
+ if (await pathExists(profilesSource) && !(await pathExists(profilesDest))) {
114
+ await fs.promises.copyFile(profilesSource, profilesDest);
107
115
  }
108
116
 
109
117
  return installedCount;
@@ -111,55 +119,50 @@ function installSelectedSkills(selectedNames) {
111
119
 
112
120
  // ---------- 场景选择(场景 → 一键装技能) ----------
113
121
 
114
- function loadProfiles() {
122
+ async function loadProfiles() {
115
123
  const profilesPath = path.join(skillsSource, ".claude", "profiles.json");
116
- if (!fs.existsSync(profilesPath)) return null;
124
+ if (!(await pathExists(profilesPath))) return null;
117
125
  try {
118
- return JSON.parse(fs.readFileSync(profilesPath, "utf-8")).profiles;
126
+ return JSON.parse(await fs.promises.readFile(profilesPath, "utf-8")).profiles;
119
127
  } catch {
120
128
  return null;
121
129
  }
122
130
  }
123
131
 
124
- function applyProfile(profile) {
125
- const pkgSkills = listSkillDirs(pkgSkillsSource);
126
- const settings = readSettings();
132
+ async function applyProfile(profile) {
133
+ const pkgSkills = await listSkillDirs(pkgSkillsSource);
134
+ const settings = await readSettings();
127
135
 
128
- // 确定要启用的技能列表
129
136
  let selected;
130
137
  if (profile.skills === null) {
131
- // null 表示"自定义",不做操作,由调用方处理
132
138
  return;
133
139
  } else if (profile.skills.length === 0) {
134
140
  selected = [];
135
141
  } else {
136
- // 只取包内实际存在的技能
137
142
  selected = profile.skills.filter((s) => pkgSkills.includes(s));
138
143
  }
139
144
 
140
- // 装启用的、删禁用的
141
- if (!fs.existsSync(skillsDest)) {
142
- fs.mkdirSync(skillsDest, { recursive: true });
145
+ if (!(await pathExists(skillsDest))) {
146
+ await fs.promises.mkdir(skillsDest, { recursive: true });
143
147
  }
144
148
  for (const name of pkgSkills) {
145
149
  const src = path.join(pkgSkillsSource, name);
146
150
  const dest = path.join(skillsDest, name);
147
151
  if (selected.includes(name)) {
148
- if (!fs.existsSync(dest)) {
152
+ if (!(await pathExists(dest))) {
149
153
  try {
150
- copyDirSync(src, dest);
154
+ await copyDir(src, dest);
151
155
  } catch (err) {
152
156
  console.error(` ❌ 安装技能 "${name}" 失败:`, err.message);
153
157
  }
154
158
  }
155
159
  } else {
156
- if (fs.existsSync(dest)) {
157
- fs.rmSync(dest, { recursive: true, force: true });
160
+ if (await pathExists(dest)) {
161
+ await fs.promises.rm(dest, { recursive: true, force: true });
158
162
  }
159
163
  }
160
164
  }
161
165
 
162
- // 更新 settings
163
166
  if (!settings.skills) settings.skills = {};
164
167
  for (const name of pkgSkills) {
165
168
  const enabled = selected.includes(name);
@@ -167,31 +170,26 @@ function applyProfile(profile) {
167
170
  settings.skills[name].enabled = enabled;
168
171
  }
169
172
 
170
- // 更新 always_apply_skills
171
173
  if (profile.always_apply) {
172
174
  settings.always_apply_skills = profile.always_apply;
173
175
  }
174
176
 
175
- // 记录当前活动场景
176
177
  settings._active_profile = profile.id;
177
- writeSettings(settings);
178
+ await writeSettings(settings);
178
179
 
179
- // 安装 CLAUDE.md(没有才装)
180
180
  const mdSource = path.join(skillsSource, "CLAUDE.md");
181
181
  const mdDest = path.join(projectRoot, "CLAUDE.md");
182
- if (fs.existsSync(mdSource) && !fs.existsSync(mdDest)) {
183
- fs.copyFileSync(mdSource, mdDest);
182
+ if (await pathExists(mdSource) && !(await pathExists(mdDest))) {
183
+ await fs.promises.copyFile(mdSource, mdDest);
184
184
  console.log(" 📝 CLAUDE.md 已创建\n");
185
185
  }
186
186
 
187
- // 复制 profiles.json 到项目(方便在项目里修改后 sync 推回)
188
187
  const profilesSource = path.join(skillsSource, ".claude", "profiles.json");
189
188
  const profilesDest = path.join(claudeDest, "profiles.json");
190
- if (fs.existsSync(profilesSource)) {
191
- fs.copyFileSync(profilesSource, profilesDest);
189
+ if (await pathExists(profilesSource)) {
190
+ await fs.promises.copyFile(profilesSource, profilesDest);
192
191
  }
193
192
 
194
- // 输出结果
195
193
  const enabledCount = selected.length;
196
194
  const disabledCount = pkgSkills.length - enabledCount;
197
195
  console.log(`\n ✅ 已切换到「${profile.name}」`);
@@ -201,10 +199,10 @@ function applyProfile(profile) {
201
199
  async function startSceneSelection() {
202
200
  const { select } = await import("@inquirer/prompts");
203
201
  const chalk = (await import("chalk")).default;
204
- const profiles = loadProfiles();
205
- const settings = readSettings();
202
+ const profiles = await loadProfiles();
203
+ const settings = await readSettings();
206
204
  const activeProfile = settings._active_profile;
207
- const hasExistingInstall = fs.existsSync(claudeDest);
205
+ const hasExistingInstall = await pathExists(claudeDest);
208
206
 
209
207
  const choices = profiles.map((p) => {
210
208
  const isActive = p.id === activeProfile;
@@ -218,7 +216,6 @@ async function startSceneSelection() {
218
216
  };
219
217
  });
220
218
 
221
- // 底部加个分隔
222
219
  choices.push(
223
220
  { name: chalk.dim("────────────────"), value: "__sep__", description: "" },
224
221
  { name: " 查看技能清单", value: "__list__", description: "列出所有技能状态" },
@@ -240,7 +237,6 @@ async function startSceneSelection() {
240
237
 
241
238
  if (selectedId === "__list__") {
242
239
  await cmdList();
243
- // 看完列表再回到场景选择
244
240
  console.log("");
245
241
  return startSceneSelection();
246
242
  }
@@ -248,13 +244,11 @@ async function startSceneSelection() {
248
244
  const profile = profiles.find((p) => p.id === selectedId);
249
245
 
250
246
  if (profile.skills === null) {
251
- // "自定义选择" → 跳到 checkbox
252
247
  await startInteractiveMenu();
253
248
  return;
254
249
  }
255
250
 
256
251
  if (profile.id === activeProfile) {
257
- // 选的已经是当前场景,问要不要微调
258
252
  const { confirm } = await import("@inquirer/prompts");
259
253
  const tweak = await confirm({
260
254
  message: "已是当前场景,是否进入自定义模式微调技能?",
@@ -266,20 +260,20 @@ async function startSceneSelection() {
266
260
  return;
267
261
  }
268
262
 
269
- applyProfile(profile);
263
+ await applyProfile(profile);
270
264
  }
271
265
 
272
266
  // ---------- 命令:list ----------
273
267
 
274
268
  async function cmdList() {
275
- if (!fs.existsSync(claudeDest)) {
269
+ if (!(await pathExists(claudeDest))) {
276
270
  console.log("\n ⚠️ 尚未安装技能,请先运行 npx mdk-skills\n");
277
271
  return;
278
272
  }
279
273
 
280
274
  const chalk = (await import("chalk")).default;
281
- const pkgSkills = getPackageSkills(skillsSource);
282
- const userSkills = getUserSkills(claudeDest);
275
+ const pkgSkills = await getPackageSkills(skillsSource);
276
+ const userSkills = await getUserSkills(claudeDest);
283
277
 
284
278
  const skillMap = new Map();
285
279
  for (const s of userSkills) skillMap.set(s.name, s);
@@ -319,27 +313,25 @@ async function cmdList() {
319
313
  async function startInteractiveMenu() {
320
314
  const { checkbox } = await import("@inquirer/prompts");
321
315
 
322
- if (!fs.existsSync(pkgSkillsSource)) {
316
+ if (!(await pathExists(pkgSkillsSource))) {
323
317
  console.log(" ⚠️ 包内没有找到技能文件\n");
324
318
  return;
325
319
  }
326
320
 
327
- const pkgSkills = getPackageSkills(skillsSource);
321
+ const pkgSkills = await getPackageSkills(skillsSource);
328
322
 
329
323
  if (pkgSkills.length === 0) {
330
324
  console.log(" ⚠️ 没有可用技能\n");
331
325
  return;
332
326
  }
333
327
 
334
- // 读取已安装状态:已装且启用的默认勾选
335
- const userSkills = getUserSkills(claudeDest);
328
+ const userSkills = await getUserSkills(claudeDest);
336
329
  const enabledSet = new Set(
337
330
  userSkills.filter((s) => s.enabled).map((s) => s.name),
338
331
  );
339
332
  const installedSet = new Set(userSkills.map((s) => s.name));
340
333
 
341
- // 首次运行全部默认勾选(省事),再次运行沿用已有状态
342
- const hasExistingInstall = fs.existsSync(claudeDest);
334
+ const hasExistingInstall = await pathExists(claudeDest);
343
335
  const defaultChecked = hasExistingInstall
344
336
  ? enabledSet
345
337
  : new Set(pkgSkills.map((s) => s.name));
@@ -364,7 +356,7 @@ async function startInteractiveMenu() {
364
356
  (name) => !selected.includes(name),
365
357
  ).length;
366
358
 
367
- const installed = installSelectedSkills(selected);
359
+ const installed = await installSelectedSkills(selected);
368
360
 
369
361
  const parts = [];
370
362
  if (newCount > 0) parts.push(`新装 ${newCount} 个`);
@@ -376,7 +368,7 @@ async function startInteractiveMenu() {
376
368
 
377
369
  // ---------- 本地源连接管理 ----------
378
370
 
379
- function cmdConnect(repoPath) {
371
+ async function cmdConnect(repoPath) {
380
372
  if (!repoPath) {
381
373
  console.log(" ⚠️ 用法:npx mdk-skills connect <仓库路径>\n");
382
374
  return;
@@ -384,20 +376,20 @@ function cmdConnect(repoPath) {
384
376
 
385
377
  repoPath = path.resolve(repoPath);
386
378
 
387
- if (!fs.existsSync(path.join(repoPath, ".claude", "skills"))) {
379
+ if (!(await pathExists(path.join(repoPath, ".claude", "skills")))) {
388
380
  console.log(` ❌ 路径 "${repoPath}" 下没有找到 .claude/skills/\n`);
389
381
  return;
390
382
  }
391
383
 
392
- const settings = readSettings();
384
+ const settings = await readSettings();
393
385
  settings._skill_source = repoPath;
394
- writeSettings(settings);
386
+ await writeSettings(settings);
395
387
 
396
388
  console.log(` ✅ 技能目录已设置: ${repoPath}\n`);
397
389
  }
398
390
 
399
- function cmdSync() {
400
- const settings = readSettings();
391
+ async function cmdSync() {
392
+ const settings = await readSettings();
401
393
  const sourcePath = settings._skill_source;
402
394
 
403
395
  if (!sourcePath) {
@@ -405,58 +397,53 @@ function cmdSync() {
405
397
  return;
406
398
  }
407
399
 
408
- if (!fs.existsSync(path.join(sourcePath, ".claude"))) {
400
+ if (!(await pathExists(path.join(sourcePath, ".claude")))) {
409
401
  console.log(` ❌ 绑定的路径 "${sourcePath}" 已失效\n`);
410
402
  return;
411
403
  }
412
404
 
413
- // 备份仓库旧内容
414
405
  const repoClaude = path.join(sourcePath, ".claude");
415
- if (fs.existsSync(repoClaude)) {
416
- backupDir(repoClaude);
406
+ if (await pathExists(repoClaude)) {
407
+ await backupDir(repoClaude);
417
408
  }
418
409
 
419
- // 技能 → 反推到仓库
420
410
  const repoSkills = path.join(sourcePath, ".claude", "skills");
421
- if (!fs.existsSync(repoSkills)) {
422
- fs.mkdirSync(repoSkills, { recursive: true });
411
+ if (!(await pathExists(repoSkills))) {
412
+ await fs.promises.mkdir(repoSkills, { recursive: true });
423
413
  }
424
- for (const name of listSkillDirs(skillsDest)) {
414
+ for (const name of await listSkillDirs(skillsDest)) {
425
415
  const src = path.join(skillsDest, name);
426
416
  const dest = path.join(repoSkills, name);
427
- if (fs.existsSync(dest)) {
428
- fs.rmSync(dest, { recursive: true, force: true });
417
+ if (await pathExists(dest)) {
418
+ await fs.promises.rm(dest, { recursive: true, force: true });
429
419
  }
430
- copyDirSync(src, dest);
420
+ await copyDir(src, dest);
431
421
  }
432
422
 
433
- // profiles.json → 反推
434
423
  const projectProfiles = path.join(claudeDest, "profiles.json");
435
- if (fs.existsSync(projectProfiles)) {
436
- fs.copyFileSync(projectProfiles, path.join(sourcePath, ".claude", "profiles.json"));
424
+ if (await pathExists(projectProfiles)) {
425
+ await fs.promises.copyFile(projectProfiles, path.join(sourcePath, ".claude", "profiles.json"));
437
426
  }
438
427
 
439
- // settings.json → 反推(过滤掉 _skill_source 和 _active_profile)
440
428
  const cleanSettings = { ...settings };
441
429
  delete cleanSettings._skill_source;
442
430
  delete cleanSettings._active_profile;
443
- fs.writeFileSync(
431
+ await fs.promises.writeFile(
444
432
  path.join(sourcePath, ".claude", "settings.json"),
445
433
  JSON.stringify(cleanSettings, null, 2) + "\n",
446
434
  "utf-8",
447
435
  );
448
436
 
449
- // CLAUDE.md → 反推
450
437
  const projectMd = path.join(projectRoot, "CLAUDE.md");
451
- if (fs.existsSync(projectMd)) {
452
- fs.copyFileSync(projectMd, path.join(sourcePath, "CLAUDE.md"));
438
+ if (await pathExists(projectMd)) {
439
+ await fs.promises.copyFile(projectMd, path.join(sourcePath, "CLAUDE.md"));
453
440
  }
454
441
 
455
442
  console.log(" ✅ 已同步到仓库\n");
456
443
  }
457
444
 
458
- function cmdClearSource() {
459
- const settings = readSettings();
445
+ async function cmdClearSource() {
446
+ const settings = await readSettings();
460
447
  if (!settings._skill_source) {
461
448
  console.log(" ⚠️ 当前未设置任何技能目录\n");
462
449
  return;
@@ -464,7 +451,7 @@ function cmdClearSource() {
464
451
 
465
452
  const oldPath = settings._skill_source;
466
453
  delete settings._skill_source;
467
- writeSettings(settings);
454
+ await writeSettings(settings);
468
455
 
469
456
  console.log(` ✅ 已清除技能目录设置: ${oldPath}\n`);
470
457
  console.log(` 💡 运行 npx mdk-skills ui 重新设置\n`);
@@ -475,15 +462,16 @@ function cmdClearSource() {
475
462
  const COMMANDS = ["list", "connect", "sync", "clear-source", "ui"];
476
463
 
477
464
  async function main() {
465
+ await initPaths();
466
+
478
467
  const command = process.argv[2];
479
468
  const args = process.argv.slice(3);
480
469
 
481
470
  if (!command) {
482
- const profiles = loadProfiles();
471
+ const profiles = await loadProfiles();
483
472
  if (profiles) {
484
473
  await startSceneSelection();
485
474
  } else {
486
- // 没有 profiles.json 时回退到原来的 checkbox
487
475
  await startInteractiveMenu();
488
476
  }
489
477
  } else if (command === "--help" || command === "-h") {
@@ -508,10 +496,10 @@ async function main() {
508
496
  npx mdk-skills ui
509
497
  `);
510
498
  } else if (command === "--connect") {
511
- cmdConnect(args[0]);
512
- skillsSource = getSkillsSource(projectRoot);
499
+ await cmdConnect(args[0]);
500
+ await initPaths();
513
501
  if (skillsSource) {
514
- const profiles = loadProfiles();
502
+ const profiles = await loadProfiles();
515
503
  if (profiles) {
516
504
  await startSceneSelection();
517
505
  } else {
@@ -519,12 +507,12 @@ async function main() {
519
507
  }
520
508
  }
521
509
  } else if (command === "connect") {
522
- cmdConnect(args[0]);
510
+ await cmdConnect(args[0]);
523
511
  } else if (command === "sync") {
524
512
  if (!skillsSource) { console.log(" ⚠️ 未设置技能目录,请先运行 npx mdk-skills ui\n"); return; }
525
- cmdSync();
513
+ await cmdSync();
526
514
  } else if (command === "clear-source") {
527
- cmdClearSource();
515
+ await cmdClearSource();
528
516
  } else if (command === "ui") {
529
517
  require("./web-ui/server");
530
518
  } else {