UNPKG

contaigents

Version:

Modular AI Content Ecosystem with Audio Generation

233 lines (232 loc) 9.02 kB
import { BaseTool } from '../base/BaseTool.js'; import { IntelligentFileService } from '../../services/IntelligentFileService.js'; /** * Tool for intelligent file writing with validation and backup */ export class WriteFileTool extends BaseTool { constructor() { const definition = { name: 'write_file', description: 'Write content to file with intelligent validation and backup options', category: 'file', parameters: [ { name: 'path', type: 'string', description: 'Path to the file to write', required: true }, { name: 'content', type: 'string', description: 'Content to write to the file', required: true }, { name: 'mode', type: 'string', description: 'Write mode: replace, append, insert, or partial', required: false, default: 'replace' }, { name: 'startLine', type: 'number', description: 'Starting line for partial write', required: false }, { name: 'endLine', type: 'number', description: 'Ending line for partial write', required: false }, { name: 'insertAt', type: 'number', description: 'Line number to insert content at', required: false }, { name: 'createBackup', type: 'boolean', description: 'Create backup before writing', required: false, default: true }, { name: 'validateSyntax', type: 'boolean', description: 'Validate syntax for code files', required: false, default: true } ] }; super(definition); this.fileService = new IntelligentFileService(); } async execute(parameters, context) { try { const { path, content, mode = 'replace', startLine, endLine, insertAt, createBackup = true, validateSyntax = true } = parameters; // Validate file path if (!this.isFilePathValid(path, context)) { return { success: false, error: `Invalid file path: ${path}` }; } let result = { operation: mode, filePath: path }; // Create backup if requested and file exists if (createBackup) { try { const existingContent = await this.fileService.readFile(path); const backupPath = `${path}.backup.${Date.now()}`; await this.fileService.saveFile({ path: backupPath, content: existingContent }); result.backupPath = backupPath; } catch (error) { // File doesn't exist, no backup needed result.backupCreated = false; } } // Validate syntax if requested if (validateSyntax && this.isCodeFile(path)) { const syntaxValidation = await this.validateSyntax(content, path); if (!syntaxValidation.valid) { return { success: false, error: `Syntax validation failed: ${syntaxValidation.errors.join(', ')}`, data: { syntaxErrors: syntaxValidation.errors } }; } result.syntaxValid = true; } // Write content based on mode switch (mode) { case 'replace': await this.fileService.saveFile({ path, content }); break; case 'append': await this.fileService.writeFilePartial(path, content, { append: true }); break; case 'insert': if (insertAt === undefined) { return { success: false, error: 'insertAt parameter required for insert mode' }; } await this.fileService.writeFilePartial(path, content, { insertAt }); break; case 'partial': if (startLine === undefined || endLine === undefined) { return { success: false, error: 'startLine and endLine parameters required for partial mode' }; } await this.fileService.writeFilePartial(path, content, { startLine, endLine }); break; default: return { success: false, error: `Unknown write mode: ${mode}` }; } // Get file stats after writing const newContent = await this.fileService.readFile(path); result.linesWritten = newContent.split('\n').length; result.bytesWritten = Buffer.byteLength(newContent, 'utf8'); return { success: true, data: result, metadata: { operation: 'write', timestamp: Date.now(), mode } }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' }; } } isFilePathValid(filePath, context) { // Basic validation - no parent directory traversal, within project scope if (filePath.includes('..') || filePath.includes('~')) { return false; } // Check if within project scope (unless absolute path is explicitly allowed) if (!filePath.startsWith('/') && !filePath.startsWith(context.projectPath)) { return true; // Relative paths are generally OK } return true; } isCodeFile(filePath) { const codeExtensions = ['.js', '.ts', '.py', '.json', '.html', '.css', '.jsx', '.tsx']; const ext = filePath.substring(filePath.lastIndexOf('.')); return codeExtensions.includes(ext.toLowerCase()); } async validateSyntax(content, filePath) { const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase(); const errors = []; try { switch (ext) { case '.json': JSON.parse(content); break; case '.js': case '.ts': // Basic syntax check - look for common issues if (this.hasUnmatchedBrackets(content)) { errors.push('Unmatched brackets detected'); } break; default: // No specific validation for other file types break; } } catch (error) { errors.push(error instanceof Error ? error.message : 'Syntax error'); } return { valid: errors.length === 0, errors }; } hasUnmatchedBrackets(content) { const brackets = { '(': ')', '[': ']', '{': '}' }; const stack = []; for (const char of content) { if (char in brackets) { stack.push(char); } else if (Object.values(brackets).includes(char)) { const last = stack.pop(); if (!last || brackets[last] !== char) { return true; } } } return stack.length > 0; } getExamples() { return [ 'write_file({"path": "new-file.md", "content": "# Hello World"})', 'write_file({"path": "config.json", "content": "{}", "validateSyntax": true})', 'write_file({"path": "app.js", "content": "console.log(\'test\');", "mode": "append"})', 'write_file({"path": "README.md", "content": "New section", "mode": "insert", "insertAt": 10})' ]; } getEstimatedExecutionTime() { return 3000; // 3 seconds for validation and writing } }