capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
111 lines • 4.43 kB
JavaScript
import { FileEditTool } from './file-edit.js';
import path from 'path';
import { readFile } from 'fs/promises';
import { toolExecutionService } from '../../services/tool-execution.js';
import { v4 as uuidv4 } from 'uuid';
export class FileEditUnifiedTool extends FileEditTool {
constructor() {
super();
this.name = 'file_edit';
this.description = 'Edit files with confirmation from user before applying changes';
}
async run(params, context) {
const resolvedPath = path.isAbsolute(params.path)
? params.path
: path.join(context.workingDirectory || process.cwd(), params.path);
if (!params.oldText) {
return this.handleFileWrite(resolvedPath, params.newText, context);
}
let oldContent = '';
try {
oldContent = await readFile(resolvedPath, 'utf8');
}
catch (error) {
throw new Error(`Failed to read file: ${error}`);
}
let newContent = oldContent;
if (params.replaceAll) {
const occurrences = oldContent.split(params.oldText).length - 1;
if (occurrences === 0) {
throw new Error(`String "${params.oldText}" not found in file`);
}
newContent = oldContent.split(params.oldText).join(params.newText);
}
else {
if (!oldContent.includes(params.oldText)) {
throw new Error(`String "${params.oldText}" not found in file`);
}
newContent = oldContent.replace(params.oldText, params.newText);
}
const toolCallId = uuidv4();
const approved = await toolExecutionService.requestConfirmation(toolCallId, 'edit', {
filePath: resolvedPath,
oldContent,
newContent
});
if (!approved) {
const { contextManager } = await import('../../services/context.js');
contextManager.addMessage({
role: 'system',
content: `Edit to ${resolvedPath} was cancelled by user`
});
throw new Error('Edit cancelled by user');
}
const { contextManager } = await import('../../services/context.js');
contextManager.addMessage({
role: 'system',
content: `Edit to ${resolvedPath} was approved by user`
});
return super.run(params, context);
}
async handleFileWrite(resolvedPath, content, context) {
let oldContent = '';
let isNewFile = false;
try {
oldContent = await readFile(resolvedPath, 'utf8');
}
catch (error) {
if (error.code === 'ENOENT') {
isNewFile = true;
}
else {
throw error;
}
}
const toolCallId = uuidv4();
const approved = await toolExecutionService.requestConfirmation(toolCallId, 'edit', {
filePath: resolvedPath + (isNewFile ? ' (new file)' : ''),
oldContent,
newContent: content
});
if (!approved) {
const { contextManager } = await import('../../services/context.js');
contextManager.addMessage({
role: 'system',
content: `Write to ${resolvedPath}${isNewFile ? ' (new file)' : ''} was cancelled by user`
});
throw new Error('Write cancelled by user');
}
const { contextManager } = await import('../../services/context.js');
contextManager.addMessage({
role: 'system',
content: `Write to ${resolvedPath}${isNewFile ? ' (new file)' : ''} was approved by user`
});
if (isNewFile) {
const { mkdir, writeFile } = await import('fs/promises');
const dir = path.dirname(resolvedPath);
await mkdir(dir, { recursive: true });
await writeFile(resolvedPath, content, 'utf8');
return {
path: resolvedPath,
created: true,
linesChanged: content.split('\n').length,
preview: content.substring(0, 200) + (content.length > 200 ? '...' : '')
};
}
else {
return super.run({ path: resolvedPath, oldText: oldContent, newText: content, replaceAll: true }, context);
}
}
}
//# sourceMappingURL=file-edit-unified.js.map