huhaa-myskills 0.3.0 → 0.3.5

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 (122) hide show
  1. package/README.md +9 -375
  2. package/bin/huhaa-myskills.mjs +149 -16
  3. package/{scripts → build}/verify.mjs +3 -1
  4. package/docs/Frontend-Spec.md +1196 -0
  5. package/docs/assets/layout-wireframe.png +0 -0
  6. package/docs/assets/theme-reference.png +0 -0
  7. package/docs/scan_rules.md +172 -0
  8. package/package.json +33 -9
  9. package/packages/scanner/src/adapters/directory-skill.mjs +239 -0
  10. package/packages/scanner/src/adapters/skill-adapter.mjs +345 -0
  11. package/packages/scanner/src/icon/brand-map.mjs +84 -0
  12. package/packages/scanner/src/icon/icon-extractor.mjs +246 -0
  13. package/packages/scanner/src/index.mjs +107 -82
  14. package/packages/scanner/src/types.d.ts +18 -0
  15. package/packages/scanner/test/icon.test.mjs +61 -0
  16. package/packages/scanner/test/scanner.test.mjs +37 -41
  17. package/packages/scanner/test/skill-adapter.test.mjs +70 -0
  18. package/packages/scanner/tests/directory-skill.test.mjs +93 -0
  19. package/packages/scanner/tests/scanner-integration.test.mjs +139 -0
  20. package/packages/server/src/index.mjs +164 -12
  21. package/packages/server/src/labels.mjs +62 -0
  22. package/packages/server/test/server.test.mjs +6 -1
  23. package/packages/web/README.md +33 -2
  24. package/packages/web/dist/assets/index-BCY2cRaO.js +175 -0
  25. package/packages/web/dist/assets/index-DALKjJPi.css +1 -0
  26. package/packages/web/dist/favicon-16x16.png +0 -0
  27. package/packages/web/dist/favicon-192x192.png +0 -0
  28. package/packages/web/dist/favicon-32x32.png +0 -0
  29. package/packages/web/dist/favicon-512x512.png +0 -0
  30. package/packages/web/dist/favicon-source.svg +5 -0
  31. package/packages/web/dist/index.html +23 -9
  32. package/packages/web/index.html +22 -8
  33. package/packages/web/package-lock.json +2989 -0
  34. package/packages/web/package.json +21 -8
  35. package/packages/web/postcss.config.js +6 -0
  36. package/packages/web/public/favicon-16x16.png +0 -0
  37. package/packages/web/public/favicon-192x192.png +0 -0
  38. package/packages/web/public/favicon-32x32.png +0 -0
  39. package/packages/web/public/favicon-512x512.png +0 -0
  40. package/packages/web/public/favicon-source.svg +5 -0
  41. package/packages/web/src/App.test.ts +40 -0
  42. package/packages/web/src/App.tsx +177 -0
  43. package/packages/web/src/components/ComingSoon.tsx +14 -0
  44. package/packages/web/src/components/layout/Sidebar.test.tsx +74 -0
  45. package/packages/web/src/components/layout/Sidebar.tsx +114 -0
  46. package/packages/web/src/components/layout/Topbar.tsx +74 -0
  47. package/packages/web/src/components/ui/ActionButton.tsx +48 -0
  48. package/packages/web/src/components/ui/button.tsx +48 -0
  49. package/packages/web/src/components/ui/card.tsx +45 -0
  50. package/packages/web/src/components/views/DashboardView.tsx +122 -0
  51. package/packages/web/src/components/views/OtherSkillsView.test.tsx +126 -0
  52. package/packages/web/src/components/views/OtherSkillsView.tsx +306 -0
  53. package/packages/web/src/components/views/SettingsView.tsx +95 -0
  54. package/packages/web/src/components/views/SkillDetail.test.tsx +57 -0
  55. package/packages/web/src/components/views/SkillDetail.tsx +81 -0
  56. package/packages/web/src/components/views/SkillsView.test.ts +28 -0
  57. package/packages/web/src/components/views/SkillsView.tsx +226 -0
  58. package/packages/web/src/hooks/QUICK_REFERENCE.md +165 -0
  59. package/packages/web/src/hooks/README.md +378 -0
  60. package/packages/web/src/hooks/__tests__/useOtherSkills.test.ts +422 -0
  61. package/packages/web/src/hooks/__tests__/useSkillIcons.test.ts +272 -0
  62. package/packages/web/src/hooks/useLiveReload.ts +20 -0
  63. package/packages/web/src/hooks/useOtherSkills.ts +387 -0
  64. package/packages/web/src/hooks/useSkillIcons.ts +248 -0
  65. package/packages/web/src/hooks/useTheme.ts +36 -0
  66. package/packages/web/src/index.css +161 -0
  67. package/packages/web/src/lib/api.test.ts +47 -0
  68. package/packages/web/src/lib/api.ts +77 -0
  69. package/packages/web/src/lib/cn.ts +7 -0
  70. package/packages/web/src/lib/editors.test.ts +30 -0
  71. package/packages/web/src/lib/editors.ts +70 -0
  72. package/packages/web/src/lib/markdown.test.ts +27 -0
  73. package/packages/web/src/lib/markdown.ts +14 -0
  74. package/packages/web/src/main.tsx +13 -0
  75. package/packages/web/src/types/index.ts +62 -0
  76. package/packages/web/src/types/other-skill.ts +88 -0
  77. package/packages/web/src/types/skill.ts +136 -0
  78. package/packages/web/src/types.ts +47 -0
  79. package/packages/web/src/vite-env.d.ts +1 -0
  80. package/packages/web/tailwind.config.ts +66 -0
  81. package/packages/web/tsconfig.json +28 -0
  82. package/packages/web/vite.config.ts +36 -0
  83. package/packages/web/vitest.config.ts +21 -0
  84. package/packages/web/vitest.setup.ts +1 -0
  85. package/scripts/activate-data-sources.sh +223 -0
  86. package/scripts/brand-completion.py +586 -0
  87. package/scripts/init-nextjs-project.sh +421 -0
  88. package/scripts/install-and-sync.sh +209 -0
  89. package/scripts/start-dev.sh +72 -0
  90. package/docs/GUIDE.md +0 -190
  91. package/docs/PLAN.md +0 -359
  92. package/docs/RULES.md +0 -329
  93. package/docs/RUNBOOK-myskills.md +0 -258
  94. package/docs/SYNC-SKILLS.md +0 -259
  95. package/docs/releases/README.md +0 -47
  96. package/docs/releases/v0.1.0.md +0 -256
  97. package/docs/releases/v0.1.1.md +0 -297
  98. package/docs/releases/v0.1.2.md +0 -207
  99. package/docs/releases/v0.1.3.md +0 -269
  100. package/docs/todo/v0.1.2.md +0 -98
  101. package/docs/todo/v0.1.3.md +0 -40
  102. package/docs/todo/v0.1.4.md +0 -36
  103. package/docs/todo/v0.1.5.md +0 -41
  104. package/docs/todo/v0.1.6.md +0 -43
  105. package/docs/todo/v0.1.7.md +0 -38
  106. package/docs/todo/v0.1.8.md +0 -36
  107. package/docs/todo/v0.1.9.md +0 -54
  108. package/packages/web/dist/assets/index-CaQOPbDU.css +0 -1
  109. package/packages/web/dist/assets/index-Dlvh4-4K.js +0 -36
  110. package/packages/web/src/App.vue +0 -457
  111. package/packages/web/src/components/AppTree.vue +0 -230
  112. package/packages/web/src/components/DetailPanel.vue +0 -551
  113. package/packages/web/src/components/DirectoryTree.vue +0 -263
  114. package/packages/web/src/components/SkillTree.vue +0 -208
  115. package/packages/web/src/lib/api.js +0 -43
  116. package/packages/web/src/main.js +0 -6
  117. package/packages/web/src/stores/i18n.js +0 -167
  118. package/packages/web/src/stores/skills.js +0 -414
  119. package/packages/web/src/styles.css +0 -1012
  120. package/packages/web/vite.config.js +0 -17
  121. /package/{scripts → build}/build-web.mjs +0 -0
  122. /package/{scripts → build}/prepare-publish.mjs +0 -0
