@panguard-ai/panguard 0.3.0 → 0.3.2

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 (72) hide show
  1. package/dist/cli/auth-guard.d.ts +9 -7
  2. package/dist/cli/auth-guard.d.ts.map +1 -1
  3. package/dist/cli/auth-guard.js +33 -199
  4. package/dist/cli/auth-guard.js.map +1 -1
  5. package/dist/cli/commands/doctor.d.ts.map +1 -1
  6. package/dist/cli/commands/doctor.js +146 -26
  7. package/dist/cli/commands/doctor.js.map +1 -1
  8. package/dist/cli/commands/guard.d.ts.map +1 -1
  9. package/dist/cli/commands/guard.js +264 -7
  10. package/dist/cli/commands/guard.js.map +1 -1
  11. package/dist/cli/commands/hacktivity.d.ts +10 -0
  12. package/dist/cli/commands/hacktivity.d.ts.map +1 -0
  13. package/dist/cli/commands/hacktivity.js +308 -0
  14. package/dist/cli/commands/hacktivity.js.map +1 -0
  15. package/dist/cli/commands/login.d.ts +3 -4
  16. package/dist/cli/commands/login.d.ts.map +1 -1
  17. package/dist/cli/commands/login.js +8 -258
  18. package/dist/cli/commands/login.js.map +1 -1
  19. package/dist/cli/commands/logout.d.ts +3 -3
  20. package/dist/cli/commands/logout.d.ts.map +1 -1
  21. package/dist/cli/commands/logout.js +7 -35
  22. package/dist/cli/commands/logout.js.map +1 -1
  23. package/dist/cli/commands/report.d.ts +2 -2
  24. package/dist/cli/commands/report.d.ts.map +1 -1
  25. package/dist/cli/commands/report.js +30 -40
  26. package/dist/cli/commands/report.js.map +1 -1
  27. package/dist/cli/commands/scan.d.ts.map +1 -1
  28. package/dist/cli/commands/scan.js +16 -19
  29. package/dist/cli/commands/scan.js.map +1 -1
  30. package/dist/cli/commands/setup-skill-scan.d.ts +39 -0
  31. package/dist/cli/commands/setup-skill-scan.d.ts.map +1 -0
  32. package/dist/cli/commands/setup-skill-scan.js +186 -0
  33. package/dist/cli/commands/setup-skill-scan.js.map +1 -0
  34. package/dist/cli/commands/setup.d.ts +11 -0
  35. package/dist/cli/commands/setup.d.ts.map +1 -0
  36. package/dist/cli/commands/setup.js +219 -0
  37. package/dist/cli/commands/setup.js.map +1 -0
  38. package/dist/cli/commands/status.js +43 -0
  39. package/dist/cli/commands/status.js.map +1 -1
  40. package/dist/cli/commands/threat.d.ts +3 -0
  41. package/dist/cli/commands/threat.d.ts.map +1 -1
  42. package/dist/cli/commands/threat.js +31 -11
  43. package/dist/cli/commands/threat.js.map +1 -1
  44. package/dist/cli/commands/trap.d.ts +2 -2
  45. package/dist/cli/commands/trap.d.ts.map +1 -1
  46. package/dist/cli/commands/trap.js +32 -29
  47. package/dist/cli/commands/trap.js.map +1 -1
  48. package/dist/cli/commands/upgrade.d.ts +3 -4
  49. package/dist/cli/commands/upgrade.d.ts.map +1 -1
  50. package/dist/cli/commands/upgrade.js +10 -234
  51. package/dist/cli/commands/upgrade.js.map +1 -1
  52. package/dist/cli/commands/whoami.d.ts +2 -2
  53. package/dist/cli/commands/whoami.d.ts.map +1 -1
  54. package/dist/cli/commands/whoami.js +9 -60
  55. package/dist/cli/commands/whoami.js.map +1 -1
  56. package/dist/cli/index.js +4 -0
  57. package/dist/cli/index.js.map +1 -1
  58. package/dist/cli/interactive.d.ts +1 -1
  59. package/dist/cli/interactive.d.ts.map +1 -1
  60. package/dist/cli/interactive.js +504 -244
  61. package/dist/cli/interactive.js.map +1 -1
  62. package/dist/cli/menu.d.ts.map +1 -1
  63. package/dist/cli/menu.js +2 -5
  64. package/dist/cli/menu.js.map +1 -1
  65. package/dist/index.d.ts +0 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +2 -2
  68. package/dist/index.js.map +1 -1
  69. package/dist/init/wizard-runner.d.ts.map +1 -1
  70. package/dist/init/wizard-runner.js +136 -6
  71. package/dist/init/wizard-runner.js.map +1 -1
  72. package/package.json +24 -12
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Panguard AI - Interactive CLI Mode
3
3
  *
4
- * Number-key menu [0]-[7] with panguard > prompt for text commands.
4
+ * Number-key menu [0]-[8] with panguard > prompt for text commands.
5
5
  * Box-bordered status panel, breadcrumb navigation, no "press any key" interrupts.
6
6
  *
7
7
  * @module @panguard-ai/panguard/cli/interactive
@@ -79,11 +79,11 @@ const MENU_DEFS = [
79
79
  key: 'report',
80
80
  icon: '\u25A0',
81
81
  number: 2,
82
- en: 'Compliance Report',
83
- zh: '\u5408\u898F\u5831\u544A',
84
- enDesc: 'Generate ISO 27001, SOC 2, TW Cyber Security Act reports',
85
- zhDesc: '\u7522\u751F ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5\u5408\u898F\u5831\u544A',
86
- tierBadge: '[PRO]',
82
+ en: 'Compliance Report [Coming Soon]',
83
+ zh: '\u5408\u898F\u5831\u544A [\u5373\u5C07\u63A8\u51FA]',
84
+ enDesc: 'ISO 27001, SOC 2, TW Cyber Security Act — under development',
85
+ zhDesc: 'ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5 \u2014 \u958B\u767C\u4E2D',
86
+ tierBadge: '',
87
87
  featureKey: 'report',
88
88
  },
