cloving 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -0
- package/dist/managers/BlockManager.js +17 -35
- package/dist/managers/ChatManager.js +53 -84
- package/dist/managers/ShellManager.js +5 -0
- package/dist/package.json +1 -1
- package/dist/utils/prompt_utils.js +1 -1
- package/dist/utils/string_utils.js +43 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71,6 +71,7 @@ generate
|
|
|
71
71
|
.command('shell')
|
|
72
72
|
.alias('sh')
|
|
73
73
|
.description('Generate a shell command based on a prompt')
|
|
74
|
+
.option('-e, --exec', 'Automatically execute the generated shell command')
|
|
74
75
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
75
76
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
76
77
|
.option('-p, --prompt <prompt>', 'Specify the prompt to use')
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const events_1 = require("events");
|
|
4
|
+
const string_utils_1 = require("../utils/string_utils");
|
|
4
5
|
// Passes through string chunks, looking for code blocks, when it finds one, it starts buffering until it finds the end of the block
|
|
5
6
|
// Then it emits the code block and clears the buffer and continues
|
|
6
7
|
class BlockManager extends events_1.EventEmitter {
|
|
@@ -13,8 +14,7 @@ class BlockManager extends events_1.EventEmitter {
|
|
|
13
14
|
}
|
|
14
15
|
// add content to the buffer, watching for code blocks along the way
|
|
15
16
|
addContent(content) {
|
|
16
|
-
|
|
17
|
-
if (content.includes('`')) {
|
|
17
|
+
if (content.includes('`') || content.includes('\n')) {
|
|
18
18
|
this.isWaitingForContent = true;
|
|
19
19
|
this.buffer += content;
|
|
20
20
|
return;
|
|
@@ -23,13 +23,13 @@ class BlockManager extends events_1.EventEmitter {
|
|
|
23
23
|
content = this.buffer + content;
|
|
24
24
|
this.buffer = '';
|
|
25
25
|
}
|
|
26
|
+
this.processCodeBlocks(content);
|
|
27
|
+
}
|
|
28
|
+
processCodeBlocks(content) {
|
|
26
29
|
const codeBlockMarker = '\n```';
|
|
27
30
|
let markerIndex = content.indexOf(codeBlockMarker);
|
|
28
|
-
if (content.startsWith('```'))
|
|
29
|
-
markerIndex = 0;
|
|
30
31
|
if (markerIndex !== -1) {
|
|
31
32
|
if (this.isBufferingCode) {
|
|
32
|
-
// End of code block
|
|
33
33
|
this.codeBuffer += content.slice(0, markerIndex + codeBlockMarker.length);
|
|
34
34
|
const rest = content.slice(markerIndex + codeBlockMarker.length);
|
|
35
35
|
this.emit('endGeneratingCode');
|
|
@@ -37,15 +37,13 @@ class BlockManager extends events_1.EventEmitter {
|
|
|
37
37
|
this.emit('content', rest);
|
|
38
38
|
}
|
|
39
39
|
else {
|
|
40
|
-
// Start of code block
|
|
41
|
-
this.emit('startGeneratingCode');
|
|
42
40
|
this.emitBuffer(content.slice(0, markerIndex));
|
|
43
41
|
this.codeBuffer = content.slice(markerIndex);
|
|
42
|
+
this.emit('startGeneratingCode');
|
|
44
43
|
this.isBufferingCode = true;
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
else {
|
|
48
|
-
// No code block marker found
|
|
49
47
|
if (this.isBufferingCode) {
|
|
50
48
|
this.codeBuffer += content;
|
|
51
49
|
}
|
|
@@ -65,21 +63,20 @@ class BlockManager extends events_1.EventEmitter {
|
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
65
|
emitCodeBlock() {
|
|
68
|
-
if (this.codeBuffer) {
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
71
|
-
const { language, currentStart, newEnd, currentCode, newCode } = codeParts;
|
|
66
|
+
if (this.codeBuffer.length > 0) {
|
|
67
|
+
const currentNewBlock = this.parseCodeBuffer();
|
|
68
|
+
if (currentNewBlock) {
|
|
72
69
|
this.emit('codeBlock', {
|
|
73
|
-
|
|
74
|
-
currentStart,
|
|
75
|
-
newEnd,
|
|
76
|
-
currentCode,
|
|
77
|
-
newCode,
|
|
70
|
+
currentNewBlock,
|
|
78
71
|
raw: this.codeBuffer,
|
|
79
72
|
});
|
|
80
73
|
this.emit('content', this.buffer);
|
|
81
74
|
this.clearBuffer();
|
|
82
75
|
}
|
|
76
|
+
else {
|
|
77
|
+
this.emit('codeBlock', this.codeBuffer);
|
|
78
|
+
this.clearBuffer();
|
|
79
|
+
}
|
|
83
80
|
}
|
|
84
81
|
}
|
|
85
82
|
/**
|
|
@@ -89,27 +86,12 @@ class BlockManager extends events_1.EventEmitter {
|
|
|
89
86
|
* current start index, divider index, and new end index. It then extracts the current code and new code
|
|
90
87
|
* from the buffer based on these indices.
|
|
91
88
|
*
|
|
92
|
-
* @return {
|
|
89
|
+
* @return {CurrentNewBlock | null}
|
|
93
90
|
* An object containing the language, current start, new end, current code, and new code.
|
|
94
91
|
*/
|
|
95
92
|
parseCodeBuffer() {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
const currentStartIndex = lines.findIndex((line) => line.startsWith('<<<<<<< CURRENT'));
|
|
99
|
-
const dividerIndex = lines.findIndex((line) => line === '=======');
|
|
100
|
-
const newEndIndex = lines.findIndex((line) => line.startsWith('>>>>>>> NEW'));
|
|
101
|
-
if (currentStartIndex === -1 || dividerIndex === -1 || newEndIndex === -1) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
const currentCode = lines.slice(currentStartIndex + 1, dividerIndex).join('\n');
|
|
105
|
-
const newCode = lines.slice(dividerIndex + 1, newEndIndex).join('\n');
|
|
106
|
-
return {
|
|
107
|
-
language,
|
|
108
|
-
currentStart: lines[currentStartIndex],
|
|
109
|
-
newEnd: lines[newEndIndex],
|
|
110
|
-
currentCode,
|
|
111
|
-
newCode,
|
|
112
|
-
};
|
|
93
|
+
const results = (0, string_utils_1.extractCurrentNewBlocks)(this.codeBuffer);
|
|
94
|
+
return results[0];
|
|
113
95
|
}
|
|
114
96
|
clearBuffer() {
|
|
115
97
|
this.buffer = '';
|
|
@@ -48,6 +48,8 @@ class ChatManager {
|
|
|
48
48
|
this.prompt = '';
|
|
49
49
|
this.isProcessing = false;
|
|
50
50
|
this.isSilent = false;
|
|
51
|
+
this.retryCount = 0;
|
|
52
|
+
this.maxRetries = 3;
|
|
51
53
|
this.isSilent = options.silent || false;
|
|
52
54
|
options.stream = true;
|
|
53
55
|
options.silent = true;
|
|
@@ -597,22 +599,48 @@ class ChatManager {
|
|
|
597
599
|
* @param accumulatedContent - A string to accumulate the complete response content.
|
|
598
600
|
*/
|
|
599
601
|
handleResponseStream(responseStream, accumulatedContent) {
|
|
602
|
+
let animationInterval;
|
|
603
|
+
this.blockManager = new BlockManager_1.default();
|
|
604
|
+
this.chunkManager = new ChunkManager_1.default();
|
|
605
|
+
this.blockManager.on('startGeneratingCode', () => {
|
|
606
|
+
process.stdout.write(colors_1.default.gray.bold('\nGenerating code... '));
|
|
607
|
+
let animationIndex = 0;
|
|
608
|
+
const animationChars = ['/', '|', '\\', '-'];
|
|
609
|
+
animationInterval = setInterval(() => {
|
|
610
|
+
process.stdout.write('\b'.repeat(1));
|
|
611
|
+
process.stdout.write(colors_1.default.gray.bold(animationChars[animationIndex]));
|
|
612
|
+
animationIndex = (animationIndex + 1) % animationChars.length;
|
|
613
|
+
}, 100);
|
|
614
|
+
});
|
|
615
|
+
this.blockManager.on('endGeneratingCode', () => {
|
|
616
|
+
clearInterval(animationInterval);
|
|
617
|
+
process.stdout.write('\b'.repeat(100));
|
|
618
|
+
});
|
|
600
619
|
this.blockManager.on('content', (content) => {
|
|
601
|
-
process.stdout.write('\n------ beginning of blockManager content ------\n');
|
|
602
620
|
process.stdout.write(content);
|
|
603
|
-
process.stdout.write('\n------ end of blockManager content ------\n');
|
|
604
621
|
accumulatedContent += content;
|
|
605
622
|
});
|
|
606
623
|
this.blockManager.on('codeBlock', (codeBlock) => {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
624
|
+
const { currentNewBlock } = codeBlock;
|
|
625
|
+
if (currentNewBlock?.currentContent && currentNewBlock?.newContent) {
|
|
626
|
+
// Current/New format
|
|
627
|
+
process.stdout.write(colors_1.default.gray.bold(`\`\`\`${currentNewBlock.language} \n`));
|
|
628
|
+
process.stdout.write(colors_1.default.gray.bold(`<<<<<<< CURRENT ${currentNewBlock.filePath}\n`));
|
|
629
|
+
process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.currentContent, { language: currentNewBlock.language }));
|
|
630
|
+
process.stdout.write(colors_1.default.gray.bold('\n=======\n'));
|
|
631
|
+
process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.newContent, { language: currentNewBlock.language }));
|
|
632
|
+
process.stdout.write(colors_1.default.gray.bold('\n>>>>>>> NEW\n```'));
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
if (codeBlock.raw) {
|
|
636
|
+
// Raw format
|
|
637
|
+
process.stdout.write(` \n`);
|
|
638
|
+
process.stdout.write((0, cli_highlight_1.default)(codeBlock.raw));
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
process.stdout.write(` \n`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
616
644
|
accumulatedContent += codeBlock.raw;
|
|
617
645
|
});
|
|
618
646
|
// Handle content chunks from the ChunkManager
|
|
@@ -620,10 +648,6 @@ class ChatManager {
|
|
|
620
648
|
let convertedStream = this.gpt.convertStream(buffer);
|
|
621
649
|
while (convertedStream !== null) {
|
|
622
650
|
const { output, lastChar } = convertedStream;
|
|
623
|
-
process.stdout.write('\n------ beginning of chunk ------\n');
|
|
624
|
-
process.stdout.write(output);
|
|
625
|
-
process.stdout.write(`lastChar: ${lastChar}\n`);
|
|
626
|
-
process.stdout.write('\n------ end of chunk ------\n');
|
|
627
651
|
// buffer the output to the BlockManager
|
|
628
652
|
this.blockManager.addContent(output);
|
|
629
653
|
// clear the buffer from the ChunkManager
|
|
@@ -640,9 +664,6 @@ class ChatManager {
|
|
|
640
664
|
return;
|
|
641
665
|
// a chunk is a JSON object with different keys depending on the AI service provider
|
|
642
666
|
const chunkString = chunk.toString();
|
|
643
|
-
process.stdout.write('\n------ beginning of chunk string ------\n');
|
|
644
|
-
process.stdout.write(chunkString);
|
|
645
|
-
process.stdout.write('\n------ end of chunk string ------\n');
|
|
646
667
|
this.chunkManager.addChunk(chunkString);
|
|
647
668
|
});
|
|
648
669
|
// Handle the end of the response stream
|
|
@@ -658,69 +679,6 @@ class ChatManager {
|
|
|
658
679
|
this.rl.prompt();
|
|
659
680
|
});
|
|
660
681
|
}
|
|
661
|
-
processOutput(output, accumulatedContent, codeBuffer, isBufferingCode) {
|
|
662
|
-
const codeBlockMarker = '\n```';
|
|
663
|
-
let currentIndex = 0;
|
|
664
|
-
while (currentIndex < output.length) {
|
|
665
|
-
let markerIndex = output.indexOf(codeBlockMarker, currentIndex);
|
|
666
|
-
if (output.startsWith('```'))
|
|
667
|
-
markerIndex = 0;
|
|
668
|
-
// Check if the code block marker is found
|
|
669
|
-
if (markerIndex !== -1) {
|
|
670
|
-
if (isBufferingCode) {
|
|
671
|
-
// End of code block
|
|
672
|
-
codeBuffer += output.slice(currentIndex, markerIndex + codeBlockMarker.length);
|
|
673
|
-
const { language, currentStart, newEnd, current: currentCode, new: newCode } = this.parseCodeBuffer(codeBuffer);
|
|
674
|
-
if (language)
|
|
675
|
-
process.stdout.write(language);
|
|
676
|
-
if (currentStart)
|
|
677
|
-
process.stdout.write(currentStart);
|
|
678
|
-
if (currentCode)
|
|
679
|
-
process.stdout.write((0, cli_highlight_1.default)(currentCode, { language }));
|
|
680
|
-
process.stdout.write('=======');
|
|
681
|
-
if (newCode)
|
|
682
|
-
process.stdout.write((0, cli_highlight_1.default)(newCode, { language }));
|
|
683
|
-
if (newEnd)
|
|
684
|
-
process.stdout.write(newEnd);
|
|
685
|
-
accumulatedContent += codeBuffer;
|
|
686
|
-
codeBuffer = '';
|
|
687
|
-
isBufferingCode = false;
|
|
688
|
-
currentIndex = markerIndex + codeBlockMarker.length;
|
|
689
|
-
}
|
|
690
|
-
else {
|
|
691
|
-
// Start of code block
|
|
692
|
-
process.stdout.write(output.slice(currentIndex, markerIndex + codeBlockMarker.length));
|
|
693
|
-
accumulatedContent += output.slice(currentIndex, markerIndex + codeBlockMarker.length);
|
|
694
|
-
codeBuffer = '';
|
|
695
|
-
isBufferingCode = true;
|
|
696
|
-
currentIndex = markerIndex + codeBlockMarker.length;
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
else {
|
|
700
|
-
// No code block marker found
|
|
701
|
-
if (isBufferingCode) {
|
|
702
|
-
// buffering code
|
|
703
|
-
codeBuffer += output.slice(currentIndex);
|
|
704
|
-
}
|
|
705
|
-
else {
|
|
706
|
-
process.stdout.write(output.slice(currentIndex));
|
|
707
|
-
accumulatedContent += output.slice(currentIndex);
|
|
708
|
-
}
|
|
709
|
-
break;
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
return [isBufferingCode, codeBuffer, accumulatedContent];
|
|
713
|
-
}
|
|
714
|
-
parseCodeBuffer(codeBuffer) {
|
|
715
|
-
const lines = codeBuffer.split('\n');
|
|
716
|
-
const language = lines[0].trim();
|
|
717
|
-
const currentStartIndex = lines.findIndex(line => line.startsWith('<<<<<<< CURRENT'));
|
|
718
|
-
const dividerIndex = lines.findIndex(line => line === '=======');
|
|
719
|
-
const newEndIndex = lines.findIndex(line => line.startsWith('>>>>>>> NEW'));
|
|
720
|
-
const current = lines.slice(currentStartIndex + 1, dividerIndex).join('\n');
|
|
721
|
-
const newCode = lines.slice(dividerIndex + 1, newEndIndex).join('\n');
|
|
722
|
-
return { language, currentStart: lines[currentStartIndex], newEnd: lines[newEndIndex], current, new: newCode };
|
|
723
|
-
}
|
|
724
682
|
async finalizeResponse(accumulatedContent) {
|
|
725
683
|
this.chatHistory.push({ role: 'assistant', content: accumulatedContent.trim() });
|
|
726
684
|
this.isProcessing = false;
|
|
@@ -728,12 +686,23 @@ class ChatManager {
|
|
|
728
686
|
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(accumulatedContent);
|
|
729
687
|
const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
|
|
730
688
|
if (!canApply) {
|
|
731
|
-
|
|
732
|
-
|
|
689
|
+
if (this.retryCount < this.maxRetries) {
|
|
690
|
+
console.log(`\n\n${summary}\n\n${colors_1.default.yellow.bold('WARNING')} Some of the provided code blocks could not be automatically applied. Retrying (Attempt ${this.retryCount + 1}/${this.maxRetries})...\n`);
|
|
691
|
+
this.retryCount++;
|
|
692
|
+
this.processUserInput(`Some of the provided code blocks could not be applied,
|
|
733
693
|
please match the existing code with a few more lines of context and make sure it is a character for character exact match.
|
|
694
|
+
Never use "rest of the existing code" or "rest of the code" in your response, instead break the code block into smaller blocks.
|
|
734
695
|
|
|
735
696
|
${summary}`);
|
|
736
|
-
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
console.log(`\n\n${colors_1.default.red.bold('ERROR')} Failed to generate a diff that could be cleanly applied after ${this.maxRetries} attempts. Please review the changes manually and try again.\n`);
|
|
701
|
+
this.retryCount = 0; // Reset the retry count for future attempts
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
this.retryCount = 0; // Reset the retry count on successful application
|
|
737
706
|
}
|
|
738
707
|
}
|
|
739
708
|
process.stdout.write(`
|
|
@@ -40,9 +40,14 @@ Please briefly explain how this shell script works.`;
|
|
|
40
40
|
console.log((0, cli_highlight_1.default)(generatedShellWithoutShebang));
|
|
41
41
|
}
|
|
42
42
|
async handleUserAction(response, prompt) {
|
|
43
|
+
let { exec } = this.options;
|
|
43
44
|
this.chatHistory.push({ role: 'assistant', content: response });
|
|
44
45
|
const generatedShell = (0, string_utils_1.extractMarkdown)(response);
|
|
45
46
|
const generatedShellWithoutShebang = generatedShell.replace(/^#!.*?\s/, '');
|
|
47
|
+
if (exec) {
|
|
48
|
+
(0, child_process_1.execSync)(generatedShellWithoutShebang, { stdio: 'inherit' });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
46
51
|
const action = await (0, prompts_1.select)({
|
|
47
52
|
message: 'What would you like to do?',
|
|
48
53
|
choices: [
|
package/dist/package.json
CHANGED
|
@@ -36,7 +36,7 @@ exports.CODEGEN_INSTRUCTIONS = `## AI Code Generation Instructions
|
|
|
36
36
|
\`\`\`typescript
|
|
37
37
|
2. **Next line: seven <, CURRENT, and the file path.**
|
|
38
38
|
<<<<<<< CURRENT path/to/file.ts
|
|
39
|
-
3. **Include the exact existing code to be changed.**
|
|
39
|
+
3. **Include the exact existing code to be changed, this section should never be just spaces or tabs unless it is a new file.**
|
|
40
40
|
4. **Divide with seven =.**
|
|
41
41
|
=======
|
|
42
42
|
5. **Add the new code.**
|
|
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.checkBlocksApplicability = exports.applyAndSaveCurrentNewBlocks = exports.
|
|
6
|
+
exports.checkBlocksApplicability = exports.applyAndSaveCurrentNewBlocks = exports.applyAndSaveBlock = exports.searchReplaceString = exports.writeFileContent = exports.ensureDirectoryExists = exports.extractCurrentNewBlocks = exports.extractBlock = exports.findBlockIndices = exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const colors_1 = __importDefault(require("colors"));
|
|
10
|
-
const BLOCK_START = '\n
|
|
10
|
+
const BLOCK_START = '\n```';
|
|
11
|
+
const BLOCK_CURRENT = '\n<<<<<<< CURRENT';
|
|
11
12
|
const BLOCK_DIVIDER = '\n=======';
|
|
12
|
-
const BLOCK_END = '\n>>>>>>> NEW';
|
|
13
|
+
const BLOCK_END = '\n>>>>>>> NEW\n```';
|
|
13
14
|
// Function to estimate token count
|
|
14
15
|
const estimateTokens = async (text) => {
|
|
15
16
|
const charCount = text.length;
|
|
@@ -65,6 +66,13 @@ const extractJsonMetadata = (response) => {
|
|
|
65
66
|
if (jsonEndIndex !== -1) {
|
|
66
67
|
jsonString = jsonString.substring(0, jsonEndIndex + 1);
|
|
67
68
|
}
|
|
69
|
+
// sanity check that the extracted string is valid JSON
|
|
70
|
+
try {
|
|
71
|
+
JSON.parse(jsonString);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
jsonString = '';
|
|
75
|
+
}
|
|
68
76
|
return jsonString;
|
|
69
77
|
};
|
|
70
78
|
exports.extractJsonMetadata = extractJsonMetadata;
|
|
@@ -171,7 +179,12 @@ const findBlockIndices = (input, startIndex) => {
|
|
|
171
179
|
const blockStart = input.indexOf(BLOCK_START, startIndex);
|
|
172
180
|
if (blockStart === -1)
|
|
173
181
|
return null;
|
|
174
|
-
const
|
|
182
|
+
const languageStart = blockStart + BLOCK_START.length;
|
|
183
|
+
const languageEnd = input.indexOf('\n', languageStart);
|
|
184
|
+
if (languageEnd === -1)
|
|
185
|
+
return null;
|
|
186
|
+
const currentStart = input.indexOf(BLOCK_CURRENT, startIndex);
|
|
187
|
+
const filePathStart = currentStart + BLOCK_CURRENT.length;
|
|
175
188
|
const filePathEnd = input.indexOf('\n', filePathStart);
|
|
176
189
|
const dividerIndex = input.indexOf(BLOCK_DIVIDER, filePathEnd);
|
|
177
190
|
if (dividerIndex === -1)
|
|
@@ -180,7 +193,8 @@ const findBlockIndices = (input, startIndex) => {
|
|
|
180
193
|
if (blockEnd === -1)
|
|
181
194
|
return null;
|
|
182
195
|
return {
|
|
183
|
-
start:
|
|
196
|
+
start: languageStart,
|
|
197
|
+
current: currentStart,
|
|
184
198
|
filePathEnd,
|
|
185
199
|
divider: dividerIndex,
|
|
186
200
|
end: blockEnd,
|
|
@@ -198,10 +212,11 @@ exports.findBlockIndices = findBlockIndices;
|
|
|
198
212
|
* @returns {CurrentNewBlock} An object containing the extracted file path, current content, and new content.
|
|
199
213
|
*/
|
|
200
214
|
const extractBlock = (input, indices) => {
|
|
201
|
-
const
|
|
215
|
+
const language = input.slice(indices.start, indices.current).trim();
|
|
216
|
+
const filePath = input.slice(indices.current + BLOCK_CURRENT.length, indices.filePathEnd).trim();
|
|
202
217
|
const currentContent = input.slice(indices.filePathEnd + 1, indices.divider).trim();
|
|
203
218
|
const newContent = input.slice(indices.divider + BLOCK_DIVIDER.length, indices.end).trim();
|
|
204
|
-
return { filePath, currentContent, newContent };
|
|
219
|
+
return { language, filePath, currentContent, newContent };
|
|
205
220
|
};
|
|
206
221
|
exports.extractBlock = extractBlock;
|
|
207
222
|
/**
|
|
@@ -286,13 +301,13 @@ exports.writeFileContent = writeFileContent;
|
|
|
286
301
|
* currentContent: "console.log('Old content')",
|
|
287
302
|
* newContent: "console.log('New content')"
|
|
288
303
|
* }
|
|
289
|
-
* const updatedContent =
|
|
304
|
+
* const updatedContent = searchReplaceString(currentContent, block)
|
|
290
305
|
* // updatedContent will be:
|
|
291
306
|
* // function example() {
|
|
292
307
|
* // console.log('New content')
|
|
293
308
|
* // }
|
|
294
309
|
*/
|
|
295
|
-
const
|
|
310
|
+
const searchReplaceString = (currentContent, block) => {
|
|
296
311
|
// 1) if block.currentContent.trim() is empty then return block.newContent
|
|
297
312
|
if (block.currentContent.trim() === '') {
|
|
298
313
|
return block.newContent;
|
|
@@ -308,26 +323,32 @@ const updateFileContent = (currentContent, block) => {
|
|
|
308
323
|
if (matchingLine === -1) {
|
|
309
324
|
return currentContent; // If no match is found, return the original content
|
|
310
325
|
}
|
|
311
|
-
// 5) compare the first line of block.currentContent with the
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
326
|
+
// 5) compare the first line of block.currentContent with the startLine line of currentContent, putting any preceeding spaces that are in currentContent but not in block.currentContent into a variable named preceedingSpaces
|
|
327
|
+
const startLine = currentContent.split('\n').findIndex((line) => line.includes(blockLines[0]));
|
|
328
|
+
if (startLine === -1) {
|
|
329
|
+
return currentContent; // If no match is found, return the original content
|
|
330
|
+
}
|
|
331
|
+
const startLineContent = currentContent.split('\n')[startLine];
|
|
332
|
+
const blockLineContent = block.currentContent.split('\n')[0];
|
|
333
|
+
const precedingSpaces = startLineContent.replace(blockLineContent, '');
|
|
316
334
|
// 6) add preceedingSpaces to the start of each line in both block.currentContent and block.newContent
|
|
317
335
|
if (precedingSpaces) {
|
|
318
|
-
const
|
|
336
|
+
const indentedCurrentContent = block.currentContent
|
|
337
|
+
.split('\n')
|
|
338
|
+
.map((line) => precedingSpaces + line)
|
|
339
|
+
.join('\n');
|
|
319
340
|
const indentedNewContent = block.newContent
|
|
320
341
|
.split('\n')
|
|
321
|
-
.map((line) =>
|
|
342
|
+
.map((line) => precedingSpaces + line)
|
|
322
343
|
.join('\n');
|
|
323
344
|
// 7) finally, do a simple currentContent.replace(block.currentContent, block.newContent) with the modified (added preceeding spaces to match) variables
|
|
324
|
-
return currentContent.replace(
|
|
345
|
+
return currentContent.replace(indentedCurrentContent, indentedNewContent);
|
|
325
346
|
}
|
|
326
347
|
else {
|
|
327
348
|
return currentContent.replace(block.currentContent, block.newContent);
|
|
328
349
|
}
|
|
329
350
|
};
|
|
330
|
-
exports.
|
|
351
|
+
exports.searchReplaceString = searchReplaceString;
|
|
331
352
|
/**
|
|
332
353
|
* Analyzes the content of a block against the file content to find matching lines.
|
|
333
354
|
*
|
|
@@ -364,7 +385,7 @@ const analyzeBlockContent = async (block, fileContent) => {
|
|
|
364
385
|
* @param {number} index - The index of the block being processed (used for logging).
|
|
365
386
|
* @returns {Promise<void>} A promise that resolves when the block has been processed.
|
|
366
387
|
*/
|
|
367
|
-
const
|
|
388
|
+
const applyAndSaveBlock = async (block, index) => {
|
|
368
389
|
const filePath = path_1.default.resolve(block.filePath);
|
|
369
390
|
let fileContent = await readFileContent(filePath);
|
|
370
391
|
if (fileContent === '') {
|
|
@@ -381,11 +402,11 @@ const processBlock = async (block, index) => {
|
|
|
381
402
|
console.log(`[${index + 1}] ${colors_1.default.red.bold(block.filePath)} ${colors_1.default.red.bold(`ERROR: Current content matches multiple (${matchingLines.length}) parts in the file`)}`);
|
|
382
403
|
return;
|
|
383
404
|
}
|
|
384
|
-
const updatedContent = (0, exports.
|
|
405
|
+
const updatedContent = (0, exports.searchReplaceString)(fileContent, block);
|
|
385
406
|
await (0, exports.writeFileContent)(filePath, updatedContent);
|
|
386
407
|
console.log(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} has been ${colors_1.default.yellow.bold('updated')}`);
|
|
387
408
|
};
|
|
388
|
-
exports.
|
|
409
|
+
exports.applyAndSaveBlock = applyAndSaveBlock;
|
|
389
410
|
/**
|
|
390
411
|
* Applies and saves the changes specified in the CurrentNewBlock array.
|
|
391
412
|
*
|
|
@@ -413,7 +434,7 @@ const applyAndSaveCurrentNewBlocks = async (blocks) => {
|
|
|
413
434
|
console.log(`\nApplying and saving current/new blocks...\n`);
|
|
414
435
|
for (const [index, block] of blocks.entries()) {
|
|
415
436
|
try {
|
|
416
|
-
await (0, exports.
|
|
437
|
+
await (0, exports.applyAndSaveBlock)(block, index);
|
|
417
438
|
}
|
|
418
439
|
catch (error) {
|
|
419
440
|
console.error(`Error processing ${colors_1.default.red(block.filePath)}:`, error);
|