agentaudit 3.10.0 → 3.10.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 (2) hide show
  1. package/cli.mjs +496 -145
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -1,16 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * AgentAudit CLI — Security scanner for AI packages
4
- *
5
- * Usage:
6
- * agentaudit Discover local MCP servers
7
- * agentaudit discover [--quick|--deep] Find MCP servers in AI editors
8
- * agentaudit scan <repo-url> [--deep] Quick scan (or deep audit with --deep)
9
- * agentaudit audit <repo-url> Deep LLM-powered security audit
10
- * agentaudit lookup <name> Look up package in registry
11
- * agentaudit setup Register + configure API key
12
- *
13
- * Global flags: --json, --quiet, --no-color
4
+ *
5
+ * Usage: agentaudit <command> [options]
6
+ *
7
+ * Commands:
8
+ * discover Find MCP servers in AI editors
9
+ * scan <url> [url...] Quick static scan (regex)
10
+ * audit <url> [url...] Deep LLM-powered security audit
11
+ * lookup <name> Look up package in registry
12
+ * model [name|reset] Configure LLM provider + model
13
+ * setup Create account / enter API key
14
+ * status Show current config + auth status
15
+ * help [command] Show help
16
+ *
17
+ * Flags: --json, --quiet, --no-color, --no-upload, --model, --export, --debug
14
18
  */
15
19
 
16
20
  import fs from 'fs';
@@ -49,6 +53,51 @@ const LLM_PROVIDERS = [
49
53
  { key: 'OPENROUTER_API_KEY', name: 'OpenRouter', provider: 'openrouter', type: 'openai', model: 'anthropic/claude-sonnet-4', url: 'https://openrouter.ai/api/v1/chat/completions' },
50
54
  ];
51
55
 
56
+ // ── Provider-specific model choices (for interactive menu) ──
57
+ const PROVIDER_MODELS = {
58
+ anthropic: [
59
+ { label: 'claude-sonnet-4-20250514', sublabel: 'fast + smart (default)', value: 'claude-sonnet-4-20250514' },
60
+ { label: 'claude-opus-4-20250514', sublabel: 'most capable', value: 'claude-opus-4-20250514' },
61
+ ],
62
+ openai: [
63
+ { label: 'gpt-4o', sublabel: 'fast multimodal (default)', value: 'gpt-4o' },
64
+ { label: 'gpt-4.1', sublabel: 'latest', value: 'gpt-4.1' },
65
+ ],
66
+ google: [
67
+ { label: 'gemini-2.5-flash', sublabel: 'fast + cheap (default)', value: 'gemini-2.5-flash' },
68
+ { label: 'gemini-2.5-pro', sublabel: 'most capable', value: 'gemini-2.5-pro' },
69
+ ],
70
+ deepseek: [
71
+ { label: 'deepseek-chat', sublabel: 'cost-effective (default)', value: 'deepseek-chat' },
72
+ ],
73
+ mistral: [
74
+ { label: 'mistral-large-latest', sublabel: 'EU-hosted (default)', value: 'mistral-large-latest' },
75
+ ],
76
+ groq: [
77
+ { label: 'llama-3.3-70b-versatile', sublabel: 'ultra-fast (default)', value: 'llama-3.3-70b-versatile' },
78
+ ],
79
+ xai: [
80
+ { label: 'grok-3', sublabel: 'real-time knowledge (default)', value: 'grok-3' },
81
+ ],
82
+ together: [
83
+ { label: 'meta-llama/Llama-3.3-70B-Instruct-Turbo', sublabel: 'open source (default)', value: 'meta-llama/Llama-3.3-70B-Instruct-Turbo' },
84
+ ],
85
+ fireworks: [
86
+ { label: 'accounts/fireworks/models/llama-v3p3-70b-instruct', sublabel: 'open source (default)', value: 'accounts/fireworks/models/llama-v3p3-70b-instruct' },
87
+ ],
88
+ cerebras: [
89
+ { label: 'llama-3.3-70b', sublabel: 'fast inference (default)', value: 'llama-3.3-70b' },
90
+ ],
91
+ zhipu: [
92
+ { label: 'glm-4.7', sublabel: 'Chinese language (default)', value: 'glm-4.7' },
93
+ ],
94
+ openrouter: [
95
+ { label: 'anthropic/claude-sonnet-4', sublabel: 'default', value: 'anthropic/claude-sonnet-4' },
96
+ { label: 'qwen/qwen3-coder', sublabel: 'code specialist', value: 'qwen/qwen3-coder' },
97
+ { label: 'meta-llama/Llama-3.3-70B', sublabel: 'open source', value: 'meta-llama/Llama-3.3-70B' },
98
+ ],
99
+ };
100
+
52
101
  // ── ANSI Colors (respects NO_COLOR and --no-color) ───────
53
102
 
54
103
  const noColor = !!(process.env.NO_COLOR || process.argv.includes('--no-color'));
@@ -114,20 +163,23 @@ function loadLlmConfig() {
114
163
  if (fs.existsSync(f)) {
115
164
  try {
116
165
  const data = JSON.parse(fs.readFileSync(f, 'utf8'));
117
- if (data.llm_model) return { llm_model: data.llm_model };
166
+ if (data.llm_model || data.preferred_provider) {
167
+ return { llm_model: data.llm_model || null, preferred_provider: data.preferred_provider || null };
168
+ }
118
169
  } catch {}
119
170
  }
120
171
  }
121
172
  return null;
122
173
  }
123
174
 