89
89
  {
@@ -101,11 +101,11 @@ const MENU_DEFS = [
101
101
  key: 'trap',
102
102
  icon: '\u00B7',
103
103
  number: 4,
104
- en: 'Honeypot System',
105
- zh: '\u871C\u7F50\u7CFB\u7D71',
106
- enDesc: 'Detect and profile attackers with decoy services',
107
- zhDesc: '\u5373\u770B\u5373\u8B58\u99ED\u5BA2\uFF0C\u5206\u6790\u653B\u64CA\u8005\u884C\u70BA',
108
- tierBadge: '[PRO]',
104
+ en: 'Honeypot System [Coming Soon]',
105
+ zh: '\u871C\u7F50\u7CFB\u7D71 [\u5373\u5C07\u63A8\u51FA]',
106
+ enDesc: 'Decoy services to detect attackers under development',
107
+ zhDesc: '\u8A98\u990C\u670D\u52D9\u5075\u6E2C\u653B\u64CA\u8005 \u2014 \u958B\u767C\u4E2D',
108
+ tierBadge: '',
109
109
  featureKey: 'trap',
110
110
  },
111
111
  {
@@ -116,7 +116,7 @@ const MENU_DEFS = [
116
116
  zh: '\u901A\u77E5\u7CFB\u7D71',
117
117
  enDesc: 'LINE, Telegram, Slack, Email, Webhook channels',
118
118
  zhDesc: 'LINE\u3001Telegram\u3001Slack\u3001Email\u3001Webhook \u901A\u77E5\u7BA1\u9053',
119
- tierBadge: '[STARTER]',
119
+ tierBadge: '',
120
120
  featureKey: 'notify',
121
121
  },
122
122
  {
@@ -127,7 +127,7 @@ const MENU_DEFS = [
127
127
  zh: '\u5A01\u8105\u60C5\u5831',
128
128
  enDesc: 'Threat intelligence REST API server',
129
129
  zhDesc: '\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u52D9',
130
- tierBadge: '[ENT]',
130
+ tierBadge: '',
131
131
  featureKey: 'threat-cloud',
132
132
  },
133
133
  {
@@ -141,8 +141,32 @@ const MENU_DEFS = [
141
141
  tierBadge: '',
142
142
  featureKey: 'demo',
143
143
  },
144
+ {
145
+ key: 'audit',
146
+ icon: '\u25A0',
147
+ number: 8,
148
+ en: 'Skill Auditor',
149
+ zh: '\u6280\u80FD\u5BE9\u8A08',
150
+ enDesc: 'Audit AI agent skills for security issues',
151
+ zhDesc: '\u5BE9\u8A08 AI \u4EE3\u7406\u6280\u80FD\u7684\u5B89\u5168\u554F\u984C',
152
+ tierBadge: '',
153
+ featureKey: 'audit',
154
+ },
144
155
  ];
145
156
  // ---------------------------------------------------------------------------
157
+ // MCP detection helper
158
+ // ---------------------------------------------------------------------------
159
+ async function isMCPConfigured() {
160
+ try {
161
+ const mcpConfig = await import('@panguard-ai/panguard-mcp/config');
162
+ const platforms = await mcpConfig.detectPlatforms();
163
+ return platforms.some((p) => p.detected && p.alreadyConfigured);
164
+ }
165
+ catch {
166
+ return false;
167
+ }
168
+ }
169
+ // ---------------------------------------------------------------------------
146
170
  // Guard process helpers
147
171
  // ---------------------------------------------------------------------------
148
172
  function isGuardRunning() {
@@ -223,20 +247,18 @@ function showHelp() {
223
247
  const menuTitle = currentLang === 'zh-TW' ? '\u4E3B\u9078\u55AE' : 'Main Menu';
224
248
  console.log(` ${c.sage(menuTitle)}`);
225
249
  console.log(c.dim(currentLang === 'zh-TW'
226
- ? ' [0]-[7] \u6309\u6578\u5B57\u9375\u5373\u523B\u9078\u64C7\uFF08\u4E0D\u9700\u6309 Enter\uFF09'
227
- : ' [0]-[7] Press number key to select instantly (no Enter needed)'));
250
+ ? ' [0]-[8] \u6309\u6578\u5B57\u9375\u5373\u523B\u9078\u64C7\uFF08\u4E0D\u9700\u6309 Enter\uFF09'
251
+ : ' [0]-[8] Press number key to select instantly (no Enter needed)'));
228
252
  console.log('');
229
253
  const promptTitle = currentLang === 'zh-TW' ? '\u6587\u5B57\u6307\u4EE4' : 'Text Commands';
230
254
  console.log(` ${c.sage(promptTitle)}`);
231
255
  const cmds = [
256
+ { cmd: 'setup', en: 'Connect AI agents via MCP', zh: '\u900F\u904E MCP \u9023\u63A5 AI \u4EE3\u7406' },
257
+ { cmd: 'audit', en: 'Audit AI agent skills', zh: '\u5BE9\u8A08 AI \u4EE3\u7406\u6280\u80FD' },
232
258
  { cmd: 'status', en: 'System status overview', zh: '\u7CFB\u7D71\u72C0\u614B\u7E3D\u89BD' },
233
- { cmd: 'login', en: 'Account login', zh: '\u5E33\u865F\u767B\u5165' },
234
- { cmd: 'logout', en: 'Account logout', zh: '\u767B\u51FA' },
235
259
  { cmd: 'config', en: 'Settings management', zh: '\u8A2D\u5B9A\u7BA1\u7406' },
236
- { cmd: 'upgrade', en: 'Upgrade plan', zh: '\u5347\u7D1A\u65B9\u6848' },
237
260
  { cmd: 'hardening', en: 'Security hardening', zh: '\u5B89\u5168\u52A0\u56FA' },
238
261
  { cmd: 'doctor', en: 'Health diagnostics', zh: '\u5065\u5EB7\u8A3A\u65B7' },
239
- { cmd: 'whoami', en: 'Current account info', zh: '\u986F\u793A\u7576\u524D\u5E33\u865F' },
240
262
  { cmd: 'help', en: 'Show this help', zh: '\u986F\u793A\u6B64\u8AAA\u660E' },
241
263
  ];
242
264
  for (const { cmd, en, zh } of cmds) {
@@ -283,12 +305,24 @@ export async function startInteractive(lang) {
283
305
  process.on('SIGINT', exit);
284
306
  process.on('SIGTERM', exit);
285
307
  renderStartup();
286
- // First-time user hint
308
+ // First-time user hint — detect MCP status and guide user
309
+ const mcpConfigured = await isMCPConfigured();
287
310
  if (!existsSync(CONFIG_PATH)) {
288
- const hint = currentLang === 'zh-TW'
289
- ? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\u6309 [0] \u57F7\u884C\u521D\u59CB\u8A2D\u5B9A\u6216\u6309 [7] \u529F\u80FD\u5C55\u793A`
290
- : ` ${c.sage('\u25C6')} First time? Press [0] for Setup Wizard or [7] for Auto Demo`;
291
- console.log(hint);
311
+ console.log(currentLang === 'zh-TW'
312
+ ? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\u6D41\u7A0B\uFF1A`
313
+ : ` ${c.sage('\u25C6')} First time? Recommended flow:`);
314
+ console.log(currentLang === 'zh-TW'
315
+ ? ` ${c.sage('[0]')} \u521D\u59CB\u8A2D\u5B9A \u2192 \u81EA\u52D5\u9023\u63A5 AI \u4EE3\u7406 + \u6280\u80FD\u5BE9\u8A08 + \u6383\u63CF`
316
+ : ` ${c.sage('[0]')} Setup Wizard \u2192 auto-connect AI agents + skill audit + scan`);
317
+ console.log(currentLang === 'zh-TW'
318
+ ? ` ${c.sage('[8]')} \u6280\u80FD\u5BE9\u8A08 \u2192 \u5BE9\u8A08\u5DF2\u5B89\u88DD AI \u6280\u80FD\u7684\u5B89\u5168\u554F\u984C`
319
+ : ` ${c.sage('[8]')} Skill Auditor \u2192 check installed AI skills for security issues`);
320
+ console.log('');
321
+ }
322
+ else if (!mcpConfigured) {
323
+ console.log(currentLang === 'zh-TW'
324
+ ? ` ${c.sage('\u25C6')} AI \u4EE3\u7406\u5C1A\u672A\u9023\u63A5\u3002\u57F7\u884C ${c.sage('panguard setup')} \u6216\u6309 ${c.sage('[0]')} \u9023\u63A5 Claude Code\u3001Cursor \u7B49\u5E73\u53F0\u3002`
325
+ : ` ${c.sage('\u25C6')} AI agents not connected. Run ${c.sage('panguard setup')} or press ${c.sage('[0]')} to connect Claude Code, Cursor, etc.`);
292
326
  console.log('');
293
327
  }
294
328
  // Main loop
@@ -362,16 +396,11 @@ async function dispatchCommand(text) {
362
396
  renderStartup();
363
397
  return true;
364
398
  case 'login':
365
- console.clear();
366
- await actionLogin();
367
- await new Promise((r) => setTimeout(r, 500));
368
- renderStartup();
369
- return true;
370
399
  case 'logout': {
371
400
  console.clear();
372
- const { logoutCommand } = await import('./commands/logout.js');
373
- const cmd = logoutCommand();
374
- await cmd.parseAsync(['logout'], { from: 'user' });
401
+ console.log('');
402
+ console.log(' Authentication removed. All features are free and open source.');
403
+ console.log('');
375
404
  await new Promise((r) => setTimeout(r, 500));
376
405
  renderStartup();
377
406
  return true;
@@ -382,12 +411,15 @@ async function dispatchCommand(text) {
382
411
  await new Promise((r) => setTimeout(r, 500));
383
412
  renderStartup();
384
413
  return true;
385
- case 'upgrade':
414
+ case 'upgrade': {
386
415
  console.clear();
387
- await actionUpgrade();
416
+ console.log('');
417
+ console.log(' All features are free and open source.');
418
+ console.log('');
388
419
  await new Promise((r) => setTimeout(r, 500));
389
420
  renderStartup();
390
421
  return true;
422
+ }
391
423
  case 'hardening':
392
424
  console.clear();
393
425
  await actionHardening();
@@ -408,9 +440,22 @@ async function dispatchCommand(text) {
408
440
  return true;
409
441
  case 'whoami': {
410
442
  console.clear();
411
- const { whoamiCommand } = await import('./commands/whoami.js');
412
- const cmd = whoamiCommand();
413
- await cmd.parseAsync(['whoami'], { from: 'user' });
443
+ console.log('');
444
+ console.log(' All features available (no login required).');
445
+ console.log('');
446
+ await new Promise((r) => setTimeout(r, 500));
447
+ renderStartup();
448
+ return true;
449
+ }
450
+ case 'audit':
451
+ console.clear();
452
+ await actionAudit();
453
+ await new Promise((r) => setTimeout(r, 500));
454
+ renderStartup();
455
+ return true;
456
+ case 'setup': {
457
+ console.clear();
458
+ await actionMCPSetup();
414
459
  await new Promise((r) => setTimeout(r, 500));
415
460
  renderStartup();
416
461
  return true;
@@ -451,6 +496,9 @@ async function dispatch(key) {
451
496
  case 'demo':
452
497
  await actionDemo();
453
498
  break;
499
+ case 'audit':
500
+ await actionAudit();
501
+ break;
454
502
  }
455
503
  }
456
504
  // ---------------------------------------------------------------------------
@@ -462,6 +510,110 @@ async function actionInit() {
462
510
  await runInitWizard(currentLang);
463
511
  }
464
512
  // ---------------------------------------------------------------------------
513
+ // Text command: setup (MCP-only, for users who already have config)
514
+ // ---------------------------------------------------------------------------
515
+ async function actionMCPSetup() {
516
+ breadcrumb(['Panguard', currentLang === 'zh-TW' ? 'MCP \u8A2D\u5B9A' : 'MCP Setup']);
517
+ const title = currentLang === 'zh-TW' ? 'AI \u4EE3\u7406\u9023\u63A5 (MCP)' : 'AI Agent Connection (MCP)';
518
+ console.log(` ${theme.brandBold(title)}`);
519
+ console.log('');
520
+ const { spinner: sp } = await import('@panguard-ai/core');
521
+ const detectSp = sp(currentLang === 'zh-TW'
522
+ ? '\u6B63\u5728\u5075\u6E2C AI \u4EE3\u7406\u5E73\u53F0...'
523
+ : 'Detecting AI agent platforms...');
524
+ try {
525
+ const mcpConfig = await import('@panguard-ai/panguard-mcp/config');
526
+ const platforms = await mcpConfig.detectPlatforms();
527
+ const detected = platforms.filter((p) => p.detected);
528
+ const unconfigured = detected.filter((p) => !p.alreadyConfigured);
529
+ if (detected.length === 0) {
530
+ detectSp.warn(currentLang === 'zh-TW'
531
+ ? '\u672A\u5075\u6E2C\u5230\u4EFB\u4F55 AI \u4EE3\u7406\u5E73\u53F0'
532
+ : 'No AI agent platforms detected');
533
+ console.log('');
534
+ console.log(c.dim(currentLang === 'zh-TW'
535
+ ? ' \u652F\u63F4\u5E73\u53F0: Claude Code, Cursor, OpenClaw, Codex, WorkBuddy, NemoClaw, Claude Desktop'
536
+ : ' Supported: Claude Code, Cursor, OpenClaw, Codex, WorkBuddy, NemoClaw, Claude Desktop'));
537
+ return;
538
+ }
539
+ detectSp.succeed(currentLang === 'zh-TW'
540
+ ? `\u5075\u6E2C\u5230 ${detected.length} \u500B\u5E73\u53F0`
541
+ : `Found ${detected.length} platform(s)`);
542
+ console.log('');
543
+ // Show all platforms
544
+ for (const p of platforms) {
545
+ const status = p.detected
546
+ ? p.alreadyConfigured
547
+ ? c.safe('\u2713 configured')
548
+ : c.caution('~ not configured')
549
+ : c.dim('- not found');
550
+ console.log(` ${p.detected ? c.bold(p.name) : c.dim(p.name)} ${status}`);
551
+ }
552
+ console.log('');
553
+ if (unconfigured.length === 0) {
554
+ console.log(c.safe(currentLang === 'zh-TW'
555
+ ? ` \u2713 \u6240\u6709\u5E73\u53F0\u5DF2\u8A2D\u5B9A\u5B8C\u6210\uFF01`
556
+ : ' \u2713 All platforms already configured!'));
557
+ console.log('');
558
+ console.log(c.dim(currentLang === 'zh-TW'
559
+ ? ' \u91CD\u555F AI \u4EE3\u7406\u5F8C\uFF0C\u8ACB\u6C42\u300Cpanguard_status\u300D\u5373\u53EF\u9A57\u8B49\u3002'
560
+ : ' Restart your AI agent, then ask "panguard_status" to verify.'));
561
+ return;
562
+ }
563
+ // Configure unconfigured platforms
564
+ const configSp = sp(currentLang === 'zh-TW'
565
+ ? `\u6B63\u5728\u8A2D\u5B9A ${unconfigured.length} \u500B\u5E73\u53F0...`
566
+ : `Configuring ${unconfigured.length} platform(s)...`);
567
+ let successCount = 0;
568
+ for (const p of unconfigured) {
569
+ const result = mcpConfig.injectMCPConfig(p.id);
570
+ if (result.success)
571
+ successCount++;
572
+ }
573
+ if (successCount === unconfigured.length) {
574
+ configSp.succeed(currentLang === 'zh-TW'
575
+ ? `${successCount} \u500B\u5E73\u53F0\u8A2D\u5B9A\u5B8C\u6210`
576
+ : `${successCount} platform(s) configured`);
577
+ }
578
+ else {
579
+ configSp.warn(currentLang === 'zh-TW'
580
+ ? `${successCount}/${unconfigured.length} \u5E73\u53F0\u8A2D\u5B9A\u6210\u529F`
581
+ : `${successCount}/${unconfigured.length} platform(s) configured`);
582
+ }
583
+ console.log('');
584
+ console.log(c.dim(currentLang === 'zh-TW'
585
+ ? ' \u91CD\u555F AI \u4EE3\u7406\u5F8C\u5373\u53EF\u4F7F\u7528 11 \u500B panguard_* MCP \u5DE5\u5177\uFF1A'
586
+ : ' Restart your AI agent to use 11 panguard_* MCP tools:'));
587
+ // Platform-specific restart instructions
588
+ const restartHints = {
589
+ 'claude-code': { en: 'Close and reopen your terminal', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F\u7D42\u7AEF\u6A5F' },
590
+ 'claude-desktop': { en: 'Quit and reopen Claude Desktop', zh: '\u9000\u51FA\u4E26\u91CD\u65B0\u958B\u555F Claude Desktop' },
591
+ cursor: { en: 'Cmd+Shift+P (or Ctrl+Shift+P) > "Reload Window"', zh: 'Cmd+Shift+P > "Reload Window"' },
592
+ openclaw: { en: 'Close and reopen OpenClaw', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F OpenClaw' },
593
+ codex: { en: 'Restart the Codex CLI session', zh: '\u91CD\u65B0\u555F\u52D5 Codex CLI' },
594
+ workbuddy: { en: 'Close and reopen WorkBuddy', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F WorkBuddy' },
595
+ nemoclaw: { en: 'Close and reopen NemoClaw', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F NemoClaw' },
596
+ };
597
+ for (const p of unconfigured) {
598
+ const hint = restartHints[p.id];
599
+ const text = hint
600
+ ? (currentLang === 'zh-TW' ? hint.zh : hint.en)
601
+ : (currentLang === 'zh-TW' ? '\u91CD\u65B0\u555F\u52D5\u61C9\u7528\u7A0B\u5F0F' : 'Restart the application');
602
+ console.log(c.dim(` ${p.name}: ${text}`));
603
+ }
604
+ console.log('');
605
+ console.log(c.dim(currentLang === 'zh-TW'
606
+ ? ' \u8A66\u8A66\u554F AI: \u300C\u5BE9\u8A08\u9019\u500B\u5C08\u6848\u7684\u6280\u80FD\u300D\u6216\u300C\u6383\u63CF\u6211\u7684\u7CFB\u7D71\u300D'
607
+ : ' Try asking your AI: "audit the skills in this project" or "scan my system"'));
608
+ }
609
+ catch (err) {
610
+ detectSp.fail(currentLang === 'zh-TW'
611
+ ? 'MCP \u8A2D\u5B9A\u5931\u6557'
612
+ : 'MCP setup failed');
613
+ console.log(formatError(err instanceof Error ? err.message : String(err), 'MCP Setup', currentLang === 'zh-TW' ? '\u8ACB\u91CD\u8A66' : 'Please retry'));
614
+ }
615
+ }
616
+ // ---------------------------------------------------------------------------
465
617
  // [1] Security Scan
466
618
  // ---------------------------------------------------------------------------
467
619
  async function actionScan() {
@@ -515,27 +667,36 @@ async function actionScan() {
515
667
  for (const f of result.findings) {
516
668
  const sev = colorSeverity(f.severity).padEnd(10);
517
669
  console.log(` ${sev} ${f.title}`);
518
- if (tier === 'community' && f.manualFix && f.manualFix.length > 0) {
519
- const fixLabel = currentLang === 'zh-TW' ? '\u624B\u52D5\u4FEE\u5FA9:' : 'Manual fix:';
670
+ // Show description if available
671
+ if (f.description) {
672
+ console.log(c.dim(` ${f.description}`));
673
+ }
674
+ if (f.manualFix && f.manualFix.length > 0) {
675
+ const fixLabel = currentLang === 'zh-TW' ? '\u4FEE\u5FA9:' : 'Fix:';
520
676
  console.log(c.dim(` ${fixLabel}`));
521
677
  for (const cmd of f.manualFix) {
522
678
  console.log(c.dim(` $ ${cmd}`));
523
679
  }
524
680
  fixableCount++;
525
681
  }
682
+ else if (f.remediation) {
683
+ // Show recommendation even if no auto-fix command
684
+ const recLabel = currentLang === 'zh-TW' ? '\u5EFA\u8B70:' : 'Recommendation:';
685
+ console.log(c.dim(` ${recLabel} ${f.remediation}`));
686
+ }
526
687
  }
527
688
  console.log('');
528
689
  const issuesText = currentLang === 'zh-TW'
529
690
  ? `${result.findings.length} \u500B\u554F\u984C`
530
691
  : `${result.findings.length} issue(s) found`;
531
692
  console.log(c.dim(` ${issuesText}`));
532
- if (tier === 'community' && fixableCount > 0) {
693
+ if (fixableCount > 0) {
533
694
  const upgradeLines = currentLang === 'zh-TW'
534
695
  ? [
535
- `\u5347\u7D1A\u5230 Solo ($9/\u6708) \u5373\u53EF\u4E00\u9375\u4FEE\u5FA9:`,
696
+ `\u53EF\u81EA\u52D5\u4FEE\u5FA9:`,
536
697
  `$ panguard scan --fix`,
537
698
  ]
538
- : [`Upgrade to Solo ($9/mo) to auto-fix all:`, `$ panguard scan --fix`];
699
+ : [`Auto-fix available:`, `$ panguard scan --fix`];
539
700
  console.log('');
540
701
  console.log(box(upgradeLines.join('\n'), { borderColor: c.sage }));
541
702
  }
@@ -546,17 +707,43 @@ async function actionScan() {
546
707
  : 'No security issues found';
547
708
  console.log(` ${c.safe(noIssues)}`);
548
709
  }
549
- nextSteps(currentLang === 'zh-TW'
550
- ? [
551
- { cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
552
- { cmd: 'scan --full', desc: '\u57F7\u884C\u5B8C\u6574\u6383\u63CF' },
553
- { cmd: 'report', desc: '\u7522\u751F\u5408\u898F\u5831\u544A' },
554
- ]
555
- : [
556
- { cmd: 'guard start', desc: 'Enable real-time protection' },
557
- { cmd: 'scan --full', desc: 'Run a comprehensive scan' },
558
- { cmd: 'report', desc: 'Generate compliance report' },
559
- ], currentLang);
710
+ // Agent auto-suggestion: offer to activate Guard if not running
711
+ const guardRunning = isGuardRunning().running;
712
+ if (!guardRunning) {
713
+ console.log('');
714
+ const agentMsg = currentLang === 'zh-TW'
715
+ ? `${c.sage('\u25C6')} \u5373\u6642\u9632\u8B77\u5C1A\u672A\u555F\u52D5\u3002\u8981\u73FE\u5728\u555F\u7528\u55CE\uFF1F`
716
+ : `${c.sage('\u25C6')} Real-time protection is not active. Enable now?`;
717
+ console.log(` ${agentMsg}`);
718
+ const guardItems = [
719
+ { key: '1', label: currentLang === 'zh-TW' ? '\u662F\uFF0C\u555F\u52D5 Guard \u9632\u8B77' : 'Yes, start Guard protection' },
720
+ { key: '2', label: currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105' : 'Audit installed skills for threats' },
721
+ { key: '3', label: currentLang === 'zh-TW' ? '\u4E0D\u7528\uFF0C\u56DE\u4E3B\u9078\u55AE' : 'No, return to main menu' },
722
+ ];
723
+ renderCompactMenu('', guardItems);
724
+ const guardChoice = await waitForCompactChoice(guardItems, currentLang);
725
+ if (guardChoice?.key === '1') {
726
+ await actionGuard();
727
+ return;
728
+ }
729
+ if (guardChoice?.key === '2') {
730
+ await actionAudit();
731
+ return;
732
+ }
733
+ }
734
+ else {
735
+ nextSteps(currentLang === 'zh-TW'
736
+ ? [
737
+ { cmd: '[8] \u6280\u80FD\u5BE9\u8A08', desc: '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105' },
738
+ { cmd: 'scan --full', desc: '\u57F7\u884C\u5B8C\u6574\u6383\u63CF' },
739
+ { cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
740
+ ]
741
+ : [
742
+ { cmd: '[8] Skill Auditor', desc: 'Audit installed skills for threats' },
743
+ { cmd: 'scan --full', desc: 'Run a comprehensive scan' },
744
+ { cmd: 'guard start', desc: 'Enable real-time protection' },
745
+ ], currentLang);
746
+ }
560
747
  }
561
748
  // ---------------------------------------------------------------------------
562
749
  // [2] Compliance Report
@@ -566,54 +753,21 @@ async function actionReport() {
566
753
  'Panguard',
567
754
  currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report',
568
755
  ]);
569
- const title = currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report';
570
- console.log(` ${theme.brandBold(title)}`);
571
756
  console.log('');
572
- const frameworkItems = [
573
- { key: '1', label: 'ISO 27001' },
574
- { key: '2', label: 'SOC 2' },
575
- {
576
- key: '3',
577
- label: currentLang === 'zh-TW'
578
- ? '\u8CC7\u901A\u5B89\u5168\u7BA1\u7406\u6CD5'
579
- : 'TW Cyber Security Act',
580
- },
581
- ];
582
- renderCompactMenu(currentLang === 'zh-TW' ? '\u9078\u64C7\u5408\u898F\u6846\u67B6' : 'Select Framework', frameworkItems);
583
- const fwChoice = await waitForCompactChoice(frameworkItems, currentLang);
584
- if (!fwChoice)
585
- return;
586
- const frameworkMap = {
587
- '1': 'iso27001',
588
- '2': 'soc2',
589
- '3': 'tw_cyber_security_act',
590
- };
591
- const framework = frameworkMap[fwChoice.key] ?? 'iso27001';
592
- const langItems = [
593
- {
594
- key: '1',
595
- label: currentLang === 'zh-TW' ? '\u7E41\u9AD4\u4E2D\u6587 (zh-TW)' : 'Chinese (zh-TW)',
596
- },
597
- { key: '2', label: currentLang === 'zh-TW' ? '\u82F1\u6587 (en)' : 'English (en)' },
598
- ];
599
- console.log('');
600
- renderCompactMenu(currentLang === 'zh-TW' ? '\u5831\u544A\u8A9E\u8A00' : 'Report Language', langItems);
601
- const langChoice = await waitForCompactChoice(langItems, currentLang);
602
- if (!langChoice)
603
- return;
604
- const reportLang = langChoice.key === '1' ? 'zh-TW' : 'en';
757
+ if (currentLang === 'zh-TW') {
758
+ console.log(' \u5408\u898F\u5831\u544A \u2014 \u5373\u5C07\u63A8\u51FA');
759
+ console.log('');
760
+ console.log(' ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5\u5408\u898F\u5831\u544A\u529F\u80FD\u958B\u767C\u4E2D\u3002');
761
+ console.log(' \u8FFD\u8E64\u9032\u5EA6: https://github.com/panguard-ai/panguard-ai');
762
+ }
763
+ else {
764
+ console.log(' Compliance Report \u2014 Coming Soon');
765
+ console.log('');
766
+ console.log(' ISO 27001, SOC 2, and TW Cyber Security Act compliance');
767
+ console.log(' reports are under active development.');
768
+ console.log(' Follow progress: https://github.com/panguard-ai/panguard-ai');
769
+ }
605
770
  console.log('');
606
- const { executeCli } = await import('@panguard-ai/panguard-report');
607
- await executeCli(['generate', '--framework', framework, '--language', reportLang]);
608
- nextSteps(currentLang === 'zh-TW'
609
- ? [
610
- { cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
611
- { cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
612
- ]
613
- : [
614
- { cmd: 'scan', desc: 'Run a security scan' },
615
- { cmd: 'guard start', desc: 'Enable real-time protection' },
616
- ], currentLang);
617
771
  }
618
772
  // ---------------------------------------------------------------------------
619
773
  // [3] Guard Engine
@@ -657,27 +811,24 @@ async function actionGuard() {
657
811
  ], currentLang);
658
812
  break;
659
813
  }
660
- // Free tier — show positive capabilities first
661
- const { tier } = getLicense();
662
- if (tier === 'community') {
663
- console.log(` ${c.sage('\u25C6')} Guard active${' '.repeat(30)}Layer 1 \u00B7 Community`);
664
- console.log('');
665
- if (currentLang === 'zh-TW') {
666
- console.log(` ${c.safe('\u2713')} \u5DF2\u77E5\u653B\u64CA\u6A21\u5F0F\u81EA\u52D5\u5C01\u9396`);
667
- console.log(` ${c.safe('\u2713')} Threat Cloud \u5A01\u8105\u60C5\u5831`);
668
- console.log(` ${c.dim('\u2500')} AI \u5206\u6790 ${c.dim('(Solo $9/\u6708)')}`);
669
- console.log(` ${c.dim('\u2500')} \u901A\u77E5\u7CFB\u7D71 ${c.dim('(Solo $9/\u6708)')}`);
670
- console.log(` ${c.dim('\u2500')} \u65E5\u8A8C\u4FDD\u7559 7 \u5929+ ${c.dim('(Solo $9/\u6708)')}`);
671
- }
672
- else {
673
- console.log(` ${c.safe('\u2713')} Auto-blocking for known attack patterns`);
674
- console.log(` ${c.safe('\u2713')} Threat Cloud intelligence`);
675
- console.log(` ${c.dim('\u2500')} AI analysis ${c.dim('(Solo $9/mo)')}`);
676
- console.log(` ${c.dim('\u2500')} Notifications ${c.dim('(Solo $9/mo)')}`);
677
- console.log(` ${c.dim('\u2500')} Log retention 7d+ ${c.dim('(Solo $9/mo)')}`);
678
- }
679
- console.log('');
814
+ // Show Guard capabilities
815
+ console.log(` ${c.sage('\u25C6')} Guard active${' '.repeat(30)}All Layers \u00B7 Free`);
816
+ console.log('');
817
+ if (currentLang === 'zh-TW') {
818
+ console.log(` ${c.safe('\u2713')} \u5DF2\u77E5\u653B\u64CA\u6A21\u5F0F\u81EA\u52D5\u5C01\u9396`);
819
+ console.log(` ${c.safe('\u2713')} Threat Cloud \u5A01\u8105\u60C5\u5831`);
820
+ console.log(` ${c.safe('\u2713')} AI \u5206\u6790`);
821
+ console.log(` ${c.safe('\u2713')} \u901A\u77E5\u7CFB\u7D71`);
822
+ console.log(` ${c.safe('\u2713')} \u65E5\u8A8C\u4FDD\u7559`);
680
823
  }
824
+ else {
825
+ console.log(` ${c.safe('\u2713')} Auto-blocking for known attack patterns`);
826
+ console.log(` ${c.safe('\u2713')} Threat Cloud intelligence`);
827
+ console.log(` ${c.safe('\u2713')} AI analysis`);
828
+ console.log(` ${c.safe('\u2713')} Notifications`);
829
+ console.log(` ${c.safe('\u2713')} Log retention`);
830
+ }
831
+ console.log('');
681
832
  // Spawn guard as background process
682
833
  const { spawn: spawnProcess } = await import('node:child_process');
683
834
  const { fileURLToPath: toPath } = await import('node:url');
@@ -783,46 +934,21 @@ async function actionTrap() {
783
934
  'Panguard',
784
935
  currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System',
785
936
  ]);
786
- const title = currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System';
787
- console.log(` ${theme.brandBold(title)}`);
788
937
  console.log('');
789
- const items = [
790
- {
791
- key: '1',
792
- label: currentLang === 'zh-TW' ? '\u57FA\u672C\u914D\u7F6E (SSH + HTTP)' : 'Config (SSH + HTTP)',
793
- },
794
- {
795
- key: '2',
796
- label: currentLang === 'zh-TW'
797
- ? '\u5B8C\u6574\u914D\u7F6E (8 \u7A2E\u670D\u52D9)'
798
- : 'Config (All 8 Services)',
799
- },
800
- { key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B' : 'Status' },
801
- {
802
- key: '4',
803
- label: currentLang === 'zh-TW' ? '\u653B\u64CA\u8005\u5206\u6790' : 'Attacker Profiles',
804
- },
805
- ];
806
- renderCompactMenu(currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System', items);
807
- const choice = await waitForCompactChoice(items, currentLang);
808
- if (!choice)
809
- return;
810
- console.log('');
811
- const { executeCli } = await import('@panguard-ai/panguard-trap');
812
- switch (choice.key) {
813
- case '1':
814
- await executeCli(['config', '--services', 'ssh,http']);
815
- break;
816
- case '2':
817
- await executeCli(['config', '--services', 'ssh,http,ftp,telnet,mysql,redis,smb,rdp']);
818
- break;
819
- case '3':
820
- await executeCli(['status']);
821
- break;
822
- case '4':
823
- await executeCli(['profiles']);
824
- break;
938
+ if (currentLang === 'zh-TW') {
939
+ console.log(' \u871C\u7F50\u7CFB\u7D71 \u2014 \u5373\u5C07\u63A8\u51FA');
940
+ console.log('');
941
+ console.log(' \u8A98\u990C\u670D\u52D9\uFF08SSH\u3001HTTP\u3001FTP\uFF09\u5075\u6E2C\u8207\u5206\u6790\u653B\u64CA\u8005\u529F\u80FD\u958B\u767C\u4E2D\u3002');
942
+ console.log(' \u8FFD\u8E64\u9032\u5EA6: https://github.com/panguard-ai/panguard-ai');
825
943
  }
944
+ else {
945
+ console.log(' Honeypot System \u2014 Coming Soon');
946
+ console.log('');
947
+ console.log(' Decoy services (SSH, HTTP, FTP) to detect and profile');
948
+ console.log(' attackers are under active development.');
949
+ console.log(' Follow progress: https://github.com/panguard-ai/panguard-ai');
950
+ }
951
+ console.log('');
826
952
  }
827
953
  // ---------------------------------------------------------------------------
828
954
  // [5] Notifications
@@ -885,7 +1011,17 @@ async function actionThreat() {
885
1011
  ? ` \u5373\u5C07\u555F\u52D5\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u5668 (port ${port})`
886
1012
  : ` Starting Threat Cloud REST API server (port ${port})...`));
887
1013
  console.log('');
888
- const { ThreatCloudServer } = await import('@panguard-ai/threat-cloud');
1014
+ let ThreatCloudServer;
1015
+ try {
1016
+ const mod = '@panguard-ai/threat-cloud';
1017
+ const tc = await import(/* webpackIgnore: true */ mod);
1018
+ ThreatCloudServer = tc.ThreatCloudServer;
1019
+ }
1020
+ catch {
1021
+ console.log(c.red(' Threat Cloud server package is not available.'));
1022
+ console.log(c.dim(' Your Guard client connects to Threat Cloud automatically.'));
1023
+ return;
1024
+ }
889
1025
  const sp = spinner(currentLang === 'zh-TW'
890
1026
  ? '\u6B63\u5728\u555F\u52D5 Threat Cloud API...'
891
1027
  : 'Starting Threat Cloud API server...');
@@ -922,7 +1058,6 @@ async function actionThreat() {
922
1058
  async function actionDemo() {
923
1059
  breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo']);
924
1060
  const { runScan } = await import('@panguard-ai/panguard-scan');
925
- const { generateComplianceReport, generateSummaryText } = await import('@panguard-ai/panguard-report');
926
1061
  const title = currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo';
927
1062
  console.log(` ${theme.brandBold(title)}`);
928
1063
  console.log(c.dim(currentLang === 'zh-TW'
@@ -963,59 +1098,14 @@ async function actionDemo() {
963
1098
  });
964
1099
  }
965
1100
  console.log('');
966
- // 2. Compliance Report
1101
+ // 2. Compliance Report (Coming Soon)
967
1102
  console.log(c.dim(currentLang === 'zh-TW'
968
- ? ' 2. \u5408\u898F\u5831\u544A (ISO 27001)'
969
- : ' 2. Compliance Report (ISO 27001)'));
970
- console.log('');
971
- const reportSp = spinner(currentLang === 'zh-TW'
972
- ? '\u6B63\u5728\u7522\u751F ISO 27001 \u5831\u544A...'
973
- : 'Generating ISO 27001 report...');
974
- try {
975
- const findings = [
976
- {
977
- findingId: 'DEMO-001',
978
- severity: 'high',
979
- title: 'Missing information security policy',
980
- description: 'No formal information security policy document found.',
981
- category: 'policy',
982
- timestamp: new Date(),
983
- source: 'panguard-scan',
984
- },
985
- {
986
- findingId: 'DEMO-002',
987
- severity: 'medium',
988
- title: 'Unencrypted data at rest',
989
- description: 'Database storage does not use encryption at rest.',
990
- category: 'encryption',
991
- timestamp: new Date(),
992
- source: 'panguard-scan',
993
- },
994
- ];
995
- const report = generateComplianceReport(findings, 'iso27001', 'en', {
996
- includeRecommendations: true,
997
- });
998
- const summary = generateSummaryText(report);
999
- reportSp.succeed(currentLang === 'zh-TW'
1000
- ? 'ISO 27001 \u5831\u544A\u5DF2\u7522\u751F'
1001
- : 'ISO 27001 report generated');
1002
- const summaryLines = summary.split('\n').slice(0, 10);
1003
- for (const line of summaryLines) {
1004
- console.log(` ${c.dim(line)}`);
1005
- }
1006
- console.log(c.dim(' ...'));
1007
- results.push({
1008
- name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
1009
- status: 'ok',
1010
- });
1011
- }
1012
- catch (err) {
1013
- reportSp.fail(`${err instanceof Error ? err.message : err}`);
1014
- results.push({
1015
- name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
1016
- status: 'fail',
1017
- });
1018
- }
1103
+ ? ' 2. \u5408\u898F\u5831\u544A \u2014 \u5373\u5C07\u63A8\u51FA'
1104
+ : ' 2. Compliance Report \u2014 Coming Soon'));
1105
+ results.push({
1106
+ name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
1107
+ status: 'warn',
1108
+ });
1019
1109
  console.log('');
1020
1110
  // 3. Guard Engine
1021
1111
  console.log(c.dim(currentLang === 'zh-TW' ? ' 3. \u5B88\u8B77\u5F15\u64CE' : ' 3. Guard Engine'));
@@ -1038,24 +1128,14 @@ async function actionDemo() {
1038
1128
  });
1039
1129
  }
1040
1130
  console.log('');
1041
- // 4. Honeypot
1042
- console.log(c.dim(currentLang === 'zh-TW' ? ' 4. \u871C\u7F50\u7CFB\u7D71' : ' 4. Honeypot System'));
1043
- console.log('');
1044
- try {
1045
- const { executeCli: trapCLI } = await import('@panguard-ai/panguard-trap');
1046
- await trapCLI(['config', '--services', 'ssh,http']);
1047
- results.push({
1048
- name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
1049
- status: 'ok',
1050
- });
1051
- }
1052
- catch (err) {
1053
- console.log(` ${c.critical(err instanceof Error ? err.message : String(err))}`);
1054
- results.push({
1055
- name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
1056
- status: 'fail',
1057
- });
1058
- }
1131
+ // 4. Honeypot (Coming Soon)
1132
+ console.log(c.dim(currentLang === 'zh-TW'
1133
+ ? ' 4. \u871C\u7F50\u7CFB\u7D71 \u2014 \u5373\u5C07\u63A8\u51FA'
1134
+ : ' 4. Honeypot System \u2014 Coming Soon'));
1135
+ results.push({
1136
+ name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
1137
+ status: 'warn',
1138
+ });
1059
1139
  console.log('');
1060
1140
  // 5. Notifications
1061
1141
  console.log(c.dim(currentLang === 'zh-TW' ? ' 5. \u901A\u77E5\u7CFB\u7D71' : ' 5. Notification System'));
@@ -1102,13 +1182,202 @@ async function actionDemo() {
1102
1182
  ], currentLang);
1103
1183
  }
1104
1184
  // ---------------------------------------------------------------------------
1105
- // Prompt commands: Upgrade
1185
+ // [8] Skill Auditor
1106
1186
  // ---------------------------------------------------------------------------
1107
- async function actionUpgrade() {
1108
- breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5347\u7D1A\u65B9\u6848' : 'Upgrade Plan']);
1109
- const { upgradeCommand } = await import('./commands/upgrade.js');
1110
- const cmd = upgradeCommand();
1111
- await cmd.parseAsync(['upgrade', '--lang', currentLang], { from: 'user' });
1187
+ async function actionAudit() {
1188
+ breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor']);
1189
+ const title = currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor';
1190
+ console.log(` ${theme.brandBold(title)}`);
1191
+ console.log('');
1192
+ console.log(c.dim(currentLang === 'zh-TW'
1193
+ ? ' \u6383\u63CF AI \u4EE3\u7406\u6280\u80FD\u76EE\u9304\uFF08\u5305\u542B SKILL.md\uFF09\u4EE5\u5075\u6E2C\u5B89\u5168\u554F\u984C\u3002'
1194
+ : ' Scan an AI agent skill directory (containing SKILL.md) for security issues.'));
1195
+ console.log('');
1196
+ // Ask for path to audit
1197
+ const pathItems = [
1198
+ {
1199
+ key: '1',
1200
+ label: currentLang === 'zh-TW'
1201
+ ? '\u5BE9\u8A08\u7576\u524D\u76EE\u9304 (.)'
1202
+ : 'Audit current directory (.)',
1203
+ },
1204
+ {
1205
+ key: '2',
1206
+ label: currentLang === 'zh-TW'
1207
+ ? '\u8F38\u5165\u81EA\u5B9A\u8DEF\u5F91'
1208
+ : 'Enter custom path',
1209
+ },
1210
+ ];
1211
+ renderCompactMenu(currentLang === 'zh-TW' ? '\u9078\u64C7\u76EE\u6A19' : 'Select target', pathItems);
1212
+ const choice = await waitForCompactChoice(pathItems, currentLang);
1213
+ if (!choice)
1214
+ return;
1215
+ let targetPath = process.cwd();
1216
+ if (choice.key === '2') {
1217
+ // Read a custom path from the user via text prompt
1218
+ const readline = await import('node:readline');
1219
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1220
+ targetPath = await new Promise((resolve) => {
1221
+ const prompt = currentLang === 'zh-TW' ? ' \u8DEF\u5F91: ' : ' Path: ';
1222
+ rl.question(prompt, (answer) => {
1223
+ rl.close();
1224
+ resolve(answer.trim() || process.cwd());
1225
+ });
1226
+ });
1227
+ }
1228
+ const path = await import('node:path');
1229
+ const resolvedPath = path.resolve(targetPath);
1230
+ console.log('');
1231
+ const sp = spinner(currentLang === 'zh-TW'
1232
+ ? `\u6B63\u5728\u5BE9\u8A08 ${resolvedPath}...`
1233
+ : `Auditing ${resolvedPath}...`);
1234
+ try {
1235
+ const { auditSkill } = await import('@panguard-ai/panguard-skill-auditor');
1236
+ const report = await auditSkill(resolvedPath);
1237
+ sp.succeed(currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5B8C\u6210' : 'Audit complete');
1238
+ // Display results
1239
+ const levelColors = {
1240
+ LOW: c.safe,
1241
+ MEDIUM: c.caution,
1242
+ HIGH: c.critical,
1243
+ CRITICAL: (s) => c.bold(c.critical(s)),
1244
+ };
1245
+ const colorFn = levelColors[report.riskLevel] ?? c.dim;
1246
+ console.log('');
1247
+ console.log(box([
1248
+ `${c.bold(currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08\u5831\u544A' : 'Skill Audit Report')}`,
1249
+ '',
1250
+ `${currentLang === 'zh-TW' ? '\u6280\u80FD' : 'Skill'}: ${report.manifest?.name ?? 'Unknown'}${report.manifest?.metadata?.version ? ` v${report.manifest.metadata.version}` : ''}`,
1251
+ `${currentLang === 'zh-TW' ? '\u4F5C\u8005' : 'Author'}: ${report.manifest?.metadata?.author ?? 'Unknown'}`,
1252
+ `${currentLang === 'zh-TW' ? '\u98A8\u96AA\u5206\u6578' : 'Risk Score'}: ${colorFn(`${report.riskScore}/100 (${report.riskLevel})`)}`,
1253
+ `${currentLang === 'zh-TW' ? '\u6642\u9593' : 'Duration'}: ${report.durationMs}ms`,
1254
+ ].join('\n'), { borderColor: c.sage }));
1255
+ console.log('');
1256
+ for (const check of report.checks) {
1257
+ const icon = check.status === 'pass'
1258
+ ? c.safe('\u2713')
1259
+ : check.status === 'fail'
1260
+ ? c.critical('\u2717')
1261
+ : check.status === 'warn'
1262
+ ? c.caution('~')
1263
+ : c.dim('i');
1264
+ const statusLabel = check.status === 'pass'
1265
+ ? 'PASS'
1266
+ : check.status === 'fail'
1267
+ ? 'FAIL'
1268
+ : check.status === 'warn'
1269
+ ? 'WARN'
1270
+ : 'INFO';
1271
+ console.log(` ${icon} [${statusLabel}] ${check.label}`);
1272
+ }
1273
+ console.log('');
1274
+ if (report.findings.length > 0) {
1275
+ console.log(c.bold(currentLang === 'zh-TW'
1276
+ ? ` \u767C\u73FE ${report.findings.length} \u500B\u554F\u984C:`
1277
+ : ` ${report.findings.length} finding(s):`));
1278
+ console.log('');
1279
+ for (const finding of report.findings) {
1280
+ const sevColor = finding.severity === 'critical'
1281
+ ? c.critical
1282
+ : finding.severity === 'high'
1283
+ ? c.caution
1284
+ : c.dim;
1285
+ console.log(` ${sevColor(`[${finding.severity.toUpperCase()}]`)} ${finding.title}`);
1286
+ console.log(` ${c.dim(finding.description)}`);
1287
+ if (finding.location)
1288
+ console.log(` ${c.dim(`at ${finding.location}`)}`);
1289
+ console.log('');
1290
+ }
1291
+ }
1292
+ nextSteps(currentLang === 'zh-TW'
1293
+ ? [
1294
+ { cmd: 'scan', desc: '\u57F7\u884C\u7CFB\u7D71\u5B89\u5168\u6383\u63CF' },
1295
+ { cmd: 'guard start', desc: '\u555F\u52D5 24/7 \u5B88\u8B77\u9632\u8B77' },
1296
+ ]
1297
+ : [
1298
+ { cmd: 'scan', desc: 'Run a system security scan' },
1299
+ { cmd: 'guard start', desc: 'Start 24/7 guard protection' },
1300
+ ], currentLang);
1301
+ }
1302
+ catch (err) {
1303
+ const errMsg = err instanceof Error ? err.message : String(err);
1304
+ const isNoSkill = errMsg.toLowerCase().includes('skill.md') || errMsg.toLowerCase().includes('not found') || errMsg.toLowerCase().includes('enoent');
1305
+ if (isNoSkill) {
1306
+ sp.warn(currentLang === 'zh-TW'
1307
+ ? '\u6B64\u76EE\u9304\u672A\u627E\u5230 AI \u6280\u80FD (SKILL.md)'
1308
+ : 'No AI skills (SKILL.md) found in this directory');
1309
+ console.log('');
1310
+ console.log(c.dim(currentLang === 'zh-TW'
1311
+ ? ' Skill Auditor \u6383\u63CF\u5305\u542B SKILL.md \u7684 AI \u6280\u80FD\u76EE\u9304\u3002'
1312
+ : ' Skill Auditor scans AI skill directories containing a SKILL.md file.'));
1313
+ console.log('');
1314
+ console.log(c.dim(currentLang === 'zh-TW'
1315
+ ? ' \u5E38\u898B\u6280\u80FD\u4F4D\u7F6E\uFF1A'
1316
+ : ' Common skill locations:'));
1317
+ console.log(c.dim(' ~/.claude/skills/'));
1318
+ console.log(c.dim(' ./.claude/skills/'));
1319
+ console.log(c.dim(' ./.mcp/'));
1320
+ console.log(c.dim(' ./skills/'));
1321
+ console.log('');
1322
+ // Auto-scan known skill directories
1323
+ const fs = await import('node:fs');
1324
+ const path = await import('node:path');
1325
+ const skillDirs = [
1326
+ path.join(homedir(), '.claude', 'skills'),
1327
+ path.join(process.cwd(), '.claude', 'skills'),
1328
+ path.join(process.cwd(), '.mcp'),
1329
+ path.join(process.cwd(), 'skills'),
1330
+ ];
1331
+ const foundDirs = [];
1332
+ for (const dir of skillDirs) {
1333
+ if (fs.existsSync(dir)) {
1334
+ // Check for subdirectories that might have SKILL.md
1335
+ try {
1336
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
1337
+ for (const entry of entries) {
1338
+ if (entry.isDirectory()) {
1339
+ const skillMdPath = path.join(dir, entry.name, 'SKILL.md');
1340
+ if (fs.existsSync(skillMdPath)) {
1341
+ foundDirs.push(path.join(dir, entry.name));
1342
+ }
1343
+ }
1344
+ }
1345
+ // Also check the directory itself
1346
+ if (fs.existsSync(path.join(dir, 'SKILL.md'))) {
1347
+ foundDirs.push(dir);
1348
+ }
1349
+ }
1350
+ catch {
1351
+ /* skip inaccessible dirs */
1352
+ }
1353
+ }
1354
+ }
1355
+ if (foundDirs.length > 0) {
1356
+ console.log(c.sage(currentLang === 'zh-TW'
1357
+ ? ` \u5728\u7CFB\u7D71\u4E2D\u627E\u5230 ${foundDirs.length} \u500B\u6280\u80FD\uFF1A`
1358
+ : ` Found ${foundDirs.length} skill(s) on your system:`));
1359
+ for (const dir of foundDirs) {
1360
+ console.log(c.dim(` ${dir}`));
1361
+ }
1362
+ console.log('');
1363
+ console.log(c.dim(currentLang === 'zh-TW'
1364
+ ? ` \u57F7\u884C\uFF1Apanguard audit skill <path> \u4F86\u5BE9\u8A08\u7279\u5B9A\u6280\u80FD`
1365
+ : ` Run: panguard audit skill <path> to audit a specific skill`));
1366
+ }
1367
+ else {
1368
+ console.log(c.dim(currentLang === 'zh-TW'
1369
+ ? ' \u7CFB\u7D71\u4E2D\u672A\u627E\u5230\u5DF2\u5B89\u88DD\u7684 AI \u6280\u80FD\u3002'
1370
+ : ' No installed AI skills found on your system.'));
1371
+ console.log(c.dim(currentLang === 'zh-TW'
1372
+ ? ' \u5728\u5305\u542B AI \u6280\u80FD\u7684\u5C08\u6848\u76EE\u9304\u4E2D\u57F7\u884C\u6B64\u6307\u4EE4\u3002'
1373
+ : ' Run this command inside a project directory that contains AI skills.'));
1374
+ }
1375
+ }
1376
+ else {
1377
+ sp.fail(currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5931\u6557' : 'Audit failed');
1378
+ console.log(formatError(errMsg, currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor', currentLang === 'zh-TW' ? '\u8ACB\u91CD\u8A66\u6216\u6AA2\u67E5\u65E5\u8A8C' : 'Please retry or check logs'));
1379
+ }
1380
+ }
1112
1381
  }
1113
1382
  // ---------------------------------------------------------------------------
1114
1383
  // Prompt commands: Hardening
@@ -1152,15 +1421,6 @@ async function actionStatus() {
1152
1421
  await cmd.parseAsync(['status'], { from: 'user' });
1153
1422
  }
1154
1423
  // ---------------------------------------------------------------------------
1155
- // Prompt commands: Login
1156
- // ---------------------------------------------------------------------------
1157
- async function actionLogin() {
1158
- breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5E33\u865F\u767B\u5165' : 'Login']);
1159
- const { loginCommand } = await import('./commands/login.js');
1160
- const cmd = loginCommand();
1161
- await cmd.parseAsync(['login'], { from: 'user' });
1162
- }
1163
- // ---------------------------------------------------------------------------
1164
1424
  // Prompt commands: Config
1165
1425
  // ---------------------------------------------------------------------------
1166
1426
  async function actionConfig() {