-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagent.js
More file actions
127 lines (107 loc) · 4.03 KB
/
Copy pathagent.js
File metadata and controls
127 lines (107 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { readFile, writeFile, unlink } from 'fs/promises';
import path from 'path';
/**
* @fileoverview A functional parser and applicator for AI-generated code patches.
* This module extracts file-specific operations and applies them to the filesystem.
*/
/**
* Represents a single operation parsed from the AI block.
* @typedef {Object} Operation
* @property {string} type - The type of operation (REPLACE, INSERT, DELETE, etc.).
* @property {string} [search] - The text to look for.
* @property {string} [replace] - The text to replace with.
* @property {string} [content] - Content for new files or insertions.
* @property {string} [after] - The anchor for insertion.
*/
/**
* Parses the raw AI output into a structured map of files and operations.
* * @param {string} rawText - The raw string from the AI code block.
* @returns {Map<string, Operation[]>} A map where keys are file paths and values are arrays of Ops.
*/
const parseAIResponse = (rawText) => {
const fileBlocks = rawText.split(/FILE: \[(.*?)\]/g).slice(1);
const result = new Map();
for (let i = 0; i < fileBlocks.length; i += 2) {
const filePath = fileBlocks[i];
const content = fileBlocks[i + 1];
const ops = content.split('----------------------------------------')
.map(block => block.trim())
.filter(block => block.startsWith('OP:'))
.map(extractOpDetails);
result.set(filePath, ops);
}
return result;
};
/**
* Extracts specific fields (SEARCH, REPLACE, etc.) from an operation block.
* * @param {string} block - The raw text block for a single operation.
* @returns {Operation} The structured operation object.
*/
const extractOpDetails = (block) => {
const getField = (field) => {
const regex = new RegExp(`${field}:\\n?([\\s\\S]*?)(?=\\n[A-Z]+:|$|\\n----------------------------------------)`, 'g');
const match = regex.exec(block);
return match ? match[1].trim() : null;
};
const typeMatch = block.match(/OP: \[(.*?)\]/);
return {
type: typeMatch ? typeMatch[1] : 'UNKNOWN',
search: getField('SEARCH'),
replace: getField('REPLACE'),
content: getField('CONTENT'),
after: getField('AFTER')
};
};
/**
* Applies operations to a single file's content.
* * @param {string} fileContent - Current content of the file.
* @param {Operation[]} ops - List of operations to apply.
* @returns {string} The transformed content.
*/
const transformContent = (fileContent, ops) => {
return ops.reduce((acc, op) => {
switch (op.type) {
case 'REPLACE':
return acc.replace(op.search, op.replace);
case 'INSERT':
return acc.replace(op.after, `${op.after}\n${op.content}`);
case 'DELETE':
return acc.replace(op.search, '');
default:
return acc;
}
}, fileContent);
};
/**
* Main orchestrator function that executes the changes.
* * @param {string} aiOutput - The raw text provided by the AI.
* @param {string} basePath - The root directory where files are located.
* @returns {Promise<void>}
*/
export const applyChanges = async (aiOutput, basePath = './') => {
const fileMap = parseAIResponse(aiOutput);
const tasks = Array.from(fileMap.entries()).map(async ([filePath, ops]) => {
const fullPath = path.join(basePath, filePath);
try {
// Handle file-level operations first
const fileOps = ops.map(o => o.type);
if (fileOps.includes('CREATE_FILE')) {
const createOp = ops.find(o => o.type === 'CREATE_FILE');
return await writeFile(fullPath, createOp.content, 'utf8');
}
if (fileOps.includes('REMOVE_FILE')) {
return await unlink(fullPath);
}
// Handle content-level operations
const original = await readFile(fullPath, 'utf8');
const updated = transformContent(original, ops);
if (original !== updated) {
await writeFile(fullPath, updated, 'utf8');
console.log(`✅ Updated: ${filePath}`);
}
} catch (error) {
console.error(`❌ Error processing ${filePath}:`, error.message);
}
});
await Promise.all(tasks);
};