mdk-skills 2.2.21 → 2.2.23

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 (134) hide show
  1. package/.claude/.install.log +3 -0
  2. package/.claude/backups/20260511.145306/.install.log +39 -0
  3. package/.claude/backups/20260511.145306/profiles.json +22 -0
  4. package/.claude/backups/20260511.145306/settings.json +11 -0
  5. package/.claude/backups/CLAUDE.md.20260511.145306 +131 -0
  6. package/.claude/profiles.json +10 -61
  7. package/.claude/settings.json +9 -27
  8. package/package.json +9 -4
  9. package/scripts/core.js +49 -4
  10. package/scripts/web-ui/dist/assets/index-B3SLlTdd.css +1 -0
  11. package/scripts/web-ui/dist/assets/index-DanvMbHP.js +7440 -0
  12. package/scripts/web-ui/dist/index.html +13 -0
  13. package/scripts/web-ui/server.js +1013 -860
  14. package/scripts/web-ui/src/App.vue +148 -125
  15. package/scripts/web-ui/src/api/skills.js +20 -0
  16. package/scripts/web-ui/src/components/SkillCard.vue +9 -0
  17. package/scripts/web-ui/src/components/StatusBar.vue +13 -0
  18. package/scripts/web-ui/src/styles/main.css +105 -1
  19. package/scripts/web-ui/src/utils/usage.js +46 -0
  20. package/scripts/web-ui/src/views/Dashboard.vue +389 -20
  21. package/scripts/web-ui/src/views/SceneSwitch.vue +4 -0
  22. package/scripts/web-ui/src/views/Settings.vue +44 -2
  23. package/scripts/web-ui/vite.config.js +11 -0
  24. package/.claude/backups/20260510.153300/.install.log +0 -10
  25. package/.claude/backups/20260510.153300/profiles.json +0 -67
  26. package/.claude/backups/20260510.153300/settings.json +0 -29
  27. package/.claude/backups/20260510.153300/skills/frontend-code-review/.meta.json +0 -6
  28. package/.claude/backups/20260510.153300/skills/frontend-code-review/SKILL.md +0 -167
  29. package/.claude/backups/20260510.153300/skills/frontend-code-review/references/checklist.md +0 -298
  30. package/.claude/backups/20260510.153300/skills/frontend-design/.meta.json +0 -6
  31. package/.claude/backups/20260510.153300/skills/frontend-design/LICENSE.txt +0 -177
  32. package/.claude/backups/20260510.153300/skills/frontend-design/SKILL.md +0 -42
  33. package/.claude/backups/20260510.153300/skills/skill-creator/.meta.json +0 -6
  34. package/.claude/backups/20260510.153300/skills/skill-creator/SKILL.md +0 -356
  35. package/.claude/backups/20260510.153300/skills/skill-creator/references/output-patterns.md +0 -82
  36. package/.claude/backups/20260510.153300/skills/skill-creator/references/workflows.md +0 -28
  37. package/.claude/backups/20260510.153300/skills/skill-creator/scripts/init_skill.py +0 -303
  38. package/.claude/backups/20260510.153300/skills/skill-creator/scripts/package_skill.py +0 -110
  39. package/.claude/backups/20260510.153300/skills/skill-creator/scripts/quick_validate.py +0 -95
  40. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/.meta.json +0 -6
  41. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/SKILL.md +0 -228
  42. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/charts.csv +0 -26
  43. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/colors.csv +0 -97
  44. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/landing.csv +0 -31
  45. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/products.csv +0 -97
  46. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/prompts.csv +0 -24
  47. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  48. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  49. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  50. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  51. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  52. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  53. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  54. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  55. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  56. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  57. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/styles.csv +0 -59
  58. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/typography.csv +0 -58
  59. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  60. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/scripts/core.py +0 -238
  61. package/.claude/backups/20260510.153300/skills/ui-ux-pro-max/scripts/search.py +0 -61
  62. package/.claude/backups/20260510.153300/skills/v3-fe-biz-patterns/.meta.json +0 -6
  63. package/.claude/backups/20260510.153300/skills/v3-fe-biz-patterns/SKILL.md +0 -26
  64. package/.claude/backups/20260510.153300/skills/v3-fe-biz-patterns/references/infinite-scroll.md +0 -292
  65. package/.claude/backups/20260510.153300/skills/v3-fe-biz-patterns/references/pinia-store.md +0 -174
  66. package/.claude/backups/20260510.153300/skills/v3-fe-biz-patterns/references/service-layer.md +0 -198
  67. package/.claude/backups/20260510.153300/skills/v3-fe-biz-patterns/references/tab-anchor.md +0 -1125
  68. package/.claude/backups/20260510.153300/skills/v3-fe-biz-patterns/references/use-loading.md +0 -114
  69. package/.claude/backups/20260510.153300/skills/vue/.meta.json +0 -6
  70. package/.claude/backups/20260510.153300/skills/vue/SKILL.md +0 -103
  71. package/.claude/backups/20260510.153300/skills/vue/references/components.md +0 -323
  72. package/.claude/backups/20260510.153300/skills/vue/references/composables.md +0 -358
  73. package/.claude/backups/20260510.153300/skills/vue/references/directives.md +0 -225
  74. package/.claude/backups/20260510.153300/skills/vue/references/gotchas.md +0 -438
  75. package/.claude/backups/20260510.153300/skills/vue/references/provide-inject.md +0 -174
  76. package/.claude/backups/20260510.153300/skills/vue/references/reactivity.md +0 -289
  77. package/.claude/backups/20260510.153300/skills/vue/references/router.md +0 -181
  78. package/.claude/backups/20260510.153300/skills/vue/references/testing.md +0 -294
  79. package/.claude/backups/20260510.153300/skills/vue/references/typescript.md +0 -172
  80. package/.claude/backups/20260510.153300/skills/vue/references/utils-client.md +0 -156
  81. package/.claude/skills/frontend-code-review/.meta.json +0 -6
  82. package/.claude/skills/frontend-code-review/SKILL.md +0 -167
  83. package/.claude/skills/frontend-code-review/references/checklist.md +0 -298
  84. package/.claude/skills/frontend-design/.meta.json +0 -6
  85. package/.claude/skills/frontend-design/LICENSE.txt +0 -177
  86. package/.claude/skills/frontend-design/SKILL.md +0 -42
  87. package/.claude/skills/skill-creator/.meta.json +0 -6
  88. package/.claude/skills/skill-creator/SKILL.md +0 -356
  89. package/.claude/skills/skill-creator/references/output-patterns.md +0 -82
  90. package/.claude/skills/skill-creator/references/workflows.md +0 -28
  91. package/.claude/skills/skill-creator/scripts/init_skill.py +0 -303
  92. package/.claude/skills/skill-creator/scripts/package_skill.py +0 -110
  93. package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -95
  94. package/.claude/skills/ui-ux-pro-max/.meta.json +0 -6
  95. package/.claude/skills/ui-ux-pro-max/SKILL.md +0 -228
  96. package/.claude/skills/ui-ux-pro-max/data/charts.csv +0 -26
  97. package/.claude/skills/ui-ux-pro-max/data/colors.csv +0 -97
  98. package/.claude/skills/ui-ux-pro-max/data/landing.csv +0 -31
  99. package/.claude/skills/ui-ux-pro-max/data/products.csv +0 -97
  100. package/.claude/skills/ui-ux-pro-max/data/prompts.csv +0 -24
  101. package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  102. package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  103. package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  104. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  105. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  106. package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  107. package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  108. package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  109. package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  110. package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  111. package/.claude/skills/ui-ux-pro-max/data/styles.csv +0 -59
  112. package/.claude/skills/ui-ux-pro-max/data/typography.csv +0 -58
  113. package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  114. package/.claude/skills/ui-ux-pro-max/scripts/core.py +0 -238
  115. package/.claude/skills/ui-ux-pro-max/scripts/search.py +0 -61
  116. package/.claude/skills/v3-fe-biz-patterns/.meta.json +0 -6
  117. package/.claude/skills/v3-fe-biz-patterns/SKILL.md +0 -26
  118. package/.claude/skills/v3-fe-biz-patterns/references/infinite-scroll.md +0 -292
  119. package/.claude/skills/v3-fe-biz-patterns/references/pinia-store.md +0 -174
  120. package/.claude/skills/v3-fe-biz-patterns/references/service-layer.md +0 -198
  121. package/.claude/skills/v3-fe-biz-patterns/references/tab-anchor.md +0 -1125
  122. package/.claude/skills/v3-fe-biz-patterns/references/use-loading.md +0 -114
  123. package/.claude/skills/vue/.meta.json +0 -6
  124. package/.claude/skills/vue/SKILL.md +0 -103
  125. package/.claude/skills/vue/references/components.md +0 -323
  126. package/.claude/skills/vue/references/composables.md +0 -358
  127. package/.claude/skills/vue/references/directives.md +0 -225
  128. package/.claude/skills/vue/references/gotchas.md +0 -438
  129. package/.claude/skills/vue/references/provide-inject.md +0 -174
  130. package/.claude/skills/vue/references/reactivity.md +0 -289
  131. package/.claude/skills/vue/references/router.md +0 -181
  132. package/.claude/skills/vue/references/testing.md +0 -294
  133. package/.claude/skills/vue/references/typescript.md +0 -172
  134. package/.claude/skills/vue/references/utils-client.md +0 -156
