import { Command } from 'commander'; import axios from 'axios'; import chalk from 'chalk'; import ora from 'ora'; import { getApiKey, getApiUrl, getProjectId } from '../utils/config.js'; import { HiveScrubber } from '../hive/scrubber.js'; // ─── Types ─────────────────────────────────────────────────────────────────── interface AntidoteSignal { title: string; instruction: string; category: 'SECURITY' | 'PERFORMANCE' | 'ARCHITECTURE' | 'MAINTAINABILITY' | 'UX' | 'ACCESSIBILITY'; severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'; example?: string; anti_example?: string; framework_tags?: string[]; } // ─── Sub-commands ───────────────────────────────────────────────────────────── async function submitAntidote(signal: AntidoteSignal): Promise { const apiKey = getApiKey(); const apiUrl = getApiUrl(); const projectId = getProjectId(); // Pre-flight scrub const scrub = HiveScrubber.scrub(signal.instruction); if (scrub.riskScore > 20) { console.error(chalk.red(`🛑 Signal blocked: sensitive data detected (risk: ${scrub.riskScore})`)); if (scrub.redactionCount > 0) console.error(chalk.dim(` Removed ${scrub.redactionCount} sensitive pattern(s)`)); process.exit(1); } const spinner = ora('Submitting signal to Global Antidote Registry...').start(); try { const payload = { ...signal, instruction: scrub.sanitizedContent, projectId }; const res = await axios.post(`${apiUrl}/api/v1/antidotes/submit`, payload, { headers: { Authorization: `Bearer ${apiKey}` } }); if (res.data.success) { spinner.succeed(chalk.green(`Signal submitted → ${chalk.bold(res.data.data?.status || 'QUARANTINE')}`)); console.log(chalk.dim(` Trust score: ${res.data.data?.trust_score || 'pending'}`)); console.log(chalk.dim(` It will be reviewed by Sigrid before promotion.`)); } else { spinner.fail(chalk.red(res.data.error || 'Submission failed')); } } catch (e: any) { spinner.fail(chalk.red(`Error: ${e.message}`)); } } async function queryAntidotes(searchText: string, category?: string): Promise { const apiKey = getApiKey(); const apiUrl = getApiUrl(); const spinner = ora('Querying Global Antidote Registry...').start(); try { const params: Record = { search: searchText }; if (category) params.category = category.toUpperCase(); const res = await axios.get(`${apiUrl}/api/v1/antidotes`, { params, headers: { Authorization: `Bearer ${apiKey}` } }); spinner.stop(); const antidotes: any[] = res.data.data?.antidotes || []; if (antidotes.length === 0) { console.log(chalk.dim(' No antidotes found for your query.')); return; } console.log(chalk.bold(`\n🧬 GLOBAL ANTIDOTE REGISTRY — ${antidotes.length} result(s)\n`)); console.log(chalk.dim('─'.repeat(60))); antidotes.forEach((a, i) => { const severityColor = a.severity === 'CRITICAL' ? chalk.red : a.severity === 'HIGH' ? chalk.yellow : chalk.cyan; console.log(`${chalk.bold(`[${i + 1}]`)} ${chalk.white.bold(a.title)}`); console.log(` ${severityColor(a.severity)} · ${chalk.dim(a.category)} · Trust: ${chalk.green(a.trust_score)}/100`); console.log(` ${chalk.dim(a.instruction.substring(0, 100))}${a.instruction.length > 100 ? '…' : ''}`); if (a.framework_tags?.length) { console.log(` ${a.framework_tags.map((t: string) => chalk.blue(`#${t}`)).join(' ')}`); } console.log(''); }); } catch (e: any) { spinner.fail(chalk.red(`Error: ${e.message}`)); } } async function showAntidoteStats(): Promise { const apiKey = getApiKey(); const apiUrl = getApiUrl(); const spinner = ora('Fetching Curator Registry statistics...').start(); try { const res = await axios.get(`${apiUrl}/api/v1/antidotes/stats`, { headers: { Authorization: `Bearer ${apiKey}` } }); spinner.stop(); const s = res.data.data; if (!s) { console.log(chalk.dim('No stats available.')); return; } console.log(chalk.bold('\n🏛️ CURATOR REGISTRY STATS\n')); console.log(chalk.dim('─'.repeat(40))); console.log(` Total Antidotes ${chalk.green.bold(s.total_antidotes || 0)}`); console.log(` Fortress Rules ${chalk.magenta.bold(s.fortress_rules || 0)} ${chalk.dim('(immutable)')}`); console.log(` Pending Review ${chalk.yellow.bold(s.pending_quarantine || 0)} ${chalk.dim('(in quarantine)')}`); console.log(` Avg Trust Score ${chalk.cyan.bold(s.average_trust_score || 0)}/100`); console.log(chalk.dim('─'.repeat(40))); if (s.categories) { console.log(chalk.bold('\n By Category:')); Object.entries(s.categories).forEach(([cat, count]) => { console.log(` ${chalk.dim(cat.padEnd(20))} ${chalk.white(count)}`); }); } console.log(''); } catch (e: any) { spinner.fail(chalk.red(`Error: ${e.message}`)); } } // ─── Command Factory ────────────────────────────────────────────────────────── export function createAntidoteCommand(): Command { const cmd = new Command('antidote') .description('Interact with the Global Antidote Registry (cross-org intelligence)'); cmd.command('submit') .description('Submit a new signal to the Global Antidote Registry') .requiredOption('-t, --title ', 'Short descriptive title') .requiredOption('-i, --instruction <text>', 'The canonical instruction (50-2000 chars)') .requiredOption('-c, --category <category>', 'Category: SECURITY | PERFORMANCE | ARCHITECTURE | MAINTAINABILITY | UX') .requiredOption('-s, --severity <severity>', 'Severity: CRITICAL | HIGH | MEDIUM | LOW') .option('-e, --example <code>', 'Good example (what TO do)') .option('-a, --anti-example <code>', 'Bad example (what NOT to do)') .option('-f, --framework-tags <tags>', 'Comma-separated tags e.g. nextjs,react,supabase') .action(async (opts) => { await submitAntidote({ title: opts.title, instruction: opts.instruction, category: opts.category.toUpperCase(), severity: opts.severity.toUpperCase(), example: opts.example, anti_example: opts.antiExample, framework_tags: opts.frameworkTags?.split(',').map((t: string) => t.trim()), }); }); cmd.command('query') .description('Search the Global Antidote Registry') .argument('<search>', 'Search text') .option('-c, --category <category>', 'Filter by category') .action(async (search, opts) => { await queryAntidotes(search, opts.category); }); cmd.command('stats') .description('Show Global Antidote Registry statistics') .action(async () => { await showAntidoteStats(); }); return cmd; }