cloving 0.3.11 ā 0.3.13
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/cloving_gpt/index.js +32 -39
- package/dist/commands/config.js +2 -2
- package/dist/commands/generate/commit.js +1 -1
- package/dist/commands/generate/docs.js +11 -0
- package/dist/commands/generate/review.js +1 -1
- package/dist/commands/tokens.js +24 -0
- package/dist/index.js +3 -3
- package/dist/managers/BlockManager.js +31 -14
- package/dist/managers/ChatManager.js +60 -224
- package/dist/managers/CodeManager.js +60 -55
- package/dist/managers/CommitManager.js +1 -1
- package/dist/managers/DocsManager.js +88 -0
- package/dist/managers/DocumentationManager.js +2 -1
- package/dist/managers/InitManager.js +20 -6
- package/dist/managers/ProxyManager.js +19 -2
- package/dist/managers/ReviewManager.js +59 -169
- package/dist/managers/ShellManager.js +49 -0
- package/dist/managers/StreamManager.js +308 -0
- package/dist/managers/UnitTestManager.js +3 -3
- package/dist/package.json +2 -2
- package/dist/utils/config_utils.js +43 -0
- package/dist/utils/git_utils.js +29 -0
- package/dist/utils/prompt_utils.js +125 -307
- package/dist/utils/prompts/CODEGEN_COULDNT_APPLY.md +5 -0
- package/dist/utils/prompts/CODEGEN_EXAMPLES.md +82 -0
- package/dist/utils/prompts/CODEGEN_INSTRUCTIONS.md +43 -0
- package/dist/utils/prompts/DOCS_INSTRUCTIONS.md +9 -0
- package/dist/utils/prompts/INIT_INSTRUCTIONS.md +132 -0
- package/dist/utils/prompts/REVIEW_INSTRUCTIONS.md +49 -0
- package/dist/utils/prompts/SHELL_INSTRUCTIONS.md +15 -0
- package/dist/utils/prompts/SPECIAL_FILES.md +66 -0
- package/dist/utils/prompts/index.js +27 -0
- package/dist/utils/string_utils.js +10 -5
- package/package.json +2 -2
|
@@ -4,21 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const copy_paste_1 = __importDefault(require("copy-paste"));
|
|
7
|
-
const readline_1 = __importDefault(require("readline"));
|
|
8
|
-
const child_process_1 = require("child_process");
|
|
9
7
|
const colors_1 = __importDefault(require("colors"));
|
|
10
|
-
const axios_1 = __importDefault(require("axios"));
|
|
11
8
|
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
12
|
-
const
|
|
13
|
-
const
|
|
9
|
+
const readline_1 = __importDefault(require("readline"));
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
14
11
|
const CommitManager_1 = __importDefault(require("./CommitManager"));
|
|
15
|
-
const ChunkManager_1 = __importDefault(require("./ChunkManager"));
|
|
16
|
-
const BlockManager_1 = __importDefault(require("./BlockManager"));
|
|
17
12
|
const ReviewManager_1 = __importDefault(require("./ReviewManager"));
|
|
18
|
-
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
19
13
|
const string_utils_1 = require("../utils/string_utils");
|
|
20
14
|
const config_utils_1 = require("../utils/config_utils");
|
|
21
15
|
const prompt_utils_1 = require("../utils/prompt_utils");
|
|
16
|
+
const prompts_1 = require("../utils/prompts");
|
|
17
|
+
const StreamManager_1 = __importDefault(require("./StreamManager"));
|
|
22
18
|
const specialCommands = [
|
|
23
19
|
'save',
|
|
24
20
|
'commit',
|
|
@@ -32,36 +28,27 @@ const specialCommands = [
|
|
|
32
28
|
'help',
|
|
33
29
|
'exit',
|
|
34
30
|
];
|
|
35
|
-
class ChatManager {
|
|
31
|
+
class ChatManager extends StreamManager_1.default {
|
|
36
32
|
/**
|
|
37
33
|
* Creates an instance of ChatManager.
|
|
38
34
|
* @param {ClovingGPTOptions} options - Configuration options for the ChatManager.
|
|
39
35
|
*/
|
|
40
36
|
constructor(options) {
|
|
41
|
-
|
|
42
|
-
this.chatHistory = [];
|
|
37
|
+
super(options);
|
|
43
38
|
this.commandHistory = [];
|
|
44
39
|
this.historyIndex = -1;
|
|
45
40
|
this.multilineInput = '';
|
|
46
41
|
this.isMultilineMode = false;
|
|
47
|
-
this.contextFiles = {};
|
|
48
|
-
this.prompt = '';
|
|
49
|
-
this.isProcessing = false;
|
|
50
42
|
this.isSilent = false;
|
|
51
|
-
this.
|
|
52
|
-
this.maxRetries = 3;
|
|
53
|
-
this.isSilent = options.silent || false;
|
|
43
|
+
this.isSilent = options.silent || (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
54
44
|
options.stream = true;
|
|
55
45
|
options.silent = true;
|
|
56
|
-
this.gpt = new cloving_gpt_1.default(options);
|
|
57
46
|
this.rl = readline_1.default.createInterface({
|
|
58
47
|
input: process.stdin,
|
|
59
48
|
output: process.stdout,
|
|
60
49
|
prompt: colors_1.default.green.bold('cloving> '),
|
|
61
50
|
historySize: 1000,
|
|
62
51
|
});
|
|
63
|
-
this.chunkManager = new ChunkManager_1.default();
|
|
64
|
-
this.blockManager = new BlockManager_1.default();
|
|
65
52
|
}
|
|
66
53
|
/**
|
|
67
54
|
* Initializes the ChatManager by loading context files, displaying a welcome message,
|
|
@@ -74,8 +61,7 @@ class ChatManager {
|
|
|
74
61
|
console.log(`\nš š š ${colors_1.default.bold('Welcome to Cloving REPL')} š š š\n`);
|
|
75
62
|
this.displayAvailableCommands();
|
|
76
63
|
console.log('\nWhat would you like to do?');
|
|
77
|
-
this.
|
|
78
|
-
this.setupEventListeners();
|
|
64
|
+
this.setupReadline();
|
|
79
65
|
}
|
|
80
66
|
displayAvailableCommands() {
|
|
81
67
|
console.log(`Type a freeform request or question to interact with your Cloving AI pair programmer.\n`);
|
|
@@ -93,69 +79,15 @@ class ChatManager {
|
|
|
93
79
|
console.log(` - ${colors_1.default.yellow(colors_1.default.bold('exit'))} Quit this session`);
|
|
94
80
|
}
|
|
95
81
|
/**
|
|
96
|
-
*
|
|
97
|
-
* If a new version is available, it notifies the user.
|
|
98
|
-
* @private
|
|
99
|
-
* @returns {Promise<void>}
|
|
100
|
-
*/
|
|
101
|
-
async checkForLatestVersion() {
|
|
102
|
-
try {
|
|
103
|
-
const response = await axios_1.default.get('https://api.github.com/repos/cloving-ai/cloving-cli/releases/latest');
|
|
104
|
-
const latestVersion = response.data.tag_name;
|
|
105
|
-
const currentVersion = `v${(0, prompt_utils_1.getPackageVersion)()}`;
|
|
106
|
-
if (latestVersion !== currentVersion) {
|
|
107
|
-
console.log(colors_1.default.bgWhite.black(`\n š A new version of Cloving is available: ${latestVersion} `));
|
|
108
|
-
console.log(colors_1.default.bgWhite.black(` You are currently using version: ${currentVersion} `));
|
|
109
|
-
console.log(colors_1.default.bgWhite.black(` To upgrade, run: ${colors_1.default.bgWhite.black.bold('npm install -g cloving@latest')} `));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
// ignore errors
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Sets up event listeners for the readline interface and process input.
|
|
82
|
+
* Sets up readline and event listeners for the readline interface and process input.
|
|
118
83
|
* @private
|
|
119
84
|
*/
|
|
120
|
-
|
|
85
|
+
setupReadline() {
|
|
86
|
+
this.rl.prompt();
|
|
121
87
|
this.rl.on('line', this.handleLine.bind(this));
|
|
122
88
|
this.rl.on('close', this.handleClose.bind(this));
|
|
123
89
|
process.stdin.on('keypress', this.handleKeypress.bind(this));
|
|
124
90
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Loads context files specified in the options or defaults to the primary language's directory.
|
|
127
|
-
* @private
|
|
128
|
-
* @returns {Promise<void>}
|
|
129
|
-
*/
|
|
130
|
-
async loadContextFiles() {
|
|
131
|
-
const config = (0, config_utils_1.getClovingConfig)();
|
|
132
|
-
const primaryLanguage = config.languages.find((lang) => lang.primary);
|
|
133
|
-
const defaultDirectory = primaryLanguage ? primaryLanguage.directory : '.';
|
|
134
|
-
const testingDirectories = config.testingFrameworks?.map((framework) => framework.directory) || [];
|
|
135
|
-
const testingDirectory = testingDirectories[0];
|
|
136
|
-
let files = this.options.files || [defaultDirectory, testingDirectory].filter(Boolean);
|
|
137
|
-
if (files.length > 0) {
|
|
138
|
-
console.log(`\nBuilding chat session context...\n`);
|
|
139
|
-
}
|
|
140
|
-
for (const file of files) {
|
|
141
|
-
// Skip if the file is not a string
|
|
142
|
-
if (!file)
|
|
143
|
-
continue;
|
|
144
|
-
const previousCount = Object.keys(this.contextFiles).length;
|
|
145
|
-
this.contextFiles = await (0, prompt_utils_1.addFileOrDirectoryToContext)(file, this.contextFiles, this.options);
|
|
146
|
-
const newCount = Object.keys(this.contextFiles).length;
|
|
147
|
-
const addedCount = newCount - previousCount;
|
|
148
|
-
const totalTokens = this.calculateTotalTokens();
|
|
149
|
-
console.log(colors_1.default.cyan(`š Loaded context from: ${colors_1.default.bold(file)}`));
|
|
150
|
-
console.log(colors_1.default.green(` ā
Added ${addedCount} file(s) to context`));
|
|
151
|
-
console.log(colors_1.default.yellow(` š Total tokens in context: ${totalTokens.toLocaleString()}\n`));
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
calculateTotalTokens() {
|
|
155
|
-
return Object.values(this.contextFiles).reduce((total, content) => {
|
|
156
|
-
return total + Math.ceil(content.length / 4);
|
|
157
|
-
}, 0);
|
|
158
|
-
}
|
|
159
91
|
/**
|
|
160
92
|
* Handles each line of input from the user.
|
|
161
93
|
* @private
|
|
@@ -167,6 +99,15 @@ class ChatManager {
|
|
|
167
99
|
return;
|
|
168
100
|
}
|
|
169
101
|
const trimmedLine = line.trim();
|
|
102
|
+
if (this.isPastedCode(trimmedLine)) {
|
|
103
|
+
console.log('Detected pasted code. Processing as multiline input...');
|
|
104
|
+
this.multilineInput = trimmedLine;
|
|
105
|
+
this.isMultilineMode = true;
|
|
106
|
+
await this.handleCommand(this.multilineInput);
|
|
107
|
+
this.multilineInput = '';
|
|
108
|
+
this.isMultilineMode = false;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
170
111
|
if (this.handleMultilineInput(trimmedLine)) {
|
|
171
112
|
return;
|
|
172
113
|
}
|
|
@@ -202,6 +143,10 @@ class ChatManager {
|
|
|
202
143
|
}
|
|
203
144
|
return false;
|
|
204
145
|
}
|
|
146
|
+
isPastedCode(line) {
|
|
147
|
+
// Check if the line contains multiple lines (indicating a paste)
|
|
148
|
+
return line.includes('\n') || line.length > 100;
|
|
149
|
+
}
|
|
205
150
|
handleExitCommand(command) {
|
|
206
151
|
if (command.toLowerCase() === 'exit') {
|
|
207
152
|
this.rl.close();
|
|
@@ -247,6 +192,11 @@ class ChatManager {
|
|
|
247
192
|
if (matchingCommand) {
|
|
248
193
|
command = matchingCommand.split(' ')[0]; // Use only the command part without arguments
|
|
249
194
|
}
|
|
195
|
+
else {
|
|
196
|
+
console.log(`No matching command found for '${command}'. Processing as user input.`);
|
|
197
|
+
await this.processUserInput(command);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
250
200
|
}
|
|
251
201
|
switch (command) {
|
|
252
202
|
case 'help':
|
|
@@ -365,23 +315,6 @@ class ChatManager {
|
|
|
365
315
|
}
|
|
366
316
|
this.rl.prompt();
|
|
367
317
|
}
|
|
368
|
-
async refreshContext() {
|
|
369
|
-
await this.reloadContextFiles();
|
|
370
|
-
const updatedSystemPrompt = (0, prompt_utils_1.generateCodegenPrompt)(this.contextFiles);
|
|
371
|
-
this.chatHistory[0] = { role: 'user', content: updatedSystemPrompt };
|
|
372
|
-
this.chatHistory[1] = { role: 'assistant', content: 'What would you like to do?' };
|
|
373
|
-
}
|
|
374
|
-
async reloadContextFiles() {
|
|
375
|
-
for (const filePath in this.contextFiles) {
|
|
376
|
-
try {
|
|
377
|
-
const content = await fs_1.default.promises.readFile(path_1.default.resolve(filePath), 'utf8');
|
|
378
|
-
this.contextFiles[filePath] = content;
|
|
379
|
-
}
|
|
380
|
-
catch (error) {
|
|
381
|
-
console.error(`Error reading file ${filePath}:`, error);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
318
|
isRemoveCommand(command) {
|
|
386
319
|
return command.startsWith('rm ');
|
|
387
320
|
}
|
|
@@ -458,14 +391,14 @@ class ChatManager {
|
|
|
458
391
|
async handleReview() {
|
|
459
392
|
this.options.stream = false;
|
|
460
393
|
const reviewManager = new ReviewManager_1.default(this.options);
|
|
461
|
-
await reviewManager.
|
|
394
|
+
await reviewManager.generateReview();
|
|
462
395
|
this.options.stream = true;
|
|
463
396
|
this.rl.prompt();
|
|
464
397
|
}
|
|
465
398
|
async handleCommit() {
|
|
466
399
|
this.options.stream = false;
|
|
467
400
|
const commitManager = new CommitManager_1.default(this.options);
|
|
468
|
-
await commitManager.
|
|
401
|
+
await commitManager.generateCommit();
|
|
469
402
|
this.options.stream = true;
|
|
470
403
|
this.rl.prompt();
|
|
471
404
|
}
|
|
@@ -521,12 +454,10 @@ class ChatManager {
|
|
|
521
454
|
return;
|
|
522
455
|
}
|
|
523
456
|
this.isProcessing = true;
|
|
524
|
-
this.chunkManager = new ChunkManager_1.default();
|
|
525
457
|
try {
|
|
526
458
|
await this.refreshContext();
|
|
527
|
-
this.
|
|
528
|
-
|
|
529
|
-
if (!this.isSilent) {
|
|
459
|
+
this.addUserPrompt(this.generatePrompt(input));
|
|
460
|
+
if (!this.gpt.silent) {
|
|
530
461
|
const fullPrompt = this.chatHistory
|
|
531
462
|
.map((m) => {
|
|
532
463
|
const content = m.content
|
|
@@ -549,25 +480,11 @@ class ChatManager {
|
|
|
549
480
|
// Review the prompt using the REPL
|
|
550
481
|
const tokenCount = Math.ceil(fullPrompt.length / 4).toLocaleString();
|
|
551
482
|
console.log(`${colors_1.default.yellow.bold('INFORMATION')} The generated prompt is approximately ${tokenCount} tokens long. Would you like to review the prompt before sending it? ${colors_1.default.gray('(Y/n)')}`);
|
|
552
|
-
const reviewPrompt = await
|
|
553
|
-
this.rl.question(colors_1.default.green.bold('cloving> '), (answer) => {
|
|
554
|
-
resolve(answer.trim().toLowerCase());
|
|
555
|
-
});
|
|
556
|
-
});
|
|
483
|
+
const reviewPrompt = await this.askQuestion('cloving> ');
|
|
557
484
|
if (reviewPrompt.startsWith('y') || reviewPrompt === '') {
|
|
558
|
-
|
|
559
|
-
const promptParts = fullPrompt.split('\n```');
|
|
560
|
-
const highlightedPrompt = promptParts
|
|
561
|
-
.map((part, index) => index % 2 === 1 ? (0, cli_highlight_1.default)(part, { language: 'typescript' }) : part)
|
|
562
|
-
.join('\n```');
|
|
563
|
-
console.log(highlightedPrompt);
|
|
564
|
-
console.log(colors_1.default.gray.bold('\n---------------------- PROMPT END ----------------------\n'));
|
|
485
|
+
this.reviewPrompt(fullPrompt);
|
|
565
486
|
console.log(`Do you want to proceed with this prompt? ${colors_1.default.gray('(Y/n)')}`);
|
|
566
|
-
const confirmPrompt = await
|
|
567
|
-
this.rl.question(colors_1.default.green.bold('cloving> '), (answer) => {
|
|
568
|
-
resolve(answer.trim().toLowerCase());
|
|
569
|
-
});
|
|
570
|
-
});
|
|
487
|
+
const confirmPrompt = await this.askQuestion('cloving> ');
|
|
571
488
|
if (!confirmPrompt.startsWith('y') && confirmPrompt !== '') {
|
|
572
489
|
console.log('Operation cancelled by user.');
|
|
573
490
|
this.isProcessing = false;
|
|
@@ -580,120 +497,40 @@ class ChatManager {
|
|
|
580
497
|
prompt: this.prompt,
|
|
581
498
|
messages: this.chatHistory,
|
|
582
499
|
});
|
|
583
|
-
|
|
584
|
-
this.handleResponseStream(responseStream, accumulatedContent);
|
|
500
|
+
this.handleResponseStream(responseStream);
|
|
585
501
|
}
|
|
586
502
|
catch (err) {
|
|
587
503
|
this.handleError(err);
|
|
588
504
|
}
|
|
589
505
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
* This method processes the incoming data chunks, converts them to readable output,
|
|
596
|
-
* and manages the accumulated content.
|
|
597
|
-
*
|
|
598
|
-
* @param responseStream - The stream of response data from the AI model.
|
|
599
|
-
* @param accumulatedContent - A string to accumulate the complete response content.
|
|
600
|
-
*/
|
|
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
|
-
});
|
|
619
|
-
this.blockManager.on('content', (content) => {
|
|
620
|
-
process.stdout.write(content);
|
|
621
|
-
accumulatedContent += content;
|
|
622
|
-
});
|
|
623
|
-
this.blockManager.on('codeBlock', (codeBlock) => {
|
|
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
|
-
}
|
|
644
|
-
accumulatedContent += codeBlock.raw;
|
|
645
|
-
});
|
|
646
|
-
// Handle content chunks from the ChunkManager
|
|
647
|
-
this.chunkManager.on('content', (buffer) => {
|
|
648
|
-
let convertedStream = this.gpt.convertStream(buffer);
|
|
649
|
-
while (convertedStream !== null) {
|
|
650
|
-
const { output, lastChar } = convertedStream;
|
|
651
|
-
// buffer the output to the BlockManager
|
|
652
|
-
this.blockManager.addContent(output);
|
|
653
|
-
// clear the buffer from the ChunkManager
|
|
654
|
-
this.chunkManager.clearBuffer(lastChar);
|
|
655
|
-
// Try to convert the rest of the buffer to a string
|
|
656
|
-
buffer = buffer.slice(lastChar);
|
|
657
|
-
convertedStream = this.gpt.convertStream(buffer);
|
|
658
|
-
}
|
|
659
|
-
});
|
|
660
|
-
// Handle incoming data chunks from the response stream
|
|
661
|
-
responseStream.data.on('data', (chunk) => {
|
|
662
|
-
// Convert the chunk to a string and add it to the ChunkManager
|
|
663
|
-
if (!chunk)
|
|
664
|
-
return;
|
|
665
|
-
// a chunk is a JSON object with different keys depending on the AI service provider
|
|
666
|
-
const chunkString = chunk.toString();
|
|
667
|
-
this.chunkManager.addChunk(chunkString);
|
|
668
|
-
});
|
|
669
|
-
// Handle the end of the response stream
|
|
670
|
-
responseStream.data.on('end', () => {
|
|
671
|
-
// Finalize the response when the stream ends
|
|
672
|
-
this.finalizeResponse(accumulatedContent);
|
|
673
|
-
});
|
|
674
|
-
// Handle any errors in the response stream
|
|
675
|
-
responseStream.data.on('error', (error) => {
|
|
676
|
-
console.error('Error streaming response:', error);
|
|
677
|
-
this.isProcessing = false;
|
|
678
|
-
process.stdout.write('\n');
|
|
679
|
-
this.rl.prompt();
|
|
506
|
+
askQuestion(prompt) {
|
|
507
|
+
return new Promise((resolve) => {
|
|
508
|
+
this.rl.question(colors_1.default.green.bold(prompt), (answer) => {
|
|
509
|
+
resolve(answer.trim().toLowerCase());
|
|
510
|
+
});
|
|
680
511
|
});
|
|
681
512
|
}
|
|
682
|
-
|
|
683
|
-
|
|
513
|
+
reviewPrompt(fullPrompt) {
|
|
514
|
+
console.log(colors_1.default.gray.bold('\n---------------------- PROMPT START ----------------------\n'));
|
|
515
|
+
const promptParts = fullPrompt.split('\n```');
|
|
516
|
+
const highlightedPrompt = promptParts
|
|
517
|
+
.map((part, index) => (index % 2 === 1 ? (0, cli_highlight_1.default)(part, { language: 'typescript' }) : part))
|
|
518
|
+
.join('\n```');
|
|
519
|
+
console.log(highlightedPrompt);
|
|
520
|
+
console.log(colors_1.default.gray.bold('\n---------------------- PROMPT END ----------------------\n'));
|
|
521
|
+
}
|
|
522
|
+
async finalizeResponse() {
|
|
523
|
+
this.addAssistantResponse(this.responseString);
|
|
684
524
|
this.isProcessing = false;
|
|
685
|
-
|
|
686
|
-
|
|
525
|
+
process.stdout.write('\b'.repeat(100));
|
|
526
|
+
if (this.responseString !== '') {
|
|
527
|
+
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
|
|
687
528
|
const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
|
|
688
529
|
if (!canApply) {
|
|
689
530
|
if (this.retryCount < this.maxRetries) {
|
|
690
531
|
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
532
|
this.retryCount++;
|
|
692
|
-
this.processUserInput(`
|
|
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.
|
|
695
|
-
|
|
696
|
-
${summary}`);
|
|
533
|
+
this.processUserInput(`${prompts_1.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
|
|
697
534
|
return;
|
|
698
535
|
}
|
|
699
536
|
else {
|
|
@@ -720,8 +557,7 @@ You can follow up with another request or:
|
|
|
720
557
|
`);
|
|
721
558
|
this.rl.prompt();
|
|
722
559
|
}
|
|
723
|
-
handleError(
|
|
724
|
-
const error = err;
|
|
560
|
+
handleError(error) {
|
|
725
561
|
let errorMessage = error.message || 'An error occurred.';
|
|
726
562
|
const errorNumber = error.response?.status || 'unknown';
|
|
727
563
|
switch (errorNumber) {
|
|
@@ -738,7 +574,6 @@ You can follow up with another request or:
|
|
|
738
574
|
errorMessage = 'Internal server error';
|
|
739
575
|
break;
|
|
740
576
|
}
|
|
741
|
-
const promptTokens = Math.ceil(this.prompt.length / 4).toLocaleString();
|
|
742
577
|
const statusMessage = error.response?.data?.statusMessage;
|
|
743
578
|
console.error(colors_1.default.red(`\nError (${errorNumber}) while submitting the prompt to the AI API\n\n${statusMessage}\n`));
|
|
744
579
|
this.isProcessing = false;
|
|
@@ -774,7 +609,8 @@ ${prompt}
|
|
|
774
609
|
|
|
775
610
|
### Note
|
|
776
611
|
|
|
777
|
-
Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block
|
|
612
|
+
Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block.
|
|
613
|
+
Do not allow empty CURRENT blocks and if you add content to the CURRENT block, you also have to add the same content to the NEW block.`;
|
|
778
614
|
}
|
|
779
615
|
handleClose() {
|
|
780
616
|
console.log('Goodbye!');
|
|
@@ -5,14 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const prompts_1 = require("@inquirer/prompts");
|
|
7
7
|
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
8
|
+
const colors_1 = __importDefault(require("colors"));
|
|
8
9
|
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
10
|
+
const config_utils_1 = require("../utils/config_utils");
|
|
9
11
|
const prompt_utils_1 = require("../utils/prompt_utils");
|
|
10
12
|
const string_utils_1 = require("../utils/string_utils");
|
|
11
|
-
|
|
13
|
+
const prompts_2 = require("../utils/prompts");
|
|
14
|
+
const StreamManager_1 = __importDefault(require("./StreamManager"));
|
|
15
|
+
class CodeManager extends StreamManager_1.default {
|
|
12
16
|
constructor(options) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
super(options);
|
|
18
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
19
|
+
options.stream = true;
|
|
16
20
|
this.gpt = new cloving_gpt_1.default(options);
|
|
17
21
|
}
|
|
18
22
|
async initialize() {
|
|
@@ -26,7 +30,7 @@ class CodeManager {
|
|
|
26
30
|
let includeMoreFiles = true;
|
|
27
31
|
while (includeMoreFiles) {
|
|
28
32
|
const contextFile = await (0, prompts_1.input)({
|
|
29
|
-
message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue)
|
|
33
|
+
message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue): `,
|
|
30
34
|
});
|
|
31
35
|
if (contextFile) {
|
|
32
36
|
this.contextFiles = await (0, prompt_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
|
|
@@ -42,26 +46,7 @@ class CodeManager {
|
|
|
42
46
|
});
|
|
43
47
|
this.options.prompt = userPrompt;
|
|
44
48
|
}
|
|
45
|
-
|
|
46
|
-
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
|
|
47
|
-
const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
|
|
48
|
-
if (!canApply) {
|
|
49
|
-
console.log('The generated code could not be automatically applied. It will try again asking for more details.');
|
|
50
|
-
console.log(`Some of the provided code blocks could not be applied,
|
|
51
|
-
please match the existing code with a few more lines of context and make sure it is a character for character exact match.
|
|
52
|
-
|
|
53
|
-
${summary}`);
|
|
54
|
-
response = await this.generateCode(this.options.prompt);
|
|
55
|
-
this.displayGeneratedCode(response);
|
|
56
|
-
}
|
|
57
|
-
this.displayGeneratedCode(response);
|
|
58
|
-
if (this.options.save) {
|
|
59
|
-
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
|
|
60
|
-
await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
await this.handleUserAction(response, this.options.prompt);
|
|
64
|
-
}
|
|
49
|
+
await this.generateCode(this.options.prompt);
|
|
65
50
|
}
|
|
66
51
|
catch (err) {
|
|
67
52
|
const error = err;
|
|
@@ -69,35 +54,26 @@ ${summary}`);
|
|
|
69
54
|
}
|
|
70
55
|
}
|
|
71
56
|
async generateCode(userPrompt) {
|
|
57
|
+
await this.checkForLatestVersion();
|
|
58
|
+
await this.loadContextFiles();
|
|
72
59
|
if (this.chatHistory.length === 0) {
|
|
73
|
-
|
|
74
|
-
this.
|
|
75
|
-
|
|
60
|
+
this.addUserPrompt((0, prompt_utils_1.generateCodegenPrompt)(this.contextFiles));
|
|
61
|
+
this.addAssistantResponse('What would you like to do?');
|
|
62
|
+
}
|
|
63
|
+
this.addUserPrompt(userPrompt);
|
|
64
|
+
try {
|
|
65
|
+
const responseStream = await this.gpt.streamText({
|
|
66
|
+
prompt: userPrompt,
|
|
67
|
+
messages: this.chatHistory,
|
|
68
|
+
});
|
|
69
|
+
this.handleResponseStream(responseStream);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const error = err;
|
|
73
|
+
console.error('Error streaming response:', error.message);
|
|
76
74
|
}
|
|
77
|
-
this.chatHistory.push({ role: 'user', content: userPrompt });
|
|
78
|
-
return await this.gpt.generateText({ prompt: userPrompt, messages: this.chatHistory });
|
|
79
|
-
}
|
|
80
|
-
displayGeneratedCode(rawCodeCommand) {
|
|
81
|
-
(0, string_utils_1.parseMarkdownInstructions)(rawCodeCommand).map((code) => {
|
|
82
|
-
if (code.trim().startsWith('```')) {
|
|
83
|
-
const lines = code.split('\n');
|
|
84
|
-
const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
|
|
85
|
-
console.log(lines[0]);
|
|
86
|
-
try {
|
|
87
|
-
console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
// don't highlight if it fails
|
|
91
|
-
console.log(lines.slice(1, -1).join('\n'));
|
|
92
|
-
}
|
|
93
|
-
console.log(lines.slice(-1)[0]);
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
75
|
}
|
|
100
|
-
async handleUserAction(response
|
|
76
|
+
async handleUserAction(response) {
|
|
101
77
|
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
|
|
102
78
|
const files = Array.from(new Set(currentNewBlocks.map((block) => block.filePath)));
|
|
103
79
|
if (this.options.save) {
|
|
@@ -133,9 +109,7 @@ ${summary}`);
|
|
|
133
109
|
includeMoreFiles = false;
|
|
134
110
|
}
|
|
135
111
|
}
|
|
136
|
-
|
|
137
|
-
this.displayGeneratedCode(newResponse);
|
|
138
|
-
await this.handleUserAction(newResponse, newPrompt);
|
|
112
|
+
await this.generateCode(newPrompt);
|
|
139
113
|
break;
|
|
140
114
|
case 'explain':
|
|
141
115
|
const explainPrompt = this.generateExplainCodePrompt(response);
|
|
@@ -163,7 +137,7 @@ ${summary}`);
|
|
|
163
137
|
break;
|
|
164
138
|
case 'saveAll':
|
|
165
139
|
await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
|
|
166
|
-
console.log('
|
|
140
|
+
console.log(colors_1.default.green('\nAll files have been saved.'));
|
|
167
141
|
break;
|
|
168
142
|
case 'done':
|
|
169
143
|
break;
|
|
@@ -176,5 +150,36 @@ ${summary}`);
|
|
|
176
150
|
|
|
177
151
|
Please briefly explain how the code works in this.`;
|
|
178
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Finalizes the response processing.
|
|
155
|
+
* This method should be overridden to provide specific finalization behavior.
|
|
156
|
+
* @protected
|
|
157
|
+
*/
|
|
158
|
+
async finalizeResponse() {
|
|
159
|
+
this.addAssistantResponse(this.responseString);
|
|
160
|
+
this.isProcessing = false;
|
|
161
|
+
if (this.responseString !== '') {
|
|
162
|
+
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
|
|
163
|
+
const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
|
|
164
|
+
if (!canApply) {
|
|
165
|
+
await this.handleUnapplicableBlocks(summary);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.retryCount = 0; // Reset the retry count on successful application
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
await this.handleUserAction(this.responseString);
|
|
172
|
+
}
|
|
173
|
+
async handleUnapplicableBlocks(summary) {
|
|
174
|
+
if (this.retryCount < this.maxRetries) {
|
|
175
|
+
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`);
|
|
176
|
+
this.retryCount++;
|
|
177
|
+
await this.generateCode(`${prompts_2.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
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`);
|
|
181
|
+
this.retryCount = 0; // Reset the retry count for future attempts
|
|
182
|
+
}
|
|
183
|
+
}
|
|
179
184
|
}
|
|
180
185
|
exports.default = CodeManager;
|
|
@@ -15,7 +15,7 @@ class CommitManager {
|
|
|
15
15
|
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
16
16
|
this.gpt = new cloving_gpt_1.default(options);
|
|
17
17
|
}
|
|
18
|
-
async
|
|
18
|
+
async generateCommit() {
|
|
19
19
|
try {
|
|
20
20
|
// Get the git diff
|
|
21
21
|
const diff = (0, child_process_1.execSync)('git diff HEAD').toString().trim();
|