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

2,047 lines • 93.6 kB
/**
 * V3 CLI Analyze Command
 * Code analysis, diff classification, AST analysis, and change risk assessment
 *
 * Features:
 * - AST analysis using ruvector (tree-sitter) with graceful fallback
 * - Symbol extraction (functions, classes, variables, types)
 * - Cyclomatic complexity scoring
 * - Diff classification and risk assessment
 * - Graph boundaries using MinCut algorithm
 * - Module communities using Louvain algorithm
 * - Circular dependency detection
 *
 * Created with ruv.io
 */
import { output } from '../output.js';
import { callMCPTool, MCPClientError } from '../mcp-client.js';
import * as path from 'path';
import * as fs from 'fs/promises';
import { writeFile } from 'fs/promises';
import { resolve } from 'path';
import { execSync } from 'child_process';
// Dynamic import for AST analyzer
async function getASTAnalyzer() {
    try {
        return await import('../ruvector/ast-analyzer.js');
    }
    catch {
        return null;
    }
}
// Dynamic import for graph analyzer
async function getGraphAnalyzer() {
    try {
        return await import('../ruvector/graph-analyzer.js');
    }
    catch {
        return null;
    }
}
// Diff subcommand
const diffCommand = {
    name: 'diff',
    description: 'Analyze git diff for change risk assessment and classification',
    options: [
        {
            name: 'risk',
            short: 'r',
            description: 'Show risk assessment',
            type: 'boolean',
            default: false,
        },
        {
            name: 'classify',
            short: 'c',
            description: 'Classify change type',
            type: 'boolean',
            default: false,
        },
        {
            name: 'reviewers',
            description: 'Show recommended reviewers',
            type: 'boolean',
            default: false,
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format: text, json, table',
            type: 'string',
            default: 'text',
            choices: ['text', 'json', 'table'],
        },
        {
            name: 'verbose',
            short: 'v',
            description: 'Show detailed file-level analysis',
            type: 'boolean',
            default: false,
        },
    ],
    examples: [
        { command: 'claude-flow analyze diff --risk', description: 'Analyze current diff with risk assessment' },
        { command: 'claude-flow analyze diff HEAD~1 --classify', description: 'Classify changes from last commit' },
        { command: 'claude-flow analyze diff main..feature --format json', description: 'Compare branches with JSON output' },
        { command: 'claude-flow analyze diff --reviewers', description: 'Get recommended reviewers for changes' },
    ],
    action: async (ctx) => {
        const ref = ctx.args[0] || 'HEAD';
        const showRisk = ctx.flags.risk;
        const showClassify = ctx.flags.classify;
        const showReviewers = ctx.flags.reviewers;
        const formatType = ctx.flags.format || 'text';
        const verbose = ctx.flags.verbose;
        // If no specific flag, show all
        const showAll = !showRisk && !showClassify && !showReviewers;
        output.printInfo(`Analyzing diff: ${output.highlight(ref)}`);
        try {
            // Call MCP tool for diff analysis
            const result = await callMCPTool('analyze_diff', {
                ref,
                includeFileRisks: verbose,
                includeReviewers: showReviewers || showAll,
            });
            // JSON output
            if (formatType === 'json') {
                output.printJson(result);
                return { success: true, data: result };
            }
            output.writeln();
            // Summary box
            const files = result.files || [];
            const risk = result.risk || { overall: 'unknown', score: 0, breakdown: { fileCount: 0, totalChanges: 0, highRiskFiles: [], securityConcerns: [], breakingChanges: [], testCoverage: 'unknown' } };
            const classification = result.classification || { category: 'unknown', confidence: 0, reasoning: '' };
            output.printBox([
                `Ref: ${result.ref || 'HEAD'}`,
                `Files: ${files.length}`,
                `Risk: ${getRiskDisplay(risk.overall)} (${risk.score}/100)`,
                `Type: ${classification.category}${classification.subcategory ? ` (${classification.subcategory})` : ''}`,
                ``,
                result.summary || 'No summary available',
            ].join('\n'), 'Diff Analysis');
            // Risk assessment
            if (showRisk || showAll) {
                output.writeln();
                output.writeln(output.bold('Risk Assessment'));
                output.writeln(output.dim('-'.repeat(50)));
                output.printTable({
                    columns: [
                        { key: 'metric', header: 'Metric', width: 25 },
                        { key: 'value', header: 'Value', width: 30 },
                    ],
                    data: [
                        { metric: 'Overall Risk', value: getRiskDisplay(risk.overall) },
                        { metric: 'Risk Score', value: `${risk.score}/100` },
                        { metric: 'Files Changed', value: risk.breakdown.fileCount },
                        { metric: 'Total Lines Changed', value: risk.breakdown.totalChanges },
                        { metric: 'Test Coverage', value: risk.breakdown.testCoverage },
                    ],
                });
                // Security concerns
                if (risk.breakdown.securityConcerns.length > 0) {
                    output.writeln();
                    output.writeln(output.bold(output.warning('Security Concerns')));
                    output.printList(risk.breakdown.securityConcerns.map(c => output.warning(c)));
                }
                // Breaking changes
                if (risk.breakdown.breakingChanges.length > 0) {
                    output.writeln();
                    output.writeln(output.bold(output.error('Potential Breaking Changes')));
                    output.printList(risk.breakdown.breakingChanges.map(c => output.error(c)));
                }
                // High risk files
                if (risk.breakdown.highRiskFiles.length > 0) {
                    output.writeln();
                    output.writeln(output.bold('High Risk Files'));
                    output.printList(risk.breakdown.highRiskFiles.map(f => output.warning(f)));
                }
            }
            // Classification
            if (showClassify || showAll) {
                output.writeln();
                output.writeln(output.bold('Classification'));
                output.writeln(output.dim('-'.repeat(50)));
                output.printTable({
                    columns: [
                        { key: 'field', header: 'Field', width: 15 },
                        { key: 'value', header: 'Value', width: 40 },
                    ],
                    data: [
                        { field: 'Category', value: classification.category },
                        { field: 'Subcategory', value: classification.subcategory || '-' },
                        { field: 'Confidence', value: `${(classification.confidence * 100).toFixed(0)}%` },
                    ],
                });
                output.writeln();
                output.writeln(output.dim(`Reasoning: ${classification.reasoning}`));
            }
            // Reviewers
            if (showReviewers || showAll) {
                output.writeln();
                output.writeln(output.bold('Recommended Reviewers'));
                output.writeln(output.dim('-'.repeat(50)));
                const reviewers = result.recommendedReviewers || [];
                if (reviewers.length > 0) {
                    output.printNumberedList(reviewers.map(r => output.highlight(r)));
                }
                else {
                    output.writeln(output.dim('No specific reviewers recommended'));
                }
            }
            // Verbose file-level details
            if (verbose && result.fileRisks) {
                output.writeln();
                output.writeln(output.bold('File-Level Analysis'));
                output.writeln(output.dim('-'.repeat(50)));
                output.printTable({
                    columns: [
                        { key: 'path', header: 'File', width: 40 },
                        { key: 'risk', header: 'Risk', width: 12, format: (v) => getRiskDisplay(String(v)) },
                        { key: 'score', header: 'Score', width: 8, align: 'right' },
                        { key: 'reasons', header: 'Reasons', width: 30, format: (v) => {
                                const reasons = v;
                                return reasons.slice(0, 2).join('; ');
                            } },
                    ],
                    data: result.fileRisks,
                });
            }
            // Files changed table
            if (formatType === 'table' || showAll) {
                output.writeln();
                output.writeln(output.bold('Files Changed'));
                output.writeln(output.dim('-'.repeat(50)));
                output.printTable({
                    columns: [
                        { key: 'status', header: 'Status', width: 10, format: (v) => getStatusDisplay(String(v)) },
                        { key: 'path', header: 'File', width: 45 },
                        { key: 'additions', header: '+', width: 8, align: 'right', format: (v) => output.success(`+${v}`) },
                        { key: 'deletions', header: '-', width: 8, align: 'right', format: (v) => output.error(`-${v}`) },
                    ],
                    data: files.slice(0, 20),
                });
                if (files.length > 20) {
                    output.writeln(output.dim(`  ... and ${files.length - 20} more files`));
                }
            }
            return { success: true, data: result };
        }
        catch (error) {
            if (error instanceof MCPClientError) {
                output.printError(`Diff analysis failed: ${error.message}`);
            }
            else {
                output.printError(`Unexpected error: ${String(error)}`);
            }
            return { success: false, exitCode: 1 };
        }
    },
};
const codeCommand = {
    name: 'code',
    description: 'Static code analysis and quality assessment',
    options: [
        { name: 'path', short: 'p', type: 'string', description: 'Path to analyze', default: '.' },
        { name: 'type', short: 't', type: 'string', description: 'Analysis type: quality, complexity, security', default: 'quality' },
        { name: 'format', short: 'f', type: 'string', description: 'Output format: text, json', default: 'text' },
    ],
    examples: [
        { command: 'claude-flow analyze code -p ./src', description: 'Analyze source directory' },
        { command: 'claude-flow analyze code --type complexity', description: 'Run complexity analysis' },
    ],
    action: async (ctx) => {
        const targetPath = resolve(ctx.flags.path || '.');
        const analysisType = ctx.flags.type || 'quality';
        const formatJson = ctx.flags.format === 'json';
        output.writeln();
        output.writeln(output.bold('Code Analysis'));
        output.writeln(output.dim('-'.repeat(50)));
        const spinner = output.createSpinner({ text: `Analyzing ${targetPath}...`, spinner: 'dots' });
        spinner.start();
        try {
            const files = await scanSourceFiles(targetPath);
            if (files.length === 0) {
                spinner.stop();
                output.printWarning('No source files found');
                return { success: true };
            }
            const fileStats = [];
            for (const filePath of files) {
                const content = await fs.readFile(filePath, 'utf-8');
                const lines = content.split('\n');
                const nonEmpty = lines.filter(l => l.trim().length > 0 && !/^\s*(\/\/|\/\*|\*\s|#)/.test(l)).length;
                const todos = (content.match(/\b(TODO|FIXME|HACK|XXX)\b/gi) || []).length;
                const fns = (content.match(/(?:export\s+)?(?:async\s+)?function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g) || []).length;
                const imps = (content.match(/^import\s+/gm) || []).length + (content.match(/require\s*\(/g) || []).length;
                let maxNesting = 0;
                let nesting = 0;
                for (const line of lines) {
                    nesting += (line.match(/\{/g) || []).length;
                    nesting -= (line.match(/\}/g) || []).length;
                    if (nesting > maxNesting)
                        maxNesting = nesting;
                }
                const securityIssues = [];
                if (/\beval\s*\(/.test(content))
                    securityIssues.push('eval()');
                if (/\bexec\s*\(/.test(content))
                    securityIssues.push('exec()');
                if (/\.innerHTML\s*=/.test(content))
                    securityIssues.push('innerHTML');
                if (/dangerouslySetInnerHTML/.test(content))
                    securityIssues.push('dangerouslySetInnerHTML');
                if (/['"](?:password|secret|api[_-]?key|token)\s*[:=]\s*['"][^'"]{3,}['"]/i.test(content))
                    securityIssues.push('hardcoded secret');
                if (/new\s+Function\s*\(/.test(content))
                    securityIssues.push('new Function()');
                fileStats.push({
                    file: filePath,
                    loc: nonEmpty,
                    todos,
                    functions: fns,
                    imports: imps,
                    maxNesting,
                    securityIssues,
                });
            }
            spinner.stop();
            const totalLoc = fileStats.reduce((s, f) => s + f.loc, 0);
            const totalTodos = fileStats.reduce((s, f) => s + f.todos, 0);
            const totalFunctions = fileStats.reduce((s, f) => s + f.functions, 0);
            const totalImports = fileStats.reduce((s, f) => s + f.imports, 0);
            const avgFileSize = Math.round(totalLoc / files.length);
            const longestFile = fileStats.reduce((a, b) => a.loc > b.loc ? a : b);
            const avgFnPerFile = (totalFunctions / files.length).toFixed(1);
            const deepestNesting = fileStats.reduce((a, b) => a.maxNesting > b.maxNesting ? a : b);
            const allSecurityIssues = fileStats.filter(f => f.securityIssues.length > 0);
            if (formatJson) {
                const jsonData = { type: analysisType, path: targetPath, files: files.length, totalLoc, totalTodos, totalFunctions, totalImports, avgFileSize, fileStats: fileStats.map(f => ({ relativePath: path.relative(targetPath, f.file), loc: f.loc, todos: f.todos, functions: f.functions, imports: f.imports, maxNesting: f.maxNesting, securityIssues: f.securityIssues })) };
                output.printJson(jsonData);
                return { success: true, data: jsonData };
            }
            if (analysisType === 'quality') {
                output.printBox([`Files: ${files.length}`, `Lines of Code: ${totalLoc.toLocaleString()}`, `Avg File Size: ${avgFileSize} LOC`, `TODO/FIXME: ${totalTodos}`, `Functions: ${totalFunctions}`, `Imports: ${totalImports}`].join('\n'), 'Quality Summary');
                output.writeln();
                output.writeln(output.bold('Largest Files'));
                output.writeln(output.dim('-'.repeat(60)));
                const top10 = [...fileStats].sort((a, b) => b.loc - a.loc).slice(0, 10);
                output.printTable({
                    columns: [
                        { key: 'file', header: 'File', width: 45 },
                        { key: 'loc', header: 'LOC', width: 8, align: 'right' },
                        { key: 'fns', header: 'Fns', width: 6, align: 'right' },
                        { key: 'todos', header: 'TODOs', width: 7, align: 'right' },
                    ],
                    data: top10.map(f => ({ file: path.relative(targetPath, f.file), loc: f.loc, fns: f.functions, todos: f.todos })),
                });
                if (totalTodos > 0) {
                    output.writeln();
                    output.printWarning(`${totalTodos} TODO/FIXME comments found across ${fileStats.filter(f => f.todos > 0).length} files`);
                }
            }
            else if (analysisType === 'complexity') {
                output.printBox([`Files: ${files.length}`, `Total Functions: ${totalFunctions}`, `Avg Functions/File: ${avgFnPerFile}`, `Deepest Nesting: ${deepestNesting.maxNesting} levels (${path.relative(targetPath, deepestNesting.file)})`, `Longest File: ${longestFile.loc} LOC (${path.relative(targetPath, longestFile.file)})`].join('\n'), 'Complexity Summary');
                output.writeln();
                output.writeln(output.bold('High Complexity Files (nesting > 5)'));
                output.writeln(output.dim('-'.repeat(60)));
                const complex = fileStats.filter(f => f.maxNesting > 5).sort((a, b) => b.maxNesting - a.maxNesting);
                if (complex.length === 0) {
                    output.printSuccess('No files with excessive nesting detected');
                }
                else {
                    output.printTable({
                        columns: [
                            { key: 'file', header: 'File', width: 45 },
                            { key: 'nesting', header: 'Max Nest', width: 10, align: 'right' },
                            { key: 'fns', header: 'Fns', width: 6, align: 'right' },
                            { key: 'loc', header: 'LOC', width: 8, align: 'right' },
                        ],
                        data: complex.slice(0, 15).map(f => ({ file: path.relative(targetPath, f.file), nesting: f.maxNesting, fns: f.functions, loc: f.loc })),
                    });
                }
            }
            else if (analysisType === 'security') {
                output.printBox([`Files Scanned: ${files.length}`, `Files with Issues: ${allSecurityIssues.length}`, `Total Issues: ${allSecurityIssues.reduce((s, f) => s + f.securityIssues.length, 0)}`].join('\n'), 'Security Summary');
                if (allSecurityIssues.length === 0) {
                    output.writeln();
                    output.printSuccess('No common security patterns detected');
                }
                else {
                    output.writeln();
                    output.writeln(output.bold('Security Concerns'));
                    output.writeln(output.dim('-'.repeat(60)));
                    output.printTable({
                        columns: [
                            { key: 'file', header: 'File', width: 40 },
                            { key: 'issues', header: 'Issues', width: 35 },
                        ],
                        data: allSecurityIssues.map(f => ({ file: path.relative(targetPath, f.file), issues: f.securityIssues.join(', ') })),
                    });
                }
            }
            else {
                output.printWarning(`Unknown analysis type: ${analysisType}. Use quality, complexity, or security.`);
            }
            return { success: true };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Code analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
// ============================================================================
// AST Analysis Subcommands (using ruvector tree-sitter with fallback)
// ============================================================================
/**
 * Helper: Truncate file path for display
 */
function truncatePathAst(filePath, maxLen = 45) {
    if (filePath.length <= maxLen)
        return filePath;
    return '...' + filePath.slice(-(maxLen - 3));
}
/**
 * Helper: Format complexity value with color coding
 */
function formatComplexityValueAst(value) {
    if (value <= 5)
        return output.success(String(value));
    if (value <= 10)
        return output.warning(String(value));
    return output.error(String(value));
}
/**
 * Helper: Get type marker for symbols
 */
function getTypeMarkerAst(type) {
    switch (type) {
        case 'function': return output.success('fn');
        case 'class': return output.info('class');
        case 'variable': return output.dim('var');
        case 'type': return output.highlight('type');
        case 'interface': return output.highlight('iface');
        default: return output.dim(type.slice(0, 5));
    }
}
/**
 * Helper: Get complexity rating text
 */
function getComplexityRatingAst(value) {
    if (value <= 5)
        return output.success('Simple');
    if (value <= 10)
        return output.warning('Moderate');
    if (value <= 20)
        return output.error('Complex');
    return output.error(output.bold('Very Complex'));
}
/**
 * AST analysis subcommand
 */
const astCommand = {
    name: 'ast',
    description: 'Analyze code using AST parsing (tree-sitter via ruvector)',
    options: [
        {
            name: 'complexity',
            short: 'c',
            description: 'Include complexity metrics',
            type: 'boolean',
            default: false,
        },
        {
            name: 'symbols',
            short: 's',
            description: 'Include symbol extraction',
            type: 'boolean',
            default: false,
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json, table)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json', 'table'],
        },
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
        {
            name: 'verbose',
            short: 'v',
            description: 'Show detailed analysis',
            type: 'boolean',
            default: false,
        },
    ],
    examples: [
        { command: 'claude-flow analyze ast src/', description: 'Analyze all files in src/' },
        { command: 'claude-flow analyze ast src/index.ts --complexity', description: 'Analyze with complexity' },
        { command: 'claude-flow analyze ast . --format json', description: 'JSON output' },
        { command: 'claude-flow analyze ast src/ --symbols', description: 'Extract symbols' },
    ],
    action: async (ctx) => {
        const targetPath = ctx.args[0] || ctx.cwd;
        const showComplexity = ctx.flags.complexity;
        const showSymbols = ctx.flags.symbols;
        const formatType = ctx.flags.format || 'text';
        const outputFile = ctx.flags.output;
        const verbose = ctx.flags.verbose;
        // If no specific flags, show summary
        const showAll = !showComplexity && !showSymbols;
        output.printInfo(`Analyzing: ${output.highlight(targetPath)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Parsing AST...', spinner: 'dots' });
        spinner.start();
        try {
            const astModule = await getASTAnalyzer();
            if (!astModule) {
                spinner.stop();
                output.printWarning('AST analyzer not available, using regex fallback');
            }
            // Resolve path and check if file or directory
            const resolvedPath = resolve(targetPath);
            const stat = await fs.stat(resolvedPath);
            const isDirectory = stat.isDirectory();
            let results = [];
            if (isDirectory) {
                // Scan directory for source files
                const files = await scanSourceFiles(resolvedPath);
                spinner.stop();
                output.printInfo(`Found ${files.length} source files`);
                spinner.start();
                for (const file of files.slice(0, 100)) {
                    try {
                        const content = await fs.readFile(file, 'utf-8');
                        if (astModule) {
                            const analyzer = astModule.createASTAnalyzer();
                            const analysis = analyzer.analyze(content, file);
                            results.push(analysis);
                        }
                        else {
                            // Fallback analysis
                            results.push(fallbackAnalyze(content, file));
                        }
                    }
                    catch {
                        // Skip files that can't be analyzed
                    }
                }
            }
            else {
                // Single file
                const content = await fs.readFile(resolvedPath, 'utf-8');
                if (astModule) {
                    const analyzer = astModule.createASTAnalyzer();
                    const analysis = analyzer.analyze(content, resolvedPath);
                    results.push(analysis);
                }
                else {
                    results.push(fallbackAnalyze(content, resolvedPath));
                }
            }
            spinner.stop();
            if (results.length === 0) {
                output.printWarning('No files analyzed');
                return { success: true };
            }
            // Calculate totals
            const totals = {
                files: results.length,
                functions: results.reduce((sum, r) => sum + r.functions.length, 0),
                classes: results.reduce((sum, r) => sum + r.classes.length, 0),
                imports: results.reduce((sum, r) => sum + r.imports.length, 0),
                avgComplexity: results.reduce((sum, r) => sum + r.complexity.cyclomatic, 0) / results.length,
                totalLoc: results.reduce((sum, r) => sum + r.complexity.loc, 0),
            };
            // JSON output
            if (formatType === 'json') {
                const jsonOutput = { files: results, totals };
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(jsonOutput, null, 2));
                    output.printSuccess(`Results written to ${outputFile}`);
                }
                else {
                    output.printJson(jsonOutput);
                }
                return { success: true, data: jsonOutput };
            }
            // Summary box
            output.printBox([
                `Files analyzed: ${totals.files}`,
                `Functions: ${totals.functions}`,
                `Classes: ${totals.classes}`,
                `Total LOC: ${totals.totalLoc}`,
                `Avg Complexity: ${formatComplexityValueAst(Math.round(totals.avgComplexity))}`,
            ].join('\n'), 'AST Analysis Summary');
            // Complexity view
            if (showComplexity || showAll) {
                output.writeln();
                output.writeln(output.bold('Complexity by File'));
                output.writeln(output.dim('-'.repeat(60)));
                const complexityData = results
                    .map(r => ({
                    file: truncatePathAst(r.filePath),
                    cyclomatic: r.complexity.cyclomatic,
                    cognitive: r.complexity.cognitive,
                    loc: r.complexity.loc,
                    rating: getComplexityRatingAst(r.complexity.cyclomatic),
                }))
                    .sort((a, b) => b.cyclomatic - a.cyclomatic)
                    .slice(0, 15);
                output.printTable({
                    columns: [
                        { key: 'file', header: 'File', width: 40 },
                        { key: 'cyclomatic', header: 'Cyclo', width: 8, align: 'right', format: (v) => formatComplexityValueAst(v) },
                        { key: 'cognitive', header: 'Cogni', width: 8, align: 'right' },
                        { key: 'loc', header: 'LOC', width: 8, align: 'right' },
                        { key: 'rating', header: 'Rating', width: 15 },
                    ],
                    data: complexityData,
                });
                if (results.length > 15) {
                    output.writeln(output.dim(`  ... and ${results.length - 15} more files`));
                }
            }
            // Symbols view
            if (showSymbols || showAll) {
                output.writeln();
                output.writeln(output.bold('Extracted Symbols'));
                output.writeln(output.dim('-'.repeat(60)));
                const allSymbols = [];
                for (const r of results) {
                    for (const fn of r.functions) {
                        allSymbols.push({ name: fn.name, type: 'function', file: truncatePathAst(r.filePath, 30), line: fn.startLine });
                    }
                    for (const cls of r.classes) {
                        allSymbols.push({ name: cls.name, type: 'class', file: truncatePathAst(r.filePath, 30), line: cls.startLine });
                    }
                }
                const displaySymbols = allSymbols.slice(0, 20);
                output.printTable({
                    columns: [
                        { key: 'type', header: 'Type', width: 8, format: (v) => getTypeMarkerAst(v) },
                        { key: 'name', header: 'Symbol', width: 30 },
                        { key: 'file', header: 'File', width: 35 },
                        { key: 'line', header: 'Line', width: 8, align: 'right' },
                    ],
                    data: displaySymbols,
                });
                if (allSymbols.length > 20) {
                    output.writeln(output.dim(`  ... and ${allSymbols.length - 20} more symbols`));
                }
            }
            // Verbose output
            if (verbose) {
                output.writeln();
                output.writeln(output.bold('Import Analysis'));
                output.writeln(output.dim('-'.repeat(60)));
                const importCounts = new Map();
                for (const r of results) {
                    for (const imp of r.imports) {
                        importCounts.set(imp, (importCounts.get(imp) || 0) + 1);
                    }
                }
                const topImports = Array.from(importCounts.entries())
                    .sort((a, b) => b[1] - a[1])
                    .slice(0, 10);
                for (const [imp, count] of topImports) {
                    output.writeln(`  ${output.highlight(count.toString().padStart(3))} ${imp}`);
                }
            }
            if (outputFile) {
                await writeFile(outputFile, JSON.stringify({ files: results, totals }, null, 2));
                output.printSuccess(`Results written to ${outputFile}`);
            }
            return { success: true, data: { files: results, totals } };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`AST analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
/**
 * Complexity analysis subcommand
 */
const complexityAstCommand = {
    name: 'complexity',
    aliases: ['cx'],
    description: 'Analyze code complexity metrics',
    options: [
        {
            name: 'threshold',
            short: 't',
            description: 'Complexity threshold to flag (default: 10)',
            type: 'number',
            default: 10,
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json'],
        },
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
    ],
    examples: [
        { command: 'claude-flow analyze complexity src/', description: 'Analyze complexity' },
        { command: 'claude-flow analyze complexity src/ --threshold 15', description: 'Flag high complexity' },
    ],
    action: async (ctx) => {
        const targetPath = ctx.args[0] || ctx.cwd;
        const threshold = ctx.flags.threshold || 10;
        const formatType = ctx.flags.format || 'text';
        const outputFile = ctx.flags.output;
        output.printInfo(`Analyzing complexity: ${output.highlight(targetPath)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Calculating complexity...', spinner: 'dots' });
        spinner.start();
        try {
            const astModule = await getASTAnalyzer();
            const resolvedPath = resolve(targetPath);
            const stat = await fs.stat(resolvedPath);
            const files = stat.isDirectory() ? await scanSourceFiles(resolvedPath) : [resolvedPath];
            const results = [];
            for (const file of files.slice(0, 100)) {
                try {
                    const content = await fs.readFile(file, 'utf-8');
                    let analysis;
                    if (astModule) {
                        const analyzer = astModule.createASTAnalyzer();
                        analysis = analyzer.analyze(content, file);
                    }
                    else {
                        analysis = fallbackAnalyze(content, file);
                    }
                    const flagged = analysis.complexity.cyclomatic > threshold;
                    const rating = analysis.complexity.cyclomatic <= 5 ? 'Simple' :
                        analysis.complexity.cyclomatic <= 10 ? 'Moderate' :
                            analysis.complexity.cyclomatic <= 20 ? 'Complex' : 'Very Complex';
                    results.push({
                        file: file,
                        cyclomatic: analysis.complexity.cyclomatic,
                        cognitive: analysis.complexity.cognitive,
                        loc: analysis.complexity.loc,
                        commentDensity: analysis.complexity.commentDensity,
                        rating,
                        flagged,
                    });
                }
                catch {
                    // Skip files that can't be analyzed
                }
            }
            spinner.stop();
            // Sort by complexity descending
            results.sort((a, b) => b.cyclomatic - a.cyclomatic);
            const flaggedCount = results.filter(r => r.flagged).length;
            const avgComplexity = results.length > 0
                ? results.reduce((sum, r) => sum + r.cyclomatic, 0) / results.length
                : 0;
            if (formatType === 'json') {
                const jsonOutput = { files: results, summary: { total: results.length, flagged: flaggedCount, avgComplexity, threshold } };
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(jsonOutput, null, 2));
                    output.printSuccess(`Results written to ${outputFile}`);
                }
                else {
                    output.printJson(jsonOutput);
                }
                return { success: true, data: jsonOutput };
            }
            // Summary
            output.printBox([
                `Files analyzed: ${results.length}`,
                `Threshold: ${threshold}`,
                `Flagged files: ${flaggedCount > 0 ? output.error(String(flaggedCount)) : output.success('0')}`,
                `Average complexity: ${formatComplexityValueAst(Math.round(avgComplexity))}`,
            ].join('\n'), 'Complexity Analysis');
            // Show flagged files first
            if (flaggedCount > 0) {
                output.writeln();
                output.writeln(output.bold(output.warning(`High Complexity Files (>${threshold})`)));
                output.writeln(output.dim('-'.repeat(60)));
                const flaggedFiles = results.filter(r => r.flagged).slice(0, 10);
                output.printTable({
                    columns: [
                        { key: 'file', header: 'File', width: 40, format: (v) => truncatePathAst(v) },
                        { key: 'cyclomatic', header: 'Cyclo', width: 8, align: 'right', format: (v) => output.error(String(v)) },
                        { key: 'cognitive', header: 'Cogni', width: 8, align: 'right' },
                        { key: 'loc', header: 'LOC', width: 8, align: 'right' },
                        { key: 'rating', header: 'Rating', width: 15 },
                    ],
                    data: flaggedFiles,
                });
            }
            // Show all files in table format
            output.writeln();
            output.writeln(output.bold('All Files'));
            output.writeln(output.dim('-'.repeat(60)));
            const displayFiles = results.slice(0, 15);
            output.printTable({
                columns: [
                    { key: 'file', header: 'File', width: 40, format: (v) => truncatePathAst(v) },
                    { key: 'cyclomatic', header: 'Cyclo', width: 8, align: 'right', format: (v) => formatComplexityValueAst(v) },
                    { key: 'cognitive', header: 'Cogni', width: 8, align: 'right' },
                    { key: 'loc', header: 'LOC', width: 8, align: 'right' },
                ],
                data: displayFiles,
            });
            if (results.length > 15) {
                output.writeln(output.dim(`  ... and ${results.length - 15} more files`));
            }
            if (outputFile) {
                await writeFile(outputFile, JSON.stringify({ files: results, summary: { total: results.length, flagged: flaggedCount, avgComplexity, threshold } }, null, 2));
                output.printSuccess(`Results written to ${outputFile}`);
            }
            return { success: true, data: { files: results, flaggedCount } };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Complexity analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
/**
 * Symbol extraction subcommand
 */
const symbolsCommand = {
    name: 'symbols',
    aliases: ['sym'],
    description: 'Extract and list code symbols (functions, classes, types)',
    options: [
        {
            name: 'type',
            short: 't',
            description: 'Filter by symbol type (function, class, all)',
            type: 'string',
            default: 'all',
            choices: ['function', 'class', 'all'],
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json'],
        },
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
    ],
    examples: [
        { command: 'claude-flow analyze symbols src/', description: 'Extract all symbols' },
        { command: 'claude-flow analyze symbols src/ --type function', description: 'Only functions' },
        { command: 'claude-flow analyze symbols src/ --format json', description: 'JSON output' },
    ],
    action: async (ctx) => {
        const targetPath = ctx.args[0] || ctx.cwd;
        const symbolType = ctx.flags.type || 'all';
        const formatType = ctx.flags.format || 'text';
        const outputFile = ctx.flags.output;
        output.printInfo(`Extracting symbols: ${output.highlight(targetPath)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Parsing code...', spinner: 'dots' });
        spinner.start();
        try {
            const astModule = await getASTAnalyzer();
            const resolvedPath = resolve(targetPath);
            const stat = await fs.stat(resolvedPath);
            const files = stat.isDirectory() ? await scanSourceFiles(resolvedPath) : [resolvedPath];
            const symbols = [];
            for (const file of files.slice(0, 100)) {
                try {
                    const content = await fs.readFile(file, 'utf-8');
                    let analysis;
                    if (astModule) {
                        const analyzer = astModule.createASTAnalyzer();
                        analysis = analyzer.analyze(content, file);
                    }
                    else {
                        analysis = fallbackAnalyze(content, file);
                    }
                    if (symbolType === 'all' || symbolType === 'function') {
                        for (const fn of analysis.functions) {
                            symbols.push({
                                name: fn.name,
                                type: 'function',
                                file,
                                startLine: fn.startLine,
                                endLine: fn.endLine,
                            });
                        }
                    }
                    if (symbolType === 'all' || symbolType === 'class') {
                        for (const cls of analysis.classes) {
                            symbols.push({
                                name: cls.name,
                                type: 'class',
                                file,
                                startLine: cls.startLine,
                                endLine: cls.endLine,
                            });
                        }
                    }
                }
                catch {
                    // Skip files that can't be parsed
                }
            }
            spinner.stop();
            // Sort by file then name
            symbols.sort((a, b) => a.file.localeCompare(b.file) || a.name.localeCompare(b.name));
            if (formatType === 'json') {
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(symbols, null, 2));
                    output.printSuccess(`Results written to ${outputFile}`);
                }
                else {
                    output.printJson(symbols);
                }
                return { success: true, data: symbols };
            }
            // Summary
            const functionCount = symbols.filter(s => s.type === 'function').length;
            const classCount = symbols.filter(s => s.type === 'class').length;
            output.printBox([
                `Total symbols: ${symbols.length}`,
                `Functions: ${functionCount}`,
                `Classes: ${classCount}`,
                `Files: ${files.length}`,
            ].join('\n'), 'Symbol Extraction');
            output.writeln();
            output.writeln(output.bold('Symbols'));
            output.writeln(output.dim('-'.repeat(60)));
            const displaySymbols = symbols.slice(0, 30);
            output.printTable({
                columns: [
                    { key: 'type', header: 'Type', width: 10, format: (v) => getTypeMarkerAst(v) },
                    { key: 'name', header: 'Name', width: 30 },
                    { key: 'file', header: 'File', width: 35, format: (v) => truncatePathAst(v, 33) },
                    { key: 'startLine', header: 'Line', width: 8, align: 'right' },
                ],
                data: displaySymbols,
            });
            if (symbols.length > 30) {
                output.writeln(output.dim(`  ... and ${symbols.length - 30} more symbols`));
            }
            if (outputFile) {
                await writeFile(outputFile, JSON.stringify(symbols, null, 2));
                output.printSuccess(`Results written to ${outputFile}`);
            }
            return { success: true, data: symbols };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Symbol extraction failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
/**
 * Imports analysis subcommand
 */
const importsCommand = {
    name: 'imports',
    aliases: ['imp'],
    description: 'Analyze import dependencies across files',
    options: [
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json'],
        },
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
        {
            name: 'external',
            short: 'e',
            description: 'Show only external (npm) imports',
            type: 'boolean',
            default: false,
        },
    ],
    examples: [
        { command: 'claude-flow analyze imports src/', description: 'Analyze all imports' },
        { command: 'claude-flow analyze imports src/ --external', description: 'Only npm packages' },
    ],
    action: async (ctx) => {
        const targetPath = ctx.args[0] || ctx.cwd;
        const formatType = ctx.flags.format || 'text';
        const outputFile = ctx.flags.output;
        const externalOnly = ctx.flags.external;
        output.printInfo(`Analyzing imports: ${output.highlight(targetPath)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Scanning imports...', spinner: 'dots' });
        spinner.start();
        try {
            const astModule = await getASTAnalyzer();
            const resolvedPath = resolve(targetPath);
            const stat = await fs.stat(resolvedPath);
            const files = stat.isDirectory() ? await scanSourceFiles(resolvedPath) : [resolvedPath];
            const importCounts = new Map();
            const fileImports = new Map();
            for (const file of files.slice(0, 100)) {
                try {
                    const content = await fs.readFile(file, 'utf-8');
                    let analysis;
                    if (astModule) {
                        const analyzer = astModule.createASTAnalyzer();
                        analysis = analyzer.analyze(content, file);
                    }
                    else {
                        analysis = fallbackAnalyze(content, file);
                    }
                    const imports = analysis.imports.filter(imp => {
                        if (externalOnly) {
                            return !imp.startsWith('.') && !imp.startsWith('/');
                        }
                        return true;
                    });
                    fileImports.set(file, imports);
                    for (const imp of imports) {
                        const existing = importCounts.get(imp) || { count: 0, files: [] };
                        existing.count++;
                        existing.files.push(file);
                        importCounts.set(imp, existing);
                    }
                }
                catch {
                    // Skip files that can't be parsed
                }
            }
            spinner.stop();
            // Sort by count
            const sortedImports = Array.from(importCounts.entries())
                .sort((a, b) => b[1].count - a[1].count);
            if (formatType === 'json') {
                const jsonOutput = {
                    imports: Object.fromEntries(sortedImports),
                    fileImports: Object.fromEntries(fileImports),
                };
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(jsonOutput, null, 2));
                    output.printSuccess(`Results written to ${outputFile}`);
                }
                else {
                    output.printJson(jsonOutput);
                }
                return { success: true, data: jsonOutput };
            }
            // Summary
            const externalImports = sortedImports.filter(([imp]) => !imp.startsWith('.') && !imp.startsWith('/'));
            const localImports = sortedImports.filter(([imp]) => imp.startsWith('.') || imp.startsWith('/'));
            output.printBox([
                `Total unique imports: ${sortedImports.length}`,
                `External (npm): ${externalImports.length}`,
                `Local (relative): ${localImports.length}`,
                `Files scanned: ${files.length}`,
            ].join('\n'), 'Import Analysis');
            // Most used imports
            output.writeln();
            output.writeln(output.bold('Most Used Imports'));
            output.writeln(output.dim('-'.repeat(60)));
            const topImports = sortedImports.slice(0, 20);
            output.printTable({
                columns: [
                    { key: 'count', header: 'Uses', width: 8, align: 'right' },
                    { key: 'import', header: 'Import', width: 50 },
                    { key: 'type', header: 'Type', width: 10 },
                ],
                data: topImports.map(([imp, data]) => ({
                    count: data.count,
                    import: imp,
                    type: imp.startsWith('.') || imp.startsWith('/') ? output.dim('local') : output.highlight('npm'),
                })),
            });
            if (sortedImports.length > 20) {
                output.writeln(output.dim(`  ... and ${sortedImports.length - 20} more imports`));
            }
            if (outputFile) {
                await writeFile(outputFile, JSON.stringify({
                    imports: Object.fromEntries(sortedImports),
                    fileImports: Object.fromEntries(fileImports),
                }, null, 2));
                output.printSuccess(`Results written to ${outputFile}`);
            }
            return { success: true, data: { imports: sortedImports } };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Import analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
/**
 * Helper: Scan directory for source files
 */
async function scanSourceFiles(dir, maxDepth = 10) {
    const files = [];
    const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
    const excludeDirs = ['node_modules', 'dist', 'build', '.git', 'coverage', '__pycache__'];
    async function scan(currentDir, depth) {
        if (depth > maxDepth)
            return;
        try {
            const entries = await fs.readdir(currentDir, { withFileTypes: true });
            for (const entry of entries) {
                const fullPath = path.join(currentDir, entry.name);
                if (entry.isDirectory()) {
                    if (!excludeDirs.includes(entry.name)) {
                        await scan(fullPath, depth + 1);
                    }
                }
                else if (entry.isFile()) {
                    const ext = path.extname(entry.name);
                    if (extensions.includes(ext)) {
                        files.push(fullPath);
                    }
                }
            }
        }
        catch {
            // Skip directories we can't read
        }
    }
    await scan(dir, 0);
    return files;
}
/**
 * Fallback analysis when ruvector is not available
 */
function fallbackAnalyze(code, filePath) {
    const lines = code.split('\n');
    const functions = [];
    const classes = [];
    const imports = [];
    const exports = [];
    // Extract functions
    const funcPattern = /(?:export\s+)?(?:async\s+)?function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>|^\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?\s*\{/gm;
    let match;
    while ((match = funcPattern.exec(code)) !== null) {
        const name = match[1] || match[2] || match[3];
        if (name && !['if', 'while', 'for', 'switch'].includes(name)) {
            const lineNum = code.substring(0, match.index).split('\n').length;
            functions.push({ name, startLine: lineNum, endLine: lineNum + 10 });
        }
    }
    // Extract classes
    const classPattern = /(?:export\s+)?class\s+(\w+)/gm;
    while ((match = classPattern.exec(code)) !== null) {
        const lineNum = code.substring(0, match.index).split('\n').length;
        classes.push({ name: match[1], startLine: lineNum, endLine: lineNum + 20 });
    }
    // Extract imports
    const importPattern = /import\s+(?:.*\s+from\s+)?['"]([^'"]+)['"]/gm;
    while ((match = importPattern.exec(code)) !== null) {
        imports.push(match[1]);
    }
    const requirePattern = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/gm;
    while ((match = requirePattern.exec(code)) !== null) {
        imports.push(match[1]);
    }
    // Extract exports
    const exportPattern = /export\s+(?:default\s+)?(?:const|let|var|function|class|interface|type|enum)\s+(\w+)/gm;
    while ((match = exportPattern.exec(code)) !== null) {
        exports.push(match[1]);
    }
    // Calculate complexity
    const nonEmptyLines = lines.filter(l => l.trim().length > 0).length;
    const commentLines = lines.filter(l => /^\s*(\/\/|\/\*|\*|#)/.test(l)).length;
    const decisionPoints = (code.match(/\b(if|else|for|while|switch|case|catch|&&|\|\||\?)\b/g) || []).length;
    let cognitive = 0;
    let nestingLevel = 0;
    for (const line of lines) {
        const opens = (line.match(/\{/g) || []).length;
        const closes = (line.match(/\}/g) || []).length;
        if (/\b(if|for|while|switch)\b/.test(line)) {
            cognitive += 1 + nestingLevel;
        }
        nestingLevel = Math.max(0, nestingLevel + opens - closes);
    }
    // Detect language
    const ext = path.extname(filePath).toLowerCase();
    const language = ext === '.ts' || ext === '.tsx' ? 'typescript' :
        ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs' ? 'javascript' :
            ext === '.py' ? 'python' : 'unknown';
    return {
        filePath,
        language,
        functions,
        classes,
        imports,
        exports,
        complexity: {
            cyclomatic: decisionPoints + 1,
            cognitive,
            loc: nonEmptyLines,
            commentDensity: lines.length > 0 ? commentLines / lines.length : 0,
        },
    };
}
// Dependencies subcommand
const depsCommand = {
    name: 'deps',
    description: 'Analyze project dependencies',
    options: [
        { name: 'outdated', short: 'o', type: 'boolean', description: 'Show only outdated dependencies' },
        { name: 'security', short: 's', type: 'boolean', description: 'Check for security vulnerabilities' },
        { name: 'format', short: 'f', type: 'string', description: 'Output format: text, json', default: 'text' },
    ],
    examples: [
        { command: 'claude-flow analyze deps --outdated', description: 'Show outdated dependencies' },
        { command: 'claude-flow analyze deps --security', description: 'Check for vulnerabilities' },
    ],
    action: async (ctx) => {
        const showOutdated = ctx.flags.outdated;
        const checkSecurity = ctx.flags.security;
        const formatJson = ctx.flags.format === 'json';
        output.writeln();
        output.writeln(output.bold('Dependency Analysis'));
        output.writeln(output.dim('-'.repeat(50)));
        try {
            const pkgPath = resolve('package.json');
            let pkgContent;
            try {
                pkgContent = await fs.readFile(pkgPath, 'utf-8');
            }
            catch {
                output.printError('No package.json found in current directory');
                return { success: false, exitCode: 1 };
            }
            const pkg = JSON.parse(pkgContent);
            const deps = Object.entries(pkg.dependencies || {});
            const devDeps = Object.entries(pkg.devDependencies || {});
            const optDeps = Object.entries(pkg.optionalDependencies || {});
            const peerDeps = Object.entries(pkg.peerDependencies || {});
            const total = deps.length + devDeps.length + optDeps.length + peerDeps.length;
            if (formatJson && !showOutdated && !checkSecurity) {
                const jsonData = { name: pkg.name, version: pkg.version, dependencies: deps.length, devDependencies: devDeps.length, optionalDependencies: optDeps.length, peerDependencies: peerDeps.length, total };
                output.printJson(jsonData);
                return { success: true, data: jsonData };
            }
            output.printBox([`Package: ${pkg.name || 'unknown'} @ ${pkg.version || '0.0.0'}`, `Dependencies: ${deps.length}`, `Dev Dependencies: ${devDeps.length}`, `Optional: ${optDeps.length}`, `Peer: ${peerDeps.length}`, `Total: ${total}`].join('\n'), 'Dependency Summary');
            if (showOutdated) {
                output.writeln();
                output.writeln(output.bold('Outdated Check'));
                output.writeln(output.dim('-'.repeat(60)));
                const outdated = [];
                const checkDeps = async (entries, category) => {
                    for (const [name, declared] of entries) {
                        try {
                            const installedPkg = resolve('node_modules', name, 'package.json');
                            const raw = await fs.readFile(installedPkg, 'utf-8');
                            const installedContent = JSON.parse(raw);
                            const installed = installedContent.version || 'unknown';
                            const cleanDeclared = declared.replace(/^[\^~>=<]+/, '');
                            if (installed !== cleanDeclared) {
                                outdated.push({ name, declared: declared, installed, category });
                            }
                        }
                        catch {
                            outdated.push({ name, declared: declared, installed: 'not installed', category });
                        }
                    }
                };
                await checkDeps(deps, 'prod');
                await checkDeps(devDeps, 'dev');
                if (outdated.length === 0) {
                    output.printSuccess('All dependencies match declared versions');
                }
                else {
                    output.printTable({
                        columns: [
                            { key: 'name', header: 'Package', width: 30 },
                            { key: 'declared', header: 'Declared', width: 14 },
                            { key: 'installed', header: 'Installed', width: 14 },
                            { key: 'category', header: 'Type', width: 6 },
                        ],
                        data: outdated.slice(0, 30),
                    });
                    if (outdated.length > 30) {
                        output.writeln(output.dim(`  ... and ${outdated.length - 30} more`));
                    }
                }
            }
            if (checkSecurity) {
                output.writeln();
                output.writeln(output.bold('Security Audit'));
                output.writeln(output.dim('-'.repeat(60)));
                try {
                    const auditRaw = execSync('npm audit --json 2>/dev/null', { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
                    const audit = JSON.parse(auditRaw);
                    const vulns = audit.metadata?.vulnerabilities || audit.vulnerabilities || {};
                    const info = vulns.info || 0;
                    const low = vulns.low || 0;
                    const moderate = vulns.moderate || 0;
                    const high = vulns.high || 0;
                    const critical = vulns.critical || 0;
                    const totalVulns = info + low + moderate + high + critical;
                    if (totalVulns === 0) {
                        output.printSuccess('No known vulnerabilities found');
                    }
                    else {
                        output.printTable({
                            columns: [
                                { key: 'severity', header: 'Severity', width: 12 },
                                { key: 'count', header: 'Count', width: 8, align: 'right' },
                            ],
                            data: [
                                ...(critical > 0 ? [{ severity: 'Critical', count: critical }] : []),
                                ...(high > 0 ? [{ severity: 'High', count: high }] : []),
                                ...(moderate > 0 ? [{ severity: 'Moderate', count: moderate }] : []),
                                ...(low > 0 ? [{ severity: 'Low', count: low }] : []),
                                ...(info > 0 ? [{ severity: 'Info', count: info }] : []),
                                { severity: 'Total', count: totalVulns },
                            ],
                        });
                        if (critical > 0 || high > 0) {
                            output.printWarning(`${critical + high} high/critical vulnerabilities found. Run 'npm audit' for details.`);
                        }
                    }
                }
                catch {
                    output.printWarning('npm audit failed. Ensure npm is available and node_modules is installed.');
                }
            }
            return { success: true };
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Dependency analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
// ============================================================================
// Graph Analysis Subcommands (MinCut, Louvain, Circular Dependencies)
// ============================================================================
/**
 * Analyze code boundaries using MinCut algorithm
 */
const boundariesCommand = {
    name: 'boundaries',
    aliases: ['boundary', 'mincut'],
    description: 'Find natural code boundaries using MinCut algorithm',
    options: [
        {
            name: 'partitions',
            short: 'p',
            description: 'Number of partitions to find',
            type: 'number',
            default: 2,
        },
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json, dot)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json', 'dot'],
        },
    ],
    examples: [
        { command: 'claude-flow analyze boundaries src/', description: 'Find code boundaries in src/' },
        { command: 'claude-flow analyze boundaries -p 3 src/', description: 'Find 3 partitions' },
        { command: 'claude-flow analyze boundaries -f dot -o graph.dot src/', description: 'Export to DOT format' },
    ],
    action: async (ctx) => {
        const targetDir = ctx.args[0] || ctx.cwd;
        const numPartitions = ctx.flags.partitions || 2;
        const outputFile = ctx.flags.output;
        const format = ctx.flags.format || 'text';
        output.printInfo(`Analyzing code boundaries in: ${output.highlight(targetDir)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Building dependency graph...', spinner: 'dots' });
        spinner.start();
        try {
            const analyzer = await getGraphAnalyzer();
            if (!analyzer) {
                spinner.stop();
                output.printError('Graph analyzer module not available');
                return { success: false, exitCode: 1 };
            }
            const result = await analyzer.analyzeGraph(resolve(targetDir), {
                includeBoundaries: true,
                includeModules: false,
                numPartitions,
            });
            spinner.stop();
            // Handle different output formats
            if (format === 'json') {
                const jsonOutput = {
                    boundaries: result.boundaries,
                    statistics: result.statistics,
                    circularDependencies: result.circularDependencies,
                };
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(jsonOutput, null, 2));
                    output.printSuccess(`Results written to ${outputFile}`);
                }
                else {
                    output.printJson(jsonOutput);
                }
                return { success: true, data: jsonOutput };
            }
            if (format === 'dot') {
                const dotOutput = analyzer.exportToDot(result, {
                    includeLabels: true,
                    highlightCycles: true,
                });
                if (outputFile) {
                    await writeFile(outputFile, dotOutput);
                    output.printSuccess(`DOT graph written to ${outputFile}`);
                    output.writeln(output.dim('Visualize with: dot -Tpng -o graph.png ' + outputFile));
                }
                else {
                    output.writeln(dotOutput);
                }
                return { success: true };
            }
            // Text format (default)
            output.printBox([
                `Files analyzed: ${result.statistics.nodeCount}`,
                `Dependencies: ${result.statistics.edgeCount}`,
                `Avg degree: ${result.statistics.avgDegree.toFixed(2)}`,
                `Density: ${(result.statistics.density * 100).toFixed(2)}%`,
                `Components: ${result.statistics.componentCount}`,
            ].join('\n'), 'Graph Statistics');
            if (result.boundaries && result.boundaries.length > 0) {
                output.writeln();
                output.writeln(output.bold('MinCut Boundaries'));
                output.writeln();
                for (let i = 0; i < result.boundaries.length; i++) {
                    const boundary = result.boundaries[i];
                    output.writeln(output.bold(`Boundary ${i + 1} (cut value: ${boundary.cutValue})`));
                    output.writeln();
                    output.writeln(output.dim('Partition 1:'));
                    const p1Display = boundary.partition1.slice(0, 10);
                    output.printList(p1Display);
                    if (boundary.partition1.length > 10) {
                        output.writeln(output.dim(`  ... and ${boundary.partition1.length - 10} more files`));
                    }
                    output.writeln();
                    output.writeln(output.dim('Partition 2:'));
                    const p2Display = boundary.partition2.slice(0, 10);
                    output.printList(p2Display);
                    if (boundary.partition2.length > 10) {
                        output.writeln(output.dim(`  ... and ${boundary.partition2.length - 10} more files`));
                    }
                    output.writeln();
                    output.writeln(output.success('Suggestion:'));
                    output.writeln(`  ${boundary.suggestion}`);
                    output.writeln();
                }
            }
            // Show circular dependencies
            if (result.circularDependencies.length > 0) {
                output.writeln();
                output.writeln(output.bold(output.warning('Circular Dependencies Detected')));
                output.writeln();
                for (const cycle of result.circularDependencies.slice(0, 5)) {
                    const severityColor = cycle.severity === 'high' ? output.error : cycle.severity === 'medium' ? output.warning : output.dim;
                    output.writeln(`${severityColor(`[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
                    output.writeln(output.dim(`  ${cycle.suggestion}`));
                    output.writeln();
                }
                if (result.circularDependencies.length > 5) {
                    output.writeln(output.dim(`... and ${result.circularDependencies.length - 5} more cycles`));
                }
            }
            if (outputFile) {
                await writeFile(outputFile, JSON.stringify(result, null, 2));
                output.printSuccess(`Full results written to ${outputFile}`);
            }
            return { success: true, data: result };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
/**
 * Analyze modules/communities using Louvain algorithm
 */
const modulesCommand = {
    name: 'modules',
    aliases: ['communities', 'louvain'],
    description: 'Detect module communities using Louvain algorithm',
    options: [
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json, dot)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json', 'dot'],
        },
        {
            name: 'min-size',
            short: 'm',
            description: 'Minimum community size to display',
            type: 'number',
            default: 2,
        },
    ],
    examples: [
        { command: 'claude-flow analyze modules src/', description: 'Detect module communities' },
        { command: 'claude-flow analyze modules -f dot -o modules.dot src/', description: 'Export colored DOT graph' },
        { command: 'claude-flow analyze modules -m 3 src/', description: 'Only show communities with 3+ files' },
    ],
    action: async (ctx) => {
        const targetDir = ctx.args[0] || ctx.cwd;
        const outputFile = ctx.flags.output;
        const format = ctx.flags.format || 'text';
        const minSize = ctx.flags['min-size'] || 2;
        output.printInfo(`Detecting module communities in: ${output.highlight(targetDir)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Building dependency graph...', spinner: 'dots' });
        spinner.start();
        try {
            const analyzer = await getGraphAnalyzer();
            if (!analyzer) {
                spinner.stop();
                output.printError('Graph analyzer module not available');
                return { success: false, exitCode: 1 };
            }
            const result = await analyzer.analyzeGraph(resolve(targetDir), {
                includeBoundaries: false,
                includeModules: true,
            });
            spinner.stop();
            // Filter communities by size
            const communities = result.communities?.filter(c => c.members.length >= minSize) || [];
            // Handle different output formats
            if (format === 'json') {
                const jsonOutput = {
                    communities,
                    statistics: result.statistics,
                };
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(jsonOutput, null, 2));
                    output.printSuccess(`Results written to ${outputFile}`);
                }
                else {
                    output.printJson(jsonOutput);
                }
                return { success: true, data: jsonOutput };
            }
            if (format === 'dot') {
                const dotOutput = analyzer.exportToDot(result, {
                    includeLabels: true,
                    colorByCommunity: true,
                    highlightCycles: true,
                });
                if (outputFile) {
                    await writeFile(outputFile, dotOutput);
                    output.printSuccess(`DOT graph written to ${outputFile}`);
                    output.writeln(output.dim('Visualize with: dot -Tpng -o modules.png ' + outputFile));
                }
                else {
                    output.writeln(dotOutput);
                }
                return { success: true };
            }
            // Text format (default)
            output.printBox([
                `Files analyzed: ${result.statistics.nodeCount}`,
                `Dependencies: ${result.statistics.edgeCount}`,
                `Communities found: ${result.communities?.length || 0}`,
                `Showing: ${communities.length} (min size: ${minSize})`,
            ].join('\n'), 'Module Detection Results');
            if (communities.length > 0) {
                output.writeln();
                output.writeln(output.bold('Detected Communities'));
                output.writeln();
                for (const community of communities.slice(0, 10)) {
                    const cohesionIndicator = community.cohesion > 0.5 ? output.success('High') :
                        community.cohesion > 0.2 ? output.warning('Medium') : output.dim('Low');
                    output.writeln(output.bold(`Community ${community.id}: ${community.suggestedName || 'unnamed'}`));
                    output.writeln(`  ${output.dim('Cohesion:')} ${cohesionIndicator} (${(community.cohesion * 100).toFixed(1)}%)`);
                    output.writeln(`  ${output.dim('Central node:')} ${community.centralNode || 'none'}`);
                    output.writeln(`  ${output.dim('Members:')} ${community.members.length} files`);
                    const displayMembers = community.members.slice(0, 5);
                    for (const member of displayMembers) {
                        output.writeln(`    - ${member}`);
                    }
                    if (community.members.length > 5) {
                        output.writeln(output.dim(`    ... and ${community.members.length - 5} more`));
                    }
                    output.writeln();
                }
                if (communities.length > 10) {
                    output.writeln(output.dim(`... and ${communities.length - 10} more communities`));
                }
            }
            if (outputFile) {
                await writeFile(outputFile, JSON.stringify(result, null, 2));
                output.printSuccess(`Full results written to ${outputFile}`);
            }
            return { success: true, data: result };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
/**
 * Build and export dependency graph
 */
const dependenciesCommand = {
    name: 'dependencies',
    aliases: ['graph'],
    description: 'Build and export full dependency graph',
    options: [
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json, dot)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json', 'dot'],
        },
        {
            name: 'include',
            short: 'i',
            description: 'File extensions to include (comma-separated)',
            type: 'string',
            default: '.ts,.tsx,.js,.jsx,.mjs,.cjs',
        },
        {
            name: 'exclude',
            short: 'e',
            description: 'Patterns to exclude (comma-separated)',
            type: 'string',
            default: 'node_modules,dist,build,.git',
        },
        {
            name: 'depth',
            short: 'd',
            description: 'Maximum directory depth',
            type: 'number',
            default: 10,
        },
    ],
    examples: [
        { command: 'claude-flow analyze dependencies src/', description: 'Build dependency graph' },
        { command: 'claude-flow analyze dependencies -f dot -o deps.dot src/', description: 'Export to DOT' },
        { command: 'claude-flow analyze dependencies -i .ts,.tsx src/', description: 'Only TypeScript files' },
    ],
    action: async (ctx) => {
        const targetDir = ctx.args[0] || ctx.cwd;
        const outputFile = ctx.flags.output;
        const format = ctx.flags.format || 'text';
        const include = (ctx.flags.include || '.ts,.tsx,.js,.jsx,.mjs,.cjs').split(',');
        const exclude = (ctx.flags.exclude || 'node_modules,dist,build,.git').split(',');
        const maxDepth = ctx.flags.depth || 10;
        output.printInfo(`Building dependency graph for: ${output.highlight(targetDir)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Scanning files...', spinner: 'dots' });
        spinner.start();
        try {
            const analyzer = await getGraphAnalyzer();
            if (!analyzer) {
                spinner.stop();
                output.printError('Graph analyzer module not available');
                return { success: false, exitCode: 1 };
            }
            const graph = await analyzer.buildDependencyGraph(resolve(targetDir), {
                include,
                exclude,
                maxDepth,
            });
            spinner.stop();
            // Detect circular dependencies
            const circularDeps = analyzer.detectCircularDependencies(graph);
            // Handle different output formats
            if (format === 'json') {
                const jsonOutput = {
                    nodes: Array.from(graph.nodes.values()),
                    edges: graph.edges,
                    metadata: graph.metadata,
                    circularDependencies: circularDeps,
                };
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(jsonOutput, null, 2));
                    output.printSuccess(`Graph written to ${outputFile}`);
                }
                else {
                    output.printJson(jsonOutput);
                }
                return { success: true, data: jsonOutput };
            }
            if (format === 'dot') {
                const result = { graph, circularDependencies: circularDeps, statistics: {
                        nodeCount: graph.nodes.size,
                        edgeCount: graph.edges.length,
                        avgDegree: 0,
                        maxDegree: 0,
                        density: 0,
                        componentCount: 0,
                    } };
                const dotOutput = analyzer.exportToDot(result, {
                    includeLabels: true,
                    highlightCycles: true,
                });
                if (outputFile) {
                    await writeFile(outputFile, dotOutput);
                    output.printSuccess(`DOT graph written to ${outputFile}`);
                    output.writeln(output.dim('Visualize with: dot -Tpng -o deps.png ' + outputFile));
                }
                else {
                    output.writeln(dotOutput);
                }
                return { success: true };
            }
            // Text format (default)
            output.printBox([
                `Files: ${graph.metadata.totalFiles}`,
                `Dependencies: ${graph.metadata.totalEdges}`,
                `Build time: ${graph.metadata.buildTime}ms`,
                `Root: ${graph.metadata.rootDir}`,
            ].join('\n'), 'Dependency Graph');
            // Show top files by imports
            output.writeln();
            output.writeln(output.bold('Most Connected Files'));
            output.writeln();
            const nodesByDegree = Array.from(graph.nodes.values())
                .map(n => ({
                ...n,
                degree: graph.edges.filter(e => e.source === n.id || e.target === n.id).length,
            }))
                .sort((a, b) => b.degree - a.degree)
                .slice(0, 10);
            output.printTable({
                columns: [
                    { key: 'path', header: 'File', width: 50 },
                    { key: 'degree', header: 'Connections', width: 12, align: 'right' },
                    { key: 'complexity', header: 'Complexity', width: 12, align: 'right' },
                ],
                data: nodesByDegree.map(n => ({
                    path: n.path.length > 48 ? '...' + n.path.slice(-45) : n.path,
                    degree: n.degree,
                    complexity: n.complexity || 0,
                })),
            });
            // Show circular dependencies
            if (circularDeps.length > 0) {
                output.writeln();
                output.writeln(output.bold(output.warning(`Circular Dependencies: ${circularDeps.length}`)));
                output.writeln();
                for (const cycle of circularDeps.slice(0, 3)) {
                    output.writeln(`  ${cycle.cycle.join(' -> ')}`);
                }
                if (circularDeps.length > 3) {
                    output.writeln(output.dim(`  ... and ${circularDeps.length - 3} more`));
                }
            }
            if (outputFile) {
                const fullOutput = {
                    nodes: Array.from(graph.nodes.values()),
                    edges: graph.edges,
                    metadata: graph.metadata,
                    circularDependencies: circularDeps,
                };
                await writeFile(outputFile, JSON.stringify(fullOutput, null, 2));
                output.printSuccess(`Full results written to ${outputFile}`);
            }
            return { success: true };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
/**
 * Detect circular dependencies
 */
const circularCommand = {
    name: 'circular',
    aliases: ['cycles'],
    description: 'Detect circular dependencies in codebase',
    options: [
        {
            name: 'output',
            short: 'o',
            description: 'Output file path',
            type: 'string',
        },
        {
            name: 'format',
            short: 'f',
            description: 'Output format (text, json)',
            type: 'string',
            default: 'text',
            choices: ['text', 'json'],
        },
        {
            name: 'severity',
            short: 's',
            description: 'Minimum severity to show (low, medium, high)',
            type: 'string',
            default: 'low',
            choices: ['low', 'medium', 'high'],
        },
    ],
    examples: [
        { command: 'claude-flow analyze circular src/', description: 'Find circular dependencies' },
        { command: 'claude-flow analyze circular -s high src/', description: 'Only high severity cycles' },
    ],
    action: async (ctx) => {
        const targetDir = ctx.args[0] || ctx.cwd;
        const outputFile = ctx.flags.output;
        const format = ctx.flags.format || 'text';
        const minSeverity = ctx.flags.severity || 'low';
        output.printInfo(`Detecting circular dependencies in: ${output.highlight(targetDir)}`);
        output.writeln();
        const spinner = output.createSpinner({ text: 'Analyzing dependencies...', spinner: 'dots' });
        spinner.start();
        try {
            const analyzer = await getGraphAnalyzer();
            if (!analyzer) {
                spinner.stop();
                output.printError('Graph analyzer module not available');
                return { success: false, exitCode: 1 };
            }
            const graph = await analyzer.buildDependencyGraph(resolve(targetDir));
            const cycles = analyzer.detectCircularDependencies(graph);
            spinner.stop();
            // Filter by severity
            const severityOrder = { low: 0, medium: 1, high: 2 };
            const minLevel = severityOrder[minSeverity] || 0;
            const filtered = cycles.filter(c => severityOrder[c.severity] >= minLevel);
            if (format === 'json') {
                const jsonOutput = { cycles: filtered, total: cycles.length, filtered: filtered.length };
                if (outputFile) {
                    await writeFile(outputFile, JSON.stringify(jsonOutput, null, 2));
                    output.printSuccess(`Results written to ${outputFile}`);
                }
                else {
                    output.printJson(jsonOutput);
                }
                return { success: true, data: jsonOutput };
            }
            // Text format
            if (filtered.length === 0) {
                output.printSuccess('No circular dependencies found!');
                return { success: true };
            }
            output.printBox([
                `Total cycles: ${cycles.length}`,
                `Shown (${minSeverity}+): ${filtered.length}`,
                `High severity: ${cycles.filter(c => c.severity === 'high').length}`,
                `Medium severity: ${cycles.filter(c => c.severity === 'medium').length}`,
                `Low severity: ${cycles.filter(c => c.severity === 'low').length}`,
            ].join('\n'), 'Circular Dependencies');
            output.writeln();
            // Group by severity
            const grouped = {
                high: filtered.filter(c => c.severity === 'high'),
                medium: filtered.filter(c => c.severity === 'medium'),
                low: filtered.filter(c => c.severity === 'low'),
            };
            for (const [severity, items] of Object.entries(grouped)) {
                if (items.length === 0)
                    continue;
                const color = severity === 'high' ? output.error : severity === 'medium' ? output.warning : output.dim;
                output.writeln(color(output.bold(`${severity.toUpperCase()} SEVERITY (${items.length})`)));
                output.writeln();
                for (const cycle of items.slice(0, 5)) {
                    output.writeln(`  ${cycle.cycle.join(' -> ')}`);
                    output.writeln(output.dim(`  Fix: ${cycle.suggestion}`));
                    output.writeln();
                }
                if (items.length > 5) {
                    output.writeln(output.dim(`  ... and ${items.length - 5} more ${severity} cycles`));
                    output.writeln();
                }
            }
            if (outputFile) {
                await writeFile(outputFile, JSON.stringify({ cycles: filtered }, null, 2));
                output.printSuccess(`Results written to ${outputFile}`);
            }
            return { success: true, data: { cycles: filtered } };
        }
        catch (error) {
            spinner.stop();
            const message = error instanceof Error ? error.message : String(error);
            output.printError(`Analysis failed: ${message}`);
            return { success: false, exitCode: 1 };
        }
    },
};
// Helper functions
function getRiskDisplay(risk) {
    switch (risk) {
        case 'critical':
            return output.color(output.bold('CRITICAL'), 'bgRed', 'white');
        case 'high-risk':
            return output.error('HIGH');
        case 'medium-risk':
            return output.warning('MEDIUM');
        case 'low-risk':
            return output.success('LOW');
        default:
            return risk;
    }
}
function getStatusDisplay(status) {
    switch (status) {
        case 'added':
            return output.success('A');
        case 'modified':
            return output.warning('M');
        case 'deleted':
            return output.error('D');
        case 'renamed':
            return output.info('R');
        default:
            return status;
    }
}
// Main analyze command
export const analyzeCommand = {
    name: 'analyze',
    description: 'Code analysis, diff classification, graph boundaries, and change risk assessment',
    aliases: ['an'],
    subcommands: [
        diffCommand,
        codeCommand,
        depsCommand,
        astCommand,
        complexityAstCommand,
        symbolsCommand,
        importsCommand,
        boundariesCommand,
        modulesCommand,
        dependenciesCommand,
        circularCommand,
    ],
    options: [
        {
            name: 'format',
            short: 'f',
            description: 'Output format: text, json, table',
            type: 'string',
            default: 'text',
        },
    ],
    examples: [
        { command: 'claude-flow analyze ast src/', description: 'Analyze code with AST parsing' },
        { command: 'claude-flow analyze complexity src/ --threshold 15', description: 'Find high-complexity files' },
        { command: 'claude-flow analyze symbols src/ --type function', description: 'Extract all functions' },
        { command: 'claude-flow analyze imports src/ --external', description: 'List npm dependencies' },
        { command: 'claude-flow analyze diff --risk', description: 'Analyze diff with risk assessment' },
        { command: 'claude-flow analyze boundaries src/', description: 'Find code boundaries using MinCut' },
        { command: 'claude-flow analyze modules src/', description: 'Detect module communities with Louvain' },
        { command: 'claude-flow analyze dependencies src/ --format dot', description: 'Export dependency graph as DOT' },
        { command: 'claude-flow analyze circular src/', description: 'Find circular dependencies' },
        { command: 'claude-flow analyze deps --security', description: 'Check dependency vulnerabilities' },
    ],
    action: async (ctx) => {
        // If no subcommand, show help
        output.writeln();
        output.writeln(output.bold('Analyze Commands'));
        output.writeln(output.dim('-'.repeat(50)));
        output.writeln();
        output.writeln(output.bold('Available subcommands:'));
        output.writeln();
        output.writeln(`  ${output.highlight('diff')}         Analyze git diff for change risk and classification`);
        output.writeln(`  ${output.highlight('code')}         Static code analysis and quality assessment`);
        output.writeln(`  ${output.highlight('deps')}         Analyze project dependencies`);
        output.writeln(`  ${output.highlight('ast')}          AST analysis with symbol extraction and complexity`);
        output.writeln(`  ${output.highlight('complexity')}   Analyze cyclomatic and cognitive complexity`);
        output.writeln(`  ${output.highlight('symbols')}      Extract functions, classes, and types`);
        output.writeln(`  ${output.highlight('imports')}      Analyze import dependencies`);
        output.writeln(`  ${output.highlight('boundaries')}   Find code boundaries using MinCut algorithm`);
        output.writeln(`  ${output.highlight('modules')}      Detect module communities using Louvain algorithm`);
        output.writeln(`  ${output.highlight('dependencies')} Build and export full dependency graph`);
        output.writeln(`  ${output.highlight('circular')}     Detect circular dependencies in codebase`);
        output.writeln();
        output.writeln(output.bold('AST Analysis Examples:'));
        output.writeln();
        output.writeln(`  ${output.dim('claude-flow analyze ast src/')}                  # Full AST analysis`);
        output.writeln(`  ${output.dim('claude-flow analyze ast src/index.ts -c')}       # Include complexity`);
        output.writeln(`  ${output.dim('claude-flow analyze complexity src/ -t 15')}     # Flag high complexity`);
        output.writeln(`  ${output.dim('claude-flow analyze symbols src/ --type fn')}    # Extract functions`);
        output.writeln(`  ${output.dim('claude-flow analyze imports src/ --external')}   # Only npm imports`);
        output.writeln();
        output.writeln(output.bold('Graph Analysis Examples:'));
        output.writeln();
        output.writeln(`  ${output.dim('claude-flow analyze boundaries src/')}            # Find natural code boundaries`);
        output.writeln(`  ${output.dim('claude-flow analyze modules src/')}               # Detect module communities`);
        output.writeln(`  ${output.dim('claude-flow analyze dependencies -f dot src/')}   # Export to DOT format`);
        output.writeln(`  ${output.dim('claude-flow analyze circular src/')}              # Find circular deps`);
        output.writeln();
        output.writeln(output.bold('Diff Analysis Examples:'));
        output.writeln();
        output.writeln(`  ${output.dim('claude-flow analyze diff --risk')}              # Risk assessment`);
        output.writeln(`  ${output.dim('claude-flow analyze diff HEAD~1 --classify')}   # Classify changes`);
        output.writeln(`  ${output.dim('claude-flow analyze diff main..feature')}       # Compare branches`);
        output.writeln();
        return { success: true };
    },
};
export default analyzeCommand;
//# sourceMappingURL=analyze.js.map