dk-frontend-skills 2.0.1 → 2.1.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fe-biz-patterns",
3
3
  "version": "1.0.0",
4
- "description": "前端业务模式库,收录前端业务中沉淀的方案和最佳实践",
4
+ "description": "小马自己的前端业务模式库,收录小马在前端业务中沉淀的方案和最佳实践",
5
5
  "tags": ["frontend", "pattern", "vue", "react"]
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dk-frontend-skills",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "dk-engineer - 幽默沉稳靠谱的前端开发助手 Claude Skills 配置包,一键注入 .claude/ 技能目录和 CLAUDE.md 人设配置",
5
5
  "author": "XiaoMa",
6
6
  "license": "MIT",
package/scripts/cli.js CHANGED
@@ -4,13 +4,16 @@ const fs = require("fs");
4
4
  const path = require("path");
5
5
  const { getPackageSkills, getUserSkills } = require("./core");
6
6
 
7
- // 定位项目根目录和包目录
8
- const projectRoot = path.resolve(__dirname, "..", "..", "..");
7
+ // npx 运行时:process.cwd() 是用户项目目录
8
+ // __dirname 是包内 scripts/ 目录
9
+ const projectRoot = process.cwd();
9
10
  const packageDir = path.join(__dirname, "..");
10
11
  const claudeDest = path.join(projectRoot, ".claude");
12
+ const skillsDest = path.join(claudeDest, "skills");
11
13
  const settingsPath = path.join(claudeDest, "settings.json");
14
+ const pkgSkillsSource = path.join(packageDir, ".claude", "skills");
12
15
 
13
- // ---------- 辅助函数 ----------
16
+ // ---------- 文件写入 ----------
14
17
 