@@ -13,19 +13,54 @@
13
13
 
14
14
  <div class="page-header">
15
15
  <h2>技能列表</h2>
16
- <n-button size="small" @click="loadSkills">
17
- <template #icon
18
- ><n-icon><RefreshOutline /></n-icon
19
- ></template>
20
- 刷新
21
- </n-button>
16
+ <div class="header-actions">
17
+ <n-select
18
+ v-model:value="sortBy"
19
+ :options="sortOptions"
20
+ size="small"
21
+ style="width: 120px"
22
+ />
23
+ <n-button size="small" @click="loadSkills">
24
+ <template #icon
25
+ ><n-icon><RefreshOutline /></n-icon
26
+ ></template>
27
+ 刷新
28
+ </n-button>
29
+ <n-button size="small" @click="openPullModal">
30
+ 拉取
31
+ </n-button>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="tag-filter" v-if="allTags.length > 0">
36
+ <n-tag
37
+ size="small"
38
+ :bordered="false"
39
+ :type="selectedTags.length === 0 ? 'primary' : 'default'"
40
+ style="cursor: pointer"
41
+ @click="selectedTags = []"
42
+ >
43
+ 全部
44
+ </n-tag>
45
+ <n-tag
46
+ v-for="tag in allTags"
47
+ :key="tag"
48
+ size="small"
49
+ :bordered="false"
50
+ :type="selectedTags.includes(tag) ? 'primary' : 'default'"
51
+ style="cursor: pointer"
52
+ @click="toggleTag(tag)"
53
+ >
54
+ {{ tag }}
55
+ </n-tag>
22
56
  </div>
