huhaa-myskills 0.2.13 → 0.3.4

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 (121) hide show
  1. package/README.md +9 -375
  2. package/bin/huhaa-myskills.mjs +194 -12
  3. package/{scripts → build}/verify.mjs +3 -1
  4. package/docs/Frontend-Spec.md +1149 -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 +1 -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-BPepSb3q.js +0 -36
  109. package/packages/web/dist/assets/index-CIGkgT5w.css +0 -1
  110. package/packages/web/src/App.vue +0 -483
  111. package/packages/web/src/components/AppTree.vue +0 -230
  112. package/packages/web/src/components/DirectoryTree.vue +0 -263
  113. package/packages/web/src/components/SkillTree.vue +0 -208
  114. package/packages/web/src/lib/api.js +0 -43
  115. package/packages/web/src/main.js +0 -6
  116. package/packages/web/src/stores/i18n.js +0 -161
  117. package/packages/web/src/stores/skills.js +0 -287
  118. package/packages/web/src/styles.css +0 -365
  119. package/packages/web/vite.config.js +0 -17
  120. /package/{scripts → build}/build-web.mjs +0 -0
  121. /package/{scripts → build}/prepare-publish.mjs +0 -0
@@ -0,0 +1,378 @@
1
+ # Hooks 文档
2
+
3
+ ## useOtherSkills
4
+
5
+ 前端 Hook 改造后的全新 `useOtherSkills`,支持从 `/api/other-skills` 获取真实数据。
6
+
7
+ ### 功能特性
8
+
9
+ ✅ 从 API 获取真实技能数据
10
+ ✅ 全文搜索(名字、描述、标签、品牌、来源)
11
+ ✅ 多种排序方式(名字、更新时间、分类)
12
+ ✅ 灵活分组方式(按分类、品牌、来源或不分组)
13
+ ✅ 多条件过滤(品牌、分类、来源)
14
+ ✅ 分页支持(limit + offset)
15
+ ✅ 自动错误处理和加载状态
16
+ ✅ 完整单元测试覆盖
17
+
18
+ ### 基础用法
19
+
20
+ ```typescript
21
+ import { useOtherSkills, GroupBy, SortBy, SortOrder } from '@/hooks/useOtherSkills'
22
+
23
+ function MyComponent() {
24
+ const {
25
+ items, // 当前过滤后的所有技能项
26
+ groups, // 分组结果
27
+ isLoading, // 加载状态
28
+ error, // 错误对象
29
+ total, // 总技能数
30
+ filtered, // 过滤后的技能数
31
+ refetch, // 手动刷新函数
32
+ } = useOtherSkills()
33
+
34
+ if (isLoading) return <div>Loading...</div>
35
+ if (error) return <div>Error: {error.message}</div>
36
+
37
+ return (
38
+ <div>
39
+ {groups.map((group) => (
40
+ <div key={group.groupKey}>
41
+ <h3>{group.icon} {group.label} ({group.count})</h3>
42
+ {group.items.map((skill) => (
43
+ <div key={skill.id}>{skill.name}</div>
44
+ ))}
45
+ </div>
46
+ ))}
47
+ </div>
48
+ )
49
+ }
50
+ ```
51
+
52
+ ### 高级用法
53
+
54
+ #### 1. 搜索和过滤
55
+
56
+ ```typescript
57
+ // 搜索 "Docker"
58
+ const { items } = useOtherSkills({ query: 'Docker' })
59
+
60
+ // 按品牌过滤
61
+ const { items } = useOtherSkills({ filterBrand: 'hermes' })
62
+
63
+ // 按分类过滤
64
+ const { items } = useOtherSkills({ filterCategory: 'ai' })
65
+
66
+ // 按来源过滤
67
+ const { items } = useOtherSkills({ filterSource: 'project-runbook' })
68
+
69
+ // 组合多个条件
70
+ const { items } = useOtherSkills({
71
+ query: 'API',
72
+ filterBrand: 'claude-code',
73
+ filterCategory: 'development',
74
+ })
75
+ ```
76
+
77
+ #### 2. 排序
78
+
79
+ ```typescript
80
+ import { SortBy, SortOrder } from '@/hooks/useOtherSkills'
81
+
82
+ // 按名字升序排序(默认)
83
+ const { items } = useOtherSkills({
84
+ sortBy: SortBy.NAME,
85
+ sortOrder: SortOrder.ASC,
86
+ })
87
+
88
+ // 按更新时间降序排序
89
+ const { items } = useOtherSkills({
90
+ sortBy: SortBy.UPDATED,
91
+ sortOrder: SortOrder.DESC,
92
+ })
93
+
94
+ // 按分类排序
95
+ const { items } = useOtherSkills({
96
+ sortBy: SortBy.CATEGORY,
97
+ sortOrder: SortOrder.ASC,
98
+ })
99
+ ```
100
+
101
+ #### 3. 分组
102
+
103
+ ```typescript
104
+ import { GroupBy } from '@/hooks/useOtherSkills'
105
+
106
+ // 按分类分组(默认)
107
+ const { groups } = useOtherSkills({ groupBy: GroupBy.CATEGORY })
108
+ // Result:
109
+ // [
110
+ // { groupKey: 'ai', label: 'AI', icon: '🤖', items: [...], count: 3 },
111
+ // { groupKey: 'development', label: 'Development', icon: '👨‍💻', items: [...], count: 5 },
112
+ // ...
113
+ // ]
114
+
115
+ // 按品牌分组
116
+ const { groups } = useOtherSkills({ groupBy: GroupBy.BRAND })
117
+ // Result:
118
+ // [
119
+ // { groupKey: 'hermes', label: 'Hermes', icon: '⚡', items: [...], count: 2 },
120
+ // { groupKey: 'claude-code', label: 'Claude Code', icon: '🤖', items: [...], count: 1 },
121
+ // ...
122
+ // ]
123
+
124
+ // 按来源分组
125
+ const { groups } = useOtherSkills({ groupBy: GroupBy.SOURCE })
126
+
127
+ // 不分组
128
+ const { groups } = useOtherSkills({ groupBy: GroupBy.NONE })
129
+ // Result:
130
+ // [
131
+ // { groupKey: 'all', label: 'All Skills', icon: '📌', items: [...], count: N },
132
+ // ]
133
+ ```
134
+
135
+ #### 4. 分页
136
+
137
+ ```typescript
138
+ // 获取前 10 条
139
+ const { items } = useOtherSkills({ limit: 10 })
140
+
141
+ // 获取第 11-20 条
142
+ const { items } = useOtherSkills({ offset: 10, limit: 10 })
143
+ ```
144
+
145
+ #### 5. 手动刷新
146
+
147
+ ```typescript
148
+ const { refetch, isLoading } = useOtherSkills()
149
+
150
+ async function handleRefresh() {
151
+ await refetch()
152
+ // 数据已刷新
153
+ }
154
+
155
+ <button onClick={handleRefresh} disabled={isLoading}>
156
+ {isLoading ? '刷新中...' : '刷新'}
157
+ </button>
158
+ ```
159
+
160
+ #### 6. 复杂场景组合
161
+
162
+ ```typescript
163
+ const { items, groups, total, filtered } = useOtherSkills({
164
+ // 搜索
165
+ query: 'deploy',
166
+
167
+ // 过滤
168
+ filterCategory: 'devops',
169
+ filterSource: 'project-runbook',
170
+
171
+ // 排序
172
+ sortBy: SortBy.UPDATED,
173
+ sortOrder: SortOrder.DESC,
174
+
175
+ // 分组
176
+ groupBy: GroupBy.BRAND,
177
+
178
+ // 分页
179
+ limit: 20,
180
+ offset: 0,
181
+ })
182
+
183
+ console.log(`总数: ${total}, 过滤后: ${filtered}`)
184
+ ```
185
+
186
+ ### 类型定义
187
+
188
+ ```typescript
189
+ // 搜索和过滤选项
190
+ interface OtherSkillsOptions {
191
+ query?: string // 搜索词
192
+ sortBy?: SortBy // 排序字段
193
+ sortOrder?: SortOrder // 排序方向
194
+ groupBy?: GroupBy // 分组方式
195
+ filterBrand?: string // 按品牌过滤
196
+ filterCategory?: string // 按分类过滤
197
+ filterSource?: string // 按来源过滤
198
+ limit?: number // 限制结果数
199
+ offset?: number // 分页偏移
200
+ }
201
+
202
+ // 技能项
203
+ interface OtherSkill {
204
+ id: string
205
+ name: string
206
+ title?: string // 人类可读标题
207
+ category?: string | string[] // 语义分类标签(可多个)
208
+ brand?: string // 品牌标识(用于 icon)
209
+ source?: string // 来源(hermes、claude-code、cursor 等)
210
+ description?: string // 简短描述
211
+ icon?: string // 自定义 icon
212
+ tags?: string[] // 搜索标签
213
+ docs?: string // 文档链接
214
+ links?: Array<{ label: string; url: string }> // 相关链接
215
+ examples?: string[] // 使用示例
216
+ updatedAt?: string // ISO 8601 时间戳
217
+ parseError?: string // 解析错误(如有)
218
+ }
219
+
220
+ // 分组结果
221
+ interface OtherSkillGroup {
222
+ groupKey: string // 分组键(category、brand 等)
223
+ label: string // 显示标签
224
+ icon: string // 分组 icon
225
+ items: OtherSkill[] // 该分组中的技能项
226
+ count: number // 项数统计
227
+ }
228
+
229
+ // 返回值
230
+ interface UseOtherSkillsReturn {
231
+ items: OtherSkill[] // 过滤后的所有技能项
232
+ groups: OtherSkillGroup[] // 分组结果
233
+ isLoading: boolean // 是否加载中
234
+ error: OtherSkillsError | null // 错误对象
235
+ total: number // 总技能数
236
+ filtered: number // 过滤后的技能数
237
+ refetch: () => Promise<void> // 手动刷新
238
+ }
239
+ ```
240
+
241
+ ### 枚举值
242
+
243
+ ```typescript
244
+ enum SortBy {
245
+ NAME = 'name', // 按名字排序
246
+ UPDATED = 'updatedAt', // 按更新时间排序
247
+ CATEGORY = 'category', // 按分类排序
248
+ }
249
+
250
+ enum SortOrder {
251
+ ASC = 'asc', // 升序
252
+ DESC = 'desc', // 降序
253
+ }
254
+
255
+ enum GroupBy {
256
+ CATEGORY = 'category', // 按分类分组
257
+ BRAND = 'brand', // 按品牌分组
258
+ SOURCE = 'source', // 按来源分组
259
+ NONE = 'none', // 不分组
260
+ }
261
+ ```
262
+
263
+ ### 错误处理
264
+
265
+ ```typescript
266
+ import { OtherSkillsError } from '@/hooks/useOtherSkills'
267
+
268
+ const { error } = useOtherSkills()
269
+
270
+ if (error instanceof OtherSkillsError) {
271
+ console.log(error.code) // 错误代码 (FETCH_FAILED, INVALID_FORMAT, etc)
272
+ console.log(error.message) // 错误信息
273
+ console.log(error.statusCode) // HTTP 状态码(如有)
274
+ }
275
+ ```
276
+
277
+ ### 单元测试
278
+
279
+ 超过 34 个单元测试覆盖:
280
+
281
+ ✅ 搜索功能(名字、描述、标签、品牌、来源)
282
+ ✅ 排序功能(升序/降序,多个排序字段)
283
+ ✅ 过滤功能(单条件、多条件组合)
284
+ ✅ 分组功能(分类、品牌、来源、无分组)
285
+ ✅ 分页功能(limit、offset)
286
+ ✅ 复合操作(搜索 + 排序 + 分组 + 分页)
287
+ ✅ 多分类支持(字符串和数组)
288
+ ✅ 错误处理
289
+
290
+ 运行测试:
291
+
292
+ ```bash
293
+ npm run test:web -- src/hooks/__tests__/useOtherSkills.test.ts
294
+ ```
295
+
296
+ ### API 说明
297
+
298
+ Hook 从 `/api/other-skills` 获取数据,格式为:
299
+
300
+ ```typescript
301
+ // GET /api/other-skills
302
+ // Response: OtherSkill[]
303
+ [
304
+ {
305
+ id: "skill-1",
306
+ name: "Docker Setup",
307
+ title: "Docker Container Management",
308
+ category: "devops",
309
+ brand: "docker",
310
+ source: "project-runbook",
311
+ description: "Learn Docker basics",
312
+ tags: ["container", "devops"],
313
+ updatedAt: "2026-01-15T10:00:00Z",
314
+ },
315
+ ...
316
+ ]
317
+ ```
318
+
319
+ ### 最佳实践
320
+
321
+ 1. **缓存搜索词** - 在输入框的 change 事件中使用防抖来避免频繁重新计算
322
+
323
+ ```typescript
324
+ const [query, setQuery] = useState('')
325
+
326
+ const handleSearchChange = useCallback(
327
+ (e: ChangeEvent<HTMLInputElement>) => {
328
+ const value = e.target.value
329
+ setQuery(value)
330
+ },
331
+ []
332
+ )
333
+
334
+ const { items } = useOtherSkills({ query })
335
+ ```
336
+
337
+ 2. **记忆化选项** - 使用 useMemo 避免不必要的重新渲染
338
+
339
+ ```typescript
340
+ const options = useMemo(
341
+ () => ({
342
+ query,
343
+ sortBy: SortBy.UPDATED,
344
+ groupBy: GroupBy.CATEGORY,
345
+ }),
346
+ [query]
347
+ )
348
+
349
+ const { groups } = useOtherSkills(options)
350
+ ```
351
+
352
+ 3. **错误恢复** - 提供手动刷新按钮
353
+
354
+ ```typescript
355
+ <div>
356
+ {error && (
357
+ <div className="error">
358
+ {error.message}
359
+ <button onClick={() => refetch()}>重试</button>
360
+ </div>
361
+ )}
362
+ </div>
363
+ ```
364
+
365
+ ### 常见问题
366
+
367
+ **Q: 如何搜索多个关键词?**
368
+ A: 目前搜索是 OR 逻辑,可以在应用层面实现客户端 AND 逻辑。
369
+
370
+ **Q: 分组结果的顺序是什么?**
371
+ A: 按分组键的字典序排序(除非另有实现)。
372
+
373
+ **Q: 能否支持自定义 icon?**
374
+ A: 可以,API 返回的 `icon` 字段会优先使用。
375
+
376
+ **Q: 性能如何?**
377
+ A: Hook 使用 useMemo 确保搜索、排序、分组操作只在依赖项变化时重新计算。
378
+