mdk-skills 2.1.12 → 2.1.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.
Files changed (184) hide show
  1. package/.claude/profiles.json +1 -1
  2. package/.claude/settings.json +7 -6
  3. package/package.json +9 -2
  4. package/scripts/cli.js +4 -1
  5. package/scripts/web-ui/index.html +12 -0
  6. package/scripts/web-ui/server.js +461 -0
  7. package/scripts/web-ui/src/App.vue +98 -0
  8. package/scripts/web-ui/src/api/skills.js +65 -0
  9. package/scripts/web-ui/src/components/SkillCard.vue +72 -0
  10. package/scripts/web-ui/src/components/StatusBar.vue +67 -0
  11. package/scripts/web-ui/src/main.js +9 -0
  12. package/scripts/web-ui/src/router/index.js +15 -0
  13. package/scripts/web-ui/src/styles/main.css +55 -0
  14. package/scripts/web-ui/src/views/Dashboard.vue +66 -0
  15. package/scripts/web-ui/src/views/SceneSwitch.vue +181 -0
  16. package/scripts/web-ui/src/views/Settings.vue +220 -0
  17. package/.claude/backups/20260510.120508/profiles.json +0 -67
  18. package/.claude/backups/20260510.120508/settings.json +0 -34
  19. package/.claude/backups/20260510.120508/skills/frontend-code-review/.meta.json +0 -6
  20. package/.claude/backups/20260510.120508/skills/frontend-code-review/SKILL.md +0 -167
  21. package/.claude/backups/20260510.120508/skills/frontend-code-review/references/checklist.md +0 -298
  22. package/.claude/backups/20260510.120508/skills/frontend-design/.meta.json +0 -6
  23. package/.claude/backups/20260510.120508/skills/frontend-design/LICENSE.txt +0 -177
  24. package/.claude/backups/20260510.120508/skills/frontend-design/SKILL.md +0 -42
  25. package/.claude/backups/20260510.120508/skills/skill-creator/.meta.json +0 -6
  26. package/.claude/backups/20260510.120508/skills/skill-creator/SKILL.md +0 -356
  27. package/.claude/backups/20260510.120508/skills/skill-creator/references/output-patterns.md +0 -82
  28. package/.claude/backups/20260510.120508/skills/skill-creator/references/workflows.md +0 -28
  29. package/.claude/backups/20260510.120508/skills/skill-creator/scripts/init_skill.py +0 -303
  30. package/.claude/backups/20260510.120508/skills/skill-creator/scripts/package_skill.py +0 -110
  31. package/.claude/backups/20260510.120508/skills/skill-creator/scripts/quick_validate.py +0 -95
  32. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/.meta.json +0 -6
  33. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/SKILL.md +0 -228
  34. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/charts.csv +0 -26
  35. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/colors.csv +0 -97
  36. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/landing.csv +0 -31
  37. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/products.csv +0 -97
  38. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/prompts.csv +0 -24
  39. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  40. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  41. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  42. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  43. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  44. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  45. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  46. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  47. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  48. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  49. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/styles.csv +0 -59
  50. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/typography.csv +0 -58
  51. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  52. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/scripts/core.py +0 -238
  53. package/.claude/backups/20260510.120508/skills/ui-ux-pro-max/scripts/search.py +0 -61
  54. package/.claude/backups/20260510.120508/skills/v3-fe-biz-patterns/.meta.json +0 -6
  55. package/.claude/backups/20260510.120508/skills/v3-fe-biz-patterns/SKILL.md +0 -26
  56. package/.claude/backups/20260510.120508/skills/v3-fe-biz-patterns/references/infinite-scroll.md +0 -292
  57. package/.claude/backups/20260510.120508/skills/v3-fe-biz-patterns/references/pinia-store.md +0 -174
  58. package/.claude/backups/20260510.120508/skills/v3-fe-biz-patterns/references/service-layer.md +0 -198
  59. package/.claude/backups/20260510.120508/skills/v3-fe-biz-patterns/references/tab-anchor.md +0 -1125
  60. package/.claude/backups/20260510.120508/skills/v3-fe-biz-patterns/references/use-loading.md +0 -114
  61. package/.claude/backups/20260510.120508/skills/vue/.meta.json +0 -6
  62. package/.claude/backups/20260510.120508/skills/vue/SKILL.md +0 -103
  63. package/.claude/backups/20260510.120508/skills/vue/references/components.md +0 -323
  64. package/.claude/backups/20260510.120508/skills/vue/references/composables.md +0 -358
  65. package/.claude/backups/20260510.120508/skills/vue/references/directives.md +0 -225
  66. package/.claude/backups/20260510.120508/skills/vue/references/gotchas.md +0 -438
  67. package/.claude/backups/20260510.120508/skills/vue/references/provide-inject.md +0 -174
  68. package/.claude/backups/20260510.120508/skills/vue/references/reactivity.md +0 -289
  69. package/.claude/backups/20260510.120508/skills/vue/references/router.md +0 -181
  70. package/.claude/backups/20260510.120508/skills/vue/references/testing.md +0 -294
  71. package/.claude/backups/20260510.120508/skills/vue/references/typescript.md +0 -172
  72. package/.claude/backups/20260510.120508/skills/vue/references/utils-client.md +0 -156
  73. package/.claude/backups/20260510.120608/profiles.json +0 -67
  74. package/.claude/backups/20260510.120608/settings.json +0 -34
  75. package/.claude/backups/20260510.120608/skills/frontend-code-review/.meta.json +0 -6
  76. package/.claude/backups/20260510.120608/skills/frontend-code-review/SKILL.md +0 -167
  77. package/.claude/backups/20260510.120608/skills/frontend-code-review/references/checklist.md +0 -298
  78. package/.claude/backups/20260510.120608/skills/frontend-design/.meta.json +0 -6
  79. package/.claude/backups/20260510.120608/skills/frontend-design/LICENSE.txt +0 -177
  80. package/.claude/backups/20260510.120608/skills/frontend-design/SKILL.md +0 -42
  81. package/.claude/backups/20260510.120608/skills/skill-creator/.meta.json +0 -6
  82. package/.claude/backups/20260510.120608/skills/skill-creator/SKILL.md +0 -356
  83. package/.claude/backups/20260510.120608/skills/skill-creator/references/output-patterns.md +0 -82
  84. package/.claude/backups/20260510.120608/skills/skill-creator/references/workflows.md +0 -28
  85. package/.claude/backups/20260510.120608/skills/skill-creator/scripts/init_skill.py +0 -303
  86. package/.claude/backups/20260510.120608/skills/skill-creator/scripts/package_skill.py +0 -110
  87. package/.claude/backups/20260510.120608/skills/skill-creator/scripts/quick_validate.py +0 -95
  88. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/.meta.json +0 -6
  89. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/SKILL.md +0 -228
  90. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/charts.csv +0 -26
  91. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/colors.csv +0 -97
  92. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/landing.csv +0 -31
  93. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/products.csv +0 -97
  94. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/prompts.csv +0 -24
  95. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  96. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  97. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  98. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  99. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  100. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  101. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  102. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  103. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  104. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  105. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/styles.csv +0 -59
  106. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/typography.csv +0 -58
  107. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  108. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/scripts/core.py +0 -238
  109. package/.claude/backups/20260510.120608/skills/ui-ux-pro-max/scripts/search.py +0 -61
  110. package/.claude/backups/20260510.120608/skills/v3-fe-biz-patterns/.meta.json +0 -6
  111. package/.claude/backups/20260510.120608/skills/v3-fe-biz-patterns/SKILL.md +0 -26
  112. package/.claude/backups/20260510.120608/skills/v3-fe-biz-patterns/references/infinite-scroll.md +0 -292
  113. package/.claude/backups/20260510.120608/skills/v3-fe-biz-patterns/references/pinia-store.md +0 -174
  114. package/.claude/backups/20260510.120608/skills/v3-fe-biz-patterns/references/service-layer.md +0 -198
  115. package/.claude/backups/20260510.120608/skills/v3-fe-biz-patterns/references/tab-anchor.md +0 -1125
  116. package/.claude/backups/20260510.120608/skills/v3-fe-biz-patterns/references/use-loading.md +0 -114
  117. package/.claude/backups/20260510.120608/skills/vue/.meta.json +0 -6
  118. package/.claude/backups/20260510.120608/skills/vue/SKILL.md +0 -103
  119. package/.claude/backups/20260510.120608/skills/vue/references/components.md +0 -323
  120. package/.claude/backups/20260510.120608/skills/vue/references/composables.md +0 -358
  121. package/.claude/backups/20260510.120608/skills/vue/references/directives.md +0 -225
  122. package/.claude/backups/20260510.120608/skills/vue/references/gotchas.md +0 -438
  123. package/.claude/backups/20260510.120608/skills/vue/references/provide-inject.md +0 -174
  124. package/.claude/backups/20260510.120608/skills/vue/references/reactivity.md +0 -289
  125. package/.claude/backups/20260510.120608/skills/vue/references/router.md +0 -181
  126. package/.claude/backups/20260510.120608/skills/vue/references/testing.md +0 -294
  127. package/.claude/backups/20260510.120608/skills/vue/references/typescript.md +0 -172
  128. package/.claude/backups/20260510.120608/skills/vue/references/utils-client.md +0 -156
  129. package/.claude/backups/20260510.120653/profiles.json +0 -67
  130. package/.claude/backups/20260510.120653/settings.json +0 -34
  131. package/.claude/backups/20260510.120653/skills/frontend-code-review/.meta.json +0 -6
  132. package/.claude/backups/20260510.120653/skills/frontend-code-review/SKILL.md +0 -167
  133. package/.claude/backups/20260510.120653/skills/frontend-code-review/references/checklist.md +0 -298
  134. package/.claude/backups/20260510.120653/skills/frontend-design/.meta.json +0 -6
  135. package/.claude/backups/20260510.120653/skills/frontend-design/LICENSE.txt +0 -177
  136. package/.claude/backups/20260510.120653/skills/frontend-design/SKILL.md +0 -42
  137. package/.claude/backups/20260510.120653/skills/skill-creator/.meta.json +0 -6
  138. package/.claude/backups/20260510.120653/skills/skill-creator/SKILL.md +0 -356
  139. package/.claude/backups/20260510.120653/skills/skill-creator/references/output-patterns.md +0 -82
  140. package/.claude/backups/20260510.120653/skills/skill-creator/references/workflows.md +0 -28
  141. package/.claude/backups/20260510.120653/skills/skill-creator/scripts/init_skill.py +0 -303
  142. package/.claude/backups/20260510.120653/skills/skill-creator/scripts/package_skill.py +0 -110
  143. package/.claude/backups/20260510.120653/skills/skill-creator/scripts/quick_validate.py +0 -95
  144. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/.meta.json +0 -6
  145. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/SKILL.md +0 -228
  146. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/charts.csv +0 -26
  147. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/colors.csv +0 -97
  148. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/landing.csv +0 -31
  149. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/products.csv +0 -97
  150. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/prompts.csv +0 -24
  151. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  152. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  153. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  154. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  155. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  156. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  157. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  158. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  159. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  160. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  161. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/styles.csv +0 -59
  162. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/typography.csv +0 -58
  163. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  164. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/scripts/core.py +0 -238
  165. package/.claude/backups/20260510.120653/skills/ui-ux-pro-max/scripts/search.py +0 -61
  166. package/.claude/backups/20260510.120653/skills/v3-fe-biz-patterns/.meta.json +0 -6
  167. package/.claude/backups/20260510.120653/skills/v3-fe-biz-patterns/SKILL.md +0 -26
  168. package/.claude/backups/20260510.120653/skills/v3-fe-biz-patterns/references/infinite-scroll.md +0 -292
  169. package/.claude/backups/20260510.120653/skills/v3-fe-biz-patterns/references/pinia-store.md +0 -174
  170. package/.claude/backups/20260510.120653/skills/v3-fe-biz-patterns/references/service-layer.md +0 -198
  171. package/.claude/backups/20260510.120653/skills/v3-fe-biz-patterns/references/tab-anchor.md +0 -1125
  172. package/.claude/backups/20260510.120653/skills/v3-fe-biz-patterns/references/use-loading.md +0 -114
  173. package/.claude/backups/20260510.120653/skills/vue/.meta.json +0 -6
  174. package/.claude/backups/20260510.120653/skills/vue/SKILL.md +0 -103
  175. package/.claude/backups/20260510.120653/skills/vue/references/components.md +0 -323
  176. package/.claude/backups/20260510.120653/skills/vue/references/composables.md +0 -358
  177. package/.claude/backups/20260510.120653/skills/vue/references/directives.md +0 -225
  178. package/.claude/backups/20260510.120653/skills/vue/references/gotchas.md +0 -438
  179. package/.claude/backups/20260510.120653/skills/vue/references/provide-inject.md +0 -174
  180. package/.claude/backups/20260510.120653/skills/vue/references/reactivity.md +0 -289
  181. package/.claude/backups/20260510.120653/skills/vue/references/router.md +0 -181
  182. package/.claude/backups/20260510.120653/skills/vue/references/testing.md +0 -294
  183. package/.claude/backups/20260510.120653/skills/vue/references/typescript.md +0 -172
  184. package/.claude/backups/20260510.120653/skills/vue/references/utils-client.md +0 -156
