dk-frontend-skills 2.1.1 → 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 +1 -1
- package/scripts/cli.js +51 -13
- package/scripts/copy-skills.js +10 -3
- package/scripts/core.js +21 -4
package/package.json
CHANGED
package/scripts/cli.js
CHANGED
|
@@ -6,7 +6,14 @@ const { getPackageSkills, getUserSkills } = require("./core");
|
|
|
6
6
|
|
|
7
7
|
// npx 运行时:process.cwd() 是用户项目目录
|
|
8
8
|
// __dirname 是包内 scripts/ 目录
|
|
9
|
-
|
|
9
|
+
// 用 fs.realpathSync.native() 归一化 Windows 路径,处理中文/Unicode 编码问题
|
|
10
|
+
const projectRoot = (() => {
|
|
11
|
+
try {
|
|
12
|
+
return fs.realpathSync.native(process.cwd());
|
|
13
|
+
} catch {
|
|
14
|
+
return process.cwd();
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
10
17
|
const packageDir = path.join(__dirname, "..");
|
|
11
18
|
const claudeDest = path.join(projectRoot, ".claude");
|
|
12
19
|
const skillsDest = path.join(claudeDest, "skills");
|
|
@@ -16,7 +23,8 @@ const pkgSkillsSource = path.join(packageDir, ".claude", "skills");
|
|
|
16
23
|
// ---------- 文件写入 ----------
|
|
17
24
|
|
|
18
25
|
function readSettings() {
|
|
19
|
-
if (!fs.existsSync(settingsPath))
|
|
26
|
+
if (!fs.existsSync(settingsPath))
|
|
27
|
+
return { skills: {}, always_apply_skills: [] };
|
|
20
28
|
try {
|
|
21
29
|
return JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
22
30
|
} catch {
|
|
@@ -28,11 +36,29 @@ function writeSettings(settings) {
|
|
|
28
36
|
if (!fs.existsSync(claudeDest)) {
|
|
29
37
|
fs.mkdirSync(claudeDest, { recursive: true });
|
|
30
38
|
}
|
|
31
|
-
fs.writeFileSync(
|
|
39
|
+
fs.writeFileSync(
|
|
40
|
+
settingsPath,
|
|
41
|
+
JSON.stringify(settings, null, 2) + "\n",
|
|
42
|
+
"utf-8",
|
|
43
|
+
);
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
// ---------- 安装勾选的技能 ----------
|
|
35
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
|
+
|
|
36
62
|
function installSelectedSkills(selectedNames) {
|
|
37
63
|
// 创建 .claude/ 和 skills/ 目录
|
|
38
64
|
if (!fs.existsSync(skillsDest)) {
|
|
@@ -49,8 +75,12 @@ function installSelectedSkills(selectedNames) {
|
|
|
49
75
|
if (selectedNames.includes(name)) {
|
|
50
76
|
// 勾选了的 → 安装(不存在才装)
|
|
51
77
|
if (!fs.existsSync(dest)) {
|
|
52
|
-
|
|
53
|
-
|
|
78
|
+
try {
|
|
79
|
+
copyDirSync(src, dest);
|
|
80
|
+
installedCount++;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.error(` ❌ 安装技能 "${name}" 失败:`, err.message);
|
|
83
|
+
}
|
|
54
84
|
}
|
|
55
85
|
} else {
|
|
56
86
|
// 没勾选的 → 如果之前装了,删掉
|
|
@@ -108,13 +138,15 @@ async function cmdList() {
|
|
|
108
138
|
const allSkills = [...skillMap.values()];
|
|
109
139
|
|
|
110
140
|
console.log(`\n${chalk.bold("📋 dk-frontend-skills 技能清单")}\n`);
|
|
111
|
-
console.log(
|
|
112
|
-
|
|
141
|
+
console.log(
|
|
142
|
+
` ${chalk.dim("状态 技能名 版本 描述")}`,
|
|
143
|
+
);
|
|
144
|
+
console.log(
|
|
145
|
+
` ${chalk.dim("─────────────────────────────────────────────────────")}`,
|
|
146
|
+
);
|
|
113
147
|
|
|
114
148
|
for (const skill of allSkills) {
|
|
115
|
-
const status = skill.enabled
|
|
116
|
-
? chalk.green("● 启用")
|
|
117
|
-
: chalk.gray("○ 停用");
|
|
149
|
+
const status = skill.enabled ? chalk.green("● 启用") : chalk.gray("○ 停用");
|
|
118
150
|
const name = chalk.white(skill.name.padEnd(20));
|
|
119
151
|
const ver = chalk.dim(skill.version.padEnd(7));
|
|
120
152
|
const desc = skill.description
|
|
@@ -123,7 +155,9 @@ async function cmdList() {
|
|
|
123
155
|
console.log(` ${status} ${name} ${ver} ${desc}`);
|
|
124
156
|
}
|
|
125
157
|
|
|
126
|
-
console.log(
|
|
158
|
+
console.log(
|
|
159
|
+
`\n ${chalk.cyan("💡")} ${chalk.dim("运行 npx dk-frontend-skills 进入交互选择模式")}\n`,
|
|
160
|
+
);
|
|
127
161
|
}
|
|
128
162
|
|
|
129
163
|
// ---------- 交互式菜单(选完即装) ----------
|
|
@@ -172,7 +206,9 @@ async function startInteractiveMenu() {
|
|
|
172
206
|
});
|
|
173
207
|
|
|
174
208
|
const newCount = selected.filter((name) => !installedSet.has(name)).length;
|
|
175
|
-
const removedCount = [...installedSet].filter(
|
|
209
|
+
const removedCount = [...installedSet].filter(
|
|
210
|
+
(name) => !selected.includes(name),
|
|
211
|
+
).length;
|
|
176
212
|
|
|
177
213
|
const installed = installSelectedSkills(selected);
|
|
178
214
|
|
|
@@ -212,7 +248,9 @@ async function main() {
|
|
|
212
248
|
} else if (command === "list") {
|
|
213
249
|
await cmdList();
|
|
214
250
|
} else if (command === "enable" || command === "disable") {
|
|
215
|
-
console.log(
|
|
251
|
+
console.log(
|
|
252
|
+
` ⚠️ 该命令已废弃,请直接运行 npx dk-frontend-skills 使用交互菜单\n`,
|
|
253
|
+
);
|
|
216
254
|
} else {
|
|
217
255
|
console.log(` 未知命令: ${command}\n 可用: ${COMMANDS.join(", ")}\n`);
|
|
218
256
|
process.exit(1);
|
package/scripts/copy-skills.js
CHANGED
|
@@ -4,12 +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 爬三级更可靠
|
|
14
|
+
const projectRoot =
|
|
15
|
+
process.env.INIT_CWD || path.resolve(__dirname, "..", "..", "..");
|
|
13
16
|
const packageDir = path.join(__dirname, "..");
|
|
14
17
|
|
|
15
18
|
// 源路径
|
|
@@ -41,7 +44,7 @@ if (fs.existsSync(claudeSource)) {
|
|
|
41
44
|
} else {
|
|
42
45
|
// 全新安装
|
|
43
46
|
appendLog(logFile, "INFO", "Install started (fresh)");
|
|
44
|
-
|
|
47
|
+
copyDirSync(claudeSource, claudeDest);
|
|
45
48
|
appendLog(logFile, "INFO", `.claude/ directory created`);
|
|
46
49
|
}
|
|
47
50
|
}
|
|
@@ -59,7 +62,11 @@ if (fs.existsSync(mdSource)) {
|
|
|
59
62
|
const bakName = `CLAUDE.md.${backupTimestamp()}`;
|
|
60
63
|
const bakPath = path.join(backupsDir, bakName);
|
|
61
64
|
fs.copyFileSync(mdDest, bakPath);
|
|
62
|
-
appendLog(
|
|
65
|
+
appendLog(
|
|
66
|
+
logFile,
|
|
67
|
+
"BACKUP",
|
|
68
|
+
`backups/${bakName} (CLAUDE.md user version preserved)`,
|
|
69
|
+
);
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
151
|
+
copyDirSync(destSkill, bakPath);
|
|
136
152
|
fs.rmSync(destSkill, { recursive: true, force: true });
|
|
137
|
-
|
|
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,
|