huhaa-myskills 0.2.12 → 0.3.0

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.
@@ -72,7 +72,7 @@ Usage:
72
72
  huhaa-myskills <command> [options]
73
73
 
74
74
  Commands:
75
- start Scan + start server + open browser (default)
75
+ start Scan + start server + open browser (runs in background by default)
76
76
  scan Scan only, dump IR JSON to stdout
77
77
  stats Scan + print human-readable summary (counts / brands / errors / samples)
78
78
  duplicates Scan + print duplicate diagnostics by name/content/path
@@ -82,8 +82,9 @@ Commands:
82
82
  dev Dev mode (Vite + nodemon)
83
83
 
84
84
  Options:
85
- -v, --version Show version
86
- -h, --help Show this message
85
+ -v, --version Show version
86
+ -h, --help Show this message
87
+ -f, --foreground Keep running in foreground (for debugging)
87
88
 
88
89
  Env:
89
90
  HUHAA_HOME override user data dir (default: ~/.config/huhaa-myskills)
@@ -94,6 +95,7 @@ Paths:
94
95
  config ${configFile()}
95
96
  cache ${cacheFile()}
96
97
  state ${stateFile()}
98
+ logs ${path.join(homeDir(), 'huhaa.log')}
97
99
  `);
98
100
  }
99
101
 
@@ -268,6 +270,7 @@ function printDupGroup(title, groups, limit) {
268
270
 
269
271
  async function cmdStart() {
270
272
  await ensureConfigOrInit();
273
+ const isForeground = process.argv.includes('--foreground') || process.argv.includes('-f');
271
274
  const preferred = parseInt(process.env.PORT || '11520', 10);
272
275
  const port = await pickPort(preferred, 10);
273
276
  if (!port) {
@@ -275,16 +278,79 @@ async function cmdStart() {
275
278
  process.exit(1);
276
279
  }
277
280
  if (port !== preferred) {
278
- console.error(`[start] port ${preferred} busy, using ${port} instead.`);
281
+ console.warn(`[start] port ${preferred} busy, using ${port} instead.`);
279
282
  }
280
283
 
284
+ const homeDirectory = homeDir();
285
+ const logFile = path.join(homeDirectory, 'huhaa.log');
286
+
287
+ // 后台模式:fork 子进程
288
+ if (!isForeground) {
289
+ return startBackgroundServer(port, logFile, homeDirectory);
290
+ }
291
+
292
+ // 前台模式:直接启动
293
+ await runServer(port, logFile, homeDirectory);
294
+ }
295
+
296
+ async function startBackgroundServer(port, logFile, homeDirectory) {
297
+ // 检查是否已有运行的实例
298
+ try {
299
+ const stateData = JSON.parse(fs.readFileSync(stateFile(), 'utf8'));
300
+ if (stateData.pid && isProcessRunning(stateData.pid)) {
301
+ console.log(`✓ HuHaa-MySkills already running on http://localhost:${stateData.port}`);
302
+ console.log(`📝 Logs: ${logFile}`);
303
+ return;
304
+ }
305
+ } catch {
306
+ // 状态文件不存在或无效,继续启动
307
+ }
308
+
309
+ // 打开日志文件用于输出重定向
310
+ const logFd = fs.openSync(logFile, 'a');
311
+
312
+ // Spawn 子进程
313
+ const child = spawn(process.execPath, [process.argv[1], 'start', '--foreground'], {
314
+ detached: true,
315
+ stdio: ['ignore', logFd, logFd],
316
+ env: {
317
+ ...process.env,
318
+ PORT: port.toString()
319
+ }
320
+ });
321
+
322
+ child.unref();
323
+
324
+ // 等待子进程启动,检查端口是否开放
325
+ await new Promise(resolve => setTimeout(resolve, 1000));
326
+
327
+ // 关闭日志文件描述符(子进程已继承)
328
+ fs.closeSync(logFd);
329
+
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"`);
334
+ }
335
+
336
+ function isProcessRunning(pid) {
337
+ try {
338
+ process.kill(pid, 0);
339
+ return true;
340
+ } catch {
341
+ return false;
342
+ }
343
+ }
344
+
345
+ async function runServer(port, logFile, homeDirectory) {
281
346
  writeJson(stateFile(), { port, pid: process.pid, startedAt: new Date().toISOString() });
282
347
 
283
348
  const { startServer } = await import(path.join(PKGS_ROOT, 'server/src/index.mjs'));
284
349
  await startServer({ port });
285
350
 
286
351
  console.log(`\n HuHaa-MySkills running: http://localhost:${port}\n`);
287
- console.log(` data dir: ${homeDir()}`);
352
+ console.log(` data dir: ${homeDirectory}`);
353
+ console.log(` logs: ${logFile}`);
288
354
  console.log(` Ctrl+C to stop.\n`);
289
355
 
290
356
  openBrowser(`http://localhost:${port}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huhaa-myskills",
3
- "version": "0.2.12",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Local skill / plugin / MCP aggregation hub. Browse all your AI agent skills at http://localhost:11520.",
6
6
  "license": "MIT",
@@ -0,0 +1 @@
1
+ .skill-tree[data-v-1b7c4344]{display:flex;flex-direction:column;overflow-y:auto;font-size:13px;border-right:1px solid #ddd;-webkit-user-select:none;user-select:none}.tree-group[data-v-1b7c4344]{border-bottom:1px solid #f0f0f0}.tree-group-header[data-v-1b7c4344]{width:100%;padding:8px 10px;background:#f8f8f8;border:none;border-bottom:1px solid #e8e8e8;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:6px;font-size:12px;text-align:left;justify-content:flex-start;transition:all .15s ease}.tree-group-header[data-v-1b7c4344]:hover{background:#f0f0f0}.tree-group-header.expanded[data-v-1b7c4344]{background:#e8e8e8}.tree-arrow[data-v-1b7c4344]{width:16px;text-align:center;flex-shrink:0;font-size:11px;color:#666}.source-icon[data-v-1b7c4344]{width:16px;text-align:center;flex-shrink:0;font-size:14px}.source-name[data-v-1b7c4344]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skill-count[data-v-1b7c4344]{margin-left:auto;color:#999;font-size:11px;font-weight:400;flex-shrink:0}.tree-group-items[data-v-1b7c4344]{background:#fafafa;display:flex;flex-direction:column}.skill-tree-item[data-v-1b7c4344]{display:flex;align-items:center;gap:6px;width:100%;padding:6px 16px;border:none;background:transparent;cursor:pointer;font-size:12px;transition:background-color .1s ease;text-align:left;color:inherit;justify-content:flex-start}.skill-tree-item[data-v-1b7c4344]:hover{background:#f0f0f0}.skill-tree-item.active[data-v-1b7c4344]{background:#e3f2fd;color:#1976d2;font-weight:500}.icon[data-v-1b7c4344]{width:16px;text-align:center;flex-shrink:0;font-size:12px}.name[data-v-1b7c4344]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.category-tag[data-v-1b7c4344]{margin-left:4px;padding:1px 4px;background:#f0f0f0;border-radius:2px;font-size:10px;color:#666;flex-shrink:0}.empty-tree[data-v-1b7c4344]{display:flex;align-items:center;justify-content:center;height:100px;color:#999;font-size:12px}.directory-tree[data-v-265e211f]{display:flex;flex-direction:column;overflow-y:auto;font-size:13px;-webkit-user-select:none;user-select:none}.tree-item[data-v-265e211f]{display:flex;flex-direction:column}.tree-dir[data-v-265e211f]{padding:6px 10px;background:transparent;border:none;cursor:pointer;display:flex;align-items:center;gap:6px;font-size:12px;text-align:left;color:inherit;transition:background-color .1s ease;width:100%;justify-content:flex-start}.tree-dir[data-v-265e211f]:hover{background:#f0f0f0}.tree-dir.selected[data-v-265e211f]{background:#e8f4f8;color:#0d5a7c;font-weight:500}.tree-arrow[data-v-265e211f]{width:16px;text-align:center;flex-shrink:0;font-size:10px;color:#666}.tree-icon[data-v-265e211f]{width:16px;text-align:center;flex-shrink:0;font-size:12px}.tree-name[data-v-265e211f]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}.tree-count[data-v-265e211f]{margin-left:auto;padding:0 4px;background:#e8e8e8;border-radius:2px;font-size:10px;color:#666;flex-shrink:0}.tree-skills[data-v-265e211f]{display:flex;flex-direction:column}.tree-skill-item[data-v-265e211f]{padding:6px 10px;background:transparent;border:none;cursor:pointer;display:flex;align-items:center;gap:6px;font-size:12px;text-align:left;color:inherit;transition:background-color .1s ease;width:100%;justify-content:flex-start}.tree-skill-item[data-v-265e211f]:hover{background:#f5f5f5}.tree-skill-item.active[data-v-265e211f]{background:#e3f2fd;color:#1976d2;font-weight:500}.empty-tree[data-v-265e211f]{display:flex;align-items:center;justify-content:center;height:100px;color:#999;font-size:12px}.app-tree[data-v-450fa8e2]{display:flex;flex-direction:column;overflow-y:auto;font-size:13px;-webkit-user-select:none;user-select:none}.tree-group[data-v-450fa8e2]{border-bottom:1px solid #f0f0f0}.tree-group-header[data-v-450fa8e2]{width:100%;padding:8px 10px;background:#f8f8f8;border:none;border-bottom:1px solid #e8e8e8;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:6px;font-size:12px;text-align:left;justify-content:flex-start;transition:all .15s ease}.tree-group-header[data-v-450fa8e2]:hover{background:#f0f0f0}.tree-group-header.expanded[data-v-450fa8e2]{background:#e8e8e8}.tree-arrow[data-v-450fa8e2]{width:16px;text-align:center;flex-shrink:0;font-size:11px;color:#666}.app-icon[data-v-450fa8e2]{width:16px;text-align:center;flex-shrink:0;font-size:14px}.app-name[data-v-450fa8e2]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skill-count[data-v-450fa8e2]{margin-left:auto;color:#999;font-size:11px;font-weight:400;flex-shrink:0}.tree-group-items[data-v-450fa8e2]{background:#fafafa;display:flex;flex-direction:column}.skill-tree-item[data-v-450fa8e2]{display:flex;align-items:center;gap:6px;width:100%;padding:6px 16px;border:none;background:transparent;cursor:pointer;font-size:12px;text-align:left;transition:background-color .1s ease;color:inherit;justify-content:flex-start}.skill-tree-item[data-v-450fa8e2]:hover{background:#f0f0f0}.skill-tree-item.active[data-v-450fa8e2]{background:#e3f2fd;color:#1976d2;font-weight:500}.icon[data-v-450fa8e2]{width:16px;text-align:center;flex-shrink:0;font-size:12px}.name[data-v-450fa8e2]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.source-tag[data-v-450fa8e2]{margin-left:4px;padding:1px 4px;background:#f0f0f0;border-radius:2px;font-size:10px;color:#666;flex-shrink:0}.empty-tree[data-v-450fa8e2]{display:flex;align-items:center;justify-content:center;height:100px;color:#999;font-size:12px}:root{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,sans-serif;color:#182033;background:#eef1f6;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}*{box-sizing:border-box}html,body,#app{height:100%;margin:0}button,input{font:inherit}button{cursor:pointer}.shell{height:100vh;display:grid;grid-template-columns:100px 1fr 400px;grid-template-rows:60px 1fr;overflow:hidden;gap:0}.sidebar{grid-column:1 / 2;grid-row:2 / 3;background:#f8fafc;border-right:1px solid #e5e7eb;overflow:hidden;padding:0;position:relative;-webkit-user-select:none;user-select:none;display:flex;flex-direction:column}.sidebar-toggle{flex:0 0 auto;height:40px;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #e5e7eb;cursor:pointer;background:#fff;font-size:14px;color:#6b7280;transition:all .2s ease;padding:0 4px}.sidebar-toggle:hover{background:#f3f4f6;color:#374151}.sidebar.collapsed .sidebar-toggle{justify-content:center;padding:0}.sidebar-content{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px}.filter-compact{display:flex;flex-direction:column;gap:8px}.filter-item{display:flex;flex-direction:column;gap:4px;font-size:11px}.filter-item span{color:#6b7280;font-weight:600;padding:0 4px}.filter-select-compact{padding:6px 4px;border:1px solid #d1d5db;border-radius:4px;background:#fff;color:#374151;font-size:11px;cursor:pointer}.filter-select-compact:hover{border-color:#9ca3af}.filter-select-compact:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 2px #8b5cf61a}.sidebar-resize-handle{position:absolute;right:0;top:0;bottom:0;width:4px;background:transparent;cursor:col-resize;transition:background .2s}.sidebar-resize-handle:hover{background:#8b5cf6}.topbar{grid-column:1 / 4;grid-row:1 / 2;padding:10px 16px;background:linear-gradient(180deg,#ffffffeb,#f8fafceb);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid #d9dee8;display:flex;align-items:center;gap:12px;height:60px}.topbar-left{display:flex;align-items:center;gap:10px;flex:1;min-width:0}.search-box{display:flex;align-items:center;gap:9px;padding:0 13px;border:1px solid #d4dbe8;border-radius:14px;background:#f8fafc;box-shadow:inset 0 1px #fffc;min-width:0;flex:1}.search-icon{color:#7c3aed;font-size:18px;line-height:1;flex-shrink:0}.search-box input{width:100%;height:40px;padding:0;border:0;outline:none;background:transparent;color:#111827;font-size:14px}.search-box input::placeholder{color:#9ca3af}.search-box:focus-within{border-color:#8b5cf6;background:#fff;box-shadow:0 0 0 4px #7c3aed1c}.topbar-right{display:flex;gap:8px;flex-shrink:0}.btn{height:40px;border:0;border-radius:12px;padding:0 14px;font-weight:600;font-size:13px;color:#374151;background:#eef2ff;transition:transform .12s ease,box-shadow .12s ease,background .12s ease;cursor:pointer;white-space:nowrap}.btn:hover{transform:translateY(-1px);box-shadow:0 8px 18px #0f172a1a}.btn:disabled{opacity:.58;cursor:wait;transform:none;box-shadow:none}.btn.soft{background:#f1f5f9;color:#475569}.btn.primary{color:#fff;background:linear-gradient(135deg,#7c3aed,#2563eb);box-shadow:0 10px 20px #4f46e52e}.main{grid-column:2 / 3;grid-row:1 / 3;overflow:hidden;display:flex;flex-direction:column;background:#f3f5fa}.content-wrapper{flex:1;overflow:hidden;display:flex;flex-direction:column;min-height:0}.error,.loading{padding:16px;text-align:center;font-size:14px}.error{color:#dc2626;background:#fee2e2}.loading{color:#7c3aed;background:#f3f4f6}.list-panel{flex:1;overflow-y:auto;display:flex;flex-direction:column;border-right:1px solid #e5e7eb}.list-head{flex:0 0 auto;padding:12px 16px;display:flex;align-items:center;justify-content:space-between;gap:16px;background:#fff;border-bottom:1px solid #e5e7eb}.list-view-label{font-weight:600;color:#111827;min-width:80px}.list-head>div:last-child{display:flex;align-items:center;gap:6px;color:#6b7280;font-size:13px}.list-head strong{color:#111827;font-size:14px}.skill-card{width:100%;padding:12px 16px;border:0;border-bottom:1px solid #e5e7eb;background:#fff;text-align:left;cursor:pointer;transition:background .2s ease;font-size:13px}.skill-card:hover{background:#f9fafb}.skill-card.selected{background:#f0f9ff;border-left:3px solid #0284c7;padding-left:13px}.skill-title{display:flex;align-items:center;gap:8px;margin-bottom:6px}.src{display:inline-block;padding:2px 6px;border-radius:4px;font-size:10px;font-weight:600;background:#e0e7ff;color:#3730a3;flex-shrink:0}.skill-title strong{color:#111827;font-weight:600;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skill-chipline{display:flex;gap:6px;margin-bottom:4px;font-size:12px}.skill-chipline span{padding:2px 6px;border-radius:3px;background:#f3f4f6;color:#6b7280}.skill-meta{font-size:12px;color:#9ca3af;margin-bottom:6px}.skill-card p{margin:0;color:#6b7280;font-size:12px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}mark{background:#fef3c7;padding:2px 0;border-radius:2px}.detail-panel-fixed{grid-column:3 / 4;grid-row:1 / 3;background:#fff;border-left:1px solid #e5e7eb;overflow:hidden;display:flex;flex-direction:column;position:relative}.detail-close-btn{flex:0 0 auto;padding:12px 16px;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:center}.detail-close-btn h3{margin:0;font-size:14px;font-weight:600}.detail-close-btn button{padding:6px 12px;border:1px solid #d1d5db;border-radius:6px;background:#fff;color:#6b7280;font-size:12px;cursor:pointer;transition:all .2s ease}.detail-close-btn button:hover{background:#f3f4f6;color:#374151}.detail-head{flex:0 0 auto;padding:16px;border-bottom:1px solid #e5e7eb}.detail-head>div>div:first-child{margin-bottom:8px}.detail-kicker{display:flex;gap:6px;align-items:center;font-size:11px;margin-bottom:4px}.detail-kicker span{padding:2px 6px;border-radius:4px;background:#f3f4f6;color:#6b7280}.detail-kicker .src{background:none;font-weight:600}.detail-head h2{margin:0 0 8px;font-size:18px}.badge-row{display:flex;gap:4px;margin-bottom:8px;flex-wrap:wrap}.badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:#e0e7ff;color:#3730a3}.badge.danger{background:#fee2e2;color:#b91c1c}.badge.warn{background:#fef3c7;color:#b45309}.detail-head p{margin:0;font-size:13px;color:#4b5563;line-height:1.5}.actions{flex:0 0 auto;padding:12px 16px;border-bottom:1px solid #e5e7eb;display:grid;grid-template-columns:repeat(2,1fr);gap:6px}.action-btn{padding:8px 12px;font-size:12px;border:1px solid #d1d5db;border-radius:8px;background:#fff;color:#374151;cursor:pointer;transition:all .2s ease;white-space:nowrap}.action-btn:hover{background:#f9fafb;border-color:#9ca3af}.notice{flex:0 0 auto;padding:8px 16px;background:#fef3c7;border-bottom:1px solid #fcd34d;font-size:12px;color:#92400e}.kv>div{display:grid;grid-template-columns:140px 1fr;gap:12px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #f3f4f6;font-size:12px}@media(max-width:1199px){.shell{grid-template-columns:80px 1fr 320px}}@media(max-width:767px){.shell{grid-template-columns:1fr 300px}.detail-panel{grid-column:2 / 3}.actions,.usage-grid{grid-template-columns:1fr}.kv>div{grid-template-columns:1fr}}.detail-close{position:absolute;top:12px;right:12px;width:32px;height:32px;border:0;border-radius:8px;background:#f3f4f6;color:#6b7280;font-size:18px;cursor:pointer;z-index:10;transition:all .2s ease}.detail-close:hover{background:#e5e7eb;color:#1f2937}.detail-scroll{flex:1 1 auto;overflow-y:auto;padding:16px}.usage-card{margin-bottom:24px}.usage-head{margin-bottom:12px}.usage-head span{display:block;font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}.usage-head h3{margin:0;font-size:14px;font-weight:600}.usage-prompt{margin-bottom:12px;padding:10px 12px;background:#f3f4f6;border-radius:8px;border-left:3px solid #8b5cf6}.usage-prompt code{font-size:11px;color:#374151;word-break:break-word}.usage-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px}.usage-grid>div{padding:8px 10px;background:#f9fafb;border-radius:6px}.usage-grid strong{display:block;font-size:11px;color:#6b7280;margin-bottom:4px}.usage-grid p{margin:0;font-size:12px;color:#374151;line-height:1.4}.usage-block{margin-bottom:12px}.usage-block strong{display:block;font-size:12px;margin-bottom:6px}.tag{display:inline-block;padding:3px 8px;margin:2px 4px 2px 0;border-radius:4px;background:#e0e7ff;color:#3730a3;font-size:11px}.param-table{display:grid;gap:8px;font-size:12px}.param-table>div{padding:8px;background:#f9fafb;border-radius:6px}.param-table code{display:block;font-size:11px;color:#dc2626;margin-bottom:2px}.param-table b{display:inline-block;padding:1px 4px;background:#fef3c7;color:#b45309;border-radius:2px;font-size:10px;margin-left:4px}.usage-link{display:block;padding:6px 0;font-size:12px;color:#0ea5e9;text-decoration:none;border-bottom:1px solid #e0e7ff;word-break:break-word}.usage-link:hover{color:#0284c7}.kv{margin-bottom:24px}.kv>div{margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #f3f4f6;font-size:12px}.kv dt{font-weight:600;color:#6b7280;word-break:break-word}.kv dd{margin:0;color:#374151;word-break:break-word}.kv code{display:block;padding:6px;background:#f3f4f6;border-radius:4px;font-size:11px;color:#374151;overflow-x:auto}.kv-error dd{color:#b91c1c}.markdown{margin-bottom:20px;font-size:13px;line-height:1.6;color:#374151}.markdown h1,.markdown h2,.markdown h3{margin-top:16px;margin-bottom:8px;font-weight:600}.markdown p{margin:8px 0}.markdown code{background:#f3f4f6;padding:2px 4px;border-radius:3px;font-size:11px;color:#dc2626}.markdown pre{background:#1f2937;padding:10px;border-radius:6px;overflow-x:auto;margin:8px 0}.markdown pre code{background:none;color:#e5e7eb;padding:0}