15
18
  function readSettings() {
16
19
  if (!fs.existsSync(settingsPath)) return { skills: {}, always_apply_skills: [] };
@@ -22,14 +25,47 @@ function readSettings() {
22
25
  }
23
26
 
24
27
  function writeSettings(settings) {
28
+ if (!fs.existsSync(claudeDest)) {
29
+ fs.mkdirSync(claudeDest, { recursive: true });
30
+ }
25
31
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
26
32
  }
27
33
 
28
- function saveSkillSelection(choices) {
34
+ // ---------- 安装勾选的技能 ----------
35
+
36
+ function installSelectedSkills(selectedNames) {
37
+ // 创建 .claude/ 和 skills/ 目录
38
+ if (!fs.existsSync(skillsDest)) {
39
+ fs.mkdirSync(skillsDest, { recursive: true });
40
+ }
41
+
42
+ const allPkgSkills = fs.readdirSync(pkgSkillsSource);
43
+ let installedCount = 0;
44
+
45
+ for (const name of allPkgSkills) {
46
+ const src = path.join(pkgSkillsSource, name);
47
+ const dest = path.join(skillsDest, name);
48
+
49
+ if (selectedNames.includes(name)) {
50
+ // 勾选了的 → 安装(不存在才装)
51
+ if (!fs.existsSync(dest)) {
52
+ fs.cpSync(src, dest, { recursive: true });
53
+ installedCount++;
54
+ }
55
+ } else {
56
+ // 没勾选的 → 如果之前装了,删掉
57
+ if (fs.existsSync(dest)) {
58
+ fs.rmSync(dest, { recursive: true, force: true });
59
+ }
60
+ }
61
+ }
62
+
63
+ // 生成 settings.json
29
64
  const settings = readSettings();
30
65
  if (!settings.skills) settings.skills = {};
31
66
 
32
- for (const { name, enabled } of choices) {
67
+ for (const name of allPkgSkills) {
68
+ const enabled = selectedNames.includes(name);
33
69
  if (!settings.skills[name]) {
34
70
  settings.skills[name] = {};
35
71
  }
@@ -37,11 +73,26 @@ function saveSkillSelection(choices) {
37
73
  }
38
74
 
39
75
  writeSettings(settings);
76
+
77
+ // 安装 CLAUDE.md(没有才装)
78
+ const mdSource = path.join(packageDir, "CLAUDE.md");
79
+ const mdDest = path.join(projectRoot, "CLAUDE.md");
80
+ if (fs.existsSync(mdSource) && !fs.existsSync(mdDest)) {
81
+ fs.copyFileSync(mdSource, mdDest);
82
+ console.log(" 📝 CLAUDE.md 已创建\n");
83
+ }
84
+
85
+ return installedCount;
40
86
  }
41
87
 
42
88
  // ---------- 命令:list ----------
43
89
 
44
90
  async function cmdList() {
91
+ if (!fs.existsSync(claudeDest)) {
92
+ console.log("\n ⚠️ 尚未安装技能,请先运行 npx dk-frontend-skills\n");
93
+ return;
94
+ }
95
+
45
96
  const chalk = (await import("chalk")).default;
46
97
  const pkgSkills = getPackageSkills(packageDir);
47
98
  const userSkills = getUserSkills(claudeDest);
@@ -72,52 +123,19 @@ async function cmdList() {
72
123
  console.log(` ${status} ${name} ${ver} ${desc}`);
73
124
  }
74
125
 
75
- console.log(`\n ${chalk.cyan("💡")} ${chalk.dim("运行 npx dk-skills 进入交互选择模式")}\n`);
126
+ console.log(`\n ${chalk.cyan("💡")} ${chalk.dim("运行 npx dk-frontend-skills 进入交互选择模式")}\n`);
76
127
  }
77
128
 
78
- // ---------- 命令:enable / disable ----------
79
-
80
- function cmdToggle(enable, names) {
81
- if (names.length === 0) {
82
- console.log(` 用法: npx dk-skills ${enable ? "enable" : "disable"} <技能名...>`);
83
- return;
84
- }
85
-
86
- const pkgSkills = getPackageSkills(packageDir);
87
- const pkgNames = new Set(pkgSkills.map((s) => s.name));
88
- const userSkills = getUserSkills(claudeDest);
89
- const userNames = new Set(userSkills.map((s) => s.name));
129
+ // ---------- 交互式菜单(选完即装) ----------
90
130
 
91
- const notFound = names.filter((n) => !pkgNames.has(n));
92
- if (notFound.length > 0) {
93
- console.log(` ⚠️ 未知技能: ${notFound.join(", ")}`);
94
- console.log(` 可用: ${[...pkgNames].join(", ")}\n`);
95
- return;
96
- }
131
+ async function startInteractiveMenu() {
132
+ const { checkbox } = await import("@inquirer/prompts");
97
133
 
98
- const notInstalled = names.filter((n) => !userNames.has(n));
99
- if (notInstalled.length > 0) {
100
- console.log(` ⚠️ 以下技能尚未安装: ${notInstalled.join(", ")}`);
101
- console.log(` 请先执行 npm i dk-frontend-skills 安装完整技能包\n`);
134
+ if (!fs.existsSync(pkgSkillsSource)) {
135
+ console.log(" ⚠️ 包内没有找到技能文件\n");
102
136
  return;
103
137
  }
104
138
 
105
- const choices = userSkills.map((s) => ({
106
- name: s.name,
107
- enabled: names.includes(s.name) ? enable : s.enabled,
108
- }));
109
-
110
- saveSkillSelection(choices);
111
-
112
- const action = enable ? "启用" : "停用";
113
- console.log(` ✅ 已${action}: ${names.join(", ")}\n`);
114
- }
115
-
116
- // ---------- 交互式菜单 (@inquirer/prompts) ----------
117
-
118
- async function startInteractiveMenu() {
119
- const { checkbox } = await import("@inquirer/prompts");
120
-
121
139
  const pkgSkills = getPackageSkills(packageDir);
122
140
 
123
141
  if (pkgSkills.length === 0) {
@@ -125,31 +143,45 @@ async function startInteractiveMenu() {
125
143
  return;
126
144
  }
127
145
 
146
+ // 读取已安装状态:已装且启用的默认勾选
128
147
  const userSkills = getUserSkills(claudeDest);
129
- const userMap = new Map(userSkills.map((s) => [s.name, s.enabled]));
148
+ const enabledSet = new Set(
149
+ userSkills.filter((s) => s.enabled).map((s) => s.name),
150
+ );
151
+ const installedSet = new Set(userSkills.map((s) => s.name));
152
+
153
+ // 首次运行全部默认勾选(省事),再次运行沿用已有状态
154
+ const hasExistingInstall = fs.existsSync(claudeDest);
155
+ const defaultChecked = hasExistingInstall
156
+ ? enabledSet
157
+ : new Set(pkgSkills.map((s) => s.name));
130
158
 
131
159
  const choices = pkgSkills.map((s) => ({
132
160
  name: s.name,
133
161
  value: s.name,
134
162
  description: s.description || undefined,
135
- checked: userMap.has(s.name) ? userMap.get(s.name) : false,
163
+ checked: defaultChecked.has(s.name),
136
164
  }));
137
165
 
138
166
  const selected = await checkbox({
139
- message: "选择要启用的技能",
167
+ message: "选择要安装的技能(未勾选的将从 .claude/ 中移除)",
140
168
  instructions: "(↑↓ 导航, 空格 开关, Enter 确认)",
141
169
  choices,
142
170
  pageSize: 15,
143
171
  loop: false,
144
172
  });
145
173
 
146
- const allChoices = pkgSkills.map((s) => ({
147
- name: s.name,
148
- enabled: selected.includes(s.name),
149
- }));
174
+ const newCount = selected.filter((name) => !installedSet.has(name)).length;
175
+ const removedCount = [...installedSet].filter((name) => !selected.includes(name)).length;
176
+
177
+ const installed = installSelectedSkills(selected);
150
178
 
151
- saveSkillSelection(allChoices);
152
- console.log("\n ✅ 技能选择已保存\n");
179
+ const parts = [];
180
+ if (newCount > 0) parts.push(`新装 ${newCount} 个`);
181
+ if (removedCount > 0) parts.push(`移除 ${removedCount} 个`);
182
+ const summary = parts.length > 0 ? `(${parts.join(",")})` : "";
183
+
184
+ console.log(`\n ✅ 技能安装完成 ${summary}\n`);
153
185
  }
154
186
 
155
187
  // ---------- 主入口 ----------
@@ -167,23 +199,20 @@ async function main() {
167
199
  dk-frontend-skills CLI
168
200
 
169
201
  用法:
170
- npx dk-skills 交互式技能选择菜单
171
- npx dk-skills list 查看技能清单
172
- npx dk-skills enable <技能名...> 启用指定技能
173
- npx dk-skills disable <技能名...> 停用指定技能
174
- npx dk-skills --help 显示帮助
202
+ npx dk-frontend-skills 交互式选择并安装技能
203
+ npx dk-frontend-skills list 查看已安装的技能
204
+ npx dk-frontend-skills --help 显示帮助
205
+
206
+ 首次运行会自动创建 .claude/ 目录,勾选要安装的技能即可
175
207
 
176
208
  示例:
177
- npx dk-skills list
178
- npx dk-skills enable vue fe-biz-patterns
179
- npx dk-skills disable moai-framework-electron
209
+ npx dk-frontend-skills
210
+ npx dk-frontend-skills list
180
211
  `);
181
212
  } else if (command === "list") {
182
213
  await cmdList();
183
- } else if (command === "enable") {
184
- cmdToggle(true, args);
185
- } else if (command === "disable") {
186
- cmdToggle(false, args);
214
+ } else if (command === "enable" || command === "disable") {
215
+ console.log(` ⚠️ 该命令已废弃,请直接运行 npx dk-frontend-skills 使用交互菜单\n`);
187
216
  } else {
188
217
  console.log(` 未知命令: ${command}\n 可用: ${COMMANDS.join(", ")}\n`);
189
218
  process.exit(1);