dk-frontend-skills 2.1.2 → 2.1.3

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": "dk-frontend-skills",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "dk-engineer - 幽默沉稳靠谱的前端开发助手 Claude Skills 配置包,一键注入 .claude/ 技能目录和 CLAUDE.md 人设配置",
5
5
  "author": "XiaoMa",
6
6
  "license": "MIT",
package/scripts/cli.js CHANGED
@@ -23,7 +23,8 @@ const pkgSkillsSource = path.join(packageDir, ".claude", "skills");
23
23
  // ---------- 文件写入 ----------
24
24
 
25
25
  function readSettings() {
26
- if (!fs.existsSync(settingsPath)) return { skills: {}, always_apply_skills: [] };
26
+ if (!fs.existsSync(settingsPath))
27
+ return { skills: {}, always_apply_skills: [] };
27
28
  try {
28
29
  return JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
29
30
  } catch {
@@ -35,11 +36,29 @@ function writeSettings(settings) {
35
36
  if (!fs.existsSync(claudeDest)) {
36
37
  fs.mkdirSync(claudeDest, { recursive: true });
37
38
  }
38
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
39
+ fs.writeFileSync(
40
+ settingsPath,
41
+ JSON.stringify(settings, null, 2) + "\n",
42
+ "utf-8",
43
+ );
39
44
  }
40
45
 
41
46
  // ---------- 安装勾选的技能 ----------
42
47
 
48
+ // 手动递归拷贝目录,绕开 Windows 中文路径下 fs.cpSync({ recursive: true }) 死锁的 bug
49
+ function copyDirSync(src, dest) {
50
+ fs.mkdirSync(dest, { recursive: true });
51
+ for (const item of fs.readdirSync(src)) {
52
+ const srcPath = path.join(src, item);
53
+ const destPath = path.join(dest, item);
54
+ if (fs.statSync(srcPath).isDirectory()) {
55
+ copyDirSync(srcPath, destPath);
56
+ } else {
57
+ fs.copyFileSync(srcPath, destPath);
58
+ }
59
+ }
60
+ }
61
+
43
62
  function installSelectedSkills(selectedNames) {
44
63
  // 创建 .claude/ 和 skills/ 目录
45
64
  if (!fs.existsSync(skillsDest)) {
@@ -57,7 +76,7 @@ function installSelectedSkills(selectedNames) {
57
76
  // 勾选了的 → 安装(不存在才装)
58
77
  if (!fs.existsSync(dest)) {
59
78
  try {
60
- fs.cpSync(src, dest, { recursive: true });
79
+ copyDirSync(src, dest);
61
80
  installedCount++;
62
81
  } catch (err) {
63
82
  console.error(` ❌ 安装技能 "${name}" 失败:`, err.message);
@@ -119,13 +138,15 @@ async function cmdList() {
119
138
  const allSkills = [...skillMap.values()];
120
139
 
121
140
  console.log(`\n${chalk.bold("📋 dk-frontend-skills 技能清单")}\n`);
122
- console.log(` ${chalk.dim("状态 技能名 版本 描述")}`);
123
- console.log(` ${chalk.dim("─────────────────────────────────────────────────────")}`);
141
+ console.log(
142
+ ` ${chalk.dim("状态 技能名 版本 描述")}`,
143
+ );
144
+ console.log(
145
+ ` ${chalk.dim("─────────────────────────────────────────────────────")}`,
146
+ );
124
147
 
125
148
  for (const skill of allSkills) {
126
- const status = skill.enabled
127
- ? chalk.green("● 启用")
128
- : chalk.gray("○ 停用");
149
+ const status = skill.enabled ? chalk.green("● 启用") : chalk.gray("○ 停用");
129
150
  const name = chalk.white(skill.name.padEnd(20));
130
151
  const ver = chalk.dim(skill.version.padEnd(7));
131
152
  const desc = skill.description
@@ -134,7 +155,9 @@ async function cmdList() {
134
155
  console.log(` ${status} ${name} ${ver} ${desc}`);
135
156
  }
136
157
 
137
- console.log(`\n ${chalk.cyan("💡")} ${chalk.dim("运行 npx dk-frontend-skills 进入交互选择模式")}\n`);
158
+ console.log(
159
+ `\n ${chalk.cyan("💡")} ${chalk.dim("运行 npx dk-frontend-skills 进入交互选择模式")}\n`,
160
+ );
138
161
  }
139
162
 
140
163
  // ---------- 交互式菜单(选完即装) ----------
@@ -183,7 +206,9 @@ async function startInteractiveMenu() {
183
206
  });
184
207
 
185
208
  const newCount = selected.filter((name) => !installedSet.has(name)).length;
186
- const removedCount = [...installedSet].filter((name) => !selected.includes(name)).length;
209
+ const removedCount = [...installedSet].filter(
210
+ (name) => !selected.includes(name),
211
+ ).length;
187
212
 
188
213
  const installed = installSelectedSkills(selected);
189
214
 
@@ -223,7 +248,9 @@ async function main() {
223
248
  } else if (command === "list") {
224
249
  await cmdList();
225
250
  } else if (command === "enable" || command === "disable") {
226
- console.log(` ⚠️ 该命令已废弃,请直接运行 npx dk-frontend-skills 使用交互菜单\n`);
251
+ console.log(
252
+ ` ⚠️ 该命令已废弃,请直接运行 npx dk-frontend-skills 使用交互菜单\n`,
253
+ );
227
254
  } else {
228
255
  console.log(` 未知命令: ${command}\n 可用: ${COMMANDS.join(", ")}\n`);
229
256
  process.exit(1);
@@ -4,13 +4,15 @@ const {
4
4
  backupTimestamp,
5
5
  appendLog,
6
6
  backupDir,
7
+ copyDirSync,
7
8
  installSkills,
8
9
  installSettings,
9
10
  } = require("./core");
10
11
 
11
12
  // 获取用户项目根目录
12
13
  // npm lifecycle 脚本会设置 INIT_CWD 为执行命令时的目录,比从 __dirname 爬三级更可靠
13
- const projectRoot = process.env.INIT_CWD || path.resolve(__dirname, "..", "..", "..");
14
+ const projectRoot =
15
+ process.env.INIT_CWD || path.resolve(__dirname, "..", "..", "..");
14
16
  const packageDir = path.join(__dirname, "..");
15
17
 
16
18
  // 源路径
@@ -42,7 +44,7 @@ if (fs.existsSync(claudeSource)) {
42
44
  } else {
43
45
  // 全新安装
44
46
  appendLog(logFile, "INFO", "Install started (fresh)");
45
- fs.cpSync(claudeSource, claudeDest, { recursive: true });
47
+ copyDirSync(claudeSource, claudeDest);
46
48
  appendLog(logFile, "INFO", `.claude/ directory created`);
47
49
  }
48
50
  }
@@ -60,7 +62,11 @@ if (fs.existsSync(mdSource)) {
60
62
  const bakName = `CLAUDE.md.${backupTimestamp()}`;
61
63
  const bakPath = path.join(backupsDir, bakName);
62
64
  fs.copyFileSync(mdDest, bakPath);
63
- appendLog(logFile, "BACKUP", `backups/${bakName} (CLAUDE.md user version preserved)`);
65
+ appendLog(
66
+ logFile,
67
+ "BACKUP",
68
+ `backups/${bakName} (CLAUDE.md user version preserved)`,
69
+ );
64
70
  }
65
71
  }
66
72
 
package/scripts/core.js CHANGED
@@ -46,7 +46,7 @@ function backupDir(dir) {
46
46
 
47
47
  for (const item of fs.readdirSync(dir)) {
48
48
  if (item === "backups") continue;
49
- fs.cpSync(path.join(dir, item), path.join(bakPath, item), { recursive: true });
49
+ copyDirSync(path.join(dir, item), path.join(bakPath, item));
50
50
  }
51
51
 
52
52
  // 清理旧备份,只保留最近 3 份
@@ -70,6 +70,22 @@ function isPlainObject(val) {
70
70
  return Object.prototype.toString.call(val) === "[object Object]";
71
71
  }
72
72
 
73
+ /**
74
+ * 手动递归拷贝目录,绕开 Windows 中文路径下 fs.cpSync({ recursive: true }) 死锁的 bug
75
+ */
76
+ function copyDirSync(src, dest) {
77
+ fs.mkdirSync(dest, { recursive: true });
78
+ for (const item of fs.readdirSync(src)) {
79
+ const srcPath = path.join(src, item);
80
+ const destPath = path.join(dest, item);
81
+ if (fs.statSync(srcPath).isDirectory()) {
82
+ copyDirSync(srcPath, destPath);
83
+ } else {
84
+ fs.copyFileSync(srcPath, destPath);
85
+ }
86
+ }
87
+ }
88
+
73
89
  /**
74
90
  * 递归深合并,用户值优先,目标新增字段补充
75
91
  */
@@ -122,7 +138,7 @@ function installSkills(skillsSource, skillsDest, logFile) {
122
138
  const version = meta ? meta.version : "?";
123
139
 
124
140
  if (!fs.existsSync(destSkill)) {
125
- fs.cpSync(srcSkill, destSkill, { recursive: true });
141
+ copyDirSync(srcSkill, destSkill);
126
142
  appendLog(logFile, "SKILL", `${dir} (${version}) → installed`);
127
143
  results.push({ name: dir, version, action: "installed" });
128
144
  } else {
@@ -132,9 +148,9 @@ function installSkills(skillsSource, skillsDest, logFile) {
132
148
  if (needUpdate) {
133
149
  const bakName = `${dir}.bak.${backupTimestamp()}`;
134
150
  const bakPath = path.join(skillsDest, bakName);
135
- fs.cpSync(destSkill, bakPath, { recursive: true });
151
+ copyDirSync(destSkill, bakPath);
136
152
  fs.rmSync(destSkill, { recursive: true, force: true });
137
- fs.cpSync(srcSkill, destSkill, { recursive: true });
153
+ copyDirSync(srcSkill, destSkill);
138
154
  appendLog(logFile, "SKILL", `${dir} (${destMeta.version} → ${meta.version}) → updated`);
139
155
  results.push({ name: dir, version: meta.version, action: "updated" });
140
156
  } else {
@@ -230,6 +246,7 @@ module.exports = {
230
246
  backupTimestamp,
231
247
  appendLog,
232
248
  backupDir,
249
+ copyDirSync,
233
250
  deepMerge,
234
251
  readMeta,
235
252
  installSkills,