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 [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(analysis);
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 = `Don't apologize and when generating code, always 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.`;
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().toLowerCase();
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 [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(lastResponse.content);
147
- if (files.length > 0) {
148
- await (0, string_utils_1.saveGeneratedFiles)(files, fileContents);
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
- return `${contextFileContents}
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
- ### Task
280
+ ### Current Request
256
281
 
257
282
  ${PREAMBLE}
258
283
 
259
- ${this.chatHistory.map(msg => `${msg.role.toUpperCase()}: ${msg.content}`).join('\n\n')}`;
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(files, fileContents) {
103
- for (const file of files) {
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 [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
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)(files, fileContents);
113
- await this.updateContextFiles(files, fileContents);
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], fileContents);
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)(files, fileContents);
207
- await this.updateContextFiles(files, fileContents);
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 [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
272
- await (0, string_utils_1.saveGeneratedFiles)(files, fileContents);
273
- await this.updateContextFiles(files, fileContents);
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 [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
304
- await (0, string_utils_1.saveGeneratedFiles)(files, fileContents);
305
- await this.updateContextFiles(files, fileContents);
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.20",
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
- const matches = rawCodeCommand.match(/(\*{2})([^\*]+)(\*{2})/g);
105
- if (!matches)
106
- return [files, fileContents];
107
- for (const match of matches) {
108
- const fileName = match.replace(/\*{2}/g, '').trim();
109
- const escapedFileName = fileName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
110
- const regex = new RegExp(`\\*\\*${escapedFileName}\\*\\*\\n\\n\\\`{3}([\\s\\S]+?)\\\`{3}(\n|$)`, 'g');
111
- const contentMatch = regex.exec(rawCodeCommand);
112
- if (contentMatch) {
113
- files.push(fileName);
114
- let content = contentMatch[1];
115
- // Remove the first word after the opening triple backticks
116
- content = content.split('\n').map((line, idx) => idx === 0 ? line.replace(/^\w+\s*/, '') : line).join('\n');
117
- fileContents[fileName] = content.trim();
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 [files, fileContents];
149
+ return fileContents;
121
150
  };
122
151
  exports.extractFilesAndContent = extractFilesAndContent;
123
- const saveGeneratedFiles = async (files, fileContents) => {
124
- for (const file of files) {
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",