cloving 0.1.24 → 0.2.0

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/README.md CHANGED
@@ -159,6 +159,43 @@ Options:
159
159
  - `-m, --model <model>`: Select the model to use
160
160
  - `-p, --prompt <prompt>`: Specify the prompt to use
161
161
 
162
+ ### Interactive Chat
163
+
164
+ Start an interactive chat session with the AI:
165
+ ```bash
166
+ cloving chat
167
+ ```
168
+ Options:
169
+ - `-f, --files <filenames...>`: Specify filenames of files with context to use for the chat
170
+ - `-t, --temperature <temperature>`: Set the temperature for the model (default 0.2)
171
+ - `-m, --model <model>`: Select the model to use
172
+
173
+ ### Proxy Server
174
+
175
+ Start a proxy server to use with Cloving:
176
+ ```bash
177
+ cloving proxy
178
+ ```
179
+ Options:
180
+ - `-f, --files <filenames...>`: Specify filenames of files with context to use for generating code
181
+ - `-t, --temperature <temperature>`: Set the temperature for the model (default 0.2)
182
+ - `-m, --model <model>`: Select the model to use
183
+
184
+ The proxy server allows you to integrate Cloving with other tools or applications. It starts a local server that can receive requests and forward them to the AI model, returning the responses. This is useful for creating custom integrations or using Cloving in scenarios where a direct CLI interaction isn't suitable.
185
+
186
+ ### Token Estimation
187
+
188
+ Estimate the number of tokens in the current working directory or specified files:
189
+ ```bash
190
+ cloving tokens
191
+ ```
192
+ or
193
+ ```bash
194
+ cloving t
195
+ ```
196
+ Options:
197
+ - `-f, --files <filenames...>`: Specify filenames of files to estimate tokens for
198
+
162
199
  ### Generating Context
163
200
 
164
201
  Create a context string for generating prompts outside of the cloving cli:
@@ -179,30 +216,6 @@ Options:
179
216
  - `-f, --files <filenames...>`: Specify filenames for the unit tests
180
217
  - `-m, --model <model>`: Select the model to use
181
218
 
182
- ### Project Commands
183
-
184
- Cloving now includes commands for managing entire projects:
185
-
186
- - Initialize a new project inside a git branch:
187
- ```bash
188
- cloving project init
189
- ```
190
-
191
- - Plan the steps to complete the project:
192
- ```bash
193
- cloving project plan
194
- ```
195
-
196
- - Generate code to build the project:
197
- ```bash
198
- cloving project build
199
- ```
200
-
201
- - Clean up and finalize the project:
202
- ```bash
203
- cloving project complete
204
- ```
205
-
206
219
  ## Command Line Options
207
220
 
208
221
  Usage: `cloving [command] [options]`
@@ -214,6 +227,9 @@ Options:
214
227
  Commands:
215
228
  - `config`: Configure Cloving with your API key and models to use
216
229
  - `init`: Setup Cloving in the current directory
230
+ - `chat`: Start an interactive chat with Cloving
231
+ - `tokens`: Estimate the number of tokens in the current working directory or specified files
232
+ - `proxy`: Start a proxy server to use with Cloving
217
233
  - `models`: List available models
218
234
  - `commit`: Alias for cloving generate commit
219
235
  - `generate`: Generate various items like unit-tests and code reviews
@@ -223,11 +239,6 @@ Commands:
223
239
  - `shell`: Generate a shell command based on a prompt
224
240
  - `unit-tests`: Generate unit tests
225
241
  - `review`: Review the code for committed changes
226
- - `project`: Commands for managing Cloving projects
227
- - `init`: Setup a new Cloving project inside a git branch
228
- - `plan`: Plan the steps to complete the project
229
- - `build`: Generate code to build the project
230
- - `complete`: Clean up and finalize the project
231
242
 
232
243
  Most commands support the following options:
233
244
  - `-s, --silent`: Run the command without asking for confirmation of submitting prompts
@@ -149,12 +149,13 @@ class ClovingGPT {
149
149
  const response = await axios_1.default.post(endpoint, payload, { headers });
150
150
  return this.adapter.extractResponse(response.data);
151
151
  }