@@ -3,7 +3,7 @@
3
3
  "profiles": [
4
4
  {
5
5
  "id": "vue3-frontend",
6
- "name": "Vue3 前端专项233",
6
+ "name": "Vue3 前端专项",
7
7
  "description": "Vue3 技术栈 | 业务架构 | 界面设计 | 代码规范评审",
8
8
  "skills": [
9
9
  "vue",
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "skills": {
3
3
  "frontend-code-review": {
4
- "enabled": false,
4
+ "enabled": true,
5
5
  "description": "前端代码全面审查"
6
6
  },
7
7
  "frontend-design": {
8
- "enabled": false,
8
+ "enabled": true,
9
9
  "description": "创意前端界面设计,落地页/品牌页/营销页"
10
10
  },
11
11
  "skill-creator": {
12
- "enabled": false,
12
+ "enabled": true,
13
13
  "description": "创建和更新技能指令"
14
14
  },
15
15
  "ui-ux-pro-max": {
16
- "enabled": false,
16
+ "enabled": true,
17
17
  "description": "UI/UX 设计智能系统,50 种样式,21 种配色方案"
18
18
  },
19
19
  "vue": {
@@ -21,7 +21,7 @@
21
21
  "description": "Vue 3 组件/组合式函数开发,提供 Composition API 最佳实践"
22
22
  },
23
23
  "v3-fe-biz-patterns": {
24
- "enabled": false,
24
+ "enabled": true,
25
25
  "description": "前端业务模式库,loading/滚动加载/导入导出/批量操作/表单联动/大列表渲染/Service层封装/Pinia Store模式/分页数据管理等常见业务场景"
26
26
  }
27
27
  },
@@ -30,5 +30,6 @@
30
30
  "frontend-design",
31
31
  "frontend-code-review",
32
32
  "ui-ux-pro-max"
33
- ]
33
+ ],
34
+ "_active_profile": "vue3-frontend"
34
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdk-skills",
3
- "version": "2.1.12",
3
+ "version": "2.1.13",
4
4
  "description": "mdk-engineer - 沉稳靠谱的前端开发助手 Claude Skills 配置包,一键注入 .claude/ 技能目录和 CLAUDE.md 人设配置",
5
5
  "author": "XiaoMa",
6
6
  "license": "MIT",
@@ -18,6 +18,13 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@inquirer/prompts": "^8.4.2",
21
- "chalk": "^5.6.2"
21
+ "@vicons/ionicons5": "^0.13.0",
22
+ "@vicons/material": "^0.13.0",
23
+ "@vitejs/plugin-vue": "^6.0.6",
24
+ "chalk": "^5.6.2",
25
+ "naive-ui": "^2.44.1",
26
+ "vite": "^8.0.11",
27
+ "vue": "^3.5.34",
28
+ "vue-router": "^5.0.6"
22
29
  }
