cloving 0.1.20 → 0.1.22

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.
@@ -3,324 +3,20 @@ 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
- const prompts_1 = require("@inquirer/prompts");
8
- const cli_highlight_1 = __importDefault(require("cli-highlight"));
9
- const fs_1 = __importDefault(require("fs"));
10
- const path_1 = __importDefault(require("path"));
11
- const command_utils_1 = require("../../utils/command_utils");
12
6
  const config_utils_1 = require("../../utils/config_utils");
13
- const string_utils_1 = require("../../utils/string_utils");
7
+ const config_utils_2 = require("../../utils/config_utils");
14
8
  const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
15
- const generateCodePrompt = (prompt, files, contextFiles, previousCode) => {
16
- const specialFileContents = (0, command_utils_1.collectSpecialFileContents)();
17
- const specialFiles = Object.keys(specialFileContents).map((file) => `### Contents of ${file}\n\n${JSON.stringify(specialFileContents[file], null, 2)}\n\n`).join('\n');
18
- const contextFileContents = Object.keys(contextFiles).map((file) => `### Contents of ${file}\n\n${contextFiles[file]}\n\n`).join('\n');
19
- let promptText = `### Description of App
20
-
21
- ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
22
-
23
- ${specialFiles}
24
-
25
- ${contextFileContents}
26
-
27
- ### Directory structure
28
-
29
- ${files.join('\n')}
30
-
31
- `;
32
- if (previousCode) {
33
- promptText += `### Previously generated code
34
-
35
- ${previousCode}
36
-
37
- `;
38
- }
39
- promptText += `### Example of a well-structured response
40
-
41
- Here are the files that I would like to generate:
42
-
43
- 1. **src/commands/generate/shell.ts**
44
-
45
- \`\`\`typescript
46
- import inquirer from 'inquirer'
47
- import highlight from 'cli-highlight'
48
- import { execSync } from 'child_process'
49
- import ncp from 'copy-paste'
50
- import { extractMarkdown } from '../../utils/string_utils'
51
- import { getConfig } from '../../utils/config_utils'
52
- import ClovingGPT from '../../cloving_gpt'
53
- import type { ClovingGPTOptions } from '../../utils/types'
54
-
55
- const generateShellPrompt = (prompt: string | undefined): string => {
56
- const shell = execSync('echo $SHELL').toString().trim()
57
- const os = execSync('echo $OSTYPE').toString().trim()
58
- 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.
59
-
60
- Do not add any commentary or context to the message other than the commit message itself.
61
-
62
- An example of the output for this should look like the following:
63
- \`\`\`
64
-
65
- ### Request
66
-
67
- Generate code that does the following: ${prompt}
68
-
69
- Do not use any data from the example response structure, only use the structure. Please 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.`;
70
- return promptText;
71
- };
72
- const generateExplainCodePrompt = (prompt) => {
73
- return `${prompt}
74
-
75
- ## Task
76
-
77
- Please briefly explain how the code works in this.`;
78
- };
79
- const generateCode = async (gpt, prompt, allSrcFiles, contextFiles, previousCode) => {
80
- const codePrompt = generateCodePrompt(prompt, allSrcFiles, contextFiles, previousCode);
81
- return await gpt.generateText({ prompt: codePrompt });
82
- };
83
- const displayGeneratedCode = (rawCodeCommand) => {
84
- (0, string_utils_1.parseMarkdownInstructions)(rawCodeCommand).map(code => {
85
- if (code.trim().startsWith('```')) {
86
- const lines = code.split('\n');
87
- const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
88
- console.log(lines[0]);
89
- try {
90
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
91
- }
92
- catch (error) {
93
- // don't highlight if it fails
94
- console.log(lines.slice(1, -1).join('\n'));
95
- }
96
- console.log(lines.slice(-1)[0]);
97
- }
98
- else {
99
- console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
100
- }
101
- });
102
- };
103
- const saveGeneratedFiles = async (files, fileContents) => {
104
- for (const file of files) {
105
- if (fileContents[file]) {
106
- const filePath = path_1.default.resolve(file);
107
- await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
108
- await fs_1.default.promises.writeFile(filePath, fileContents[file]);
109
- console.log(`${file} has been saved.`);
110
- }
111
- else {
112
- console.log(`File content not found for ${file}.`);
113
- }
114
- }
115
- };
116
- const updateContextFiles = async (contextFiles, files, fileContents) => {
117
- for (const file of files) {
118
- if (fileContents[file]) {
119
- contextFiles[file] = fileContents[file];
120
- }
121
- }
122
- };
123
- const handleUserAction = async (gpt, rawCodeCommand, prompt, allSrcFiles, contextFiles, options) => {
124
- const [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
125
- if (options.save) {
126
- await saveGeneratedFiles(files, fileContents);
127
- await updateContextFiles(contextFiles, files, fileContents);
128
- return;
129
- }
130
- const action = await (0, prompts_1.select)({
131
- message: 'What would you like to do?',
132
- choices: [
133
- { name: 'Revise', value: 'revise' },
134
- { name: 'Explain', value: 'explain' },
135
- { name: 'Save a Source Code File', value: 'save' },
136
- { name: 'Save All Source Code Files', value: 'saveAll' },
137
- { name: 'Copy Source Code to Clipboard', value: 'copySource' },
138
- { name: 'Copy Entire Response to Clipboard', value: 'copyAll' },
139
- { name: 'Done', value: 'done' },
140
- ],
141
- });
142
- switch (action) {
143
- case 'revise':
144
- const newPrompt = await (0, prompts_1.input)({
145
- message: 'How would you like to modify the output:',
146
- });
147
- let includeMoreFiles = true;
148
- while (includeMoreFiles) {
149
- const contextFile = await (0, prompts_1.input)({
150
- message: 'Enter the relative path of a file or directory you would like to include as context (or press enter to continue):',
151
- });
152
- if (contextFile) {
153
- contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, contextFiles, options);
154
- }
155
- else {
156
- includeMoreFiles = false;
157
- }
158
- }
159
- const newRawCodeCommand = await generateCode(gpt, newPrompt, allSrcFiles, contextFiles, rawCodeCommand);
160
- displayGeneratedCode(newRawCodeCommand);
161
- await handleUserAction(gpt, newRawCodeCommand, newPrompt, allSrcFiles, contextFiles, options);
162
- break;
163
- case 'explain':
164
- const explainPrompt = generateExplainCodePrompt(rawCodeCommand);
165
- const explainCodeCommand = await gpt.generateText({ prompt: explainPrompt });
166
- console.log((0, cli_highlight_1.default)(explainCodeCommand, { language: 'markdown' }));
167
- break;
168
- case 'copySource':
169
- let copyAnother = true;
170
- while (copyAnother) {
171
- const fileToCopy = await (0, prompts_1.select)({
172
- message: 'Which file do you want to copy to the clipboard?',
173
- choices: files.map(file => ({ name: file, value: file })),
174
- });
175
- if (fileContents[fileToCopy]) {
176
- copy_paste_1.default.copy(fileContents[fileToCopy], () => {
177
- console.log(`${fileToCopy} copied to clipboard`);
178
- });
179
- }
180
- else {
181
- console.log('File content not found.');
182
- }
183
- const copyMore = await (0, prompts_1.confirm)({
184
- message: 'Do you want to copy another file?',
185
- default: true,
186
- });
187
- copyAnother = copyMore;
188
- }
189
- break;
190
- case 'copyAll':
191
- copy_paste_1.default.copy(rawCodeCommand, () => {
192
- console.log('Entire response copied to clipboard');
193
- });
194
- break;
195
- case 'save':
196
- let saveAnother = true;
197
- while (saveAnother) {
198
- const fileToSave = await (0, prompts_1.select)({
199
- message: 'Which file do you want to save?',
200
- choices: files.map(file => ({ name: file, value: file })),
201
- });
202
- if (fileContents[fileToSave]) {
203
- const filePath = path_1.default.resolve(fileToSave);
204
- await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
205
- await fs_1.default.promises.writeFile(filePath, fileContents[fileToSave]);
206
- console.log(`${fileToSave} has been saved.`);
207
- await updateContextFiles(contextFiles, [fileToSave], fileContents);
208
- }
209
- else {
210
- console.log('File content not found.');
211
- }
212
- const saveMore = await (0, prompts_1.confirm)({
213
- message: 'Do you want to save another file?',
214
- default: true,
215
- });
216
- saveAnother = saveMore;
217
- }
218
- break;
219
- case 'saveAll':
220
- await saveGeneratedFiles(files, fileContents);
221
- await updateContextFiles(contextFiles, files, fileContents);
222
- console.log('All files have been saved.');
223
- break;
224
- case 'done':
225
- break;
226
- }
227
- };
9
+ const CodeManager_1 = __importDefault(require("../../managers/CodeManager"));
228
10
  const code = async (options) => {
229
- let { prompt, files } = options;
230
- options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
11
+ options.silent = (0, config_utils_2.getConfig)(options).globalSilent || false;
231
12
  const gpt = new cloving_gpt_1.default(options);
232
13
  const allSrcFiles = await (0, config_utils_1.getAllFiles)(options, false);
233
- let contextFiles = {};
234
- try {
235
- if (files) {
236
- let expandedFiles = [];
237
- for (const file of files) {
238
- const filePath = path_1.default.resolve(file);
239
- if (await fs_1.default.promises.stat(filePath).then(stat => stat.isDirectory()).catch(() => false)) {
240
- const dirFiles = await (0, command_utils_1.getAllFilesInDirectory)(filePath);
241
- expandedFiles = expandedFiles.concat(dirFiles.map(f => path_1.default.relative(process.cwd(), f)));
242
- }
243
- else {
244
- expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
245
- }
246
- }
247
- files = expandedFiles;
248
- for (const file of files) {
249
- const filePath = path_1.default.resolve(file);
250
- if (await fs_1.default.promises.stat(filePath).then(stat => stat.isFile()).catch(() => false)) {
251
- const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
252
- contextFiles[file] = content;
253
- }
254
- }
255
- }
256
- else {
257
- let includeMoreFiles = true;
258
- while (includeMoreFiles) {
259
- const contextFile = await (0, prompts_1.input)({
260
- message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue):`,
261
- });
262
- if (contextFile) {
263
- contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, contextFiles, options);
264
- }
265
- else {
266
- includeMoreFiles = false;
267
- }
268
- }
269
- }
270
- if (!prompt) {
271
- const userPrompt = await (0, prompts_1.input)({
272
- message: 'What would you like the code to do:',
273
- });
274
- prompt = userPrompt;
275
- }
276
- let rawCodeCommand = await generateCode(gpt, prompt, allSrcFiles, contextFiles);
277
- displayGeneratedCode(rawCodeCommand);
278
- if (options.save) {
279
- const [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
280
- await saveGeneratedFiles(files, fileContents);
281
- await updateContextFiles(contextFiles, files, fileContents);
282
- }
283
- else {
284
- await handleUserAction(gpt, rawCodeCommand, prompt, allSrcFiles, contextFiles, options);
285
- }
286
- if (options.interactive) {
287
- let continueInteractive = true;
288
- while (continueInteractive) {
289
- const newPrompt = await (0, prompts_1.input)({
290
- message: 'Revise the code (or press enter to finish):',
291
- });
292
- if (newPrompt.trim() === '') {
293
- continueInteractive = false;
294
- }
295
- else {
296
- let includeMoreFiles = true;
297
- while (includeMoreFiles) {
298
- const contextFile = await (0, prompts_1.input)({
299
- message: 'Enter the relative path of a file or directory you would like to include as context (or press enter to continue):',
300
- });
301
- if (contextFile) {
302
- contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, contextFiles, options);
303
- }
304
- else {
305
- includeMoreFiles = false;
306
- }
307
- }
308
- rawCodeCommand = await generateCode(gpt, newPrompt, allSrcFiles, contextFiles, rawCodeCommand);
309
- displayGeneratedCode(rawCodeCommand);
310
- if (options.save || options.interactive) {
311
- const [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
312
- await saveGeneratedFiles(files, fileContents);
313
- await updateContextFiles(contextFiles, files, fileContents);
314
- }
315
- else {
316
- await handleUserAction(gpt, rawCodeCommand, newPrompt, allSrcFiles, contextFiles, options);
317
- }
318
- }
319
- }
320
- }
14
+ const codeManager = new CodeManager_1.default(gpt, options, allSrcFiles);
15
+ if (options.prompt) {
16
+ await codeManager.processCode(options.prompt);
321
17
  }
322
- catch (error) {
323
- console.error('Could not generate code', error);
18
+ else {
19
+ console.error('No prompt provided.');
324
20
  }
325
21
  };
326
22
  exports.default = code;
@@ -7,27 +7,9 @@ const fs_1 = __importDefault(require("fs"));
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const child_process_1 = require("child_process");
9
9
  const string_utils_1 = require("../../utils/string_utils");
10
+ const git_utils_1 = require("../../utils/git_utils");
10
11
  const config_utils_1 = require("../../utils/config_utils");
11
12
  const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
12
- const generateCommitMessagePrompt = (diff) => {
13
- return `Generate a concise and meaningful commit message based on a diff.
14
-
15
- Do not add any commentary or context to the message other than the commit message itself.
16
-
17
- An example of the output for this should look like the following:
18
-
19
- \`\`\`plaintext
20
- Update dependencies and package versions
21
-
22
- - Upgrade Ruby gems including aws-sdk, honeybadger, irb, and rubocop
23
- - Update Node.js packages including esbuild, tinymce, and trix
24
- - Bump TypeScript and ESLint related packages to latest versions
25
- \`\`\`
26
-
27
- Here is the diff to help you write the commit message:
28
-
29
- ${diff}`;
30
- };
31
13
  const commit = async (options) => {
32
14
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
33
15
  const gpt = new cloving_gpt_1.default(options);
@@ -40,7 +22,7 @@ const commit = async (options) => {
40
22
  return;
41
23
  }
42
24
  // Generate the prompt for commit message
43
- const prompt = generateCommitMessagePrompt(diff);
25
+ const prompt = (0, git_utils_1.generateCommitMessagePrompt)(diff);
44
26
  // Instantiate ClovingGPT and get the commit message
45
27
  const rawCommitMessage = await gpt.generateText({ prompt });
46
28
  // Clean the commit message using extractMarkdown
@@ -110,7 +110,17 @@ ${contextFileContents}
110
110
  ${gitDiff}
111
111
  ==== end diff =====`;
112
112
  }
113
- promptText += `
113
+ if (files) {
114
+ promptText += `
115
+
116
+ ### Request
117
+
118
+ I would like you to explain the code and document a description of it.
119
+ List any bugs in the new code as well as recommended fixes for those bugs with code examples.
120
+ Format the output of this code review in Markdown format.`;
121
+ }
122
+ else {
123
+ promptText += `
114
124
 
115
125
  ### Request
116
126
 
@@ -118,6 +128,7 @@ Do not use any data from the example response structure, only use the structure.
118
128
  I would like you to explain why these change are being made and document a description of these changes.
119
129
  Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
120
130
  Format the output of this code review in Markdown format.`;
131
+ }
121
132
  if (prompt) {
122
133
  promptText += `
123
134
 
@@ -0,0 +1,11 @@
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 ProxyManager_1 = __importDefault(require("../managers/ProxyManager"));
7
+ const proxy = async (options) => {
8
+ const proxyManager = new ProxyManager_1.default(options);
9
+ await proxyManager.initialize();
10
+ };
11
+ exports.default = proxy;
@@ -0,0 +1,43 @@
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 config_utils_1 = require("../utils/config_utils");
9
+ const command_utils_1 = require("../utils/command_utils");
10
+ const tokens = async (options) => {
11
+ let contextFiles = {};
12
+ let files = options.files || '.';
13
+ let prompt = '';
14
+ let expandedFiles = [];
15
+ for (const file of files) {
16
+ const filePath = path_1.default.resolve(file);
17
+ if (await fs_1.default.promises.stat(filePath).then(stat => stat.isDirectory()).catch(() => false)) {
18
+ const dirFiles = await (0, command_utils_1.getAllFilesInDirectory)(filePath);
19
+ expandedFiles = expandedFiles.concat(dirFiles.map(f => path_1.default.relative(process.cwd(), f)));
20
+ }
21
+ else {
22
+ expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
23
+ }
24
+ }
25
+ files = expandedFiles;
26
+ for (const file of files) {
27
+ const filePath = path_1.default.resolve(file);
28
+ if (await fs_1.default.promises.stat(filePath).then(stat => stat.isFile()).catch(() => false)) {
29
+ const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
30
+ contextFiles[file] = content;
31
+ }
32
+ }
33
+ const contextFileContents = Object.keys(contextFiles).map((file) => `### Contents of ${file}\n\n${contextFiles[file]}\n\n`).join('\n');
34
+ prompt += `
35
+ ### Description of App
36
+
37
+ ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
38
+
39
+ ${contextFileContents}`;
40
+ const tokens = Math.ceil(prompt.length / 4).toLocaleString();
41
+ console.log(`Estimated tokens: ${tokens}`);
42
+ };
43
+ exports.default = tokens;
package/dist/index.js CHANGED
@@ -9,7 +9,10 @@ const fs_1 = require("fs");
9
9
  const path_1 = require("path");
10
10
  const config_1 = __importDefault(require("./commands/config"));
11
11
  const init_1 = __importDefault(require("./commands/init"));
12
+ const proxy_1 = __importDefault(require("./commands/proxy"));
13
+ const tokens_1 = __importDefault(require("./commands/tokens"));
12
14
  const models_1 = __importDefault(require("./commands/models"));
15
+ const chat_1 = __importDefault(require("./commands/chat"));
13
16
  const shell_1 = __importDefault(require("./commands/generate/shell"));
14
17
  const code_1 = __importDefault(require("./commands/generate/code"));
15
18
  const commit_1 = __importDefault(require("./commands/generate/commit"));
@@ -41,11 +44,31 @@ program
41
44
  .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
42
45
  .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
43
46
  .action(init_1.default);
47
+ program
48
+ .command('chat')
49
+ .option('-f, --files <filenames...>', 'Specify filenames of files with context to use for generating code')
50
+ .option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
51
+ .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5:sonnet-20240620)')
52
+ .description('Start an interactive chat with cloving')
53
+ .action(chat_1.default);
54
+ program
55
+ .command('proxy')
56
+ .option('-f, --files <filenames...>', 'Specify filenames of files with context to use for generating code')
57
+ .option('-t, --temperature <temperature>', 'Temperature for the model (default 0.2)')
58
+ .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5:sonnet-20240620)')
59
+ .description('Start a proxy server to use with cloving')
60
+ .action(proxy_1.default);
44
61
  program
45
62
  .command('models')
46
63
  .alias('m')
47
64
  .description('List available models')
48
65
  .action(models_1.default);
66
+ program
67
+ .command('tokens')
68
+ .alias('t')
69
+ .option('-f, --files <filenames...>', 'Specify filenames of files with context to use for generating code')
70
+ .description('Estimate the number of tokens in the current working directory or specified files')
71
+ .action(tokens_1.default);
49
72
  program
50
73
  .command('commit')
51
74
  .alias('c')