23
57
 
24
58
  <n-spin :show="loading">
25
59
  <SkillCard
26
- v-for="skill in skills"
60
+ v-for="skill in displaySkills"
27
61
  :key="skill.name"
28
62
  :skill="skill"
63
+ :usages="getUsage(skill.name)"
29
64
  @refresh="loadSkills"
30
65
  @click="showSkillDetail(skill)"
31
66
  />
@@ -45,30 +80,98 @@
45
80
  :title="detailSkill?.name || '技能详情'"
46
81
  preset="card"
47
82
  style="width: 720px"
48
- content-style="max-height: 65vh; overflow-y: auto; padding: 16px 24px; background: #fff;"
83
+ content-style="padding: 0;"
49
84
  :mask-closable="true"
50
85
  >
51
- <div v-if="detailLoading" class="detail-status">
52
- <n-spin />
86
+ <div class="detail-body">
87
+ <!-- 编辑面板 -->
88
+ <div class="edit-panel" v-if="detailSkill">
89
+ <div class="edit-row">
90
+ <span class="edit-label">版本</span>
91
+ <n-input v-model:value="editVersion" size="small" style="width: 120px" />
92
+ </div>
93
+ <div class="edit-row">
94
+ <span class="edit-label">描述</span>
95
+ <n-input v-model:value="editDescription" type="textarea" size="small" :rows="2" />
96
+ </div>
97
+ <div class="edit-row">
98
+ <span class="edit-label">标签</span>
99
+ <n-dynamic-tags v-model:value="editTags" size="small" />
100
+ </div>
101
+ <div class="edit-actions">
102
+ <n-button size="small" type="primary" @click="saveMeta" :loading="savingMeta">保存</n-button>
103
+ <n-button size="small" type="error" @click="handleDelete">删除技能</n-button>
104
+ </div>
105
+ <n-divider style="margin: 12px 0" />
106
+ </div>
107
+
108
+ <div v-if="detailLoading" class="detail-status">
109
+ <n-spin />
110
+ </div>
111
+ <div
112
+ v-else-if="detailContent"
113
+ class="markdown-content"
114
+ v-html="renderedDetail"
115
+ />
116
+ <n-empty v-else description="该技能没有 SKILL.md 文档" />
117
+ </div>
118
+ </n-modal>
119
+
120
+ <!-- 拉取弹窗 -->
121
+ <n-modal
122
+ v-model:show="showPullModal"
123
+ title="从远程仓库拉取"
124
+ preset="card"
125
+ style="width: 600px"
126
+ :mask-closable="true"
127
+ >
128
+ <div class="pull-modal-body">
129
+ <div class="pull-input-row">
130
+ <n-input v-model:value="pullUrl" placeholder="输入 GitHub 仓库地址..." size="small" @keyup.enter="handlePull" />
131
+ <n-button size="small" type="primary" @click="handlePull" :loading="pulling">拉取</n-button>
132
+ </div>
133
+
134
+ <div v-if="pullError" class="pull-error">
135
+ <n-alert type="error" :bordered="false" closable @close="pullError = ''">{{ pullError }}</n-alert>
136
+ </div>
137
+
138
+ <div v-if="pullResult" class="pull-result">
139
+ <div v-if="pullResult.imported && pullResult.imported.length > 0" class="pull-result-line">
140
+ <span class="pull-result-label">已导入:</span>
141
+ <n-tag v-for="name in pullResult.imported" :key="name" size="small" type="success" style="margin: 2px">{{ name }}</n-tag>
142
+ </div>
143
+ <div v-if="pullResult.skipped && pullResult.skipped.length > 0" class="pull-result-line">
144
+ <span class="pull-result-label">跳过:</span>
145
+ <n-tag v-for="name in pullResult.skipped" :key="name" size="small" style="margin: 2px">{{ name }}</n-tag>
146
+ <span class="pull-result-hint">(无 SKILL.md)</span>
147
+ </div>
148
+
149
+ <div v-if="pullResult.imported && pullResult.imported.length > 0" class="install-panel">
150
+ <div class="install-panel-label">选择要安装到项目目录的技能:</div>
151
+ <div class="install-check-list">
152
+ <n-checkbox v-for="name in pullResult.imported" :key="name" v-model:checked="installSelected[name]" style="margin-right: 16px">
153
+ {{ name }}
154
+ </n-checkbox>
155
+ </div>
156
+ <n-button size="small" type="primary" @click="handleInstallPull" :loading="installing" class="install-btn">
157
+ 安装选中
158
+ </n-button>
159
+ </div>
160
+ </div>
53
161
  </div>
