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.
- package/dist/commands/generate/code.js +31 -49
- package/dist/commands/generate/review.js +7 -1
- package/dist/commands/generate/shell.js +3 -1
- package/dist/commands/generate/unit_tests.js +2 -21
- package/dist/commands/project/build.js +7 -1
- package/dist/index.js +1 -0
- package/dist/package.json +6 -1
- package/dist/utils/string_utils.js +23 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
@@ -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
|
-
|
|
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.
|
|
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;
|