Skip to main content
Glama
doc-organizer.js9.98 kB
// DOCUMENT ORGANIZER AGENT // Handles file naming standards and filing structure // Works with Document Control Agent to maintain organization import { promises as fs } from 'fs'; import path from 'path'; import { configManager } from '../../config-manager.js'; import { actionLogger } from '../../logging/agent-action-logger.js'; import { codeIntegrityChecker } from './code-integrity-checker.js'; export class DocumentOrganizer { constructor() { this.name = 'DocumentOrganizer'; this.role = 'File naming and organization standards'; // Naming standards this.namingRules = { agents: { pattern: '{type}-{name}.js', types: ['core', 'specialized'], example: 'core-doc-control.js' }, tools: { pattern: '{service}-{function}-tool.js', example: 'n8n-workflow-tool.js' }, workflows: { pattern: '{project}-{type}-workflow.json', example: 'production-master-workflow.json' }, logs: { pattern: '{category}_{date}.log', example: 'actions_2025-10-02.log' } }; // Filing structure this.filingRules = { 'agents/core': 'Reusable core agents', 'agents/specialized': 'Project-specific agents', 'tools': 'Integration and utility tools', 'workflows': 'N8N and automation workflows', 'logs/agent-actions': 'Agent action logs', 'logs/system': 'System logs', 'inventory': 'Agent and tool inventories', 'backups/daily': 'Automated daily backups', 'backups/manual': 'Manual backup snapshots' }; } // Organize a new file async organize(filePath, metadata = {}) { console.log(`📋 ${this.name}: Organizing file...`); const sessionId = await actionLogger.startSession( this.name, `Organize file: ${path.basename(filePath)}` ); try { // Step 1: Validate file exists const exists = await this.fileExists(filePath); if (!exists) { throw new Error(`File not found: ${filePath}`); } // Step 2: Determine file type const fileType = await this.determineFileType(filePath); console.log(` Type: ${fileType}`); // Step 3: Generate standard name const standardName = await this.generateStandardName(filePath, fileType, metadata); console.log(` Standard name: ${standardName}`); // Step 4: Determine correct location const targetLocation = await this.determineLocation(fileType, metadata); console.log(` Target location: ${targetLocation}`); // Step 5: Create full target path const targetPath = path.join(targetLocation, standardName); // Step 6: Check if move needed if (filePath === targetPath) { console.log(` ✅ File already in correct location`); await actionLogger.endSession('completed'); return { organized: false, path: filePath }; } // Step 7: Check code integrity before move const integrityCheck = await codeIntegrityChecker.validateMove(filePath, targetPath); if (!integrityCheck.safe) { console.log(` ⚠️ Integrity issues found - applying fixes...`); await codeIntegrityChecker.autoFix(filePath, targetPath, integrityCheck.issues); } // Step 8: Move file await this.moveFile(filePath, targetPath); // Step 9: Update any references await this.updateReferences(filePath, targetPath); await actionLogger.endSession('completed'); console.log(` ✅ File organized successfully`); return { organized: true, oldPath: filePath, newPath: targetPath, standardName: standardName, location: targetLocation }; } catch (error) { await actionLogger.endSession('failed'); console.error(` ❌ Organization failed:`, error); throw error; } } // Determine file type async determineFileType(filePath) { const fileName = path.basename(filePath).toLowerCase(); const content = await fs.readFile(filePath, 'utf-8'); // Check for agent if (content.includes('export class') && ( content.includes('Agent') || fileName.includes('agent') || fileName.includes('orchestrator') || fileName.includes('controller') )) { return 'agent'; } // Check for tool if (fileName.includes('tool') || fileName.includes('client') || fileName.includes('integration')) { return 'tool'; } // Check for workflow if (fileName.includes('workflow') && fileName.endsWith('.json')) { return 'workflow'; } // Check for config if (fileName.includes('config') && fileName.endsWith('.json')) { return 'config'; } // Check for log if (fileName.endsWith('.log') || fileName.includes('log')) { return 'log'; } return 'other'; } // Generate standard name async generateStandardName(filePath, fileType, metadata) { const fileName = path.basename(filePath); const ext = path.extname(fileName); const nameWithoutExt = fileName.replace(ext, ''); switch (fileType) { case 'agent': // Determine if core or specialized const agentType = metadata.core ? 'core' : 'specialized'; const cleanName = nameWithoutExt .replace(/agent/gi, '') .replace(/[-_]+/g, '-') .toLowerCase() .trim(); return `${agentType}-${cleanName}${ext}`; case 'tool': const cleanToolName = nameWithoutExt .replace(/tool/gi, '') .replace(/[-_]+/g, '-') .toLowerCase() .trim(); return `${cleanToolName}-tool${ext}`; case 'workflow': // Keep workflow names descriptive return fileName.toLowerCase(); default: return fileName; } } // Determine correct location async determineLocation(fileType, metadata) { const config = await configManager.initialize(); const root = config.paths.root; switch (fileType) { case 'agent': const agentSubType = metadata.core ? 'core' : 'specialized'; return path.join(root, 'src', 'agents', agentSubType); case 'tool': return path.join(root, 'src', 'tools'); case 'workflow': return path.join(root, 'workflows'); case 'config': return root; case 'log': return config.paths.logs.system; default: return path.dirname(metadata.originalPath || ''); } } // Move file safely async moveFile(sourcePath, destPath) { await actionLogger.logAction('file_move', { description: `Moving file`, sourcePath, destPath }, { path: sourcePath }, { path: destPath }); // Ensure destination directory exists await fs.mkdir(path.dirname(destPath), { recursive: true }); // Move file await fs.rename(sourcePath, destPath); console.log(` 📦 Moved to: ${destPath}`); } // Update references in config or other files async updateReferences(oldPath, newPath) { const config = await configManager.initialize(); const configStr = JSON.stringify(config); // Check if path is in config if (configStr.includes(oldPath)) { console.log(` 🔄 Updating config references...`); // Update would happen here - simplified for now } } // Helper: Check if file exists async fileExists(filePath) { try { await fs.access(filePath); return true; } catch { return false; } } // Batch organize directory async organizeDirectory(dirPath) { console.log(`📁 ${this.name}: Organizing directory: ${dirPath}`); const files = await fs.readdir(dirPath); const results = []; for (const file of files) { const filePath = path.join(dirPath, file); const stat = await fs.stat(filePath); if (stat.isFile() && /\.(js|ts|json)$/.test(file)) { try { const result = await this.organize(filePath); results.push(result); } catch (error) { console.error(` ❌ Failed to organize ${file}:`, error.message); } } } console.log(`✅ ${this.name}: Organized ${results.length} files`); return results; } } export const documentOrganizer = new DocumentOrganizer();

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