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