package/README.md CHANGED
@@ -1,384 +1,18 @@
1
- # 🎯 HuHaa-MySkills
1
+ # 📚 HuHaa-MySkills 文档
2
2
 
3
- **本地 AI 技能聚合中枢** — 一行命令发现、同步、管理你所有的 AI 技能和编辑器规则
3
+ ## 前端相关
4
4
 
5
- > 把散落在 Hermes / Claude Code / Cursor / Codex / Obsidian / 各项目里的「技能、插件、MCP、规则」全部聚合到一个浏览器界面。搜索、复制、在编辑器中打开,一切就在本地。
5
+ - **[docs/Frontend-Spec.md](./docs/Frontend-Spec.md)** 🎨 完整前端规范(工程 + 设计 + 框架 + 重构计划 + 实现)
6
6
 
7
- ---
8
-
9
- ## 🚀 一键启动(推荐)
10
-
11
- ```bash
12
- curl -fsSL https://raw.githubusercontent.com/aquarkgn/HuHaa-MySkills/main/install-and-sync.sh | bash
13
- ```
14
-
15
- **做了什么?**
16
- 1. 检查系统要求(Node.js 20+)
17
- 2. npm 全局安装 HuHaa-MySkills
18
- 3. 自动扫描已安装的编辑器(全球 20+ 款编辑器和开发工具)
19
- 4. 显示交互菜单,让你选择要同步的编辑器
20
- 5. 自动同步技能配置
21
- 6. 启动本地服务 `http://localhost:11520`
22
-
23
- **支持的编辑器和工具:**
24
- - **代码编辑器** — Cursor、VS Code、VS Code Insiders、Windsurf、Zed、Sublime Text、TextMate
25
- - **终端编辑器** — Neovim、Vim、Emacs、Helix
26
- - **其他编辑器** — Atom、Kate、Gedit、BBEdit、JetBrains IDEs
27
- - **新兴工具** — Openclaw、Herems、Trae、Trae CN
28
-
29
- **就这么简单!** 无需 git clone,无需手动配置,一条命令搞定。
30
-
31
- ---
32
-
33
- ## ✨ 核心功能
34
-
35
- ### 🔍 多源技能聚合
36
-
37
- HuHaa-MySkills 自动扫描并统一展示:
38
-
39
- - **Hermes Skills** - `~/.hermes/skills/*/SKILL.md`
40
- - **Claude Code Skills** - `~/.claude/skills/*/SKILL.md`
41
- - **Cursor Rules** - `~/.cursor/.cursorrules` 和 `.cursor/rules/*`
42
- - **Codex Instructions** - `AGENTS.md` 和 `CLAUDE.md`
43
- - **MCP Servers** - 自动识别所有配置的 MCP 服务和工具
44
- - **Hermes Plugins** - `~/.hermes/plugins/*/manifest.*`
45
- - **项目 Runbooks** - 项目根目录的 `docs/RUNBOOK-*.md`
46
- - **Obsidian Notes** - 带 `#skill` 标签的笔记(可选)
47
-
48
- ### 💻 浏览器界面(localhost:11520)
49
-
50
- ```
51
- ┌─────────────────────────────────────────────────────┐
52
- │ HuHaa-MySkills - 技能中心 │
53
- ├─────────────────────────────────────────────────────┤
54
- │ │
55
- │ 左侧树状目录 右侧预览/详情 │
56
- │ ├─ 按来源分组 ┌──────────────────┐ │
57
- │ ├─ 按类别分组 │ skill: new-api │ │
58
- │ ├─ 按品牌分组 │ 部署优化技能 │ │
59
- │ └─ 按产品分组 │ │ │
60
- │ │ 📋 复制路径 │ │
61
- │ 🔍 搜索框 │ 📝 复制原文 │ │
62
- │ (支持快捷键 /) │ 🔧 在 Cursor打开 │ │
63
- │ └──────────────────┘ │
64
- └─────────────────────────────────────────────────────┘
65
- ```
66
-
67
- ### 🎯 功能特性
68
-
69
- - **快速搜索** - Fuse.js 全文搜索,<5ms 延迟,支持 800+ 条目
70
- - **多维度过滤** - 按来源、编辑器、品牌、类别、产品组合筛选
71
- - **一键复制** - 复制路径、相对路径、原文本、调用 prompt
72
- - **编辑器集成** - 在 Cursor / VS Code 中一键打开
73
- - **热更新** - 文件变更自动刷新界面
74
- - **智能同步** - 自动同步技能到 20+ 个编辑器配置(v0.1.3+)
75
- - **本地优先** - 零上传,所有数据在本地,无云依赖
76
-
77
- ---
78
-
79
- ## 📖 使用流程
80
-
81
- ### 1️⃣ 安装 & 初始化(一键完成)
82
-
83
- ```bash
84
- curl -fsSL https://raw.githubusercontent.com/aquarkgn/HuHaa-MySkills/main/install-and-sync.sh | bash
85
- ```
86
-
87
- 🎉 第一次运行会:
88
- - 安装依赖
89
- - 初始化配置 `~/.config/huhaa-myskills/sources.yaml`
90
- - 自动启动浏览器打开 `http://localhost:11520`
91
-
92
- ### 2️⃣ 浏览器中探索
93
-
94
- 访问 **http://localhost:11520**:
95
- - 搜索你的技能(例:输入 "deploy" 找部署相关技能)
96
- - 点击任何技能查看详情
97
- - 复制到剪贴板或在编辑器中打开
98
-
99
- ### 3️⃣ 同步到编辑器(可选)
100
-
101
- 如果要让技能配置与编辑器同步:
102
-
103
- ```bash
104
- # 已全局安装的情况下
105
- huhaa-myskills sync
106
-
107
- # 或用 npm
108
- npm run sync
109
- ```
110
-
111
- 交互选择要同步的编辑器,自动完成。
7
+ ## 业务需求
112
8
 
113
- ### 4️⃣ 后续启动
9
+ - **[docs/hermes_docs_project_plan.md](./docs/hermes_docs_project_plan.md)** — 📋 项目需求与功能规划
114
10
 
115
- ```bash
116
- # 启动浏览器界面
117
- huhaa-myskills start
11
+ ## 资源
118
12
 
119
- # 查看统计信息
120
- huhaa-myskills stats
121
-
122
- # 扫描并输出 JSON(调试用)
123
- huhaa-myskills scan
124
- ```
13
+ - `docs/assets/layout-wireframe.png` — 布局蓝图
14
+ - `docs/assets/theme-reference.png` — 视觉参考
125
15
 
126
16
  ---
127
17
 
128
- ## 🔧 配置
129
-
130
- ### 默认配置位置
131
-
132
- ```bash
133
- ~/.config/huhaa-myskills/sources.yaml
134
- ```
135
-
136
- ### 添加更多扫描源
137
-
138
- 编辑 `sources.yaml`,例如添加项目 runbooks:
139
-
140
- ```yaml
141
- sources:
142
- hermes:
143
- enabled: true
144
- claude-code:
145
- enabled: true
146
- mcp-config:
147
- enabled: true
148
-
149
- # 添加项目目录
150
- project-runbook:
151
- enabled: true
152
- roots:
153
- - ~/Project/HuHaa-AI-Platform
154
- - ~/Project/Another-Project
155
- glob: 'docs/RUNBOOK-*.md'
156
- ```
157
-
158
- 保存后浏览器会自动重新扫描。
159
-
160
- ### 自定义数据目录
161
-
162
- ```bash
163
- HUHAA_HOME=/tmp/huhaa-myskills huhaa-myskills start
164
- ```
165
-
166
- ### 自定义端口
167
-
168
- ```bash
169
- PORT=11530 huhaa-myskills start
170
- ```
171
-
172
- ---
173
-
174
- ## 📋 其他安装方式
175
-
176
- ### 方式 B:本地克隆(适合开发者)
177
-
178
- ```bash
179
- git clone https://github.com/aquarkgn/HuHaa-MySkills.git
180
- cd HuHaa-MySkills
181
- npm install
182
- npm start
183
- ```
184
-
185
- ### 方式 C:npm 全局安装(推荐)
186
-
187
- ```bash
188
- npm install -g huhaa-myskills
189
- huhaa-myskills init
190
- huhaa-myskills start
191
- ```
192
-
193
- ---
194
-
195
- ## 🎮 v0.1.2 新增:编辑器技能同步
196
-
197
- 自动将项目技能同步到你的所有编辑器配置目录。
198
-
199
- **支持的编辑器:**
200
- - Cursor - 同步 `.cursorrules`
201
- - VS Code - 同步代码片段
202
- - Helix - 同步 `config.toml`
203
- - Neovim - 生成 `init.lua`
204
- - Sublime Text - 同步用户设置
205
- - Windsurf - 同步编辑器配置
206
-
207
- **使用:**
208
-
209
- ```bash
210
- # 方式 1:交互选择
211
- npm run sync
212
-
213
- # 方式 2:远程执行
214
- npm run sync:remote
215
-
216
- # 选择要同步的编辑器
217
- # [1] cursor
218
- # [2] vscode
219
- # ...
220
- # 请输入选择: 0 # 全选
221
- ```
222
-
223
- ---
224
-
225
- ## 🔐 安全性
226
-
227
- HuHaa-MySkills 是本地优先的设计:
228
-
229
- - ✅ **零上传** - 所有数据在本地 `~/.config/huhaa-myskills/`
230
- - ✅ **仅本地绑定** - 服务器只监听 `127.0.0.1`,不暴露到网络
231
- - ✅ **路径白名单** - 复制/打开文件严格校验,防止路径穿越
232
- - ✅ **配置隐蔽** - MCP 密钥等敏感信息在浏览器中被屏蔽
233
- - ✅ **无云依赖** - 完全离线工作,无 telemetry
234
-
235
- ---
236
-
237
- ## 📊 了解更多
238
-
239
- | 文档 | 链接 |
240
- |------|------|
241
- | 完整使用指南 | [docs/SYNC-SKILLS.md](./docs/SYNC-SKILLS.md) |
242
- | 快速启动 | [docs/GUIDE.md](./docs/GUIDE.md) |
243
- | 发布规范 | [docs/RULES.md](./docs/RULES.md) |
244
- | 项目规划 | [docs/PLAN.md](./docs/PLAN.md) |
245
- | API 文档 | 见下方 API 部分 |
246
-
247
- ---
248
-
249
- ## 🛠 命令参考(高级)
250
-
251
- 首次安装后自动配置完成。以下命令用于日常管理和调试:
252
-
253
- | 命令 | 说明 |
254
- |------|------|
255
- | `huhaa-myskills start` | 启动服务,自动打开浏览器 |
256
- | `huhaa-myskills init` | 重新初始化配置 |
257
- | `huhaa-myskills scan` | 扫描并输出 JSON 到命令行 |
258
- | `huhaa-myskills stats` | 显示扫描统计 |
259
- | `huhaa-myskills duplicates` | 查找重复项目 |
260
- | `huhaa-myskills sync` | 同步技能到编辑器(交互式) |
261
- | `huhaa-myskills purge` | 删除所有数据和配置 |
262
-
263
- 对应 npm scripts:
264
- ```bash
265
- npm start # 启动
266
- npm run init # 初始化
267
- npm run scan # 扫描
268
- npm run stats # 统计
269
- npm run sync # 编辑器同步
270
- npm run purge # 删除数据
271
- npm run dev # 开发模式(热重载)
272
- npm test # 运行测试
273
- ```
274
-
275
- ---
276
-
277
- ## 🧪 验证安装
278
-
279
- 确保系统完整性:
280
-
281
- ```bash
282
- npm run verify
283
- ```
284
-
285
- 预期输出:
286
- ```
287
- [verify] PASS build + tests + HTTP/API/static smoke checks
288
- ```
289
-
290
- ---
291
-
292
- ## 🚮 卸载 & 清理
293
-
294
- 完全移除 HuHaa-MySkills 和相关数据:
295
-
296
- ```bash
297
- # 删除所有配置和数据
298
- npm run purge
299
-
300
- # 删除全局安装(如已全局安装)
301
- npm uninstall -g huhaa-myskills
302
-
303
- # 删除项目代码
304
- rm -rf HuHaa-MySkills
305
- ```
306
-
307
- ---
308
-
309
- ## 💻 系统要求
310
-
311
- - **Node.js** 20+(自动检查)
312
- - **npm** 8+
313
- - **macOS** 或 **Linux**(Windows 需要 WSL 或类似环境)
314
-
315
- macOS 额外支持:
316
- - `pbcopy` 用于剪贴板操作
317
- - Finder "显示文件" 功能
318
- - Cursor 编辑器集成
319
-
320
- ---
321
-
322
- ## 🏗 项目结构(开发者)
323
-
324
- ```
325
- service/bin/ CLI 入口
326
- service/packages/
327
- ├─ scanner/ 多源适配器 + IR 规范化
328
- ├─ server/ Fastify API 服务
329
- └─ web/ Vue 3 前端 SPA
330
- service/scripts/ 工具脚本(同步、验证等)
331
- service/config/ 配置模板
332
- docs/ 文档和规划
333
- ```
334
-
335
- ---
336
-
337
- ## 🔗 API 参考(前端开发)
338
-
339
- | 方法 | 端点 | 说明 |
340
- |------|------|------|
341
- | GET | `/api/health` | 健康检查 |
342
- | GET | `/api/skills` | 获取所有技能列表 |
343
- | GET | `/api/skills/:id` | 获取单个技能详情(含 raw) |
344
- | GET | `/api/stats` | 获取扫描统计 |
345
- | GET | `/api/reload-state` | 获取重载状态 |
346
- | POST | `/api/reload` | 触发重新扫描 |
347
- | GET | `/api/events` | SSE 事件流 |
348
- | POST | `/api/copy` | 复制到剪贴板 |
349
- | POST | `/api/open` | 在编辑器中打开 |
350
-
351
- ---
352
-
353
- ## 📈 版本历史
354
-
355
- - **v0.1.4** (2026-06-18) - 多源扫描增强(~/skills、~/mcp、~/skill)
356
- - **v0.1.3** (2026-06-18) - 全球 20+ 编辑器支持
357
- - **v0.1.2** (2026-06-18) - 编辑器技能自动同步
358
- - **v0.1.1** (2026-06-18) - npm 全局安装支持
359
- - **v0.1.0** (2026-06-17) - 首个可用版本
360
-
361
- [查看完整版本历史](./CHANGELOG.md)
362
-
363
- ---
364
-
365
- ## 📝 贡献指南
366
-
367
- 感谢你的贡献!请参考 [RULES.md](./docs/RULES.md) 了解:
368
- - Conventional Commits 规范
369
- - Pull Request 流程
370
- - Issue 报告格式
371
-
372
- ---
373
-
374
- ## 📄 许可证
375
-
376
- MIT
377
-
378
- ---
379
-
380
- **问题反馈** → [GitHub Issues](https://github.com/aquarkgn/HuHaa-MySkills/issues)
381
-
382
- **功能建议** → [Discussion](https://github.com/aquarkgn/HuHaa-MySkills/discussions)
383
-
384
- **快速开始** → 回到上面的 [🚀 一键启动](#-一键启动推荐)
18
+ **快速开始**:见 docs/Frontend-Spec.md §VI 「快速启动指南」
@@ -17,7 +17,7 @@ const require = createRequire(import.meta.url);
17
17
 
18
18
  // Load paths helper
19
19
  const pathsLib = require('./lib/paths.mjs');
20
- const { homeDir, configFile, cacheFile, stateFile, ensureHomeDir, writeJson } = pathsLib;
20
+ const { homeDir, configFile, cacheFile, stateFile, ensureHomeDir, writeJson, readJson } = pathsLib;
21
21
 
22
22
  // Load port helper
23
23
  const { pickPort } = require('./lib/port.mjs');
@@ -38,11 +38,14 @@ const cmd = process.argv[2] || 'start';
38
38
 
39
39
  const handlers = {
40
40
  start: cmdStart,
41
+ stop: cmdStop,
41
42
  scan: cmdScan,
42
43
  stats: cmdStats,
43
44
  duplicates: cmdDuplicates,
44
45
  init: cmdInit,
45
46
  purge: cmdPurge,
47
+ uninstall: cmdUninstall,
48
+ restart: cmdRestart,
46
49
  dev: cmdDev,
47
50
  sync: cmdSync,
48
51
  help: cmdHelp,
@@ -73,13 +76,16 @@ Usage:
73
76
 
74
77
  Commands:
75
78
  start Scan + start server + open browser (runs in background by default)
79
+ stop Stop the background server
80
+ restart Restart the background server (kill + restart)
76
81
  scan Scan only, dump IR JSON to stdout
77
82
  stats Scan + print human-readable summary (counts / brands / errors / samples)
78
83
  duplicates Scan + print duplicate diagnostics by name/content/path
79
84
  init Write default sources.yaml to ~/.config/huhaa-myskills/
80
85
  purge Remove all user data under ~/.config/huhaa-myskills/
86
+ uninstall Stop service + uninstall global npm package (prompts before deleting user data)
81
87
  sync Sync current skills to selected editors
82
- dev Dev mode (Vite + nodemon)
88
+ dev Dev mode: Vite frontend hot reload (connect to backend separately)
83
89
 
84
90
  Options:
85
91
  -v, --version Show version
@@ -124,6 +130,113 @@ async function cmdPurge() {
124
130
  console.log('[purge] code dir is untouched. delete the repo manually if needed.');
125
131
  }
126
132
 
133
+ async function cmdStop() {
134
+ const state = readJson(stateFile());
135
+ if (!state || !state.pid) {
136
+ console.log('[stop] 未发现运行中的实例(无 state.json 记录)');
137
+ return false;
138
+ }
139
+ if (!isProcessRunning(state.pid)) {
140
+ console.log('[stop] 实例未运行,清理残留状态文件');
141
+ try { fs.unlinkSync(stateFile()); } catch {}
142
+ return false;
143
+ }
144
+ try {
145
+ process.kill(state.pid, 'SIGTERM');
146
+ console.log(`✓ 已停止 HuHaa-MySkills (pid ${state.pid}, port ${state.port ?? '未知'})`);
147
+ } catch (err) {
148
+ console.error(`[stop] 停止失败: ${err.message}`);
149
+ return false;
150
+ }
151
+ try { fs.unlinkSync(stateFile()); } catch {}
152
+ return true;
153
+ }
154
+
155
+ async function cmdRestart() {
156
+ const isForeground = process.argv.includes('--foreground') || process.argv.includes('-f');
157
+ const state = readJson(stateFile());
158
+
159
+ if (!state || !state.pid) {
160
+ console.log('[restart] 未发现运行中的实例,启动新服务...');
161
+ await cmdStart();
162
+ return;
163
+ }
164
+
165
+ if (!isProcessRunning(state.pid)) {
166
+ console.log('[restart] 旧实例未运行,清理状态并启动新服务...');
167
+ try { fs.unlinkSync(stateFile()); } catch {}
168
+ await cmdStart();
169
+ return;
170
+ }
171
+
172
+ // Kill old process
173
+ try {
174
+ process.kill(state.pid, 'SIGTERM');
175
+ console.log(`[restart] 已停止旧实例 (PID ${state.pid})`);
176
+ } catch (err) {
177
+ console.error(`[restart] 停止失败: ${err.message}`);
178
+ }
179
+
180
+ try { fs.unlinkSync(stateFile()); } catch {}
181
+
182
+ // Wait and restart
183
+ await new Promise(resolve => setTimeout(resolve, 500));
184
+ console.log('[restart] 启动新实例...');
185
+ await cmdStart();
186
+ }
187
+
188
+ async function cmdUninstall() {
189
+ const readline = await import('node:readline');
190
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
191
+ const question = (q) => new Promise(resolve => rl.question(q, resolve));
192
+
193
+ const dir = homeDir();
194
+ console.log('\n[uninstall] 即将卸载 HuHaa-MySkills,将执行以下操作:');
195
+ console.log(' 1. 停止运行中的后台服务');
196
+ console.log(` 2. 删除用户数据目录: ${dir}`);
197
+ console.log(' 3. 卸载全局 npm 包: huhaa-myskills');
198
+ console.log('\n⚠️ 此操作不可恢复,代码仓库本身不会被删除。');
199
+
200
+ const ans = await question('\n是否全部卸载?输入 y 确认,其它键取消: ');
201
+ rl.close();
202
+
203
+ if (ans.trim().toLowerCase() !== 'y') {
204
+ console.log('[uninstall] 已取消,未做任何更改。');
205
+ return;
206
+ }
207
+
208
+ // 1. 停止后台服务
209
+ console.log('\n[1/3] 停止后台服务...');
210
+ await cmdStop();
211
+
212
+ // 2. 删除用户数据目录
213
+ console.log('\n[2/3] 删除用户数据目录...');
214
+ if (fs.existsSync(dir)) {
215
+ fs.rmSync(dir, { recursive: true, force: true });
216
+ console.log(`✓ 已删除 ${dir}`);
217
+ } else {
218
+ console.log(`[uninstall] 目录不存在,跳过: ${dir}`);
219
+ }
220
+
221
+ // 3. 卸载全局 npm 包
222
+ console.log('\n[3/3] 卸载全局 npm 包...');
223
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
224
+ await new Promise((resolve) => {
225
+ const child = spawn(npmCmd, ['uninstall', '-g', 'huhaa-myskills'], { stdio: 'inherit' });
226
+ child.on('close', (code) => {
227
+ if (code === 0) console.log('✓ 已卸载全局 npm 包 huhaa-myskills');
228
+ else console.warn(`[uninstall] npm 退出码 ${code}(若未全局安装可忽略)`);
229
+ resolve();
230
+ });
231
+ child.on('error', () => {
232
+ console.warn('[uninstall] 未找到 npm 命令,跳过全局包卸载');
233
+ resolve();
234
+ });
235
+ });
236
+
237
+ console.log('\n[uninstall] 完成 ✓');
238
+ }
239
+
127
240
  async function cmdScan() {
128
241
  const { scan } = await import(path.join(PKGS_ROOT, 'scanner/src/index.mjs'));
129
242
  await ensureConfigOrInit();
@@ -272,13 +385,30 @@ async function cmdStart() {
272
385
  await ensureConfigOrInit();
273
386
  const isForeground = process.argv.includes('--foreground') || process.argv.includes('-f');
274
387
  const preferred = parseInt(process.env.PORT || '11520', 10);
388
+
389
+ // 后台模式下先做幂等检查:已有实例在运行则直接返回,
390
+ // 避免 pickPort 误判端口被占用而打印"改用其它端口"的误导信息。
391
+ if (!isForeground) {
392
+ try {
393
+ const stateData = JSON.parse(fs.readFileSync(stateFile(), 'utf8'));
394
+ if (stateData.pid && isProcessRunning(stateData.pid)) {
395
+ const logFile = path.join(homeDir(), 'huhaa.log');
396
+ console.log(`✓ HuHaa-MySkills 已在运行: http://localhost:${stateData.port}`);
397
+ console.log(`📝 日志: ${logFile}`);
398
+ return;
399
+ }
400
+ } catch {
401
+ // 状态文件不存在或无效,继续正常启动流程
402
+ }
403
+ }
404
+
275
405
  const port = await pickPort(preferred, 10);
276
406
  if (!port) {
277
- console.error(`[start] no free port in ${preferred}..${preferred + 10}, abort.`);
407
+ console.error(`[start] ${preferred}..${preferred + 10} 范围内无空闲端口,已中止。`);
278
408
  process.exit(1);
279
409
  }
280
410
  if (port !== preferred) {
281
- console.warn(`[start] port ${preferred} busy, using ${port} instead.`);
411
+ console.warn(`[start] 端口 ${preferred} 被占用,改用 ${port}。`);
282
412
  }
283
413
 
284
414
  const homeDirectory = homeDir();
@@ -298,8 +428,8 @@ async function startBackgroundServer(port, logFile, homeDirectory) {
298
428
  try {
299
429
  const stateData = JSON.parse(fs.readFileSync(stateFile(), 'utf8'));
300
430
  if (stateData.pid && isProcessRunning(stateData.pid)) {
301
- console.log(`✓ HuHaa-MySkills already running on http://localhost:${stateData.port}`);
302
- console.log(`📝 Logs: ${logFile}`);
431
+ console.log(`✓ HuHaa-MySkills 已在运行: http://localhost:${stateData.port}`);
432
+ console.log(`📝 日志: ${logFile}`);
303
433
  return;
304
434
  }
305
435
  } catch {
@@ -327,10 +457,10 @@ async function startBackgroundServer(port, logFile, homeDirectory) {
327
457
  // 关闭日志文件描述符(子进程已继承)
328
458
  fs.closeSync(logFd);
329
459
 
330
- console.log(`✓ HuHaa-MySkills running in background at http://localhost:${port}`);
331
- console.log(`📝 Logs: ${logFile}`);
332
- console.log(`💡 To view logs: tail -f ${logFile}`);
333
- console.log(`💡 To stop: pkill -f "huhaa-myskills start"`);
460
+ console.log(`✓ HuHaa-MySkills 已在后台运行: http://localhost:${port}`);
461
+ console.log(`📝 日志: ${logFile}`);
462
+ console.log(`💡 查看日志: tail -f ${logFile}`);
463
+ console.log(`💡 停止服务: huhaa-myskills stop`);
334
464
  }
335
465
 
336
466
  function isProcessRunning(pid) {
@@ -348,10 +478,10 @@ async function runServer(port, logFile, homeDirectory) {
348
478
  const { startServer } = await import(path.join(PKGS_ROOT, 'server/src/index.mjs'));
349
479
  await startServer({ port });
350
480
 
351
- console.log(`\n HuHaa-MySkills running: http://localhost:${port}\n`);
352
- console.log(` data dir: ${homeDirectory}`);
353
- console.log(` logs: ${logFile}`);
354
- console.log(` Ctrl+C to stop.\n`);
481
+ console.log(`\n HuHaa-MySkills 运行中: http://localhost:${port}\n`);
482
+ console.log(` 数据目录: ${homeDirectory}`);
483
+ console.log(` 日志: ${logFile}`);
484
+ console.log(` Ctrl+C 停止。\n`);
355
485
 
356
486
  openBrowser(`http://localhost:${port}`);
357
487
 
@@ -364,8 +494,11 @@ async function runServer(port, logFile, homeDirectory) {
364
494
  }
365
495
 
366
496
  async function cmdDev() {
367
- console.log('[dev] dev mode comes in P3. running start for now.');
368
- await cmdStart();
497
+ console.log('[dev] Frontend dev mode delegated to packages/web.');
498
+ console.log('[dev] Run: npm run dev (frontend on 11521)');
499
+ console.log('[dev] In another terminal: npm run start (backend on 11520)');
500
+ console.log('[dev] Or: npm run restart (if backend already running)');
501
+ process.exit(0);
369
502
  }
370
503
 
371
504
  async function cmdSync() {
@@ -36,6 +36,8 @@ function write(file, text) {
36
36
  async function main() {
37
37
  try {
38
38
  run('npm', ['run', 'build:web']);
39
+ run('npm', ['run', 'typecheck:web']);
40
+ run('npm', ['run', 'test:web']);
39
41
  run('node', ['--test', ...testFiles]);
40
42
 
41
43
  const skillRoot = path.join(tempRoot, 'skills');
@@ -100,7 +102,7 @@ limits:
100
102
 
101
103
  const css = await fetchText(`${base}${cssMatch[0]}`);
102
104
  assert.equal(css.contentType.split(';')[0], 'text/css');
103
- assert.ok(css.body.includes('toolbar-card'));
105
+ assert.ok(css.body.includes('topbar') || css.body.includes('sidebar') || css.body.includes('detail'));
104
106
 
105
107
  console.log('\n[verify] PASS build + tests + HTTP/API/static smoke checks');
106
108
  } finally {