cloving 0.2.1 → 0.3.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 +1 -1
- package/dist/cloving_gpt/adapters/claude.js +7 -4
- package/dist/cloving_gpt/adapters/gemini.js +2 -1
- package/dist/cloving_gpt/adapters/mistral.js +1 -1
- package/dist/cloving_gpt/adapters/openai.js +1 -1
- package/dist/cloving_gpt/index.js +16 -7
- package/dist/commands/generate/code.js +3 -13
- package/dist/commands/generate/commit.js +3 -43
- package/dist/commands/generate/init.js +15 -0
- package/dist/commands/generate/review.js +1 -5
- package/dist/commands/generate/shell.js +3 -103
- package/dist/commands/generate/unit_tests.js +5 -51
- package/dist/commands/init.js +62 -53
- package/dist/file2.js +4 -0
- package/dist/managers/ChatManager.js +438 -128
- package/dist/managers/CodeManager.js +66 -185
- package/dist/managers/CommitManager.js +55 -0
- package/dist/managers/ProxyManager.js +2 -2
- package/dist/managers/ReviewManager.js +28 -23
- package/dist/managers/ShellManager.js +106 -0
- package/dist/newfile.js +2 -0
- package/dist/package.json +11 -10
- package/dist/utils/command_utils.js +195 -20
- package/dist/utils/string_utils.js +222 -60
- package/package.json +11 -10
|
@@ -3,81 +3,68 @@ 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 copy_paste_1 = __importDefault(require("copy-paste"));
|
|
7
6
|
const prompts_1 = require("@inquirer/prompts");
|
|
8
7
|
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
9
|
-
const
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
11
9
|
const command_utils_1 = require("../utils/command_utils");
|
|
12
|
-
const config_utils_1 = require("../utils/config_utils");
|
|
13
10
|
const string_utils_1 = require("../utils/string_utils");
|
|
14
11
|
class CodeManager {
|
|
15
|
-
constructor(
|
|
16
|
-
this.gpt = gpt;
|
|
12
|
+
constructor(options) {
|
|
17
13
|
this.options = options;
|
|
18
|
-
this.allSrcFiles = allSrcFiles;
|
|
19
14
|
this.contextFiles = {};
|
|
15
|
+
this.chatHistory = [];
|
|
16
|
+
this.gpt = new cloving_gpt_1.default(options);
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
18
|
+
async initialize() {
|
|
19
|
+
try {
|
|
20
|
+
if (this.options.files) {
|
|
21
|
+
for (const file of this.options.files) {
|
|
22
|
+
this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(file, this.contextFiles, this.options);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
let includeMoreFiles = true;
|
|
27
|
+
while (includeMoreFiles) {
|
|
28
|
+
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):`,
|
|
30
|
+
});
|
|
31
|
+
if (contextFile) {
|
|
32
|
+
this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
includeMoreFiles = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!this.options.prompt) {
|
|
40
|
+
const userPrompt = await (0, prompts_1.input)({
|
|
41
|
+
message: 'What would you like the code to do:',
|
|
42
|
+
});
|
|
43
|
+
this.options.prompt = userPrompt;
|
|
44
|
+
}
|
|
45
|
+
let response = await this.generateCode(this.options.prompt);
|
|
46
|
+
this.displayGeneratedCode(response);
|
|
47
|
+
if (this.options.save) {
|
|
48
|
+
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
|
|
49
|
+
await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
await this.handleUserAction(response, this.options.prompt);
|
|
53
|
+
}
|
|
48
54
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
import ClovingGPT from '../../cloving_gpt'
|
|
63
|
-
import type { ClovingGPTOptions } from '../../utils/types'
|
|
64
|
-
|
|
65
|
-
const generateShellPrompt = (prompt: string | undefined): string => {
|
|
66
|
-
const shell = execSync('echo $SHELL').toString().trim()
|
|
67
|
-
const os = execSync('echo $OSTYPE').toString().trim()
|
|
68
|
-
return \`Generate an executable \${shell} script that works on \${os}. Try to make it a single line if possible and as simple and straightforward as possible.
|
|
69
|
-
|
|
70
|
-
Do not add any commentary or context to the message other than the commit message itself.
|
|
71
|
-
|
|
72
|
-
An example of the output for this should look like the following:
|
|
73
|
-
\`\`\`
|
|
74
|
-
|
|
75
|
-
### Request
|
|
76
|
-
|
|
77
|
-
Generate code that does the following: ${prompt}
|
|
78
|
-
|
|
79
|
-
Do not use any data from the example response structure, only use the structure. Generate the code and include filenames with paths to the code files mentioned and do not be lazy and ask me to keep the existing code or show things like previous code remains unchanged, always include existing code in the response.`;
|
|
80
|
-
return promptText;
|
|
55
|
+
catch (err) {
|
|
56
|
+
const error = err;
|
|
57
|
+
console.error('Could not generate code', error.message);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async generateCode(userPrompt) {
|
|
61
|
+
if (this.chatHistory.length === 0) {
|
|
62
|
+
const systemPrompt = (0, command_utils_1.generateCodegenPrompt)(this.contextFiles);
|
|
63
|
+
this.chatHistory.push({ role: 'user', content: systemPrompt });
|
|
64
|
+
this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
|
|
65
|
+
}
|
|
66
|
+
this.chatHistory.push({ role: 'user', content: userPrompt });
|
|
67
|
+
return await this.gpt.generateText({ prompt: userPrompt, messages: this.chatHistory });
|
|
81
68
|
}
|
|
82
69
|
displayGeneratedCode(rawCodeCommand) {
|
|
83
70
|
(0, string_utils_1.parseMarkdownInstructions)(rawCodeCommand).map(code => {
|
|
@@ -99,19 +86,11 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
99
86
|
}
|
|
100
87
|
});
|
|
101
88
|
}
|
|
102
|
-
async
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.contextFiles[file] = fileContents[file];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
async handleUserAction(rawCodeCommand, prompt) {
|
|
110
|
-
const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
|
|
111
|
-
const files = Object.keys(fileContents);
|
|
89
|
+
async handleUserAction(response, prompt) {
|
|
90
|
+
const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
|
|
91
|
+
const files = Array.from(new Set(currentNewBlocks.map(block => block.filePath)));
|
|
112
92
|
if (this.options.save) {
|
|
113
|
-
await (0, string_utils_1.
|
|
114
|
-
await this.updateContextFiles(fileContents);
|
|
93
|
+
await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
|
|
115
94
|
return;
|
|
116
95
|
}
|
|
117
96
|
const action = await (0, prompts_1.select)({
|
|
@@ -143,42 +122,16 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
143
122
|
includeMoreFiles = false;
|
|
144
123
|
}
|
|
145
124
|
}
|
|
146
|
-
const
|
|
147
|
-
this.displayGeneratedCode(
|
|
148
|
-
await this.handleUserAction(
|
|
125
|
+
const newResponse = await this.generateCode(newPrompt);
|
|
126
|
+
this.displayGeneratedCode(newResponse);
|
|
127
|
+
await this.handleUserAction(newResponse, newPrompt);
|
|
149
128
|
break;
|
|
150
129
|
case 'explain':
|
|
151
|
-
const explainPrompt = this.generateExplainCodePrompt(
|
|
152
|
-
|
|
130
|
+
const explainPrompt = this.generateExplainCodePrompt(response);
|
|
131
|
+
this.chatHistory.push({ role: 'user', content: explainPrompt });
|
|
132
|
+
const explainCodeCommand = await this.gpt.generateText({ prompt: explainPrompt, messages: this.chatHistory });
|
|
153
133
|
console.log((0, cli_highlight_1.default)(explainCodeCommand, { language: 'markdown' }));
|
|
154
134
|
break;
|
|
155
|
-
case 'copySource':
|
|
156
|
-
let copyAnother = true;
|
|
157
|
-
while (copyAnother) {
|
|
158
|
-
const fileToCopy = await (0, prompts_1.select)({
|
|
159
|
-
message: 'Which file do you want to copy to the clipboard?',
|
|
160
|
-
choices: files.map(file => ({ name: file, value: file })),
|
|
161
|
-
});
|
|
162
|
-
if (fileContents[fileToCopy]) {
|
|
163
|
-
copy_paste_1.default.copy(fileContents[fileToCopy], () => {
|
|
164
|
-
console.log(`${fileToCopy} copied to clipboard`);
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
console.log('File content not found.');
|
|
169
|
-
}
|
|
170
|
-
const copyMore = await (0, prompts_1.confirm)({
|
|
171
|
-
message: 'Do you want to copy another file?',
|
|
172
|
-
default: true,
|
|
173
|
-
});
|
|
174
|
-
copyAnother = copyMore;
|
|
175
|
-
}
|
|
176
|
-
break;
|
|
177
|
-
case 'copyAll':
|
|
178
|
-
copy_paste_1.default.copy(rawCodeCommand, () => {
|
|
179
|
-
console.log('Entire response copied to clipboard');
|
|
180
|
-
});
|
|
181
|
-
break;
|
|
182
135
|
case 'save':
|
|
183
136
|
let saveAnother = true;
|
|
184
137
|
while (saveAnother) {
|
|
@@ -186,16 +139,7 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
186
139
|
message: 'Which file do you want to save?',
|
|
187
140
|
choices: files.map(file => ({ name: file, value: file })),
|
|
188
141
|
});
|
|
189
|
-
|
|
190
|
-
const filePath = path_1.default.resolve(fileToSave);
|
|
191
|
-
await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
192
|
-
await fs_1.default.promises.writeFile(filePath, fileContents[fileToSave]);
|
|
193
|
-
console.log(`${fileToSave} has been saved.`);
|
|
194
|
-
await this.updateContextFiles({ [fileToSave]: fileContents[fileToSave] });
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
console.log('File content not found.');
|
|
198
|
-
}
|
|
142
|
+
await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks.filter(block => block.filePath === fileToSave));
|
|
199
143
|
const saveMore = await (0, prompts_1.confirm)({
|
|
200
144
|
message: 'Do you want to save another file?',
|
|
201
145
|
default: true,
|
|
@@ -204,8 +148,7 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
204
148
|
}
|
|
205
149
|
break;
|
|
206
150
|
case 'saveAll':
|
|
207
|
-
await (0, string_utils_1.
|
|
208
|
-
await this.updateContextFiles(fileContents);
|
|
151
|
+
await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
|
|
209
152
|
console.log('All files have been saved.');
|
|
210
153
|
break;
|
|
211
154
|
case 'done':
|
|
@@ -215,71 +158,9 @@ Do not use any data from the example response structure, only use the structure.
|
|
|
215
158
|
generateExplainCodePrompt(prompt) {
|
|
216
159
|
return `${prompt}
|
|
217
160
|
|
|
218
|
-
|
|
161
|
+
# Task
|
|
219
162
|
|
|
220
163
|
Please briefly explain how the code works in this.`;
|
|
221
164
|
}
|
|
222
|
-
async generateCode(prompt, previousCode) {
|
|
223
|
-
const codePrompt = this.generateCodePrompt(prompt, previousCode);
|
|
224
|
-
return await this.gpt.generateText({ prompt: codePrompt });
|
|
225
|
-
}
|
|
226
|
-
async processCode(prompt) {
|
|
227
|
-
try {
|
|
228
|
-
if (this.options.files) {
|
|
229
|
-
let expandedFiles = [];
|
|
230
|
-
for (const file of this.options.files) {
|
|
231
|
-
const filePath = path_1.default.resolve(file);
|
|
232
|
-
if (await fs_1.default.promises.stat(filePath).then(stat => stat.isDirectory()).catch(() => false)) {
|
|
233
|
-
const dirFiles = await (0, command_utils_1.getAllFilesInDirectory)(filePath);
|
|
234
|
-
expandedFiles = expandedFiles.concat(dirFiles.map(f => path_1.default.relative(process.cwd(), f)));
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
this.options.files = expandedFiles;
|
|
241
|
-
for (const file of this.options.files) {
|
|
242
|
-
const filePath = path_1.default.resolve(file);
|
|
243
|
-
if (await fs_1.default.promises.stat(filePath).then(stat => stat.isFile()).catch(() => false)) {
|
|
244
|
-
const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
|
|
245
|
-
this.contextFiles[file] = content;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
let includeMoreFiles = true;
|
|
251
|
-
while (includeMoreFiles) {
|
|
252
|
-
const contextFile = await (0, prompts_1.input)({
|
|
253
|
-
message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue):`,
|
|
254
|
-
});
|
|
255
|
-
if (contextFile) {
|
|
256
|
-
this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
includeMoreFiles = false;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
if (!prompt) {
|
|
264
|
-
const userPrompt = await (0, prompts_1.input)({
|
|
265
|
-
message: 'What would you like the code to do:',
|
|
266
|
-
});
|
|
267
|
-
prompt = userPrompt;
|
|
268
|
-
}
|
|
269
|
-
let rawCodeCommand = await this.generateCode(prompt);
|
|
270
|
-
this.displayGeneratedCode(rawCodeCommand);
|
|
271
|
-
if (this.options.save) {
|
|
272
|
-
const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
|
|
273
|
-
await (0, string_utils_1.saveGeneratedFiles)(fileContents);
|
|
274
|
-
await this.updateContextFiles(fileContents);
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
await this.handleUserAction(rawCodeCommand, prompt);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
catch (error) {
|
|
281
|
-
console.error('Could not generate code', error);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
165
|
}
|
|
285
166
|
exports.default = CodeManager;
|
|
@@ -0,0 +1,55 @@
|
|
|
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 fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const string_utils_1 = require("../utils/string_utils");
|
|
10
|
+
const git_utils_1 = require("../utils/git_utils");
|
|
11
|
+
const config_utils_1 = require("../utils/config_utils");
|
|
12
|
+
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
13
|
+
class CommitManager {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
16
|
+
this.gpt = new cloving_gpt_1.default(options);
|
|
17
|
+
}
|
|
18
|
+
async commit() {
|
|
19
|
+
try {
|
|
20
|
+
// Get the git diff
|
|
21
|
+
const diff = (0, child_process_1.execSync)('git diff HEAD').toString().trim();
|
|
22
|
+
// Check if the diff is blank
|
|
23
|
+
if (!diff) {
|
|
24
|
+
console.error('No changes to commit.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Generate the prompt for commit message
|
|
28
|
+
const prompt = (0, git_utils_1.generateCommitMessagePrompt)(diff);
|
|
29
|
+
// Instantiate ClovingGPT and get the commit message
|
|
30
|
+
const rawCommitMessage = await this.gpt.generateText({ prompt });
|
|
31
|
+
// Clean the commit message using extractMarkdown
|
|
32
|
+
const commitMessage = (0, string_utils_1.extractMarkdown)(rawCommitMessage);
|
|
33
|
+
// Write the commit message to a temporary file
|
|
34
|
+
const tempCommitFilePath = path_1.default.join('.git', 'SUGGESTED_COMMIT_EDITMSG');
|
|
35
|
+
fs_1.default.writeFileSync(tempCommitFilePath, commitMessage);
|
|
36
|
+
// Commit the changes using the generated commit message
|
|
37
|
+
try {
|
|
38
|
+
(0, child_process_1.execFileSync)('git', ['commit', '-a', '--edit', '--file', tempCommitFilePath], { stdio: 'inherit' });
|
|
39
|
+
}
|
|
40
|
+
catch (commitError) {
|
|
41
|
+
// If commit is canceled (non-zero exit), handle it here
|
|
42
|
+
console.log('Commit was canceled or failed.');
|
|
43
|
+
}
|
|
44
|
+
// Remove the temporary file using fs
|
|
45
|
+
fs_1.default.unlink(tempCommitFilePath, (err) => {
|
|
46
|
+
if (err)
|
|
47
|
+
throw err;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error('Could not generate commit message:', error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.default = CommitManager;
|
|
@@ -60,7 +60,7 @@ class ProxyManager {
|
|
|
60
60
|
const { messages, stream } = req.body;
|
|
61
61
|
const content = messages.map((message) => message.content).join('\n\n');
|
|
62
62
|
const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
|
|
63
|
-
const prompt =
|
|
63
|
+
const prompt = `# Task
|
|
64
64
|
|
|
65
65
|
${content}
|
|
66
66
|
|
|
@@ -70,7 +70,7 @@ ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
|
|
|
70
70
|
|
|
71
71
|
${contextFileContents}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
# Task
|
|
74
74
|
|
|
75
75
|
${content}`;
|
|
76
76
|
if (stream) {
|
|
@@ -8,15 +8,18 @@ const prompts_1 = require("@inquirer/prompts");
|
|
|
8
8
|
const copy_paste_1 = __importDefault(require("copy-paste"));
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
11
12
|
const git_utils_1 = require("../utils/git_utils");
|
|
12
13
|
const string_utils_1 = require("../utils/string_utils");
|
|
13
14
|
const command_utils_1 = require("../utils/command_utils");
|
|
14
15
|
const config_utils_1 = require("../utils/config_utils");
|
|
15
16
|
class ReviewManager {
|
|
16
|
-
constructor(
|
|
17
|
+
constructor(options) {
|
|
17
18
|
this.contextFiles = {};
|
|
18
|
-
this.
|
|
19
|
+
this.chatHistory = [];
|
|
20
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
19
21
|
this.options = options;
|
|
22
|
+
this.gpt = new cloving_gpt_1.default(options);
|
|
20
23
|
}
|
|
21
24
|
async loadContextFiles() {
|
|
22
25
|
if (this.options.files) {
|
|
@@ -41,12 +44,9 @@ class ReviewManager {
|
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
${this.options.prompt || 'Please provide a code review.'}
|
|
48
|
-
|
|
49
|
-
### Example of a well-structured response
|
|
47
|
+
generateReviewPrompt() {
|
|
48
|
+
if (this.chatHistory.length === 0) {
|
|
49
|
+
let systemPrompt = `### Example of a well-structured response
|
|
50
50
|
|
|
51
51
|
# Code Review: Enhanced Code Generation and File Handling
|
|
52
52
|
|
|
@@ -95,11 +95,11 @@ ${this.options.prompt || 'Please provide a code review.'}
|
|
|
95
95
|
// ...
|
|
96
96
|
}
|
|
97
97
|
\`\`\`
|
|
98
|
+
|
|
98
99
|
`;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
### Description of App
|
|
100
|
+
if (this.options.files) {
|
|
101
|
+
const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
|
|
102
|
+
systemPrompt += `### Description of App
|
|
103
103
|
|
|
104
104
|
${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
|
|
105
105
|
|
|
@@ -110,26 +110,31 @@ ${contextFileContents}
|
|
|
110
110
|
I would like you to explain the code and document a description of it.
|
|
111
111
|
List any bugs in the new code as well as recommended fixes for those bugs with code examples.
|
|
112
112
|
Format the output of this code review in Markdown format.`;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
### Request
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
systemPrompt += `### Request
|
|
118
116
|
|
|
119
117
|
Do not use any data from the example response structure, only use the structure.
|
|
120
118
|
I would like you to explain why these change are being made and document a description of these changes.
|
|
121
119
|
Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
|
|
122
120
|
Format the output of this code review in Markdown format.`;
|
|
121
|
+
}
|
|
122
|
+
this.chatHistory.push({ role: 'user', content: systemPrompt });
|
|
123
|
+
this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
|
|
123
124
|
}
|
|
124
|
-
|
|
125
|
+
this.chatHistory.push({ role: 'user', content: this.options.prompt || 'Please provide a code review.' });
|
|
126
|
+
return this.options.prompt || 'Please provide a code review.';
|
|
125
127
|
}
|
|
126
128
|
async getGitDiffPrompt() {
|
|
127
129
|
const gitDiff = await (0, git_utils_1.getGitDiff)();
|
|
128
|
-
|
|
130
|
+
this.options.prompt = `## Diff Content
|
|
129
131
|
${gitDiff}
|
|
130
|
-
==== end diff =====
|
|
131
132
|
|
|
132
|
-
|
|
133
|
+
# Task
|
|
134
|
+
|
|
135
|
+
${this.options.prompt || 'Please provide a code review.'}`;
|
|
136
|
+
const prompt = this.generateReviewPrompt();
|
|
137
|
+
return prompt;
|
|
133
138
|
}
|
|
134
139
|
displayAnalysis(analysis) {
|
|
135
140
|
(0, string_utils_1.parseMarkdownInstructions)(analysis).map(code => {
|
|
@@ -195,8 +200,8 @@ ${this.generatePrompt()}`;
|
|
|
195
200
|
async review() {
|
|
196
201
|
try {
|
|
197
202
|
await this.loadContextFiles();
|
|
198
|
-
const prompt = this.options.files ? this.
|
|
199
|
-
const analysis = await this.gpt.generateText({ prompt });
|
|
203
|
+
const prompt = this.options.files ? this.generateReviewPrompt() : await this.getGitDiffPrompt();
|
|
204
|
+
const analysis = await this.gpt.generateText({ prompt, messages: this.chatHistory });
|
|
200
205
|
this.displayAnalysis(analysis);
|
|
201
206
|
await this.handleUserAction(analysis);
|
|
202
207
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
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 copy_paste_1 = __importDefault(require("copy-paste"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
10
|
+
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
11
|
+
const command_utils_1 = require("../utils/command_utils");
|
|
12
|
+
const string_utils_1 = require("../utils/string_utils");
|
|
13
|
+
const config_utils_1 = require("../utils/config_utils");
|
|
14
|
+
class ShellManager {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.chatHistory = [];
|
|
18
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
19
|
+
this.gpt = new cloving_gpt_1.default(options);
|
|
20
|
+
}
|
|
21
|
+
generateExplainShellPrompt(prompt) {
|
|
22
|
+
return `${prompt}
|
|
23
|
+
|
|
24
|
+
# Task
|
|
25
|
+
|
|
26
|
+
Please briefly explain how this shell script works.`;
|
|
27
|
+
}
|
|
28
|
+
async generateShell(prompt) {
|
|
29
|
+
if (this.chatHistory.length === 0) {
|
|
30
|
+
const systemPrompt = (0, command_utils_1.generateShellPrompt)();
|
|
31
|
+
this.chatHistory.push({ role: 'user', content: systemPrompt });
|
|
32
|
+
this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
|
|
33
|
+
}
|
|
34
|
+
this.chatHistory.push({ role: 'user', content: prompt });
|
|
35
|
+
return await this.gpt.generateText({ prompt, messages: this.chatHistory });
|
|
36
|
+
}
|
|
37
|
+
displayGeneratedShell(rawShellCommand) {
|
|
38
|
+
const generatedShell = (0, string_utils_1.extractMarkdown)(rawShellCommand);
|
|
39
|
+
const generatedShellWithoutShebang = generatedShell.replace(/^#!.*?\s/, '');
|
|
40
|
+
console.log((0, cli_highlight_1.default)(generatedShellWithoutShebang));
|
|
41
|
+
}
|
|
42
|
+
async handleUserAction(response, prompt) {
|
|
43
|
+
this.chatHistory.push({ role: 'assistant', content: response });
|
|
44
|
+
const generatedShell = (0, string_utils_1.extractMarkdown)(response);
|
|
45
|
+
const generatedShellWithoutShebang = generatedShell.replace(/^#!.*?\s/, '');
|
|
46
|
+
const action = await (0, prompts_1.select)({
|
|
47
|
+
message: 'What would you like to do?',
|
|
48
|
+
choices: [
|
|
49
|
+
{ name: 'Execute', value: 'execute' },
|
|
50
|
+
{ name: 'Revise', value: 'revise' },
|
|
51
|
+
{ name: 'Explain', value: 'explain' },
|
|
52
|
+
{ name: 'Copy to Clipboard', value: 'copy' },
|
|
53
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
switch (action) {
|
|
57
|
+
case 'execute':
|
|
58
|
+
(0, child_process_1.execSync)(generatedShellWithoutShebang, { stdio: 'inherit' });
|
|
59
|
+
break;
|
|
60
|
+
case 'revise':
|
|
61
|
+
const newPrompt = await (0, prompts_1.input)({
|
|
62
|
+
message: 'How would you like to modify the output:',
|
|
63
|
+
});
|
|
64
|
+
const newRawShellCommand = await this.generateShell(newPrompt);
|
|
65
|
+
this.displayGeneratedShell(newRawShellCommand);
|
|
66
|
+
await this.handleUserAction(newRawShellCommand, newPrompt);
|
|
67
|
+
break;
|
|
68
|
+
case 'explain':
|
|
69
|
+
const explainPrompt = this.generateExplainShellPrompt(generatedShellWithoutShebang);
|
|
70
|
+
this.chatHistory.push({ role: 'user', content: explainPrompt });
|
|
71
|
+
const explainShellCommand = await this.gpt.generateText({ prompt: explainPrompt, messages: this.chatHistory });
|
|
72
|
+
console.log((0, cli_highlight_1.default)(explainShellCommand, { language: 'markdown' }));
|
|
73
|
+
break;
|
|
74
|
+
case 'copy':
|
|
75
|
+
copy_paste_1.default.copy(generatedShellWithoutShebang, (err) => {
|
|
76
|
+
if (err) {
|
|
77
|
+
console.error('Error: Unable to copy to clipboard.', err);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log('Script copied to clipboard.');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
break;
|
|
84
|
+
case 'cancel':
|
|
85
|
+
console.log('Operation cancelled.');
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async generateAndHandleShell() {
|
|
90
|
+
let { prompt: userPrompt } = this.options;
|
|
91
|
+
try {
|
|
92
|
+
if (!userPrompt) {
|
|
93
|
+
userPrompt = await (0, prompts_1.input)({
|
|
94
|
+
message: 'What would you like to do: '
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const rawShellCommand = await this.generateShell(userPrompt);
|
|
98
|
+
this.displayGeneratedShell(rawShellCommand);
|
|
99
|
+
await this.handleUserAction(rawShellCommand, userPrompt);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error('Could not generate shell script', error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.default = ShellManager;
|
package/dist/newfile.js
ADDED
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloving",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"packageManager": "yarn@1.22.22",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,25 +36,26 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@inquirer/prompts": "^5.3.
|
|
39
|
+
"@inquirer/prompts": "^5.3.8",
|
|
40
40
|
"axios": "^1.7.3",
|
|
41
41
|
"cli-highlight": "^2.1.11",
|
|
42
|
+
"colors": "^1.4.0",
|
|
42
43
|
"commander": "^12.1.0",
|
|
43
44
|
"copy-paste": "^1.5.3",
|
|
44
|
-
"eslint": "^9.
|
|
45
|
+
"eslint": "^9.9.0",
|
|
45
46
|
"eslint-config-prettier": "^9.1.0",
|
|
46
47
|
"eslint-config-raine": "^0.5.0",
|
|
47
48
|
"eslint-config-standard": "^17.1.0",
|
|
48
49
|
"eslint-plugin-import": "^2.29.1",
|
|
49
|
-
"eslint-plugin-jsdoc": "^
|
|
50
|
-
"eslint-plugin-n": "^17.10.
|
|
51
|
-
"eslint-plugin-promise": "^7.
|
|
50
|
+
"eslint-plugin-jsdoc": "^50.1.0",
|
|
51
|
+
"eslint-plugin-n": "^17.10.2",
|
|
52
|
+
"eslint-plugin-promise": "^7.1.0",
|
|
52
53
|
"express": "^4.19.2",
|
|
53
|
-
"ignore": "^5.3.
|
|
54
|
+
"ignore": "^5.3.2",
|
|
54
55
|
"ini": "^4.1.3",
|
|
55
56
|
"isbinaryfile": "^5.0.2",
|
|
56
57
|
"nock": "^13.5.4",
|
|
57
|
-
"vite": "^5.
|
|
58
|
+
"vite": "^5.4.0",
|
|
58
59
|
"vite-node": "^2.0.5"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
@@ -64,11 +65,11 @@
|
|
|
64
65
|
"@types/ini": "^4.1.1",
|
|
65
66
|
"@types/inquirer": "^9.0.7",
|
|
66
67
|
"@types/jest": "^29.5.12",
|
|
67
|
-
"@types/node": "^22.
|
|
68
|
+
"@types/node": "^22.2.0",
|
|
68
69
|
"husky": "^9.1.4",
|
|
69
70
|
"jest": "^29.7.0",
|
|
70
71
|
"lint-prepush": "^2.2.2",
|
|
71
|
-
"lint-staged": "^15.2.
|
|
72
|
+
"lint-staged": "^15.2.9",
|
|
72
73
|
"ts-jest": "^29.2.4",
|
|
73
74
|
"typescript": "^5.5.4"
|
|
74
75
|
},
|