124
- function saveLlmConfig(model) {
175
+ function saveLlmConfig(model, provider) {
125
176
  // Merge into existing credentials
126
177
  let existing = {};
127
178
  if (fs.existsSync(USER_CRED_FILE)) {
128
179
  try { existing = JSON.parse(fs.readFileSync(USER_CRED_FILE, 'utf8')); } catch {}
129
180
  }
130
- existing.llm_model = model;
181
+ if (model !== undefined) existing.llm_model = model;
182
+ if (provider !== undefined) existing.preferred_provider = provider;
131
183
  const json = JSON.stringify(existing, null, 2);
132
184
  fs.mkdirSync(USER_CRED_DIR, { recursive: true });
133
185
  fs.writeFileSync(USER_CRED_FILE, json, { mode: 0o600 });
@@ -136,12 +188,32 @@ function saveLlmConfig(model) {
136
188
  if (fs.existsSync(SKILL_CRED_FILE)) {
137
189
  try { skillExisting = JSON.parse(fs.readFileSync(SKILL_CRED_FILE, 'utf8')); } catch {}
138
190
  }
139
- skillExisting.llm_model = model;
191
+ if (model !== undefined) skillExisting.llm_model = model;
192
+ if (provider !== undefined) skillExisting.preferred_provider = provider;
140
193
  fs.mkdirSync(path.dirname(SKILL_CRED_FILE), { recursive: true });
141
194
  fs.writeFileSync(SKILL_CRED_FILE, JSON.stringify(skillExisting, null, 2), { mode: 0o600 });
142
195
  } catch {}
143
196
  }
144
197
 
198
+ function resolveProvider() {
199
+ const config = loadLlmConfig();
200
+ const preferred = config?.preferred_provider;
201
+
202
+ if (preferred) {
203
+ // Find provider by name, check if any of their keys is set
204
+ const match = LLM_PROVIDERS.find(p => p.provider === preferred && process.env[p.key]);
205
+ if (match) return match;
206
+ // Key missing for preferred provider — warn + fallback
207
+ const providerInfo = LLM_PROVIDERS.find(p => p.provider === preferred);
208
+ if (providerInfo && !quietMode) {
209
+ console.log(` ${c.yellow}Preferred provider "${providerInfo.name}" missing key (${providerInfo.key}), falling back...${c.reset}`);
210
+ }
211
+ }
212
+
213
+ // Fallback: first match wins
214
+ return LLM_PROVIDERS.find(p => process.env[p.key]) || null;
215
+ }
216
+
145
217
  function saveCredentials(data) {
146
218
  const json = JSON.stringify(data, null, 2);
147
219
  fs.mkdirSync(USER_CRED_DIR, { recursive: true });
@@ -398,31 +470,17 @@ async function setupCommand() {
398
470
 
399
471
  console.log();
400
472
 
401
- // ── LLM model configuration ──
473
+ // ── LLM configuration hint ──
402
474
  const llmConfig = loadLlmConfig();
403
- console.log(` ${c.bold}LLM Model Configuration${c.reset}`);
404
- console.log(` ${c.dim}Used for deep audits. Requires an LLM API key in your environment.${c.reset}`);
405
- console.log();
406
- if (llmConfig) {
407
- console.log(` ${icons.safe} Current model: ${c.bold}${llmConfig.llm_model}${c.reset}`);
408
- console.log();
409
- }
410
- console.log(` ${c.dim}Examples:${c.reset}`);
411
- console.log(` ${c.dim}claude-sonnet-4-20250514${c.reset} ${c.dim}(Anthropic)${c.reset}`);
412
- console.log(` ${c.dim}gpt-4o${c.reset} ${c.dim}(OpenAI)${c.reset}`);
413
- console.log(` ${c.dim}gemini-2.5-flash${c.reset} ${c.dim}(Google)${c.reset}`);
414
- console.log(` ${c.dim}qwen/qwen3.5-coder-32b${c.reset} ${c.dim}(OpenRouter)${c.reset}`);
415
- console.log(` ${c.dim}deepseek-chat${c.reset} ${c.dim}(DeepSeek)${c.reset}`);
416
- console.log();
417
- const modelAnswer = await askQuestion(` Model ${c.dim}(Enter to keep default, or type model name)${c.reset}: `);
418
- if (modelAnswer) {
419
- saveLlmConfig(modelAnswer);
420
- console.log(` ${icons.safe} Model set to ${c.bold}${modelAnswer}${c.reset}`);
421
- } else if (llmConfig) {
422
- console.log(` ${c.dim}Keeping: ${llmConfig.llm_model}${c.reset}`);
423
- } else {
424
- console.log(` ${c.dim}No custom model — will use provider default${c.reset}`);
425
- }
475
+ console.log(` ${c.bold}LLM Configuration${c.reset}`);
476
+ if (llmConfig?.llm_model || llmConfig?.preferred_provider) {
477
+ const parts = [];
478
+ if (llmConfig.preferred_provider) parts.push(llmConfig.preferred_provider);
479
+ if (llmConfig.llm_model) parts.push(llmConfig.llm_model);
480
+ console.log(` ${icons.safe} Current: ${c.bold}${parts.join(' → ')}${c.reset}`);
481
+ }
482
+ console.log(` ${c.dim}Run ${c.cyan}agentaudit model${c.dim} to configure your LLM provider and model.${c.reset}`);
483
+ console.log(` ${c.dim}Deep audits require an LLM API key in your environment.${c.reset}`);
426
484
  console.log();
427
485
 
428
486
  console.log(` ${c.bold}Ready!${c.reset} You can now:`);
@@ -1513,8 +1571,8 @@ async function auditRepo(url) {
1513
1571
  console.log(` ${c.green}done${c.reset}`);
1514
1572
 
1515
1573
  // Step 4: LLM Analysis
1516
- // Detect provider from environment variables (first match wins)
1517
- const activeLlm = LLM_PROVIDERS.find(p => process.env[p.key]);
1574
+ // Resolve provider: preferred_provider from config first match fallback
1575
+ const activeLlm = resolveProvider();
1518
1576
  const llmApiKey = activeLlm ? process.env[activeLlm.key] : null;
1519
1577
  const activeProvider = activeLlm ? activeLlm.name : null;
1520
1578
 
@@ -1529,34 +1587,16 @@ async function auditRepo(url) {
1529
1587
  }
1530
1588
 
1531
1589
  if (!activeLlm) {
1532
- // No LLM API key — clear explanation
1590
+ // No LLM API key — compact explanation
1533
1591
  console.log();
1534
1592
  console.log(` ${c.yellow}No LLM API key found.${c.reset} The ${c.bold}audit${c.reset} command needs an LLM to analyze code.`);
1535
1593
  console.log();
1536
- console.log(` ${c.bold}Option 1: Set an API key${c.reset} (any one of these):`);
1537
- console.log(` ${c.cyan}ANTHROPIC_API_KEY${c.reset} Anthropic Claude`);
1538
- console.log(` ${c.cyan}OPENAI_API_KEY${c.reset} OpenAI GPT-4o`);
1539
- console.log(` ${c.cyan}GEMINI_API_KEY${c.reset} Google Gemini`);
1540
- console.log(` ${c.cyan}DEEPSEEK_API_KEY${c.reset} DeepSeek`);
1541
- console.log(` ${c.cyan}MISTRAL_API_KEY${c.reset} Mistral`);
1542
- console.log(` ${c.cyan}XAI_API_KEY${c.reset} xAI Grok`);
1543
- console.log(` ${c.cyan}GROQ_API_KEY${c.reset} Groq`);
1544
- console.log(` ${c.cyan}TOGETHER_API_KEY${c.reset} Together AI`);
1545
- console.log(` ${c.cyan}FIREWORKS_API_KEY${c.reset} Fireworks AI`);
1546
- console.log(` ${c.cyan}CEREBRAS_API_KEY${c.reset} Cerebras`);
1547
- console.log(` ${c.cyan}ZAI_API_KEY${c.reset} Zhipu AI (GLM)`);
1548
- console.log(` ${c.cyan}OPENROUTER_API_KEY${c.reset} OpenRouter (any model)`);
1549
- console.log();
1550
- console.log(` ${c.dim}export ANTHROPIC_API_KEY=sk-ant-...${c.reset}`);
1551
- console.log(` ${c.dim}export OPENAI_API_KEY=sk-...${c.reset}`);
1594
+ console.log(` ${c.bold}Set an API key${c.reset} (e.g. ${c.cyan}export OPENROUTER_API_KEY=sk-or-...${c.reset})`);
1595
+ console.log(` ${c.dim}Run "agentaudit model" to configure provider + model interactively${c.reset}`);
1552
1596
  console.log();
1553
- console.log(` ${c.bold}Option 2: Export for manual review${c.reset}`);
1554
- console.log(` ${c.cyan}agentaudit audit ${url} --export${c.reset}`);
1555
- console.log(` ${c.dim}Creates a markdown file you can paste into any LLM${c.reset}`);
1556
- console.log();
1557
- console.log(` ${c.bold}Option 3: Use MCP in Claude/Cursor/Windsurf (no API key needed)${c.reset}`);
1558
- console.log(` ${c.dim}Add AgentAudit as MCP server — your editor's agent runs the audit using its own LLM.${c.reset}`);
1559
- console.log(` ${c.dim}Config: { "mcpServers": { "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } } }${c.reset}`);
1597
+ console.log(` ${c.bold}Or export for manual review:${c.reset} ${c.cyan}agentaudit audit ${url} --export${c.reset}`);
1598
+ console.log(` ${c.bold}Or use as MCP server${c.reset} in Cursor/Claude ${c.dim}(no extra API key needed)${c.reset}`);
1599
+ console.log(` ${c.dim}{ "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } }${c.reset}`);
1560
1600
  console.log();
1561
1601
 
1562
1602
  // Check if --export flag
@@ -1793,9 +1833,12 @@ async function auditRepo(url) {
1793
1833
  console.log();
1794
1834
  }
1795
1835
 
1796
- // Upload to registry
1797
- const creds = loadCredentials();
1798
- if (creds) {
1836
+ // Upload to registry (skip with --no-upload)
1837
+ const noUpload = process.argv.includes('--no-upload');
1838
+ let creds = loadCredentials();
1839
+ if (noUpload) {
1840
+ // Skip silently
1841
+ } else if (creds) {
1799
1842
  process.stdout.write(` Uploading report to registry...`);
1800
1843
  try {
1801
1844
  const res = await fetch(`${REGISTRY_URL}/api/reports`, {
@@ -1817,8 +1860,51 @@ async function auditRepo(url) {
1817
1860
  } catch (err) {
1818
1861
  console.log(` ${c.yellow}failed${c.reset}`);
1819
1862
  }
1863
+ } else if (process.stdin.isTTY) {
1864
+ // No credentials — offer inline registration
1865
+ console.log();
1866
+ console.log(` ${c.bold}Share this audit with the community?${c.reset}`);
1867
+ console.log(` ${c.dim}Uploading helps others assess package security. Account is free.${c.reset}`);
1868
+ console.log();
1869
+ console.log(` ${c.bold}1)${c.reset} Create account + upload ${c.dim}(free)${c.reset}`);
1870
+ console.log(` ${c.bold}2)${c.reset} Skip`);
1871
+ console.log();
1872
+ const uploadChoice = await askQuestion(` Choice ${c.dim}(1/2)${c.reset}: `);
1873
+ if (uploadChoice === '1') {
1874
+ const name = await askQuestion(` Agent name ${c.dim}(e.g. my-scanner, claude-desktop)${c.reset}: `);
1875
+ if (!name || !/^[a-zA-Z0-9._-]{2,64}$/.test(name)) {
1876
+ console.log(` ${c.red}Invalid name. Use 2-64 chars: letters, numbers, dash, underscore, dot.${c.reset}`);
1877
+ } else {
1878
+ try {
1879
+ process.stdout.write(` Registering ${c.bold}${name}${c.reset}...`);
1880
+ const regData = await registerAgent(name);
1881
+ saveCredentials({ api_key: regData.api_key, agent_name: regData.agent_name });
1882
+ console.log(` ${c.green}done!${c.reset}`);
1883
+ creds = { api_key: regData.api_key, agent_name: regData.agent_name };
1884
+ process.stdout.write(` Uploading report...`);
1885
+ const res = await fetch(`${REGISTRY_URL}/api/reports`, {
1886
+ method: 'POST',
1887
+ headers: {
1888
+ 'Authorization': `Bearer ${creds.api_key}`,
1889
+ 'Content-Type': 'application/json',
1890
+ },
1891
+ body: JSON.stringify(report),
1892
+ signal: AbortSignal.timeout(15_000),
1893
+ });
1894
+ if (res.ok) {
1895
+ console.log(` ${c.green}done${c.reset}`);
1896
+ console.log(` ${c.dim}Report: ${REGISTRY_URL}/skills/${slug}${c.reset}`);
1897
+ } else {
1898
+ console.log(` ${c.yellow}failed (HTTP ${res.status})${c.reset}`);
1899
+ }
1900
+ } catch (err) {
1901
+ console.log(` ${c.red}failed${c.reset}`);
1902
+ console.log(` ${c.dim}${err.message}${c.reset}`);
1903
+ }
1904
+ }
1905
+ }
1820
1906
  } else {
1821
- console.log(` ${c.dim}Run ${c.cyan}agentaudit setup${c.dim} to upload reports to the registry${c.reset}`);
1907
+ console.log(` ${c.dim}Run ${c.cyan}agentaudit setup${c.dim} to create an account and upload reports${c.reset}`);
1822
1908
  }
1823
1909
 
1824
1910
  console.log();
@@ -1871,60 +1957,196 @@ async function main() {
1871
1957
  // --no-color already handled at top level for `c` object
1872
1958
 
1873
1959
  // Strip global flags from args (including --model <value>)
1874
- const globalFlags = new Set(['--json', '--quiet', '-q', '--no-color']);
1960
+ const globalFlags = new Set(['--json', '--quiet', '-q', '--no-color', '--no-upload']);
1875
1961
  let args = rawArgs.filter(a => !globalFlags.has(a));
1876
1962
  // Remove --model <value> pair
1877
1963
  const modelIdx = args.indexOf('--model');
1878
1964
  if (modelIdx !== -1) args.splice(modelIdx, 2);
1879
1965
 
1966
+ // Detect per-command --help BEFORE stripping (e.g. `agentaudit model --help`)
1967
+ const wantsHelp = args.includes('--help') || args.includes('-h');
1968
+ // Strip --help/-h from args for routing
1969
+ args = args.filter(a => a !== '--help' && a !== '-h');
1970
+
1880
1971
  if (args[0] === '-v' || args[0] === '--version') {
1881
1972
  console.log(`agentaudit ${getVersion()}`);
1882
1973
  process.exitCode = 0; return;
1883
1974
  }
1884
-
1885
- if (args[0] === '--help' || args[0] === '-h') {
1975
+
1976
+ // Subcommand help definitions
1977
+ const subcommandHelp = {
1978
+ discover: [
1979
+ `${c.bold}agentaudit discover${c.reset} [options]`,
1980
+ ``,
1981
+ `Find MCP servers configured in your AI editors (Cursor, Claude Desktop, VS Code, Windsurf).`,
1982
+ ``,
1983
+ `${c.bold}Options:${c.reset}`,
1984
+ ` --quick, -s Auto-scan all discovered servers (regex-based)`,
1985
+ ` --deep, -a Interactively select servers for deep LLM audit`,
1986
+ ``,
1987
+ `${c.bold}Examples:${c.reset}`,
1988
+ ` agentaudit discover`,
1989
+ ` agentaudit discover --quick`,
1990
+ ` agentaudit discover --deep`,
1991
+ ],
1992
+ scan: [
1993
+ `${c.bold}agentaudit scan${c.reset} <url> [url...] [options]`,
1994
+ ``,
1995
+ `Quick regex-based static security scan (~2s). Checks for 15+ patterns`,
1996
+ `(command injection, eval, hardcoded secrets, path traversal, etc.)`,
1997
+ ``,
1998
+ `${c.bold}Options:${c.reset}`,
1999
+ ` --deep Run deep LLM audit instead (same as \`agentaudit audit\`)`,
2000
+ ``,
2001
+ `${c.bold}Examples:${c.reset}`,
2002
+ ` agentaudit scan https://github.com/owner/repo`,
2003
+ ` agentaudit scan https://github.com/a/b https://github.com/c/d`,
2004
+ ` agentaudit scan https://github.com/owner/repo --deep`,
2005
+ ],
2006
+ audit: [
2007
+ `${c.bold}agentaudit audit${c.reset} <url> [url...] [options]`,
2008
+ ``,
2009
+ `Deep LLM-powered 3-pass security audit (~30s). Requires an LLM API key.`,
2010
+ ``,
2011
+ `${c.bold}Options:${c.reset}`,
2012
+ ` --model <name> Override LLM model for this run`,
2013
+ ` --no-upload Skip uploading report to registry`,
2014
+ ` --export Export audit payload as markdown (for manual LLM review)`,
2015
+ ` --debug Show raw LLM response on parse errors`,
2016
+ ``,
2017
+ `${c.bold}Examples:${c.reset}`,
2018
+ ` agentaudit audit https://github.com/owner/repo`,
2019
+ ` agentaudit audit https://github.com/owner/repo --no-upload`,
2020
+ ` agentaudit audit https://github.com/owner/repo --model gpt-4o`,
2021
+ ` agentaudit audit https://github.com/owner/repo --export`,
2022
+ ],
2023
+ lookup: [
2024
+ `${c.bold}agentaudit lookup${c.reset} <name> [options]`,
2025
+ ``,
2026
+ `Look up a package in the AgentAudit registry. Shows trust score,`,
2027
+ `findings, confidence level, and audit history.`,
2028
+ ``,
2029
+ `${c.bold}Aliases:${c.reset} lookup, check`,
2030
+ ``,
2031
+ `${c.bold}Examples:${c.reset}`,
2032
+ ` agentaudit lookup fastmcp`,
2033
+ ` agentaudit lookup @anthropic/mcp-server-filesystem --json`,
2034
+ ` agentaudit check fastmcp`,
2035
+ ],
2036
+ check: null, // alias → lookup
2037
+ model: [
2038
+ `${c.bold}agentaudit model${c.reset} [name|reset]`,
2039
+ ``,
2040
+ `Configure LLM provider and model. Interactive two-step menu when`,
2041
+ `called without arguments: choose provider, then choose model.`,
2042
+ ``,
2043
+ `${c.bold}Usage:${c.reset}`,
2044
+ ` agentaudit model Interactive provider + model menu`,
2045
+ ` agentaudit model <name> Set model directly (keeps current provider)`,
2046
+ ` agentaudit model reset Reset provider + model to defaults`,
2047
+ ``,
2048
+ `${c.bold}Examples:${c.reset}`,
2049
+ ` agentaudit model`,
2050
+ ` agentaudit model gpt-4o`,
2051
+ ` agentaudit model qwen/qwen3-coder`,
2052
+ ` agentaudit model reset`,
2053
+ ],
2054
+ setup: [
2055
+ `${c.bold}agentaudit setup${c.reset}`,
2056
+ ``,
2057
+ `Create an AgentAudit account or enter an existing API key.`,
2058
+ `This is for uploading audit reports to the registry — not for`,
2059
+ `LLM provider configuration (use \`agentaudit model\` for that).`,
2060
+ ``,
2061
+ `${c.bold}Examples:${c.reset}`,
2062
+ ` agentaudit setup`,
2063
+ ],
2064
+ status: [
2065
+ `${c.bold}agentaudit status${c.reset}`,
2066
+ ``,
2067
+ `Show current configuration: active LLM provider, model, account`,
2068
+ `status, and which API keys are available.`,
2069
+ ``,
2070
+ `${c.bold}Aliases:${c.reset} status, whoami`,
2071
+ ``,
2072
+ `${c.bold}Examples:${c.reset}`,
2073
+ ` agentaudit status`,
2074
+ ` agentaudit status --json`,
2075
+ ],
2076
+ };
2077
+
2078
+ // Show subcommand help: `agentaudit help <cmd>` or `agentaudit <cmd> --help`
2079
+ function showSubcommandHelp(cmd) {
2080
+ let helpLines = subcommandHelp[cmd];
2081
+ if (helpLines === null && subcommandHelp[cmd] === null) {
2082
+ // alias redirect
2083
+ const alias = cmd === 'check' ? 'lookup' : cmd;
2084
+ helpLines = subcommandHelp[alias];
2085
+ }
2086
+ if (!helpLines) {
2087
+ console.log(` ${c.red}No help available for "${cmd}"${c.reset}`);
2088
+ console.log(` ${c.dim}Run ${c.cyan}agentaudit help${c.dim} for available commands${c.reset}`);
2089
+ return;
2090
+ }
1886
2091
  banner();
1887
- console.log(` ${c.bold}Commands:${c.reset}`);
2092
+ for (const line of helpLines) console.log(` ${line}`);
1888
2093
  console.log();
1889
- console.log(` ${c.cyan}agentaudit${c.reset} Discover MCP servers (same as discover)`);
1890
- console.log(` ${c.cyan}agentaudit discover${c.reset} Find MCP servers in your AI editors (Cursor, Claude, VS Code, Windsurf)`);
1891
- console.log(` ${c.cyan}agentaudit discover --quick${c.reset} Discover + auto-scan all servers`);
1892
- console.log(` ${c.cyan}agentaudit discover --deep${c.reset} Discover + select servers to deep-audit`);
1893
- console.log(` ${c.cyan}agentaudit scan${c.reset} <url> [url...] Quick static scan (regex, local)`);
1894
- console.log(` ${c.cyan}agentaudit scan${c.reset} <url> ${c.dim}--deep${c.reset} Deep audit (same as audit)`);
1895
- console.log(` ${c.cyan}agentaudit audit${c.reset} <url> [url...] Deep LLM-powered security audit`);
1896
- console.log(` ${c.cyan}agentaudit lookup${c.reset} <name> Look up package in registry`);
1897
- console.log(` ${c.cyan}agentaudit model${c.reset} [name|reset] View or set default LLM model`);
1898
- console.log(` ${c.cyan}agentaudit setup${c.reset} Register + configure API key`);
2094
+ }
2095
+
2096
+ // Handle: `agentaudit <cmd> --help` (wantsHelp + has a command)
2097
+ if (wantsHelp && args[0] && args[0] !== '--help' && args[0] !== '-h') {
2098
+ const cmd = args[0];
2099
+ if (subcommandHelp[cmd] !== undefined) {
2100
+ showSubcommandHelp(cmd);
2101
+ process.exitCode = 0; return;
2102
+ }
2103
+ }
2104
+
2105
+ // Handle: `agentaudit help [cmd]`
2106
+ if (args[0] === 'help') {
2107
+ const helpCmd = args[1];
2108
+ if (helpCmd && subcommandHelp[helpCmd] !== undefined) {
2109
+ showSubcommandHelp(helpCmd);
2110
+ process.exitCode = 0; return;
2111
+ }
2112
+ // No subcommand or unknown → show main help
2113
+ wantsHelp || (args[0] = '--help'); // fall through to main help
2114
+ }
2115
+
2116
+ if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help' || (wantsHelp && args.length === 0)) {
2117
+ banner();
2118
+ console.log(` ${c.bold}USAGE${c.reset}`);
2119
+ console.log(` agentaudit <command> [options]`);
1899
2120
  console.log();
1900
- console.log(` ${c.bold}Global flags:${c.reset}`);
1901
- console.log(` ${c.dim}--json Output JSON to stdout (machine-readable)${c.reset}`);
1902
- console.log(` ${c.dim}--quiet Suppress banner and tree visualization${c.reset}`);
1903
- console.log(` ${c.dim}--no-color Disable ANSI colors (also: NO_COLOR env)${c.reset}`);
1904
- console.log(` ${c.dim}--model <name> Override LLM model (e.g. qwen/qwen3.5-coder-32b)${c.reset}`);
2121
+ console.log(` ${c.bold}SCAN & AUDIT${c.reset}`);
2122
+ console.log(` ${c.cyan}discover${c.reset} Find MCP servers in your AI editors`);
2123
+ console.log(` ${c.cyan}scan${c.reset} <url> [url...] Quick static scan (regex, ~2s)`);
2124
+ console.log(` ${c.cyan}audit${c.reset} <url> [url...] Deep LLM-powered security audit (~30s)`);
2125
+ console.log(` ${c.cyan}lookup${c.reset} <name> Look up package in registry`);
1905
2126
  console.log();
1906
- console.log(` ${c.bold}Quick Scan${c.reset} vs ${c.bold}Deep Audit${c.reset}:`);
1907
- console.log(` ${c.dim}scan = fast regex-based static analysis (~2s)${c.reset}`);
1908
- console.log(` ${c.dim}audit = deep LLM analysis with 3-pass methodology (~30s)${c.reset}`);
2127
+ console.log(` ${c.bold}CONFIGURATION${c.reset}`);
2128
+ console.log(` ${c.cyan}model${c.reset} Configure LLM provider + model`);
2129
+ console.log(` ${c.cyan}setup${c.reset} Create account / enter API key`);
2130
+ console.log(` ${c.cyan}status${c.reset} Show current config + auth status`);
1909
2131
  console.log();
1910
- console.log(` ${c.bold}Exit codes:${c.reset}`);
1911
- console.log(` ${c.dim}0 = clean / success 1 = findings detected 2 = error${c.reset}`);
2132
+ console.log(` ${c.bold}FLAGS${c.reset}`);
2133
+ console.log(` ${c.dim}--json Machine-readable JSON output${c.reset}`);
2134
+ console.log(` ${c.dim}--quiet Suppress banner${c.reset}`);
2135
+ console.log(` ${c.dim}--no-color Disable ANSI colors (also: NO_COLOR env)${c.reset}`);
2136
+ console.log(` ${c.dim}--model <name> Override LLM model for this run${c.reset}`);
2137
+ console.log(` ${c.dim}--no-upload Skip uploading report to registry${c.reset}`);
2138
+ console.log(` ${c.dim}--export Export audit payload as markdown${c.reset}`);
2139
+ console.log(` ${c.dim}--debug Show raw LLM response on parse errors${c.reset}`);
1912
2140
  console.log();
1913
- console.log(` ${c.bold}Examples:${c.reset}`);
1914
- console.log(` agentaudit`);
2141
+ console.log(` ${c.bold}EXAMPLES${c.reset}`);
1915
2142
  console.log(` agentaudit discover --quick`);
1916
2143
  console.log(` agentaudit scan https://github.com/owner/repo`);
1917
2144
  console.log(` agentaudit audit https://github.com/owner/repo`);
1918
2145
  console.log(` agentaudit lookup fastmcp --json`);
1919
2146
  console.log();
1920
- console.log(` ${c.bold}For deep audits,${c.reset} set any LLM API key:`);
1921
- console.log(` ${c.dim}ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, DEEPSEEK_API_KEY,${c.reset}`);
1922
- console.log(` ${c.dim}MISTRAL_API_KEY, XAI_API_KEY, GROQ_API_KEY, TOGETHER_API_KEY,${c.reset}`);
1923
- console.log(` ${c.dim}FIREWORKS_API_KEY, CEREBRAS_API_KEY, ZAI_API_KEY, or OPENROUTER_API_KEY${c.reset}`);
1924
- console.log();
1925
- console.log(` ${c.bold}Or use as MCP server${c.reset} in Cursor/Claude ${c.dim}(no extra API key needed):${c.reset}`);
1926
- console.log(` ${c.dim}Add to your MCP config:${c.reset}`);
1927
- console.log(` ${c.dim}{ "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } }${c.reset}`);
2147
+ console.log(` ${c.bold}LEARN MORE${c.reset}`);
2148
+ console.log(` ${c.dim}agentaudit help <command>${c.reset} Help for a specific command`);
2149
+ console.log(` ${c.dim}https://agentaudit.dev${c.reset} Documentation`);
1928
2150
  console.log();
1929
2151
  process.exitCode = 0; return;
1930
2152
  }
@@ -1940,25 +2162,98 @@ async function main() {
1940
2162
  return;
1941
2163
  }
1942
2164
 
2165
+ if (command === 'status' || command === 'whoami') {
2166
+ // ── Status / diagnostic overview ──
2167
+ const config = loadLlmConfig();
2168
+ const creds = loadCredentials();
2169
+ const resolved = resolveProvider();
2170
+
2171
+ console.log(` ${c.bold}Status${c.reset}`);
2172
+ console.log();
2173
+
2174
+ // Provider + Model
2175
+ const prefProvider = config?.preferred_provider;
2176
+ const prefModel = config?.llm_model;
2177
+ if (prefProvider && resolved) {
2178
+ console.log(` Provider ${c.bold}${resolved.name}${c.reset} ${c.dim}(${resolved.key})${c.reset} ${c.green}✔${c.reset}`);
2179
+ } else if (resolved) {
2180
+ console.log(` Provider ${c.bold}${resolved.name}${c.reset} ${c.dim}(auto-detected via ${resolved.key})${c.reset} ${c.green}✔${c.reset}`);
2181
+ } else {
2182
+ console.log(` Provider ${c.yellow}none${c.reset} ${c.dim}— set an API key or run ${c.cyan}agentaudit model${c.dim}${c.reset}`);
2183
+ }
2184
+
2185
+ if (prefModel) {
2186
+ console.log(` Model ${c.bold}${prefModel}${c.reset} ${c.dim}(custom)${c.reset}`);
2187
+ } else if (resolved) {
2188
+ console.log(` Model ${c.dim}${resolved.model} (provider default)${c.reset}`);
2189
+ } else {
2190
+ console.log(` Model ${c.dim}—${c.reset}`);
2191
+ }
2192
+ console.log();
2193
+
2194
+ // Account / auth
2195
+ if (creds) {
2196
+ console.log(` Account ${c.bold}${creds.agent_name}${c.reset} ${c.green}✔ logged in${c.reset}`);
2197
+ console.log(` ${c.dim} Key: ${creds.api_key.slice(0, 12)}...${c.reset}`);
2198
+ } else {
2199
+ console.log(` Account ${c.yellow}not configured${c.reset} ${c.dim}— run ${c.cyan}agentaudit setup${c.dim} to create one${c.reset}`);
2200
+ }
2201
+ console.log();
2202
+
2203
+ // All provider keys
2204
+ const seen = new Set();
2205
+ const keyLines = [];
2206
+ for (const p of LLM_PROVIDERS) {
2207
+ if (seen.has(p.provider)) continue;
2208
+ seen.add(p.provider);
2209
+ const keys = LLM_PROVIDERS.filter(x => x.provider === p.provider);
2210
+ const hasKey = keys.some(x => process.env[x.key]);
2211
+ keyLines.push({ name: p.name, key: p.key, hasKey });
2212
+ }
2213
+ console.log(` ${c.bold}API Keys${c.reset}`);
2214
+ for (const k of keyLines) {
2215
+ const icon = k.hasKey ? `${c.green}✔${c.reset}` : `${c.dim}✗${c.reset}`;
2216
+ const label = k.hasKey ? k.name : `${c.dim}${k.name}${c.reset}`;
2217
+ console.log(` ${icon} ${label} ${c.dim}${k.key}${c.reset}`);
2218
+ }
2219
+ console.log();
2220
+
2221
+ // JSON mode
2222
+ if (jsonMode) {
2223
+ const status = {
2224
+ version: getVersion(),
2225
+ provider: resolved ? { name: resolved.name, provider: resolved.provider, key: resolved.key, model: prefModel || resolved.model } : null,
2226
+ account: creds ? { agent_name: creds.agent_name, has_key: true } : null,
2227
+ api_keys: keyLines.reduce((acc, k) => { acc[k.key] = k.hasKey; return acc; }, {}),
2228
+ };
2229
+ console.log(JSON.stringify(status, null, 2));
2230
+ }
2231
+ return;
2232
+ }
2233
+
1943
2234
  if (command === 'model') {
1944
2235
  const newModel = targets.filter(t => !t.startsWith('--'))[0];
1945
- const current = loadLlmConfig()?.llm_model;
2236
+ const config = loadLlmConfig();
2237
+ const current = config?.llm_model;
2238
+ const currentProvider = config?.preferred_provider;
1946
2239
 
1947
- // Direct set: agentaudit model <name>
2240
+ // Direct set: agentaudit model reset
1948
2241
  if (newModel === 'reset' || newModel === 'clear') {
1949
2242
  for (const f of [USER_CRED_FILE, SKILL_CRED_FILE]) {
1950
2243
  if (fs.existsSync(f)) {
1951
2244
  try {
1952
2245
  const data = JSON.parse(fs.readFileSync(f, 'utf8'));
1953
2246
  delete data.llm_model;
2247
+ delete data.preferred_provider;
1954
2248
  fs.writeFileSync(f, JSON.stringify(data, null, 2), { mode: 0o600 });
1955
2249
  } catch {}
1956
2250
  }
1957
2251
  }
1958
- console.log(` ${icons.safe} Model reset to provider default`);
2252
+ console.log(` ${icons.safe} Model + provider reset to defaults`);
1959
2253
  return;
1960
2254
  }
1961
2255
 
2256
+ // Direct set: agentaudit model <name> (sets model only, keeps provider)
1962
2257
  if (newModel) {
1963
2258
  saveLlmConfig(newModel);
1964
2259
  console.log(` ${icons.safe} Model set to ${c.bold}${newModel}${c.reset}`);
@@ -1966,82 +2261,136 @@ async function main() {
1966
2261
  return;
1967
2262
  }
1968
2263
 
1969
- // No argument — interactive menu
1970
- const activeLlm = LLM_PROVIDERS.find(p => process.env[p.key]);
1971
- console.log(` ${c.bold}LLM Model Configuration${c.reset}`);
2264
+ // ── No argument — interactive two-step menu ──
2265
+
2266
+ // Build deduplicated provider list
2267
+ const seen = new Set();
2268
+ const providerList = [];
2269
+ for (const p of LLM_PROVIDERS) {
2270
+ if (seen.has(p.provider)) continue;
2271
+ seen.add(p.provider);
2272
+ // Check if ANY key for this provider is set
2273
+ const keys = LLM_PROVIDERS.filter(x => x.provider === p.provider);
2274
+ const hasKey = keys.some(x => process.env[x.key]);
2275
+ const keyName = p.key;
2276
+ providerList.push({
2277
+ label: p.name,
2278
+ sublabel: hasKey
2279
+ ? `${keyName} ${c.green}✔${c.reset}`
2280
+ : `${keyName} ${c.red}✗${c.reset} ${c.dim}set: export ${keyName}=...${c.reset}`,
2281
+ value: p.provider,
2282
+ hasKey,
2283
+ keyName,
2284
+ });
2285
+ }
2286
+
2287
+ // Show status
2288
+ const resolved = resolveProvider();
2289
+ console.log(` ${c.bold}LLM Configuration${c.reset}`);
1972
2290
  console.log();
1973
- if (current) {
1974
- console.log(` Current: ${c.bold}${c.cyan}${current}${c.reset}`);
2291
+ if (currentProvider && current) {
2292
+ const provInfo = LLM_PROVIDERS.find(p => p.provider === currentProvider);
2293
+ console.log(` Active: ${c.bold}${c.cyan}${provInfo?.name || currentProvider} → ${current}${c.reset}`);
2294
+ } else if (current) {
2295
+ console.log(` Active: ${c.bold}${c.cyan}${resolved?.name || 'auto'} → ${current}${c.reset}`);
2296
+ } else if (resolved) {
2297
+ console.log(` Active: ${c.dim}${resolved.name} → ${resolved.model} (defaults)${c.reset}`);
1975
2298
  } else {
1976
- console.log(` Current: ${c.dim}(provider default${activeLlm ? `: ${activeLlm.model}` : ''})${c.reset}`);
1977
- }
1978
- if (activeLlm) {
1979
- console.log(` Provider: ${c.dim}${activeLlm.name} (${activeLlm.key})${c.reset}`);
2299
+ console.log(` Active: ${c.yellow}no provider configured${c.reset}`);
1980
2300
  }
1981
2301
  console.log();
1982
2302
 
1983
- // Build menu items — deduplicate providers, show popular models
2303
+ // Step A: Provider selection
2304
+ const providerChoices = [
2305
+ ...providerList,
2306
+ { label: '(keep current)', sublabel: '', value: '__keep__', hasKey: true },
2307
+ ];
2308
+
2309
+ const selectedProvider = await singleSelect(providerChoices, {
2310
+ title: 'Choose provider',
2311
+ hint: '↑↓ move Enter select Esc cancel',
2312
+ });
2313
+
2314
+ if (selectedProvider === null) {
2315
+ console.log(` ${c.dim}Cancelled${c.reset}`);
2316
+ return;
2317
+ }
2318
+
2319
+ if (selectedProvider === '__keep__') {
2320
+ console.log(` ${c.dim}Keeping current config${c.reset}`);
2321
+ return;
2322
+ }
2323
+
2324
+ // Check if provider has key set
2325
+ const chosenEntry = providerList.find(p => p.value === selectedProvider);
2326
+ if (chosenEntry && !chosenEntry.hasKey) {
2327
+ console.log();
2328
+ console.log(` ${c.yellow}${chosenEntry.keyName} is not set.${c.reset}`);
2329
+ console.log(` ${c.dim}Run: export ${chosenEntry.keyName}=your-key-here${c.reset}`);
2330
+ console.log(` ${c.dim}Then run "agentaudit model" again.${c.reset}`);
2331
+ return;
2332
+ }
2333
+
2334
+ // Step B: Model selection for chosen provider
2335
+ console.log();
2336
+ const providerModels = PROVIDER_MODELS[selectedProvider] || [];
1984
2337
  const modelChoices = [
1985
- { label: 'claude-sonnet-4-20250514', sublabel: 'Anthropic — fast + smart', value: 'claude-sonnet-4-20250514' },
1986
- { label: 'claude-opus-4-20250514', sublabel: 'Anthropic — most capable', value: 'claude-opus-4-20250514' },
1987
- { label: 'gpt-4o', sublabel: 'OpenAI — fast multimodal', value: 'gpt-4o' },
1988
- { label: 'gpt-4.1', sublabel: 'OpenAI — latest', value: 'gpt-4.1' },
1989
- { label: 'gemini-2.5-flash', sublabel: 'Google — fast + cheap', value: 'gemini-2.5-flash' },
1990
- { label: 'gemini-2.5-pro', sublabel: 'Google — most capable', value: 'gemini-2.5-pro' },
1991
- { label: 'deepseek-chat', sublabel: 'DeepSeek — cost-effective', value: 'deepseek-chat' },
1992
- { label: 'qwen/qwen3-coder', sublabel: 'OpenRouter — code specialist', value: 'qwen/qwen3-coder' },
1993
- { label: 'mistral-large-latest', sublabel: 'Mistral — EU-hosted', value: 'mistral-large-latest' },
1994
- { label: 'grok-3', sublabel: 'xAI — real-time knowledge', value: 'grok-3' },
1995
- { label: 'meta-llama/Llama-3.3-70B-Instruct-Turbo', sublabel: 'Together AI — open source', value: 'meta-llama/Llama-3.3-70B-Instruct-Turbo' },
1996
- { label: 'llama-3.3-70b-versatile', sublabel: 'Groq — ultra-fast inference', value: 'llama-3.3-70b-versatile' },
1997
- { label: '(reset to provider default)', sublabel: '', value: '__reset__' },
1998
- { label: '(enter custom model name)', sublabel: '', value: '__custom__' },
2338
+ ...providerModels,
2339
+ { label: '(enter custom model name)', sublabel: '', value: '__custom__' },
2340
+ { label: '(reset to provider default)', sublabel: '', value: '__reset__' },
1999
2341
  ];
2000
2342
 
2001
- const selected = await singleSelect(modelChoices, {
2002
- title: 'Choose default model',
2003
- hint: '↑↓=move Enter=select Esc=cancel',
2343
+ const providerName = LLM_PROVIDERS.find(p => p.provider === selectedProvider)?.name || selectedProvider;
2344
+ const selectedModel = await singleSelect(modelChoices, {
2345
+ title: `Choose model for ${providerName}`,
2346
+ hint: '↑↓ move Enter select Esc cancel',
2004
2347
  });
2005
2348
 
2006
- if (selected === null) {
2349
+ if (selectedModel === null) {
2007
2350
  console.log(` ${c.dim}Cancelled${c.reset}`);
2008
2351
  return;
2009
2352
  }
2010
2353
 
2011
- if (selected === '__reset__') {
2354
+ if (selectedModel === '__reset__') {
2355
+ // Save provider, clear model (use provider default)
2356
+ saveLlmConfig(undefined, selectedProvider);
2357
+ // Also clear llm_model from both files
2012
2358
  for (const f of [USER_CRED_FILE, SKILL_CRED_FILE]) {
2013
2359
  if (fs.existsSync(f)) {
2014
2360
  try {
2015
2361
  const data = JSON.parse(fs.readFileSync(f, 'utf8'));
2016
2362
  delete data.llm_model;
2363
+ data.preferred_provider = selectedProvider;
2017
2364
  fs.writeFileSync(f, JSON.stringify(data, null, 2), { mode: 0o600 });
2018
2365
  } catch {}
2019
2366
  }
2020
2367
  }
2368
+ const defaultModel = LLM_PROVIDERS.find(p => p.provider === selectedProvider)?.model;
2021
2369
  console.log();
2022
- console.log(` ${icons.safe} Model reset to provider default`);
2370
+ console.log(` ${icons.safe} Provider: ${c.bold}${providerName}${c.reset}, model: ${c.dim}${defaultModel} (default)${c.reset}`);
2023
2371
  return;
2024
2372
  }
2025
2373
 
2026
- if (selected === '__custom__') {
2374
+ if (selectedModel === '__custom__') {
2027
2375
  console.log();
2028
2376
  const custom = await askQuestion(` Model name: `);
2029
2377
  if (!custom) {
2030
2378
  console.log(` ${c.dim}Cancelled${c.reset}`);
2031
2379
  return;
2032
2380
  }
2033
- saveLlmConfig(custom);
2034
- console.log(` ${icons.safe} Model set to ${c.bold}${custom}${c.reset}`);
2035
- if (current) console.log(` ${c.dim}Was: ${current}${c.reset}`);
2381
+ saveLlmConfig(custom, selectedProvider);
2382
+ console.log(` ${icons.safe} Provider: ${c.bold}${providerName}${c.reset}, model: ${c.bold}${custom}${c.reset}`);
2383
+ if (current) console.log(` ${c.dim}Was: ${currentProvider || 'auto'} → ${current}${c.reset}`);
2036
2384
  return;
2037
2385
  }
2038
2386
 
2039
- saveLlmConfig(selected);
2387
+ // Normal model selection
2388
+ saveLlmConfig(selectedModel, selectedProvider);
2040
2389
  console.log();
2041
- console.log(` ${icons.safe} Model set to ${c.bold}${selected}${c.reset}`);
2042
- if (current) console.log(` ${c.dim}Was: ${current}${c.reset}`);
2390
+ console.log(` ${icons.safe} Provider: ${c.bold}${providerName}${c.reset}, model: ${c.bold}${selectedModel}${c.reset}`);
2391
+ if (current) console.log(` ${c.dim}Was: ${currentProvider || 'auto'} → ${current}${c.reset}`);
2043
2392
  console.log();
2044
- console.log(` ${c.dim}Tip: agentaudit model <name> to set directly, or agentaudit model reset${c.reset}`);
2393
+ console.log(` ${c.dim}Tip: agentaudit model <name> to set model directly, or agentaudit model reset${c.reset}`);
2045
2394
  return;
2046
2395
  }
2047
2396
 
@@ -2056,6 +2405,7 @@ async function main() {
2056
2405
  const names = targets.filter(t => !t.startsWith('--'));
2057
2406
  if (names.length === 0) {
2058
2407
  console.log(` ${c.red}Error: package name required${c.reset}`);
2408
+ console.log(` ${c.dim}Usage: ${c.cyan}agentaudit lookup <name>${c.dim} — e.g. agentaudit lookup fastmcp${c.reset}`);
2059
2409
  process.exitCode = 2;
2060
2410
  return;
2061
2411
  }
@@ -2068,7 +2418,6 @@ async function main() {
2068
2418
  console.log(JSON.stringify(results.length === 1 ? (results[0] || { error: 'not_found' }) : results, null, 2));
2069
2419
  }
2070
2420
  process.exitCode = 0; return;
2071
- return;
2072
2421
  }
2073
2422
 
2074
2423
  if (command === 'scan') {
@@ -2130,6 +2479,8 @@ async function main() {
2130
2479
  const urls = targets.filter(t => !t.startsWith('--'));
2131
2480
  if (urls.length === 0) {
2132
2481
  console.log(` ${c.red}Error: at least one repository URL required${c.reset}`);
2482
+ console.log(` ${c.dim}Usage: ${c.cyan}agentaudit audit <url>${c.dim} — e.g. agentaudit audit https://github.com/owner/repo${c.reset}`);
2483
+ console.log(` ${c.dim}Tip: use ${c.cyan}agentaudit discover --deep${c.dim} to find & audit locally installed MCP servers${c.reset}`);
2133
2484
  process.exitCode = 2;
2134
2485
  return;
2135
2486
  }
@@ -2144,7 +2495,7 @@ async function main() {
2144
2495
  }
2145
2496
 
2146
2497
  console.log(` ${c.red}Unknown command: ${command}${c.reset}`);
2147
- console.log(` ${c.dim}Run agentaudit --help for usage${c.reset}`);
2498
+ console.log(` ${c.dim}Run ${c.cyan}agentaudit help${c.dim} for available commands${c.reset}`);
2148
2499
  process.exitCode = 2;
2149
2500
  }
2150
2501
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.10.0",
3
+ "version": "3.10.2",
4
4
  "description": "Security scanner for AI packages — MCP server + CLI",
5
5
  "type": "module",
6
6
  "bin": {