UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

743 lines (651 loc) 27.8 kB
#!/usr/bin/env node /** * Claude Flow V3 Statusline Generator (Optimized) * Displays real-time V3 implementation progress and system status * * Usage: node statusline.cjs [--json] [--compact] * * Performance notes: * - Single git execSync call (combines branch + status + upstream) * - No recursive file reading (only stat/readdir, never read test contents) * - No ps aux calls (uses process.memoryUsage() + file-based metrics) * - Strict 2s timeout on all execSync calls * - Shared settings cache across functions */ /* eslint-disable @typescript-eslint/no-var-requires */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const os = require('os'); // Configuration const CONFIG = { maxAgents: 15, }; const CWD = process.cwd(); // ANSI colors const c = { reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', red: '\x1b[0;31m', green: '\x1b[0;32m', yellow: '\x1b[0;33m', blue: '\x1b[0;34m', purple: '\x1b[0;35m', cyan: '\x1b[0;36m', brightRed: '\x1b[1;31m', brightGreen: '\x1b[1;32m', brightYellow: '\x1b[1;33m', brightBlue: '\x1b[1;34m', brightPurple: '\x1b[1;35m', brightCyan: '\x1b[1;36m', brightWhite: '\x1b[1;37m', }; // Safe execSync with strict timeout (returns empty string on failure) function safeExec(cmd, timeoutMs = 2000) { try { return execSync(cmd, { encoding: 'utf-8', timeout: timeoutMs, stdio: ['pipe', 'pipe', 'pipe'], }).trim(); } catch { return ''; } } // Safe JSON file reader (returns null on failure) function readJSON(filePath) { try { if (fs.existsSync(filePath)) { return JSON.parse(fs.readFileSync(filePath, 'utf-8')); } } catch { /* ignore */ } return null; } // Safe file stat (returns null on failure) function safeStat(filePath) { try { return fs.statSync(filePath); } catch { /* ignore */ } return null; } // Shared settings cache — read once, used by multiple functions let _settingsCache = undefined; function getSettings() { if (_settingsCache !== undefined) return _settingsCache; _settingsCache = readJSON(path.join(CWD, '.claude', 'settings.json')) || readJSON(path.join(CWD, '.claude', 'settings.local.json')) || null; return _settingsCache; } // ─── Data Collection (all pure-Node.js or single-exec) ────────── // Get all git info in ONE shell call function getGitInfo() { const result = { name: 'user', gitBranch: '', modified: 0, untracked: 0, staged: 0, ahead: 0, behind: 0, }; // Single shell: get user.name, branch, porcelain status, and upstream diff const script = [ 'git config user.name 2>/dev/null || echo user', 'echo "---SEP---"', 'git branch --show-current 2>/dev/null', 'echo "---SEP---"', 'git status --porcelain 2>/dev/null', 'echo "---SEP---"', 'git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null || echo "0 0"', ].join('; '); const raw = safeExec(`sh -c '${script}'`, 3000); if (!raw) return result; const parts = raw.split('---SEP---').map(s => s.trim()); if (parts.length >= 4) { result.name = parts[0] || 'user'; result.gitBranch = parts[1] || ''; // Parse porcelain status if (parts[2]) { for (const line of parts[2].split('\n')) { if (!line || line.length < 2) continue; const x = line[0], y = line[1]; if (x === '?' && y === '?') { result.untracked++; continue; } if (x !== ' ' && x !== '?') result.staged++; if (y !== ' ' && y !== '?') result.modified++; } } // Parse ahead/behind const ab = (parts[3] || '0 0').split(/\s+/); result.ahead = parseInt(ab[0]) || 0; result.behind = parseInt(ab[1]) || 0; } return result; } // Detect model name from Claude config (pure file reads, no exec) function getModelName() { try { const claudeConfig = readJSON(path.join(os.homedir(), '.claude.json')); if (claudeConfig?.projects) { for (const [projectPath, projectConfig] of Object.entries(claudeConfig.projects)) { if (CWD === projectPath || CWD.startsWith(projectPath + '/')) { const usage = projectConfig.lastModelUsage; if (usage) { const ids = Object.keys(usage); if (ids.length > 0) { let modelId = ids[ids.length - 1]; let latest = 0; for (const id of ids) { const ts = usage[id]?.lastUsedAt ? new Date(usage[id].lastUsedAt).getTime() : 0; if (ts > latest) { latest = ts; modelId = id; } } if (modelId.includes('opus')) return 'Opus 4.6'; if (modelId.includes('sonnet')) return 'Sonnet 4.6'; if (modelId.includes('haiku')) return 'Haiku 4.5'; return modelId.split('-').slice(1, 3).join(' '); } } break; } } } } catch { /* ignore */ } // Fallback: settings.json model field const settings = getSettings(); if (settings?.model) { const m = settings.model; if (m.includes('opus')) return 'Opus 4.6'; if (m.includes('sonnet')) return 'Sonnet 4.6'; if (m.includes('haiku')) return 'Haiku 4.5'; } return 'Claude Code'; } // Get learning stats from memory database (pure stat calls) function getLearningStats() { const memoryPaths = [ path.join(CWD, '.swarm', 'memory.db'), path.join(CWD, '.claude-flow', 'memory.db'), path.join(CWD, '.claude', 'memory.db'), path.join(CWD, 'data', 'memory.db'), path.join(CWD, '.agentdb', 'memory.db'), ]; for (const dbPath of memoryPaths) { const stat = safeStat(dbPath); if (stat) { const sizeKB = stat.size / 1024; const patterns = Math.floor(sizeKB / 2); return { patterns, sessions: Math.max(1, Math.floor(patterns / 10)), }; } } // Check session files count let sessions = 0; try { const sessDir = path.join(CWD, '.claude', 'sessions'); if (fs.existsSync(sessDir)) { sessions = fs.readdirSync(sessDir).filter(f => f.endsWith('.json')).length; } } catch { /* ignore */ } return { patterns: 0, sessions }; } // V3 progress from metrics files (pure file reads) function getV3Progress() { const learning = getLearningStats(); const totalDomains = 5; const dddData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'ddd-progress.json')); let dddProgress = dddData?.progress || 0; let domainsCompleted = Math.min(5, Math.floor(dddProgress / 20)); if (dddProgress === 0 && learning.patterns > 0) { if (learning.patterns >= 500) domainsCompleted = 5; else if (learning.patterns >= 200) domainsCompleted = 4; else if (learning.patterns >= 100) domainsCompleted = 3; else if (learning.patterns >= 50) domainsCompleted = 2; else if (learning.patterns >= 10) domainsCompleted = 1; dddProgress = Math.floor((domainsCompleted / totalDomains) * 100); } return { domainsCompleted, totalDomains, dddProgress, patternsLearned: learning.patterns, sessionsCompleted: learning.sessions, }; } // Security status (pure file reads) function getSecurityStatus() { const totalCves = 3; const auditData = readJSON(path.join(CWD, '.claude-flow', 'security', 'audit-status.json')); if (auditData) { return { status: auditData.status || 'PENDING', cvesFixed: auditData.cvesFixed || 0, totalCves: auditData.totalCves || 3, }; } let cvesFixed = 0; try { const scanDir = path.join(CWD, '.claude', 'security-scans'); if (fs.existsSync(scanDir)) { cvesFixed = Math.min(totalCves, fs.readdirSync(scanDir).filter(f => f.endsWith('.json')).length); } } catch { /* ignore */ } return { status: cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING', cvesFixed, totalCves, }; } // Swarm status (pure file reads, NO ps aux) function getSwarmStatus() { const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json')); if (activityData?.swarm) { return { activeAgents: activityData.swarm.agent_count || 0, maxAgents: CONFIG.maxAgents, coordinationActive: activityData.swarm.coordination_active || activityData.swarm.active || false, }; } const progressData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'v3-progress.json')); if (progressData?.swarm) { return { activeAgents: progressData.swarm.activeAgents || progressData.swarm.agent_count || 0, maxAgents: progressData.swarm.totalAgents || CONFIG.maxAgents, coordinationActive: progressData.swarm.active || (progressData.swarm.activeAgents > 0), }; } return { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false }; } // System metrics (uses process.memoryUsage() — no shell spawn) function getSystemMetrics() { const memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024); const learning = getLearningStats(); const agentdb = getAgentDBStats(); // Intelligence from learning.json const learningData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'learning.json')); let intelligencePct = 0; let contextPct = 0; if (learningData?.intelligence?.score !== undefined) { intelligencePct = Math.min(100, Math.floor(learningData.intelligence.score)); } else { const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 10)) : 0; const fromVectors = agentdb.vectorCount > 0 ? Math.min(100, Math.floor(agentdb.vectorCount / 100)) : 0; intelligencePct = Math.max(fromPatterns, fromVectors); } // Maturity fallback (pure fs checks, no git exec) if (intelligencePct === 0) { let score = 0; if (fs.existsSync(path.join(CWD, '.claude'))) score += 15; const srcDirs = ['src', 'lib', 'app', 'packages', 'v3']; for (const d of srcDirs) { if (fs.existsSync(path.join(CWD, d))) { score += 15; break; } } const testDirs = ['tests', 'test', '__tests__', 'spec']; for (const d of testDirs) { if (fs.existsSync(path.join(CWD, d))) { score += 10; break; } } const cfgFiles = ['package.json', 'tsconfig.json', 'pyproject.toml', 'Cargo.toml', 'go.mod']; for (const f of cfgFiles) { if (fs.existsSync(path.join(CWD, f))) { score += 5; break; } } intelligencePct = Math.min(100, score); } if (learningData?.sessions?.total !== undefined) { contextPct = Math.min(100, learningData.sessions.total * 5); } else { contextPct = Math.min(100, Math.floor(learning.sessions * 5)); } // Sub-agents from file metrics (no ps aux) let subAgents = 0; const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json')); if (activityData?.processes?.estimated_agents) { subAgents = activityData.processes.estimated_agents; } return { memoryMB, contextPct, intelligencePct, subAgents }; } // ADR status (count files only — don't read contents) function getADRStatus() { const complianceData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'adr-compliance.json')); if (complianceData) { const checks = complianceData.checks || {}; const total = Object.keys(checks).length; const impl = Object.values(checks).filter(c => c.compliant).length; return { count: total, implemented: impl, compliance: complianceData.compliance || 0 }; } // Fallback: just count ADR files (don't read them) const adrPaths = [ path.join(CWD, 'v3', 'implementation', 'adrs'), path.join(CWD, 'docs', 'adrs'), path.join(CWD, '.claude-flow', 'adrs'), ]; for (const adrPath of adrPaths) { try { if (fs.existsSync(adrPath)) { const files = fs.readdirSync(adrPath).filter(f => f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\d{4}-/.test(f)) ); // Estimate: ~70% implemented in mature projects const implemented = Math.floor(files.length * 0.7); const compliance = files.length > 0 ? Math.floor((implemented / files.length) * 100) : 0; return { count: files.length, implemented, compliance }; } } catch { /* ignore */ } } return { count: 0, implemented: 0, compliance: 0 }; } // Hooks status (shared settings cache) function getHooksStatus() { let enabled = 0; const total = 17; const settings = getSettings(); if (settings?.hooks) { for (const category of Object.keys(settings.hooks)) { const h = settings.hooks[category]; if (Array.isArray(h) && h.length > 0) enabled++; } } try { const hooksDir = path.join(CWD, '.claude', 'hooks'); if (fs.existsSync(hooksDir)) { const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js') || f.endsWith('.sh')).length; enabled = Math.max(enabled, hookFiles); } } catch { /* ignore */ } return { enabled, total }; } // AgentDB stats (pure stat calls) function getAgentDBStats() { let vectorCount = 0; let dbSizeKB = 0; let namespaces = 0; let hasHnsw = false; const dbFiles = [ path.join(CWD, '.swarm', 'memory.db'), path.join(CWD, '.claude-flow', 'memory.db'), path.join(CWD, '.claude', 'memory.db'), path.join(CWD, 'data', 'memory.db'), ]; for (const f of dbFiles) { const stat = safeStat(f); if (stat) { dbSizeKB = stat.size / 1024; vectorCount = Math.floor(dbSizeKB / 2); namespaces = 1; break; } } if (vectorCount === 0) { const dbDirs = [ path.join(CWD, '.claude-flow', 'agentdb'), path.join(CWD, '.swarm', 'agentdb'), path.join(CWD, '.agentdb'), ]; for (const dir of dbDirs) { try { if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) { const files = fs.readdirSync(dir); namespaces = files.filter(f => f.endsWith('.db') || f.endsWith('.sqlite')).length; for (const file of files) { const stat = safeStat(path.join(dir, file)); if (stat?.isFile()) dbSizeKB += stat.size / 1024; } vectorCount = Math.floor(dbSizeKB / 2); break; } } catch { /* ignore */ } } } const hnswPaths = [ path.join(CWD, '.swarm', 'hnsw.index'), path.join(CWD, '.claude-flow', 'hnsw.index'), ]; for (const p of hnswPaths) { const stat = safeStat(p); if (stat) { hasHnsw = true; vectorCount = Math.max(vectorCount, Math.floor(stat.size / 512)); break; } } return { vectorCount, dbSizeKB: Math.floor(dbSizeKB), namespaces, hasHnsw }; } // Test stats (count files only — NO reading file contents) function getTestStats() { let testFiles = 0; function countTestFiles(dir, depth = 0) { if (depth > 2) return; // Shallower recursion limit try { if (!fs.existsSync(dir)) return; const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') { countTestFiles(path.join(dir, entry.name), depth + 1); } else if (entry.isFile()) { const n = entry.name; if (n.includes('.test.') || n.includes('.spec.') || n.includes('_test.') || n.includes('_spec.')) { testFiles++; } } } } catch { /* ignore */ } } for (const d of ['tests', 'test', '__tests__', 'v3/__tests__']) { countTestFiles(path.join(CWD, d)); } countTestFiles(path.join(CWD, 'src')); // Estimate ~4 test cases per file (avoids reading every file) return { testFiles, testCases: testFiles * 4 }; } // Integration status (shared settings + file checks) function getIntegrationStatus() { const mcpServers = { total: 0, enabled: 0 }; const settings = getSettings(); if (settings?.mcpServers && typeof settings.mcpServers === 'object') { const servers = Object.keys(settings.mcpServers); mcpServers.total = servers.length; mcpServers.enabled = settings.enabledMcpjsonServers ? settings.enabledMcpjsonServers.filter(s => servers.includes(s)).length : servers.length; } // Fallback: .mcp.json if (mcpServers.total === 0) { const mcpConfig = readJSON(path.join(CWD, '.mcp.json')) || readJSON(path.join(os.homedir(), '.claude', 'mcp.json')); if (mcpConfig?.mcpServers) { const s = Object.keys(mcpConfig.mcpServers); mcpServers.total = s.length; mcpServers.enabled = s.length; } } const hasDatabase = ['.swarm/memory.db', '.claude-flow/memory.db', 'data/memory.db'] .some(p => fs.existsSync(path.join(CWD, p))); const hasApi = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY); return { mcpServers, hasDatabase, hasApi }; } // Session stats (pure file reads) function getSessionStats() { for (const p of ['.claude-flow/session.json', '.claude/session.json']) { const data = readJSON(path.join(CWD, p)); if (data?.startTime) { const diffMs = Date.now() - new Date(data.startTime).getTime(); const mins = Math.floor(diffMs / 60000); const duration = mins < 60 ? `${mins}m` : `${Math.floor(mins / 60)}h${mins % 60}m`; return { duration }; } } return { duration: '' }; } // ─── Rendering ────────────────────────────────────────────────── function progressBar(current, total) { const width = 5; const filled = Math.round((current / total) * width); return '[' + '\u25CF'.repeat(filled) + '\u25CB'.repeat(width - filled) + ']'; } // Single-line statusline for Claude Code's status bar function generateStatusline() { const git = getGitInfo(); const modelName = getModelName(); const swarm = getSwarmStatus(); const system = getSystemMetrics(); const session = getSessionStats(); const hooks = getHooksStatus(); const integration = getIntegrationStatus(); const parts = []; // Branding parts.push(`${c.bold}${c.brightPurple}\u258A Claude Flow V3${c.reset}`); // User + swarm indicator const dot = swarm.coordinationActive ? `${c.brightGreen}\u25CF${c.reset}` : `${c.brightCyan}\u25CF${c.reset}`; parts.push(`${dot} ${c.brightCyan}${git.name}${c.reset}`); // Git branch + changes if (git.gitBranch) { let branchPart = `${c.brightBlue}\u23C7 ${git.gitBranch}${c.reset}`; const changes = []; if (git.staged > 0) changes.push(`${c.brightGreen}+${git.staged}${c.reset}`); if (git.modified > 0) changes.push(`${c.brightYellow}~${git.modified}${c.reset}`); if (git.untracked > 0) changes.push(`${c.dim}?${git.untracked}${c.reset}`); if (changes.length > 0) branchPart += ` ${changes.join('')}`; if (git.ahead > 0) branchPart += ` ${c.brightGreen}\u2191${git.ahead}${c.reset}`; if (git.behind > 0) branchPart += ` ${c.brightRed}\u2193${git.behind}${c.reset}`; parts.push(branchPart); } // Model parts.push(`${c.purple}${modelName}${c.reset}`); // Session duration if (session.duration) { parts.push(`${c.cyan}\u23F1 ${session.duration}${c.reset}`); } // Intelligence % const intellColor = system.intelligencePct >= 80 ? c.brightGreen : system.intelligencePct >= 40 ? c.brightYellow : c.dim; parts.push(`${intellColor}\u25CF ${system.intelligencePct}%${c.reset}`); // Swarm agents (only if active) if (swarm.activeAgents > 0 || swarm.coordinationActive) { parts.push(`${c.brightYellow}\u25C9 ${swarm.activeAgents}/${swarm.maxAgents}${c.reset}`); } // Hooks (compact) if (hooks.enabled > 0) { parts.push(`${c.brightGreen}\u2713${hooks.enabled}h${c.reset}`); } // MCP (compact) if (integration.mcpServers.total > 0) { const mcpCol = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen : c.brightYellow; parts.push(`${mcpCol}MCP${integration.mcpServers.enabled}${c.reset}`); } return parts.join(` ${c.dim}\u2502${c.reset} `); } // Multi-line dashboard (for --dashboard flag) function generateDashboard() { const git = getGitInfo(); const modelName = getModelName(); const progress = getV3Progress(); const security = getSecurityStatus(); const swarm = getSwarmStatus(); const system = getSystemMetrics(); const adrs = getADRStatus(); const hooks = getHooksStatus(); const agentdb = getAgentDBStats(); const tests = getTestStats(); const session = getSessionStats(); const integration = getIntegrationStatus(); const lines = []; // Header let header = `${c.bold}${c.brightPurple}\u258A Claude Flow V3 ${c.reset}`; header += `${swarm.coordinationActive ? c.brightCyan : c.dim}\u25CF ${c.brightCyan}${git.name}${c.reset}`; if (git.gitBranch) { header += ` ${c.dim}\u2502${c.reset} ${c.brightBlue}\u23C7 ${git.gitBranch}${c.reset}`; const changes = git.modified + git.staged + git.untracked; if (changes > 0) { let ind = ''; if (git.staged > 0) ind += `${c.brightGreen}+${git.staged}${c.reset}`; if (git.modified > 0) ind += `${c.brightYellow}~${git.modified}${c.reset}`; if (git.untracked > 0) ind += `${c.dim}?${git.untracked}${c.reset}`; header += ` ${ind}`; } if (git.ahead > 0) header += ` ${c.brightGreen}\u2191${git.ahead}${c.reset}`; if (git.behind > 0) header += ` ${c.brightRed}\u2193${git.behind}${c.reset}`; } header += ` ${c.dim}\u2502${c.reset} ${c.purple}${modelName}${c.reset}`; if (session.duration) header += ` ${c.dim}\u2502${c.reset} ${c.cyan}\u23F1 ${session.duration}${c.reset}`; lines.push(header); // Separator lines.push(`${c.dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`); // Line 1: DDD Domains + perf const domainsColor = progress.domainsCompleted >= 3 ? c.brightGreen : progress.domainsCompleted > 0 ? c.yellow : c.red; let perfIndicator; if (agentdb.hasHnsw && agentdb.vectorCount > 0) { const speedup = agentdb.vectorCount > 10000 ? '12500x' : agentdb.vectorCount > 1000 ? '150x' : '10x'; perfIndicator = `${c.brightGreen}\u26A1 HNSW ${speedup}${c.reset}`; } else if (progress.patternsLearned > 0) { const pk = progress.patternsLearned >= 1000 ? `${(progress.patternsLearned / 1000).toFixed(1)}k` : String(progress.patternsLearned); perfIndicator = `${c.brightYellow}\uD83D\uDCDA ${pk} patterns${c.reset}`; } else { perfIndicator = `${c.dim}\u26A1 target: 150x-12500x${c.reset}`; } lines.push( `${c.brightCyan}\uD83C\uDFD7\uFE0F DDD Domains${c.reset} ${progressBar(progress.domainsCompleted, progress.totalDomains)} ` + `${domainsColor}${progress.domainsCompleted}${c.reset}/${c.brightWhite}${progress.totalDomains}${c.reset} ${perfIndicator}` ); // Line 2: Swarm + Hooks + CVE + Memory + Intelligence const swarmInd = swarm.coordinationActive ? `${c.brightGreen}\u25C9${c.reset}` : `${c.dim}\u25CB${c.reset}`; const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red; const secIcon = security.status === 'CLEAN' ? '\uD83D\uDFE2' : security.status === 'IN_PROGRESS' ? '\uD83D\uDFE1' : '\uD83D\uDD34'; const secColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed; const hooksColor = hooks.enabled > 0 ? c.brightGreen : c.dim; const intellColor = system.intelligencePct >= 80 ? c.brightGreen : system.intelligencePct >= 40 ? c.brightYellow : c.dim; lines.push( `${c.brightYellow}\uD83E\uDD16 Swarm${c.reset} ${swarmInd} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` + `${c.brightPurple}\uD83D\uDC65 ${system.subAgents}${c.reset} ` + `${c.brightBlue}\uD83E\uDE9D ${hooksColor}${hooks.enabled}${c.reset}/${c.brightWhite}${hooks.total}${c.reset} ` + `${secIcon} ${secColor}CVE ${security.cvesFixed}${c.reset}/${c.brightWhite}${security.totalCves}${c.reset} ` + `${c.brightCyan}\uD83D\uDCBE ${system.memoryMB}MB${c.reset} ` + `${intellColor}\uD83E\uDDE0 ${String(system.intelligencePct).padStart(3)}%${c.reset}` ); // Line 3: Architecture const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red; const adrColor = adrs.count > 0 ? (adrs.implemented === adrs.count ? c.brightGreen : c.yellow) : c.dim; const adrDisplay = adrs.compliance > 0 ? `${adrColor}\u25CF${adrs.compliance}%${c.reset}` : `${adrColor}\u25CF${adrs.implemented}/${adrs.count}${c.reset}`; lines.push( `${c.brightPurple}\uD83D\uDD27 Architecture${c.reset} ` + `${c.cyan}ADRs${c.reset} ${adrDisplay} ${c.dim}\u2502${c.reset} ` + `${c.cyan}DDD${c.reset} ${dddColor}\u25CF${String(progress.dddProgress).padStart(3)}%${c.reset} ${c.dim}\u2502${c.reset} ` + `${c.cyan}Security${c.reset} ${secColor}\u25CF${security.status}${c.reset}` ); // Line 4: AgentDB, Tests, Integration const hnswInd = agentdb.hasHnsw ? `${c.brightGreen}\u26A1${c.reset}` : ''; const sizeDisp = agentdb.dbSizeKB >= 1024 ? `${(agentdb.dbSizeKB / 1024).toFixed(1)}MB` : `${agentdb.dbSizeKB}KB`; const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim; const testColor = tests.testFiles > 0 ? c.brightGreen : c.dim; let integStr = ''; if (integration.mcpServers.total > 0) { const mcpCol = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen : integration.mcpServers.enabled > 0 ? c.brightYellow : c.red; integStr += `${c.cyan}MCP${c.reset} ${mcpCol}\u25CF${integration.mcpServers.enabled}/${integration.mcpServers.total}${c.reset}`; } if (integration.hasDatabase) integStr += (integStr ? ' ' : '') + `${c.brightGreen}\u25C6${c.reset}DB`; if (integration.hasApi) integStr += (integStr ? ' ' : '') + `${c.brightGreen}\u25C6${c.reset}API`; if (!integStr) integStr = `${c.dim}\u25CF none${c.reset}`; lines.push( `${c.brightCyan}\uD83D\uDCCA AgentDB${c.reset} ` + `${c.cyan}Vectors${c.reset} ${vectorColor}\u25CF${agentdb.vectorCount}${hnswInd}${c.reset} ${c.dim}\u2502${c.reset} ` + `${c.cyan}Size${c.reset} ${c.brightWhite}${sizeDisp}${c.reset} ${c.dim}\u2502${c.reset} ` + `${c.cyan}Tests${c.reset} ${testColor}\u25CF${tests.testFiles}${c.reset} ${c.dim}(~${tests.testCases} cases)${c.reset} ${c.dim}\u2502${c.reset} ` + integStr ); return lines.join('\n'); } // JSON output function generateJSON() { const git = getGitInfo(); return { user: { name: git.name, gitBranch: git.gitBranch, modelName: getModelName() }, v3Progress: getV3Progress(), security: getSecurityStatus(), swarm: getSwarmStatus(), system: getSystemMetrics(), adrs: getADRStatus(), hooks: getHooksStatus(), agentdb: getAgentDBStats(), tests: getTestStats(), git: { modified: git.modified, untracked: git.untracked, staged: git.staged, ahead: git.ahead, behind: git.behind }, lastUpdated: new Date().toISOString(), }; } // ─── Main ─────────────────────────────────────────────────────── if (process.argv.includes('--json')) { console.log(JSON.stringify(generateJSON(), null, 2)); } else if (process.argv.includes('--compact')) { console.log(JSON.stringify(generateJSON())); } else if (process.argv.includes('--single-line')) { console.log(generateStatusline()); } else { // Default: multi-line dashboard console.log(generateDashboard()); }