cloving 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,29 +62,6 @@ Do not add any commentary or context to the message other than the commit messag
62
62
  An example of the output for this should look like the following:
63
63
  \`\`\`
64
64
 
65
- 2. **src/commands/generate/code.ts**
66
-
67
- \`\`\`typescript
68
- import ncp from 'copy-paste'
69
- import inquirer from 'inquirer'
70
- import highlight from 'cli-highlight'
71
- import { collectSpecialFileContents } from '../../utils/command_utils'
72
- import { getConfig, getClovingConfig, getAllFiles } from '../../utils/config_utils'
73
- import { parseMarkdownInstructions, extractMarkdown } from '../../utils/string_utils'
74
- import ClovingGPT from '../../cloving_gpt'
75
- import type { ClovingGPTOptions } from '../../utils/types'
76
- import fs from 'fs'
77
- import path from 'path'
78
-
79
- const generateCodePrompt = (prompt: string | undefined, files: string[], contextFiles: Record<string, string>, previousCode?: string): string => {
80
- const specialFileContents = collectSpecialFileContents()
81
- const specialFiles = Object.keys(specialFileContents).map((file) => \`### Contents of \${file}\\n\\n\${JSON.stringify(specialFileContents[file], null, 2)}\\n\\n\`).join('\\n')
82
- const contextFileContents = Object.keys(contextFiles).map((file) => \`### Contents of \${file}\\n\\n\${contextFiles[file]}\\n\\n\`).join('\\n')
83
-
84
- return 'Here is a description of my app:'
85
- }
86
- \`\`\`
87
-
88
65
  ### Request
89
66
 
90
67
  Generate code that does the following: ${prompt}
@@ -94,8 +71,10 @@ Do not use any data from the example response structure, only use the structure.
94
71
  };
95
72
  const generateExplainCodePrompt = (prompt) => {
96
73
  return `${prompt}
97
-
98
- Please briefly explain how this code works.`;
74
+
75
+ ## Task
76
+
77
+ Please briefly explain how the code works in this.`;
99
78
  };
100
79
  const generateCode = async (gpt, prompt, allSrcFiles, contextFiles, previousCode) => {
101
80
  const codePrompt = generateCodePrompt(prompt, allSrcFiles, contextFiles, previousCode);
@@ -107,7 +86,13 @@ const displayGeneratedCode = (rawCodeCommand) => {
107
86
  const lines = code.split('\n');
108
87
  const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
109
88
  console.log(lines[0]);
110
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
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
+ }
111
96
  console.log(lines.slice(-1)[0]);
112
97
  }
113
98
  else {
@@ -115,28 +100,8 @@ const displayGeneratedCode = (rawCodeCommand) => {
115
100
  }
116
101
  });
117
102
  };