152
- catch (error) {
152
+ catch (err) {
153
+ const error = err;
153
154
  let errorMessage = error instanceof Error ? error.message : 'connection error';
154
155
  if (errorMessage === '') {
155
156
  errorMessage = 'connection error';
156
157
  }
157
- console.error(`Error communicating with the GPT server (${this.adapter.getEndpoint(this.stream)}):`, errorMessage);
158
+ console.error(`Error communicating with the GPT server (${this.adapter.getEndpoint(this.stream)})\n\n${JSON.stringify(error.response?.data || "''", null, 2)}\n`);
158
159
  throw error;
159
160
  }
160
161
  }
@@ -6,17 +6,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const config_utils_1 = require("../../utils/config_utils");
7
7
  const config_utils_2 = require("../../utils/config_utils");
8
8
  const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
9
+ const ChatManager_1 = __importDefault(require("../../managers/ChatManager"));
9
10
  const CodeManager_1 = __importDefault(require("../../managers/CodeManager"));
10
11
  const code = async (options) => {
11
12
  options.silent = (0, config_utils_2.getConfig)(options).globalSilent || false;
12
13
  const gpt = new cloving_gpt_1.default(options);
13
- const allSrcFiles = await (0, config_utils_1.getAllFiles)(options, false);
14
- const codeManager = new CodeManager_1.default(gpt, options, allSrcFiles);
15
- if (options.prompt) {
16
- await codeManager.processCode(options.prompt);
14
+ if (options.interactive) {
15
+ options.silent = (0, config_utils_2.getConfig)(options).globalSilent || false;
16
+ options.stream = true;
17
+ const chatManager = new ChatManager_1.default(options);
18
+ await chatManager.initialize();
17
19
  }
18
20
  else {
19
- console.error('No prompt provided.');
21
+ const allSrcFiles = await (0, config_utils_1.getAllFiles)(options, false);
22
+ const codeManager = new CodeManager_1.default(gpt, options, allSrcFiles);
23
+ if (options.prompt) {
24
+ await codeManager.processCode(options.prompt);
25
+ }
26
+ else {
27
+ console.error('No prompt provided.');
28
+ }
20
29
  }
21
30
  };
22
31
  exports.default = code;
@@ -3,204 +3,13 @@ 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 cli_highlight_1 = __importDefault(require("cli-highlight"));
7
- const prompts_1 = require("@inquirer/prompts");
8
- const copy_paste_1 = __importDefault(require("copy-paste"));
9
- const fs_1 = __importDefault(require("fs"));
10
- const path_1 = __importDefault(require("path"));
11
6
  const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
12
- const git_utils_1 = require("../../utils/git_utils");
13
- const string_utils_1 = require("../../utils/string_utils");
14
- const command_utils_1 = require("../../utils/command_utils");
7
+ const ReviewManager_1 = __importDefault(require("../../managers/ReviewManager"));
15
8
  const config_utils_1 = require("../../utils/config_utils");
16
9
  const review = async (options) => {
17
- let { prompt, files } = options;
18
10
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
19
11
  const gpt = new cloving_gpt_1.default(options);
20
- let promptText = `### Prompt
21
-
22
- ${prompt || 'Please provide a code review.'}
23
-
24
- ### Example of a well-structured response
25
-
26
- # Code Review: Enhanced Code Generation and File Handling
27
-
28
- ## Changes Overview
29
-
30
- These changes primarily focus on improving the code generation process and adding more robust file handling capabilities. The main modifications include:
31
-
32
- 1. Enhancing the code generation prompt with a structured example response.
33
- 2. Implementing a new function to extract file names and contents from the generated code.
34
- 3. Adding a new option to save generated code directly to files.
35
- 4. Improving the copy-to-clipboard functionality to allow copying individual files.
36
- 5. Removing an unused import in the review.ts file.
37
-
38
- ## Reason for Changes
39
-
40
- The changes aim to enhance the usability and flexibility of the code generation tool by providing clearer prompts, better file management, and more control over the generated code.
41
-
42
- ## Detailed Description
43
-
44
- 1. **Enhanced Code Generation Prompt** - The \`generateCodePrompt\` function now includes a structured example of a well-formatted response. This helps guide the AI to produce more consistent and usable output, making it easier to parse and use the generated code.
45
- 2. **File Extraction Function** - A new function \`extractFilesAndContent\` has been added to parse the raw code command and extract individual file names and their contents. This enables more granular control over the generated code, allowing for file-specific operations.
46
- 3. **Save to File Option** - A new 'Save Source Code to Files' option has been added to the user action menu. This feature allows users to save generated code directly to files in the appropriate directory structure.
47
- 4. **Improved Copy to Clipboard** - The 'Copy Source Code to Clipboard' option has been enhanced to allow users to choose specific files to copy, rather than copying all generated code at once.
48
- 5. **Removed Unused Import** - The unused import of \`execFileSync\` from 'child_process' has been removed from the review.ts file, improving code cleanliness.
49
-
50
- ## Potential Bugs and Recommended Fixes
51
-
52
- ### 1. Possible undefined access in \`extractFilesAndContent\`
53
-
54
- \`\`\`typescript
55
- const contentMatch = regex.exec(rawCodeCommand)
56
- if (contentMatch) {
57
- files.push(fileName)
58
- let content = contentMatch[1]
59
- // ...
60
- }
61
- \`\`\`
62
-
63
- There's a potential for \`contentMatch[1]\` to be undefined if the regex doesn't capture any groups. To fix this, add a null check:
64
-
65
- \`\`\`typescript
66
- const contentMatch = regex.exec(rawCodeCommand)
67
- if (contentMatch && contentMatch[1]) {
68
- files.push(fileName)
69
- let content = contentMatch[1]
70
- // ...
71
- }
72
- \`\`\`
73
- `;
74
- let contextFiles = {};
75
- try {
76
- // Define the prompt for analysis
77
- if (files) {
78
- let expandedFiles = [];
79
- for (const file of files) {
80
- const filePath = path_1.default.resolve(file);
81
- if (await fs_1.default.promises.stat(filePath).then(stat => stat.isDirectory()).catch(() => false)) {
82
- const dirFiles = await (0, command_utils_1.getAllFilesInDirectory)(filePath);
83
- expandedFiles = expandedFiles.concat(dirFiles.map(f => path_1.default.relative(process.cwd(), f)));
84
- }
85
- else {
86
- expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
87
- }
88
- }
89
- files = expandedFiles;
90
- for (const file of files) {
91
- const filePath = path_1.default.resolve(file);
92
- if (await fs_1.default.promises.stat(filePath).then(stat => stat.isFile()).catch(() => false)) {
93
- const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
94
- contextFiles[file] = content;
95
- }
96
- }
97
- const contextFileContents = Object.keys(contextFiles).map((file) => `### Contents of ${file}\n\n${contextFiles[file]}\n\n`).join('\n');
98
- promptText += `
99
- ### Description of App
100
-
101
- ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
102
-
103
- ${contextFileContents}
104
-
105
- `;
106
- }
107
- else {
108
- const gitDiff = await (0, git_utils_1.getGitDiff)();
109
- promptText = `==== begin diff =====
110
- ${gitDiff}
111
- ==== end diff =====`;
112
- }
113
- if (files) {
114
- promptText += `
115
-
116
- ### Request
117
-
118
- I would like you to explain the code and document a description of it.
119
- List any bugs in the new code as well as recommended fixes for those bugs with code examples.
120
- Format the output of this code review in Markdown format.`;
121
- }
122
- else {
123
- promptText += `
124
-
125
- ### Request
126
-
127
- Do not use any data from the example response structure, only use the structure.
128
- I would like you to explain why these change are being made and document a description of these changes.
129
- Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
130
- Format the output of this code review in Markdown format.`;
131
- }
132
- if (prompt) {
133
- promptText += `
134
-
135
- ### Additional Context
136
-
137
- ${prompt}`;
138
- }
139
- // get the analysis
140
- const analysis = await gpt.generateText({ prompt: promptText });
141
- // Print the analysis to the console
142
- (0, string_utils_1.parseMarkdownInstructions)(analysis).map(code => {
143
- if (code.trim().startsWith('```')) {
144
- const lines = code.split('\n');
145
- const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
146
- console.log(lines[0]);
147
- try {
148
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
149
- }
150
- catch (error) {
151
- // don't highlight if it fails
152
- console.log(lines.slice(1, -1).join('\n'));
153
- }
154
- console.log(lines.slice(-1)[0]);
155
- }
156
- else {
157
- console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
158
- }
159
- });
160
- // Modified Function to extract specific sections from the analysis
161
- const extractSection = (section) => {
162
- const regex = new RegExp(`## ${section}[\\s\\S]*?(?=\\n## Potential Bugs and Recommended Fixes|$)`, 'g');
163
- const match = analysis.match(regex);
164
- return match ? match[0].trim() : '';
165
- };
166
- // Prompt the user with options for copying to clipboard
167
- const clipboardOption = await (0, prompts_1.select)({
168
- message: 'What would you like to copy to the clipboard?',
169
- choices: [
170
- { name: 'Copy only the Changes Overview', value: 'Changes Overview' },
171
- { name: 'Copy only the Potential Bugs and Recommended Fixes', value: 'Potential Bugs and Recommended Fixes' },
172
- { name: 'Copy everything', value: 'Everything' },
173
- { name: 'Done', value: 'Done' }
174
- ]
175
- });
176
- let contentToCopy = '';
177
- switch (clipboardOption) {
178
- case 'Changes Overview':
179
- contentToCopy = extractSection('Changes Overview');
180
- break;
181
- case 'Potential Bugs and Recommended Fixes':
182
- contentToCopy = extractSection('Potential Bugs and Recommended Fixes');
183
- break;
184
- case 'Everything':
185
- contentToCopy = analysis;
186
- break;
187
- case 'Done':
188
- console.log('No content copied to clipboard.');
189
- return;
190
- }
191
- if (contentToCopy) {
192
- copy_paste_1.default.copy(contentToCopy, (err) => {
193
- if (err) {
194
- console.error('Error: Unable to copy to clipboard.', err);
195
- }
196
- else {
197
- console.log('Selected content copied to clipboard');
198
- }
199
- });
200
- }
201
- }
202
- catch (error) {
203
- console.error('Error during analysis:', error.message);
204
- }
12
+ const reviewManager = new ReviewManager_1.default(gpt, options);
13
+ await reviewManager.review();
205
14
  };
206
15
  exports.default = review;
@@ -42,11 +42,8 @@ technologies used. This will provide better context for future Cloving requests.
42
42
  return;
43
43
  }