23
30
  }
package/scripts/cli.js CHANGED
@@ -487,7 +487,7 @@ function cmdDisconnect() {
487
487
 
488
488
  // ---------- 主入口 ----------
489
489
 
490
- const COMMANDS = ["list", "connect", "sync", "disconnect"];
490
+ const COMMANDS = ["list", "connect", "sync", "disconnect", "ui"];
491
491
 
492
492
  async function main() {
493
493
  const command = process.argv[2];
@@ -511,6 +511,7 @@ async function main() {
511
511
  npx mdk-skills connect 绑定本地技能源仓库路径
512
512
  npx mdk-skills sync 将项目中的技能修改推送到仓库
513
513
  npx mdk-skills disconnect 解绑本地源,恢复使用 npm 包内置技能
514
+ npx mdk-skills ui 启动 Web 管理面板
514
515
  npx mdk-skills --connect 绑定本地仓库后直接进入场景选择
515
516
  npx mdk-skills --help 显示帮助
516
517
 
@@ -542,6 +543,8 @@ async function main() {
542
543
  cmdSync();
543
544
  } else if (command === "disconnect") {
544
545
  cmdDisconnect();
546
+ } else if (command === "ui") {
547
+ require("./web-ui/server");
545
548
  } else {
546
549
  console.log(` 未知命令: ${command}\n 可用: ${COMMANDS.join(", ")}\n`);
547
550
  process.exit(1);
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>mdk-skills 管理面板</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.js"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,461 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { createServer: createViteServer } = require("vite");
4
+ const core = require("../core");
5
+
6
+ // ---------- 路径(与 cli.js 保持一致) ----------
7
+
8
+ const projectRoot = (() => {
9
+ try {
10
+ return fs.realpathSync.native(process.cwd());
11
+ } catch {
12
+ return process.cwd();
13
+ }
14
+ })();
15
+ const packageDir = path.join(__dirname, "..", "..");
16
+ const skillsSource = core.getSkillsSource(projectRoot, packageDir);
17
+ const claudeDest = path.join(projectRoot, ".claude");
18
+ const skillsDest = path.join(claudeDest, "skills");
19
+ const settingsPath = path.join(claudeDest, "settings.json");
20
+ const pkgSkillsSource = path.join(skillsSource, ".claude", "skills");
21
+
22
+ // ---------- 工具函数 ----------
23
+
24
+ function readSettings() {
25
+ if (!fs.existsSync(settingsPath))
26
+ return { skills: {}, always_apply_skills: [] };
27
+ try {
28
+ return JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
29
+ } catch {
30
+ return { skills: {}, always_apply_skills: [] };
31
+ }
32
+ }
33
+
34
+ function writeSettings(settings) {
35
+ if (!fs.existsSync(claudeDest)) {
36
+ fs.mkdirSync(claudeDest, { recursive: true });
37
+ }
38
+ fs.writeFileSync(
39
+ settingsPath,
40
+ JSON.stringify(settings, null, 2) + "\n",
41
+ "utf-8",
42
+ );
43
+ }
44
+
45
+ function sendJSON(res, data, status = 200) {
46
+ res.writeHead(status, { "Content-Type": "application/json" });
47
+ res.end(JSON.stringify(data));
48
+ }
49
+
50
+ function parseBody(req) {
51
+ return new Promise((resolve) => {
52
+ let body = "";
53
+ req.on("data", (chunk) => (body += chunk));
54
+ req.on("end", () => {
55
+ try {
56
+ resolve(JSON.parse(body));
57
+ } catch {
58
+ resolve({});
59
+ }
60
+ });
61
+ });
62
+ }
63
+
64
+ // 手动递归拷贝目录
65
+ function copyDirSync(src, dest) {
66
+ fs.mkdirSync(dest, { recursive: true });
67
+ for (const item of fs.readdirSync(src)) {
68
+ const srcPath = path.join(src, item);
69
+ const destPath = path.join(dest, item);
70
+ if (fs.statSync(srcPath).isDirectory()) {
71
+ copyDirSync(srcPath, destPath);
72
+ } else {
73
+ fs.copyFileSync(srcPath, destPath);
74
+ }
75
+ }
76
+ }
77
+
78
+ // 读取 profiles.json
79
+ function loadProfiles() {
80
+ const profilesPath = path.join(skillsSource, ".claude", "profiles.json");
81
+ if (!fs.existsSync(profilesPath)) return null;
82
+ try {
83
+ return JSON.parse(fs.readFileSync(profilesPath, "utf-8")).profiles;
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+
89
+ // 应用场景
90
+ function applyProfile(profile) {
91
+ const pkgSkills = fs.existsSync(pkgSkillsSource)
92
+ ? fs.readdirSync(pkgSkillsSource)
93
+ : [];
94
+ const settings = readSettings();
95
+
96
+ let selected;
97
+ if (profile.skills === null) {
98
+ return { selected: [], message: "自定义场景,不做自动切换" };
99
+ } else if (profile.skills.length === 0) {
100
+ selected = [];
101
+ } else {
102
+ selected = profile.skills.filter((s) => pkgSkills.includes(s));
103
+ }
104
+
105
+ // 安装/删除技能
106
+ if (!fs.existsSync(skillsDest)) {
107
+ fs.mkdirSync(skillsDest, { recursive: true });
108
+ }
109
+ for (const name of pkgSkills) {
110
+ const src = path.join(pkgSkillsSource, name);
111
+ const dest = path.join(skillsDest, name);
112
+ if (selected.includes(name)) {
113
+ if (!fs.existsSync(dest)) {
114
+ try {
115
+ copyDirSync(src, dest);
116
+ } catch (err) {
117
+ console.error(`安装技能 "${name}" 失败:`, err.message);
118
+ }
119
+ }
120
+ } else {
121
+ if (fs.existsSync(dest)) {
122
+ fs.rmSync(dest, { recursive: true, force: true });
123
+ }
124
+ }
125
+ }
126
+
127
+ // 更新 settings
128
+ if (!settings.skills) settings.skills = {};
129
+ for (const name of pkgSkills) {
130
+ const enabled = selected.includes(name);
131
+ if (!settings.skills[name]) settings.skills[name] = {};
132
+ settings.skills[name].enabled = enabled;
133
+ }
134
+
135
+ if (profile.always_apply) {
136
+ settings.always_apply_skills = profile.always_apply;
137
+ }
138
+
139
+ settings._active_profile = profile.id;
140
+ writeSettings(settings);
141
+
142
+ // 复制 profiles.json
143
+ const profilesSource = path.join(skillsSource, ".claude", "profiles.json");
144
+ const profilesDest = path.join(claudeDest, "profiles.json");
145
+ if (fs.existsSync(profilesSource)) {
146
+ fs.copyFileSync(profilesSource, profilesDest);
147
+ }
148
+
149
+ return {
150
+ selected,
151
+ enabled: selected.length,
152
+ disabled: pkgSkills.length - selected.length,
153
+ };
154
+ }
155
+
156
+ // 安装勾选的技能
157
+ function installSelectedSkills(selectedNames) {
158
+ if (!fs.existsSync(pkgSkillsSource)) return { installed: 0 };
159
+
160
+ if (!fs.existsSync(skillsDest)) {
161
+ fs.mkdirSync(skillsDest, { recursive: true });
162
+ }
163
+
164
+ const allPkgSkills = fs.readdirSync(pkgSkillsSource);
165
+ let installedCount = 0;
166
+
167
+ for (const name of allPkgSkills) {
168
+ const src = path.join(pkgSkillsSource, name);
169
+ const dest = path.join(skillsDest, name);
170
+
171
+ if (selectedNames.includes(name)) {
172
+ if (!fs.existsSync(dest)) {
173
+ try {
174
+ copyDirSync(src, dest);
175
+ installedCount++;
176
+ } catch (err) {
177
+ console.error(`安装技能 "${name}" 失败:`, err.message);
178
+ }
179
+ }
180
+ } else {
181
+ if (fs.existsSync(dest)) {
182
+ fs.rmSync(dest, { recursive: true, force: true });
183
+ }
184
+ }
185
+ }
186
+
187
+ const settings = readSettings();
188
+ if (!settings.skills) settings.skills = {};
189
+ for (const name of allPkgSkills) {
190
+ const enabled = selectedNames.includes(name);
191
+ if (!settings.skills[name]) settings.skills[name] = {};
192
+ settings.skills[name].enabled = enabled;
193
+ }
194
+ writeSettings(settings);
195
+
196
+ return { installed: installedCount };
197
+ }
198
+
199
+ // ---------- API 路由 ----------
200
+
201
+ async function handleApi(req, res) {
202
+ const url = new URL(req.url, `http://${req.headers.host || "localhost"}`);
203
+ const pathname = url.pathname;
204
+ const method = req.method;
205
+
206
+ // CORS
207
+ res.setHeader("Access-Control-Allow-Origin", "*");
208
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
209
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
210
+
211
+ if (method === "OPTIONS") {
212
+ res.writeHead(204);
213
+ res.end();
214
+ return;
215
+ }
216
+
217
+ try {
218
+ // GET /api/skills — 获取所有技能列表(含启用状态)
219
+ if (method === "GET" && pathname === "/api/skills") {
220
+ const pkgSkills = core.getPackageSkills(skillsSource);
221
+ const userSkills = core.getUserSkills(claudeDest);
222
+ const userMap = new Map(userSkills.map((s) => [s.name, s]));
223
+ const result = pkgSkills.map((s) => ({
224
+ ...s,
225
+ enabled: userMap.has(s.name) ? userMap.get(s.name).enabled : false,
226
+ installed: userMap.has(s.name),
227
+ }));
228
+ return sendJSON(res, result);
229
+ }
230
+
231
+ // POST /api/skills/toggle — 开关技能
232
+ if (method === "POST" && pathname === "/api/skills/toggle") {
233
+ const body = await parseBody(req);
234
+ const { name, enabled } = body;
235
+ if (!name) return sendJSON(res, { error: "缺少技能名" }, 400);
236
+ const settings = readSettings();
237
+ if (!settings.skills) settings.skills = {};
238
+ if (!settings.skills[name]) settings.skills[name] = {};
239
+ settings.skills[name].enabled = !!enabled;
240
+ writeSettings(settings);
241
+ return sendJSON(res, { ok: true, name, enabled: !!enabled });
242
+ }
243
+
244
+ // POST /api/skills/install — 自定义勾选安装
245
+ if (method === "POST" && pathname === "/api/skills/install") {
246
+ const body = await parseBody(req);
247
+ const { selected } = body;
248
+ if (!Array.isArray(selected))
249
+ return sendJSON(res, { error: "参数错误" }, 400);
250
+ const result = installSelectedSkills(selected);
251
+ return sendJSON(res, { ok: true, ...result });
252
+ }
253
+
254
+ // GET /api/profiles — 获取场景列表
255
+ if (method === "GET" && pathname === "/api/profiles") {
256
+ const profiles = loadProfiles();
257
+ const settings = readSettings();
258
+ return sendJSON(res, {
259
+ profiles: profiles || [],
260
+ activeProfile: settings._active_profile || null,
261
+ });
262
+ }
263
+
264
+ // POST /api/profiles/apply — 应用场景
265
+ if (method === "POST" && pathname === "/api/profiles/apply") {
266
+ const body = await parseBody(req);
267
+ const { profileId } = body;
268
+ if (!profileId) return sendJSON(res, { error: "缺少场景 ID" }, 400);
269
+ const profiles = loadProfiles();
270
+ const profile = profiles?.find((p) => p.id === profileId);
271
+ if (!profile)
272
+ return sendJSON(res, { error: "场景不存在: " + profileId }, 404);
273
+ const result = applyProfile(profile);
274
+ return sendJSON(res, { ok: true, ...result });
275
+ }
276
+
277
+ // GET /api/status — 当前状态总览
278
+ if (method === "GET" && pathname === "/api/status") {
279
+ const settings = readSettings();
280
+ const userSkills = core.getUserSkills(claudeDest);
281
+ const profiles = loadProfiles();
282
+ const activeProfile = profiles?.find(
283
+ (p) => p.id === settings._active_profile,
284
+ );
285
+ const enabledCount = userSkills.filter((s) => s.enabled).length;
286
+ const mdExists = fs.existsSync(path.join(projectRoot, "CLAUDE.md"));
287
+ return sendJSON(res, {
288
+ enabledCount,
289
+ totalCount: userSkills.length,
290
+ activeProfile: activeProfile
291
+ ? { id: activeProfile.id, name: activeProfile.name }
292
+ : null,
293
+ sourcePath: settings._skill_source || null,
294
+ hasClaudeMd: mdExists,
295
+ });
296
+ }
297
+
298
+ // GET /api/source — 获取本地源信息
299
+ if (method === "GET" && pathname === "/api/source") {
300
+ const settings = readSettings();
301
+ return sendJSON(res, {
302
+ connected: !!settings._skill_source,
303
+ path: settings._skill_source || null,
304
+ });
305
+ }
306
+
307
+ // POST /api/source/connect — 绑定本地源
308
+ if (method === "POST" && pathname === "/api/source/connect") {
309
+ const body = await parseBody(req);
310
+ const repoPath = body.path;
311
+ if (!repoPath)
312
+ return sendJSON(res, { error: "缺少仓库路径" }, 400);
313
+ const resolved = path.resolve(repoPath);
314
+ if (!fs.existsSync(path.join(resolved, ".claude", "skills"))) {
315
+ return sendJSON(res, { error: `路径 "${resolved}" 下没有 .claude/skills/` }, 400);
316
+ }
317
+ const settings = readSettings();
318
+ settings._skill_source = resolved;
319
+ writeSettings(settings);
320
+ return sendJSON(res, { ok: true, path: resolved });
321
+ }
322
+
323
+ // POST /api/source/disconnect — 解绑本地源
324
+ if (method === "POST" && pathname === "/api/source/disconnect") {
325
+ const settings = readSettings();
326
+ if (!settings._skill_source) {
327
+ return sendJSON(res, { error: "当前未绑定任何技能源" }, 400);
328
+ }
329
+ delete settings._skill_source;
330
+ writeSettings(settings);
331
+ return sendJSON(res, { ok: true });
332
+ }
333
+
334
+ // POST /api/source/sync — 同步到本地源
335
+ if (method === "POST" && pathname === "/api/source/sync") {
336
+ const settings = readSettings();
337
+ const sourcePath = settings._skill_source;
338
+ if (!sourcePath)
339
+ return sendJSON(res, { error: "尚未绑定技能源" }, 400);
340
+ if (!fs.existsSync(path.join(sourcePath, ".claude"))) {
341
+ return sendJSON(res, { error: "绑定的路径已失效" }, 400);
342
+ }
343
+
344
+ const repoClaude = path.join(sourcePath, ".claude");
345
+ core.backupDir(repoClaude);
346
+
347
+ // 技能反推
348
+ const repoSkills = path.join(sourcePath, ".claude", "skills");
349
+ if (!fs.existsSync(repoSkills))
350
+ fs.mkdirSync(repoSkills, { recursive: true });
351
+ if (fs.existsSync(skillsDest)) {
352
+ for (const name of fs.readdirSync(skillsDest)) {
353
+ const src = path.join(skillsDest, name);
354
+ const dest = path.join(repoSkills, name);
355
+ if (fs.existsSync(dest))
356
+ fs.rmSync(dest, { recursive: true, force: true });
357
+ copyDirSync(src, dest);
358
+ }
359
+ }
360
+
361
+ // profiles.json 反推
362
+ const projectProfiles = path.join(claudeDest, "profiles.json");
363
+ if (fs.existsSync(projectProfiles)) {
364
+ fs.copyFileSync(
365
+ projectProfiles,
366
+ path.join(sourcePath, ".claude", "profiles.json"),
367
+ );
368
+ }
369
+
370
+ // settings.json 反推
371
+ const cleanSettings = { ...settings };
372
+ delete cleanSettings._skill_source;
373
+ delete cleanSettings._active_profile;
374
+ fs.writeFileSync(
375
+ path.join(sourcePath, ".claude", "settings.json"),
376
+ JSON.stringify(cleanSettings, null, 2) + "\n",
377
+ "utf-8",
378
+ );
379
+
380
+ // CLAUDE.md 反推
381
+ const projectMd = path.join(projectRoot, "CLAUDE.md");
382
+ if (fs.existsSync(projectMd)) {
383
+ fs.copyFileSync(projectMd, path.join(sourcePath, "CLAUDE.md"));
384
+ }
385
+
386
+ return sendJSON(res, { ok: true });
387
+ }
388
+
389
+ // GET /api/diagnose — 健康检查
390
+ if (method === "GET" && pathname === "/api/diagnose") {
391
+ const results = [];
392
+ const pkgSkills = core.getPackageSkills(skillsSource);
393
+ for (const skill of pkgSkills) {
394
+ const skillDir = path.join(skillsDest, skill.name);
395
+ const issues = [];
396
+ if (!fs.existsSync(skillDir)) {
397
+ issues.push("未安装");
398
+ } else {
399
+ const metaPath = path.join(skillDir, ".meta.json");
400
+ if (!fs.existsSync(metaPath)) {
401
+ issues.push("缺少 .meta.json");
402
+ } else {
403
+ try {
404
+ const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
405
+ if (!meta.version) issues.push("版本号缺失");
406
+ } catch {
407
+ issues.push(".meta.json 格式错误");
408
+ }
409
+ }
410
+ }
411
+ results.push({
412
+ name: skill.name,
413
+ healthy: issues.length === 0,
414
+ issues,
415
+ });
416
+ }
417
+ return sendJSON(res, results);
418
+ }
419
+
420
+ // 404
421
+ sendJSON(res, { error: "Not Found: " + pathname }, 404);
422
+ } catch (err) {
423
+ console.error("API 错误:", err);
424
+ sendJSON(res, { error: err.message }, 500);
425
+ }
426
+ }
427
+
428
+ // ---------- 启动 ----------
429
+
430
+ async function startUI() {
431
+ const vite = await createViteServer({
432
+ root: __dirname,
433
+ server: { port: 3344, open: false },
434
+ plugins: [
435
+ {
436
+ name: "api-routes",
437
+ configureServer(viteServer) {
438
+ viteServer.middlewares.use((req, res, next) => {
439
+ if (req.url.startsWith("/api")) {
440
+ handleApi(req, res);
441
+ } else {
442
+ next();
443
+ }
444
+ });
445
+ },
446
+ },
447
+ ],
448
+ });
449
+
450
+ await vite.listen();
451
+ const port = vite.config.server.port;
452
+ console.log(`\n 🖥️ mdk-skills Web UI`);
453
+ console.log(` ─────────────────────`);
454
+ console.log(` 地址: http://localhost:${port}\n`);
455
+ console.log(` 按 Ctrl+C 停止服务\n`);
456
+ }
457
+
458
+ startUI().catch((err) => {
459
+ console.error("启动失败:", err);
460
+ process.exit(1);
461
+ });
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <n-message-provider>
3
+ <n-notification-provider>
4
+ <n-config-provider :theme="theme">
5
+ <n-layout class="app-layout">
6
+ <!-- 导航栏 -->
7
+ <n-layout-header class="app-header" bordered>
8
+ <div class="header-inner">
9
+ <div class="header-left">
10
+ <span class="logo">mdk-skills</span>
11
+ <n-menu
12
+ v-model:value="activeKey"
13
+ mode="horizontal"
14
+ :options="menuOptions"
15
+ @update:value="onMenuChange"
16
+ />
17
+ </div>
18
+ <n-tag v-if="statusData" :type="statusData.sourcePath ? 'success' : 'default'" size="small">
19
+ {{ statusData.sourcePath ? "本地源" : "npm 源" }}
20
+ </n-tag>
21
+ </div>
22
+ </n-layout-header>
23
+
24
+ <!-- 状态条 -->
25
+ <StatusBar v-if="statusData" :data="statusData" />
26
+
27
+ <!-- 内容区 -->
28
+ <n-layout-content class="app-content">
29
+ <router-view @refresh="loadStatus" />
30
+ </n-layout-content>
31
+ </n-layout>
32
+ </n-config-provider>
33
+ </n-notification-provider>
34
+ </n-message-provider>
35
+ </template>
36
+
37
+ <script setup>
38
+ import { h, ref, onMounted, computed } from "vue";
39
+ import { useRouter, useRoute } from "vue-router";
40
+ import { NIcon, useMessage } from "naive-ui";
41
+ import {
42
+ DashboardOutlined,
43
+ SwapOutlined,
44
+ SettingOutlined,
45
+ } from "@vicons/material";
46
+ import StatusBar from "./components/StatusBar.vue";
47
+ import { getStatus } from "./api/skills";
48
+
49
+ const router = useRouter();
50
+ const route = useRoute();
51
+ const message = useMessage();
52
+
53
+ const statusData = ref(null);
54
+
55
+ const theme = ref(null);
56
+
57
+ const menuOptions = [
58
+ {
59
+ label: "仪表盘",
60
+ key: "Dashboard",
61
+ icon: () => h(NIcon, null, { default: () => h(DashboardOutlined) }),
62
+ },
63
+ {
64
+ label: "场景切换",
65
+ key: "Scenes",
66
+ icon: () => h(NIcon, null, { default: () => h(SwapOutlined) }),
67
+ },
68
+ {
69
+ label: "设置",
70
+ key: "Settings",
71
+ icon: () => h(NIcon, null, { default: () => h(SettingOutlined) }),
72
+ },
73
+ ];
74
+
75
+ const activeKey = ref(route.name || "Dashboard");
76
+
77
+ function onMenuChange(key) {
78
+ router.push({ name: key });
79
+ }
80
+
81
+ router.afterEach((to) => {
82
+ activeKey.value = to.name;
83
+ });
84
+
85
+ async function loadStatus() {
86
+ try {
87
+ statusData.value = await getStatus();
88
+ } catch {
89
+ // 静默失败
90
+ }
91
+ }
92
+
93
+ onMounted(loadStatus);
94
+ </script>
95
+
96
+ <style>
97
+ @import "./styles/main.css";
98
+ </style>