Elysia-Suite's picture
Upload 24 files
d58d97b verified
raw
history blame
8.34 kB
/*
ELYSIA CODE COMPANION v1.2.2 - Code Analyzer
Static analysis tools for code insights
*/
import FileSystem from "./filesystem.js";
import Utils from "./utils.js";
const Analyzer = {
// Analyze Entire Project
analyzeProject() {
const stats = FileSystem.getStats();
const tree = FileSystem.buildTree();
const analysis = {
summary: {
name: FileSystem.folderName,
totalFiles: stats.totalFiles,
totalSize: Utils.formatFileSize(stats.totalSize),
languages: stats.languages,
fileTypes: stats.fileTypes
},
structure: tree,
insights: []
};
// Generate insights
if (stats.totalFiles === 0) {
analysis.insights.push({
type: "warning",
message: "No files found in this folder"
});
}
if (stats.totalFiles > 500) {
analysis.insights.push({
type: "info",
message: `Large project detected (${stats.totalFiles} files). Consider analyzing specific files instead of the whole project.`
});
}
// Check for common patterns
const hasPackageJson = FileSystem.files.some(f => f.name === "package.json");
const hasRequirementsTxt = FileSystem.files.some(f => f.name === "requirements.txt");
const hasCargoToml = FileSystem.files.some(f => f.name === "Cargo.toml");
if (hasPackageJson) {
analysis.insights.push({
type: "success",
message: "JavaScript/Node.js project detected (package.json found)"
});
}
if (hasRequirementsTxt) {
analysis.insights.push({
type: "success",
message: "Python project detected (requirements.txt found)"
});
}
if (hasCargoToml) {
analysis.insights.push({
type: "success",
message: "Rust project detected (Cargo.toml found)"
});
}
return analysis;
},
// Analyze Single File
async analyzeFile(filePath) {
const fileEntry = FileSystem.getFileByPath(filePath);
if (!fileEntry) {
throw new Error("File not found");
}
const content = await FileSystem.readFile(fileEntry);
const analysis = {
name: fileEntry.name,
path: filePath,
size: Utils.formatFileSize(fileEntry.size),
extension: fileEntry.extension,
language: fileEntry.language,
lines: content.split("\n").length,
content,
insights: []
};
// Code-specific analysis
if (Utils.isCodeFile(fileEntry.name)) {
// Count lines of code (non-empty, non-comment)
const codeLines = content.split("\n").filter(line => {
const trimmed = line.trim();
return trimmed.length > 0 && !trimmed.startsWith("//") && !trimmed.startsWith("#");
});
analysis.linesOfCode = codeLines.length;
// Detect TODO/FIXME comments (optimized regex)
const todoRegex = /(?:TODO|FIXME|HACK|XXX|NOTE):/gi;
const todos = content.match(todoRegex);
if (todos && todos.length > 0) {
analysis.insights.push({
type: "info",
message: `Found ${todos.length} TODO/FIXME comment(s)`
});
}
// Check file size
if (fileEntry.size > 1024 * 100) {
// 100KB
analysis.insights.push({
type: "warning",
message: "Large file detected - consider splitting into smaller modules"
});
}
// Check for very long lines
const longLines = content.split("\n").filter(line => line.length > 120);
if (longLines.length > 5) {
analysis.insights.push({
type: "info",
message: `${longLines.length} lines exceed 120 characters - consider refactoring for readability`
});
}
}
return analysis;
},
// Generate Project Tree (Text Format)
generateTreeText(tree, indent = "", isRoot = true) {
let text = "";
// Only show root node name
if (isRoot && tree.name) {
const icon = tree.type === "directory" ? "πŸ“" : "πŸ“„";
text += `${icon} ${tree.name}\n`;
}
if (tree.children) {
tree.children.forEach((child, index) => {
const isLast = index === tree.children.length - 1;
const prefix = indent + (isLast ? "└─ " : "β”œβ”€ ");
const childIndent = indent + (isLast ? " " : "β”‚ ");
const icon = child.type === "directory" ? "πŸ“" : "πŸ“„";
text += `${prefix}${icon} ${child.name}\n`;
// Recursively process children
if (child.children && child.children.length > 0) {
text += this.generateTreeText(child, childIndent, false);
}
});
}
return text;
},
// Get Files for AI Context (smart selection)
async getContextFiles(query = "", maxFiles = 5) {
const mentionedFiles = [];
const configFiles = [];
// PRIORITY 1: Files explicitly mentioned in query (highest relevance)
if (query) {
const mentioned = FileSystem.files.filter(
f =>
query.toLowerCase().includes(f.name.toLowerCase()) ||
query.toLowerCase().includes(f.path.toLowerCase())
);
mentionedFiles.push(...mentioned);
}
// PRIORITY 2: Important config files (context)
const configs = FileSystem.files.filter(f =>
[
"package.json",
"tsconfig.json",
"requirements.txt",
"Cargo.toml",
"README.md",
".env.example",
"docker-compose.yml"
].includes(f.name)
);
configFiles.push(...configs);
// Merge with priority: mentioned files FIRST, then config files
const relevantFilesMap = new Map();
mentionedFiles.forEach(f => relevantFilesMap.set(f.path, f)); // Priority 1
configFiles.forEach(f => {
if (!relevantFilesMap.has(f.path)) {
// Don't add duplicates
relevantFilesMap.set(f.path, f); // Priority 2
}
});
// Convert Map to array and limit
const relevantFiles = Array.from(relevantFilesMap.values());
const selectedFiles = relevantFiles.slice(0, maxFiles);
const filesWithContent = await Promise.all(
selectedFiles.map(async file => {
try {
const content = await FileSystem.readFile(file);
return {
name: file.name,
path: file.path,
language: file.language,
content: content.substring(0, 5000) // Limit to 5000 chars per file
};
} catch (err) {
console.error(`Failed to read ${file.path}:`, err);
return null;
}
})
);
return filesWithContent.filter(f => f !== null);
},
// Parse Command from User Input
parseCommand(input) {
const trimmed = input.trim();
if (!trimmed.startsWith("/")) {
return { type: "message", content: trimmed };
}
const parts = trimmed.substring(1).split(" ");
const command = parts[0].toLowerCase();
const args = parts.slice(1).join(" ");
return { type: "command", command, args };
}
};
export default Analyzer;