@panguard-ai/panguard 0.3.1 → 0.3.3

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 +477 -236
  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 +25 -13
@@ -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
  }
@@ -556,7 +717,8 @@ async function actionScan() {
556
717
  console.log(` ${agentMsg}`);
557
718
  const guardItems = [
558
719
  { key: '1', label: currentLang === 'zh-TW' ? '\u662F\uFF0C\u555F\u52D5 Guard \u9632\u8B77' : 'Yes, start Guard protection' },
559
- { key: '2', label: currentLang === 'zh-TW' ? '\u4E0D\u7528\uFF0C\u56DE\u4E3B\u9078\u55AE' : 'No, return to main menu' },
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' },
560
722
  ];
561
723
  renderCompactMenu('', guardItems);
562
724
  const guardChoice = await waitForCompactChoice(guardItems, currentLang);
@@ -564,16 +726,22 @@ async function actionScan() {
564
726
  await actionGuard();
565
727
  return;
566
728
  }
729
+ if (guardChoice?.key === '2') {
730
+ await actionAudit();
731
+ return;
732
+ }
567
733
  }
568
734
  else {
569
735
  nextSteps(currentLang === 'zh-TW'
570
736
  ? [
737
+ { cmd: '[8] \u6280\u80FD\u5BE9\u8A08', desc: '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105' },
571
738
  { cmd: 'scan --full', desc: '\u57F7\u884C\u5B8C\u6574\u6383\u63CF' },
572
- { cmd: 'report', desc: '\u7522\u751F\u5408\u898F\u5831\u544A' },
739
+ { cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
573
740
  ]
574
741
  : [
742
+ { cmd: '[8] Skill Auditor', desc: 'Audit installed skills for threats' },
575
743
  { cmd: 'scan --full', desc: 'Run a comprehensive scan' },
576
- { cmd: 'report', desc: 'Generate compliance report' },
744
+ { cmd: 'guard start', desc: 'Enable real-time protection' },
577
745
  ], currentLang);
578
746
  }
579
747
  }
@@ -585,54 +753,21 @@ async function actionReport() {
585
753
  'Panguard',
586
754
  currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report',
587
755
  ]);
588
- const title = currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report';
589
- console.log(` ${theme.brandBold(title)}`);
590
- console.log('');
591
- const frameworkItems = [
592
- { key: '1', label: 'ISO 27001' },
593
- { key: '2', label: 'SOC 2' },
594
- {
595
- key: '3',
596
- label: currentLang === 'zh-TW'
597
- ? '\u8CC7\u901A\u5B89\u5168\u7BA1\u7406\u6CD5'
598
- : 'TW Cyber Security Act',
599
- },
600
- ];
601
- renderCompactMenu(currentLang === 'zh-TW' ? '\u9078\u64C7\u5408\u898F\u6846\u67B6' : 'Select Framework', frameworkItems);
602
- const fwChoice = await waitForCompactChoice(frameworkItems, currentLang);
603
- if (!fwChoice)
604
- return;
605
- const frameworkMap = {
606
- '1': 'iso27001',
607
- '2': 'soc2',
608
- '3': 'tw_cyber_security_act',
609
- };
610
- const framework = frameworkMap[fwChoice.key] ?? 'iso27001';
611
- const langItems = [
612
- {
613
- key: '1',
614
- label: currentLang === 'zh-TW' ? '\u7E41\u9AD4\u4E2D\u6587 (zh-TW)' : 'Chinese (zh-TW)',
615
- },
616
- { key: '2', label: currentLang === 'zh-TW' ? '\u82F1\u6587 (en)' : 'English (en)' },
617
- ];
618
756
  console.log('');
619
- renderCompactMenu(currentLang === 'zh-TW' ? '\u5831\u544A\u8A9E\u8A00' : 'Report Language', langItems);
620
- const langChoice = await waitForCompactChoice(langItems, currentLang);
621
- if (!langChoice)
622
- return;
623
- 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
+ }
624
770
  console.log('');
625
- const { executeCli } = await import('@panguard-ai/panguard-report');
626
- await executeCli(['generate', '--framework', framework, '--language', reportLang]);
627
- nextSteps(currentLang === 'zh-TW'
628
- ? [
629
- { cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
630
- { cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
631
- ]
632
- : [
633
- { cmd: 'scan', desc: 'Run a security scan' },
634
- { cmd: 'guard start', desc: 'Enable real-time protection' },
635
- ], currentLang);
636
771
  }
637
772
  // ---------------------------------------------------------------------------
638
773
  // [3] Guard Engine
@@ -676,27 +811,24 @@ async function actionGuard() {
676
811
  ], currentLang);
677
812
  break;
678
813
  }
