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.
@@ -5,14 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const prompts_1 = require("@inquirer/prompts");
7
7
  const cli_highlight_1 = __importDefault(require("cli-highlight"));
8
+ const colors_1 = __importDefault(require("colors"));
8
9
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
10
+ const config_utils_1 = require("../utils/config_utils");
9
11
  const prompt_utils_1 = require("../utils/prompt_utils");
10
12
  const string_utils_1 = require("../utils/string_utils");
11
- class CodeManager {
13
+ const prompts_2 = require("../utils/prompts");
14
+ const StreamManager_1 = __importDefault(require("./StreamManager"));
15
+ class CodeManager extends StreamManager_1.default {
12
16
  constructor(options) {
13
- this.options = options;
14
- this.contextFiles = {};
15
- this.chatHistory = [];
17
+ super(options);
18
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
19
+ options.stream = true;
16
20
  this.gpt = new cloving_gpt_1.default(options);
17
21
  }
18
22
  async initialize() {
@@ -26,7 +30,7 @@ class CodeManager {
26
30
  let includeMoreFiles = true;
27
31
  while (includeMoreFiles) {
28
32
  const contextFile = await (0, prompts_1.input)({
29
- message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue):`,
33
+ message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue): `,
30
34
  });
31
35
  if (contextFile) {
32
36
  this.contextFiles = await (0, prompt_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
@@ -42,26 +46,7 @@ class CodeManager {
42
46
  });
43
47
  this.options.prompt = userPrompt;
44
48
  }
45
- let response = await this.generateCode(this.options.prompt);
46
- const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
47
- const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
48
- if (!canApply) {
49
- console.log('The generated code could not be automatically applied. It will try again asking for more details.');
50
- console.log(`Some of the provided code blocks could not be applied,
51
- please match the existing code with a few more lines of context and make sure it is a character for character exact match.
52
-
53
- ${summary}`);
54
- response = await this.generateCode(this.options.prompt);
55
- this.displayGeneratedCode(response);
56
- }
57
- this.displayGeneratedCode(response);
58
- if (this.options.save) {
59
- const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
60
- await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
61
- }
62
- else {
63
- await this.handleUserAction(response, this.options.prompt);
64
- }
49
+ await this.generateCode(this.options.prompt);
65
50
  }
66
51
  catch (err) {
67
52
  const error = err;
@@ -69,35 +54,26 @@ ${summary}`);
69
54
  }
70
55
  }
71
56
  async generateCode(userPrompt) {
57
+ await this.checkForLatestVersion();
58
+ await this.loadContextFiles();
72
59
  if (this.chatHistory.length === 0) {
73
- const systemPrompt = (0, prompt_utils_1.generateCodegenPrompt)(this.contextFiles);
74
- this.chatHistory.push({ role: 'user', content: systemPrompt });
75
- this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
60
+ this.addUserPrompt((0, prompt_utils_1.generateCodegenPrompt)(this.contextFiles));
61
+ this.addAssistantResponse('What would you like to do?');
62
+ }
63
+ this.addUserPrompt(userPrompt);
64
+ try {
65
+ const responseStream = await this.gpt.streamText({
66
+ prompt: userPrompt,
67
+ messages: this.chatHistory,
68
+ });
69
+ this.handleResponseStream(responseStream);
70
+ }
71
+ catch (err) {
72
+ const error = err;
73
+ console.error('Error streaming response:', error.message);
76
74
  }
77
- this.chatHistory.push({ role: 'user', content: userPrompt });
78
- return await this.gpt.generateText({ prompt: userPrompt, messages: this.chatHistory });
79
- }
80
- displayGeneratedCode(rawCodeCommand) {
81
- (0, string_utils_1.parseMarkdownInstructions)(rawCodeCommand).map((code) => {
82
- if (code.trim().startsWith('```')) {
83
- const lines = code.split('\n');
84
- const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
85
- console.log(lines[0]);
86
- try {
87
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
88
- }
89
- catch (error) {
90
- // don't highlight if it fails
91
- console.log(lines.slice(1, -1).join('\n'));
92
- }
93
- console.log(lines.slice(-1)[0]);
94
- }
95
- else {
96
- console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
97
- }
98
- });
99
75
  }
