// CODE INTEGRITY CHECKER AGENT
// Validates code before directory changes to prevent breaking imports
// This agent works automatically before any file move
import { promises as fs } from 'fs';
import path from 'path';
import { configManager } from '../config-manager.js';
import { actionLogger } from '../logging/agent-action-logger.js';
export class CodeIntegrityChecker {
constructor() {
this.name = 'CodeIntegrityChecker';
this.role = 'Validate code integrity before changes';
}
// Main method: Check if moving a file will break code
async validateMove(sourcePath, destPath) {
console.log(`🔍 ${this.name}: Validating move...`);
console.log(` From: ${sourcePath}`);
console.log(` To: ${destPath}`);
await actionLogger.logAction('integrity_check', {
description: 'Code integrity validation',
sourcePath,
destPath
});
const issues = [];
// Check 1: Find all files that import this file
const importers = await this.findImporters(sourcePath);
if (importers.length > 0) {
issues.push({
type: 'import_references',
severity: 'high',
message: `${importers.length} files import this file`,
files: importers,
fix: 'Update import paths in these files'
});
}
// Check 2: Validate imports within the file being moved
const brokenImports = await this.checkImportsInFile(sourcePath, destPath);
if (brokenImports.length > 0) {
issues.push({
type: 'broken_imports',
severity: 'high',
message: `${brokenImports.length} imports will break`,
imports: brokenImports,
fix: 'Update import paths in the moved file'
});
}
// Check 3: Check config references
const configRefs = await this.checkConfigReferences(sourcePath);
if (configRefs) {
issues.push({
type: 'config_reference',
severity: 'medium',
message: 'File referenced in config.json',
fix: 'Update config.json paths'
});
}
const result = {
safe: issues.length === 0,
issues: issues,
recommendation: issues.length === 0
? 'Safe to move'
: 'Fix issues before moving or accept automated fixes'
};
console.log(` ${result.safe ? '✅' : '⚠️'} ${result.recommendation}`);
return result;
}
// Find all files that import the target file
async findImporters(targetPath) {
const config = await configManager.initialize();
const searchPaths = [
config.paths.root,
config.paths.aiProjects
];
const importers = [];
const targetFileName = path.basename(targetPath);
for (const searchPath of searchPaths) {
const files = await this.findJSFiles(searchPath);
for (const file of files) {
try {
const content = await fs.readFile(file, 'utf-8');
// Check if file imports target
if (this.importsFile(content, targetFileName, targetPath)) {
importers.push(file);
}
} catch (error) {
// Skip files that can't be read
}
}
}
return importers;
}
// Check if file imports another file
importsFile(content, fileName, fullPath) {
const importPatterns = [
new RegExp(`import.*from\\s+['"](.*${fileName}.*?)['"]`, 'g'),
new RegExp(`require\\(['"](.*${fileName}.*?)['"]\\)`, 'g')
];
for (const pattern of importPatterns) {
if (pattern.test(content)) {
return true;
}
}
return false;
}
// Check imports within the file that's being moved
async checkImportsInFile(sourcePath, destPath) {
const brokenImports = [];
try {
const content = await fs.readFile(sourcePath, 'utf-8');
const imports = this.extractImports(content);
const sourceDir = path.dirname(sourcePath);
const destDir = path.dirname(destPath);
for (const imp of imports) {
// Check if relative import will break
if (imp.startsWith('.')) {
const resolvedPath = path.resolve(sourceDir, imp);
const newRelativePath = path.relative(destDir, resolvedPath);
if (newRelativePath !== imp) {
brokenImports.push({
old: imp,
new: newRelativePath.replace(/\\/g, '/')
});
}
}
}
} catch (error) {
// File doesn't exist or can't be read
}
return brokenImports;
}
// Extract import statements from code
extractImports(content) {
const imports = [];
// Match ES6 imports
const es6Pattern = /import\s+.*?\s+from\s+['"](.*?)['"]/g;
let match;
while ((match = es6Pattern.exec(content)) !== null) {
imports.push(match[1]);
}
// Match CommonJS requires
const cjsPattern = /require\(['"](.*?)['"]\)/g;
while ((match = cjsPattern.exec(content)) !== null) {
imports.push(match[1]);
}
return imports;
}
// Check if file is referenced in config
async checkConfigReferences(filePath) {
const config = await configManager.initialize();
const configStr = JSON.stringify(config);
const fileName = path.basename(filePath);
return configStr.includes(fileName);
}
// Find all JS/TS files recursively
async findJSFiles(dir) {
const files = [];
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
files.push(...await this.findJSFiles(fullPath));
} else if (entry.isFile() && /\.(js|ts)$/.test(entry.name)) {
files.push(fullPath);
}
}
} catch (error) {
// Skip directories we can't read
}
return files;
}
// Auto-fix broken imports
async autoFix(sourcePath, destPath, issues) {
console.log(`🔧 ${this.name}: Applying automatic fixes...`);
const sessionId = await actionLogger.startSession(this.name, 'Auto-fix code integrity issues');
try {
// Fix imports in files that reference the moved file
const importIssue = issues.find(i => i.type === 'import_references');
if (importIssue) {
for (const file of importIssue.files) {
await this.updateImportPath(file, sourcePath, destPath);
}
}
// Fix imports within the moved file
const brokenIssue = issues.find(i => i.type === 'broken_imports');
if (brokenIssue) {
await this.fixFileImports(destPath, brokenIssue.imports);
}
await actionLogger.endSession('completed');
console.log(`✅ ${this.name}: Fixes applied`);
return { success: true };
} catch (error) {
await actionLogger.endSession('failed');
console.error(`❌ ${this.name}: Fix failed:`, error);
throw error;
}
}
// Update import path in a file
async updateImportPath(filePath, oldPath, newPath) {
const content = await fs.readFile(filePath, 'utf-8');
const oldRelative = path.relative(path.dirname(filePath), oldPath).replace(/\\/g, '/');
const newRelative = path.relative(path.dirname(filePath), newPath).replace(/\\/g, '/');
const updated = content.replace(
new RegExp(oldRelative, 'g'),
newRelative
);
await fs.writeFile(filePath, updated);
console.log(` Updated: ${filePath}`);
}
// Fix imports within a moved file
async fixFileImports(filePath, brokenImports) {
let content = await fs.readFile(filePath, 'utf-8');
for (const imp of brokenImports) {
content = content.replace(imp.old, imp.new);
}
await fs.writeFile(filePath, content);
console.log(` Fixed imports in: ${filePath}`);
}
}
export const codeIntegrityChecker = new CodeIntegrityChecker();