#!/usr/bin/env node // llm-security CLI — standalone entry point for llm-security scanners. // Usage: llm-security [args] // Works without Claude Code. Zero dependencies. import { spawn } from 'node:child_process'; import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = resolve(__dirname, '..'); const PKG = JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf8')); const USAGE = `llm-security v${PKG.version} — AI security scanning for Claude Code projects Usage: llm-security [options] Commands: scan [--fail-on ] [--compact] [--format sarif] [--output-file ] [--baseline] [--save-baseline] Run deterministic deep-scan (10 scanners) deep-scan Alias for scan posture Quick security posture assessment (16 categories) audit-bom [--output-file ] Generate AI Bill of Materials (CycloneDX 1.6) ide-scan [target|url] [--vscode-only] [--intellij-only] [--include-builtin] [--online] [--format compact|json] [--fail-on ] Scan installed VS Code / JetBrains extensions, OR fetch a remote VSIX: - https://marketplace.visualstudio.com/items?itemName=publisher.name - https://open-vsx.org/extension/publisher/name[/version] - https://example.com/foo.vsix (direct .vsix download) benchmark [--adaptive] [--category ] Run attack simulation benchmark Options: --help Show this help --version Show version `; const [subcommand, ...rest] = process.argv.slice(2); if (!subcommand || subcommand === '--help' || subcommand === '-h') { process.stdout.write(USAGE); process.exit(0); } if (subcommand === '--version' || subcommand === '-v') { process.stdout.write(`${PKG.version}\n`); process.exit(0); } // Map subcommands to scanner scripts and their arguments const COMMANDS = { scan: { script: 'scanners/scan-orchestrator.mjs' }, 'deep-scan': { script: 'scanners/scan-orchestrator.mjs' }, posture: { script: 'scanners/posture-scanner.mjs' }, 'audit-bom': { script: 'scanners/ai-bom-generator.mjs' }, 'ide-scan': { script: 'scanners/ide-extension-scanner.mjs' }, benchmark: { script: 'scanners/attack-simulator.mjs', prependArgs: ['--benchmark', '--json'] }, }; const cmd = COMMANDS[subcommand]; if (!cmd) { process.stderr.write(`Unknown command: ${subcommand}\n\n`); process.stderr.write(USAGE); process.exit(1); } const scriptPath = resolve(ROOT, cmd.script); const args = [...(cmd.prependArgs || []), ...rest]; const child = spawn('node', [scriptPath, ...args], { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'pipe'], }); child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr); child.on('close', (code) => { process.exitCode = code ?? 1; });