mdk-skills 2.2.11 → 2.2.13
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
CHANGED
package/scripts/web-ui/server.js
CHANGED
|
@@ -497,10 +497,21 @@ async function handleApi(req, res) {
|
|
|
497
497
|
}
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
// README.md 缺失状态(独立于骨架文件)
|
|
501
|
+
let readmeStatus = null;
|
|
502
|
+
if (sourcePath) {
|
|
503
|
+
const skillsDir = path.join(sourcePath, ".claude", "skills");
|
|
504
|
+
readmeStatus = {
|
|
505
|
+
root: fs.existsSync(path.join(sourcePath, "README.md")),
|
|
506
|
+
skills: fs.existsSync(path.join(skillsDir, "README.md")),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
500
510
|
return sendJSON(res, {
|
|
501
511
|
connected: !!sourcePath,
|
|
502
512
|
path: sourcePath,
|
|
503
513
|
needsInit,
|
|
514
|
+
readmeStatus,
|
|
504
515
|
});
|
|
505
516
|
}
|
|
506
517
|
|
|
@@ -668,7 +679,109 @@ async function handleApi(req, res) {
|
|
|
668
679
|
}
|
|
669
680
|
}
|
|
670
681
|
|
|
671
|
-
|
|
682
|
+
// README.md 缺失状态
|
|
683
|
+
const readmeStatus = {
|
|
684
|
+
root: fs.existsSync(path.join(sourcePath, "README.md")),
|
|
685
|
+
skills: fs.existsSync(path.join(skillsDir, "README.md")),
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
return sendJSON(res, { ok: true, ...created, readmeStatus });
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// POST /api/readme/create — 创建 README.md 文档
|
|
692
|
+
if (method === "POST" && pathname === "/api/readme/create") {
|
|
693
|
+
const body = await parseBody(req);
|
|
694
|
+
const settings = readSettings();
|
|
695
|
+
const sourcePath = settings._skill_source;
|
|
696
|
+
if (!sourcePath) return sendJSON(res, { error: "未绑定本地源" }, 400);
|
|
697
|
+
|
|
698
|
+
const { root, skills, projectName, teamName } = body;
|
|
699
|
+
const name = projectName || "我的技能仓库";
|
|
700
|
+
const team = teamName || "未设定";
|
|
701
|
+
const created = [];
|
|
702
|
+
const skipped = [];
|
|
703
|
+
|
|
704
|
+
// 获取技能列表
|
|
705
|
+
const skillsDir = path.join(sourcePath, ".claude", "skills");
|
|
706
|
+
const skillNames = fs.existsSync(skillsDir)
|
|
707
|
+
? core.listSkillDirs(skillsDir)
|
|
708
|
+
: [];
|
|
709
|
+
const skillsList = skillNames.length
|
|
710
|
+
? skillNames.map((s) => `- \`${s}\``).join("\n")
|
|
711
|
+
: "<!-- 暂无技能 -->";
|
|
712
|
+
|
|
713
|
+
// 根目录 README.md
|
|
714
|
+
if (root) {
|
|
715
|
+
const rootPath = path.join(sourcePath, "README.md");
|
|
716
|
+
if (!fs.existsSync(rootPath)) {
|
|
717
|
+
const tmpl = `# ${name}
|
|
718
|
+
|
|
719
|
+
> 维护团队:${team}
|
|
720
|
+
|
|
721
|
+
## 简介
|
|
722
|
+
|
|
723
|
+
<!--
|
|
724
|
+
在这里写这个技能仓库的简介。
|
|
725
|
+
|
|
726
|
+
示例:
|
|
727
|
+
本仓库包含面向 mdk-engineer 的前端开发技能集,涵盖 Vue3 组件开发、Chrome DevTools 使用、性能优化等方向。
|
|
728
|
+
-->
|
|
729
|
+
|
|
730
|
+
## 技能列表
|
|
731
|
+
|
|
732
|
+
${skillsList}
|
|
733
|
+
|
|
734
|
+
## 使用方式
|
|
735
|
+
|
|
736
|
+
本仓库通过 mdk-skills 管理技能,连接本地源后即可在 Web UI 中查看和安装技能。
|
|
737
|
+
|
|
738
|
+
## License
|
|
739
|
+
|
|
740
|
+
<!-- 许可证信息 -->
|
|
741
|
+
`;
|
|
742
|
+
fs.writeFileSync(rootPath, tmpl, "utf-8");
|
|
743
|
+
created.push("README.md");
|
|
744
|
+
} else {
|
|
745
|
+
skipped.push("README.md");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// skills/README.md
|
|
750
|
+
if (skills) {
|
|
751
|
+
const skillsReadmePath = path.join(skillsDir, "README.md");
|
|
752
|
+
if (!fs.existsSync(skillsReadmePath)) {
|
|
753
|
+
const tmpl = `# 技能说明
|
|
754
|
+
|
|
755
|
+
## 快速开始
|
|
756
|
+
|
|
757
|
+
<!--
|
|
758
|
+
安装 mdk-skills 后连接本仓库,即可在 Web UI 中查看所有技能。
|
|
759
|
+
-->
|
|
760
|
+
|
|
761
|
+
## 场景建议
|
|
762
|
+
|
|
763
|
+
<!--
|
|
764
|
+
这里可以写哪些技能适合一起使用,不同开发场景下的推荐组合。
|
|
765
|
+
|
|
766
|
+
示例:
|
|
767
|
+
- 日常开发:启用代码格式化、ESLint 校验、Git 辅助
|
|
768
|
+
- 性能优化:启用 Lighthouse、Bundle Analysis
|
|
769
|
+
-->
|
|
770
|
+
|
|
771
|
+
## 注意事项
|
|
772
|
+
|
|
773
|
+
<!--
|
|
774
|
+
技能使用过程中的注意事项。
|
|
775
|
+
-->
|
|
776
|
+
`;
|
|
777
|
+
fs.writeFileSync(skillsReadmePath, tmpl, "utf-8");
|
|
778
|
+
created.push("skills/README.md");
|
|
779
|
+
} else {
|
|
780
|
+
skipped.push("skills/README.md");
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return sendJSON(res, { ok: true, created, skipped });
|
|
672
785
|
}
|
|
673
786
|
|
|
674
787
|
// GET /api/diagnose — 健康检查
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
|
|
27
27
|
<!-- 内容区 -->
|
|
28
28
|
<n-layout-content class="app-content">
|
|
29
|
-
<
|
|
29
|
+
<transition name="fade" mode="out-in">
|
|
30
|
+
<router-view @refresh="loadStatus" />
|
|
31
|
+
</transition>
|
|
30
32
|
</n-layout-content>
|
|
31
33
|
</n-layout>
|
|
32
34
|
</n-config-provider>
|
|
@@ -100,4 +102,14 @@ onMounted(loadStatus);
|
|
|
100
102
|
|
|
101
103
|
<style>
|
|
102
104
|
@import "./styles/main.css";
|
|
105
|
+
|
|
106
|
+
.fade-enter-active,
|
|
107
|
+
.fade-leave-active {
|
|
108
|
+
transition: opacity 0.2s ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.fade-enter-from,
|
|
112
|
+
.fade-leave-to {
|
|
113
|
+
opacity: 0;
|
|
114
|
+
}
|
|
103
115
|
</style>
|
|
@@ -101,3 +101,10 @@ export function getClaudeMd() {
|
|
|
101
101
|
export function getSkillReadme(name) {
|
|
102
102
|
return request("/skills/" + encodeURIComponent(name) + "/readme");
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
export function createReadme(data) {
|
|
106
|
+
return request("/readme/create", {
|
|
107
|
+
method: "POST",
|
|
108
|
+
body: JSON.stringify(data),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
11
|
<n-spin :show="loading">
|
|
12
|
-
<
|
|
13
|
-
|
|
12
|
+
<transition name="fade">
|
|
13
|
+
<div v-if="content" class="markdown-content" v-html="rendered" />
|
|
14
|
+
<n-empty v-else-if="loaded" description="暂无 CLAUDE.md 文档" />
|
|
15
|
+
</transition>
|
|
14
16
|
</n-spin>
|
|
15
17
|
</div>
|
|
16
18
|
</template>
|
|
@@ -45,6 +47,7 @@ marked.use({
|
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
const loading = ref(false);
|
|
50
|
+
const loaded = ref(false);
|
|
48
51
|
const content = ref(null);
|
|
49
52
|
const rendered = ref("");
|
|
50
53
|
|
|
@@ -66,6 +69,7 @@ function renderMd(text) {
|
|
|
66
69
|
|
|
67
70
|
async function loadClaudeMd() {
|
|
68
71
|
loading.value = true;
|
|
72
|
+
loaded.value = false;
|
|
69
73
|
try {
|
|
70
74
|
const res = await getClaudeMd();
|
|
71
75
|
if (res.content) {
|
|
@@ -79,6 +83,7 @@ async function loadClaudeMd() {
|
|
|
79
83
|
content.value = null;
|
|
80
84
|
} finally {
|
|
81
85
|
loading.value = false;
|
|
86
|
+
loaded.value = true;
|
|
82
87
|
}
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -97,4 +102,14 @@ onMounted(loadClaudeMd);
|
|
|
97
102
|
font-size: 20px;
|
|
98
103
|
font-weight: 600;
|
|
99
104
|
}
|
|
105
|
+
|
|
106
|
+
.fade-enter-active,
|
|
107
|
+
.fade-leave-active {
|
|
108
|
+
transition: opacity 0.2s ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.fade-enter-from,
|
|
112
|
+
.fade-leave-to {
|
|
113
|
+
opacity: 0;
|
|
114
|
+
}
|
|
100
115
|
</style>
|
|
@@ -31,6 +31,18 @@
|
|
|
31
31
|
>
|
|
32
32
|
<template #prefix>📁</template>
|
|
33
33
|
</n-input>
|
|
34
|
+
|
|
35
|
+
<!-- 独立于 init:仅 README.md 缺失时的创建入口 -->
|
|
36
|
+
<n-alert
|
|
37
|
+
v-if="sourceInfo.connected && sourceInfo.needsInit === false && hasReadmeMissing"
|
|
38
|
+
type="info"
|
|
39
|
+
:bordered="false"
|
|
40
|
+
class="init-alert"
|
|
41
|
+
>
|
|
42
|
+
<template #header>缺少 README.md 文档</template>
|
|
43
|
+
创建后将生成模板文件,可在对应目录中自行编辑
|
|
44
|
+
</n-alert>
|
|
45
|
+
|
|
34
46
|
<div class="action-buttons">
|
|
35
47
|
<n-button
|
|
36
48
|
v-if="!sourceInfo.connected"
|
|
@@ -56,6 +68,13 @@
|
|
|
56
68
|
>
|
|
57
69
|
同步到仓库
|
|
58
70
|
</n-button>
|
|
71
|
+
<n-button
|
|
72
|
+
v-if="sourceInfo.connected && sourceInfo.needsInit === false && hasReadmeMissing"
|
|
73
|
+
ghost
|
|
74
|
+
@click="onShowCreateReadme"
|
|
75
|
+
>
|
|
76
|
+
创建文档
|
|
77
|
+
</n-button>
|
|
59
78
|
<n-button
|
|
60
79
|
v-if="sourceInfo.connected"
|
|
61
80
|
type="error"
|
|
@@ -69,6 +88,51 @@
|
|
|
69
88
|
</div>
|
|
70
89
|
</n-card>
|
|
71
90
|
|
|
91
|
+
<!-- 创建 README.md 对话框 -->
|
|
92
|
+
<n-modal
|
|
93
|
+
v-model:show="showReadmeDialog"
|
|
94
|
+
title="创建文档"
|
|
95
|
+
preset="card"
|
|
96
|
+
style="width: 520px"
|
|
97
|
+
:mask-closable="false"
|
|
98
|
+
>
|
|
99
|
+
<div class="readme-dialog-desc">检测到缺少以下文档,是否创建?</div>
|
|
100
|
+
|
|
101
|
+
<n-checkbox v-model:checked="readmeForm.root" class="readme-checkbox">
|
|
102
|
+
<div class="readme-checkbox-content">
|
|
103
|
+
<span class="readme-checkbox-title">根目录 README.md</span>
|
|
104
|
+
<span class="readme-checkbox-desc">仪表盘 → "仓库说明"折叠面板,适合写项目简介、维护团队、许可证</span>
|
|
105
|
+
</div>
|
|
106
|
+
</n-checkbox>
|
|
107
|
+
|
|
108
|
+
<n-checkbox v-model:checked="readmeForm.skills" class="readme-checkbox">
|
|
109
|
+
<div class="readme-checkbox-content">
|
|
110
|
+
<span class="readme-checkbox-title">.claude/skills/README.md</span>
|
|
111
|
+
<span class="readme-checkbox-desc">场景切换 → "技能说明"折叠面板,适合写使用指南、场景搭配建议</span>
|
|
112
|
+
</div>
|
|
113
|
+
</n-checkbox>
|
|
114
|
+
|
|
115
|
+
<n-divider />
|
|
116
|
+
|
|
117
|
+
<n-form label-placement="top">
|
|
118
|
+
<n-form-item label="项目名称(选填)">
|
|
119
|
+
<n-input v-model:value="readmeForm.projectName" placeholder="我的技能仓库" />
|
|
120
|
+
</n-form-item>
|
|
121
|
+
<n-form-item label="维护团队(选填)">
|
|
122
|
+
<n-input v-model:value="readmeForm.teamName" placeholder="未设定" />
|
|
123
|
+
</n-form-item>
|
|
124
|
+
</n-form>
|
|
125
|
+
|
|
126
|
+
<template #footer>
|
|
127
|
+
<n-space justify="end">
|
|
128
|
+
<n-button @click="showReadmeDialog = false">跳过</n-button>
|
|
129
|
+
<n-button type="primary" :loading="creatingReadme" @click="onCreateReadme">
|
|
130
|
+
创建
|
|
131
|
+
</n-button>
|
|
132
|
+
</n-space>
|
|
133
|
+
</template>
|
|
134
|
+
</n-modal>
|
|
135
|
+
|
|
72
136
|
<!-- 健康检查 -->
|
|
73
137
|
<n-card title="健康检查" size="small" class="section">
|
|
74
138
|
<template #header-extra>
|
|
@@ -100,7 +164,7 @@
|
|
|
100
164
|
</template>
|
|
101
165
|
|
|
102
166
|
<script setup>
|
|
103
|
-
import { ref, onMounted } from "vue";
|
|
167
|
+
import { ref, computed, onMounted } from "vue";
|
|
104
168
|
import { useMessage } from "naive-ui";
|
|
105
169
|
import {
|
|
106
170
|
CheckmarkCircleOutline,
|
|
@@ -113,6 +177,7 @@ import {
|
|
|
113
177
|
syncSource,
|
|
114
178
|
initSource,
|
|
115
179
|
diagnose,
|
|
180
|
+
createReadme,
|
|
116
181
|
} from "../api/skills";
|
|
117
182
|
|
|
118
183
|
const emit = defineEmits(["refresh"]);
|
|
@@ -127,6 +192,21 @@ const disconnecting = ref(false);
|
|
|
127
192
|
const diagnosing = ref(false);
|
|
128
193
|
const diagnosis = ref([]);
|
|
129
194
|
|
|
195
|
+
// 创建 README.md 对话框
|
|
196
|
+
const showReadmeDialog = ref(false);
|
|
197
|
+
const creatingReadme = ref(false);
|
|
198
|
+
const readmeForm = ref({
|
|
199
|
+
root: true,
|
|
200
|
+
skills: true,
|
|
201
|
+
projectName: "",
|
|
202
|
+
teamName: "",
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const hasReadmeMissing = computed(() => {
|
|
206
|
+
if (!sourceInfo.value.readmeStatus) return false;
|
|
207
|
+
return !sourceInfo.value.readmeStatus.root || !sourceInfo.value.readmeStatus.skills;
|
|
208
|
+
});
|
|
209
|
+
|
|
130
210
|
async function loadSource() {
|
|
131
211
|
sourceInfo.value = await getSource();
|
|
132
212
|
}
|
|
@@ -175,6 +255,17 @@ async function onInit() {
|
|
|
175
255
|
if (res.settings) parts.push("settings.json");
|
|
176
256
|
if (res.metaFiles > 0) parts.push(`${res.metaFiles} 个 .meta.json`);
|
|
177
257
|
message.success(`初始化完成:${parts.join("、") || "无缺失文件"}`);
|
|
258
|
+
|
|
259
|
+
// 检测 README.md 缺失情况,弹创建对话框
|
|
260
|
+
if (res.readmeStatus) {
|
|
261
|
+
const anyMissing = !res.readmeStatus.root || !res.readmeStatus.skills;
|
|
262
|
+
if (anyMissing) {
|
|
263
|
+
readmeForm.value.root = !res.readmeStatus.root;
|
|
264
|
+
readmeForm.value.skills = !res.readmeStatus.skills;
|
|
265
|
+
showReadmeDialog.value = true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
178
269
|
await loadSource();
|
|
179
270
|
emit("refresh");
|
|
180
271
|
} else {
|
|
@@ -187,6 +278,35 @@ async function onInit() {
|
|
|
187
278
|
}
|
|
188
279
|
}
|
|
189
280
|
|
|
281
|
+
async function onCreateReadme() {
|
|
282
|
+
creatingReadme.value = true;
|
|
283
|
+
try {
|
|
284
|
+
const res = await createReadme({
|
|
285
|
+
root: readmeForm.value.root,
|
|
286
|
+
skills: readmeForm.value.skills,
|
|
287
|
+
projectName: readmeForm.value.projectName.trim(),
|
|
288
|
+
teamName: readmeForm.value.teamName.trim(),
|
|
289
|
+
});
|
|
290
|
+
if (res.ok) {
|
|
291
|
+
const parts = res.created || [];
|
|
292
|
+
message.success(`已创建:${parts.join("、") || "无"}`);
|
|
293
|
+
showReadmeDialog.value = false;
|
|
294
|
+
} else {
|
|
295
|
+
message.error(res.error || "创建失败");
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
message.error("创建失败");
|
|
299
|
+
} finally {
|
|
300
|
+
creatingReadme.value = false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function onShowCreateReadme() {
|
|
305
|
+
readmeForm.value.root = !sourceInfo.value.readmeStatus?.root;
|
|
306
|
+
readmeForm.value.skills = !sourceInfo.value.readmeStatus?.skills;
|
|
307
|
+
showReadmeDialog.value = true;
|
|
308
|
+
}
|
|
309
|
+
|
|
190
310
|
async function onDisconnect() {
|
|
191
311
|
disconnecting.value = true;
|
|
192
312
|
try {
|
|
@@ -259,4 +379,32 @@ onMounted(loadSource);
|
|
|
259
379
|
color: #d03050;
|
|
260
380
|
font-size: 13px;
|
|
261
381
|
}
|
|
382
|
+
|
|
383
|
+
.readme-dialog-desc {
|
|
384
|
+
margin-bottom: 16px;
|
|
385
|
+
font-size: 14px;
|
|
386
|
+
color: #555;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.readme-checkbox {
|
|
390
|
+
display: flex;
|
|
391
|
+
align-items: flex-start;
|
|
392
|
+
margin-bottom: 12px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.readme-checkbox-content {
|
|
396
|
+
display: flex;
|
|
397
|
+
flex-direction: column;
|
|
398
|
+
gap: 2px;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.readme-checkbox-title {
|
|
402
|
+
font-size: 14px;
|
|
403
|
+
font-weight: 500;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.readme-checkbox-desc {
|
|
407
|
+
font-size: 12px;
|
|
408
|
+
color: #999;
|
|
409
|
+
}
|
|
262
410
|
</style>
|