cloving 0.1.22 → 0.1.24
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.
|
@@ -64,7 +64,8 @@ Enumerate all the files in the provided list${gitDiff ? ' and git diff' : ''} as
|
|
|
64
64
|
Also, list any test files that might be relevant to these files.`;
|
|
65
65
|
};
|
|
66
66
|
const handleUserAction = async (analysis, autoSave = false) => {
|
|
67
|
-
const
|
|
67
|
+
const fileContents = (0, string_utils_1.extractFilesAndContent)(analysis);
|
|
68
|
+
const files = Object.keys(fileContents);
|
|
68
69
|
if (autoSave) {
|
|
69
70
|
for (const file of files) {
|
|
70
71
|
if (fileContents[file]) {
|
|
@@ -13,7 +13,7 @@ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
|
13
13
|
const git_utils_1 = require("../utils/git_utils");
|
|
14
14
|
const string_utils_1 = require("../utils/string_utils");
|
|
15
15
|
const command_utils_1 = require("../utils/command_utils");
|
|
16
|
-
const PREAMBLE = `
|
|
16
|
+
const PREAMBLE = `When generating code, don't apologize and wherever possible include filenames in bold with paths to the code files mentioned and do not be lazy and ask me to keep the existing code or show things like previous code remains unchanged, always include existing code in the response.`;
|
|
17
17
|
class ChatManager {
|
|
18
18
|
constructor(options) {
|
|
19
19
|
this.options = options;
|
|
@@ -24,6 +24,7 @@ class ChatManager {
|
|
|
24
24
|
this.isMultilineMode = false;
|
|
25
25
|
this.contextFiles = {};
|
|
26
26
|
this.prompt = '';
|
|
27
|
+
this.isProcessing = false;
|
|
27
28
|
options.stream = true;
|
|
28
29
|
options.silent = true;
|
|
29
30
|
this.gpt = new cloving_gpt_1.default(options);
|
|
@@ -69,6 +70,9 @@ class ChatManager {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
async handleLine(line) {
|
|
73
|
+
if (this.isProcessing) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
72
76
|
if (this.isMultilineMode) {
|
|
73
77
|
if (line.trim() === '```') {
|
|
74
78
|
this.isMultilineMode = false;
|
|
@@ -84,17 +88,17 @@ class ChatManager {
|
|
|
84
88
|
else if (line.trim() === '```') {
|
|
85
89
|
this.isMultilineMode = true;
|
|
86
90
|
this.multilineInput = '';
|
|
87
|
-
console.log('Entering multiline mode. Type ``` on a new line to end
|
|
91
|
+
console.log('Entering multiline mode. Type ``` on a new line to end.\n');
|
|
88
92
|
this.rl.prompt();
|
|
89
93
|
return;
|
|
90
94
|
}
|
|
91
|
-
const trimmedLine = line.trim()
|
|
95
|
+
const trimmedLine = line.trim();
|
|
92
96
|
if (trimmedLine === '') {
|
|
93
97
|
this.rl.prompt();
|
|
94
98
|
return;
|
|
95
99
|
}
|
|
96
100
|
this.updateCommandHistory(trimmedLine);
|
|
97
|
-
if (trimmedLine === 'exit') {
|
|
101
|
+
if (trimmedLine.toLowerCase() === 'exit') {
|
|
98
102
|
this.rl.close();
|
|
99
103
|
return;
|
|
100
104
|
}
|
|
@@ -143,9 +147,9 @@ class ChatManager {
|
|
|
143
147
|
async handleSave() {
|
|
144
148
|
const lastResponse = this.chatHistory.filter(msg => msg.role === 'assistant').pop();
|
|
145
149
|
if (lastResponse) {
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
148
|
-
await (0, string_utils_1.saveGeneratedFiles)(
|
|
150
|
+
const fileContents = (0, string_utils_1.extractFilesAndContent)(lastResponse.content);
|
|
151
|
+
if (Object.keys(fileContents).length > 0) {
|
|
152
|
+
await (0, string_utils_1.saveGeneratedFiles)(fileContents);
|
|
149
153
|
console.info('Files have been saved.');
|
|
150
154
|
}
|
|
151
155
|
else {
|
|
@@ -192,6 +196,12 @@ class ChatManager {
|
|
|
192
196
|
}
|
|
193
197
|
}
|
|
194
198
|
async processUserInput(input) {
|
|
199
|
+
if (this.isProcessing) {
|
|
200
|
+
console.log('Please wait for the current request to complete.');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
this.isProcessing = true;
|
|
204
|
+
this.chunkManager = new ChunkManager_1.default();
|
|
195
205
|
try {
|
|
196
206
|
this.chatHistory.push({ role: 'user', content: input });
|
|
197
207
|
this.prompt = this.generatePrompt(input);
|
|
@@ -214,10 +224,14 @@ class ChatManager {
|
|
|
214
224
|
});
|
|
215
225
|
responseStream.data.on('end', () => {
|
|
216
226
|
this.chatHistory.push({ role: 'assistant', content: accumulatedContent.trim() });
|
|
227
|
+
this.isProcessing = false;
|
|
228
|
+
process.stdout.write('\n');
|
|
217
229
|
this.rl.prompt();
|
|
218
230
|
});
|
|
219
231
|
responseStream.data.on('error', (error) => {
|
|
220
232
|
console.error('Error streaming response:', error);
|
|
233
|
+
this.isProcessing = false;
|
|
234
|
+
process.stdout.write('\n');
|
|
221
235
|
this.rl.prompt();
|
|
222
236
|
});
|
|
223
237
|
}
|
|
@@ -242,7 +256,8 @@ class ChatManager {
|
|
|
242
256
|
}
|
|
243
257
|
// get token estimate for prompt
|
|
244
258
|
const promptTokens = Math.ceil(this.prompt.length / 4).toLocaleString();
|
|
245
|
-
console.error(`Error processing a ${promptTokens} token prompt:`, errorMessage, `(${errorNumber})`);
|
|
259
|
+
console.error(`Error processing a ${promptTokens} token prompt:`, errorMessage, `(${errorNumber})\n`);
|
|
260
|
+
this.isProcessing = false;
|
|
246
261
|
this.rl.prompt();
|
|
247
262
|
}
|
|
248
263
|
}
|
|
@@ -250,13 +265,23 @@ class ChatManager {
|
|
|
250
265
|
const contextFileContents = Object.keys(this.contextFiles)
|
|
251
266
|
.map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`)
|
|
252
267
|
.join('\n');
|
|
253
|
-
|
|
268
|
+
const allButLast = this.chatHistory.slice(0, -1).map(msg => `${msg.role.toUpperCase()}: ${msg.content}`).join('\n\n');
|
|
269
|
+
const lastUserMessage = this.chatHistory.slice(-1).map(msg => msg.content).join('\n\n');
|
|
270
|
+
return `### Request
|
|
271
|
+
|
|
272
|
+
${lastUserMessage}
|
|
273
|
+
|
|
274
|
+
${contextFileContents}
|
|
275
|
+
|
|
276
|
+
### Full Chat History Context
|
|
277
|
+
|
|
278
|
+
${allButLast}
|
|
254
279
|
|
|
255
|
-
###
|
|
280
|
+
### Current Request
|
|
256
281
|
|
|
257
282
|
${PREAMBLE}
|
|
258
283
|
|
|
259
|
-
${
|
|
284
|
+
${lastUserMessage}`;
|
|
260
285
|
}
|
|
261
286
|
handleClose() {
|
|
262
287
|
console.log('Goodbye!');
|
|
@@ -99,18 +99,19 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
99
99
|
}
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
|
-
async updateContextFiles(
|
|
103
|
-
for (const file of
|
|
102
|
+
async updateContextFiles(fileContents) {
|
|
103
|
+
for (const file of Object.keys(fileContents)) {
|
|
104
104
|
if (fileContents[file]) {
|
|
105
105
|
this.contextFiles[file] = fileContents[file];
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
async handleUserAction(rawCodeCommand, prompt) {
|
|
110
|
-
const
|
|
110
|
+
const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
|
|
111
|
+
const files = Object.keys(fileContents);
|
|
111
112
|
if (this.options.save) {
|
|
112
|
-
await (0, string_utils_1.saveGeneratedFiles)(
|
|
113
|
-
await this.updateContextFiles(
|
|
113
|
+
await (0, string_utils_1.saveGeneratedFiles)(fileContents);
|
|
114
|
+
await this.updateContextFiles(fileContents);
|
|
114
115
|
return;
|
|
115
116
|
}
|
|
116
117
|
const action = await (0, prompts_1.select)({
|
|
@@ -190,7 +191,7 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
190
191
|
await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
191
192
|
await fs_1.default.promises.writeFile(filePath, fileContents[fileToSave]);
|
|
192
193
|
console.log(`${fileToSave} has been saved.`);
|
|
193
|
-
await this.updateContextFiles([fileToSave]
|
|
194
|
+
await this.updateContextFiles({ [fileToSave]: fileContents[fileToSave] });
|
|
194
195
|
}
|
|
195
196
|
else {
|
|
196
197
|
console.log('File content not found.');
|
|
@@ -203,8 +204,8 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
203
204
|
}
|
|
204
205
|
break;
|
|
205
206
|
case 'saveAll':
|
|
206
|
-
await (0, string_utils_1.saveGeneratedFiles)(
|
|
207
|
-
await this.updateContextFiles(
|
|
207
|
+
await (0, string_utils_1.saveGeneratedFiles)(fileContents);
|
|
208
|
+
await this.updateContextFiles(fileContents);
|
|
208
209
|
console.log('All files have been saved.');
|
|
209
210
|
break;
|
|
210
211
|
case 'done':
|
|
@@ -268,9 +269,9 @@ Please briefly explain how the code works in this.`;
|
|
|
268
269
|
let rawCodeCommand = await this.generateCode(prompt);
|
|
269
270
|
this.displayGeneratedCode(rawCodeCommand);
|
|
270
271
|
if (this.options.save) {
|
|
271
|
-
const
|
|
272
|
-
await (0, string_utils_1.saveGeneratedFiles)(
|
|
273
|
-
await this.updateContextFiles(
|
|
272
|
+
const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
|
|
273
|
+
await (0, string_utils_1.saveGeneratedFiles)(fileContents);
|
|
274
|
+
await this.updateContextFiles(fileContents);
|
|
274
275
|
}
|
|
275
276
|
else {
|
|
276
277
|
await this.handleUserAction(rawCodeCommand, prompt);
|
|
@@ -300,9 +301,9 @@ Please briefly explain how the code works in this.`;
|
|
|
300
301
|
rawCodeCommand = await this.generateCode(newPrompt, rawCodeCommand);
|
|
301
302
|
this.displayGeneratedCode(rawCodeCommand);
|
|
302
303
|
if (this.options.save || this.options.interactive) {
|
|
303
|
-
const
|
|
304
|
-
await (0, string_utils_1.saveGeneratedFiles)(
|
|
305
|
-
await this.updateContextFiles(
|
|
304
|
+
const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
|
|
305
|
+
await (0, string_utils_1.saveGeneratedFiles)(fileContents);
|
|
306
|
+
await this.updateContextFiles(fileContents);
|
|
306
307
|
}
|
|
307
308
|
else {
|
|
308
309
|
await this.handleUserAction(rawCodeCommand, newPrompt);
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloving",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"packageManager": "yarn@1.22.22",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"eslint-plugin-jsdoc": "^48.11.0",
|
|
50
50
|
"eslint-plugin-n": "^17.10.1",
|
|
51
51
|
"eslint-plugin-promise": "^7.0.0",
|
|
52
|
+
"express": "^4.19.2",
|
|
52
53
|
"ignore": "^5.3.1",
|
|
53
54
|
"ini": "^4.1.3",
|
|
54
55
|
"isbinaryfile": "^5.0.2",
|
|
@@ -57,7 +58,9 @@
|
|
|
57
58
|
"vite-node": "^2.0.5"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
61
|
+
"@types/body-parser": "^1.19.5",
|
|
60
62
|
"@types/copy-paste": "^1.1.33",
|
|
63
|
+
"@types/express": "^4.17.21",
|
|
61
64
|
"@types/ini": "^4.1.1",
|
|
62
65
|
"@types/inquirer": "^9.0.7",
|
|
63
66
|
"@types/jest": "^29.5.12",
|
|
@@ -98,30 +98,59 @@ const parseMarkdownInstructions = (input) => {
|
|
|
98
98
|
exports.parseMarkdownInstructions = parseMarkdownInstructions;
|
|
99
99
|
const extractFilesAndContent = (rawCodeCommand) => {
|
|
100
100
|
if (!rawCodeCommand)
|
|
101
|
-
return
|
|
101
|
+
return {};
|
|
102
102
|
const files = [];
|
|
103
103
|
const fileContents = {};
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
104
|
+
let currentIndex = 0;
|
|
105
|
+
const commandLength = rawCodeCommand.length;
|
|
106
|
+
while (currentIndex < commandLength) {
|
|
107
|
+
let fileNameStart = rawCodeCommand.indexOf('**', currentIndex);
|
|
108
|
+
if (fileNameStart === -1)
|
|
109
|
+
break;
|
|
110
|
+
let fileNameEnd = rawCodeCommand.indexOf('**', fileNameStart + 2);
|
|
111
|
+
if (fileNameEnd === -1)
|
|
112
|
+
break;
|
|
113
|
+
let nextBoldStart = rawCodeCommand.indexOf('**', fileNameEnd + 2);
|
|
114
|
+
let nextCodeStart = rawCodeCommand.indexOf('\n```', fileNameEnd + 2);
|
|
115
|
+
while (nextBoldStart !== -1 && nextCodeStart > nextBoldStart) {
|
|
116
|
+
fileNameStart = nextBoldStart;
|
|
117
|
+
fileNameEnd = rawCodeCommand.indexOf('**', fileNameStart + 2);
|
|
118
|
+
nextBoldStart = rawCodeCommand.indexOf('**', fileNameEnd + 2);
|
|
119
|
+
nextCodeStart = rawCodeCommand.indexOf('\n```', fileNameEnd + 2);
|
|
118
120
|
}
|
|
121
|
+
const fileName = rawCodeCommand.slice(fileNameStart + 2, fileNameEnd).trim();
|
|
122
|
+
files.push(fileName);
|
|
123
|
+
const codeBlockStart = rawCodeCommand.indexOf('\n```', fileNameEnd);
|
|
124
|
+
if (codeBlockStart === -1)
|
|
125
|
+
break;
|
|
126
|
+
let codeBlockEnd = codeBlockStart + 4;
|
|
127
|
+
while (codeBlockEnd < commandLength) {
|
|
128
|
+
if (rawCodeCommand.slice(codeBlockEnd, codeBlockEnd + 5) === '\n```\n') {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
else if (rawCodeCommand.slice(codeBlockEnd, codeBlockEnd + 4) === '\n```' && codeBlockEnd + 4 === commandLength) {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
codeBlockEnd++;
|
|
135
|
+
}
|
|
136
|
+
if (codeBlockEnd >= commandLength)
|
|
137
|
+
break;
|
|
138
|
+
let content = rawCodeCommand.slice(codeBlockStart + 4, codeBlockEnd).trim();
|
|
139
|
+
const firstNewlineIndex = content.indexOf('\n');
|
|
140
|
+
if (firstNewlineIndex !== -1) {
|
|
141
|
+
content = content.slice(firstNewlineIndex + 1);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
content = content.replace(/^\w+\s*/, '');
|
|
145
|
+
}
|
|
146
|
+
fileContents[fileName] = content.trim();
|
|
147
|
+
currentIndex = codeBlockEnd + 5;
|
|
119
148
|
}
|
|
120
|
-
return
|
|
149
|
+
return fileContents;
|
|
121
150
|
};
|
|
122
151
|
exports.extractFilesAndContent = extractFilesAndContent;
|
|
123
|
-
const saveGeneratedFiles = async (
|
|
124
|
-
for (const file of
|
|
152
|
+
const saveGeneratedFiles = async (fileContents) => {
|
|
153
|
+
for (const file of Object.keys(fileContents)) {
|
|
125
154
|
if (fileContents[file]) {
|
|
126
155
|
const filePath = path_1.default.resolve(file);
|
|
127
156
|
await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|