@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
JavaScript
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;