mdk-skills 2.2.9 → 2.2.10

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,67 +1,73 @@
1
- {
2
- "version": 1,
3
- "profiles": [
4
- {
5
- "id": "vue3-frontend",
6
- "name": "Vue3 前端专项",
7
- "description": "Vue3 技术栈 | 业务架构 | 界面设计 | 代码规范评审",
8
- "skills": [
9
- "vue",
10
- "v3-fe-biz-patterns",
11
- "frontend-design",
12
- "frontend-code-review",
13
- "ui-ux-pro-max",
14
- "skill-creator"
15
- ],
16
- "always_apply": [
17
- "vue",
18
- "frontend-design",
19
- "frontend-code-review",
20
- "ui-ux-pro-max"
21
- ]
22
- },
23
- {
24
- "id": "react-frontend",
25
- "name": "React 前端专项",
26
- "description": "React 技术栈 | 界面设计 | 代码评审 | 通用前端能力",
27
- "skills": [
28
- "frontend-design",
29
- "frontend-code-review",
30
- "ui-ux-pro-max",
31
- "skill-creator"
32
- ],
33
- "always_apply": [
34
- "frontend-design",
35
- "frontend-code-review",
36
- "ui-ux-pro-max"
37
- ]
38
- },
39
- {
40
- "id": "backend-java",
41
- "name": "Java 后端开发",
42
- "description": "接口架构设计 | 数据库建模 | Java 后端体系能力",
43
- "skills": ["skill-creator"],
44
- "always_apply": []
45
- },
46
- {
47
- "id": "backend-python",
48
- "name": "Python 后端开发",
49
- "description": "接口架构设计 | 数据层设计 | Python 后端体系能力",
50
- "skills": ["skill-creator"],
51
- "always_apply": []
52
- },
53
- {
54
- "id": "backend-node",
55
- "name": "Node.js 后端开发",
56
- "description": "接口架构设计 | 服务端架构 | Node 后端体系能力",
57
- "skills": ["skill-creator"],
58
- "always_apply": []
59
- },
60
- {
61
- "id": "custom",
62
- "name": "自定义技能组合",
63
- "description": "自由勾选技能项,按需个性化配置",
64
- "skills": null
65
- }
66
- ]
67
- }
1
+ {
2
+ "version": 1,
3
+ "profiles": [
4
+ {
5
+ "id": "vue3-frontend",
6
+ "name": "Vue3 前端专项",
7
+ "description": "Vue3 技术栈 | 业务架构 | 界面设计 | 代码规范评审",
8
+ "skills": [
9
+ "vue",
10
+ "v3-fe-biz-patterns",
11
+ "frontend-design",
12
+ "frontend-code-review",
13
+ "ui-ux-pro-max",
14
+ "skill-creator"
15
+ ],
16
+ "always_apply": [
17
+ "vue",
18
+ "frontend-design",
19
+ "frontend-code-review",
20
+ "ui-ux-pro-max"
21
+ ]
22
+ },
23
+ {
24
+ "id": "react-frontend",
25
+ "name": "React 前端专项",
26
+ "description": "React 技术栈 | 界面设计 | 代码评审 | 通用前端能力",
27
+ "skills": [
28
+ "frontend-design",
29
+ "frontend-code-review",
30
+ "ui-ux-pro-max",
31
+ "skill-creator"
32
+ ],
33
+ "always_apply": [
34
+ "frontend-design",
35
+ "frontend-code-review",
36
+ "ui-ux-pro-max"
37
+ ]
38
+ },
39
+ {
40
+ "id": "backend-java",
41
+ "name": "Java 后端开发",
42
+ "description": "接口架构设计 | 数据库建模 | Java 后端体系能力",
43
+ "skills": [
44
+ "skill-creator"
45
+ ],
46
+ "always_apply": []
47
+ },
48
+ {
49
+ "id": "backend-python",
50
+ "name": "Python 后端开发",
51
+ "description": "接口架构设计 | 数据层设计 | Python 后端体系能力",
52
+ "skills": [
53
+ "skill-creator"
54
+ ],
55
+ "always_apply": []
56
+ },
57
+ {
58
+ "id": "backend-node",
59
+ "name": "Node.js 后端开发",
60
+ "description": "接口架构设计 | 服务端架构 | Node 后端体系能力",
61
+ "skills": [
62
+ "skill-creator"
63
+ ],
64
+ "always_apply": []
65
+ },
66
+ {
67
+ "id": "custom",
68
+ "name": "自定义技能组合",
69
+ "description": "自由勾选技能项,按需个性化配置",
70
+ "skills": null
71
+ }
72
+ ]
73
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdk-skills",
3
- "version": "2.2.9",
3
+ "version": "2.2.10",
4
4
  "description": "mdk-engineer - 沉稳靠谱的前端开发助手 Claude Skills 配置包,一键注入 .claude/ 技能目录和 CLAUDE.md 人设配置",
5
5
  "author": "XiaoMa",
6
6
  "license": "MIT",
@@ -411,12 +411,43 @@ async function handleApi(req, res) {
411
411
  });