679
- // Free tier — show positive capabilities first
680
- const { tier } = getLicense();
681
- if (tier === 'community') {
682
- console.log(` ${c.sage('\u25C6')} Guard active${' '.repeat(30)}Layer 1 \u00B7 Community`);
683
- console.log('');
684
- if (currentLang === 'zh-TW') {
685
- console.log(` ${c.safe('\u2713')} \u5DF2\u77E5\u653B\u64CA\u6A21\u5F0F\u81EA\u52D5\u5C01\u9396`);
686
- console.log(` ${c.safe('\u2713')} Threat Cloud \u5A01\u8105\u60C5\u5831`);
687
- console.log(` ${c.dim('\u2500')} AI \u5206\u6790 ${c.dim('(Solo $9/\u6708)')}`);
688
- console.log(` ${c.dim('\u2500')} \u901A\u77E5\u7CFB\u7D71 ${c.dim('(Solo $9/\u6708)')}`);
689
- console.log(` ${c.dim('\u2500')} \u65E5\u8A8C\u4FDD\u7559 7 \u5929+ ${c.dim('(Solo $9/\u6708)')}`);
690
- }
691
- else {
692
- console.log(` ${c.safe('\u2713')} Auto-blocking for known attack patterns`);
693
- console.log(` ${c.safe('\u2713')} Threat Cloud intelligence`);
694
- console.log(` ${c.dim('\u2500')} AI analysis ${c.dim('(Solo $9/mo)')}`);
695
- console.log(` ${c.dim('\u2500')} Notifications ${c.dim('(Solo $9/mo)')}`);
696
- console.log(` ${c.dim('\u2500')} Log retention 7d+ ${c.dim('(Solo $9/mo)')}`);
697
- }
698
- 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`);
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`);
699
830
  }
831
+ console.log('');
700
832
  // Spawn guard as background process
701
833
  const { spawn: spawnProcess } = await import('node:child_process');
702
834
  const { fileURLToPath: toPath } = await import('node:url');
@@ -802,46 +934,21 @@ async function actionTrap() {
802
934
  'Panguard',
803
935
  currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System',
804
936
  ]);
805
- const title = currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System';
806
- console.log(` ${theme.brandBold(title)}`);
807
937
  console.log('');
808
- const items = [
809
- {
810
- key: '1',
811
- label: currentLang === 'zh-TW' ? '\u57FA\u672C\u914D\u7F6E (SSH + HTTP)' : 'Config (SSH + HTTP)',
812
- },
813
- {
814
- key: '2',
815
- label: currentLang === 'zh-TW'
816
- ? '\u5B8C\u6574\u914D\u7F6E (8 \u7A2E\u670D\u52D9)'
817
- : 'Config (All 8 Services)',
818
- },
819
- { key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B' : 'Status' },
820
- {
821
- key: '4',
822
- label: currentLang === 'zh-TW' ? '\u653B\u64CA\u8005\u5206\u6790' : 'Attacker Profiles',
823
- },
824
- ];
825
- renderCompactMenu(currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System', items);
826
- const choice = await waitForCompactChoice(items, currentLang);
827
- if (!choice)
828
- return;
829
- console.log('');
830
- const { executeCli } = await import('@panguard-ai/panguard-trap');
831
- switch (choice.key) {
832
- case '1':
833
- await executeCli(['config', '--services', 'ssh,http']);
834
- break;
835
- case '2':
836
- await executeCli(['config', '--services', 'ssh,http,ftp,telnet,mysql,redis,smb,rdp']);
837
- break;
838
- case '3':
839
- await executeCli(['status']);
840
- break;
841
- case '4':
842
- await executeCli(['profiles']);
843
- 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');
844
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('');
845
952
  }
846
953
  // ---------------------------------------------------------------------------
847
954
  // [5] Notifications
@@ -904,7 +1011,17 @@ async function actionThreat() {
904
1011
  ? ` \u5373\u5C07\u555F\u52D5\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u5668 (port ${port})`
905
1012
  : ` Starting Threat Cloud REST API server (port ${port})...`));
906
1013
  console.log('');
907
- 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
+ }
908
1025
  const sp = spinner(currentLang === 'zh-TW'
909
1026
  ? '\u6B63\u5728\u555F\u52D5 Threat Cloud API...'
910
1027
  : 'Starting Threat Cloud API server...');
