cloving 0.3.12 → 0.3.14

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.
Files changed (39) hide show
  1. package/dist/cloving_gpt/index.js +1 -1
  2. package/dist/commands/config.js +2 -2
  3. package/dist/index.js +0 -0
  4. package/dist/managers/BlockManager.js +20 -7
  5. package/dist/managers/ChatManager.js +40 -19
  6. package/dist/managers/CodeManager.js +13 -11
  7. package/dist/managers/DocsManager.js +2 -0
  8. package/dist/managers/InitManager.js +18 -5
  9. package/dist/managers/ProxyManager.js +19 -2
  10. package/dist/managers/StreamManager.js +44 -27
  11. package/dist/package.json +1 -1
  12. package/dist/utils/prompts/CODEGEN_INSTRUCTIONS.md +8 -7
  13. package/dist/utils/string_utils.js +10 -5
  14. package/package.json +1 -1
  15. package/dist/abstract_gpt/index.js +0 -55
  16. package/dist/cloving_gpt/adapters/Adapter.js +0 -2
  17. package/dist/cloving_gpt/adapters/gpt4all.js +0 -29
  18. package/dist/commands/analyze.js +0 -47
  19. package/dist/commands/check.js +0 -7
  20. package/dist/commands/commit.js +0 -44
  21. package/dist/commands/describe.js +0 -235
  22. package/dist/commands/generate/documentation.js +0 -11
  23. package/dist/commands/generate/fizzbuzz.js +0 -35
  24. package/dist/commands/generate/init.js +0 -15
  25. package/dist/commands/generate/sh.js +0 -95
  26. package/dist/commands/project/build.js +0 -88
  27. package/dist/commands/project/complete.js +0 -12
  28. package/dist/commands/project/init.js +0 -82
  29. package/dist/commands/project/plan.js +0 -108
  30. package/dist/commands/review.js +0 -50
  31. package/dist/commands/unit_tests.js +0 -150
  32. package/dist/commands/update.js +0 -7
  33. package/dist/file2.js +0 -4
  34. package/dist/managers/DocumentationManager.js +0 -114
  35. package/dist/managers/src/managers/ProxyManager.js +0 -1
  36. package/dist/newfile.js +0 -2
  37. package/dist/utils/command_utils.js +0 -384
  38. package/dist/utils/gitUtils.js +0 -42
  39. package/dist/utils/modelUtils.js +0 -7
