dk-frontend-skills 2.1.3 → 3.0.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.
- package/package.json +11 -5
- package/scripts/build-skills.js +89 -0
- package/scripts/cli.js +99 -37
- package/scripts/copy-skills.js +13 -20
- package/scripts/core.js +53 -59
- package/skills-index.json +99 -0
- package/.claude/skills/agentation/.meta.json +0 -6
- package/.claude/skills/agentation/SKILL.md +0 -107
- package/.claude/skills/fe-biz-patterns/.meta.json +0 -6
- package/.claude/skills/fe-biz-patterns/SKILL.md +0 -26
- package/.claude/skills/fe-biz-patterns/references/infinite-scroll.md +0 -292
- package/.claude/skills/fe-biz-patterns/references/pinia-store.md +0 -174
- package/.claude/skills/fe-biz-patterns/references/service-layer.md +0 -198
- package/.claude/skills/fe-biz-patterns/references/tab-anchor.md +0 -1125
- package/.claude/skills/fe-biz-patterns/references/use-loading.md +0 -114
- package/.claude/skills/frontend-code-review/.meta.json +0 -6
- package/.claude/skills/frontend-code-review/SKILL.md +0 -167
- package/.claude/skills/frontend-code-review/references/checklist.md +0 -298
- package/.claude/skills/frontend-design/.meta.json +0 -6
- package/.claude/skills/frontend-design/LICENSE.txt +0 -177
- package/.claude/skills/frontend-design/SKILL.md +0 -42
- package/.claude/skills/moai-framework-electron/.meta.json +0 -6
- package/.claude/skills/moai-framework-electron/SKILL.md +0 -328
- package/.claude/skills/skill-creator/.meta.json +0 -6
- package/.claude/skills/skill-creator/SKILL.md +0 -356
- package/.claude/skills/skill-creator/references/output-patterns.md +0 -82
- package/.claude/skills/skill-creator/references/workflows.md +0 -28
- package/.claude/skills/skill-creator/scripts/init_skill.py +0 -303
- package/.claude/skills/skill-creator/scripts/package_skill.py +0 -110
- package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -95
- package/.claude/skills/ui-ux-pro-max/.meta.json +0 -6
- package/.claude/skills/ui-ux-pro-max/SKILL.md +0 -228
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +0 -26
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +0 -97
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +0 -31
- package/.claude/skills/ui-ux-pro-max/data/products.csv +0 -97
- package/.claude/skills/ui-ux-pro-max/data/prompts.csv +0 -24
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +0 -59
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +0 -58
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +0 -238
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +0 -61
- package/.claude/skills/vue/.meta.json +0 -6
- package/.claude/skills/vue/SKILL.md +0 -103
- package/.claude/skills/vue/references/components.md +0 -323
- package/.claude/skills/vue/references/composables.md +0 -358
- package/.claude/skills/vue/references/directives.md +0 -225
- package/.claude/skills/vue/references/gotchas.md +0 -438
- package/.claude/skills/vue/references/provide-inject.md +0 -174
- package/.claude/skills/vue/references/reactivity.md +0 -289
- package/.claude/skills/vue/references/router.md +0 -181
- package/.claude/skills/vue/references/testing.md +0 -294
- package/.claude/skills/vue/references/typescript.md +0 -172
- package/.claude/skills/vue/references/utils-client.md +0 -156
package/package.json
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dk-frontend-skills",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "dk-engineer - 幽默沉稳靠谱的前端开发助手 Claude Skills 配置包,一键注入 .claude/ 技能目录和 CLAUDE.md 人设配置",
|
|
5
5
|
"author": "XiaoMa",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"private": false,
|
|
8
8
|
"files": [
|
|
9
|
-
".claude/",
|
|
10
9
|
"CLAUDE.md",
|
|
11
|
-
"scripts/"
|
|
10
|
+
"scripts/",
|
|
11
|
+
"skills-index.json",
|
|
12
|
+
".claude/settings.json",
|
|
13
|
+
".claude/settings.local.json"
|
|
12
14
|
],
|
|
13
15
|
"bin": {
|
|
14
|
-
"dk-skills": "./scripts/cli.js"
|
|
16
|
+
"dk-skills": "./scripts/cli.js",
|
|
17
|
+
"dk-frontend-skills": "./scripts/cli.js"
|
|
15
18
|
},
|
|
16
19
|
"scripts": {
|
|
20
|
+
"build-skills": "node scripts/build-skills.js",
|
|
17
21
|
"postinstall": "node scripts/copy-skills.js"
|
|
18
22
|
},
|
|
19
23
|
"dependencies": {
|
|
20
24
|
"@inquirer/prompts": "^8.4.2",
|
|
21
|
-
"chalk": "^5.6.2"
|
|
25
|
+
"chalk": "^5.6.2",
|
|
26
|
+
"cli-progress": "^3.12.0",
|
|
27
|
+
"tar": "^7.5.15"
|
|
22
28
|
}
|
|
23
29
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 技能打包脚本
|
|
5
|
+
* 将 skills/<name>/ 目录打包成 dist/skills/<name>-<version>.tar.gz
|
|
6
|
+
* 并更新 skills-index.json 中的版本和文件大小
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const tar = require("tar");
|
|
12
|
+
const { execSync } = require("child_process");
|
|
13
|
+
|
|
14
|
+
const repoRoot = path.join(__dirname, "..");
|
|
15
|
+
const skillsDir = path.join(repoRoot, "skills");
|
|
16
|
+
const distDir = path.join(repoRoot, "dist", "skills");
|
|
17
|
+
const indexPath = path.join(repoRoot, "skills-index.json");
|
|
18
|
+
|
|
19
|
+
async function build() {
|
|
20
|
+
// 读取已有的索引
|
|
21
|
+
const index = JSON.parse(fs.readFileSync(indexPath, "utf-8"));
|
|
22
|
+
const indexSkills = index.skills;
|
|
23
|
+
|
|
24
|
+
// 确保输出目录存在
|
|
25
|
+
fs.mkdirSync(distDir, { recursive: true });
|
|
26
|
+
|
|
27
|
+
const skillNames = fs.readdirSync(skillsDir);
|
|
28
|
+
|
|
29
|
+
for (const name of skillNames) {
|
|
30
|
+
const skillPath = path.join(skillsDir, name);
|
|
31
|
+
if (!fs.statSync(skillPath).isDirectory()) continue;
|
|
32
|
+
|
|
33
|
+
// 读取 .meta.json 获取版本
|
|
34
|
+
const metaPath = path.join(skillPath, ".meta.json");
|
|
35
|
+
if (!fs.existsSync(metaPath)) {
|
|
36
|
+
console.log(` ⚠️ ${name}: 缺少 .meta.json,跳过`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
41
|
+
const version = meta.version || "0.0.0";
|
|
42
|
+
const fileName = `${name}-${version}.tar.gz`;
|
|
43
|
+
const outputPath = path.join(distDir, fileName);
|
|
44
|
+
|
|
45
|
+
console.log(` 📦 ${name} (${version}) → ${fileName}`);
|
|
46
|
+
|
|
47
|
+
// 打包:进入 skills/ 目录,只打包当前技能子目录
|
|
48
|
+
await tar.c(
|
|
49
|
+
{
|
|
50
|
+
gzip: true,
|
|
51
|
+
file: outputPath,
|
|
52
|
+
cwd: skillsDir,
|
|
53
|
+
portable: true,
|
|
54
|
+
},
|
|
55
|
+
[name],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// 获取文件大小
|
|
59
|
+
const stat = fs.statSync(outputPath);
|
|
60
|
+
|
|
61
|
+
// 更新索引
|
|
62
|
+
if (indexSkills[name]) {
|
|
63
|
+
indexSkills[name].version = version;
|
|
64
|
+
indexSkills[name].fileName = fileName;
|
|
65
|
+
indexSkills[name].size = stat.size;
|
|
66
|
+
} else {
|
|
67
|
+
indexSkills[name] = {
|
|
68
|
+
version,
|
|
69
|
+
description: meta.description || "",
|
|
70
|
+
fileName,
|
|
71
|
+
tags: meta.tags || [],
|
|
72
|
+
size: stat.size,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 更新索引的更新时间
|
|
78
|
+
index.updatedAt = new Date().toISOString().slice(0, 10);
|
|
79
|
+
|
|
80
|
+
// 写回索引
|
|
81
|
+
fs.writeFileSync(indexPath, JSON.stringify(index, null, 2) + "\n", "utf-8");
|
|
82
|
+
console.log(`\n ✅ skills-index.json 已更新`);
|
|
83
|
+
console.log(` 📂 压缩包输出目录: dist/skills/\n`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
build().catch((err) => {
|
|
87
|
+
console.error("打包失败:", err);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
package/scripts/cli.js
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const
|
|
5
|
+
const tar = require("tar");
|
|
6
|
+
const {
|
|
7
|
+
getPackageSkills,
|
|
8
|
+
getUserSkills,
|
|
9
|
+
readSkillsIndex,
|
|
10
|
+
downloadFile,
|
|
11
|
+
} = require("./core");
|
|
6
12
|
|
|
7
13
|
// npx 运行时:process.cwd() 是用户项目目录
|
|
8
14
|
// __dirname 是包内 scripts/ 目录
|
|
@@ -18,7 +24,6 @@ const packageDir = path.join(__dirname, "..");
|
|
|
18
24
|
const claudeDest = path.join(projectRoot, ".claude");
|
|
19
25
|
const skillsDest = path.join(claudeDest, "skills");
|
|
20
26
|
const settingsPath = path.join(claudeDest, "settings.json");
|
|
21
|
-
const pkgSkillsSource = path.join(packageDir, ".claude", "skills");
|
|
22
27
|
|
|
23
28
|
// ---------- 文件写入 ----------
|
|
24
29
|
|
|
@@ -43,47 +48,110 @@ function writeSettings(settings) {
|
|
|
43
48
|
);
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
// ----------
|
|
51
|
+
// ---------- 远程下载与安装 ----------
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
async function downloadAndInstallSkill(name, index) {
|
|
54
|
+
const skillInfo = index.skills[name];
|
|
55
|
+
if (!skillInfo) {
|
|
56
|
+
console.error(` ❌ 技能 "${name}" 不在索引中`);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const destDir = path.join(skillsDest, name);
|
|
61
|
+
|
|
62
|
+
// 检查本地是否已安装且版本一致
|
|
63
|
+
const localMetaPath = path.join(destDir, ".meta.json");
|
|
64
|
+
if (fs.existsSync(localMetaPath)) {
|
|
65
|
+
try {
|
|
66
|
+
const localMeta = JSON.parse(fs.readFileSync(localMetaPath, "utf-8"));
|
|
67
|
+
if (localMeta.version === skillInfo.version) {
|
|
68
|
+
return true; // 已是最新,跳过
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// 元信息损坏,重新下载
|
|
58
72
|
}
|
|
59
73
|
}
|
|
74
|
+
|
|
75
|
+
// 构造下载地址
|
|
76
|
+
const url = `${index.baseUrl}/${skillInfo.fileName}`;
|
|
77
|
+
const tempDir = path.join(claudeDest, ".temp");
|
|
78
|
+
if (!fs.existsSync(tempDir)) {
|
|
79
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
const tempFile = path.join(tempDir, skillInfo.fileName);
|
|
82
|
+
|
|
83
|
+
// 下载带进度条
|
|
84
|
+
const { SingleBar, Presets } = await import("cli-progress");
|
|
85
|
+
const bar = new SingleBar(
|
|
86
|
+
{
|
|
87
|
+
format: ` 📥 ${name} |{bar}| {percentage}% | {value}/{total} bytes`,
|
|
88
|
+
barCompleteChar: "\u2588",
|
|
89
|
+
barIncompleteChar: "\u2591",
|
|
90
|
+
hideCursor: true,
|
|
91
|
+
},
|
|
92
|
+
Presets.shades_classic,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
console.log(` ⏬ 正在下载 ${name} (${skillInfo.version})...`);
|
|
97
|
+
bar.start(skillInfo.size || 100, 0);
|
|
98
|
+
await downloadFile(url, tempFile, (downloaded, total) => {
|
|
99
|
+
bar.setTotal(total || skillInfo.size || 100);
|
|
100
|
+
bar.update(downloaded);
|
|
101
|
+
});
|
|
102
|
+
bar.stop();
|
|
103
|
+
|
|
104
|
+
// 创建目标目录,解压
|
|
105
|
+
if (fs.existsSync(destDir)) {
|
|
106
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
109
|
+
|
|
110
|
+
await tar.x({
|
|
111
|
+
file: tempFile,
|
|
112
|
+
C: destDir,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// 清理临时文件
|
|
116
|
+
fs.rmSync(tempFile, { force: true });
|
|
117
|
+
|
|
118
|
+
console.log(` ✅ ${name} (${skillInfo.version}) 安装成功`);
|
|
119
|
+
return true;
|
|
120
|
+
} catch (err) {
|
|
121
|
+
bar.stop();
|
|
122
|
+
// 清理残留
|
|
123
|
+
if (fs.existsSync(tempFile)) fs.rmSync(tempFile, { force: true });
|
|
124
|
+
if (fs.existsSync(destDir))
|
|
125
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
126
|
+
console.error(` ❌ 下载 "${name}" 失败: ${err.message}`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
60
129
|
}
|
|
61
130
|
|
|
62
|
-
function installSelectedSkills(selectedNames) {
|
|
63
|
-
//
|
|
131
|
+
async function installSelectedSkills(selectedNames) {
|
|
132
|
+
// 创建目录
|
|
64
133
|
if (!fs.existsSync(skillsDest)) {
|
|
65
134
|
fs.mkdirSync(skillsDest, { recursive: true });
|
|
66
135
|
}
|
|
67
136
|
|
|
68
|
-
const
|
|
137
|
+
const index = readSkillsIndex(packageDir);
|
|
138
|
+
if (!index) {
|
|
139
|
+
console.error(" ❌ skills-index.json 读取失败");
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const indexSkills = index.skills;
|
|
69
144
|
let installedCount = 0;
|
|
70
145
|
|
|
71
|
-
for (const name of
|
|
72
|
-
const src = path.join(pkgSkillsSource, name);
|
|
146
|
+
for (const name of Object.keys(indexSkills)) {
|
|
73
147
|
const dest = path.join(skillsDest, name);
|
|
74
148
|
|
|
75
149
|
if (selectedNames.includes(name)) {
|
|
76
|
-
// 勾选了的 →
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
copyDirSync(src, dest);
|
|
80
|
-
installedCount++;
|
|
81
|
-
} catch (err) {
|
|
82
|
-
console.error(` ❌ 安装技能 "${name}" 失败:`, err.message);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
150
|
+
// 勾选了的 → 下载/更新
|
|
151
|
+
const ok = await downloadAndInstallSkill(name, index);
|
|
152
|
+
if (ok) installedCount++;
|
|
85
153
|
} else {
|
|
86
|
-
// 没勾选的 →
|
|
154
|
+
// 没勾选的 → 移除
|
|
87
155
|
if (fs.existsSync(dest)) {
|
|
88
156
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
89
157
|
}
|
|
@@ -94,7 +162,7 @@ function installSelectedSkills(selectedNames) {
|
|
|
94
162
|
const settings = readSettings();
|
|
95
163
|
if (!settings.skills) settings.skills = {};
|
|
96
164
|
|
|
97
|
-
for (const name of
|
|
165
|
+
for (const name of Object.keys(indexSkills)) {
|
|
98
166
|
const enabled = selectedNames.includes(name);
|
|
99
167
|
if (!settings.skills[name]) {
|
|
100
168
|
settings.skills[name] = {};
|
|
@@ -109,7 +177,7 @@ function installSelectedSkills(selectedNames) {
|
|
|
109
177
|
const mdDest = path.join(projectRoot, "CLAUDE.md");
|
|
110
178
|
if (fs.existsSync(mdSource) && !fs.existsSync(mdDest)) {
|
|
111
179
|
fs.copyFileSync(mdSource, mdDest);
|
|
112
|
-
console.log(" 📝 CLAUDE.md
|
|
180
|
+
console.log(" 📝 CLAUDE.md 已创建");
|
|
113
181
|
}
|
|
114
182
|
|
|
115
183
|
return installedCount;
|
|
@@ -164,12 +232,6 @@ async function cmdList() {
|
|
|
164
232
|
|
|
165
233
|
async function startInteractiveMenu() {
|
|
166
234
|
const { checkbox } = await import("@inquirer/prompts");
|
|
167
|
-
|
|
168
|
-
if (!fs.existsSync(pkgSkillsSource)) {
|
|
169
|
-
console.log(" ⚠️ 包内没有找到技能文件\n");
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
235
|
const pkgSkills = getPackageSkills(packageDir);
|
|
174
236
|
|
|
175
237
|
if (pkgSkills.length === 0) {
|
|
@@ -210,7 +272,7 @@ async function startInteractiveMenu() {
|
|
|
210
272
|
(name) => !selected.includes(name),
|
|
211
273
|
).length;
|
|
212
274
|
|
|
213
|
-
const installed = installSelectedSkills(selected);
|
|
275
|
+
const installed = await installSelectedSkills(selected);
|
|
214
276
|
|
|
215
277
|
const parts = [];
|
|
216
278
|
if (newCount > 0) parts.push(`新装 ${newCount} 个`);
|
package/scripts/copy-skills.js
CHANGED
|
@@ -3,9 +3,6 @@ const path = require("path");
|
|
|
3
3
|
const {
|
|
4
4
|
backupTimestamp,
|
|
5
5
|
appendLog,
|
|
6
|
-
backupDir,
|
|
7
|
-
copyDirSync,
|
|
8
|
-
installSkills,
|
|
9
6
|
installSettings,
|
|
10
7
|
} = require("./core");
|
|
11
8
|
|
|
@@ -30,25 +27,21 @@ const logFile = path.join(claudeDest, ".install.log");
|
|
|
30
27
|
|
|
31
28
|
console.log("\n📦 dk-frontend-skills 技能包安装开始\n");
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const skillsSource = path.join(claudeSource, "skills");
|
|
40
|
-
const skillsDest = path.join(claudeDest, "skills");
|
|
30
|
+
// 确保 .claude/ 目录存在
|
|
31
|
+
if (!fs.existsSync(claudeDest)) {
|
|
32
|
+
fs.mkdirSync(claudeDest, { recursive: true });
|
|
33
|
+
appendLog(logFile, "INFO", `.claude/ directory created`);
|
|
34
|
+
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
appendLog(logFile, "INFO", "Install started (fresh)");
|
|
47
|
-
copyDirSync(claudeSource, claudeDest);
|
|
48
|
-
appendLog(logFile, "INFO", `.claude/ directory created`);
|
|
49
|
-
}
|
|
36
|
+
// 确保 .claude/skills/ 目录存在(不覆盖已有技能)
|
|
37
|
+
const skillsDest = path.join(claudeDest, "skills");
|
|
38
|
+
if (!fs.existsSync(skillsDest)) {
|
|
39
|
+
fs.mkdirSync(skillsDest, { recursive: true });
|
|
50
40
|
}
|
|
51
41
|
|
|
42
|
+
// 安装 settings.json(深度合并)
|
|
43
|
+
installSettings(claudeSource, claudeDest, logFile);
|
|
44
|
+
|
|
52
45
|
// 安装 CLAUDE.md
|
|
53
46
|
if (fs.existsSync(mdSource)) {
|
|
54
47
|
if (!fs.existsSync(mdDest)) {
|
|
@@ -83,4 +76,4 @@ if (fs.existsSync(logFile)) {
|
|
|
83
76
|
console.log("");
|
|
84
77
|
}
|
|
85
78
|
|
|
86
|
-
console.log("💡 提示:运行 npx dk-skills
|
|
79
|
+
console.log("💡 提示:运行 npx dk-skills 可交互选择安装/启用/禁用技能\n");
|
package/scripts/core.js
CHANGED
|
@@ -118,51 +118,6 @@ function readMeta(skillDir) {
|
|
|
118
118
|
return null;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
/**
|
|
122
|
-
* 安装技能:逐个复制,补缺不覆盖
|
|
123
|
-
*/
|
|
124
|
-
function installSkills(skillsSource, skillsDest, logFile) {
|
|
125
|
-
if (!fs.existsSync(skillsSource)) return [];
|
|
126
|
-
|
|
127
|
-
if (!fs.existsSync(skillsDest)) {
|
|
128
|
-
fs.mkdirSync(skillsDest, { recursive: true });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const skillDirs = fs.readdirSync(skillsSource);
|
|
132
|
-
const results = [];
|
|
133
|
-
|
|
134
|
-
for (const dir of skillDirs) {
|
|
135
|
-
const srcSkill = path.join(skillsSource, dir);
|
|
136
|
-
const destSkill = path.join(skillsDest, dir);
|
|
137
|
-
const meta = readMeta(srcSkill);
|
|
138
|
-
const version = meta ? meta.version : "?";
|
|
139
|
-
|
|
140
|
-
if (!fs.existsSync(destSkill)) {
|
|
141
|
-
copyDirSync(srcSkill, destSkill);
|
|
142
|
-
appendLog(logFile, "SKILL", `${dir} (${version}) → installed`);
|
|
143
|
-
results.push({ name: dir, version, action: "installed" });
|
|
144
|
-
} else {
|
|
145
|
-
const destMeta = readMeta(destSkill);
|
|
146
|
-
const needUpdate = meta && destMeta && meta.version !== destMeta.version;
|
|
147
|
-
|
|
148
|
-
if (needUpdate) {
|
|
149
|
-
const bakName = `${dir}.bak.${backupTimestamp()}`;
|
|
150
|
-
const bakPath = path.join(skillsDest, bakName);
|
|
151
|
-
copyDirSync(destSkill, bakPath);
|
|
152
|
-
fs.rmSync(destSkill, { recursive: true, force: true });
|
|
153
|
-
copyDirSync(srcSkill, destSkill);
|
|
154
|
-
appendLog(logFile, "SKILL", `${dir} (${destMeta.version} → ${meta.version}) → updated`);
|
|
155
|
-
results.push({ name: dir, version: meta.version, action: "updated" });
|
|
156
|
-
} else {
|
|
157
|
-
appendLog(logFile, "SKILL", `${dir} (${version}) → skipped (exists)`);
|
|
158
|
-
results.push({ name: dir, version, action: "skipped" });
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return results;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
121
|
/**
|
|
167
122
|
* 安装 settings.json,深度合并
|
|
168
123
|
*/
|
|
@@ -192,21 +147,31 @@ function installSettings(sourceDir, destDir, logFile) {
|
|
|
192
147
|
}
|
|
193
148
|
|
|
194
149
|
/**
|
|
195
|
-
*
|
|
150
|
+
* 读取 skills-index.json 获取包内技能列表
|
|
196
151
|
*/
|
|
197
|
-
function
|
|
198
|
-
const
|
|
199
|
-
if (!fs.existsSync(
|
|
152
|
+
function readSkillsIndex(packageDir) {
|
|
153
|
+
const indexPath = path.join(packageDir, "skills-index.json");
|
|
154
|
+
if (!fs.existsSync(indexPath)) return null;
|
|
155
|
+
try {
|
|
156
|
+
return JSON.parse(fs.readFileSync(indexPath, "utf-8"));
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
200
161
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
162
|
+
/**
|
|
163
|
+
* 获取包的技能列表(从 skills-index.json 读取)
|
|
164
|
+
*/
|
|
165
|
+
function getPackageSkills(packageDir) {
|
|
166
|
+
const index = readSkillsIndex(packageDir);
|
|
167
|
+
if (!index) return [];
|
|
168
|
+
|
|
169
|
+
return Object.entries(index.skills).map(([name, info]) => ({
|
|
170
|
+
name,
|
|
171
|
+
version: info.version,
|
|
172
|
+
description: info.description || "",
|
|
173
|
+
tags: info.tags || [],
|
|
174
|
+
}));
|
|
210
175
|
}
|
|
211
176
|
|
|
212
177
|
/**
|
|
@@ -241,6 +206,34 @@ function getUserSkills(claudeDest) {
|
|
|
241
206
|
});
|
|
242
207
|
}
|
|
243
208
|
|
|
209
|
+
/**
|
|
210
|
+
* 从远程下载文件到本地临时路径
|
|
211
|
+
*/
|
|
212
|
+
async function downloadFile(url, destPath, onProgress) {
|
|
213
|
+
const response = await fetch(url);
|
|
214
|
+
if (!response.ok) {
|
|
215
|
+
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const total = parseInt(response.headers.get("content-length") || "0", 10);
|
|
219
|
+
let downloaded = 0;
|
|
220
|
+
|
|
221
|
+
const reader = response.body.getReader();
|
|
222
|
+
const writer = fs.createWriteStream(destPath);
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
while (true) {
|
|
226
|
+
const { done, value } = await reader.read();
|
|
227
|
+
if (done) break;
|
|
228
|
+
downloaded += value.length;
|
|
229
|
+
if (onProgress) onProgress(downloaded, total);
|
|
230
|
+
writer.write(value);
|
|
231
|
+
}
|
|
232
|
+
} finally {
|
|
233
|
+
writer.close();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
244
237
|
module.exports = {
|
|
245
238
|
timestamp,
|
|
246
239
|
backupTimestamp,
|
|
@@ -249,8 +242,9 @@ module.exports = {
|
|
|
249
242
|
copyDirSync,
|
|
250
243
|
deepMerge,
|
|
251
244
|
readMeta,
|
|
252
|
-
installSkills,
|
|
253
245
|
installSettings,
|
|
246
|
+
readSkillsIndex,
|
|
254
247
|
getPackageSkills,
|
|
255
248
|
getUserSkills,
|
|
249
|
+
downloadFile,
|
|
256
250
|
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "3.0.0",
|
|
3
|
+
"updatedAt": "2026-05-09",
|
|
4
|
+
"baseUrl": "https://github.com/Milo-K0/dk-frontend-skills/releases/download/skills",
|
|
5
|
+
"skills": {
|
|
6
|
+
"agentation": {
|
|
7
|
+
"version": "1.0.0",
|
|
8
|
+
"description": "Agentation React 可视化反馈工具栏",
|
|
9
|
+
"fileName": "agentation-1.0.0.tar.gz",
|
|
10
|
+
"tags": [
|
|
11
|
+
"react",
|
|
12
|
+
"ui",
|
|
13
|
+
"debug"
|
|
14
|
+
],
|
|
15
|
+
"size": 1374
|
|
16
|
+
},
|
|
17
|
+
"fe-biz-patterns": {
|
|
18
|
+
"version": "1.0.0",
|
|
19
|
+
"description": "小马自己的前端业务模式库,收录小马在前端业务中沉淀的方案和最佳实践",
|
|
20
|
+
"fileName": "fe-biz-patterns-1.0.0.tar.gz",
|
|
21
|
+
"tags": [
|
|
22
|
+
"frontend",
|
|
23
|
+
"pattern",
|
|
24
|
+
"vue",
|
|
25
|
+
"react"
|
|
26
|
+
],
|
|
27
|
+
"size": 18175
|
|
28
|
+
},
|
|
29
|
+
"frontend-code-review": {
|
|
30
|
+
"version": "1.0.0",
|
|
31
|
+
"description": "前端代码全面审查,含 React/Vue/Angular/原生 JS/TS 项目",
|
|
32
|
+
"fileName": "frontend-code-review-1.0.0.tar.gz",
|
|
33
|
+
"tags": [
|
|
34
|
+
"frontend",
|
|
35
|
+
"review",
|
|
36
|
+
"react",
|
|
37
|
+
"vue",
|
|
38
|
+
"angular"
|
|
39
|
+
],
|
|
40
|
+
"size": 5976
|
|
41
|
+
},
|
|
42
|
+
"frontend-design": {
|
|
43
|
+
"version": "1.0.0",
|
|
44
|
+
"description": "创意前端界面设计,落地页/品牌页/营销页/Html/CSS",
|
|
45
|
+
"fileName": "frontend-design-1.0.0.tar.gz",
|
|
46
|
+
"tags": [
|
|
47
|
+
"frontend",
|
|
48
|
+
"design",
|
|
49
|
+
"ui",
|
|
50
|
+
"css"
|
|
51
|
+
],
|
|
52
|
+
"size": 5965
|
|
53
|
+
},
|
|
54
|
+
"moai-framework-electron": {
|
|
55
|
+
"version": "1.0.0",
|
|
56
|
+
"description": "Electron 桌面应用开发,含主进程/渲染进程/IPC/自动更新/打包",
|
|
57
|
+
"fileName": "moai-framework-electron-1.0.0.tar.gz",
|
|
58
|
+
"tags": [
|
|
59
|
+
"electron",
|
|
60
|
+
"desktop",
|
|
61
|
+
"node"
|
|
62
|
+
],
|
|
63
|
+
"size": 6015
|
|
64
|
+
},
|
|
65
|
+
"skill-creator": {
|
|
66
|
+
"version": "1.0.0",
|
|
67
|
+
"description": "创建和更新技能的指导工具",
|
|
68
|
+
"fileName": "skill-creator-1.0.0.tar.gz",
|
|
69
|
+
"tags": [
|
|
70
|
+
"meta",
|
|
71
|
+
"tool"
|
|
72
|
+
],
|
|
73
|
+
"size": 12936
|
|
74
|
+
},
|
|
75
|
+
"ui-ux-pro-max": {
|
|
76
|
+
"version": "1.0.0",
|
|
77
|
+
"description": "UI/UX 设计智能系统,50 种样式,21 种配色方案,50 种字体搭配",
|
|
78
|
+
"fileName": "ui-ux-pro-max-1.0.0.tar.gz",
|
|
79
|
+
"tags": [
|
|
80
|
+
"design",
|
|
81
|
+
"ui",
|
|
82
|
+
"ux",
|
|
83
|
+
"style"
|
|
84
|
+
],
|
|
85
|
+
"size": 99673
|
|
86
|
+
},
|
|
87
|
+
"vue": {
|
|
88
|
+
"version": "1.0.0",
|
|
89
|
+
"description": "Vue 3 组件/组合式函数开发,Composition API 最佳实践",
|
|
90
|
+
"fileName": "vue-1.0.0.tar.gz",
|
|
91
|
+
"tags": [
|
|
92
|
+
"vue",
|
|
93
|
+
"frontend",
|
|
94
|
+
"composition-api"
|
|
95
|
+
],
|
|
96
|
+
"size": 20091
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|