412
412
  }
413
413
 
414
- // GET /api/source — 获取本地源信息
414
+ // GET /api/source — 获取本地源信息(含初始化检测)
415
415
  if (method === "GET" && pathname === "/api/source") {
416
416
  const settings = readSettings();
417
+ const sourcePath = settings._skill_source || null;
418
+
419
+ let needsInit = false;
420
+ if (sourcePath) {
421
+ const claudeDir = path.join(sourcePath, ".claude");
422
+ if (fs.existsSync(claudeDir)) {
423
+ // 检查 profiles.json
424
+ const hasProfiles = fs.existsSync(path.join(claudeDir, "profiles.json"));
425
+ // 检查 settings.json
426
+ const hasSettings = fs.existsSync(path.join(claudeDir, "settings.json"));
427
+ // 检查每个 skill 目录的 .meta.json
428
+ const skillsDir = path.join(claudeDir, "skills");
429
+ let allMeta = true;
430
+ if (fs.existsSync(skillsDir)) {
431
+ for (const name of fs.readdirSync(skillsDir)) {
432
+ const skillDir = path.join(skillsDir, name);
433
+ if (fs.statSync(skillDir).isDirectory()) {
434
+ if (!fs.existsSync(path.join(skillDir, ".meta.json"))) {
435
+ allMeta = false;
436
+ break;
437
+ }
438
+ }
439
+ }
440
+ }
441
+ needsInit = !hasProfiles || !hasSettings || !allMeta;
442
+ } else {
443
+ needsInit = true;
444
+ }
445
+ }
446
+
417
447
  return sendJSON(res, {
418
- connected: !!settings._skill_source,
419
- path: settings._skill_source || null,
448
+ connected: !!sourcePath,
449
+ path: sourcePath,
450
+ needsInit,
420
451
  });
421
452
  }
422
453
 
@@ -506,6 +537,81 @@ async function handleApi(req, res) {
506
537
  return sendJSON(res, { ok: true });
507
538
  }
508
539
 
540
+ // POST /api/source/init — 初始化本地源骨架文件(缺啥补啥)
541
+ if (method === "POST" && pathname === "/api/source/init") {
542
+ const settings = readSettings();
543
+ const sourcePath = settings._skill_source;
544
+ if (!sourcePath) return sendJSON(res, { error: "未绑定本地源" }, 400);
545
+
546
+ const claudeDir = path.join(sourcePath, ".claude");
547
+ const skillsDir = path.join(claudeDir, "skills");
548
+ const profilesPath = path.join(claudeDir, "profiles.json");
549
+ const settingsPath = path.join(claudeDir, "settings.json");
550
+
551
+ const created = { profiles: false, settings: false, metaFiles: 0 };
552
+
553
+ // 1. profiles.json
554
+ if (!fs.existsSync(profilesPath)) {
555
+ fs.writeFileSync(
556
+ profilesPath,
557
+ JSON.stringify(
558
+ {
559
+ version: 1,
560
+ profiles: [
561
+ {
562
+ id: "custom",
563
+ name: "自定义技能组合",
564
+ description: "自由勾选技能项,按需个性化配置",
565
+ skills: null,
566
+ },
567
+ ],
568
+ },
569
+ null,
570
+ 2,
571
+ ) + "\n",
572
+ "utf-8",
573
+ );
574
+ created.profiles = true;
575
+ }
576
+
577
+ // 2. settings.json
578
+ if (!fs.existsSync(settingsPath)) {
579
+ fs.writeFileSync(
580
+ settingsPath,
581
+ JSON.stringify({ skills: {}, always_apply_skills: [] }, null, 2) + "\n",
582
+ "utf-8",
583
+ );
584
+ created.settings = true;
585
+ }
586
+
587
+ // 3. 每个 skill 目录的 .meta.json
588
+ if (fs.existsSync(skillsDir)) {
589
+ for (const name of fs.readdirSync(skillsDir)) {
590
+ const skillDir = path.join(skillsDir, name);
591
+ if (!fs.statSync(skillDir).isDirectory()) continue;
592
+ const metaPath = path.join(skillDir, ".meta.json");
593
+ if (!fs.existsSync(metaPath)) {
594
+ fs.writeFileSync(
595
+ metaPath,
596
+ JSON.stringify(
597
+ {
598
+ version: "1.0.0",
599
+ description: name,
600
+ tags: [],
601
+ },
602
+ null,
603
+ 2,
604
+ ) + "\n",
605
+ "utf-8",
606
+ );
607
+ created.metaFiles++;
608
+ }
609
+ }
610
+ }
611
+
612
+ return sendJSON(res, { ok: true, ...created });
613
+ }
614
+
509
615
  // GET /api/diagnose — 健康检查
