cloving 0.3.11 → 0.3.12
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 +31 -38
- 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 +17 -13
- package/dist/managers/ChatManager.js +22 -208
- package/dist/managers/CodeManager.js +58 -55
- package/dist/managers/CommitManager.js +1 -1
- package/dist/managers/DocsManager.js +86 -0
- package/dist/managers/DocumentationManager.js +2 -1
- package/dist/managers/InitManager.js +2 -1
- package/dist/managers/ReviewManager.js +59 -169
- package/dist/managers/ShellManager.js +49 -0
- package/dist/managers/StreamManager.js +291 -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 +42 -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/package.json +2 -2
|
@@ -3,20 +3,18 @@ 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
|
-
const
|
|
7
|
-
const os_1 = __importDefault(require("os"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const axios_1 = __importDefault(require("axios"));
|
|
10
|
-
const child_process_1 = require("child_process");
|
|
6
|
+
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
11
7
|
const process_1 = __importDefault(require("process"));
|
|
12
|
-
const prompts_1 = require("@inquirer/prompts");
|
|
13
8
|
const colors_1 = __importDefault(require("colors"));
|
|
9
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
14
11
|
const claude_1 = require("./adapters/claude");
|
|
15
12
|
const openai_1 = require("./adapters/openai");
|
|
16
13
|
const mistral_1 = require("./adapters/mistral");
|
|
17
14
|
const ollama_1 = require("./adapters/ollama");
|
|
18
15
|
const gemini_1 = require("./adapters/gemini");
|
|
19
16
|
const azureopenai_1 = require("./adapters/azureopenai");
|
|
17
|
+
const string_utils_1 = require("../utils/string_utils");
|
|
20
18
|
const config_utils_1 = require("../utils/config_utils");
|
|
21
19
|
class ClovingGPT {
|
|
22
20
|
constructor(options) {
|
|
@@ -75,41 +73,36 @@ class ClovingGPT {
|
|
|
75
73
|
default: true,
|
|
76
74
|
});
|
|
77
75
|
if (reviewPrompt) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const editedPrompt = fs_1.default.readFileSync(tempFile, 'utf-8').trim();
|
|
91
|
-
fs_1.default.unlinkSync(tempFile);
|
|
92
|
-
if (editedPrompt === '') {
|
|
93
|
-
console.log('Prompt is empty.');
|
|
94
|
-
resolve(null);
|
|
95
|
-
return;
|
|
76
|
+
(0, string_utils_1.parseMarkdownInstructions)(fullPrompt).map((code) => {
|
|
77
|
+
if (code.trim().startsWith('```')) {
|
|
78
|
+
const lines = code.split('\n');
|
|
79
|
+
const language = code.match(/```(\w+)/)?.[1] || 'default';
|
|
80
|
+
console.log(lines[0]);
|
|
81
|
+
try {
|
|
82
|
+
if (language === 'default') {
|
|
83
|
+
console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n')));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
|
|
87
|
+
}
|
|
96
88
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
101
|
-
if (!confirmPrompt) {
|
|
102
|
-
resolve(null);
|
|
103
|
-
return;
|
|
89
|
+
catch (error) {
|
|
90
|
+
// don't highlight if it fails
|
|
91
|
+
console.log(lines.slice(1, -1).join('\n'));
|
|
104
92
|
}
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
console.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
93
|
+
console.log(lines.slice(-1)[0]);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const confirmPrompt = await (0, prompts_1.confirm)({
|
|
100
|
+
message: `Are you sure you want to continue?`,
|
|
101
|
+
default: true,
|
|
112
102
|
});
|
|
103
|
+
if (!confirmPrompt) {
|
|
104
|
+
process_1.default.exit(1);
|
|
105
|
+
}
|
|
113
106
|
}
|
|
114
107
|
return prompt;
|
|
115
108
|
}
|
|
@@ -6,6 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const CommitManager_1 = __importDefault(require("../../managers/CommitManager"));
|
|
7
7
|
const commit = async (options) => {
|
|
8
8
|
const commitManager = new CommitManager_1.default(options);
|
|
9
|
-
await commitManager.
|
|
9
|
+
await commitManager.generateCommit();
|
|
10
10
|
};
|
|
11
11
|
exports.default = commit;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const DocsManager_1 = __importDefault(require("../../managers/DocsManager"));
|
|
7
|
+
const docs = async (options) => {
|
|
8
|
+
const docsManager = new DocsManager_1.default(options);
|
|
9
|
+
await docsManager.generateDocs();
|
|
10
|
+
};
|
|
11
|
+
exports.default = docs;
|
|
@@ -6,6 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const ReviewManager_1 = __importDefault(require("../../managers/ReviewManager"));
|
|
7
7
|
const review = async (options) => {
|
|
8
8
|
const reviewManager = new ReviewManager_1.default(options);
|
|
9
|
-
await reviewManager.
|
|
9
|
+
await reviewManager.generateReview();
|
|
10
10
|
};
|
|
11
11
|
exports.default = review;
|
package/dist/commands/tokens.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module tokens
|
|
4
|
+
* This module provides functionality to estimate the number of tokens in the context files
|
|
5
|
+
* and project description for GPT processing.
|
|
6
|
+
*/
|
|
2
7
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
8
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
9
|
};
|
|
@@ -7,10 +12,19 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
7
12
|
const path_1 = __importDefault(require("path"));
|
|
8
13
|
const config_utils_1 = require("../utils/config_utils");
|
|
9
14
|
const prompt_utils_1 = require("../utils/prompt_utils");
|
|
15
|
+
/**
|
|
16
|
+
* Calculates the estimated number of tokens for the given context files and project description.
|
|
17
|
+
* @param {ClovingGPTOptions} options - The options for token calculation, including file paths.
|
|
18
|
+
* @returns {Promise<void>} A promise that resolves when the token estimation is complete.
|
|
19
|
+
*/
|
|
10
20
|
const tokens = async (options) => {
|
|
11
21
|
let contextFiles = {};
|
|
12
22
|
let files = options.files || '.';
|
|
13
23
|
let prompt = '';
|
|
24
|
+
/**
|
|
25
|
+
* Expands the given file paths to include all files in directories.
|
|
26
|
+
* @type {string[]}
|
|
27
|
+
*/
|
|
14
28
|
let expandedFiles = [];
|
|
15
29
|
for (const file of files) {
|
|
16
30
|
const filePath = path_1.default.resolve(file);
|
|
@@ -26,6 +40,9 @@ const tokens = async (options) => {
|
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
42
|
files = expandedFiles;
|
|
43
|
+
/**
|
|
44
|
+
* Reads the content of each file and stores it in the contextFiles object.
|
|
45
|
+
*/
|
|
29
46
|
for (const file of files) {
|
|
30
47
|
const filePath = path_1.default.resolve(file);
|
|
31
48
|
if (await fs_1.default.promises
|
|
@@ -36,6 +53,9 @@ const tokens = async (options) => {
|
|
|
36
53
|
contextFiles[file] = content;
|
|
37
54
|
}
|
|
38
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Generates the prompt content by combining the app description and context file contents.
|
|
58
|
+
*/
|
|
39
59
|
const contextFileContents = Object.keys(contextFiles)
|
|
40
60
|
.map((file) => `### Contents of ${file}\n\n${contextFiles[file]}\n\n`)
|
|
41
61
|
.join('\n');
|
|
@@ -45,6 +65,10 @@ const tokens = async (options) => {
|
|
|
45
65
|
${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
|
|
46
66
|
|
|
47
67
|
${contextFileContents}`;
|
|
68
|
+
/**
|
|
69
|
+
* Estimates the number of tokens based on the prompt length.
|
|
70
|
+
* This is a rough estimation and may not be exact.
|
|
71
|
+
*/
|
|
48
72
|
const tokens = Math.ceil(prompt.length / 4).toLocaleString();
|
|
49
73
|
console.log(`Estimated tokens: ${tokens}`);
|
|
50
74
|
};
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ const commit_1 = __importDefault(require("./commands/generate/commit"));
|
|
|
17
17
|
const context_1 = __importDefault(require("./commands/generate/context"));
|
|
18
18
|
const unit_tests_1 = __importDefault(require("./commands/generate/unit_tests"));
|
|
19
19
|
const review_1 = __importDefault(require("./commands/generate/review"));
|
|
20
|
-
const
|
|
20
|
+
const docs_1 = __importDefault(require("./commands/generate/docs"));
|
|
21
21
|
const prompt_utils_1 = require("./utils/prompt_utils");
|
|
22
22
|
const program = new commander_1.Command();
|
|
23
23
|
program
|
|
@@ -123,12 +123,12 @@ generate
|
|
|
123
123
|
.option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
|
|
124
124
|
.action(review_1.default);
|
|
125
125
|
generate
|
|
126
|
-
.command('
|
|
126
|
+
.command('docs')
|
|
127
127
|
.alias('d')
|
|
128
128
|
.description('Generate documentation for the project')
|
|
129
129
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
130
130
|
.option('-f, --files <filenames...>', 'Specify filenames for files you want to document')
|
|
131
131
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
132
132
|
.option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
|
|
133
|
-
.action(
|
|
133
|
+
.action(docs_1.default);
|
|
134
134
|
program.parse(process.argv);
|
|
@@ -23,7 +23,17 @@ class BlockManager extends events_1.EventEmitter {
|
|
|
23
23
|
content = this.buffer + content;
|
|
24
24
|
this.buffer = '';
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
// if content has multiple \n``` in the string, then we need to split it up
|
|
27
|
+
if (content.includes('\n```')) {
|
|
28
|
+
while (content.includes('\n```')) {
|
|
29
|
+
const index = content.indexOf('\n```');
|
|
30
|
+
this.processCodeBlocks(content.slice(0, index + 4));
|
|
31
|
+
content = content.slice(index + 4);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (content !== '') {
|
|
35
|
+
this.processCodeBlocks(content);
|
|
36
|
+
}
|
|
27
37
|
}
|
|
28
38
|
processCodeBlocks(content) {
|
|
29
39
|
const codeBlockMarker = '\n```';
|
|
@@ -65,18 +75,12 @@ class BlockManager extends events_1.EventEmitter {
|
|
|
65
75
|
emitCodeBlock() {
|
|
66
76
|
if (this.codeBuffer.length > 0) {
|
|
67
77
|
const currentNewBlock = this.parseCodeBuffer();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
this.clearBuffer();
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
this.emit('codeBlock', this.codeBuffer);
|
|
78
|
-
this.clearBuffer();
|
|
79
|
-
}
|
|
78
|
+
this.emit('codeBlock', {
|
|
79
|
+
currentNewBlock,
|
|
80
|
+
raw: this.codeBuffer,
|
|
81
|
+
});
|
|
82
|
+
this.emit('content', this.buffer);
|
|
83
|
+
this.clearBuffer();
|
|
80
84
|
}
|
|
81
85
|
}
|
|
82
86
|
/**
|
|
@@ -4,21 +4,16 @@ 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
|
-
const config_utils_1 = require("../utils/config_utils");
|
|
21
14
|
const prompt_utils_1 = require("../utils/prompt_utils");
|
|
15
|
+
const prompts_1 = require("../utils/prompts");
|
|
16
|
+
const StreamManager_1 = __importDefault(require("./StreamManager"));
|
|
22
17
|
const specialCommands = [
|
|
23
18
|
'save',
|
|
24
19
|
'commit',
|
|
@@ -32,36 +27,27 @@ const specialCommands = [
|
|
|
32
27
|
'help',
|
|
33
28
|
'exit',
|
|
34
29
|
];
|
|
35
|
-
class ChatManager {
|
|
30
|
+
class ChatManager extends StreamManager_1.default {
|
|
36
31
|
/**
|
|
37
32
|
* Creates an instance of ChatManager.
|
|
38
33
|
* @param {ClovingGPTOptions} options - Configuration options for the ChatManager.
|
|
39
34
|
*/
|
|
40
35
|
constructor(options) {
|
|
41
|
-
|
|
42
|
-
this.chatHistory = [];
|
|
36
|
+
super(options);
|
|
43
37
|
this.commandHistory = [];
|
|
44
38
|
this.historyIndex = -1;
|
|
45
39
|
this.multilineInput = '';
|
|
46
40
|
this.isMultilineMode = false;
|
|
47
|
-
this.contextFiles = {};
|
|
48
|
-
this.prompt = '';
|
|
49
|
-
this.isProcessing = false;
|
|
50
41
|
this.isSilent = false;
|
|
51
|
-
this.retryCount = 0;
|
|
52
|
-
this.maxRetries = 3;
|
|
53
42
|
this.isSilent = options.silent || false;
|
|
54
43
|
options.stream = true;
|
|
55
44
|
options.silent = true;
|
|
56
|
-
this.gpt = new cloving_gpt_1.default(options);
|
|
57
45
|
this.rl = readline_1.default.createInterface({
|
|
58
46
|
input: process.stdin,
|
|
59
47
|
output: process.stdout,
|
|
60
48
|
prompt: colors_1.default.green.bold('cloving> '),
|
|
61
49
|
historySize: 1000,
|
|
62
50
|
});
|
|
63
|
-
this.chunkManager = new ChunkManager_1.default();
|
|
64
|
-
this.blockManager = new BlockManager_1.default();
|
|
65
51
|
}
|
|
66
52
|
/**
|
|
67
53
|
* Initializes the ChatManager by loading context files, displaying a welcome message,
|
|
@@ -74,8 +60,7 @@ class ChatManager {
|
|
|
74
60
|
console.log(`\n🍀 🍀 🍀 ${colors_1.default.bold('Welcome to Cloving REPL')} 🍀 🍀 🍀\n`);
|
|
75
61
|
this.displayAvailableCommands();
|
|
76
62
|
console.log('\nWhat would you like to do?');
|
|
77
|
-
this.
|
|
78
|
-
this.setupEventListeners();
|
|
63
|
+
this.setupReadline();
|
|
79
64
|
}
|
|
80
65
|
displayAvailableCommands() {
|
|
81
66
|
console.log(`Type a freeform request or question to interact with your Cloving AI pair programmer.\n`);
|
|
@@ -93,69 +78,15 @@ class ChatManager {
|
|
|
93
78
|
console.log(` - ${colors_1.default.yellow(colors_1.default.bold('exit'))} Quit this session`);
|
|
94
79
|
}
|
|
95
80
|
/**
|
|
96
|
-
*
|
|
97
|
-
* If a new version is available, it notifies the user.
|
|
81
|
+
* Sets up readline and event listeners for the readline interface and process input.
|
|
98
82
|
* @private
|
|
99
|
-
* @returns {Promise<void>}
|
|
100
83
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
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.
|
|
118
|
-
* @private
|
|
119
|
-
*/
|
|
120
|
-
setupEventListeners() {
|
|
84
|
+
setupReadline() {
|
|
85
|
+
this.rl.prompt();
|
|
121
86
|
this.rl.on('line', this.handleLine.bind(this));
|
|
122
87
|
this.rl.on('close', this.handleClose.bind(this));
|
|
123
88
|
process.stdin.on('keypress', this.handleKeypress.bind(this));
|
|
124
89
|
}
|
|
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
90
|
/**
|
|
160
91
|
* Handles each line of input from the user.
|
|
161
92
|
* @private
|
|
@@ -365,23 +296,6 @@ class ChatManager {
|
|
|
365
296
|
}
|
|
366
297
|
this.rl.prompt();
|
|
367
298
|
}
|
|
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
299
|
isRemoveCommand(command) {
|
|
386
300
|
return command.startsWith('rm ');
|
|
387
301
|
}
|
|
@@ -458,14 +372,14 @@ class ChatManager {
|
|
|
458
372
|
async handleReview() {
|
|
459
373
|
this.options.stream = false;
|
|
460
374
|
const reviewManager = new ReviewManager_1.default(this.options);
|
|
461
|
-
await reviewManager.
|
|
375
|
+
await reviewManager.generateReview();
|
|
462
376
|
this.options.stream = true;
|
|
463
377
|
this.rl.prompt();
|
|
464
378
|
}
|
|
465
379
|
async handleCommit() {
|
|
466
380
|
this.options.stream = false;
|
|
467
381
|
const commitManager = new CommitManager_1.default(this.options);
|
|
468
|
-
await commitManager.
|
|
382
|
+
await commitManager.generateCommit();
|
|
469
383
|
this.options.stream = true;
|
|
470
384
|
this.rl.prompt();
|
|
471
385
|
}
|
|
@@ -521,11 +435,9 @@ class ChatManager {
|
|
|
521
435
|
return;
|
|
522
436
|
}
|
|
523
437
|
this.isProcessing = true;
|
|
524
|
-
this.chunkManager = new ChunkManager_1.default();
|
|
525
438
|
try {
|
|
526
439
|
await this.refreshContext();
|
|
527
|
-
this.
|
|
528
|
-
this.addChatHistory(this.prompt);
|
|
440
|
+
this.addUserPrompt(this.generatePrompt(input));
|
|
529
441
|
if (!this.isSilent) {
|
|
530
442
|
const fullPrompt = this.chatHistory
|
|
531
443
|
.map((m) => {
|
|
@@ -580,120 +492,23 @@ class ChatManager {
|
|
|
580
492
|
prompt: this.prompt,
|
|
581
493
|
messages: this.chatHistory,
|
|
582
494
|
});
|
|
583
|
-
|
|
584
|
-
this.handleResponseStream(responseStream, accumulatedContent);
|
|
495
|
+
this.handleResponseStream(responseStream);
|
|
585
496
|
}
|
|
586
497
|
catch (err) {
|
|
587
498
|
this.handleError(err);
|
|
588
499
|
}
|
|
589
500
|
}
|
|
590
|
-
|
|
591
|
-
this.
|
|
592
|
-
}
|
|
593
|
-
/**
|
|
594
|
-
* Handles the streaming response from the AI model.
|
|
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();
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
async finalizeResponse(accumulatedContent) {
|
|
683
|
-
this.chatHistory.push({ role: 'assistant', content: accumulatedContent.trim() });
|
|
501
|
+
async finalizeResponse() {
|
|
502
|
+
this.addAssistantResponse(this.responseString);
|
|
684
503
|
this.isProcessing = false;
|
|
685
|
-
if (
|
|
686
|
-
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(
|
|
504
|
+
if (this.responseString !== '') {
|
|
505
|
+
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
|
|
687
506
|
const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
|
|
688
507
|
if (!canApply) {
|
|
689
508
|
if (this.retryCount < this.maxRetries) {
|
|
690
509
|
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
510
|
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}`);
|
|
511
|
+
this.processUserInput(`${prompts_1.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
|
|
697
512
|
return;
|
|
698
513
|
}
|
|
699
514
|
else {
|
|
@@ -720,8 +535,7 @@ You can follow up with another request or:
|
|
|
720
535
|
`);
|
|
721
536
|
this.rl.prompt();
|
|
722
537
|
}
|
|
723
|
-
handleError(
|
|
724
|
-
const error = err;
|
|
538
|
+
handleError(error) {
|
|
725
539
|
let errorMessage = error.message || 'An error occurred.';
|
|
726
540
|
const errorNumber = error.response?.status || 'unknown';
|
|
727
541
|
switch (errorNumber) {
|
|
@@ -738,7 +552,6 @@ You can follow up with another request or:
|
|
|
738
552
|
errorMessage = 'Internal server error';
|
|
739
553
|
break;
|
|
740
554
|
}
|
|
741
|
-
const promptTokens = Math.ceil(this.prompt.length / 4).toLocaleString();
|
|
742
555
|
const statusMessage = error.response?.data?.statusMessage;
|
|
743
556
|
console.error(colors_1.default.red(`\nError (${errorNumber}) while submitting the prompt to the AI API\n\n${statusMessage}\n`));
|
|
744
557
|
this.isProcessing = false;
|
|
@@ -774,7 +587,8 @@ ${prompt}
|
|
|
774
587
|
|
|
775
588
|
### Note
|
|
776
589
|
|
|
777
|
-
Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block
|
|
590
|
+
Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block.
|
|
591
|
+
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
592
|
}
|
|
779
593
|
handleClose() {
|
|
780
594
|
console.log('Goodbye!');
|