@@ -941,7 +1058,6 @@ async function actionThreat() {
941
1058
  async function actionDemo() {
942
1059
  breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo']);
943
1060
  const { runScan } = await import('@panguard-ai/panguard-scan');
944
- const { generateComplianceReport, generateSummaryText } = await import('@panguard-ai/panguard-report');
945
1061
  const title = currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo';
946
1062
  console.log(` ${theme.brandBold(title)}`);
947
1063
  console.log(c.dim(currentLang === 'zh-TW'
@@ -982,59 +1098,14 @@ async function actionDemo() {
982
1098
  });
983
1099
  }
984
1100
  console.log('');
985
- // 2. Compliance Report
1101
+ // 2. Compliance Report (Coming Soon)
986
1102
  console.log(c.dim(currentLang === 'zh-TW'
987
- ? ' 2. \u5408\u898F\u5831\u544A (ISO 27001)'
988
- : ' 2. Compliance Report (ISO 27001)'));
989
- console.log('');
990
- const reportSp = spinner(currentLang === 'zh-TW'
991
- ? '\u6B63\u5728\u7522\u751F ISO 27001 \u5831\u544A...'
992
- : 'Generating ISO 27001 report...');
993
- try {
994
- const findings = [
995
- {
996
- findingId: 'DEMO-001',
997
- severity: 'high',
998
- title: 'Missing information security policy',
999
- description: 'No formal information security policy document found.',
1000
- category: 'policy',
1001
- timestamp: new Date(),
1002
- source: 'panguard-scan',
1003
- },
1004
- {
1005
- findingId: 'DEMO-002',
1006
- severity: 'medium',
1007
- title: 'Unencrypted data at rest',
1008
- description: 'Database storage does not use encryption at rest.',
1009
- category: 'encryption',
1010
- timestamp: new Date(),
1011
- source: 'panguard-scan',
1012
- },
1013
- ];
1014
- const report = generateComplianceReport(findings, 'iso27001', 'en', {
1015
- includeRecommendations: true,
1016
- });
1017
- const summary = generateSummaryText(report);
1018
- reportSp.succeed(currentLang === 'zh-TW'
1019
- ? 'ISO 27001 \u5831\u544A\u5DF2\u7522\u751F'
1020
- : 'ISO 27001 report generated');
1021
- const summaryLines = summary.split('\n').slice(0, 10);
1022
- for (const line of summaryLines) {
1023
- console.log(` ${c.dim(line)}`);
1024
- }
1025
- console.log(c.dim(' ...'));
1026
- results.push({
1027
- name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
1028
- status: 'ok',
1029
- });
1030
- }
1031
- catch (err) {
1032
- reportSp.fail(`${err instanceof Error ? err.message : err}`);
1033
- results.push({
1034
- name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
1035
- status: 'fail',
1036
- });
1037
- }
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
+ });
1038
1109
  console.log('');
1039
1110
  // 3. Guard Engine
1040
1111
  console.log(c.dim(currentLang === 'zh-TW' ? ' 3. \u5B88\u8B77\u5F15\u64CE' : ' 3. Guard Engine'));
@@ -1057,24 +1128,14 @@ async function actionDemo() {
1057
1128
  });
1058
1129
  }
1059
1130
  console.log('');
1060
- // 4. Honeypot
1061
- console.log(c.dim(currentLang === 'zh-TW' ? ' 4. \u871C\u7F50\u7CFB\u7D71' : ' 4. Honeypot System'));
1062
- console.log('');
1063
- try {
1064
- const { executeCli: trapCLI } = await import('@panguard-ai/panguard-trap');
1065
- await trapCLI(['config', '--services', 'ssh,http']);
1066
- results.push({
1067
- name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
1068
- status: 'ok',
1069
- });
1070
- }
1071
- catch (err) {
1072
- console.log(` ${c.critical(err instanceof Error ? err.message : String(err))}`);
1073
- results.push({
1074
- name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
1075
- status: 'fail',
1076
- });
1077
- }
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
+ });
1078
1139
  console.log('');
1079
1140
  // 5. Notifications
1080
1141
  console.log(c.dim(currentLang === 'zh-TW' ? ' 5. \u901A\u77E5\u7CFB\u7D71' : ' 5. Notification System'));
@@ -1121,13 +1182,202 @@ async function actionDemo() {
1121
1182
  ], currentLang);
1122
1183
  }
1123
1184
  // ---------------------------------------------------------------------------
1124
- // Prompt commands: Upgrade
1185
+ // [8] Skill Auditor
1125
1186
  // ---------------------------------------------------------------------------
1126
- async function actionUpgrade() {
1127
- breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5347\u7D1A\u65B9\u6848' : 'Upgrade Plan']);
1128
- const { upgradeCommand } = await import('./commands/upgrade.js');
1129
- const cmd = upgradeCommand();
1130
- 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
+ }
1131
1381
  }
1132
1382
  // ---------------------------------------------------------------------------
1133
1383
  // Prompt commands: Hardening
@@ -1171,15 +1421,6 @@ async function actionStatus() {
1171
1421
  await cmd.parseAsync(['status'], { from: 'user' });
1172
1422
  }
1173
1423
  // ---------------------------------------------------------------------------
1174
- // Prompt commands: Login
1175
- // ---------------------------------------------------------------------------
1176
- async function actionLogin() {
1177
- breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5E33\u865F\u767B\u5165' : 'Login']);
1178
- const { loginCommand } = await import('./commands/login.js');
1179
- const cmd = loginCommand();
1180
- await cmd.parseAsync(['login'], { from: 'user' });
1181
- }
1182
- // ---------------------------------------------------------------------------
1183
1424
  // Prompt commands: Config
1184
1425
  // ---------------------------------------------------------------------------
1185
1426
  async function actionConfig() {