54
- <div
55
- v-else-if="detailContent"
56
- class="markdown-content"
57
- v-html="renderedDetail"
58
- />
59
- <n-empty v-else description="该技能没有 SKILL.md 文档" />
60
162
  </n-modal>
61
163
  </div>
62
164
  </template>
63
165
 
64
166
  <script setup>
65
- import { ref, onMounted, onActivated, onUnmounted, nextTick } from "vue";
66
- import { NIcon } from "naive-ui";
167
+ import { ref, computed, onMounted, onActivated, onUnmounted, nextTick } from "vue";
168
+ import { NIcon, useMessage } from "naive-ui";
67
169
  import { RefreshOutline } from "@vicons/ionicons5";
68
170
  import { marked } from "marked";
69
171
  import hljs from "highlight.js";
70
172
  import SkillCard from "../components/SkillCard.vue";
71
- import { getSkills, getReadme, getSkillReadme } from "../api/skills";
173
+ import { getSkills, getReadme, getSkillReadme, updateSkillMeta, deleteSkill, pullSkills, installSkills } from "../api/skills";
174
+ import { sortSkills, getUsageMap } from "../utils/usage";
72
175
 
73
176
  // marked 配置:代码高亮 + 外链安全
74
177
  marked.use({
@@ -95,6 +198,7 @@ marked.use({
95
198
  });
96
199
 
97
200
  const emit = defineEmits(["refresh"]);
201
+ const message = useMessage();
98
202
  const skills = ref([]);
99
203
  const loading = ref(false);
100
204
 
@@ -110,6 +214,114 @@ const detailLoading = ref(false);
110
214
  const detailContent = ref(null);
111
215
  const renderedDetail = ref("");
112
216
 
217
+ // 编辑面板
218
+ const editVersion = ref("");
219
+ const editDescription = ref("");
220
+ const editTags = ref([]);
221
+ const savingMeta = ref(false);
222
+
223
+ // 标签筛选 & 排序
224
+ const selectedTags = ref([]);
225
+ const sortBy = ref("name");
226
+ const sortOptions = [
227
+ { label: "按名称", value: "name" },
228
+ { label: "按频率", value: "frequency" },
229
+ { label: "最近使用", value: "recent" },
230
+ ];
231
+
232
+
233
+ // 拉取 & 安装
234
+ const pullUrl = ref("");
235
+ const pulling = ref(false);
236
+ const pullResult = ref(null);
237
+ const pullError = ref("");
238
+ const showPullModal = ref(false);
239
+ const installSelected = ref({});
240
+ const installing = ref(false);
241
+
242
+ function openPullModal() {
243
+ showPullModal.value = true;
244
+ pullUrl.value = "";
245
+ pullResult.value = null;
246
+ pullError.value = "";
247
+ }
248
+
249
+ async function handlePull() {
250
+ if (!pullUrl.value.trim()) {
251
+ pullError.value = "请输入仓库地址";
252
+ return;
253
+ }
254
+ pulling.value = true;
255
+ pullResult.value = null;
256
+ pullError.value = "";
257
+ try {
258
+ const res = await pullSkills(pullUrl.value.trim());
259
+ if (res.ok) {
260
+ pullResult.value = { imported: res.imported, skipped: res.skipped };
261
+ // 默认全选
262
+ const sel = {};
263
+ (res.imported || []).forEach((n) => { sel[n] = true; });
264
+ installSelected.value = sel;
265
+ await loadSkills();
266
+ } else {
267
+ pullError.value = res.error || "拉取失败";
268
+ }
269
+ } catch (e) {
270
+ pullError.value = "拉取失败,请检查地址或网络";
271
+ } finally {
272
+ pulling.value = false;
273
+ }
274
+ }
275
+
276
+ async function handleInstallPull() {
277
+ const selected = Object.keys(installSelected.value).filter((k) => installSelected.value[k]);
278
+ if (selected.length === 0) {
279
+ pullError.value = "请至少选择一个技能";
280
+ return;
281
+ }
282
+ installing.value = true;
283
+ try {
284
+ const res = await installSkills(selected);
285
+ if (res.ok) {
286
+ message.success("安装完成");
287
+ await loadSkills();
288
+ pullResult.value = null;
289
+ } else {
290
+ pullError.value = res.error || "安装失败";
291
+ }
292
+ } catch {
293
+ pullError.value = "安装失败";
294
+ } finally {
295
+ installing.value = false;
296
+ }
297
+ }
298
+ const allTags = computed(() => {
299
+ const set = new Set();
300
+ skills.value.forEach((s) => (s.tags || []).forEach((t) => set.add(t)));
301
+ return [...set].sort();
302
+ });
303
+
304
+ const displaySkills = computed(() => {
305
+ let filtered = skills.value;
306
+ if (selectedTags.value.length > 0) {
307
+ filtered = skills.value.filter((s) =>
308
+ selectedTags.value.every((t) => (s.tags || []).includes(t))
309
+ );
310
+ }
311
+ return sortSkills(filtered, sortBy.value);
312
+ });
313
+
314
+ function toggleTag(tag) {
315
+ const idx = selectedTags.value.indexOf(tag);
316
+ if (idx === -1) selectedTags.value.push(tag);
317
+ else selectedTags.value.splice(idx, 1);
318
+ }
319
+
320
+ function getUsage(name) {
321
+ const usage = getUsageMap();
322
+ return usage[name]?.count || 0;
323
+ }
324
+
113
325
  /** 去掉 YAML frontmatter(--- 包裹的元数据) */
114
326
  function stripFrontmatter(text) {
115
327
  if (!text) return text;
@@ -156,6 +368,9 @@ async function showSkillDetail(skill) {
156
368
  detailLoading.value = true;
157
369
  detailContent.value = null;
158
370
  renderedDetail.value = "";
371
+ editVersion.value = skill.version || "1.0.0";
372
+ editDescription.value = skill.description || "";
373
+ editTags.value = [...(skill.tags || [])];
159
374
  try {
160
375
  const res = await getSkillReadme(skill.name);
161
376
  if (res.content) {
@@ -170,6 +385,46 @@ async function showSkillDetail(skill) {
170
385
  }
171
386
  }
172
387
 
388
+ async function saveMeta() {
389
+ if (!detailSkill.value) return;
390
+ savingMeta.value = true;
391
+ try {
392
+ const res = await updateSkillMeta(detailSkill.value.name, {
393
+ version: editVersion.value,
394
+ description: editDescription.value,
395
+ tags: editTags.value,
396
+ });
397
+ if (res.ok) {
398
+ message.success("技能信息已更新");
399
+ detailSkill.value.version = editVersion.value;
400
+ detailSkill.value.description = editDescription.value;
401
+ detailSkill.value.tags = [...editTags.value];
402
+ await loadSkills();
403
+ }
404
+ } catch {
405
+ message.error("保存失败");
406
+ } finally {
407
+ savingMeta.value = false;
408
+ }
409
+ }
410
+
411
+ async function handleDelete() {
412
+ if (!detailSkill.value) return;
413
+ const ok = window.confirm(`确定要删除技能 "${detailSkill.value.name}" 吗?
414
+ 将同时删除源目录和项目目录中的文件。`);
415
+ if (!ok) return;
416
+ try {
417
+ const res = await deleteSkill(detailSkill.value.name);
418
+ if (res.ok) {
419
+ message.success(`技能 "${detailSkill.value.name}" 已删除`);
420
+ detailVisible.value = false;
421
+ await loadSkills();
422
+ }
423
+ } catch {
424
+ message.error("删除失败");
425
+ }
426
+ }
427
+
173
428
  // 首次加载
174
429
  onMounted(() => {
175
430
  loadSkills();
@@ -195,7 +450,9 @@ onUnmounted(() => document.removeEventListener("visibilitychange", onFocus));
195
450
  display: flex;
196
451
  align-items: center;
197
452
  justify-content: space-between;
198
- margin-bottom: 20px;
453
+ margin-bottom: 4px;
454
+ flex-wrap: wrap;
455
+ gap: 8px;
199
456
  }
200
457
 
201
458
  .page-header h2 {
@@ -203,9 +460,121 @@ onUnmounted(() => document.removeEventListener("visibilitychange", onFocus));
203
460
  font-weight: 600;
204
461
  }
205
462
 
463
+ .header-actions {
464
+ display: flex;
465
+ align-items: center;
466
+ gap: 8px;
467
+ }
468
+
469
+ .tag-filter {
470
+ display: flex;
471
+ align-items: center;
472
+ gap: 6px;
473
+ margin-bottom: 16px;
474
+ flex-wrap: wrap;
475
+ }
476
+
206
477
  .detail-status {
207
478
  display: flex;
208
479
  justify-content: center;
209
480
  padding: 40px 0;
210
481
  }
482
+
483
+ .detail-body {
484
+ max-height: 65vh;
485
+ overflow-y: auto;
486
+ padding: 16px 24px;
487
+ }
488
+
489
+ .edit-panel {
490
+ margin-bottom: 4px;
491
+ }
492
+
493
+ .edit-row {
494
+ display: flex;
495
+ align-items: flex-start;
496
+ gap: 10px;
497
+ margin-bottom: 10px;
498
+ }
499
+
500
+ .edit-label {
501
+ width: 50px;
502
+ font-size: 13px;
503
+ color: #888;
504
+ line-height: 30px;
505
+ flex-shrink: 0;
506
+ }
507
+
508
+
509
+ .pull-modal-body {
510
+ min-height: 100px;
511
+ }
512
+
513
+ .pull-section {
514
+ margin-top: 12px;
515
+ }
516
+
517
+ .pull-input-row {
518
+ display: flex;
519
+ gap: 8px;
520
+ margin-bottom: 8px;
521
+ }
522
+
523
+ .pull-input-row .n-input {
524
+ flex: 1;
525
+ }
526
+
527
+ .pull-error {
528
+ margin-bottom: 8px;
529
+ }
530
+
531
+ .pull-result {
532
+ margin-top: 8px;
533
+ }
534
+
535
+ .pull-result-line {
536
+ display: flex;
537
+ align-items: center;
538
+ flex-wrap: wrap;
539
+ gap: 4px;
540
+ margin-bottom: 8px;
541
+ font-size: 13px;
542
+ }
543
+
544
+ .pull-result-label {
545
+ color: #888;
546
+ flex-shrink: 0;
547
+ }
548
+
549
+ .pull-result-hint {
550
+ color: #999;
551
+ font-size: 12px;
552
+ }
553
+
554
+ .install-panel {
555
+ margin-top: 12px;
556
+ padding: 12px;
557
+ background: rgba(0, 0, 0, 0.02);
558
+ border-radius: 6px;
559
+ }
560
+
561
+ .install-panel-label {
562
+ font-size: 13px;
563
+ margin-bottom: 8px;
564
+ color: #666;
565
+ }
566
+
567
+ .install-check-list {
568
+ margin-bottom: 10px;
569
+ }
570
+
571
+ .install-btn {
572
+ float: right;
573
+ }
574
+
575
+ .edit-actions {
576
+ display: flex;
577
+ justify-content: flex-end;
578
+ gap: 8px;
579
+ }
211
580
  </style>
@@ -211,6 +211,7 @@ import {
211
211
  installSkills,
212
212
  getSkillsReadme,
213
213
  } from "../api/skills";
214
+ import { recordUsage } from "../utils/usage";
214
215
 
215
216
  // marked 配置:代码高亮 + 外链安全
216
217
  marked.use({
@@ -348,6 +349,7 @@ async function onApplyCustom() {
348
349
  message.success(`已安装 ${customSelected.value.length} 个技能`);
349
350
  activeId.value = "custom";
350
351
  showCustom.value = false;
352
+ customSelected.value.forEach(recordUsage);
351
353
  emit("refresh");
352
354
  }
353
355
  } catch {
@@ -409,6 +411,7 @@ async function onApply(profile) {
409
411
  if (res.ok) {
410
412
  activeId.value = profile.id;
411
413
  message.success(`已切换到「${profile.name}」`);
414
+ (profile.skills || []).forEach(recordUsage);
412
415
  emit("refresh");
413
416
  }
414
417
  } catch {
@@ -441,6 +444,7 @@ onMounted(() => {
441
444
  display: grid;
442
445
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
443
446
  gap: 16px;
447
+ margin: 2px;
444
448
  }
445
449
 
446
450
  .scene-card {
@@ -18,8 +18,21 @@
18
18
 
19
19
  <!-- 初始化提示 -->
20
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 缺失,初始化后将生成骨架文件
21
+ <template #header>本地源缺少必要文件</template>
22
+ <div class="missing-list">
23
+ <template v-if="sourceInfo.missingConfig?.length">
24
+ <div class="missing-item" v-for="f in sourceInfo.missingConfig" :key="f">
25
+ 缺少配置文件:<code>{{ f }}</code>
26
+ </div>
27
+ </template>
28
+ <template v-if="sourceInfo.missingSkillDocs?.length">
29
+ <div class="missing-item">
30
+ 以下技能缺少 SKILL.md 文档:
31
+ <code v-for="name in sourceInfo.missingSkillDocs" :key="name" class="skill-tag">{{ name }}</code>
32
+ </div>
33
+ </template>
34
+ <div class="missing-hint">可手动检测该文件夹是否为skill,若该目录不是 skill,可手动删除</div>
35
+ </div>
23
36
  </n-alert>
24
37
 
25
38
  <div class="source-actions">
@@ -355,6 +368,35 @@ onMounted(loadSource);
355
368
  margin-bottom: 12px;
356
369
  }
357
370
 
371
+ .missing-list {
372
+ display: flex;
373
+ flex-direction: column;
374
+ gap: 4px;
375
+ }
376
+
377
+ .missing-item {
378
+ font-size: 13px;
379
+ line-height: 1.6;
380
+ }
381
+
382
+ .missing-item code {
383
+ font-size: 12px;
384
+ background: rgba(0, 0, 0, 0.06);
385
+ padding: 1px 6px;
386
+ border-radius: 3px;
387
+ }
388
+
389
+ .skill-tag {
390
+ display: inline-block;
391
+ margin: 1px 2px;
392
+ }
393
+
394
+ .missing-hint {
395
+ margin-top: 4px;
396
+ font-size: 12px;
397
+ opacity: 0.7;
398
+ }
399
+
358
400
  .source-status {
359
401
  margin-bottom: 16px;
360
402
  }
@@ -0,0 +1,11 @@
1
+ const { defineConfig } = require("vite");
2
+ const vue = require("@vitejs/plugin-vue");
3
+
4
+ module.exports = defineConfig({
5
+ root: __dirname,
6
+ plugins: [vue()],
7
+ build: {
8
+ outDir: "dist",
9
+ emptyOutDir: true,
10
+ },
11
+ });
@@ -1,10 +0,0 @@
1
- [2026-05-10 15:19:53] [INFO] Install started (upgrade)
2
- [2026-05-10 15:19:53] [SKILL] frontend-code-review (1.0.0) → skipped (exists)
3
- [2026-05-10 15:19:53] [SKILL] frontend-design (1.0.0) → skipped (exists)
4
- [2026-05-10 15:19:53] [SKILL] skill-creator (1.0.0) → skipped (exists)
5
- [2026-05-10 15:19:53] [SKILL] ui-ux-pro-max (1.0.0) → skipped (exists)
6
- [2026-05-10 15:19:53] [SKILL] v3-fe-biz-patterns (1.0.0) → skipped (exists)
7
- [2026-05-10 15:19:53] [SKILL] vue (1.0.0) → skipped (exists)
8
- [2026-05-10 15:19:53] [SETTINGS] settings.json merged
9
- [2026-05-10 15:19:53] [BACKUP] backups/CLAUDE.md.20260510.151953 (CLAUDE.md user version preserved)
10
- [2026-05-10 15:33:00] [INFO] Install started (upgrade)
@@ -1,67 +0,0 @@
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,29 +0,0 @@
1
- {
2
- "skills": {
3
- "frontend-code-review": {
4
- "enabled": true,
5
- "description": "前端代码全面审查"
6
- },
7
- "frontend-design": {
8
- "enabled": true,
9
- "description": "创意前端界面设计,落地页/品牌页/营销页"
10
- },
11
- "skill-creator": {
12
- "enabled": true,
13
- "description": "创建和更新技能指令"
14
- },
15
- "ui-ux-pro-max": {
16
- "enabled": true,
17
- "description": "UI/UX 设计智能系统,50 种样式,21 种配色方案"
18
- },
19
- "vue": {
20
- "enabled": true,
21
- "description": "Vue 3 组件/组合式函数开发,提供 Composition API 最佳实践"
22
- },
23
- "v3-fe-biz-patterns": {
24
- "enabled": true,
25
- "description": "前端业务模式库,loading/滚动加载/导入导出/批量操作/表单联动/大列表渲染/Service层封装/Pinia Store模式/分页数据管理等常见业务场景"
26
- }
27
- },
28
- "always_apply_skills": []
29
- }
@@ -1,6 +0,0 @@
1
- {
2
- "name": "frontend-code-review",
3
- "version": "1.0.0",
4
- "description": "前端代码全面审查,含 React/Vue/Angular/原生 JS/TS 项目",
5
- "tags": ["frontend", "review", "react", "vue", "angular"]
6
- }