@@ -32,7 +32,7 @@ class ClovingGPT {
32
32
  const { provider, model, config: modelConfig } = primaryModel;
33
33
  this.apiKey = modelConfig.apiKey;
34
34
  this.temperature = options.temperature || modelConfig.temperature || 0.2;
35
- this.silent = options.silent || modelConfig.silent;
35
+ this.silent = options.silent || modelConfig.silent || false;
36
36
  this.stream = options.stream || false;
37
37
  this.timeout = options.timeout || 60000; // Default timeout of 60 seconds
38
38
  switch (provider) {
@@ -89,14 +89,14 @@ const config = async () => {
89
89
  */
90
90
  const review = await (0, prompts_1.confirm)({
91
91
  message: `Do you want to review all prompts before they are sent to ${selectedModel}?`,
92
- default: true,
92
+ default: false,
93
93
  });
94
94
  /**
95
95
  * Prompts the user to decide if they trust the selected model with sensitive information.
96
96
  */
97
97
  const trust = await (0, prompts_1.confirm)({
98
98
  message: `Do you trust ${selectedModel} with sensitive information?`,
99
- default: false,
99
+ default: true,
100
100
  });
101
101
  /**
102
102
  * Prompts the user to enter the temperature setting for the selected model.
package/dist/index.js CHANGED
File without changes
@@ -24,14 +24,19 @@ class BlockManager extends events_1.EventEmitter {
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);
27
+ const codeBlockRegex = /\n```[\s\S]*?\n```/g;
28
+ const codeBlocks = content.match(codeBlockRegex);
29
+ if (codeBlocks) {
30
+ let lastIndex = 0;
31
+ for (const block of codeBlocks) {
32
+ const index = content.indexOf(block, lastIndex);
33
+ this.processCodeBlocks(content.slice(lastIndex, index));
34
+ this.processCodeBlocks(block);
35
+ lastIndex = index + block.length;
32
36
  }
37
+ this.processCodeBlocks(content.slice(lastIndex));
33
38
  }
34
- if (content !== '') {
39
+ else {
35
40
  this.processCodeBlocks(content);
36
41
  }
37
42
  }
@@ -95,7 +100,15 @@ class BlockManager extends events_1.EventEmitter {
95
100
  */
96
101
  parseCodeBuffer() {
97
102
  const results = (0, string_utils_1.extractCurrentNewBlocks)(this.codeBuffer);
98
- return results[0];
103
+ if (results.length === 0) {
104
+ return null;
105
+ }
106
+ const block = results[0];
107
+ if (!block.currentContent || !block.newContent) {
108
+ console.warn('Incomplete code block detected. Skipping.');
109
+ return null;
110
+ }
111
+ return block;
99
112
  }
100
113
  clearBuffer() {
101
114
  this.buffer = '';
@@ -11,6 +11,7 @@ const child_process_1 = require("child_process");
11
11
  const CommitManager_1 = __importDefault(require("./CommitManager"));
12
12
  const ReviewManager_1 = __importDefault(require("./ReviewManager"));
13
13
  const string_utils_1 = require("../utils/string_utils");
14
+ const config_utils_1 = require("../utils/config_utils");
14
15
  const prompt_utils_1 = require("../utils/prompt_utils");
15
16
  const prompts_1 = require("../utils/prompts");
16
17
  const StreamManager_1 = __importDefault(require("./StreamManager"));
@@ -39,7 +40,7 @@ class ChatManager extends StreamManager_1.default {
39
40
  this.multilineInput = '';
40
41
  this.isMultilineMode = false;
41
42
  this.isSilent = false;
42
- this.isSilent = options.silent || false;
43
+ this.isSilent = options.silent || (0, config_utils_1.getConfig)(options).globalSilent || false;
43
44
  options.stream = true;
44
45
  options.silent = true;
45
46
  this.rl = readline_1.default.createInterface({
@@ -98,6 +99,14 @@ class ChatManager extends StreamManager_1.default {
98
99
  return;
99
100
  }
100
101
  const trimmedLine = line.trim();
102
+ if (this.isPastedCode(trimmedLine)) {
103
+ this.multilineInput = trimmedLine;
104
+ this.isMultilineMode = true;
105
+ await this.handleCommand(this.multilineInput);
106
+ this.multilineInput = '';
107
+ this.isMultilineMode = false;
108
+ return;
109
+ }
101
110
  if (this.handleMultilineInput(trimmedLine)) {
102
111
  return;
103
112
  }
@@ -133,6 +142,10 @@ class ChatManager extends StreamManager_1.default {
133
142
  }
134
143
  return false;
135
144
  }
145
+ isPastedCode(line) {
146
+ // Check if the line contains multiple lines (indicating a paste)
147
+ return line.includes('\n') || line.length > 100;
148
+ }
136
149
  handleExitCommand(command) {
137
150
  if (command.toLowerCase() === 'exit') {
138
151
  this.rl.close();
@@ -178,6 +191,11 @@ class ChatManager extends StreamManager_1.default {
178
191
  if (matchingCommand) {
179
192
  command = matchingCommand.split(' ')[0]; // Use only the command part without arguments
180
193
  }
194
+ else {
195
+ console.log(`No matching command found for '${command}'. Processing as user input.`);
196
+ await this.processUserInput(command);
197
+ return;
198
+ }
181
199
  }
182
200
  switch (command) {
183
201
  case 'help':
@@ -438,7 +456,7 @@ class ChatManager extends StreamManager_1.default {
438
456
  try {
439
457
  await this.refreshContext();
440
458
  this.addUserPrompt(this.generatePrompt(input));
441
- if (!this.isSilent) {
459
+ if (!this.gpt.silent) {
442
460
  const fullPrompt = this.chatHistory
443
461
  .map((m) => {
444
462
  const content = m.content
@@ -461,25 +479,11 @@ class ChatManager extends StreamManager_1.default {
461
479
  // Review the prompt using the REPL
462
480
  const tokenCount = Math.ceil(fullPrompt.length / 4).toLocaleString();
463
481
  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)')}`);
464
- const reviewPrompt = await new Promise((resolve) => {
465
- this.rl.question(colors_1.default.green.bold('cloving> '), (answer) => {
466
- resolve(answer.trim().toLowerCase());
467
- });
468
- });
482
+ const reviewPrompt = await this.askQuestion('cloving> ');
469
483
  if (reviewPrompt.startsWith('y') || reviewPrompt === '') {
470
- console.log(colors_1.default.gray.bold('\n---------------------- PROMPT START ----------------------\n'));
471
- const promptParts = fullPrompt.split('\n```');
472
- const highlightedPrompt = promptParts
473
- .map((part, index) => index % 2 === 1 ? (0, cli_highlight_1.default)(part, { language: 'typescript' }) : part)
474
- .join('\n```');
475
- console.log(highlightedPrompt);
476
- console.log(colors_1.default.gray.bold('\n---------------------- PROMPT END ----------------------\n'));
484
+ this.reviewPrompt(fullPrompt);
477
485
  console.log(`Do you want to proceed with this prompt? ${colors_1.default.gray('(Y/n)')}`);
478
- const confirmPrompt = await new Promise((resolve) => {
479
- this.rl.question(colors_1.default.green.bold('cloving> '), (answer) => {
480
- resolve(answer.trim().toLowerCase());
481
- });
482
- });
486
+ const confirmPrompt = await this.askQuestion('cloving> ');
483
487
  if (!confirmPrompt.startsWith('y') && confirmPrompt !== '') {
484
488
  console.log('Operation cancelled by user.');
485
489
  this.isProcessing = false;
@@ -498,9 +502,26 @@ class ChatManager extends StreamManager_1.default {
498
502
  this.handleError(err);
499
503
  }
500
504
  }
505
+ askQuestion(prompt) {
506
+ return new Promise((resolve) => {
507
+ this.rl.question(colors_1.default.green.bold(prompt), (answer) => {
508
+ resolve(answer.trim().toLowerCase());
509
+ });
510
+ });
511
+ }
512
+ reviewPrompt(fullPrompt) {
513
+ console.log(colors_1.default.gray.bold('\n---------------------- PROMPT START ----------------------\n'));
514
+ const promptParts = fullPrompt.split('\n```');
515
+ const highlightedPrompt = promptParts
516
+ .map((part, index) => (index % 2 === 1 ? (0, cli_highlight_1.default)(part, { language: 'typescript' }) : part))
517
+ .join('\n```');
518
+ console.log(highlightedPrompt);
519
+ console.log(colors_1.default.gray.bold('\n---------------------- PROMPT END ----------------------\n'));
520
+ }
501
521
  async finalizeResponse() {
502
522
  this.addAssistantResponse(this.responseString);
503
523
  this.isProcessing = false;
524
+ process.stdout.write('\b'.repeat(100));
504
525
  if (this.responseString !== '') {
505
526
  const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
506
527
  const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
@@ -162,22 +162,24 @@ Please briefly explain how the code works in this.`;
162
162
  const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
163
163
  const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
164
164
  if (!canApply) {
165
- if (this.retryCount < this.maxRetries) {
166
- 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`);
167
- this.retryCount++;
168
- this.generateCode(`${prompts_2.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
169
- return;
170
- }
171
- else {
172
- 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`);
173
- this.retryCount = 0; // Reset the retry count for future attempts
174
- }
165
+ await this.handleUnapplicableBlocks(summary);
175
166
  }
176
167
  else {
177
168
  this.retryCount = 0; // Reset the retry count on successful application
178
169
  }
179
170
  }
180
- this.handleUserAction(this.responseString);
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
+ }
181
183
  }
182
184
  }
183
185
  exports.default = CodeManager;
@@ -81,6 +81,8 @@ class DocsManager extends StreamManager_1.default {
81
81
  }
82
82
  }
83
83
  }
84
+ // Reset responseString after processing
85
+ this.responseString = '';
84
86
  }
85
87
  }
86
88
  exports.default = DocsManager;
@@ -182,15 +182,28 @@ ${JSON.stringify(projectDetails, null, 2)}`;
182
182
  }
183
183
  const clovingJson = JSON.parse(cleanAiChatResponse);
184
184
  const directoriesToCheck = [
185
- ...clovingJson.languages.map((language) => language.directory),
186
- ...clovingJson.frameworks.map((framework) => framework.directory),
187
- ...clovingJson.testingFrameworks.map((testingFramework) => testingFramework.directory),
185
+ ...clovingJson.languages.map((language) => ({
186
+ type: 'Language',
187
+ name: language.name,
188
+ directory: language.directory,
189
+ })),
190
+ ...clovingJson.frameworks.map((framework) => ({
191
+ type: 'Framework',
192
+ name: framework.name,
193
+ directory: framework.directory,
194
+ })),
195
+ ...clovingJson.testingFrameworks.map((testingFramework) => ({
196
+ type: 'Testing Framework',
197
+ name: testingFramework.name,
198
+ directory: testingFramework.directory,
199
+ })),
188
200
  ];
189
- const missingDirectories = directoriesToCheck.filter((directory) => !fs_1.default.existsSync(directory));
201
+ const missingDirectories = directoriesToCheck.filter((item) => !fs_1.default.existsSync(item.directory));
190
202
  if (missingDirectories.length > 0) {
203
+ const missingDirMessages = missingDirectories.map((item) => `${item.type} ${colors_1.default.bold(item.name)}: ${colors_1.default.red.bold(item.directory)}`);
191
204
  return [
192
205
  false,
193
- `${colors_1.default.red.bold('ERROR:')} The following directories were not found: ${colors_1.default.red.bold(missingDirectories.join(', '))}`,
206
+ `${colors_1.default.red.bold('ERROR:')} The following directories were not found:\n${missingDirMessages.join('\n')}`,
194
207
  ];
195
208
  }
196
209
  return [true, ''];
@@ -73,7 +73,17 @@ class ProxyManager {
73
73
  async handleChatCompletions(req, res) {
74
74
  try {
75
75
  const { messages, stream } = req.body;
76
- const content = messages.map((message) => message.content).join('\n\n');
76
+ if (!Array.isArray(messages)) {
77
+ return res.status(400).json({ error: 'Invalid request body: messages must be an array' });
78
+ }
79
+ const content = messages
80
+ .map((message) => {
81
+ if (typeof message !== 'object' || typeof message.content !== 'string') {
82
+ throw new Error('Invalid message format');
83
+ }
84
+ return message.content;
85
+ })
86
+ .join('\n\n');
77
87
  const contextFileContents = Object.keys(this.contextFiles)
78
88
  .map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`)
79
89
  .join('\n');
@@ -99,7 +109,14 @@ ${content}`;
99
109
  }
100
110
  catch (error) {
101
111
  console.error('Error processing request:', error);
102
- res.status(500).json({ error: 'Internal server error' });
112
+ if (error instanceof Error && error.message === 'Invalid message format') {
113
+ res.status(400).json({
114
+ error: 'Invalid request body: each message must have a content property of type string',
115
+ });
116
+ }
117
+ else {
118
+ res.status(500).json({ error: 'Internal server error' });
119
+ }
103
120
  }
104
121
  }
105
122
  async handleStreamResponse(prompt, res) {
@@ -142,17 +142,23 @@ class StreamManager extends events_1.EventEmitter {
142
142
  * @param {CurrentNewBlock} currentNewBlock - The current and new content of the code block.
143
143
  */
144
144
  displayCurrentNewBlock(currentNewBlock) {
145
+ const language = currentNewBlock.language.split('\n')[0];
146
+ process.stdout.write(colors_1.default.gray.bold(`\`\`\`${language} \n`));
147
+ process.stdout.write(colors_1.default.gray.bold(`<<<<<<< CURRENT ${currentNewBlock.filePath}\n`));
145
148
  try {
146
- process.stdout.write(colors_1.default.gray.bold(`\`\`\`${currentNewBlock.language} \n`));
147
- process.stdout.write(colors_1.default.gray.bold(`<<<<<<< CURRENT ${currentNewBlock.filePath}\n`));
148
- process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.currentContent, { language: currentNewBlock.language }));
149
- process.stdout.write(colors_1.default.gray.bold('\n=======\n'));
150
- process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.newContent, { language: currentNewBlock.language }));
151
- process.stdout.write(colors_1.default.gray.bold('\n>>>>>>> NEW\n```'));
149
+ process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.currentContent, { language }));
152
150
  }