510
616
  if (method === "GET" && pathname === "/api/diagnose") {
511
617
  const results = [];
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <n-message-provider>
3
3
  <n-notification-provider>
4
- <n-config-provider :theme="theme">
4
+ <n-config-provider :theme="theme" :locale="zhCN" :date-locale="dateZhCN">
5
5
  <n-layout class="app-layout">
6
6
  <!-- 导航栏 -->
7
7
  <n-layout-header class="app-header" bordered>
@@ -37,7 +37,7 @@
37
37
  <script setup>
38
38
  import { h, ref, onMounted, computed } from "vue";
39
39
  import { useRouter, useRoute } from "vue-router";
40
- import { NIcon } from "naive-ui";
40
+ import { NIcon, zhCN, dateZhCN } from "naive-ui";
41
41
  import {
42
42
  AppsOutline,
43
43
  SwapHorizontalOutline,
@@ -78,6 +78,10 @@ export function syncSource() {
78
78
  return request("/source/sync", { method: "POST" });
79
79
  }
80
80
 
81
+ export function initSource() {
82
+ return request("/source/init", { method: "POST" });
83
+ }
84
+
81
85
  export function diagnose() {
82
86
  return request("/diagnose");
83
87
  }
@@ -23,7 +23,11 @@
23
23
  <n-button size="tiny" quaternary @click="openEdit(profile)">
24
24
  <template #icon><n-icon size="16"><PencilOutline /></n-icon></template>
25
25
  </n-button>
26
- <n-popconfirm @positive-click="onDelete(profile)">
26
+ <n-popconfirm
27
+ :negative-text="'取消'"
28
+ :positive-text="'确认删除'"
29
+ @positive-click="onDelete(profile)"
30
+ >
27
31
  <template #trigger>
28
32
  <n-button size="tiny" quaternary>
29
33
  <template #icon><n-icon size="16"><TrashOutline /></n-icon></template>
@@ -16,6 +16,12 @@
16
16
  </n-alert>
17
17
  </div>
18
18
 
19
+ <!-- 初始化提示 -->
20
+ <n-alert v-if="sourceInfo.connected && sourceInfo.needsInit" type="warning" :bordered="false" class="init-alert">
21
+ <template #header>本地源缺少配置文件</template>
22
+ profiles.json、settings.json 或 .meta.json 缺失,初始化后将生成骨架文件
23
+ </n-alert>
24
+
19
25
  <div class="source-actions">
20
26
  <n-input
21
27
  v-if="!sourceInfo.connected"
@@ -35,6 +41,14 @@
35
41
  >
36
42
  连接
37
43
  </n-button>
44
+ <n-button
45
+ v-if="sourceInfo.connected && sourceInfo.needsInit"
46
+ type="warning"
47
+ :loading="initializing"
48
+ @click="onInit"
49
+ >
50
+ 初始化
51
+ </n-button>
38
52
  <n-button
39
53
  v-if="sourceInfo.connected"
40
54
  :loading="syncing"
@@ -97,15 +111,17 @@ import {
97
111
  connectSource,
98
112
  disconnectSource,
99
113
  syncSource,
114
+ initSource,
100
115
  diagnose,
101
116
  } from "../api/skills";
102
117
 
103
118
  const emit = defineEmits(["refresh"]);
104
119
  const message = useMessage();
105
120
 
106
- const sourceInfo = ref({ connected: false, path: null });
121
+ const sourceInfo = ref({ connected: false, path: null, needsInit: false });
107
122
  const repoPath = ref("");
108
123
  const connecting = ref(false);
124
+ const initializing = ref(false);
109
125
  const syncing = ref(false);
110
126
  const disconnecting = ref(false);
111
127
  const diagnosing = ref(false);
@@ -149,6 +165,28 @@ async function onSync() {
149
165
  }
150
166
  }
151
167
 
168
+ async function onInit() {
169
+ initializing.value = true;
170
+ try {
171
+ const res = await initSource();
172
+ if (res.ok) {
173
+ const parts = [];
174
+ if (res.profiles) parts.push("profiles.json");
175
+ if (res.settings) parts.push("settings.json");
176
+ if (res.metaFiles > 0) parts.push(`${res.metaFiles} 个 .meta.json`);
177
+ message.success(`初始化完成:${parts.join("、") || "无缺失文件"}`);
178
+ await loadSource();
179
+ emit("refresh");
180
+ } else {
181
+ message.error(res.error || "初始化失败");
182
+ }
183
+ } catch {
184
+ message.error("初始化失败");
185
+ } finally {
186
+ initializing.value = false;
187
+ }
188
+ }
189
+
152
190
  async function onDisconnect() {
153
191
  disconnecting.value = true;
154
192
  try {
@@ -193,6 +231,10 @@ onMounted(loadSource);
193
231
  margin-bottom: 20px;
194
232
  }
195
233
 
234
+ .init-alert {
235
+ margin-bottom: 12px;
236
+ }
237
+
196
238
  .source-status {
197
239
  margin-bottom: 16px;
198
240
  }