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,421 @@
1
+ #!/bin/bash
2
+ # HuHaa-MySkills 前端框架初始化脚本
3
+ # Next.js 15 + React 18 + TypeScript 完整项目结构
4
+
5
+ set -e
6
+
7
+ PROJECT_NAME="huhaa-myskills-web"
8
+ WORKSPACE_DIR="/Users/mac/Project/HuHaa-MySkills"
9
+
10
+ echo "🚀 初始化 Next.js 前端项目..."
11
+ cd "$WORKSPACE_DIR"
12
+
13
+ # 1. 创建 Next.js 项目(使用官方 create-next-app)
14
+ npm create next-app@latest "$PROJECT_NAME" \
15
+ --typescript \
16
+ --tailwind \
17
+ --app \
18
+ --eslint \
19
+ --import-alias "@/*" \
20
+ --skip-git \
21
+ --no-src-dir
22
+
23
+ cd "$PROJECT_NAME"
24
+
25
+ # 2. 安装核心依赖
26
+ echo "📦 安装依赖包..."
27
+ npm install \
28
+ react@18.3.1 \
29
+ react-dom@18.3.1 \
30
+ zustand@4.4.7 \
31
+ @tanstack/react-query@5.36.2 \
32
+ react-hook-form@7.52.0 \
33
+ zod@3.23.5 \
34
+ lucide-react@0.408.0 \
35
+ class-variance-authority@0.7.0 \
36
+ clsx@2.1.0 \
37
+ tailwind-merge@2.3.0
38
+
39
+ # 3. 安装 shadcn/ui 和开发工具
40
+ npm install --save-dev \
41
+ @types/node@20.11.5 \
42
+ @types/react@18.2.48 \
43
+ @types/react-dom@18.2.18 \
44
+ typescript@5.3.3 \
45
+ autoprefixer@10.4.17 \
46
+ postcss@8.4.33 \
47
+ prettier@3.1.1 \
48
+ eslint@8.56.0 \
49
+ eslint-config-next@15.0.0 \
50
+ @typescript-eslint/parser@6.16.0 \
51
+ @typescript-eslint/eslint-plugin@6.16.0 \
52
+ eslint-plugin-react@7.33.2 \
53
+ eslint-plugin-react-hooks@4.6.0
54
+
55
+ # 4. 创建项目目录结构
56
+ echo "📁 创建项目目录结构..."
57
+
58
+ mkdir -p \
59
+ src/components/ui \
60
+ src/components/common \
61
+ src/components/layout \
62
+ src/features \
63
+ src/hooks \
64
+ src/lib \
65
+ src/types \
66
+ src/constants \
67
+ src/config \
68
+ src/styles \
69
+ src/providers \
70
+ tests/unit \
71
+ tests/integration \
72
+ tests/__mocks__ \
73
+ public/images \
74
+ public/icons
75
+
76
+ # 5. 创建核心配置文件
77
+ echo "⚙️ 创建配置文件..."
78
+
79
+ # TypeScript 配置
80
+ cat > tsconfig.json << 'EOF'
81
+ {
82
+ "compilerOptions": {
83
+ "target": "ES2020",
84
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
85
+ "jsx": "react-jsx",
86
+ "module": "ESNext",
87
+ "moduleResolution": "bundler",
88
+ "resolveJsonModule": true,
89
+ "strict": true,
90
+ "noImplicitAny": true,
91
+ "strictNullChecks": true,
92
+ "noUnusedLocals": true,
93
+ "noUnusedParameters": true,
94
+ "noImplicitReturns": true,
95
+ "forceConsistentCasingInFileNames": true,
96
+ "esModuleInterop": true,
97
+ "skipLibCheck": true,
98
+ "allowSyntheticDefaultImports": true,
99
+ "declaration": true,
100
+ "declarationMap": true,
101
+ "sourceMap": true,
102
+ "baseUrl": ".",
103
+ "paths": {
104
+ "@/*": ["src/*"],
105
+ "@/components/*": ["src/components/*"],
106
+ "@/lib/*": ["src/lib/*"],
107
+ "@/types/*": ["src/types/*"],
108
+ "@/hooks/*": ["src/hooks/*"],
109
+ "@/config/*": ["src/config/*"],
110
+ "@/styles/*": ["src/styles/*"],
111
+ "@/constants/*": ["src/constants/*"]
112
+ }
113
+ },
114
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
115
+ "exclude": ["node_modules"]
116
+ }
117
+ EOF
118
+
119
+ # ESLint 配置
120
+ cat > .eslintrc.json << 'EOF'
121
+ {
122
+ "extends": ["next/core-web-vitals", "next/typescript"],
123
+ "rules": {
124
+ "@typescript-eslint/no-explicit-any": "error",
125
+ "@typescript-eslint/no-unused-vars": "error",
126
+ "react-hooks/rules-of-hooks": "error",
127
+ "react-hooks/exhaustive-deps": "warn",
128
+ "react/prop-types": "off",
129
+ "no-console": ["warn", { "allow": ["warn", "error"] }]
130
+ }
131
+ }
132
+ EOF
133
+
134
+ # Prettier 配置
135
+ cat > .prettierrc << 'EOF'
136
+ {
137
+ "printWidth": 100,
138
+ "tabWidth": 2,
139
+ "useTabs": false,
140
+ "semi": true,
141
+ "singleQuote": true,
142
+ "trailingComma": "es5",
143
+ "bracketSpacing": true,
144
+ "jsxBracketSameLine": false,
145
+ "arrowParens": "always"
146
+ }
147
+ EOF
148
+
149
+ # Next.js 配置
150
+ cat > next.config.js << 'EOF'
151
+ /** @type {import('next').NextConfig} */
152
+ const nextConfig = {
153
+ reactStrictMode: true,
154
+ swcMinify: true,
155
+ images: {
156
+ formats: ['image/avif', 'image/webp'],
157
+ remotePatterns: [],
158
+ },
159
+ typescript: {
160
+ tsconfigPath: './tsconfig.json',
161
+ },
162
+ };
163
+
164
+ module.exports = nextConfig;
165
+ EOF
166
+
167
+ # 6. 创建 Tailwind 配置和主题
168
+ cat > tailwind.config.ts << 'EOF'
169
+ import type { Config } from 'tailwindcss'
170
+ import defaultTheme from 'tailwindcss/defaultTheme'
171
+
172
+ const config: Config = {
173
+ darkMode: ['class'],
174
+ content: [
175
+ './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
176
+ './src/components/**/*.{js,ts,jsx,tsx,mdx}',
177
+ './src/app/**/*.{js,ts,jsx,tsx,mdx}',
178
+ ],
179
+ theme: {
180
+ extend: {
181
+ fontFamily: {
182
+ sans: ['var(--font-sans)', ...defaultTheme.fontFamily.sans],
183
+ },
184
+ colors: {
185
+ border: 'hsl(var(--color-border))',
186
+ input: 'hsl(var(--color-input))',
187
+ ring: 'hsl(var(--color-ring))',
188
+ background: 'hsl(var(--color-background))',
189
+ foreground: 'hsl(var(--color-foreground))',
190
+ primary: {
191
+ DEFAULT: 'hsl(var(--color-primary))',
192
+ foreground: 'hsl(var(--color-primary-foreground))',
193
+ },
194
+ secondary: {
195
+ DEFAULT: 'hsl(var(--color-secondary))',
196
+ foreground: 'hsl(var(--color-secondary-foreground))',
197
+ },
198
+ destructive: {
199
+ DEFAULT: 'hsl(var(--color-destructive))',
200
+ foreground: 'hsl(var(--color-destructive-foreground))',
201
+ },
202
+ muted: {
203
+ DEFAULT: 'hsl(var(--color-muted))',
204
+ foreground: 'hsl(var(--color-muted-foreground))',
205
+ },
206
+ accent: {
207
+ DEFAULT: 'hsl(var(--color-accent))',
208
+ foreground: 'hsl(var(--color-accent-foreground))',
209
+ },
210
+ },
211
+ borderRadius: {
212
+ lg: 'var(--radius-lg)',
213
+ md: 'var(--radius-md)',
214
+ sm: 'var(--radius-sm)',
215
+ },
216
+ },
217
+ },
218
+ plugins: [require('tailwindcss-animate')],
219
+ }
220
+
221
+ export default config
222
+ EOF
223
+
224
+ # 7. 创建全局样式和主题变量
225
+ mkdir -p src/styles
226
+ cat > src/styles/globals.css << 'EOF'
227
+ @tailwind base;
228
+ @tailwind components;
229
+ @tailwind utilities;
230
+
231
+ @layer base {
232
+ :root {
233
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
234
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
235
+ sans-serif;
236
+
237
+ /* Light Mode (Default) */
238
+ --color-border: 220 13% 91%;
239
+ --color-input: 220 13% 91%;
240
+ --color-ring: 215 100% 46%;
241
+
242
+ --color-background: 0 0% 100%;
243
+ --color-foreground: 220 9% 12%;
244
+
245
+ --color-primary: 215 100% 46%;
246
+ --color-primary-foreground: 220 9% 12%;
247
+
248
+ --color-secondary: 217 33% 17%;
249
+ --color-secondary-foreground: 210 40% 96%;
250
+
251
+ --color-destructive: 0 84% 60%;
252
+ --color-destructive-foreground: 210 40% 96%;
253
+
254
+ --color-muted: 220 13% 91%;
255
+ --color-muted-foreground: 217 33% 40%;
256
+
257
+ --color-accent: 38 92% 50%;
258
+ --color-accent-foreground: 220 9% 12%;
259
+
260
+ --radius-lg: 0.5rem;
261
+ --radius-md: 0.375rem;
262
+ --radius-sm: 0.25rem;
263
+ }
264
+
265
+ .dark {
266
+ --color-border: 217 33% 17%;
267
+ --color-input: 217 33% 17%;
268
+ --color-ring: 215 100% 46%;
269
+
270
+ --color-background: 220 9% 8%;
271
+ --color-foreground: 210 40% 96%;
272
+
273
+ --color-primary: 215 100% 46%;
274
+ --color-primary-foreground: 220 9% 12%;
275
+
276
+ --color-secondary: 210 40% 96%;
277
+ --color-secondary-foreground: 217 33% 17%;
278
+
279
+ --color-destructive: 0 84% 60%;
280
+ --color-destructive-foreground: 0 0% 100%;
281
+
282
+ --color-muted: 217 33% 17%;
283
+ --color-muted-foreground: 215 14% 55%;
284
+
285
+ --color-accent: 38 92% 50%;
286
+ --color-accent-foreground: 220 9% 12%;
287
+ }
288
+ }
289
+
290
+ @layer base {
291
+ body {
292
+ @apply bg-background text-foreground;
293
+ }
294
+ }
295
+ EOF
296
+
297
+ # 8. 创建 package.json 脚本
298
+ cat > package.json << 'EOF'
299
+ {
300
+ "name": "huhaa-myskills-web",
301
+ "version": "1.0.0",
302
+ "description": "HuHaa MySkills - 前端应用",
303
+ "private": true,
304
+ "scripts": {
305
+ "dev": "next dev",
306
+ "build": "next build",
307
+ "start": "next start",
308
+ "lint": "next lint",
309
+ "format": "prettier --write \"src/**/*.{ts,tsx,json,css}\"",
310
+ "format:check": "prettier --check \"src/**/*.{ts,tsx,json,css}\"",
311
+ "type-check": "tsc --noEmit",
312
+ "test": "jest",
313
+ "test:watch": "jest --watch",
314
+ "test:coverage": "jest --coverage"
315
+ },
316
+ "dependencies": {
317
+ "react": "^18.3.1",
318
+ "react-dom": "^18.3.1",
319
+ "next": "^15.0.0",
320
+ "zustand": "^4.4.7",
321
+ "@tanstack/react-query": "^5.36.2",
322
+ "react-hook-form": "^7.52.0",
323
+ "zod": "^3.23.5",
324
+ "lucide-react": "^0.408.0",
325
+ "class-variance-authority": "^0.7.0",
326
+ "clsx": "^2.1.0",
327
+ "tailwind-merge": "^2.3.0"
328
+ },
329
+ "devDependencies": {
330
+ "typescript": "^5.3.3",
331
+ "@types/node": "^20.11.5",
332
+ "@types/react": "^18.2.48",
333
+ "@types/react-dom": "^18.2.18",
334
+ "tailwindcss": "^3.4.1",
335
+ "postcss": "^8.4.33",
336
+ "autoprefixer": "^10.4.17",
337
+ "prettier": "^3.1.1",
338
+ "eslint": "^8.56.0",
339
+ "eslint-config-next": "^15.0.0",
340
+ "@typescript-eslint/parser": "^6.16.0",
341
+ "@typescript-eslint/eslint-plugin": "^6.16.0",
342
+ "eslint-plugin-react": "^7.33.2",
343
+ "eslint-plugin-react-hooks": "^4.6.0",
344
+ "jest": "^29.7.0",
345
+ "@testing-library/react": "^14.1.2",
346
+ "@testing-library/jest-dom": "^6.1.5"
347
+ },
348
+ "engines": {
349
+ "node": ">=20.0.0",
350
+ "npm": ">=10.0.0"
351
+ }
352
+ }
353
+ EOF
354
+
355
+ # 9. 创建示例组件结构
356
+ cat > src/types/index.ts << 'EOF'
357
+ /**
358
+ * 全局类型定义
359
+ * 按功能模块组织,避免类型定义分散
360
+ */
361
+
362
+ // 通用类型
363
+ export interface BaseResponse<T> {
364
+ code: number
365
+ message: string
366
+ data: T
367
+ }
368
+
369
+ export interface PaginatedResponse<T> {
370
+ items: T[]
371
+ total: number
372
+ page: number
373
+ pageSize: number
374
+ }
375
+
376
+ // 用户相关
377
+ export interface User {
378
+ id: string
379
+ name: string
380
+ email: string
381
+ avatar?: string
382
+ createdAt: string
383
+ }
384
+
385
+ // 技能相关
386
+ export interface Skill {
387
+ id: string
388
+ name: string
389
+ description: string
390
+ category: string
391
+ level: 1 | 2 | 3 | 4 | 5
392
+ createdAt: string
393
+ }
394
+ EOF
395
+
396
+ cat > src/lib/cn.ts << 'EOF'
397
+ import { type ClassValue, clsx } from 'clsx'
398
+ import { twMerge } from 'tailwind-merge'
399
+
400
+ /**
401
+ * 合并 Tailwind CSS 类名
402
+ * 处理 Tailwind 类名冲突
403
+ */
404
+ export function cn(...inputs: ClassValue[]) {
405
+ return twMerge(clsx(inputs))
406
+ }
407
+ EOF
408
+
409
+ echo "✅ 项目初始化完成!"
410
+ echo ""
411
+ echo "📋 后续步骤:"
412
+ echo "1. cd $PROJECT_NAME"
413
+ echo "2. npm run dev"
414
+ echo "3. 访问 http://localhost:3000"
415
+ echo ""
416
+ echo "📚 规范文档:"
417
+ echo " - TypeScript Strict 模式已启用"
418
+ echo " - Tailwind CSS + CSS Variables 主题系统就绪"
419
+ echo " - shadcn/ui 组件库已配置"
420
+ echo " - ESLint + Prettier 自动格式化已启用"
421
+ echo ""
@@ -0,0 +1,209 @@
1
+ #!/bin/bash
2
+
3
+ ###############################################################################
4
+ # HuHaa-MySkills 远程安装与技能同步入口 (v0.1.5)
5
+ #
6
+ # 用法:
7
+ # curl -fsSL https://raw.githubusercontent.com/aquarkgn/HuHaa-MySkills/main/install-and-sync.sh | bash
8
+ # 或
9
+ # curl -fsSL https://raw.githubusercontent.com/aquarkgn/HuHaa-MySkills/main/install-and-sync.sh | bash -s -- --sync-only
10
+ #
11
+ # 参数:
12
+ # --sync-only 只执行同步,不安装
13
+ # --local PATH 使用本地项目路径
14
+ # --branch NAME 指定分支(默认 main)
15
+ #
16
+ ###############################################################################
17
+
18
+ set -euo pipefail
19
+
20
+ # 配置
21
+ REPO_URL="https://github.com/aquarkgn/HuHaa-MySkills.git"
22
+ REPO_RAW="https://raw.githubusercontent.com/aquarkgn/HuHaa-MySkills"
23
+ BRANCH="main"
24
+ SYNC_ONLY=false
25
+ LOCAL_PATH=""
26
+
27
+ # 颜色定义
28
+ readonly BLUE='\033[0;34m'
29
+ readonly GREEN='\033[0;32m'
30
+ readonly YELLOW='\033[1;33m'
31
+ readonly RED='\033[0;31m'
32
+ readonly CYAN='\033[0;36m'
33
+ readonly NC='\033[0m'
34
+
35
+ # 日志函数
36
+ log_info() { echo -e "${BLUE}ℹ${NC} $*"; }
37
+ log_success() { echo -e "${GREEN}✓${NC} $*"; }
38
+ log_warn() { echo -e "${YELLOW}⚠${NC} $*"; }
39
+ log_error() { echo -e "${RED}✗${NC} $*" >&2; }
40
+
41
+ # 解析命令行参数
42
+ parse_args() {
43
+ while [[ $# -gt 0 ]]; do
44
+ case "$1" in
45
+ --sync-only)
46
+ SYNC_ONLY=true
47
+ shift
48
+ ;;
49
+ --local)
50
+ LOCAL_PATH="$2"
51
+ SYNC_ONLY=true
52
+ shift 2
53
+ ;;
54
+ --branch)
55
+ BRANCH="$2"
56
+ shift 2
57
+ ;;
58
+ *)
59
+ log_error "未知参数: $1"
60
+ exit 1
61
+ ;;
62
+ esac
63
+ done
64
+ }
65
+
66
+ # 获取临时目录
67
+ get_temp_dir() {
68
+ if command -v mktemp &>/dev/null; then
69
+ mktemp -d
70
+ else
71
+ local tmpdir="/tmp/huhaa-myskills-$$"
72
+ mkdir -p "$tmpdir"
73
+ echo "$tmpdir"
74
+ fi
75
+ }
76
+
77
+ # 检查依赖
78
+ check_deps() {
79
+ if ! command -v curl &>/dev/null; then
80
+ log_error "需要 curl,请先安装"
81
+ exit 1
82
+ fi
83
+
84
+ if ! $SYNC_ONLY && ! command -v git &>/dev/null; then
85
+ log_error "需要 git,请先安装"
86
+ exit 1
87
+ fi
88
+ }
89
+
90
+ # 下载并执行同步脚本
91
+ download_and_sync() {
92
+ local sync_script_url="$REPO_RAW/$BRANCH/service/scripts/sync-skills.sh"
93
+
94
+ log_info "下载同步脚本..."
95
+
96
+ local temp_script
97
+ temp_script=$(mktemp)
98
+ trap "rm -f '$temp_script'" EXIT
99
+
100
+ if ! curl -fsSL "$sync_script_url" -o "$temp_script"; then
101
+ log_error "下载失败: $sync_script_url"
102
+ return 1
103
+ fi
104
+
105
+ if [[ -n "$LOCAL_PATH" ]]; then
106
+ export HUHAA_LOCAL_PATH="$LOCAL_PATH"
107
+ fi
108
+
109
+ log_success "同步脚本已下载"
110
+
111
+ # 执行同步脚本
112
+ bash "$temp_script"
113
+ }
114
+
115
+ # 本地安装(git clone)
116
+ local_install() {
117
+ local temp_dir
118
+ temp_dir=$(get_temp_dir)
119
+ trap "rm -rf '$temp_dir'" EXIT
120
+
121
+ log_info "克隆项目仓库..."
122
+
123
+ if ! git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$temp_dir"; then
124
+ log_error "克隆失败"
125
+ return 1
126
+ fi
127
+
128
+ local_path="$temp_dir"
129
+ log_success "项目已克隆到临时目录"
130
+
131
+ # 执行同步
132
+ export HUHAA_LOCAL_PATH="$local_path"
133
+ download_and_sync
134
+ }
135
+
136
+ # 检查 npm 全局安装
137
+ npm_install() {
138
+ if ! command -v npm &>/dev/null; then
139
+ log_warn "未安装 npm,跳过全局安装"
140
+ return 0
141
+ fi
142
+
143
+ log_info "正在进行 npm 全局安装..."
144
+
145
+ if npm install -g "github:aquarkgn/HuHaa-MySkills#$BRANCH"; then
146
+ log_success "npm 全局安装完成"
147
+
148
+ # 获取安装位置
149
+ local npm_prefix
150
+ npm_prefix=$(npm prefix -g)
151
+ export HUHAA_LOCAL_PATH="$npm_prefix/node_modules/huhaa-myskills"
152
+
153
+ return 0
154
+ else
155
+ log_warn "npm 全局安装失败,将使用远程脚本"
156
+ return 1
157
+ fi
158
+ }
159
+
160
+ # 主函数
161
+ main() {
162
+ echo -e "\n${CYAN}╔════════════════════════════════════════════════════════╗${NC}"
163
+ echo -e "${CYAN}║ HuHaa-MySkills v0.1.5 - 编辑器技能自动同步 ║${NC}"
164
+ echo -e "${CYAN}╚════════════════════════════════════════════════════════╝${NC}\n"
165
+
166
+ parse_args "$@"
167
+ check_deps
168
+
169
+ if $SYNC_ONLY; then
170
+ log_info "执行同步模式"
171
+ if [[ -z "$LOCAL_PATH" ]]; then
172
+ # 只下载并执行远程脚本
173
+ download_and_sync
174
+ else
175
+ # 使用本地路径
176
+ if [[ ! -d "$LOCAL_PATH" ]]; then
177
+ log_error "本地路径不存在: $LOCAL_PATH"
178
+ exit 1
179
+ fi
180
+ export HUHAA_LOCAL_PATH="$LOCAL_PATH"
181
+ download_and_sync
182
+ fi
183
+ else
184
+ log_info "执行完整安装 + 同步流程"
185
+
186
+ # 尝试 npm 全局安装
187
+ if ! npm_install; then
188
+ # 如果 npm 安装失败,使用 git clone
189
+ log_info "使用 git clone 方式安装"
190
+ local_install
191
+ else
192
+ # npm 安装成功,执行同步
193
+ download_and_sync
194
+ fi
195
+ fi
196
+
197
+ echo -e "\n${GREEN}╔════════════════════════════════════════════════════════╗${NC}"
198
+ echo -e "${GREEN}║ ✨ 同步完成! ✨ ║${NC}"
199
+ echo -e "${GREEN}╚════════════════════════════════════════════════════════╝${NC}\n"
200
+
201
+ log_info "后续步骤:"
202
+ echo " 1. 启动 HuHaa: npm start"
203
+ echo " 2. 查看技能中心: http://localhost:11520"
204
+ echo " 3. 在编辑器中使用你的技能"
205
+ echo -e "\n更多信息: https://github.com/aquarkgn/HuHaa-MySkills\n"
206
+ }
207
+
208
+ # 执行主程序
209
+ main "$@"
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+
3
+ # HuHaa-MySkills 智能启动脚本 - 完全后台运行
4
+ # 功能:后台启动,自动处理端口占用,重启本服务或切换端口
5
+
6
+ DEFAULT_PORT=11522
7
+ PORT=$DEFAULT_PORT
8
+ MAX_ATTEMPTS=5
9
+ ATTEMPT=0
10
+
11
+ # 日志文件
12
+ LOG_FILE="/tmp/huhaa-myskills-dev.log"
13
+
14
+ # 检查端口是否被占用
15
+ check_port() {
16
+ lsof -i :$1 >/dev/null 2>&1
17
+ return $?
18
+ }
19
+
20
+ # 获取占用端口的进程信息
21
+ get_process_info() {
22
+ lsof -ti :$1 2>/dev/null | head -1
23
+ }
24
+
25
+ # 检查是否为本服务进程
26
+ is_our_process() {
27
+ pid=$1
28
+ if [ -z "$pid" ]; then return 1; fi
29
+ ps -p $pid 2>/dev/null | grep -E "huhaa-myskills|node.*bin" >/dev/null 2>&1
30
+ return $?
31
+ }
32
+
33
+ # 主逻辑(后台运行)
34
+ {
35
+ ATTEMPT=0
36
+ while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
37
+ if check_port $PORT; then
38
+ # 端口被占用
39
+ pid=$(get_process_info $PORT)
40
+ if is_our_process $pid; then
41
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] ✓ Service already running on port $PORT (PID: $pid)"
42
+ # 杀死旧进程并重启
43
+ kill -9 $pid 2>/dev/null
44
+ sleep 1
45
+ nohup npm run start >/dev/null 2>&1 &
46
+ sleep 2
47
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] ✓ Service restarted on port $PORT"
48
+ exit 0
49
+ else
50
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] ⚠ Port $PORT occupied by other process (PID: $pid), trying $((PORT + 1))..."
51
+ PORT=$((PORT + 1))
52
+ ATTEMPT=$((ATTEMPT + 1))
53
+ fi
54
+ else
55
+ # 端口空闲,启动服务
56
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting HuHaa-MySkills on port $PORT..."
57
+ nohup npm run start >/dev/null 2>&1 &
58
+ sleep 2
59
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] ✓ Service started on port $PORT"
60
+ exit 0
61
+ fi
62
+ done
63
+
64
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] ✗ Failed to start: no available ports"
65
+ exit 1
66
+ } >> $LOG_FILE 2>&1 &
67
+
68
+ # 立即返回(真正的后台运行)
69
+ exec 2>/dev/null
70
+ exit 0
71
+
72
+