UNPKG

@deriv-com/shiftai-cli

Version:

A comprehensive AI code detection and analysis CLI tool for tracking AI-generated code in projects

219 lines (190 loc) 6.89 kB
const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); const display = require('../utils/display'); const AIDetector = require('../core/ai-detector'); const LocalStorage = require('../core/local-storage'); /** * Analytics Command - View AI code usage analytics */ async function analyticsCommand(options = {}) { try { display.clearAndShowHeader('ShiftAI Analytics', 'AI Code Usage Analysis'); const days = parseInt(options.days) || 30; const format = options.format || 'table'; const analyticsData = await getLocalAnalytics(); if (!analyticsData) { console.log(display.warning('No analytics data available', 'Run some commits with AI code to generate analytics')); return; } // Display based on format switch (format.toLowerCase()) { case 'json': console.log(JSON.stringify(analyticsData, null, 2)); break; case 'csv': displayCSVAnalytics(analyticsData); break; case 'table': default: displayTableAnalytics(analyticsData); break; } } catch (error) { console.log(display.error('Analytics failed', error.message)); process.exit(1); } } /** * Get analytics from local files and storage */ async function getLocalAnalytics() { const spinner = display.spinner('Analyzing local files...'); spinner.start(); try { const cwd = process.cwd(); const detector = new AIDetector(); // Get all text-like files (excluding binary extensions) const patterns = [ '**/*', // All files '!**/*.{png,jpg,jpeg,gif,svg,ico,bmp,tiff,webp}', // Exclude images '!**/*.{mp3,mp4,avi,mov,wmv,flv,webm,ogg,wav}', // Exclude media '!**/*.{zip,rar,tar,gz,7z,pdf,doc,docx,xls,xlsx}', // Exclude archives/docs '!**/*.{exe,dll,so,dylib,bin}', // Exclude binaries '!**/*.json', // Exclude JSON files (data/config files) '!**/node_modules/**', '!**/.git/**', '!**/dist/**', '!**/build/**', '!**/coverage/**', '!**/*.min.*', // Exclude minified files '!**/.next/**', '!**/package-lock.json', // Exclude package lock files '!**/yarn.lock', // Exclude yarn lock files '!**/pnpm-lock.yaml' // Exclude pnpm lock files ]; const files = glob.sync(patterns, { cwd, dot: true // Include dotfiles }); spinner.text = `Scanning ${files.length} files...`; // Scan files for AI code const scanResults = []; for (const file of files) { const filePath = path.join(cwd, file); try { const result = await detector.scanFile(filePath); if (result.supported) { scanResults.push(result); } } catch (error) { // Skip files with errors } } // Generate analytics const analytics = detector.generateAnalytics(scanResults); // Add file details const filesWithAI = scanResults.filter(r => r.hasAICode); spinner.succeed(`Analyzed ${scanResults.length} files`); return { summary: analytics, languageBreakdown: analytics.languageBreakdown, files: filesWithAI.map(file => ({ path: file.filePath.replace(cwd + path.sep, ''), language: file.language, blockCount: file.blocks.length, blocks: file.blocks.map(block => ({ startLine: block.startLine, endLine: block.endLine, linesOfCode: block.code.split('\n').length })) })), timestamp: new Date().toISOString(), source: 'local' }; } catch (error) { spinner.fail('Local analysis failed'); throw error; } } /** * Display analytics in table format */ function displayTableAnalytics(data) { // Summary statistics console.log(display.summary('Summary Statistics', { 'Total Files': data.summary.totalFiles || data.files?.length || 0, 'Files with AI Code': data.summary.filesWithAI || data.files?.filter(f => f.blockCount > 0).length || 0, 'Total AI Blocks': data.summary.totalAIBlocks || data.files?.reduce((sum, f) => sum + f.blockCount, 0) || 0, 'Supported Files': data.summary.supportedFiles || data.summary.totalFiles || 0 })); // Language breakdown if (data.languageBreakdown && Object.keys(data.languageBreakdown).length > 0) { const headers = ['Language', 'Files', 'Blocks', 'Lines of Code']; const rows = Object.entries(data.languageBreakdown).map(([lang, stats]) => [ lang, stats.files, stats.blocks, stats.linesOfCode ]); console.log('\n' + display.table(headers, rows, { title: 'Language Breakdown', style: 'modern' })); } // File details if (data.files && data.files.length > 0) { const fileHeaders = ['File Path', 'Language', 'AI Blocks', 'Total Lines']; const fileRows = data.files.slice(0, 20).map(file => [ file.path, file.language || 'Unknown', file.blockCount, file.blocks ? file.blocks.reduce((sum, block) => sum + block.linesOfCode, 0) : 0 ]); console.log('\n' + display.table(fileHeaders, fileRows, { title: `Files with AI Code ${data.files.length > 20 ? '(Top 20)' : ''}`, style: 'compact' })); if (data.files.length > 20) { console.log(display.info('Note:', `Showing top 20 of ${data.files.length} files with AI code`)); } } // Show data source and timestamp const sourceInfo = data.source === 'local' ? 'Local Analysis' : 'Remote Repository'; console.log(display.info('Data Source:', `${sourceInfo} • Generated: ${new Date(data.timestamp).toLocaleString()}`)); } /** * Display analytics in CSV format */ function displayCSVAnalytics(data) { console.log('Analytics Type,Value'); console.log(`Total Files,${data.summary.totalFiles || 0}`); console.log(`Files with AI Code,${data.summary.filesWithAI || 0}`); console.log(`Total AI Blocks,${data.summary.totalAIBlocks || 0}`); console.log(`Supported Files,${data.summary.supportedFiles || 0}`); console.log('\nLanguage,Files,Blocks,Lines of Code'); if (data.languageBreakdown) { Object.entries(data.languageBreakdown).forEach(([lang, stats]) => { console.log(`${lang},${stats.files},${stats.blocks},${stats.linesOfCode}`); }); } console.log('\nFile Path,Language,AI Blocks,Total Lines'); if (data.files) { data.files.forEach(file => { const totalLines = file.blocks ? file.blocks.reduce((sum, block) => sum + block.linesOfCode, 0) : 0; console.log(`"${file.path}","${file.language || 'Unknown'}",${file.blockCount},${totalLines}`); }); } } /** * Get current git branch */ function getCurrentBranch() { try { const { execSync } = require('child_process'); return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim(); } catch (error) { return 'main'; } } module.exports = analyticsCommand;