100
- async handleUserAction(response, prompt) {
76
+ async handleUserAction(response) {
101
77
  const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
102
78
  const files = Array.from(new Set(currentNewBlocks.map((block) => block.filePath)));
103
79
  if (this.options.save) {
@@ -133,9 +109,7 @@ ${summary}`);
133
109
  includeMoreFiles = false;
134
110
  }
135
111
  }
136
- const newResponse = await this.generateCode(newPrompt);
137
- this.displayGeneratedCode(newResponse);
138
- await this.handleUserAction(newResponse, newPrompt);
112
+ await this.generateCode(newPrompt);
139
113
  break;
140
114
  case 'explain':
141
115
  const explainPrompt = this.generateExplainCodePrompt(response);
@@ -163,7 +137,7 @@ ${summary}`);
163
137
  break;
164
138
  case 'saveAll':
165
139
  await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
166
- console.log('All files have been saved.');
140
+ console.log(colors_1.default.green('\nAll files have been saved.'));
167
141
  break;
168
142
  case 'done':
169
143
  break;
@@ -176,5 +150,34 @@ ${summary}`);
176
150
 
177
151
  Please briefly explain how the code works in this.`;
178
152
  }
153
+ /**
154
+ * Finalizes the response processing.
155
+ * This method should be overridden to provide specific finalization behavior.
156
+ * @protected
157
+ */
158
+ async finalizeResponse() {
159
+ this.addAssistantResponse(this.responseString);
160
+ this.isProcessing = false;
161
+ if (this.responseString !== '') {
162
+ const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
163
+ const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
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
+ }
175
+ }
176
+ else {
177
+ this.retryCount = 0; // Reset the retry count on successful application
178
+ }
179
+ }
180
+ this.handleUserAction(this.responseString);
181
+ }
179
182
  }
180
183
  exports.default = CodeManager;
@@ -15,7 +15,7 @@ class CommitManager {
15
15
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
16
16
  this.gpt = new cloving_gpt_1.default(options);
17
17
  }
