trace.ai-cli
Version:
A powerful AI-powered CLI tool
164 lines (138 loc) • 5.89 kB
JavaScript
const fs = require('fs').promises;
const path = require('path');
const { getFileType } = require('../utils/fileUtils');
const { processWithAI } = require('./aiService');
function buildSimpleFileTree(folderPath, items, statsCache, depth = 0) {
let tree = '';
const indent = ' '.repeat(depth);
for (const item of items) {
const itemPath = path.join(folderPath, item);
const stats = statsCache.get(itemPath);
tree += `${indent}${item}${stats.isDirectory() ? '/' : ''}\n`;
if (stats.isDirectory()) {
try {
const subItems = fs.readdirSync(itemPath);
const subStatsCache = new Map();
for (const subItem of subItems) {
const subItemPath = path.join(itemPath, subItem);
subStatsCache.set(subItemPath, fs.statSync(subItemPath));
}
tree += buildSimpleFileTree(itemPath, subItems, subStatsCache, depth + 1);
} catch (error) {
tree += `${indent} (error accessing directory)\n`;
}
}
}
return tree;
}
async function analyzeFolder(folderPath, maxDepth = 2, currentDepth = 0) {
try {
const stats = await fs.stat(folderPath);
if (!stats.isDirectory()) {
throw new Error('Path is not a directory');
}
const analysis = {
path: folderPath,
files: [],
folders: [],
fileTree: '', // Store simple file tree
summary: {
totalFiles: 0,
totalFolders: 0,
fileTypes: {},
languages: {},
size: 0
}
};
const items = await fs.readdir(folderPath);
const statsCache = new Map();
// Cache stats for all items
for (const item of items) {
const itemPath = path.join(folderPath, item);
statsCache.set(itemPath, await fs.stat(itemPath));
}
// Build simple file tree
analysis.fileTree = buildSimpleFileTree(folderPath, items, statsCache);
for (const item of items) {
const itemPath = path.join(folderPath, item);
const itemStats = statsCache.get(itemPath);
if (itemStats.isDirectory()) {
analysis.folders.push(item);
analysis.summary.totalFolders++;
if (currentDepth < maxDepth) {
try {
const subAnalysis = await analyzeFolder(itemPath, maxDepth, currentDepth + 1);
analysis.summary.totalFiles += subAnalysis.summary.totalFiles;
analysis.summary.totalFolders += subAnalysis.summary.totalFolders;
analysis.summary.size += subAnalysis.summary.size;
Object.keys(subAnalysis.summary.fileTypes).forEach(type => {
analysis.summary.fileTypes[type] = (analysis.summary.fileTypes[type] || 0) + subAnalysis.summary.fileTypes[type];
});
Object.keys(subAnalysis.summary.languages).forEach(lang => {
analysis.summary.languages[lang] = (analysis.summary.languages[lang] || 0) + subAnalysis.summary.languages[lang];
});
} catch (error) {
console.warn(`Warning: Cannot access folder ${itemPath}`);
}
}
} else {
const fileType = getFileType(itemPath);
analysis.files.push({
name: item,
type: fileType,
size: itemStats.size,
modified: itemStats.mtime
});
analysis.summary.totalFiles++;
analysis.summary.size += itemStats.size;
analysis.summary.fileTypes[fileType] = (analysis.summary.fileTypes[fileType] || 0) + 1;
if (fileType !== 'image' && fileType !== 'Unknown') {
analysis.summary.languages[fileType] = (analysis.summary.languages[fileType] || 0) + 1;
}
}
}
return analysis;
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`Folder not found: ${folderPath}`);
}
if (error.code === 'EACCES') {
throw new Error(`Access denied: ${folderPath}`);
}
throw error;
}
}
async function analyzeFolderStructure(folderPath, query = '') {
try {
const analysis = await analyzeFolder(folderPath);
const structureText = `
Folder: ${analysis.path}
Files: ${analysis.summary.totalFiles}
Folders: ${analysis.summary.totalFolders}
Total Size: ${(analysis.summary.size / 1024).toFixed(2)} KB
Files and Directories:
${analysis.fileTree}
File Types:
${Object.entries(analysis.summary.fileTypes).map(([type, count]) => `- ${type}: ${count}`).join('\n')}
Languages/Technologies:
${Object.entries(analysis.summary.languages).map(([lang, count]) => `- ${lang}: ${count} files`).join('\n')}
`;
let prompt;
if (query) {
prompt = `Project Structure:
${structureText}
User Question: ${query}`;
} else {
prompt = `Analyze this project structure:
${structureText}
Provide a brief overview of the project based on its file structure.`;
}
const result = await processWithAI(prompt);
return { text: result || 'No response generated' }; // Ensure text property is always defined
} catch (error) {
throw error;
}
}
module.exports = {
analyzeFolderStructure
};