UNPKG

@bestdefense/bd-agent

Version:

An AI-powered coding assistant CLI that connects to AWS Bedrock

265 lines 11.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.advancedEditTools = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const util_1 = require("util"); const chalk_1 = __importDefault(require("chalk")); const readFile = (0, util_1.promisify)(fs.readFile); const writeFile = (0, util_1.promisify)(fs.writeFile); function findBestMatch(content, searchString) { const index = content.indexOf(searchString); if (index === -1) return null; // Calculate line number const lines = content.substring(0, index).split('\n'); return { index, line: lines.length }; } function getLineContext(content, lineNumber, contextLines = 3) { const lines = content.split('\n'); const startLine = Math.max(0, lineNumber - contextLines - 1); const endLine = Math.min(lines.length, lineNumber + contextLines); return lines.slice(startLine, endLine) .map((line, idx) => { const currentLine = startLine + idx + 1; const prefix = currentLine === lineNumber ? chalk_1.default.yellow('>>> ') : ' '; return chalk_1.default.gray(`${String(currentLine).padStart(4)} │`) + prefix + line; }) .join('\n'); } exports.advancedEditTools = [ { name: 'edit', description: 'Perform precise string replacement in a file. Finds exact match of old_str and replaces with new_str.', input_schema: { type: 'object', properties: { file_path: { type: 'string', description: 'The file path to edit' }, old_str: { type: 'string', description: 'The exact string to replace (must match exactly including whitespace)' }, new_str: { type: 'string', description: 'The string to replace it with' }, dry_run: { type: 'boolean', description: 'If true, show what would be changed without modifying the file', default: false } }, required: ['file_path', 'old_str', 'new_str'] }, execute: async ({ file_path, old_str, new_str, dry_run = false }) => { try { const absolutePath = path.resolve(file_path); // Check if file exists if (!fs.existsSync(absolutePath)) { return { success: false, error: `File not found: ${absolutePath}` }; } const content = await readFile(absolutePath, 'utf-8'); // Find the string to replace const match = findBestMatch(content, old_str); if (!match) { // Provide helpful context about what was searched const similarityThreshold = 0.8; const lines = content.split('\n'); let bestMatch = { line: -1, similarity: 0, content: '' }; // Simple similarity check (could be enhanced with proper string similarity algorithms) for (let i = 0; i < lines.length; i++) { if (lines[i].includes(old_str.substring(0, Math.min(20, old_str.length)))) { bestMatch = { line: i + 1, similarity: 0.5, content: lines[i] }; break; } } let errorMsg = `String not found in file: ${file_path}\n`; errorMsg += `Searched for:\n${chalk_1.default.red(old_str)}\n`; if (bestMatch.line > 0) { errorMsg += `\nDid you mean this line (${bestMatch.line})?\n`; errorMsg += chalk_1.default.gray(bestMatch.content); } return { success: false, error: errorMsg }; } // Count occurrences const occurrences = (content.match(new RegExp(old_str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length; if (occurrences > 1) { const context = getLineContext(content, match.line); return { success: false, error: `String appears ${occurrences} times in the file. Please provide more context to make it unique.\n\nFirst occurrence at line ${match.line}:\n${context}` }; } // Perform the replacement const updatedContent = content.replace(old_str, new_str); if (dry_run) { const context = getLineContext(updatedContent, match.line); return { success: true, message: 'Dry run - no changes made', diff: { old: old_str, new: new_str, line_start: match.line, line_end: match.line + old_str.split('\n').length - 1 }, preview: context }; } await writeFile(absolutePath, updatedContent, 'utf-8'); return { success: true, message: `File edited successfully at line ${match.line}`, diff: { old: old_str, new: new_str, line_start: match.line, line_end: match.line + old_str.split('\n').length - 1 } }; } catch (error) { return { success: false, error: error.message }; } } }, { name: 'multi_edit', description: 'Perform multiple edits in a single file atomically. All edits must succeed or none are applied.', input_schema: { type: 'object', properties: { file_path: { type: 'string', description: 'The file path to edit' }, edits: { type: 'array', description: 'Array of edit operations to perform', items: { type: 'object', properties: { old_str: { type: 'string', description: 'The exact string to replace' }, new_str: { type: 'string', description: 'The string to replace it with' } }, required: ['old_str', 'new_str'] } }, dry_run: { type: 'boolean', description: 'If true, validate all edits without modifying the file', default: false } }, required: ['file_path', 'edits'] }, execute: async ({ file_path, edits, dry_run = false }) => { try { const absolutePath = path.resolve(file_path); if (!fs.existsSync(absolutePath)) { return { success: false, error: `File not found: ${absolutePath}` }; } let content = await readFile(absolutePath, 'utf-8'); const originalContent = content; const appliedEdits = []; // Validate and apply each edit sequentially for (let i = 0; i < edits.length; i++) { const edit = edits[i]; const match = findBestMatch(content, edit.old_str); if (!match) { return { success: false, error: `Edit ${i + 1} failed: String not found: "${edit.old_str.substring(0, 50)}${edit.old_str.length > 50 ? '...' : ''}"` }; } // Check for uniqueness const occurrences = (content.match(new RegExp(edit.old_str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length; if (occurrences > 1) { return { success: false, error: `Edit ${i + 1} failed: String appears ${occurrences} times. Must be unique for safe editing.` }; } // Apply the edit content = content.replace(edit.old_str, edit.new_str); appliedEdits.push({ ...edit, line: match.line }); } if (dry_run) { return { success: true, message: `Dry run successful. ${edits.length} edits validated.`, preview: appliedEdits.map((edit, i) => `Edit ${i + 1} at line ${edit.line}: "${edit.old_str.substring(0, 30)}..." → "${edit.new_str.substring(0, 30)}..."`).join('\n') }; } // Write the file with all edits applied await writeFile(absolutePath, content, 'utf-8'); return { success: true, message: `Successfully applied ${edits.length} edits to ${file_path}`, diff: { old: originalContent, new: content } }; } catch (error) { return { success: false, error: error.message }; } } } ]; //# sourceMappingURL=advanced-edit.js.map