Skip to main content
Glama
code-integrity-checker.js9.33 kB
// 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();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bermingham85/mcp-puppet-pipeline'

If you have feedback or need assistance with the MCP directory API, please join our Discord server