cloving 0.1.15 → 0.1.17
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 +40 -32
- package/dist/cloving_gpt/adapters/claude.js +0 -2
- package/dist/cloving_gpt/adapters/openai.js +1 -0
- package/dist/cloving_gpt/index.js +12 -18
- package/dist/commands/config.js +54 -82
- package/dist/commands/generate/code.js +47 -91
- package/dist/commands/generate/context.js +8 -16
- package/dist/commands/generate/review.js +12 -16
- package/dist/commands/generate/shell.js +22 -35
- package/dist/commands/generate/unit_tests.js +156 -68
- package/dist/commands/init.js +9 -17
- package/dist/commands/project/build.js +11 -19
- package/dist/commands/project/init.js +4 -8
- package/dist/index.js +6 -0
- package/dist/package.json +16 -17
- package/dist/utils/config_utils.js +2 -1
- package/dist/utils/git_utils.js +3 -1
- package/dist/utils/string_utils.js +3 -1
- package/package.json +16 -17
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
7
|
-
const
|
|
7
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
8
8
|
const copy_paste_1 = __importDefault(require("copy-paste"));
|
|
9
9
|
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
10
10
|
const git_utils_1 = require("../../utils/git_utils");
|
|
@@ -16,7 +16,7 @@ const review = async (options) => {
|
|
|
16
16
|
try {
|
|
17
17
|
// Define the prompt for analysis
|
|
18
18
|
const gitDiff = await (0, git_utils_1.getGitDiff)();
|
|
19
|
-
const
|
|
19
|
+
const promptText = `==== begin diff =====
|
|
20
20
|
${gitDiff}
|
|
21
21
|
==== end diff =====
|
|
22
22
|
|
|
@@ -77,7 +77,7 @@ I would like you to explain why these change are being made and document a descr
|
|
|
77
77
|
Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
|
|
78
78
|
Format the output of this code review in Markdown format.`;
|
|
79
79
|
// get the analysis
|
|
80
|
-
const analysis = await gpt.generateText({ prompt });
|
|
80
|
+
const analysis = await gpt.generateText({ prompt: promptText });
|
|
81
81
|
// Print the analysis to the console
|
|
82
82
|
(0, string_utils_1.parseMarkdownInstructions)(analysis).map(code => {
|
|
83
83
|
if (code.trim().startsWith('```')) {
|
|
@@ -104,19 +104,15 @@ Format the output of this code review in Markdown format.`;
|
|
|
104
104
|
return match ? match[0].trim() : '';
|
|
105
105
|
};
|
|
106
106
|
// Prompt the user with options for copying to clipboard
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
name: '
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{ name: 'Done', value: 'Done' }
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
]);
|
|
107
|
+
const clipboardOption = await (0, prompts_1.select)({
|
|
108
|
+
message: 'What would you like to copy to the clipboard?',
|
|
109
|
+
choices: [
|
|
110
|
+
{ name: 'Copy only the Changes Overview', value: 'Changes Overview' },
|
|
111
|
+
{ name: 'Copy only the Potential Bugs and Recommended Fixes', value: 'Potential Bugs and Recommended Fixes' },
|
|
112
|
+
{ name: 'Copy everything', value: 'Everything' },
|
|
113
|
+
{ name: 'Done', value: 'Done' }
|
|
114
|
+
]
|
|
115
|
+
});
|
|
120
116
|
let contentToCopy = '';
|
|
121
117
|
switch (clipboardOption) {
|
|
122
118
|
case 'Changes Overview':
|
|
@@ -3,13 +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 inquirer_1 = __importDefault(require("inquirer"));
|
|
7
|
-
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
8
6
|
const child_process_1 = require("child_process");
|
|
7
|
+
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
9
8
|
const copy_paste_1 = __importDefault(require("copy-paste"));
|
|
10
9
|
const string_utils_1 = require("../../utils/string_utils");
|
|
11
10
|
const config_utils_1 = require("../../utils/config_utils");
|
|
12
11
|
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
12
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
13
13
|
const generateShellPrompt = (prompt) => {
|
|
14
14
|
const shell = (0, child_process_1.execSync)('echo $SHELL').toString().trim();
|
|
15
15
|
const os = (0, child_process_1.execSync)('echo $OSTYPE').toString().trim();
|
|
@@ -48,32 +48,24 @@ const displayGeneratedShell = (rawShellCommand) => {
|
|
|
48
48
|
const handleUserAction = async (gpt, rawShellCommand, prompt) => {
|
|
49
49
|
const generatedShell = (0, string_utils_1.extractMarkdown)(rawShellCommand);
|
|
50
50
|
const generatedShellWithoutShebang = generatedShell.replace(/^#!.*?\s/, '');
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
name: '
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
{ name: 'Cancel', value: 'cancel' },
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
]);
|
|
51
|
+
const action = await (0, prompts_1.select)({
|
|
52
|
+
message: 'What would you like to do?',
|
|
53
|
+
choices: [
|
|
54
|
+
{ name: 'Execute', value: 'execute' },
|
|
55
|
+
{ name: 'Revise', value: 'revise' },
|
|
56
|
+
{ name: 'Explain', value: 'explain' },
|
|
57
|
+
{ name: 'Copy to Clipboard', value: 'copy' },
|
|
58
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
59
|
+
],
|
|
60
|
+
});
|
|
65
61
|
switch (action) {
|
|
66
62
|
case 'execute':
|
|
67
63
|
(0, child_process_1.execSync)(generatedShellWithoutShebang, { stdio: 'inherit' });
|
|
68
64
|
break;
|
|
69
65
|
case 'revise':
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
name: 'newPrompt',
|
|
74
|
-
message: 'How would you like to modify the output:',
|
|
75
|
-
},
|
|
76
|
-
]);
|
|
66
|
+
const newPrompt = await (0, prompts_1.input)({
|
|
67
|
+
message: 'How would you like to modify the output:',
|
|
68
|
+
});
|
|
77
69
|
const newRawShellCommand = await generateShell(gpt, newPrompt);
|
|
78
70
|
displayGeneratedShell(newRawShellCommand);
|
|
79
71
|
await handleUserAction(gpt, newRawShellCommand, newPrompt);
|
|
@@ -99,23 +91,18 @@ const handleUserAction = async (gpt, rawShellCommand, prompt) => {
|
|
|
99
91
|
}
|
|
100
92
|
};
|
|
101
93
|
const shell = async (options) => {
|
|
102
|
-
let { prompt } = options;
|
|
94
|
+
let { prompt: userPrompt } = options;
|
|
103
95
|
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
104
96
|
const gpt = new cloving_gpt_1.default(options);
|
|
105
97
|
try {
|
|
106
|
-
if (!
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
name: 'userPrompt',
|
|
111
|
-
message: 'What would you like to do: '
|
|
112
|
-
}
|
|
113
|
-
]);
|
|
114
|
-
prompt = userPrompt;
|
|
98
|
+
if (!userPrompt) {
|
|
99
|
+
userPrompt = await (0, prompts_1.input)({
|
|
100
|
+
message: 'What would you like to do: '
|
|
101
|
+
});
|
|
115
102
|
}
|
|
116
|
-
const rawShellCommand = await generateShell(gpt,
|
|
103
|
+
const rawShellCommand = await generateShell(gpt, userPrompt);
|
|
117
104
|
displayGeneratedShell(rawShellCommand);
|
|
118
|
-
await handleUserAction(gpt, rawShellCommand,
|
|
105
|
+
await handleUserAction(gpt, rawShellCommand, userPrompt);
|
|
119
106
|
}
|
|
120
107
|
catch (error) {
|
|
121
108
|
console.error('Could not generate shell script', error);
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
9
|
-
const
|
|
9
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const git_utils_1 = require("../../utils/git_utils");
|
|
12
12
|
const config_utils_1 = require("../../utils/config_utils");
|
|
@@ -23,7 +23,10 @@ const extractChangedFiles = (gitDiff) => {
|
|
|
23
23
|
};
|
|
24
24
|
const generatePrompt = (files, srcFiles, testFiles, gitDiff) => {
|
|
25
25
|
const filesList = files.length > 0 ? files.join('\n') : 'No files provided';
|
|
26
|
-
return
|
|
26
|
+
return `Enumerate all the files in the provided list${gitDiff ? ' and git diff' : ''} as well as the file names of anything that these files interact with.
|
|
27
|
+
Also, list any test files that might be relevant to these files.
|
|
28
|
+
|
|
29
|
+
### List of Code Files
|
|
27
30
|
|
|
28
31
|
${filesList}
|
|
29
32
|
|
|
@@ -35,13 +38,13 @@ ${srcFiles}
|
|
|
35
38
|
|
|
36
39
|
${testFiles}
|
|
37
40
|
|
|
38
|
-
### Git Diff Content
|
|
41
|
+
${gitDiff ? '### Git Diff Content' : ''}
|
|
39
42
|
|
|
40
|
-
${gitDiff}
|
|
43
|
+
${gitDiff || ''}
|
|
41
44
|
|
|
42
45
|
## Example Output
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
Here are the files that are relevant:
|
|
45
48
|
|
|
46
49
|
\`\`\`plaintext
|
|
47
50
|
## files
|
|
@@ -55,69 +58,68 @@ test/models/foo_test.rb
|
|
|
55
58
|
test/controllers/baz_controller_test.rb
|
|
56
59
|
\`\`\`
|
|
57
60
|
|
|
58
|
-
###
|
|
59
|
-
|
|
60
|
-
Please enumerate all the files in the provided list${gitDiff ? ' and git diff' : ''} as well as the file names of anything that these files interact with.
|
|
61
|
+
### Prompt
|
|
61
62
|
|
|
63
|
+
Enumerate all the files in the provided list${gitDiff ? ' and git diff' : ''} as well as the file names of anything that these files interact with.
|
|
62
64
|
Also, list any test files that might be relevant to these files.`;
|
|
63
65
|
};
|
|
64
|
-
const handleUserAction = async (analysis) => {
|
|
66
|
+
const handleUserAction = async (analysis, autoSave = false) => {
|
|
65
67
|
const [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(analysis);
|
|
66
|
-
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
if (autoSave) {
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
if (fileContents[file]) {
|
|
71
|
+
const filePath = path_1.default.resolve(file);
|
|
72
|
+
await fs_1.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
73
|
+
await fs_1.promises.writeFile(filePath, fileContents[file]);
|
|
74
|
+
console.log(`${file} has been saved.`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.log(`File content not found for ${file}.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
console.log('All unit test files have been saved.');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const action = await (0, prompts_1.select)({
|
|
84
|
+
message: 'What would you like to do?',
|
|
85
|
+
choices: [
|
|
86
|
+
{ name: 'Save a Unit Test File', value: 'save' },
|
|
87
|
+
{ name: 'Save All Unit Test Files', value: 'saveAll' },
|
|
88
|
+
{ name: 'Copy Unit Test to Clipboard', value: 'copyTest' },
|
|
89
|
+
{ name: 'Copy Entire Response to Clipboard', value: 'copyAll' },
|
|
90
|
+
{ name: 'Done', value: 'done' },
|
|
91
|
+
],
|
|
92
|
+
});
|
|
80
93
|
switch (action) {
|
|
81
94
|
case 'save':
|
|
82
95
|
let saveAnother = true;
|
|
83
96
|
while (saveAnother) {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
message: 'Which unit test file do you want to save?',
|
|
89
|
-
choices: files.map(file => ({ name: file, value: file })),
|
|
90
|
-
},
|
|
91
|
-
]);
|
|
97
|
+
const fileToSave = await (0, prompts_1.select)({
|
|
98
|
+
message: 'Which unit test file do you want to save?',
|
|
99
|
+
choices: files.map(file => ({ name: file, value: file })),
|
|
100
|
+
});
|
|
92
101
|
if (fileContents[fileToSave]) {
|
|
93
102
|
const filePath = path_1.default.resolve(fileToSave);
|
|
94
|
-
fs_1.promises.mkdir(path_1.default.dirname(filePath), { recursive: true })
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.catch((error) => console.error(`Error saving ${fileToSave}:`, error));
|
|
103
|
+
await fs_1.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
104
|
+
await fs_1.promises.writeFile(filePath, fileContents[fileToSave]);
|
|
105
|
+
console.log(`${fileToSave} has been saved.`);
|
|
98
106
|
}
|
|
99
107
|
else {
|
|
100
108
|
console.log('File content not found.');
|
|
101
109
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
message: 'Do you want to save another file?',
|
|
107
|
-
default: false,
|
|
108
|
-
},
|
|
109
|
-
]);
|
|
110
|
-
saveAnother = saveMore;
|
|
110
|
+
saveAnother = await (0, prompts_1.confirm)({
|
|
111
|
+
message: 'Do you want to save another file?',
|
|
112
|
+
default: false,
|
|
113
|
+
});
|
|
111
114
|
}
|
|
112
115
|
break;
|
|
113
116
|
case 'saveAll':
|
|
114
117
|
for (const file of files) {
|
|
115
118
|
if (fileContents[file]) {
|
|
116
119
|
const filePath = path_1.default.resolve(file);
|
|
117
|
-
fs_1.promises.mkdir(path_1.default.dirname(filePath), { recursive: true })
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
.catch((error) => console.error(`Error saving ${file}:`, error));
|
|
120
|
+
await fs_1.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
121
|
+
await fs_1.promises.writeFile(filePath, fileContents[file]);
|
|
122
|
+
console.log(`${file} has been saved.`);
|
|
121
123
|
}
|
|
122
124
|
else {
|
|
123
125
|
console.log(`File content not found for ${file}.`);
|
|
@@ -126,14 +128,10 @@ const handleUserAction = async (analysis) => {
|
|
|
126
128
|
console.log('All unit test files have been saved.');
|
|
127
129
|
break;
|
|
128
130
|
case 'copyTest':
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
message: 'Which unit test file do you want to copy to the clipboard?',
|
|
134
|
-
choices: files.map(file => ({ name: file, value: file })),
|
|
135
|
-
},
|
|
136
|
-
]);
|
|
131
|
+
const fileToCopy = await (0, prompts_1.select)({
|
|
132
|
+
message: 'Which unit test file do you want to copy to the clipboard?',
|
|
133
|
+
choices: files.map(file => ({ name: file, value: file })),
|
|
134
|
+
});
|
|
137
135
|
if (fileContents[fileToCopy]) {
|
|
138
136
|
process.nextTick(() => {
|
|
139
137
|
require('copy-paste').copy(fileContents[fileToCopy], () => {
|
|
@@ -157,7 +155,7 @@ const handleUserAction = async (analysis) => {
|
|
|
157
155
|
}
|
|
158
156
|
};
|
|
159
157
|
const unitTests = async (options) => {
|
|
160
|
-
const { files } = options;
|
|
158
|
+
const { files, save } = options;
|
|
161
159
|
const gpt = new cloving_gpt_1.default(options);
|
|
162
160
|
const allSrcFiles = await (0, config_utils_1.getAllFiles)(options, true);
|
|
163
161
|
const testingDirectory = (0, config_utils_1.getTestingDirectory)();
|
|
@@ -168,15 +166,22 @@ const unitTests = async (options) => {
|
|
|
168
166
|
const testFiles = (0, child_process_1.execFileSync)('find', [testingDirectory, '-type', 'f']).toString().trim();
|
|
169
167
|
let contextFiles;
|
|
170
168
|
if (files) {
|
|
169
|
+
console.log('Generating unit tests for the provided files...');
|
|
170
|
+
console.log('Gathering a list of files related to the changes...');
|
|
171
171
|
const prompt = generatePrompt(files, allSrcFiles.join('\n'), testFiles);
|
|
172
172
|
contextFiles = await gpt.generateText({ prompt });
|
|
173
173
|
}
|
|
174
174
|
else {
|
|
175
|
+
console.log('Generating unit tests for the git diff between this branch and the default branch...');
|
|
176
|
+
console.log('Gathering a list of files related to the changes...');
|
|
175
177
|
const gitDiff = await (0, git_utils_1.getGitDiff)();
|
|
176
178
|
const changedFiles = extractChangedFiles(gitDiff);
|
|
177
179
|
const prompt = generatePrompt(changedFiles, allSrcFiles.join('\n'), testFiles, gitDiff);
|
|
178
180
|
contextFiles = await gpt.generateText({ prompt });
|
|
179
181
|
}
|
|
182
|
+
contextFiles = (0, string_utils_1.extractMarkdown)(contextFiles);
|
|
183
|
+
console.log(contextFiles);
|
|
184
|
+
console.log('\nBuilding the tests for these files...');
|
|
180
185
|
// Initialize variables
|
|
181
186
|
const lines = [];
|
|
182
187
|
const context = [];
|
|
@@ -189,38 +194,121 @@ const unitTests = async (options) => {
|
|
|
189
194
|
for (const codeFile of lines) {
|
|
190
195
|
if (await fs_1.promises.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
|
|
191
196
|
const fileContents = await fs_1.promises.readFile(codeFile, 'utf-8');
|
|
192
|
-
context.push(`### Contents of
|
|
197
|
+
context.push(`### Contents of **${codeFile}\n\n${fileContents.split('\n').map(line => ` ${line}`).join('\n')}**\n\n`);
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
200
|
// Generate the message for unit test creation
|
|
196
201
|
let analysis = '';
|
|
197
202
|
if ((files || []).length > 0) {
|
|
198
|
-
const message = `
|
|
203
|
+
const message = `Create unit tests for the List of Files. Always show filenames for the generated code.
|
|
199
204
|
|
|
200
|
-
##
|
|
205
|
+
## List of Files
|
|
201
206
|
${(files || []).join('\n')}
|
|
202
|
-
|
|
207
|
+
|
|
208
|
+
## Example Response Structure
|
|
209
|
+
|
|
210
|
+
Here are the unit tests for the provided list of files:
|
|
211
|
+
|
|
212
|
+
1. **tests/utils/model_utils.test.ts**
|
|
213
|
+
|
|
214
|
+
\`\`\`typescript
|
|
215
|
+
import { getModel } from '../../src/utils/model_utils'
|
|
216
|
+
|
|
217
|
+
describe('modelUtils', () => {
|
|
218
|
+
const originalEnv = process.env
|
|
219
|
+
|
|
220
|
+
beforeEach(() => {
|
|
221
|
+
jest.resetModules()
|
|
222
|
+
process.env = { ...originalEnv }
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
afterAll(() => {
|
|
226
|
+
process.env = originalEnv
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
test('getModel should return default model when CLOVING_MODEL is not set', () => {
|
|
230
|
+
delete process.env.CLOVING_MODEL
|
|
231
|
+
const result = getModel()
|
|
232
|
+
expect(result).toBe('claude:claude-3-5-sonnet-20240620')
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
test('getModel should return CLOVING_MODEL when it is set', () => {
|
|
236
|
+
process.env.CLOVING_MODEL = 'openai:gpt-4'
|
|
237
|
+
const result = getModel()
|
|
238
|
+
expect(result).toBe('openai:gpt-4')
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
\`\`\`
|
|
203
242
|
|
|
204
243
|
## Context
|
|
205
|
-
|
|
244
|
+
|
|
245
|
+
${context.join('\n\n')}
|
|
246
|
+
|
|
247
|
+
## Prompt
|
|
248
|
+
|
|
249
|
+
Create unit tests for the List of Files. Always show filenames for the generated code.`;
|
|
206
250
|
// Get the model and analysis using ClovingGPT
|
|
207
251
|
analysis = await gpt.generateText({ prompt: message });
|
|
208
252
|
}
|
|
209
253
|
else {
|
|
210
|
-
const message = `
|
|
254
|
+
const message = `Create unit tests for the Code Diff. Always show filenames for the generated code.
|
|
255
|
+
|
|
256
|
+
## Code Diff
|
|
211
257
|
|
|
212
|
-
## Begin diff
|
|
213
258
|
${await (0, git_utils_1.getGitDiff)()}
|
|
214
|
-
## End diff
|
|
215
259
|
|
|
216
260
|
## Context
|
|
217
261
|
|
|
218
|
-
${context.join('\n\n')}
|
|
262
|
+
${context.join('\n\n')}
|
|
263
|
+
|
|
264
|
+
## Example Response Structure
|
|
265
|
+
|
|
266
|
+
Here are the unit tests for the code diff:
|
|
267
|
+
|
|
268
|
+
1. **tests/utils/model_utils.test.ts**
|
|
269
|
+
|
|
270
|
+
\`\`\`typescript
|
|
271
|
+
import { getModel } from '../../src/utils/model_utils'
|
|
272
|
+
|
|
273
|
+
describe('modelUtils', () => {
|
|
274
|
+
const originalEnv = process.env
|
|
275
|
+
|
|
276
|
+
beforeEach(() => {
|
|
277
|
+
jest.resetModules()
|
|
278
|
+
process.env = { ...originalEnv }
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
afterAll(() => {
|
|
282
|
+
process.env = originalEnv
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
test('getModel should return default model when CLOVING_MODEL is not set', () => {
|
|
286
|
+
delete process.env.CLOVING_MODEL
|
|
287
|
+
const result = getModel()
|
|
288
|
+
expect(result).toBe('claude:claude-3-5-sonnet-20240620')
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
test('getModel should return CLOVING_MODEL when it is set', () => {
|
|
292
|
+
process.env.CLOVING_MODEL = 'openai:gpt-4'
|
|
293
|
+
const result = getModel()
|
|
294
|
+
expect(result).toBe('openai:gpt-4')
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
\`\`\`
|
|
298
|
+
|
|
299
|
+
## Prompt
|
|
300
|
+
|
|
301
|
+
Create unit tests for the Code Diff. Always show filenames for the generated code.`;
|
|
219
302
|
// Get the model and analysis using ClovingGPT
|
|
220
303
|
analysis = await gpt.generateText({ prompt: message });
|
|
221
304
|
}
|
|
222
|
-
|
|
305
|
+
if (analysis) {
|
|
306
|
+
console.log((0, cli_highlight_1.default)(analysis));
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
console.log('No unit tests generated.');
|
|
310
|
+
}
|
|
223
311
|
// Add the new user action handling
|
|
224
|
-
await handleUserAction(analysis);
|
|
312
|
+
await handleUserAction(analysis, save);
|
|
225
313
|
};
|
|
226
314
|
exports.default = unitTests;
|
package/dist/commands/init.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.init = void 0;
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const
|
|
10
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
11
11
|
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
12
12
|
const ignore_1 = __importDefault(require("ignore"));
|
|
13
13
|
const string_utils_1 = require("../utils/string_utils");
|
|
@@ -42,14 +42,10 @@ 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
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
message: 'No special files detected. Please describe the main technologies used in your project:',
|
|
50
|
-
validate: (input) => input.trim().length > 0 || 'Please provide a description of the technologies used.',
|
|
51
|
-
},
|
|
52
|
-
]);
|
|
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
|
+
});
|
|
53
49
|
specialFileContents['description'] = technologies;
|
|
54
50
|
}
|
|
55
51
|
const tempFilePath = path_1.default.join(os_1.default.tmpdir(), `describe_${Date.now()}.tmp`);
|
|
@@ -207,14 +203,10 @@ Here is an example response:
|
|
|
207
203
|
console.log('[done] Project data saved to cloving.json');
|
|
208
204
|
// Prompt the user if they want to review the generated cloving.json
|
|
209
205
|
if (!options.silent) {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
message: 'Do you want to review the generated data?',
|
|
215
|
-
default: true,
|
|
216
|
-
},
|
|
217
|
-
]);
|
|
206
|
+
const reviewAnswer = await (0, prompts_1.confirm)({
|
|
207
|
+
message: 'Do you want to review the generated data?',
|
|
208
|
+
default: true,
|
|
209
|
+
});
|
|
218
210
|
if (reviewAnswer) {
|
|
219
211
|
console.log(cleanAiChatResponse);
|
|
220
212
|
}
|
|
@@ -7,7 +7,7 @@ exports.buildProject = void 0;
|
|
|
7
7
|
const copy_paste_1 = __importDefault(require("copy-paste"));
|
|
8
8
|
const fs_1 = require("fs");
|
|
9
9
|
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
10
|
-
const
|
|
10
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
11
11
|
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
12
12
|
const config_utils_1 = require("../../utils/config_utils");
|
|
13
13
|
const git_utils_1 = require("../../utils/git_utils");
|
|
@@ -42,15 +42,11 @@ const buildProject = async (options) => {
|
|
|
42
42
|
console.log('No plan items found.');
|
|
43
43
|
process.exit(1);
|
|
44
44
|
}
|
|
45
|
-
// Use inquirer to select a plan item
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
message: 'Select a plan item:',
|
|
51
|
-
choices: planItems.map(([key, item]) => ({ name: key, value: key })),
|
|
52
|
-
},
|
|
53
|
-
]);
|
|
45
|
+
// Use @inquirer/prompts to select a plan item
|
|
46
|
+
const selectedPlanItemKey = await (0, prompts_1.select)({
|
|
47
|
+
message: 'Select a plan item:',
|
|
48
|
+
choices: planItems.map(([key, item]) => ({ name: key, value: key })),
|
|
49
|
+
});
|
|
54
50
|
const selectedPlanItem = planItems.find(([key]) => key === selectedPlanItemKey);
|
|
55
51
|
if (!selectedPlanItem) {
|
|
56
52
|
console.log('Selected plan item not found.');
|
|
@@ -77,15 +73,11 @@ const buildProject = async (options) => {
|
|
|
77
73
|
console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
|
|
78
74
|
}
|
|
79
75
|
});
|
|
80
|
-
// Prompt the user to copy the project to the clipboard using inquirer
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
message: 'Do you want to copy this project to the clipboard?',
|
|
86
|
-
default: true,
|
|
87
|
-
},
|
|
88
|
-
]);
|
|
76
|
+
// Prompt the user to copy the project to the clipboard using @inquirer/prompts
|
|
77
|
+
const copyToClipboard = await (0, prompts_1.confirm)({
|
|
78
|
+
message: 'Do you want to copy this project to the clipboard?',
|
|
79
|
+
default: true,
|
|
80
|
+
});
|
|
89
81
|
if (copyToClipboard) {
|
|
90
82
|
copy_paste_1.default.copy(projectCode, () => {
|
|
91
83
|
console.log('Project copied to clipboard');
|
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.initProject = void 0;
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
|
-
const
|
|
8
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
9
9
|
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
10
10
|
const config_utils_1 = require("../../utils/config_utils");
|
|
11
11
|
const git_utils_1 = require("../../utils/git_utils");
|
|
@@ -52,13 +52,9 @@ const initProject = async (options) => {
|
|
|
52
52
|
console.log(`You are on the ${defaultBranchName} branch. Please checkout to a git branch and try again. For example: git branch -b my-new-project`);
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
name: 'projectTask',
|
|
59
|
-
message: 'Describe the task you want to accomplish with this project:',
|
|
60
|
-
},
|
|
61
|
-
]);
|
|
55
|
+
const projectTask = await (0, prompts_1.input)({
|
|
56
|
+
message: 'Describe the task you want to accomplish with this project:',
|
|
57
|
+
});
|
|
62
58
|
const prompt = await generatePrompt(projectName, projectTask);
|
|
63
59
|
const contextFiles = await gpt.generateText({ prompt });
|
|
64
60
|
// Initialize variables
|
package/dist/index.js
CHANGED
|
@@ -65,6 +65,7 @@ generate
|
|
|
65
65
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
66
66
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
67
67
|
.option('-p, --prompt <prompt>', 'Specify the prompt to use')
|
|
68
|
+
.option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
|
|
68
69
|
.action(shell_1.default);
|
|
69
70
|
generate
|
|
70
71
|
.command('code')
|
|
@@ -74,12 +75,14 @@ generate
|
|
|
74
75
|
.option('-i, --interactive', 'Auto-save generated code and then show a new prompt with existing context to revise the code further')
|
|
75
76
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
76
77
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5:sonnet-20240620)')
|
|
78
|
+
.option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
|
|
77
79
|
.option('-p, --prompt <prompt>', 'Specify the prompt to use')
|
|
78
80
|
.option('-f, --files <filenames...>', 'Specify filenames of files with context to use for generating code')
|
|
79
81
|
.action(code_1.default);
|
|
80
82
|
generate
|
|
81
83
|
.command('commit')
|
|
82
84
|
.description('Generate a commit message and commit the changes')
|
|
85
|
+
.option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
|
|
83
86
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
84
87
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
85
88
|
.action(commit_1.default);
|
|
@@ -92,9 +95,11 @@ generate
|
|
|
92
95
|
.command('unit-tests')
|
|
93
96
|
.alias('u')
|
|
94
97
|
.description('Generate unit tests (if you don\'t specify filenames, it will generate tests for commited changes that differ from the main/master branch)')
|
|
98
|
+
.option('--save', 'Automatically save all files after generating the unit tests')
|
|
95
99
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
96
100
|
.option('-f, --files <filenames...>', 'Specify filenames for the unit tests')
|
|
97
101
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
102
|
+
.option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
|
|
98
103
|
.action(unit_tests_1.default);
|
|
99
104
|
generate
|
|
100
105
|
.command('review')
|
|
@@ -102,6 +107,7 @@ generate
|
|
|
102
107
|
.description('Review the code for commited changes that differ from the main/master branch')
|
|
103
108
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
104
109
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
110
|
+
.option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
|
|
105
111
|
.action(review_1.default);
|
|
106
112
|
// Project commands
|
|
107
113
|
const project = program
|