118
- const extractFilesAndContent = (rawCodeCommand) => {
119
- const files = [];
120
- const fileContents = {};
121
- const matches = rawCodeCommand.match(/(\*{2})([^\*]+)(\*{2})/g);
122
- if (!matches)
123
- return [files, fileContents];
124
- for (const match of matches) {
125
- const fileName = match.replace(/\*{2}/g, '').trim();
126
- const regex = new RegExp(`\\*\\*${fileName}\\*\\*\\n\\n\\\`{3}([\\s\\S]+?)\\\`{3}`, 'g');
127
- const contentMatch = regex.exec(rawCodeCommand);
128
- if (contentMatch) {
129
- files.push(fileName);
130
- let content = contentMatch[1];
131
- // Remove the first word after the opening triple backticks
132
- content = content.split('\n').map((line, idx) => idx === 0 ? line.replace(/^\w+\s*/, '') : line).join('\n');
133
- fileContents[fileName] = content;
134
- }
135
- }
136
- return [files, fileContents];
137
- };
138
103
  const handleUserAction = async (gpt, rawCodeCommand, prompt, allSrcFiles, contextFiles) => {
139
- const [files, fileContents] = extractFilesAndContent(rawCodeCommand);
104
+ const [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
140
105
  const { action } = await inquirer_1.default.prompt([
141
106
  {
142
107
  type: 'list',
@@ -289,7 +254,7 @@ const code = async (options) => {
289
254
  type: 'confirm',
290
255
  name: 'includeMore',
291
256
  message: `Do you want to include any${firstFile ? '' : ' other'} files or directories as context for the prompt?`,
292
- default: false,
257
+ default: true,
293
258
  },
294
259
  ]);
295
260
  firstFile = false;
@@ -338,7 +303,24 @@ const code = async (options) => {
338
303
  }
339
304
  const rawCodeCommand = await generateCode(gpt, prompt, allSrcFiles, contextFiles);
340
305
  displayGeneratedCode(rawCodeCommand);
341
- await handleUserAction(gpt, rawCodeCommand, prompt, allSrcFiles, contextFiles);
306
+ if (options.save) {
307
+ const [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
308
+ files.forEach(file => {
309
+ if (fileContents[file]) {
310
+ const filePath = path_1.default.resolve(file);
311
+ fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
312
+ fs_1.default.writeFileSync(filePath, fileContents[file]);
313
+ console.log(`${file} has been saved.`);
314
+ }
315
+ else {
316
+ console.log(`File content not found for ${file}.`);
317
+ }
318
+ });
319
+ console.log('All files have been saved.');
320
+ }
321
+ else {
322
+ await handleUserAction(gpt, rawCodeCommand, prompt, allSrcFiles, contextFiles);
323
+ }
342
324
  }
343
325
  catch (error) {
344
326
  console.error('Could not generate code', error);
@@ -84,7 +84,13 @@ Format the output of this code review in Markdown format.`;
84
84
  const lines = code.split('\n');
85
85
  const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
86
86
  console.log(lines[0]);
87
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
87
+ try {
88
+ console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
89
+ }
90
+ catch (error) {
91
+ // don't highlight if it fails
92
+ console.log(lines.slice(1, -1).join('\n'));
93
+ }
88
94
  console.log(lines.slice(-1)[0]);
89
95
  }
90
96
  else {
@@ -31,7 +31,9 @@ ${prompt}`;
31
31
  };
32
32
  const generateExplainShellPrompt = (prompt) => {
33
33
  return `${prompt}
34
-
34
+
35
+ ## Task
36
+
35
37
  Please briefly explain how this shell script works.`;
36
38
  };
37
39
  const generateShell = async (gpt, prompt) => {
@@ -10,6 +10,7 @@ const inquirer_1 = __importDefault(require("inquirer"));
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");
13
+ const string_utils_1 = require("../../utils/string_utils");
13
14
  const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
14
15
  const extractChangedFiles = (gitDiff) => {
15
16
  const fileRegex = /diff --git a\/(.+?) b\/(.+?)\n/g;
@@ -60,28 +61,8 @@ Please enumerate all the files in the provided list${gitDiff ? ' and git diff' :
60
61
 
61
62
  Also, list any test files that might be relevant to these files.`;
62
63
  };
63
- const extractFilesAndContent = (analysis) => {
64
- const files = [];
65
- const fileContents = {};
66
- const matches = analysis.match(/(\*{2})([^\*]+)(\*{2})/g);
67
- if (!matches)
68
- return [files, fileContents];
69
- for (const match of matches) {
70
- const fileName = match.replace(/\*{2}/g, '').trim();
71
- const regex = new RegExp(`\\*\\*${fileName}\\*\\*\\n\\n\\\`{3}([\\s\\S]+?)\\\`{3}`, 'g');
72
- const contentMatch = regex.exec(analysis);
73
- if (contentMatch) {
74
- files.push(fileName);
75
- let content = contentMatch[1];
76
- // Remove the first word after the opening triple backticks
77
- content = content.split('\n').map((line, idx) => idx === 0 ? line.replace(/^\w+\s*/, '') : line).join('\n');
78
- fileContents[fileName] = content;
79
- }
80
- }
81
- return [files, fileContents];
82
- };
83
64
  const handleUserAction = async (analysis) => {
84
- const [files, fileContents] = extractFilesAndContent(analysis);
65
+ const [files, fileContents] = (0, string_utils_1.extractFilesAndContent)(analysis);
85
66
  const { action } = await inquirer_1.default.prompt([
86
67
  {
87
68
  type: 'list',
@@ -64,7 +64,13 @@ const buildProject = async (options) => {
64
64
  const lines = code.split('\n');
65
65
  const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
66
66
  console.log(lines[0]);
67
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
67
+ try {
68
+ console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
69
+ }
70
+ catch (error) {
71
+ // don't highlight if it fails
72
+ console.log(lines.slice(1, -1).join('\n'));
73
+ }
68
74
  console.log(lines.slice(-1)[0]);
69
75
  }
70
76
  else {
package/dist/index.js CHANGED
@@ -69,6 +69,7 @@ generate
69
69
  .command('code')
70
70
  .alias('c')
71
71
  .description('Generate code based on a prompt')
72
+ .option('--save', 'Automatically save all files to dist after generating the code')
72
73
  .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
73
74
  .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
74
75
  .option('-p, --prompt <prompt>', 'Specify the prompt to use')
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -68,5 +68,10 @@
68
68
  "lint-staged": "^15.2.7",
69
69
  "ts-jest": "^29.2.0",
70
70
  "typescript": "^5.5.3"
71
+ },
72
+ "resolutions": {
73
+ "eslint": "8.57.0",
74
+ "eslint-plugin-n": "16.6.2",
75
+ "eslint-plugin-import": "2.29.1"
71
76
  }
72
77
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
3
+ exports.extractFilesAndContent = exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
4
4
  // Function to estimate token count
5
5
  const estimateTokens = async (text) => {
6
6
  const charCount = text.length;
@@ -91,3 +91,25 @@ const parseMarkdownInstructions = (input) => {
91
91
  return result;
92
92
  };
93
93
  exports.parseMarkdownInstructions = parseMarkdownInstructions;
94
+ const extractFilesAndContent = (rawCodeCommand) => {
95
+ const files = [];
96
+ const fileContents = {};
97
+ const matches = rawCodeCommand.match(/(\*{2})([^\*]+)(\*{2})/g);
98
+ if (!matches)
99
+ return [files, fileContents];
100
+ for (const match of matches) {
101
+ const fileName = match.replace(/\*{2}/g, '').trim();
102
+ const escapedFileName = fileName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
103
+ const regex = new RegExp(`\\*\\*${escapedFileName}\\*\\*\\n\\n\\\`{3}([\\s\\S]+?)\\\`{3}`, 'g');
104
+ const contentMatch = regex.exec(rawCodeCommand);
105
+ if (contentMatch) {
106
+ files.push(fileName);
107
+ let content = contentMatch[1];
108
+ // Remove the first word after the opening triple backticks
109
+ content = content.split('\n').map((line, idx) => idx === 0 ? line.replace(/^\w+\s*/, '') : line).join('\n');
110
+ fileContents[fileName] = content;
111
+ }
112
+ }
113
+ return [files, fileContents];
114
+ };
115
+ exports.extractFilesAndContent = extractFilesAndContent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {