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.
- package/.claude/profiles.json +73 -67
- package/package.json +1 -1
- package/scripts/web-ui/server.js +109 -3
- package/scripts/web-ui/src/App.vue +2 -2
- package/scripts/web-ui/src/api/skills.js +4 -0
- package/scripts/web-ui/src/views/SceneSwitch.vue +5 -1
- package/scripts/web-ui/src/views/Settings.vue +43 -1
package/.claude/profiles.json
CHANGED
|
@@ -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": [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
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
package/scripts/web-ui/server.js
CHANGED
|
@@ -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: !!
|
|
419
|
-
path:
|
|
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
|
|
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
|
}
|