153
151
  catch (error) {
154
- console.error(colors_1.default.red('Error displaying the code block:'), error);
152
+ process.stdout.write(currentNewBlock.currentContent);
155
153
  }
154
+ process.stdout.write(colors_1.default.gray.bold('\n=======\n'));
155
+ try {
156
+ process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.newContent, { language }));
157
+ }
158
+ catch (error) {
159
+ process.stdout.write(currentNewBlock.newContent);
160
+ }
161
+ process.stdout.write(colors_1.default.gray.bold('\n>>>>>>> NEW\n```'));
156
162
  }
157
163
  /**
158
164
  * Displays a raw code block.
@@ -235,27 +241,38 @@ class StreamManager extends events_1.EventEmitter {
235
241
  * @returns {Promise<void>}
236
242
  */
237
243
  async loadContextFiles() {
238
- const config = (0, config_utils_1.getClovingConfig)();
239
- const primaryLanguage = config.languages.find((lang) => lang.primary);
240
- const defaultDirectory = primaryLanguage ? primaryLanguage.directory : '.';
241
- const testingDirectories = config.testingFrameworks?.map((framework) => framework.directory) || [];
242
- const testingDirectory = testingDirectories[0];
243
- let files = this.options.files || [defaultDirectory, testingDirectory].filter(Boolean);
244
- if (files.length > 0) {
245
- console.log(`\nBuilding AI prompt context...\n`);
244
+ try {
245
+ const config = (0, config_utils_1.getClovingConfig)();
246
+ const primaryLanguage = config.languages.find((lang) => lang.primary);
247
+ const defaultDirectory = primaryLanguage ? primaryLanguage.directory : '.';
248
+ const testingDirectories = config.testingFrameworks?.map((framework) => framework.directory) || [];
249
+ const testingDirectory = testingDirectories[0];
250
+ let files = this.options.files || [defaultDirectory, testingDirectory].filter(Boolean);
251
+ if (files.length > 0) {
252
+ console.log(`\nBuilding AI prompt context...\n`);
253
+ }
254
+ for (const file of files) {
255
+ // Skip if the file is not a string
256
+ if (!file)
257
+ continue;
258
+ try {
259
+ const previousCount = Object.keys(this.contextFiles).length;
260
+ this.contextFiles = await (0, prompt_utils_2.addFileOrDirectoryToContext)(file, this.contextFiles, this.options);
261
+ const newCount = Object.keys(this.contextFiles).length;
262
+ const addedCount = newCount - previousCount;
263
+ const totalTokens = this.calculateTotalTokens();
264
+ console.log(colors_1.default.cyan(`📁 Loaded context from: ${colors_1.default.bold(file)}`));
265
+ console.log(colors_1.default.green(` ✅ Added ${addedCount} file(s) to context`));
266
+ console.log(colors_1.default.yellow(` 📊 Total tokens in context: ${totalTokens.toLocaleString()}\n`));
267
+ }
268
+ catch (fileError) {
269
+ console.error(colors_1.default.red(`Error loading context from ${file}:`), fileError);
270
+ }
271
+ }
246
272
  }
247
- for (const file of files) {
248
- // Skip if the file is not a string
249
- if (!file)
250
- continue;
251
- const previousCount = Object.keys(this.contextFiles).length;
252
- this.contextFiles = await (0, prompt_utils_2.addFileOrDirectoryToContext)(file, this.contextFiles, this.options);
253
- const newCount = Object.keys(this.contextFiles).length;
254
- const addedCount = newCount - previousCount;
255
- const totalTokens = this.calculateTotalTokens();
256
- console.log(colors_1.default.cyan(`📁 Loaded context from: ${colors_1.default.bold(file)}`));
257
- console.log(colors_1.default.green(` ✅ Added ${addedCount} file(s) to context`));
258
- console.log(colors_1.default.yellow(` 📊 Total tokens in context: ${totalTokens.toLocaleString()}\n`));
273
+ catch (error) {
274
+ console.error(colors_1.default.red('Error loading context files:'), error);
275
+ throw new Error('Failed to load context files');
259
276
  }
260
277
  }
261
278
  calculateTotalTokens() {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,18 +19,19 @@
19
19
 
20
20
  ## *CURRENT/NEW* Block Format
21
21
 
22
- 1. **Start a block with a one line description of change**
23
- 1. **Then provide three backticks and the language name.**
22
+ 1. **Start a block with a one line description of change followed by three backticks and the language name like this:**
23
+ About to add documentation to the `UserService` class.
24
+
24
25
  ```typescript
25
- 2. **Next line: seven <, CURRENT, and the file path.**
26
+ 2. **Next line: seven <, CURRENT, and the file path like this:**
26
27
  <<<<<<< CURRENT path/to/file.ts
27
- 3. **Include the exact existing code to be changed, this section should always have some code characters in it, not just spaces or tabs. The only exception is when creating a brand new file.**
28
- 4. **Divide with seven =.**
28
+ 3. **Include the exact existing code to be changed, this section should always have some code characters in it, not just spaces or tabs.**
29
+ 4. **Divide with seven = like this:**
29
30
  =======
30
31
  5. **Add the new code.**
31
- 6. **End with seven > and NEW.**
32
+ 6. **End with seven > and NEW like this:**
32
33
  >>>>>>> NEW
33
- 7. **Close the block with three backticks.**
34
+ 7. **Close the block with three backticks like this:**
34
35
  ```
35
36
 
36
37
  ## Don't Invent Code That Isn't Provided in Context
@@ -474,10 +474,18 @@ const checkBlocksApplicability = async (blocks) => {
474
474
  for (const [index, block] of blocks.entries()) {
475
475
  const filePath = path_1.default.resolve(block.filePath);
476
476
  let fileContent = await readFileContent(filePath);
477
- if (fileContent === '') {
477
+ if (block.newContent.trim() === '') {
478
+ allApplicable = false;
479
+ summary.push(`[${index + 1}] ${colors_1.default.red.bold(block.filePath)} ${colors_1.default.red.bold('cannot be applied: New content is empty')}`);
480
+ }
481
+ else if (fileContent === '') {
478
482
  summary.push(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} can be ${colors_1.default.green.bold('created')}`);
479
483
  }
480
- else if (block.currentContent.trim() !== '') {
484
+ else if (block.currentContent.trim() === '') {
485
+ allApplicable = false;
486
+ summary.push(`[${index + 1}] ${colors_1.default.red.bold(block.filePath)} ${colors_1.default.red.bold(`cannot be applied: File '${block.filePath}' already exists`)}`);
487
+ }
488
+ else {
481
489
  const { matchingLines } = await analyzeBlockContent(block, fileContent);
482
490
  if (matchingLines.length === 0) {
483
491
  allApplicable = false;
@@ -491,9 +499,6 @@ const checkBlocksApplicability = async (blocks) => {
491
499
  summary.push(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} can be ${colors_1.default.yellow.bold('updated')}`);
492
500
  }
493
501
  }
494
- else {
495
- summary.push(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} can be ${colors_1.default.yellow.bold('updated')}`);
496
- }
497
502
  }
498
503
  return [allApplicable, summary.join('\n')];
499
504
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,55 +0,0 @@
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 axios_1 = __importDefault(require("axios"));
7
- class AbstractGPT {
8
- constructor() {
9
- const clovingModel = process.env.CLOVING_MODEL;
10
- this.apiKey = process.env.CLOVING_API_KEY || '';
11
- if (!clovingModel || !this.apiKey) {
12
- throw new Error("CLOVING_MODEL and CLOVING_API_KEY environment variables must be set");
13
- }
14
- const [provider, model] = clovingModel.split(':');
15
- this.provider = provider;
16
- this.model = model;
17
- }
18
- getEndpoint() {
19
- switch (this.provider) {
20
- case 'openai':
21
- return `https://api.openai.com/v1/engines/${this.model}/completions`;
22
- case 'claude':
23
- return `https://api.anthropic.com/v1/claude/${this.model}`;
24
- case 'gpt4all':
25
- return `https://api.gpt4all.io/v1/models/${this.model}/completions`;
26
- default:
27
- throw new Error(`Unsupported provider: ${this.provider}`);
28
- }
29
- }
30
- async generateText(request) {
31
- const endpoint = this.getEndpoint();
32
- const response = await axios_1.default.post(endpoint, {
33
- model: this.model,
34
- prompt: request.prompt,
35
- max_tokens: request.maxTokens || 100,
36
- temperature: request.temperature || 0.7
37
- }, {
38
- headers: {
39
- 'Authorization': `Bearer ${this.apiKey}`,
40
- 'Content-Type': 'application/json'
41
- }
42
- });
43
- switch (this.provider) {
44
- case 'openai':
45
- return response.data.choices[0].text;
46
- case 'claude':
47
- return response.data.completion;
48
- case 'gpt4all':
49
- return response.data.text;
50
- default:
51
- throw new Error(`Unsupported provider: ${this.provider}`);
52
- }
53
- }
54
- }
55
- exports.default = AbstractGPT;
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GPT4AllAdapter = void 0;
4
- class GPT4AllAdapter {
5
- constructor(model) {
6
- this.model = model;
7
- }
8
- getEndpoint() {
9
- return `https://api.gpt4all.io/v1/models/${this.model}/completions`;
10
- }
11
- getHeaders(apiKey) {
12
- return {
13
- 'Authorization': `Bearer ${apiKey}`,
14
- 'Content-Type': 'application/json'
15
- };
16
- }
17
- getPayload(request) {
18
- return {
19
- model: this.model,
20
- prompt: request.prompt,
21
- max_tokens: request.maxTokens,
22
- temperature: request.temperature || 0.7
23
- };
24
- }
25
- extractResponse(data) {
26
- return data.text;
27
- }
28
- }
29
- exports.GPT4AllAdapter = GPT4AllAdapter;
@@ -1,47 +0,0 @@
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 readline_1 = __importDefault(require("readline"));
7
- const child_process_1 = require("child_process");
8
- const git_utils_1 = require("../utils/git_utils");
9
- const model_utils_1 = require("../utils/model_utils");
10
- const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
11
- const analyze = async () => {
12
- try {
13
- // Define the prompt for analysis
14
- const gitDiff = await (0, git_utils_1.getGitDiff)();
15
- const model = (0, model_utils_1.getModel)();
16
- const prompt = `Explain why the change is being made and document a description of these changes.
17
- Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
18
-
19
- ${gitDiff}`;
20
- // Instantiate ClovingGPT and get the analysis
21
- const gpt = new cloving_gpt_1.default();
22
- const analysis = await gpt.generateText({ prompt });
23
- // Print the analysis to the console
24
- console.log(analysis);
25
- // Prompt the user to copy the analysis to the clipboard
26
- const rl = readline_1.default.createInterface({
27
- input: process.stdin,
28
- output: process.stdout
29
- });
30
- rl.question('Do you want to copy the analysis to the clipboard? [Y/n] ', (answer) => {
31
- if (answer.toLowerCase() === 'y' || answer === '') {
32
- try {
33
- (0, child_process_1.execFileSync)('pbcopy', { input: analysis });
34
- console.log('Analysis copied to clipboard');
35
- }
36
- catch (error) {
37
- console.error('Error: pbcopy command not found. Unable to copy to clipboard.');
38
- }
39
- }
40
- rl.close();
41
- });
42
- }
43
- catch (error) {
44
- console.error('Error during analysis:', error.message);
45
- }
46
- };
47
- exports.default = analyze;
@@ -1,7 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = (options) => {
4
- if (options.verbose)
5
- console.log('Verbose mode enabled');
6
- console.log('Checking something...');
7
- };