cloving 0.3.11 → 0.3.13
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/dist/cloving_gpt/index.js +32 -39
- package/dist/commands/config.js +2 -2
- package/dist/commands/generate/commit.js +1 -1
- package/dist/commands/generate/docs.js +11 -0
- package/dist/commands/generate/review.js +1 -1
- package/dist/commands/tokens.js +24 -0
- package/dist/index.js +3 -3
- package/dist/managers/BlockManager.js +31 -14
- package/dist/managers/ChatManager.js +60 -224
- package/dist/managers/CodeManager.js +60 -55
- package/dist/managers/CommitManager.js +1 -1
- package/dist/managers/DocsManager.js +88 -0
- package/dist/managers/DocumentationManager.js +2 -1
- package/dist/managers/InitManager.js +20 -6
- package/dist/managers/ProxyManager.js +19 -2
- package/dist/managers/ReviewManager.js +59 -169
- package/dist/managers/ShellManager.js +49 -0
- package/dist/managers/StreamManager.js +308 -0
- package/dist/managers/UnitTestManager.js +3 -3
- package/dist/package.json +2 -2
- package/dist/utils/config_utils.js +43 -0
- package/dist/utils/git_utils.js +29 -0
- package/dist/utils/prompt_utils.js +125 -307
- package/dist/utils/prompts/CODEGEN_COULDNT_APPLY.md +5 -0
- package/dist/utils/prompts/CODEGEN_EXAMPLES.md +82 -0
- package/dist/utils/prompts/CODEGEN_INSTRUCTIONS.md +43 -0
- package/dist/utils/prompts/DOCS_INSTRUCTIONS.md +9 -0
- package/dist/utils/prompts/INIT_INSTRUCTIONS.md +132 -0
- package/dist/utils/prompts/REVIEW_INSTRUCTIONS.md +49 -0
- package/dist/utils/prompts/SHELL_INSTRUCTIONS.md +15 -0
- package/dist/utils/prompts/SPECIAL_FILES.md +66 -0
- package/dist/utils/prompts/index.js +27 -0
- package/dist/utils/string_utils.js +10 -5
- package/package.json +2 -2
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
// Reset responseString after processing
|
|
85
|
+
this.responseString = '';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
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:
|
|
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:
|
|
26
|
+
content: prompts_2.INIT_INSTRUCTIONS,
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
@@ -181,15 +182,28 @@ ${JSON.stringify(projectDetails, null, 2)}`;
|
|
|
181
182
|
}
|
|
182
183
|
const clovingJson = JSON.parse(cleanAiChatResponse);
|
|
183
184
|
const directoriesToCheck = [
|
|
184
|
-
...clovingJson.languages.map((language) =>
|
|
185
|
-
|
|
186
|
-
|
|
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
|
+
})),
|
|
187
200
|
];
|
|
188
|
-
const missingDirectories = directoriesToCheck.filter((
|
|
201
|
+
const missingDirectories = directoriesToCheck.filter((item) => !fs_1.default.existsSync(item.directory));
|
|
189
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)}`);
|
|
190
204
|
return [
|
|
191
205
|
false,
|
|
192
|
-
`${colors_1.default.red.bold('ERROR:')} The following directories were not found
|
|
206
|
+
`${colors_1.default.red.bold('ERROR:')} The following directories were not found:\n${missingDirMessages.join('\n')}`,
|
|
193
207
|
];
|
|
194
208
|
}
|
|
195
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
|
-
|
|
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
|
-
|
|
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) {
|
|
@@ -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
|
|
10
|
-
const
|
|
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
|
-
|
|
13
|
+
const StreamManager_1 = __importDefault(require("./StreamManager"));
|
|
14
|
+
class ReviewManager extends StreamManager_1.default {
|
|
17
15
|
constructor(options) {
|
|
18
|
-
|
|
19
|
-
this.chatHistory = [];
|
|
16
|
+
super(options);
|
|
20
17
|
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
21
|
-
|
|
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.
|
|
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
|
|
76
|
+
async generateReview(extraPrompt) {
|
|
227
77
|
try {
|
|
78
|
+
await this.checkForLatestVersion();
|
|
228
79
|
await this.loadContextFiles();
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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 {
|