44
44
  if (!(0, command_utils_1.checkForSpecialFiles)()) {
45
- const technologies = await (0, prompts_1.input)({
46
- message: 'No special files detected. Please describe the main technologies used in your project:',
47
- validate: (input) => input.trim().length > 0 || 'Please provide a description of the technologies used.',
48
- });
49
- specialFileContents['description'] = technologies;
45
+ console.error('No dependencies files detected. Please add a dependency file (e.g. package.json, Gemfile, requirements.txt, etc.) to your project and run `cloving init` again.');
46
+ return;
50
47
  }
51
48
  const tempFilePath = path_1.default.join(os_1.default.tmpdir(), `describe_${Date.now()}.tmp`);
52
49
  try {
package/dist/index.js CHANGED
@@ -19,10 +19,6 @@ const commit_1 = __importDefault(require("./commands/generate/commit"));
19
19
  const context_1 = __importDefault(require("./commands/generate/context"));
20
20
  const unit_tests_1 = __importDefault(require("./commands/generate/unit_tests"));
21
21
  const review_1 = __importDefault(require("./commands/generate/review"));
22
- const init_2 = __importDefault(require("./commands/project/init"));
23
- const plan_1 = __importDefault(require("./commands/project/plan"));
24
- const build_1 = __importDefault(require("./commands/project/build"));
25
- const complete_1 = __importDefault(require("./commands/project/complete"));
26
22
  // Function to get version from package.json
27
23
  const getPackageVersion = () => {
28
24
  const packagePath = (0, path_1.join)(__dirname, 'package.json');
@@ -134,29 +130,4 @@ generate
134
130
  .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
135
131
  .option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
136
132
  .action(review_1.default);
137
- // Project commands
138
- const project = program
139
- .command('project')
140
- .alias('p')
141
- .description('Commands for cloving a project');
142
- project
143
- .command('init')
144
- .alias('i')
145
- .description('Setup a new cloving project inside a git branch')
146
- .action(init_2.default);
147
- project
148
- .command('plan')
149
- .alias('p')
150
- .description('Plan the steps to complete the project')
151
- .action(plan_1.default);
152
- project
153
- .command('build')
154
- .alias('b')
155
- .description('Generate code to build the project')
156
- .action(build_1.default);
157
- project
158
- .command('complete')
159
- .alias('c')
160
- .description('Clean up and finalize the project')
161
- .action(complete_1.default);
162
133
  program.parse(process.argv);
@@ -9,6 +9,7 @@ const fs_1 = __importDefault(require("fs"));
9
9
  const readline_1 = __importDefault(require("readline"));
10
10
  const child_process_1 = require("child_process");
11
11
  const ChunkManager_1 = __importDefault(require("./ChunkManager"));
12
+ const ReviewManager_1 = __importDefault(require("./ReviewManager"));
12
13
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
13
14
  const git_utils_1 = require("../utils/git_utils");
14
15
  const string_utils_1 = require("../utils/string_utils");
@@ -124,6 +125,9 @@ class ChatManager {
124
125
  case 'commit':
125
126
  await this.handleCommit();
126
127
  break;
128
+ case 'review':
129
+ await this.handleReview();
130
+ break;
127
131
  default:
128
132
  if (this.isGitCommand(command)) {
129
133
  this.executeGitCommand(command);
@@ -144,6 +148,12 @@ class ChatManager {
144
148
  console.error('No response to copy.');
145
149
  }
146
150
  }
151
+ async handleReview() {
152
+ this.gpt.stream = false;
153
+ const reviewManager = new ReviewManager_1.default(this.gpt, this.options);
154
+ await reviewManager.review();
155
+ this.gpt.stream = true;
156
+ }
147
157
  async handleSave() {
148
158
  const lastResponse = this.chatHistory.filter(msg => msg.role === 'assistant').pop();
149
159
  if (lastResponse) {
@@ -151,19 +161,23 @@ class ChatManager {
151
161
  if (Object.keys(fileContents).length > 0) {
152
162
  await (0, string_utils_1.saveGeneratedFiles)(fileContents);
153
163
  console.info('Files have been saved.');
164
+ this.rl.prompt();
154
165
  }
155
166
  else {
156
167
  console.info('No files found to save in the last response.');
168
+ this.rl.prompt();
157
169
  }
158
170
  }
159
171
  else {
160
172
  console.error('No response to save files from.');
173
+ this.rl.prompt();
161
174
  }
162
175
  }
163
176
  async handleCommit() {
164
177
  const diff = (0, child_process_1.execSync)('git diff HEAD').toString().trim();
165
178
  if (!diff) {
166
179
  console.error('No changes to commit.');
180
+ this.rl.prompt();
167
181
  return;
168
182
  }
169
183
  this.prompt = (0, git_utils_1.generateCommitMessagePrompt)(diff);
@@ -183,6 +197,7 @@ class ChatManager {
183
197
  if (err)
184
198
  throw err;
185
199
  });
200
+ this.rl.prompt();
186
201
  }
187
202
  isGitCommand(command) {
188
203
  return command.split(' ').length <= 3 && command.startsWith('git ');
@@ -194,6 +209,7 @@ class ChatManager {
194
209
  catch (error) {
195
210
  console.error('Error running command:', error);
196
211
  }
212
+ this.rl.prompt();
197
213
  }
198
214
  async processUserInput(input) {
199
215
  if (this.isProcessing) {
@@ -276,41 +276,6 @@ Please briefly explain how the code works in this.`;
276
276
  else {
277
277
  await this.handleUserAction(rawCodeCommand, prompt);
278
278
  }
279
- if (this.options.interactive) {
280
- let continueInteractive = true;
281
- while (continueInteractive) {
282
- const newPrompt = await (0, prompts_1.input)({
283
- message: 'Revise the code (or press enter to finish):',
284
- });
285
- if (newPrompt.trim() === '') {
286
- continueInteractive = false;
287
- }
288
- else {
289
- let includeMoreFiles = true;
290
- while (includeMoreFiles) {
291
- const contextFile = await (0, prompts_1.input)({
292
- message: 'Enter the relative path of a file or directory you would like to include as context (or press enter to continue):',
293
- });
294
- if (contextFile) {
295
- this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
296
- }
297
- else {
298
- includeMoreFiles = false;
299
- }
300
- }
301
- rawCodeCommand = await this.generateCode(newPrompt, rawCodeCommand);
302
- this.displayGeneratedCode(rawCodeCommand);
303
- if (this.options.save || this.options.interactive) {
304
- const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
305
- await (0, string_utils_1.saveGeneratedFiles)(fileContents);
306
- await this.updateContextFiles(fileContents);
307
- }
308
- else {
309
- await this.handleUserAction(rawCodeCommand, newPrompt);
310
- }
311
- }
312
- }
313
- }
314
279
  }
315
280
  catch (error) {
316
281
  console.error('Could not generate code', error);
@@ -0,0 +1,208 @@
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 cli_highlight_1 = __importDefault(require("cli-highlight"));
7
+ const prompts_1 = require("@inquirer/prompts");
8
+ const copy_paste_1 = __importDefault(require("copy-paste"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const git_utils_1 = require("../utils/git_utils");
12
+ const string_utils_1 = require("../utils/string_utils");
13
+ const command_utils_1 = require("../utils/command_utils");
14
+ const config_utils_1 = require("../utils/config_utils");
15
+ class ReviewManager {
16
+ constructor(gpt, options) {
17
+ this.contextFiles = {};
18
+ this.gpt = gpt;
19
+ this.options = options;
20
+ }
21
+ async loadContextFiles() {
22
+ if (this.options.files) {
23
+ let expandedFiles = [];
24
+ for (const file of this.options.files) {
25
+ const filePath = path_1.default.resolve(file);
26
+ if (await fs_1.default.promises.stat(filePath).then(stat => stat.isDirectory()).catch(() => false)) {
27
+ const dirFiles = await (0, command_utils_1.getAllFilesInDirectory)(filePath);
28
+ expandedFiles = expandedFiles.concat(dirFiles.map(f => path_1.default.relative(process.cwd(), f)));
29
+ }
30
+ else {
31
+ expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
32
+ }
33
+ }
34
+ this.options.files = expandedFiles;
35
+ for (const file of this.options.files) {
36
+ const filePath = path_1.default.resolve(file);
37
+ if (await fs_1.default.promises.stat(filePath).then(stat => stat.isFile()).catch(() => false)) {
38
+ const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
39
+ this.contextFiles[file] = content;
40
+ }
41
+ }
42
+ }
43
+ }
44
+ generatePrompt() {
45
+ let promptText = `### Prompt
46
+
47
+ ${this.options.prompt || 'Please provide a code review.'}
48
+
49
+ ### Example of a well-structured response
50
+
51
+ # Code Review: Enhanced Code Generation and File Handling
52
+
53
+ ## Changes Overview
54
+
55
+ These changes primarily focus on improving the code generation process and adding more robust file handling capabilities. The main modifications include:
56
+
57
+ 1. Enhancing the code generation prompt with a structured example response.
58
+ 2. Implementing a new function to extract file names and contents from the generated code.
59
+ 3. Adding a new option to save generated code directly to files.
60
+ 4. Improving the copy-to-clipboard functionality to allow copying individual files.
61
+ 5. Removing an unused import in the review.ts file.
62
+
63
+ ## Reason for Changes
64
+
65
+ The changes aim to enhance the usability and flexibility of the code generation tool by providing clearer prompts, better file management, and more control over the generated code.
66
+
67
+ ## Detailed Description
68
+
69
+ 1. **Enhanced Code Generation Prompt** - The \`generateCodePrompt\` function now includes a structured example of a well-formatted response. This helps guide the AI to produce more consistent and usable output, making it easier to parse and use the generated code.
70
+ 2. **File Extraction Function** - A new function \`extractFilesAndContent\` has been added to parse the raw code command and extract individual file names and their contents. This enables more granular control over the generated code, allowing for file-specific operations.
71
+ 3. **Save to File Option** - A new 'Save Source Code to Files' option has been added to the user action menu. This feature allows users to save generated code directly to files in the appropriate directory structure.
72
+ 4. **Improved Copy to Clipboard** - The 'Copy Source Code to Clipboard' option has been enhanced to allow users to choose specific files to copy, rather than copying all generated code at once.
73
+ 5. **Removed Unused Import** - The unused import of \`execFileSync\` from 'child_process' has been removed from the review.ts file, improving code cleanliness.
74
+
75
+ ## Potential Bugs and Recommended Fixes
76
+
77
+ ### 1. Possible undefined access in \`extractFilesAndContent\`
78
+
79
+ \`\`\`typescript
80
+ const contentMatch = regex.exec(rawCodeCommand)
81
+ if (contentMatch) {
82
+ files.push(fileName)
83
+ let content = contentMatch[1]
84
+ // ...
85
+ }
86
+ \`\`\`
87
+
88
+ There's a potential for \`contentMatch[1]\` to be undefined if the regex doesn't capture any groups. To fix this, add a null check:
89
+
90
+ \`\`\`typescript
91
+ const contentMatch = regex.exec(rawCodeCommand)
92
+ if (contentMatch && contentMatch[1]) {
93
+ files.push(fileName)
94
+ let content = contentMatch[1]
95
+ // ...
96
+ }
97
+ \`\`\`
98
+ `;
99
+ if (this.options.files) {
100
+ const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
101
+ promptText += `
102
+ ### Description of App
103
+
104
+ ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
105
+
106
+ ${contextFileContents}
107
+
108
+ ### Request
109
+
110
+ I would like you to explain the code and document a description of it.
111
+ List any bugs in the new code as well as recommended fixes for those bugs with code examples.
112
+ Format the output of this code review in Markdown format.`;
113
+ }
114
+ else {
115
+ promptText += `
116
+
117
+ ### Request
118
+
119
+ Do not use any data from the example response structure, only use the structure.
120
+ I would like you to explain why these change are being made and document a description of these changes.
121
+ Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
122
+ Format the output of this code review in Markdown format.`;
123
+ }
124
+ return promptText;
125
+ }
126
+ async getGitDiffPrompt() {
127
+ const gitDiff = await (0, git_utils_1.getGitDiff)();
128
+ return `==== begin diff =====
129
+ ${gitDiff}
130
+ ==== end diff =====
131
+
132
+ ${this.generatePrompt()}`;
133
+ }
134
+ displayAnalysis(analysis) {
135
+ (0, string_utils_1.parseMarkdownInstructions)(analysis).map(code => {
136
+ if (code.trim().startsWith('```')) {
137
+ const lines = code.split('\n');
138
+ const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
139
+ console.log(lines[0]);
140
+ try {
141
+ console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
142
+ }
143
+ catch (error) {
144
+ // don't highlight if it fails
145
+ console.log(lines.slice(1, -1).join('\n'));
146
+ }
147
+ console.log(lines.slice(-1)[0]);
148
+ }
149
+ else {
150
+ console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
151
+ }
152
+ });
153
+ }
154
+ async handleUserAction(analysis) {
155
+ const extractSection = (section) => {
156
+ const regex = new RegExp(`## ${section}[\\s\\S]*?(?=\\n## Potential Bugs and Recommended Fixes|$)`, 'g');
157
+ const match = analysis.match(regex);
158
+ return match ? match[0].trim() : '';
159
+ };
160
+ const clipboardOption = await (0, prompts_1.select)({
161
+ message: 'What would you like to copy to the clipboard?',
162
+ choices: [
163
+ { name: 'Copy only the Changes Overview', value: 'Changes Overview' },
164
+ { name: 'Copy only the Potential Bugs and Recommended Fixes', value: 'Potential Bugs and Recommended Fixes' },
165
+ { name: 'Copy everything', value: 'Everything' },
166
+ { name: 'Done', value: 'Done' }
167
+ ]
168
+ });
169
+ let contentToCopy = '';
170
+ switch (clipboardOption) {
171
+ case 'Changes Overview':
172
+ contentToCopy = extractSection('Changes Overview');
173
+ break;
174
+ case 'Potential Bugs and Recommended Fixes':
175
+ contentToCopy = extractSection('Potential Bugs and Recommended Fixes');
176
+ break;
177
+ case 'Everything':
178
+ contentToCopy = analysis;
179
+ break;
180
+ case 'Done':
181
+ console.log('No content copied to clipboard.');
182
+ return;
183
+ }
184
+ if (contentToCopy) {
185
+ copy_paste_1.default.copy(contentToCopy, (err) => {
186
+ if (err) {
187
+ console.error('Error: Unable to copy to clipboard.', err);
188
+ }
189
+ else {
190
+ console.log('Selected content copied to clipboard');
191
+ }
192
+ });
193
+ }
194
+ }
195
+ async review() {
196
+ try {
197
+ await this.loadContextFiles();
198
+ const prompt = this.options.files ? this.generatePrompt() : await this.getGitDiffPrompt();
199
+ const analysis = await this.gpt.generateText({ prompt });
200
+ this.displayAnalysis(analysis);
201
+ await this.handleUserAction(analysis);
202
+ }
203
+ catch (error) {
204
+ console.error('Error during analysis:', error.message);
205
+ }
206
+ }
207
+ }
208
+ exports.default = ReviewManager;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.1.24",
3
+ "version": "0.2.0",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",