cloving 0.1.4 → 0.1.7
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 +117 -36
- package/dist/commands/generate/commit.js +44 -0
- package/dist/commands/generate/review.js +57 -0
- package/dist/commands/generate/unit_tests.js +125 -0
- package/dist/commands/init.js +2 -8
- package/dist/commands/project/build.js +88 -0
- package/dist/commands/project/complete.js +12 -0
- package/dist/commands/project/init.js +76 -0
- package/dist/commands/project/plan.js +108 -0
- package/dist/index.js +53 -19
- package/dist/utils/config_utils.js +83 -4
- package/dist/utils/git_utils.js +34 -22
- package/dist/utils/string_utils.js +36 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
Here's the updated README.md incorporating the latest changes:
|
|
2
|
+
|
|
1
3
|
# Cloving
|
|
2
4
|
|
|
3
|
-
Cloving is an innovative tool designed to seamlessly integrate AI into various aspects of the developer workflow. It
|
|
5
|
+
Cloving is an innovative CLI tool designed to seamlessly integrate AI into various aspects of the developer workflow. It enhances productivity and code quality by leveraging AI for tasks such as generating commit messages, conducting code reviews, creating unit tests, and even assisting with entire project lifecycles.
|
|
4
6
|
|
|
5
7
|
## Features
|
|
6
8
|
|
|
@@ -9,15 +11,43 @@ Cloving is an innovative tool designed to seamlessly integrate AI into various a
|
|
|
9
11
|
- **AI-assisted unit test generation**
|
|
10
12
|
- **Seamless integration with existing development workflows**
|
|
11
13
|
- **Customizable AI prompts for different tasks**
|
|
14
|
+
- **Project initialization, planning, and building with AI assistance**
|
|
15
|
+
- **Support for multiple AI models including OpenAI, Claude, and Ollama**
|
|
12
16
|
|
|
13
17
|
## Prerequisites
|
|
14
18
|
|
|
15
19
|
- Node.js (version 20 or higher)
|
|
16
20
|
- Git
|
|
17
|
-
- API access to AI chat models (e.g., OpenAI GPT-
|
|
21
|
+
- API access to AI chat models (e.g., OpenAI GPT-4, Claude, Ollama, etc.)
|
|
18
22
|
|
|
19
23
|
## Installation
|
|
20
24
|
|
|
25
|
+
You have three options to install and use Cloving:
|
|
26
|
+
|
|
27
|
+
### 1. Global Installation via npm
|
|
28
|
+
|
|
29
|
+
Install Cloving globally using npm:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install cloving -g
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This allows you to run `cloving` commands from anywhere in your terminal.
|
|
36
|
+
|
|
37
|
+
### 2. Run with npx
|
|
38
|
+
|
|
39
|
+
You can use npx to run Cloving without installing it globally:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx cloving [command]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This method downloads and executes Cloving on-the-fly, ensuring you always use the latest version.
|
|
46
|
+
|
|
47
|
+
### 3. Clone and Install from Source
|
|
48
|
+
|
|
49
|
+
For development or to use the latest unreleased features:
|
|
50
|
+
|
|
21
51
|
1. Clone this repository:
|
|
22
52
|
```bash
|
|
23
53
|
git clone https://github.com/cloving-ai/cloving-cli.git
|
|
@@ -34,76 +64,127 @@ Cloving is an innovative tool designed to seamlessly integrate AI into various a
|
|
|
34
64
|
yarn link
|
|
35
65
|
```
|
|
36
66
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
After installation, configure Cloving with your API key and preferred model:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cloving config
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Choose the installation method that best suits your needs and workflow. The global npm installation is recommended for most users, while npx is great for one-off usage or trying out Cloving. Cloning from source is ideal for contributors or those who want the absolute latest features.
|
|
41
74
|
|
|
42
75
|
## Usage
|
|
43
76
|
|
|
44
77
|
Cloving provides several commands to help integrate AI into your development workflow:
|
|
45
78
|
|
|
46
|
-
###
|
|
79
|
+
### Configuration
|
|
47
80
|
|
|
48
|
-
|
|
81
|
+
Configure Cloving with your API key and model:
|
|
49
82
|
```bash
|
|
50
|
-
cloving
|
|
83
|
+
cloving config
|
|
51
84
|
```
|
|
52
|
-
This will generate an AI-powered commit message based on your recent changes, allow you to review and edit it, and then commit your changes.
|
|
53
85
|
|
|
54
|
-
###
|
|
86
|
+
### Initialization
|
|
55
87
|
|
|
56
|
-
|
|
88
|
+
Set up Cloving in the current project:
|
|
57
89
|
```bash
|
|
58
|
-
cloving
|
|
90
|
+
cloving init
|
|
59
91
|
```
|
|
92
|
+
Options:
|
|
93
|
+
- `-s, --silent`: Run without asking for confirmation of submitting prompts
|
|
94
|
+
- `-m, --model <model>`: Select the model to use
|
|
60
95
|
|
|
61
|
-
###
|
|
96
|
+
### Listing Available Models
|
|
62
97
|
|
|
63
|
-
|
|
98
|
+
List the available AI models:
|
|
64
99
|
```bash
|
|
65
|
-
cloving
|
|
100
|
+
cloving models
|
|
66
101
|
```
|
|
67
102
|
|
|
68
|
-
###
|
|
103
|
+
### Generating Commit Messages
|
|
69
104
|
|
|
70
|
-
|
|
105
|
+
Generate an AI-powered commit message:
|
|
71
106
|
```bash
|
|
72
|
-
cloving
|
|
107
|
+
cloving commit
|
|
108
|
+
```
|
|
109
|
+
or
|
|
110
|
+
```bash
|
|
111
|
+
cloving generate commit
|
|
73
112
|
```
|
|
113
|
+
Options:
|
|
114
|
+
- `-s, --silent`: Run without asking for confirmation
|
|
115
|
+
- `-m, --model <model>`: Select the model to use
|
|
74
116
|
|
|
75
|
-
###
|
|
117
|
+
### Generating Unit Tests
|
|
76
118
|
|
|
77
|
-
|
|
119
|
+
Generate unit tests for changes or specific files:
|
|
78
120
|
```bash
|
|
79
|
-
cloving
|
|
121
|
+
cloving generate unit-tests
|
|
80
122
|
```
|
|
123
|
+
Options:
|
|
124
|
+
- `-s, --silent`: Run without asking for confirmation
|
|
125
|
+
- `-f, --files <filenames...>`: Specify filenames for the unit tests
|
|
126
|
+
- `-m, --model <model>`: Select the model to use
|
|
81
127
|
|
|
82
|
-
###
|
|
128
|
+
### Code Reviews
|
|
83
129
|
|
|
84
|
-
|
|
130
|
+
Get an AI-powered code review:
|
|
85
131
|
```bash
|
|
86
|
-
cloving
|
|
132
|
+
cloving generate review
|
|
87
133
|
```
|
|
134
|
+
Options:
|
|
135
|
+
- `-s, --silent`: Run without asking for confirmation
|
|
136
|
+
- `-m, --model <model>`: Select the model to use
|
|
137
|
+
|
|
138
|
+
### Project Commands
|
|
139
|
+
|
|
140
|
+
Cloving now includes commands for managing entire projects:
|
|
141
|
+
|
|
142
|
+
- Initialize a new project inside a git branch:
|
|
143
|
+
```bash
|
|
144
|
+
cloving project init
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- Plan the steps to complete the project:
|
|
148
|
+
```bash
|
|
149
|
+
cloving project plan
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- Generate code to build the project:
|
|
153
|
+
```bash
|
|
154
|
+
cloving project build
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- Clean up and finalize the project:
|
|
158
|
+
```bash
|
|
159
|
+
cloving project complete
|
|
160
|
+
```
|
|
88
161
|
|
|
89
162
|
## Command Line Options
|
|
90
163
|
|
|
91
164
|
Usage: `cloving [options] [command]`
|
|
92
165
|
|
|
93
|
-
Integrate AI into your development workflow for generating commit messages, code reviews, and unit tests.
|
|
94
|
-
|
|
95
166
|
Options:
|
|
96
|
-
- `-V, --version
|
|
97
|
-
- `-h, --help
|
|
167
|
+
- `-V, --version`: Output the version number
|
|
168
|
+
- `-h, --help`: Display help for command
|
|
98
169
|
|
|
99
170
|
Commands:
|
|
100
|
-
- `
|
|
101
|
-
- `
|
|
102
|
-
- `models
|
|
103
|
-
- `
|
|
104
|
-
- `
|
|
105
|
-
- `
|
|
106
|
-
- `
|
|
171
|
+
- `config`: Configure Cloving with your API key and models to use
|
|
172
|
+
- `init`: Setup Cloving in the current directory
|
|
173
|
+
- `models`: List available models
|
|
174
|
+
- `commit`: Alias for cloving generate commit
|
|
175
|
+
- `generate`: Generate various items like unit-tests and code reviews
|
|
176
|
+
- `commit`: Generate a commit message and commit the changes
|
|
177
|
+
- `unit-tests`: Generate unit tests
|
|
178
|
+
- `review`: Review the code for committed changes
|
|
179
|
+
- `project`: Commands for managing Cloving projects
|
|
180
|
+
- `init`: Setup a new Cloving project inside a git branch
|
|
181
|
+
- `plan`: Plan the steps to complete the project
|
|
182
|
+
- `build`: Generate code to build the project
|
|
183
|
+
- `complete`: Clean up and finalize the project
|
|
184
|
+
|
|
185
|
+
Most commands support the following options:
|
|
186
|
+
- `-s, --silent`: Run the command without asking for confirmation of submitting prompts
|
|
187
|
+
- `-m, --model <model>`: Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)
|
|
107
188
|
|
|
108
189
|
## Contributing
|
|
109
190
|
|
|
@@ -0,0 +1,44 @@
|
|
|
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 child_process_1 = require("child_process");
|
|
9
|
+
const git_utils_1 = require("../../utils/git_utils");
|
|
10
|
+
const string_utils_1 = require("../../utils/string_utils");
|
|
11
|
+
const config_utils_1 = require("../../utils/config_utils");
|
|
12
|
+
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
13
|
+
const generateAndCommitMessage = async (options) => {
|
|
14
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
15
|
+
const gpt = new cloving_gpt_1.default(options);
|
|
16
|
+
try {
|
|
17
|
+
// Generate the prompt for commit message
|
|
18
|
+
const prompt = (0, git_utils_1.generateCommitMessagePrompt)();
|
|
19
|
+
// Instantiate ClovingGPT and get the commit message
|
|
20
|
+
const rawCommitMessage = await gpt.generateText({ prompt });
|
|
21
|
+
// Clean the commit message using extractMarkdown
|
|
22
|
+
const commitMessage = (0, string_utils_1.extractMarkdown)(rawCommitMessage);
|
|
23
|
+
// Write the commit message to a temporary file
|
|
24
|
+
const tempCommitFilePath = path_1.default.join('.git', 'SUGGESTED_COMMIT_EDITMSG');
|
|
25
|
+
fs_1.default.writeFileSync(tempCommitFilePath, commitMessage);
|
|
26
|
+
// Commit the changes using the generated commit message
|
|
27
|
+
try {
|
|
28
|
+
(0, child_process_1.execFileSync)('git', ['commit', '-a', '--edit', '--file', tempCommitFilePath], { stdio: 'inherit' });
|
|
29
|
+
}
|
|
30
|
+
catch (commitError) {
|
|
31
|
+
// If commit is canceled (non-zero exit), handle it here
|
|
32
|
+
console.log('Commit was canceled or failed.');
|
|
33
|
+
}
|
|
34
|
+
// Remove the temporary file using fs
|
|
35
|
+
fs_1.default.unlink(tempCommitFilePath, (err) => {
|
|
36
|
+
if (err)
|
|
37
|
+
throw err;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('Could not generate commit message');
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.default = generateAndCommitMessage;
|
|
@@ -0,0 +1,57 @@
|
|
|
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 child_process_1 = require("child_process");
|
|
7
|
+
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
8
|
+
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
9
|
+
const git_utils_1 = require("../../utils/git_utils");
|
|
10
|
+
const string_utils_1 = require("../../utils/string_utils");
|
|
11
|
+
const config_utils_1 = require("../../utils/config_utils");
|
|
12
|
+
const string_utils_2 = require("../../utils/string_utils");
|
|
13
|
+
const command_utils_1 = require("../../utils/command_utils");
|
|
14
|
+
const review = async (options) => {
|
|
15
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
16
|
+
const gpt = new cloving_gpt_1.default(options);
|
|
17
|
+
try {
|
|
18
|
+
// Define the prompt for analysis
|
|
19
|
+
const gitDiff = await (0, git_utils_1.getGitDiff)();
|
|
20
|
+
const prompt = `Explain why the change is being made and document a description of these changes.
|
|
21
|
+
Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
|
|
22
|
+
Format the output of this code review in Markdown format.
|
|
23
|
+
|
|
24
|
+
${gitDiff}`;
|
|
25
|
+
// get the analysis
|
|
26
|
+
const analysis = await gpt.generateText({ prompt });
|
|
27
|
+
const markdown = (0, string_utils_1.extractMarkdown)(analysis);
|
|
28
|
+
// Print the analysis to the console
|
|
29
|
+
(0, string_utils_2.parseMarkdownInstructions)(markdown).map(code => {
|
|
30
|
+
if (code.trim().startsWith('```')) {
|
|
31
|
+
const lines = code.split('\n');
|
|
32
|
+
const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
|
|
33
|
+
console.log(lines[0]);
|
|
34
|
+
console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
|
|
35
|
+
console.log(lines.slice(-1)[0]);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
// Prompt the user to copy the analysis to the clipboard
|
|
42
|
+
const answer = await (0, command_utils_1.promptUser)('Do you want to copy the analysis to the clipboard? [Y/n] ');
|
|
43
|
+
if (answer.toLowerCase() === 'y' || answer === '') {
|
|
44
|
+
try {
|
|
45
|
+
(0, child_process_1.execFileSync)('pbcopy', { input: markdown });
|
|
46
|
+
console.log('Analysis copied to clipboard');
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Error: pbcopy command not found. Unable to copy to clipboard.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('Error during analysis:', error.message);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
exports.default = review;
|
|
@@ -0,0 +1,125 @@
|
|
|
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 = require("fs");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
9
|
+
const git_utils_1 = require("../../utils/git_utils");
|
|
10
|
+
const config_utils_1 = require("../../utils/config_utils");
|
|
11
|
+
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
12
|
+
const extractChangedFiles = (gitDiff) => {
|
|
13
|
+
const fileRegex = /diff --git a\/(.+?) b\/(.+?)\n/g;
|
|
14
|
+
const files = new Set();
|
|
15
|
+
let match;
|
|
16
|
+
while ((match = fileRegex.exec(gitDiff)) !== null) {
|
|
17
|
+
files.add(match[1]);
|
|
18
|
+
}
|
|
19
|
+
return Array.from(files);
|
|
20
|
+
};
|
|
21
|
+
const generatePrompt = (files, srcFiles, testFiles, gitDiff) => {
|
|
22
|
+
const filesList = files.length > 0 ? files.join('\n') : 'No files provided';
|
|
23
|
+
const diffSection = gitDiff ? `Here is my git diff:\n\n==== begin git diff ====\n${gitDiff}\n==== end git diff ====\n` : '';
|
|
24
|
+
return `Here is a list of code files I want to generate unit tests for:
|
|
25
|
+
|
|
26
|
+
==== begin list of files ====
|
|
27
|
+
${filesList}
|
|
28
|
+
==== end list of files ====
|
|
29
|
+
|
|
30
|
+
Here is a list of all my source files:
|
|
31
|
+
|
|
32
|
+
==== begin list of source files ====
|
|
33
|
+
${srcFiles}
|
|
34
|
+
==== end list of source files ====
|
|
35
|
+
|
|
36
|
+
Here is a list of all my test files:
|
|
37
|
+
|
|
38
|
+
==== begin list of test files ====
|
|
39
|
+
${testFiles}
|
|
40
|
+
==== end list of test files ====
|
|
41
|
+
|
|
42
|
+
${diffSection}
|
|
43
|
+
|
|
44
|
+
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.
|
|
45
|
+
|
|
46
|
+
Also, list any test files that might be relevant to these files.
|
|
47
|
+
|
|
48
|
+
Give me this output format for your answer:
|
|
49
|
+
|
|
50
|
+
## files
|
|
51
|
+
|
|
52
|
+
app/models/foo.rb
|
|
53
|
+
app/views/baz/bar.html.erb
|
|
54
|
+
|
|
55
|
+
## relevant test files
|
|
56
|
+
|
|
57
|
+
test/models/foo_test.rb
|
|
58
|
+
test/controllers/baz_controller_test.rb`;
|
|
59
|
+
};
|
|
60
|
+
const unitTests = async (options) => {
|
|
61
|
+
const { files } = options;
|
|
62
|
+
const gpt = new cloving_gpt_1.default(options);
|
|
63
|
+
const allSrcFiles = await (0, config_utils_1.getAllFiles)(options, true);
|
|
64
|
+
const testingDirectory = (0, config_utils_1.getTestingDirectory)();
|
|
65
|
+
if (allSrcFiles.length === 0 || !testingDirectory) {
|
|
66
|
+
console.error('Could not find any source files to generate unit tests. Please run: cloving init. Then try again.');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const testFiles = (0, child_process_1.execFileSync)('find', [testingDirectory, '-type', 'f']).toString().trim();
|
|
70
|
+
let contextFiles;
|
|
71
|
+
if (files) {
|
|
72
|
+
const prompt = generatePrompt(files, allSrcFiles.join('\n'), testFiles);
|
|
73
|
+
contextFiles = await gpt.generateText({ prompt });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const gitDiff = await (0, git_utils_1.getGitDiff)();
|
|
77
|
+
const changedFiles = extractChangedFiles(gitDiff);
|
|
78
|
+
const prompt = generatePrompt(changedFiles, allSrcFiles.join('\n'), testFiles, gitDiff);
|
|
79
|
+
contextFiles = await gpt.generateText({ prompt });
|
|
80
|
+
}
|
|
81
|
+
// Initialize variables
|
|
82
|
+
const lines = [];
|
|
83
|
+
const context = [];
|
|
84
|
+
// Read each line of the context files
|
|
85
|
+
contextFiles.split('\n').forEach((line) => {
|
|
86
|
+
if (line.trim()) {
|
|
87
|
+
lines.push(line.trim());
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
for (const codeFile of lines) {
|
|
91
|
+
if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
|
|
92
|
+
const fileContents = await fs_1.promises.readFile(codeFile, 'utf-8');
|
|
93
|
+
context.push(`### **Contents of ${codeFile}**\n\n${fileContents}\n\n### **End of ${codeFile}**`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Generate the message for unit test creation
|
|
97
|
+
let analysis = '';
|
|
98
|
+
if ((files || []).length > 0) {
|
|
99
|
+
const message = `please create unit tests for these files:
|
|
100
|
+
|
|
101
|
+
## begin list of files
|
|
102
|
+
${(files || []).join('\n')}
|
|
103
|
+
## end list of files
|
|
104
|
+
|
|
105
|
+
## context
|
|
106
|
+
${context.join('\n\n')}`;
|
|
107
|
+
// Get the model and analysis using ClovingGPT
|
|
108
|
+
analysis = await gpt.generateText({ prompt: message });
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const message = `please create unit tests for these changes:
|
|
112
|
+
|
|
113
|
+
## begin diff
|
|
114
|
+
${await (0, git_utils_1.getGitDiff)()}
|
|
115
|
+
## end diff
|
|
116
|
+
|
|
117
|
+
## context
|
|
118
|
+
|
|
119
|
+
${context.join('\n\n')}`;
|
|
120
|
+
// Get the model and analysis using ClovingGPT
|
|
121
|
+
analysis = await gpt.generateText({ prompt: message });
|
|
122
|
+
}
|
|
123
|
+
console.log((0, cli_highlight_1.default)(analysis));
|
|
124
|
+
};
|
|
125
|
+
exports.default = unitTests;
|
package/dist/commands/init.js
CHANGED
|
@@ -7,10 +7,9 @@ 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 string_utils_1 = require("../utils/string_utils");
|
|
11
10
|
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
12
11
|
const ignore_1 = __importDefault(require("ignore"));
|
|
13
|
-
const
|
|
12
|
+
const string_utils_1 = require("../utils/string_utils");
|
|
14
13
|
const config_utils_1 = require("../utils/config_utils");
|
|
15
14
|
const command_utils_1 = require("../utils/command_utils");
|
|
16
15
|
// Main function for the describe command
|
|
@@ -202,13 +201,8 @@ Here is an example response:
|
|
|
202
201
|
],
|
|
203
202
|
"projectType": "Command-line tool",
|
|
204
203
|
}`;
|
|
205
|
-
if (process.env.DEBUG === '1') {
|
|
206
|
-
(0, string_utils_1.estimateTokens)(prompt).then(tokenCount => {
|
|
207
|
-
console.log(`Estimated token count: ${tokenCount}`);
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
204
|
const aiChatResponse = await gpt.generateText({ prompt });
|
|
211
|
-
const cleanAiChatResponse = (0,
|
|
205
|
+
const cleanAiChatResponse = (0, string_utils_1.extractJsonMetadata)(aiChatResponse);
|
|
212
206
|
fs_1.default.writeFileSync(tempFilePath, cleanAiChatResponse);
|
|
213
207
|
// Save the AI chat response to cloving.json
|
|
214
208
|
fs_1.default.writeFileSync('cloving.json', cleanAiChatResponse);
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
exports.buildProject = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const cli_highlight_1 = __importDefault(require("cli-highlight"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
12
|
+
const config_utils_1 = require("../../utils/config_utils");
|
|
13
|
+
const git_utils_1 = require("../../utils/git_utils");
|
|
14
|
+
const string_utils_1 = require("../../utils/string_utils");
|
|
15
|
+
const command_utils_1 = require("../../utils/command_utils");
|
|
16
|
+
const generatePrompt = async (projectName, projectTask, filesList, incompletePlanItem) => {
|
|
17
|
+
const context = [];
|
|
18
|
+
for (const codeFile of filesList) {
|
|
19
|
+
if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
|
|
20
|
+
const fileContents = await fs_1.promises.readFile(codeFile, 'utf-8');
|
|
21
|
+
context.push(`### **Contents of ${codeFile}**\n\n${fileContents}\n\n### **End of ${codeFile}**`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return `==== begin task description ====
|
|
25
|
+
Task name: ${projectName}
|
|
26
|
+
Task description: ${projectTask}
|
|
27
|
+
==== end task description ====
|
|
28
|
+
|
|
29
|
+
${context}
|
|
30
|
+
|
|
31
|
+
Please implement the following plan item, please include full file path names in all code snippets:
|
|
32
|
+
|
|
33
|
+
${incompletePlanItem}`;
|
|
34
|
+
};
|
|
35
|
+
const buildProject = async (options) => {
|
|
36
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
37
|
+
const gpt = new cloving_gpt_1.default(options);
|
|
38
|
+
const projectName = (0, git_utils_1.getCurrentBranchName)();
|
|
39
|
+
const projectConfig = (0, config_utils_1.getProjectConfig)(projectName);
|
|
40
|
+
// Filter out completed plan items for selection
|
|
41
|
+
const planItems = Object.entries(projectConfig.plan || {});
|
|
42
|
+
if (planItems.length === 0) {
|
|
43
|
+
console.log('No plan items found.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
// Use inquirer to select a plan item
|
|
47
|
+
const { selectedPlanItemKey } = await inquirer_1.default.prompt([
|
|
48
|
+
{
|
|
49
|
+
type: 'list',
|
|
50
|
+
name: 'selectedPlanItemKey',
|
|
51
|
+
message: 'Select a plan item:',
|
|
52
|
+
choices: planItems.map(([key, item]) => ({ name: key, value: key })),
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
const selectedPlanItem = planItems.find(([key]) => key === selectedPlanItemKey);
|
|
56
|
+
if (!selectedPlanItem) {
|
|
57
|
+
console.log('Selected plan item not found.');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const planText = `## **${selectedPlanItem[0]}**\n\n${selectedPlanItem[1].details.join('\n\n')}`;
|
|
61
|
+
const prompt = await generatePrompt(projectName, projectConfig.task, projectConfig.files || [], planText);
|
|
62
|
+
const projectCode = await gpt.generateText({ prompt });
|
|
63
|
+
(0, string_utils_1.parseMarkdownInstructions)(projectCode).map(code => {
|
|
64
|
+
if (code.trim().startsWith('```')) {
|
|
65
|
+
const lines = code.split('\n');
|
|
66
|
+
const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
|
|
67
|
+
console.log(lines[0]);
|
|
68
|
+
console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
|
|
69
|
+
console.log(lines.slice(-1)[0]);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// Prompt the user to copy the project to the clipboard
|
|
76
|
+
const answer = await (0, command_utils_1.promptUser)('Do you want to copy this project to the clipboard? [Y/n] ');
|
|
77
|
+
if (answer.toLowerCase() === 'y' || answer === '') {
|
|
78
|
+
try {
|
|
79
|
+
(0, child_process_1.execFileSync)('pbcopy', { input: projectCode });
|
|
80
|
+
console.log('Project copied to clipboard');
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error('Error: pbcopy command not found. Unable to copy to clipboard.');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
exports.buildProject = buildProject;
|
|
88
|
+
exports.default = exports.buildProject;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.completeProject = void 0;
|
|
4
|
+
const config_utils_1 = require("../../utils/config_utils");
|
|
5
|
+
const git_utils_1 = require("../../utils/git_utils");
|
|
6
|
+
const completeProject = async (options) => {
|
|
7
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
8
|
+
const projectName = (0, git_utils_1.getCurrentBranchName)();
|
|
9
|
+
(0, config_utils_1.removeProjectConfig)(projectName);
|
|
10
|
+
};
|
|
11
|
+
exports.completeProject = completeProject;
|
|
12
|
+
exports.default = exports.completeProject;
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
exports.initProject = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
9
|
+
const config_utils_1 = require("../../utils/config_utils");
|
|
10
|
+
const command_utils_1 = require("../../utils/command_utils");
|
|
11
|
+
const git_utils_1 = require("../../utils/git_utils");
|
|
12
|
+
const config_utils_2 = require("../../utils/config_utils");
|
|
13
|
+
const generatePrompt = async (projectName, projectTask) => {
|
|
14
|
+
const filesList = await (0, config_utils_2.getAllFiles)({}, false);
|
|
15
|
+
return `Here is a list of all my source files:
|
|
16
|
+
|
|
17
|
+
==== begin list of source files ====
|
|
18
|
+
${filesList.join("\n")}
|
|
19
|
+
==== end list of source files ====
|
|
20
|
+
|
|
21
|
+
Please enumerate all the files in the provided list that are pertinent for this task:
|
|
22
|
+
|
|
23
|
+
==== begin task description ====
|
|
24
|
+
Task name: ${projectName}
|
|
25
|
+
Task description: ${projectTask}
|
|
26
|
+
==== end task description ====
|
|
27
|
+
|
|
28
|
+
Also, list any test files that might be relevant to these files.
|
|
29
|
+
|
|
30
|
+
Give me this output format for your answer:
|
|
31
|
+
|
|
32
|
+
## files
|
|
33
|
+
|
|
34
|
+
app/models/foo.rb
|
|
35
|
+
app/views/baz/bar.html.erb
|
|
36
|
+
|
|
37
|
+
## relevant test files
|
|
38
|
+
|
|
39
|
+
test/models/foo_test.rb
|
|
40
|
+
test/controllers/baz_controller_test.rb`;
|
|
41
|
+
};
|
|
42
|
+
const initProject = async (options) => {
|
|
43
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
44
|
+
const gpt = new cloving_gpt_1.default(options);
|
|
45
|
+
const defaultBranchName = await (0, git_utils_1.getDefaultBranchName)();
|
|
46
|
+
const projectName = (0, git_utils_1.getCurrentBranchName)();
|
|
47
|
+
if (projectName === defaultBranchName) {
|
|
48
|
+
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`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const projectTask = await (0, command_utils_1.promptUser)('Describe the task you want to accomplish with this project: ');
|
|
52
|
+
const prompt = await generatePrompt(projectName, projectTask);
|
|
53
|
+
const contextFiles = await gpt.generateText({ prompt });
|
|
54
|
+
// Initialize variables
|
|
55
|
+
const lines = [];
|
|
56
|
+
const files = [];
|
|
57
|
+
// Read each line of the context files
|
|
58
|
+
contextFiles.split('\n').forEach((line) => {
|
|
59
|
+
if (line.trim()) {
|
|
60
|
+
lines.push(line.trim());
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
for (const codeFile of lines) {
|
|
64
|
+
if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
|
|
65
|
+
files.push(codeFile);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const projectConfig = {
|
|
69
|
+
name: projectName,
|
|
70
|
+
task: projectTask,
|
|
71
|
+
files
|
|
72
|
+
};
|
|
73
|
+
(0, config_utils_1.saveProjectConfig)(projectName, projectConfig);
|
|
74
|
+
};
|
|
75
|
+
exports.initProject = initProject;
|
|
76
|
+
exports.default = exports.initProject;
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
exports.planProject = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
|
|
9
|
+
const config_utils_1 = require("../../utils/config_utils");
|
|
10
|
+
const git_utils_1 = require("../../utils/git_utils");
|
|
11
|
+
const parsePlan = (plan) => {
|
|
12
|
+
const lines = plan.split('\n').map(line => line.trim());
|
|
13
|
+
const structured = {};
|
|
14
|
+
let currentTitle = null;
|
|
15
|
+
let currentDetails = [];
|
|
16
|
+
lines.forEach(line => {
|
|
17
|
+
const sectionMatch = line.match(/^#### \d+\. \*\*(.+)\*\*$/);
|
|
18
|
+
const detailMatch = line.match(/^-\s*\*\*(.+)\*\*:?\s*(.+)$/);
|
|
19
|
+
if (sectionMatch) {
|
|
20
|
+
if (currentTitle) {
|
|
21
|
+
structured[currentTitle] = {
|
|
22
|
+
details: currentDetails
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
currentTitle = sectionMatch[1];
|
|
26
|
+
currentDetails = [];
|
|
27
|
+
}
|
|
28
|
+
else if (detailMatch && currentDetails) {
|
|
29
|
+
currentDetails.push(`${detailMatch[1]}: ${detailMatch[2]}`);
|
|
30
|
+
}
|
|
31
|
+
else if (currentDetails && line.startsWith('- ')) {
|
|
32
|
+
currentDetails.push(line.replace(/^- /, ''));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (currentTitle) {
|
|
36
|
+
structured[currentTitle] = {
|
|
37
|
+
details: currentDetails
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return structured;
|
|
41
|
+
};
|
|
42
|
+
const generatePrompt = async (projectName, projectTask, filesList) => {
|
|
43
|
+
const context = [];
|
|
44
|
+
for (const codeFile of filesList) {
|
|
45
|
+
if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
|
|
46
|
+
const fileContents = await fs_1.promises.readFile(codeFile, 'utf-8');
|
|
47
|
+
context.push(`### **Contents of ${codeFile}**\n\n${fileContents}\n\n### **End of ${codeFile}**`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return `==== begin task description ====
|
|
51
|
+
Task name: ${projectName}
|
|
52
|
+
Task description: ${projectTask}
|
|
53
|
+
==== end task description ====
|
|
54
|
+
|
|
55
|
+
${context}
|
|
56
|
+
|
|
57
|
+
Please create a plan to execute this project. Do not include the Implementation Steps. Only include Plan for Project Execution.
|
|
58
|
+
|
|
59
|
+
Here is an example plan. Do not use any of the information from this plan in your response, only use it as a template to create your own plan.
|
|
60
|
+
|
|
61
|
+
==== begin example plan ====
|
|
62
|
+
### Plan for Project Execution
|
|
63
|
+
|
|
64
|
+
#### 1. **Understanding Existing Code**
|
|
65
|
+
- **Review existing command implementations**: Familiarize yourself with the structure and implementation style of existing commands such as project init and project plan.
|
|
66
|
+
- **Identify utility functions**: Note the utility functions being used across the commands, such as those in config_utils, git_utils, and command_utils.
|
|
67
|
+
|
|
68
|
+
#### 2. **Design New Commands**
|
|
69
|
+
- **Define functionalities**: Clearly outline what each new command should accomplish:
|
|
70
|
+
- project build: This command should compile or prepare the project based on the planned actions.
|
|
71
|
+
- project complete: This command should finalize the project, possibly including cleanup, documentation, or deployment steps.
|
|
72
|
+
|
|
73
|
+
#### 3. **Implement project build**
|
|
74
|
+
- **Create build.ts**: Implement the logic for building the project.
|
|
75
|
+
- **Generate prompt**: Create a prompt that describes the build process based on the project configuration.
|
|
76
|
+
- **Invoke AI Model**: Use an AI model to generate the steps or scripts needed to build the project.
|
|
77
|
+
- **Execute Build Steps**: Implement logic to execute the build steps.
|
|
78
|
+
|
|
79
|
+
#### 4. **Implement project complete**
|
|
80
|
+
- **Create complete.ts**: Implement the logic for completing the project.
|
|
81
|
+
- **Generate prompt**: Create a prompt that describes the completion process based on the project configuration.
|
|
82
|
+
- **Invoke AI Model**: Use an AI model to generate the steps or scripts needed to complete the project.
|
|
83
|
+
- **Execute Completion Steps**: Implement logic to execute the completion steps.
|
|
84
|
+
|
|
85
|
+
#### 5. **Integration and Testing**
|
|
86
|
+
- **Integrate new commands**: Ensure the new commands are integrated into the existing command structure.
|
|
87
|
+
- **Unit tests**: Develop unit tests for the new commands to ensure they function correctly.
|
|
88
|
+
- **End-to-end tests**: Run end-to-end tests to validate the entire workflow from project initialization to completion.
|
|
89
|
+
|
|
90
|
+
#### 6. **Documentation and Cleanup**
|
|
91
|
+
- **Update README**: Document the new commands and their usage.
|
|
92
|
+
- **Code cleanup**: Refactor and clean up any redundant code or comments.
|
|
93
|
+
==== end example plan ====`;
|
|
94
|
+
};
|
|
95
|
+
const planProject = async (options) => {
|
|
96
|
+
options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
|
|
97
|
+
const gpt = new cloving_gpt_1.default(options);
|
|
98
|
+
const projectName = (0, git_utils_1.getCurrentBranchName)();
|
|
99
|
+
const projectConfig = (0, config_utils_1.getProjectConfig)(projectName);
|
|
100
|
+
const prompt = await generatePrompt(projectName, projectConfig.task, projectConfig.files || []);
|
|
101
|
+
const projectPlan = await gpt.generateText({ prompt });
|
|
102
|
+
const planItems = parsePlan(projectPlan);
|
|
103
|
+
console.log(planItems);
|
|
104
|
+
projectConfig.plan = planItems;
|
|
105
|
+
(0, config_utils_1.saveProjectConfig)(projectName, projectConfig);
|
|
106
|
+
};
|
|
107
|
+
exports.planProject = planProject;
|
|
108
|
+
exports.default = exports.planProject;
|
package/dist/index.js
CHANGED
|
@@ -5,48 +5,82 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
|
-
const commit_1 = __importDefault(require("./commands/commit"));
|
|
9
|
-
const unit_tests_1 = __importDefault(require("./commands/unit_tests"));
|
|
10
|
-
const review_1 = __importDefault(require("./commands/review"));
|
|
11
|
-
const init_1 = __importDefault(require("./commands/init"));
|
|
12
8
|
const config_1 = __importDefault(require("./commands/config"));
|
|
9
|
+
const init_1 = __importDefault(require("./commands/init"));
|
|
13
10
|
const models_1 = __importDefault(require("./commands/models"));
|
|
11
|
+
const commit_1 = __importDefault(require("./commands/generate/commit"));
|
|
12
|
+
const unit_tests_1 = __importDefault(require("./commands/generate/unit_tests"));
|
|
13
|
+
const review_1 = __importDefault(require("./commands/generate/review"));
|
|
14
|
+
const init_2 = __importDefault(require("./commands/project/init"));
|
|
15
|
+
const plan_1 = __importDefault(require("./commands/project/plan"));
|
|
16
|
+
const build_1 = __importDefault(require("./commands/project/build"));
|
|
17
|
+
const complete_1 = __importDefault(require("./commands/project/complete"));
|
|
14
18
|
const program = new commander_1.Command();
|
|
15
19
|
program
|
|
16
20
|
.name('cloving')
|
|
17
21
|
.description('Integrate AI into your development workflow for generating commit messages, code reviews, and unit tests.')
|
|
18
22
|
.version('1.0.0');
|
|
23
|
+
program
|
|
24
|
+
.command('config')
|
|
25
|
+
.description('Configure cloving with your API key and models to use')
|
|
26
|
+
.action(config_1.default);
|
|
27
|
+
program
|
|
28
|
+
.command('init')
|
|
29
|
+
.description('Setup cloving in the current directory')
|
|
30
|
+
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
31
|
+
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
32
|
+
.action(init_1.default);
|
|
33
|
+
program
|
|
34
|
+
.command('models')
|
|
35
|
+
.description('List available models')
|
|
36
|
+
.action(models_1.default);
|
|
19
37
|
program
|
|
38
|
+
.command('commit')
|
|
39
|
+
.description('Alias for cloving generate commit')
|
|
40
|
+
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
41
|
+
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
42
|
+
.action(commit_1.default);
|
|
43
|
+
// Generate commands
|
|
44
|
+
const generate = program
|
|
45
|
+
.command('generate')
|
|
46
|
+
.description('Generate various items like unit-tests and code reviews');
|
|
47
|
+
generate
|
|
20
48
|
.command('commit')
|
|
21
49
|
.description('Generate a commit message and commit the changes')
|
|
22
50
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
23
51
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
24
52
|
.action(commit_1.default);
|
|
25
|
-
|
|
53
|
+
generate
|
|
26
54
|
.command('unit-tests')
|
|
27
55
|
.description('Generate unit tests (if you don\'t specify filenames, it will generate tests for commited changes that differ from the main/master branch)')
|
|
28
56
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
29
57
|
.option('-f, --files <filenames...>', 'Specify filenames for the unit tests')
|
|
30
58
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
31
59
|
.action(unit_tests_1.default);
|
|
32
|
-
|
|
33
|
-
.command('models')
|
|
34
|
-
.description('List available models')
|
|
35
|
-
.action(models_1.default);
|
|
36
|
-
program
|
|
60
|
+
generate
|
|
37
61
|
.command('review')
|
|
38
62
|
.description('Review the code for commited changes that differ from the main/master branch')
|
|
39
63
|
.option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
|
|
40
64
|
.option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
|
|
41
65
|
.action(review_1.default);
|
|
42
|
-
|
|
66
|
+
// Project commands
|
|
67
|
+
const project = program
|
|
68
|
+
.command('project')
|
|
69
|
+
.description('Commands for cloving a project');
|
|
70
|
+
project
|
|
43
71
|
.command('init')
|
|
44
|
-
.description('Setup
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
.
|
|
50
|
-
|
|
51
|
-
.
|
|
72
|
+
.description('Setup a new cloving project')
|
|
73
|
+
.action(init_2.default);
|
|
74
|
+
project
|
|
75
|
+
.command('plan')
|
|
76
|
+
.description('Plan a cloving project')
|
|
77
|
+
.action(plan_1.default);
|
|
78
|
+
project
|
|
79
|
+
.command('build')
|
|
80
|
+
.description('Build the next step in the project')
|
|
81
|
+
.action(build_1.default);
|
|
82
|
+
project
|
|
83
|
+
.command('complete')
|
|
84
|
+
.description('Clean up and finalize the project')
|
|
85
|
+
.action(complete_1.default);
|
|
52
86
|
program.parse(process.argv);
|
|
@@ -3,13 +3,17 @@ 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
|
-
exports.getPrimaryModel = exports.saveConfig = exports.readClovingConfig = exports.getConfig = exports.CLOVINGFILE_PATH = exports.CLOVING_CONFIG_PATH = void 0;
|
|
6
|
+
exports.getTestingDirectory = exports.getAllFiles = exports.removeProjectConfig = exports.saveProjectConfig = exports.getProjectConfig = exports.getPrimaryModel = exports.saveConfig = exports.readClovingConfig = exports.getConfig = exports.CLOVINGPROJECT_PATH = exports.CLOVINGFILE_PATH = exports.CLOVING_CONFIG_PATH = void 0;
|
|
7
7
|
const os_1 = __importDefault(require("os"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const ini_1 = __importDefault(require("ini"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
11
13
|
exports.CLOVING_CONFIG_PATH = path_1.default.join(os_1.default.homedir(), '.clovingconfig');
|
|
12
14
|
exports.CLOVINGFILE_PATH = 'cloving.json';
|
|
15
|
+
exports.CLOVINGPROJECT_PATH = '.clovingprojects';
|
|
16
|
+
let clovingConfig;
|
|
13
17
|
const getConfig = (options) => {
|
|
14
18
|
try {
|
|
15
19
|
if (fs_1.default.existsSync(exports.CLOVING_CONFIG_PATH)) {
|
|
@@ -48,12 +52,16 @@ const getConfig = (options) => {
|
|
|
48
52
|
};
|
|
49
53
|
exports.getConfig = getConfig;
|
|
50
54
|
const readClovingConfig = () => {
|
|
51
|
-
if (
|
|
55
|
+
if (clovingConfig) {
|
|
56
|
+
return clovingConfig;
|
|
57
|
+
}
|
|
58
|
+
else if (fs_1.default.existsSync(exports.CLOVINGFILE_PATH)) {
|
|
52
59
|
const configFile = fs_1.default.readFileSync(exports.CLOVINGFILE_PATH, 'utf-8');
|
|
53
|
-
|
|
60
|
+
clovingConfig = JSON.parse(configFile);
|
|
61
|
+
return clovingConfig;
|
|
54
62
|
}
|
|
55
63
|
else {
|
|
56
|
-
throw new Error(
|
|
64
|
+
throw new Error(`${exports.CLOVING_CONFIG_PATH} file not found`);
|
|
57
65
|
}
|
|
58
66
|
};
|
|
59
67
|
exports.readClovingConfig = readClovingConfig;
|
|
@@ -87,3 +95,74 @@ const getPrimaryModel = (partialModel) => {
|
|
|
87
95
|
}
|
|
88
96
|
};
|
|
89
97
|
exports.getPrimaryModel = getPrimaryModel;
|
|
98
|
+
const getProjectConfig = (name) => {
|
|
99
|
+
const projectConfigPath = path_1.default.join(exports.CLOVINGPROJECT_PATH, name, 'config.ini');
|
|
100
|
+
if (fs_1.default.existsSync(projectConfigPath)) {
|
|
101
|
+
const configFile = fs_1.default.readFileSync(projectConfigPath, 'utf-8');
|
|
102
|
+
return ini_1.default.parse(configFile);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
throw new Error(`${projectConfigPath} file not found`);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
exports.getProjectConfig = getProjectConfig;
|
|
109
|
+
const saveProjectConfig = (name, config) => {
|
|
110
|
+
const projectConfigPath = path_1.default.join(exports.CLOVINGPROJECT_PATH, name, 'config.ini');
|
|
111
|
+
const projectConfigDir = path_1.default.dirname(projectConfigPath);
|
|
112
|
+
// Create directory if it doesn't exist
|
|
113
|
+
if (!fs_1.default.existsSync(projectConfigDir)) {
|
|
114
|
+
fs_1.default.mkdirSync(projectConfigDir, { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
const iniString = ini_1.default.stringify(config);
|
|
117
|
+
fs_1.default.writeFileSync(projectConfigPath, iniString);
|
|
118
|
+
console.log(`Configuration saved to ${projectConfigPath}`);
|
|
119
|
+
};
|
|
120
|
+
exports.saveProjectConfig = saveProjectConfig;
|
|
121
|
+
const removeProjectConfig = (name) => {
|
|
122
|
+
const projectConfigPath = path_1.default.join(exports.CLOVINGPROJECT_PATH, name, 'config.ini');
|
|
123
|
+
if (fs_1.default.existsSync(projectConfigPath)) {
|
|
124
|
+
fs_1.default.unlinkSync(projectConfigPath);
|
|
125
|
+
console.log(`Project completed and file removed: ${projectConfigPath}`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.error(`Configuration not found: ${projectConfigPath}`);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
exports.removeProjectConfig = removeProjectConfig;
|
|
132
|
+
const setupTestingPrompt = async (gpt, config) => {
|
|
133
|
+
const prompt = `I have the following cloving.json configuration file:
|
|
134
|
+
|
|
135
|
+
==== begin cloving.json ====
|
|
136
|
+
${JSON.stringify(config, null, 2)}
|
|
137
|
+
==== end cloving.json ====
|
|
138
|
+
|
|
139
|
+
Please provide instructions on how to set up testing for this project.`;
|
|
140
|
+
const setupInstructions = await gpt.generateText({ prompt });
|
|
141
|
+
console.log(setupInstructions);
|
|
142
|
+
process.exit(0);
|
|
143
|
+
};
|
|
144
|
+
const getAllFiles = async (options, forTesting) => {
|
|
145
|
+
const gpt = new cloving_gpt_1.default(options);
|
|
146
|
+
// Read the cloving.json file
|
|
147
|
+
const config = (0, exports.readClovingConfig)();
|
|
148
|
+
const testingDirectory = (0, exports.getTestingDirectory)();
|
|
149
|
+
let allSrcFiles = [];
|
|
150
|
+
if (forTesting && !testingDirectory) {
|
|
151
|
+
await setupTestingPrompt(gpt, config);
|
|
152
|
+
}
|
|
153
|
+
// Collect srcFiles for each language with specified directory and extension
|
|
154
|
+
for (const language of config.languages) {
|
|
155
|
+
if (language.directory && language.extension) {
|
|
156
|
+
const srcFiles = (0, child_process_1.execFileSync)('find', [language.directory, '-type', 'f', '-name', `*${language.extension}`]).toString().trim().split('\n');
|
|
157
|
+
allSrcFiles = allSrcFiles.concat(srcFiles);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Ensure allSrcFiles is unique
|
|
161
|
+
return Array.from(new Set(allSrcFiles));
|
|
162
|
+
};
|
|
163
|
+
exports.getAllFiles = getAllFiles;
|
|
164
|
+
const getTestingDirectory = () => {
|
|
165
|
+
const config = (0, exports.readClovingConfig)();
|
|
166
|
+
return config.testingFrameworks?.find((framework) => framework.directory)?.directory;
|
|
167
|
+
};
|
|
168
|
+
exports.getTestingDirectory = getTestingDirectory;
|
package/dist/utils/git_utils.js
CHANGED
|
@@ -3,7 +3,7 @@ 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
|
-
exports.getGitDiff = exports.generateCommitMessagePrompt = void 0;
|
|
6
|
+
exports.getCurrentBranchName = exports.getDefaultBranchName = exports.getGitDiff = exports.generateCommitMessagePrompt = void 0;
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const readline_1 = __importDefault(require("readline"));
|
|
9
9
|
const askQuestion = (question) => {
|
|
@@ -39,27 +39,8 @@ ${diff}`;
|
|
|
39
39
|
exports.generateCommitMessagePrompt = generateCommitMessagePrompt;
|
|
40
40
|
const getGitDiff = async () => {
|
|
41
41
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.toString()
|
|
45
|
-
.trim();
|
|
46
|
-
if (mainBranchName === '') {
|
|
47
|
-
console.error('Could not determine the default branch.');
|
|
48
|
-
const answer = await askQuestion('Do you want to run `git remote set-head origin -a`? [Y/n] ');
|
|
49
|
-
if (answer.toLowerCase() === 'y' || answer === '') {
|
|
50
|
-
(0, child_process_1.execSync)('git remote set-head origin -a');
|
|
51
|
-
mainBranchName = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD | sed \'s@^refs/remotes/origin/@@\'')
|
|
52
|
-
.toString()
|
|
53
|
-
.trim();
|
|
54
|
-
if (mainBranchName === '') {
|
|
55
|
-
throw new Error('Error: Could not determine the default branch (main or master)');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
throw new Error('Operation aborted by user.');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const gitDiff = (0, child_process_1.execSync)(`git diff ${mainBranchName} --`).toString().trim();
|
|
42
|
+
const defaultBranchName = await (0, exports.getDefaultBranchName)();
|
|
43
|
+
const gitDiff = (0, child_process_1.execSync)(`git diff ${defaultBranchName} --`).toString().trim();
|
|
63
44
|
return gitDiff;
|
|
64
45
|
}
|
|
65
46
|
catch (error) {
|
|
@@ -68,3 +49,34 @@ const getGitDiff = async () => {
|
|
|
68
49
|
}
|
|
69
50
|
};
|
|
70
51
|
exports.getGitDiff = getGitDiff;
|
|
52
|
+
const getDefaultBranchName = async () => {
|
|
53
|
+
let defaultBranchName = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD | sed \'s@^refs/remotes/origin/@@\'')
|
|
54
|
+
.toString()
|
|
55
|
+
.trim();
|
|
56
|
+
if (defaultBranchName === '') {
|
|
57
|
+
console.error('Could not determine the default branch.');
|
|
58
|
+
const answer = await askQuestion('Do you want to run `git remote set-head origin -a`? [Y/n] ');
|
|
59
|
+
if (answer.toLowerCase() === 'y' || answer === '') {
|
|
60
|
+
(0, child_process_1.execSync)('git remote set-head origin -a');
|
|
61
|
+
defaultBranchName = await (0, exports.getDefaultBranchName)();
|
|
62
|
+
if (defaultBranchName === '') {
|
|
63
|
+
throw new Error('Error: Could not determine the default branch (main or master)');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
throw new Error('Operation aborted by user.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return defaultBranchName;
|
|
71
|
+
};
|
|
72
|
+
exports.getDefaultBranchName = getDefaultBranchName;
|
|
73
|
+
const getCurrentBranchName = () => {
|
|
74
|
+
try {
|
|
75
|
+
return (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD').toString().trim();
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error('Error getting current branch name:', error.message);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
exports.getCurrentBranchName = getCurrentBranchName;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
|
|
3
|
+
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;
|
|
@@ -61,3 +61,38 @@ const extractMarkdown = (response) => {
|
|
|
61
61
|
return markdownString;
|
|
62
62
|
};
|
|
63
63
|
exports.extractMarkdown = extractMarkdown;
|
|
64
|
+
const parseMarkdownInstructions = (input) => {
|
|
65
|
+
const lines = input.split('\n');
|
|
66
|
+
const result = [];
|
|
67
|
+
let buffer = [];
|
|
68
|
+
let inCodeBlock = false;
|
|
69
|
+
lines.forEach(line => {
|
|
70
|
+
if (line.trim().startsWith('```')) {
|
|
71
|
+
if (inCodeBlock) {
|
|
72
|
+
// End of code block
|
|
73
|
+
buffer.push(line);
|
|
74
|
+
result.push(buffer.join('\n'));
|
|
75
|
+
buffer = [];
|
|
76
|
+
inCodeBlock = false;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Start of code block
|
|
80
|
+
if (buffer.length > 0) {
|
|
81
|
+
result.push(buffer.join('\n'));
|
|
82
|
+
buffer = [];
|
|
83
|
+
}
|
|
84
|
+
buffer.push(line);
|
|
85
|
+
inCodeBlock = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
buffer.push(line);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// If buffer has any remaining lines, add them to the result
|
|
93
|
+
if (buffer.length > 0) {
|
|
94
|
+
result.push(buffer.join('\n'));
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
};
|
|
98
|
+
exports.parseMarkdownInstructions = parseMarkdownInstructions;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloving",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"axios": "^1.7.2",
|
|
41
|
+
"cli-highlight": "^2.1.11",
|
|
41
42
|
"commander": "^12.1.0",
|
|
42
43
|
"eslint": "^9.6.0",
|
|
43
44
|
"eslint-config-prettier": "^9.1.0",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"eslint-plugin-promise": "^6.4.0",
|
|
50
51
|
"ignore": "^5.3.1",
|
|
51
52
|
"ini": "^4.1.3",
|
|
53
|
+
"inquirer": "^10.0.1",
|
|
52
54
|
"nock": "^13.5.4",
|
|
53
55
|
"vite": "^5.3.3",
|
|
54
56
|
"vite-node": "^2.0.1"
|