18
- async commit() {
18
+ async generateCommit() {
19
19
  try {
20
20
  // Get the git diff
21
21
  const diff = (0, child_process_1.execSync)('git diff HEAD').toString().trim();
@@ -0,0 +1,86 @@
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 prompts_1 = require("@inquirer/prompts");
7
+ const colors_1 = __importDefault(require("colors"));
8
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
9
+ const config_utils_1 = require("../utils/config_utils");
10
+ const prompts_2 = require("../utils/prompts");
11
+ const prompt_utils_1 = require("../utils/prompt_utils");
12
+ const string_utils_1 = require("../utils/string_utils");
13
+ const StreamManager_1 = __importDefault(require("./StreamManager"));
14
+ /**
15
+ * Manages the generation of documentation for specified files.
16
+ */
17
+ class DocsManager extends StreamManager_1.default {
18
+ /**
19
+ * Creates a new DocumentationManager instance.
20
+ * @param {ClovingGPTOptions} options - Configuration options for the DocumentationManager.
21
+ */
22
+ constructor(options) {
23
+ super(options);
24
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
25
+ options.stream = true;
26
+ this.gpt = new cloving_gpt_1.default(options);
27
+ }
28
+ /**
29
+ * Generates documentation for the specified files.
30
+ * @returns {Promise<void>}
31
+ */
32
+ async generateDocs(extraPrompt) {
33
+ try {
34
+ this.isProcessing = true;
35
+ if (this.retryCount === 0) {
36
+ await this.checkForLatestVersion();
37
+ }
38
+ await this.loadContextFiles();
39
+ this.addUserPrompt(`${(0, prompt_utils_1.generateDocsPrompt)(this.contextFiles)}\n\n${extraPrompt || ''}`);
40
+ const responseStream = await this.gpt.streamText({
41
+ prompt: this.prompt,
42
+ messages: this.chatHistory,
43
+ });
44
+ this.handleResponseStream(responseStream);
45
+ }
46
+ catch (error) {
47
+ console.error(colors_1.default.red('Error generating documentation:'), error);
48
+ }
49
+ }
50
+ /**
51
+ * Finalizes the response processing.
52
+ * This method should be overridden to provide specific finalization behavior.
53
+ * @protected
54
+ */
55
+ async finalizeResponse() {
56
+ this.addAssistantResponse(this.responseString);
57
+ this.isProcessing = false;
58
+ if (this.responseString !== '') {
59
+ const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
60
+ const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
61
+ if (canApply && currentNewBlocks.length > 0) {
62
+ const shouldSave = await (0, prompts_1.confirm)({
63
+ message: '\n\nDo you want to save the generated documentation?',
64
+ default: true,
65
+ });
66
+ if (shouldSave) {
67
+ await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
68
+ console.log(colors_1.default.green('\nDocumentation has been saved to the respective files.'));
69
+ }
70
+ }
71
+ else {
72
+ if (this.retryCount < this.maxRetries) {
73
+ 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`);
74
+ this.retryCount++;
75
+ this.generateDocs(`\n\n${prompts_2.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
76
+ return;
77
+ }
78
+ else {
79
+ 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`);
80
+ this.retryCount = 0; // Reset the retry count for future attempts
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ exports.default = DocsManager;
@@ -9,6 +9,7 @@ const cli_highlight_1 = __importDefault(require("cli-highlight"));
9
9
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
10
10
  const config_utils_1 = require("../utils/config_utils");
11
11
  const prompt_utils_1 = require("../utils/prompt_utils");
12
+ const prompts_2 = require("../utils/prompts");
12
13
  const string_utils_1 = require("../utils/string_utils");
13
14
  /**
14
15
  * Manages the generation of documentation for specified files.
@@ -33,7 +34,7 @@ class DocumentationManager {
33
34
  try {
34
35
  await this.loadContextFiles();
35
36
  if (this.chatHistory.length === 0) {
36
- this.chatHistory.push({ role: 'user', content: prompt_utils_1.CODEGEN_INSTRUCTIONS });
37
+ this.chatHistory.push({ role: 'user', content: prompts_2.CODEGEN_INSTRUCTIONS });
37
38
  this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
38
39
  }
39
40
  const prompt = this.generateDocumentationPrompt();
@@ -14,6 +14,7 @@ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
14
14
  const string_utils_1 = require("../utils/string_utils");
15
15
  const config_utils_1 = require("../utils/config_utils");
16
16
  const prompt_utils_1 = require("../utils/prompt_utils");
17
+ const prompts_2 = require("../utils/prompts");
17
18
  class InitManager {
18
19
  constructor(options) {
19
20
  this.options = options;
@@ -22,7 +23,7 @@ class InitManager {
22
23
  this.gpt = new cloving_gpt_1.default(this.options);
23
24
  this.chatHistory.push({
24
25
  role: 'system',
25
- content: prompt_utils_1.INIT_INSTRUCTIONS,
26
+ content: prompts_2.INIT_INSTRUCTIONS,
26
27
  });
27
28
  }
28
29
  /**
@@ -3,181 +3,31 @@ 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
6
  const copy_paste_1 = __importDefault(require("copy-paste"));
9
- const fs_1 = __importDefault(require("fs"));
10
- const path_1 = __importDefault(require("path"));
7
+ const colors_1 = __importDefault(require("colors"));
8
+ const prompts_1 = require("@inquirer/prompts");
11
9
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
12
10
  const git_utils_1 = require("../utils/git_utils");
13
- const string_utils_1 = require("../utils/string_utils");
14
11
  const prompt_utils_1 = require("../utils/prompt_utils");
15
12
  const config_utils_1 = require("../utils/config_utils");
16
- class ReviewManager {
13
+ const StreamManager_1 = __importDefault(require("./StreamManager"));
14
+ class ReviewManager extends StreamManager_1.default {
17
15
  constructor(options) {
18
- this.contextFiles = {};
19
- this.chatHistory = [];
16
+ super(options);
20
17
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
21
- this.options = options;
18
+ options.stream = true;
22
19
  this.gpt = new cloving_gpt_1.default(options);
23
20
  }
24
- /**
25
- * Loads the context files specified in the options. If directories are provided,
26
- * it expands them to include all files within. The contents of each file are
27
- * stored in the `contextFiles` object for later use.
28
- */
29
- async loadContextFiles() {
30
- if (this.options.files) {
31
- let expandedFiles = [];
32
- for (const file of this.options.files) {
33
- const filePath = path_1.default.resolve(file);
34
- if (await fs_1.default.promises
35
- .stat(filePath)
36
- .then((stat) => stat.isDirectory())
37
- .catch(() => false)) {
38
- const dirFiles = await (0, prompt_utils_1.getAllFilesInDirectory)(filePath);
39
- expandedFiles = expandedFiles.concat(dirFiles.map((f) => path_1.default.relative(process.cwd(), f)));
40
- }
41
- else {
42
- expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
43
- }
44
- }
45
- this.options.files = expandedFiles;
46
- for (const file of this.options.files) {
47
- const filePath = path_1.default.resolve(file);
48
- if (await fs_1.default.promises
49
- .stat(filePath)
50
- .then((stat) => stat.isFile())
51
- .catch(() => false)) {
52
- const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
53
- this.contextFiles[file] = content;
54
- }
55
- }
56
- }
57
- }
58
- /**
59
- * Generates a review prompt based on the current chat history and options.
60
- * If no chat history exists, it initializes it with a system prompt and a request
61
- * for user input. The prompt includes a structured example response for code review.
62
- *
63
- * @returns {string} The generated review prompt.
64
- */
65
- generateReviewPrompt() {
66
- if (this.chatHistory.length === 0) {
67
- let systemPrompt = `### Example of a well-structured response
68
-
69
- # Code Review: Enhanced Code Generation and File Handling
70
-
71
- ## Changes Overview
72
-
73
- These changes primarily focus on improving the code generation process and adding more robust file handling capabilities. The main modifications include:
74
-
75
- 1. Enhancing the code generation prompt with a structured example response.
76
- 2. Implementing a new function to extract file names and contents from the generated code.
77
- 3. Adding a new option to save generated code directly to files.
78
- 4. Improving the copy-to-clipboard functionality to allow copying individual files.
79
- 5. Removing an unused import in the review.ts file.
80
-
81
- ## Reason for Changes
82
-
83
- 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.
84
-
85
- ## Detailed Description
86
-
87
- 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.
88
- 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.
89
- 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.
90
- 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.
91
- 5. **Removed Unused Import** - The unused import of \`execFileSync\` from 'child_process' has been removed from the review.ts file, improving code cleanliness.
92
-
93
- ## Potential Bugs and Recommended Fixes
94
-
95
- ### 1. Possible undefined access in \`extractFilesAndContent\`
96
-
97
- \`\`\`typescript
98
- const contentMatch = regex.exec(rawCodeCommand)
99
- if (contentMatch) {
100
- files.push(fileName)
101
- let content = contentMatch[1]
102
- // ...
103
- }
104
- \`\`\`
105
-
106
- 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:
107
-
108
- \`\`\`typescript
109
- const contentMatch = regex.exec(rawCodeCommand)
110
- if (contentMatch && contentMatch[1]) {
111
- files.push(fileName)
112
- let content = contentMatch[1]
113
- // ...
114
- }
115
- \`\`\`
116
-
117
- `;
118
- if (this.options.files) {
119
- const contextFileContents = Object.keys(this.contextFiles)
120
- .map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`)
121
- .join('\n');
122
- systemPrompt += `### Description of App
123
-
124
- ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
125
-
126
- ${contextFileContents}
127
-
128
- ### Request
129
-
130
- I would like you to explain the code and document a description of it.
131
- List any bugs in the new code as well as recommended fixes for those bugs with code examples.
132
- Format the output of this code review in Markdown format.`;
133
- }
134
- else {
135
- systemPrompt += `### Request
136
-
137
- Do not use any data from the example response structure, only use the structure.
138
- I would like you to explain why these change are being made and document a description of these changes.
139
- Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
140
- Format the output of this code review in Markdown format.`;
141
- }
142
- this.chatHistory.push({ role: 'user', content: systemPrompt });
143
- this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
144
- }
145
- this.chatHistory.push({
146
- role: 'user',
147
- content: this.options.prompt || 'Please provide a code review.',
148
- });
149
- return this.options.prompt || 'Please provide a code review.';
150
- }
151
21
  async getGitDiffPrompt() {
152
22
  const gitDiff = await (0, git_utils_1.getGitDiff)();
153
- this.options.prompt = `## Diff Content
23
+ return `${(0, prompt_utils_1.generateReviewPrompt)(this.options, this.contextFiles)}
24
+
25
+ ## Diff Content
154
26
  ${gitDiff}
155
27
 
156
28
  # Task
157
29
 
158
30
  ${this.options.prompt || 'Please provide a code review.'}`;
159
- const prompt = this.generateReviewPrompt();
160
- return prompt;
161
- }
162
- displayAnalysis(analysis) {
163
- (0, string_utils_1.parseMarkdownInstructions)(analysis).map((code) => {
164
- if (code.trim().startsWith('```')) {
165
- const lines = code.split('\n');
166
- const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
167
- console.log(lines[0]);
168
- try {
169
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
170
- }
171
- catch (error) {
172
- // don't highlight if it fails
173
- console.log(lines.slice(1, -1).join('\n'));
174
- }
175
- console.log(lines.slice(-1)[0]);
176
- }
177
- else {
178
- console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
179
- }
180
- });
181
31
  }
182
32
  async handleUserAction(analysis) {
183
33
  const extractSection = (section) => {
@@ -215,27 +65,67 @@ ${this.options.prompt || 'Please provide a code review.'}`;
215
65
  if (contentToCopy) {
216
66
  copy_paste_1.default.copy(contentToCopy, (err) => {
217
67
  if (err) {
218
- console.error('Error: Unable to copy to clipboard.', err);
68
+ console.error(colors_1.default.red('Error: Unable to copy to clipboard.'), err);
219
69
  }
220
70
  else {
221
- console.log('Selected content copied to clipboard');
71
+ console.log(colors_1.default.green('Selected content copied to clipboard'));
222
72
  }
223
73
  });
224
74
  }
225
75
  }
226
- async review() {
76
+ async generateReview(extraPrompt) {
227
77
  try {
78
+ await this.checkForLatestVersion();
228
79
  await this.loadContextFiles();
229
- const prompt = this.options.files
230
- ? this.generateReviewPrompt()
231
- : await this.getGitDiffPrompt();
232
- const analysis = await this.gpt.generateText({ prompt, messages: this.chatHistory });
233
- this.displayAnalysis(analysis);
234
- await this.handleUserAction(analysis);
80
+ if (this.options.files) {
81
+ if (this.chatHistory.length === 0) {
82
+ this.addUserPrompt(`${(0, prompt_utils_1.generateReviewPrompt)(this.options, this.contextFiles)}`);
83
+ this.addAssistantResponse('What would you like to do?');
84
+ }
85
+ this.addUserPrompt(`${this.options.prompt || 'Please provide a code review.'}\n\n${extraPrompt || ''}`);
86
+ }
87
+ else {
88
+ if (this.chatHistory.length === 0) {
89
+ this.addUserPrompt(await this.getGitDiffPrompt());
90
+ this.addAssistantResponse('What would you like to do?');
91
+ }
92
+ this.addUserPrompt(`${this.options.prompt || 'Please provide a code review.'}\n\n${extraPrompt || ''}`);
93
+ }
94
+ const responseStream = await this.gpt.streamText({
95
+ prompt: this.prompt,
96
+ messages: this.chatHistory,
97
+ });
98
+ this.handleResponseStream(responseStream);
235
99
  }
236
100
  catch (error) {
237
- console.error('Error during analysis:', error.message);
101
+ this.handleError(error);
238
102
  }
239
103
  }
104
+ handleError(error) {
105
+ let errorMessage = error.message || 'An error occurred.';
106
+ const errorNumber = error.response?.status || 'unknown';
107
+ switch (errorNumber) {
108
+ case 400:
109
+ errorMessage = 'Invalid model or prompt size too large. Try specifying fewer files.';
110
+ break;
111
+ case 403:
112
+ errorMessage = 'Inactive subscription or usage limit reached';
113
+ break;
114
+ case 429:
115
+ errorMessage = 'Rate limit error';
116
+ break;
117
+ case 500:
118
+ errorMessage = 'Internal server error';
119
+ break;
120
+ }
121
+ const statusMessage = error.response?.data?.statusMessage;
122
+ console.error(colors_1.default.red(`\nError (${errorNumber}) while submitting the prompt to the AI API\n\n${statusMessage}\n`));
123
+ this.isProcessing = false;
124
+ }
125
+ async finalizeResponse() {
126
+ this.addAssistantResponse(this.responseString);
127
+ this.isProcessing = false;
128
+ await this.handleUserAction(this.responseString);
129
+ }
240
130
  }
241
131
  exports.default = ReviewManager;
@@ -11,13 +11,26 @@ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
11
11
  const prompt_utils_1 = require("../utils/prompt_utils");
12
12
  const string_utils_1 = require("../utils/string_utils");
13
13
  const config_utils_1 = require("../utils/config_utils");
14
+ /**
15
+ * ShellManager class for handling shell script generation and execution.
16
+ */
14
17
  class ShellManager {
18
+ /**
19
+ * Creates an instance of ShellManager.
20
+ * @param {ClovingGPTOptions} options - The options for ClovingGPT.
21
+ */
15
22
  constructor(options) {
16
23
  this.options = options;
17
24
  this.chatHistory = [];
18
25
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
19
26
  this.gpt = new cloving_gpt_1.default(options);
20
27
  }
28
+ /**
29
+ * Generates a prompt to explain a shell script.
30
+ * @param {string} prompt - The original shell script prompt.
31
+ * @returns {string} The prompt for explaining the shell script.
32
+ * @private
33
+ */
21
34
  generateExplainShellPrompt(prompt) {
22
35
  return `${prompt}
23
36
 
@@ -25,6 +38,12 @@ class ShellManager {
25
38
 
26
39
  Please briefly explain how this shell script works.`;
27
40
  }
41
+ /**
42
+ * Generates a shell script based on the given prompt.
43
+ * @param {string} prompt - The user's prompt for generating a shell script.
44
+ * @returns {Promise<string>} A promise that resolves to the generated shell script.
45
+ * @private
46
+ */
28
47
  async generateShell(prompt) {
29
48
  if (this.chatHistory.length === 0) {
30
49
  const systemPrompt = (0, prompt_utils_1.generateShellPrompt)();
@@ -34,11 +53,23 @@ Please briefly explain how this shell script works.`;
34
53
  this.chatHistory.push({ role: 'user', content: prompt });
35
54
  return await this.gpt.generateText({ prompt, messages: this.chatHistory });
36
55
  }
56
+ /**
57
+ * Displays the generated shell script with syntax highlighting.
58
+ * @param {string} rawShellCommand - The raw shell command to display.
59
+ * @private
60
+ */
37
61
  displayGeneratedShell(rawShellCommand) {
38
62
  const generatedShell = (0, string_utils_1.extractMarkdown)(rawShellCommand);
39
63
  const generatedShellWithoutShebang = generatedShell.replace(/^#!.*?\s/, '');
40
64
  console.log((0, cli_highlight_1.default)(generatedShellWithoutShebang));
41
65
  }
66
+ /**
67
+ * Handles user actions after generating a shell script.
68
+ * @param {string} response - The generated shell script.
69
+ * @param {string} prompt - The original user prompt.
70
+ * @returns {Promise<void>}
71
+ * @private
72
+ */
42
73
  async handleUserAction(response, prompt) {
43
74
  let { exec } = this.options;
44
75
  this.chatHistory.push({ role: 'assistant', content: response });
@@ -94,6 +125,24 @@ Please briefly explain how this shell script works.`;
94
125
  break;
95
126
  }
96
127
  }
128
+ /**
129
+ * Generates a shell script based on user input and handles subsequent user actions.
130
+ *
131
+ * This method is the main entry point for generating and interacting with shell scripts.
132
+ * It performs the following steps:
133
+ * 1. Obtains a user prompt (either from options or by asking the user).
134
+ * 2. Generates a shell script based on the prompt.
135
+ * 3. Displays the generated script with syntax highlighting.
136
+ * 4. Handles user actions (execute, revise, explain, copy, or cancel) for the generated script.
137
+ *
138
+ * @returns {Promise<void>} A promise that resolves when the generation and handling process is complete.
139
+ * @throws {Error} If there's an issue during the shell script generation process.
140
+ * @public
141
+ *
142
+ * @example
143
+ * const shellManager = new ShellManager(options);
144
+ * await shellManager.generateAndHandleShell();
145
+ */
97
146
  async generateAndHandleShell() {
98
147
  let { prompt: userPrompt } = this.options;
99
148
  try {