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.
Files changed (64) hide show
  1. package/package.json +11 -5
  2. package/scripts/build-skills.js +89 -0
  3. package/scripts/cli.js +99 -37
  4. package/scripts/copy-skills.js +13 -20
  5. package/scripts/core.js +53 -59
  6. package/skills-index.json +99 -0
  7. package/.claude/skills/agentation/.meta.json +0 -6
  8. package/.claude/skills/agentation/SKILL.md +0 -107
  9. package/.claude/skills/fe-biz-patterns/.meta.json +0 -6
  10. package/.claude/skills/fe-biz-patterns/SKILL.md +0 -26
  11. package/.claude/skills/fe-biz-patterns/references/infinite-scroll.md +0 -292
  12. package/.claude/skills/fe-biz-patterns/references/pinia-store.md +0 -174
  13. package/.claude/skills/fe-biz-patterns/references/service-layer.md +0 -198
  14. package/.claude/skills/fe-biz-patterns/references/tab-anchor.md +0 -1125
  15. package/.claude/skills/fe-biz-patterns/references/use-loading.md +0 -114
  16. package/.claude/skills/frontend-code-review/.meta.json +0 -6
  17. package/.claude/skills/frontend-code-review/SKILL.md +0 -167
  18. package/.claude/skills/frontend-code-review/references/checklist.md +0 -298
  19. package/.claude/skills/frontend-design/.meta.json +0 -6
  20. package/.claude/skills/frontend-design/LICENSE.txt +0 -177
  21. package/.claude/skills/frontend-design/SKILL.md +0 -42
  22. package/.claude/skills/moai-framework-electron/.meta.json +0 -6
  23. package/.claude/skills/moai-framework-electron/SKILL.md +0 -328
  24. package/.claude/skills/skill-creator/.meta.json +0 -6
  25. package/.claude/skills/skill-creator/SKILL.md +0 -356
  26. package/.claude/skills/skill-creator/references/output-patterns.md +0 -82
  27. package/.claude/skills/skill-creator/references/workflows.md +0 -28
  28. package/.claude/skills/skill-creator/scripts/init_skill.py +0 -303
  29. package/.claude/skills/skill-creator/scripts/package_skill.py +0 -110
  30. package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -95
  31. package/.claude/skills/ui-ux-pro-max/.meta.json +0 -6
  32. package/.claude/skills/ui-ux-pro-max/SKILL.md +0 -228
  33. package/.claude/skills/ui-ux-pro-max/data/charts.csv +0 -26
  34. package/.claude/skills/ui-ux-pro-max/data/colors.csv +0 -97
  35. package/.claude/skills/ui-ux-pro-max/data/landing.csv +0 -31
  36. package/.claude/skills/ui-ux-pro-max/data/products.csv +0 -97
  37. package/.claude/skills/ui-ux-pro-max/data/prompts.csv +0 -24
  38. package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  39. package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  40. package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  41. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  42. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  43. package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  44. package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  45. package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  46. package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  47. package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  48. package/.claude/skills/ui-ux-pro-max/data/styles.csv +0 -59
  49. package/.claude/skills/ui-ux-pro-max/data/typography.csv +0 -58
  50. package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  51. package/.claude/skills/ui-ux-pro-max/scripts/core.py +0 -238
  52. package/.claude/skills/ui-ux-pro-max/scripts/search.py +0 -61
  53. package/.claude/skills/vue/.meta.json +0 -6
  54. package/.claude/skills/vue/SKILL.md +0 -103
  55. package/.claude/skills/vue/references/components.md +0 -323
  56. package/.claude/skills/vue/references/composables.md +0 -358
  57. package/.claude/skills/vue/references/directives.md +0 -225
  58. package/.claude/skills/vue/references/gotchas.md +0 -438
  59. package/.claude/skills/vue/references/provide-inject.md +0 -174
  60. package/.claude/skills/vue/references/reactivity.md +0 -289
  61. package/.claude/skills/vue/references/router.md +0 -181
  62. package/.claude/skills/vue/references/testing.md +0 -294
  63. package/.claude/skills/vue/references/typescript.md +0 -172
  64. 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": "2.1.3",
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 { getPackageSkills, getUserSkills } = require("./core");
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
- // 手动递归拷贝目录,绕开 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);
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
- // 创建 .claude/ 和 skills/ 目录
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 allPkgSkills = fs.readdirSync(pkgSkillsSource);
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 allPkgSkills) {
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
- if (!fs.existsSync(dest)) {
78
- try {
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 allPkgSkills) {
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 已创建\n");
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} 个`);
@@ -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
- if (fs.existsSync(claudeSource)) {
34
- if (fs.existsSync(claudeDest)) {
35
- // .claude 已存在 备份后再操作
36
- appendLog(logFile, "INFO", "Install started (upgrade)");
37
- backupDir(claudeDest);
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
- installSkills(skillsSource, skillsDest, logFile);
43
- installSettings(claudeSource, claudeDest, logFile);
44
- } else {
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 可交互选择启用/禁用技能\n");
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 getPackageSkills(packageDir) {
198
- const skillsSource = path.join(packageDir, ".claude", "skills");
199
- if (!fs.existsSync(skillsSource)) return [];
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
- return fs.readdirSync(skillsSource).map((name) => {
202
- const meta = readMeta(path.join(skillsSource, name));
203
- return {
204
- name,
205
- version: meta ? meta.version : "?",
206
- description: meta ? meta.description : "",
207
- tags: meta ? meta.tags : [],
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
+ }
@@ -1,6 +0,0 @@
1
- {
2
- "name": "agentation",
3
- "version": "1.0.0",
4
- "description": "Agentation React 可视化反馈工具栏",
5
- "tags": ["react", "ui", "debug"]
6
- }