cloving 0.1.0
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/LICENSE.md +21 -0
- package/README.md +119 -0
- package/dist/abstract_gpt/index.js +55 -0
- package/dist/cloving_gpt/adapters/Adapter.js +2 -0
- package/dist/cloving_gpt/adapters/claude.js +60 -0
- package/dist/cloving_gpt/adapters/gpt4all.js +29 -0
- package/dist/cloving_gpt/adapters/index.js +2 -0
- package/dist/cloving_gpt/adapters/ollama.js +67 -0
- package/dist/cloving_gpt/adapters/openai.js +41 -0
- package/dist/cloving_gpt/index.js +116 -0
- package/dist/commands/analyze.js +47 -0
- package/dist/commands/check.js +7 -0
- package/dist/commands/commit.js +44 -0
- package/dist/commands/config.js +40 -0
- package/dist/commands/describe.js +235 -0
- package/dist/commands/init.js +169 -0
- package/dist/commands/models.js +17 -0
- package/dist/commands/review.js +50 -0
- package/dist/commands/unit_tests.js +119 -0
- package/dist/commands/update.js +7 -0
- package/dist/index.js +47 -0
- package/dist/utils/command_utils.js +151 -0
- package/dist/utils/gitUtils.js +42 -0
- package/dist/utils/git_utils.js +70 -0
- package/dist/utils/modelUtils.js +7 -0
- package/dist/utils/model_utils.js +7 -0
- package/dist/utils/string_utils.js +63 -0
- package/dist/utils/types.js +2 -0
- package/package.json +65 -0
|
@@ -0,0 +1,151 @@
|
|
|
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.getConfig = exports.saveConfig = exports.fetchModels = exports.promptUser = exports.runCommand = exports.generateFileList = exports.checkForSpecialFiles = exports.collectSpecialFileContents = exports.SPECIAL_FILES = exports.CONFIG_PATH = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const readline_1 = __importDefault(require("readline"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
exports.CONFIG_PATH = path_1.default.join(os_1.default.homedir(), '.cloving.json');
|
|
13
|
+
exports.SPECIAL_FILES = [
|
|
14
|
+
'package.json',
|
|
15
|
+
'Gemfile',
|
|
16
|
+
'requirements.txt',
|
|
17
|
+
'Pipfile',
|
|
18
|
+
'pyproject.toml',
|
|
19
|
+
'pom.xml',
|
|
20
|
+
'build.gradle',
|
|
21
|
+
'.csproj',
|
|
22
|
+
'packages.config',
|
|
23
|
+
'composer.json',
|
|
24
|
+
'CMakeLists.txt',
|
|
25
|
+
'conanfile.txt',
|
|
26
|
+
'conanfile.py',
|
|
27
|
+
'go.mod',
|
|
28
|
+
'Cargo.toml',
|
|
29
|
+
'Package.swift',
|
|
30
|
+
'build.gradle.kts',
|
|
31
|
+
'Podfile',
|
|
32
|
+
'Cartfile',
|
|
33
|
+
'cpanfile',
|
|
34
|
+
'DESCRIPTION',
|
|
35
|
+
'mix.exs',
|
|
36
|
+
'build.sbt',
|
|
37
|
+
'pubspec.yaml',
|
|
38
|
+
'stack.yaml',
|
|
39
|
+
'cabal.project',
|
|
40
|
+
'Project.toml',
|
|
41
|
+
'rockspec',
|
|
42
|
+
'rebar.config',
|
|
43
|
+
'project.clj'
|
|
44
|
+
];
|
|
45
|
+
const collectSpecialFileContents = () => {
|
|
46
|
+
const specialFileContents = {};
|
|
47
|
+
for (const file of exports.SPECIAL_FILES) {
|
|
48
|
+
if (fs_1.default.existsSync(file)) {
|
|
49
|
+
try {
|
|
50
|
+
const content = fs_1.default.readFileSync(file, 'utf-8');
|
|
51
|
+
specialFileContents[file] = file.endsWith('.json') ? JSON.parse(content) : content;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
specialFileContents[file] = fs_1.default.readFileSync(file, 'utf-8');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return specialFileContents;
|
|
59
|
+
};
|
|
60
|
+
exports.collectSpecialFileContents = collectSpecialFileContents;
|
|
61
|
+
const checkForSpecialFiles = () => {
|
|
62
|
+
return exports.SPECIAL_FILES.some(file => fs_1.default.existsSync(file));
|
|
63
|
+
};
|
|
64
|
+
exports.checkForSpecialFiles = checkForSpecialFiles;
|
|
65
|
+
const generateFileList = async () => {
|
|
66
|
+
try {
|
|
67
|
+
const lsOutput = await (0, exports.runCommand)('ls', []);
|
|
68
|
+
const findOutput = await (0, exports.runCommand)('find', ['.']);
|
|
69
|
+
const cleanedFindOutput = findOutput.map(file => file.startsWith('./') ? file.slice(2) : file);
|
|
70
|
+
const files = [...lsOutput, ...cleanedFindOutput];
|
|
71
|
+
const uniqueFiles = Array.from(new Set(files));
|
|
72
|
+
return uniqueFiles.filter(file => file && !file.includes('.git') && !file.includes('node_modules') && !file.includes('tmp'));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error('Error generating file list:', error.message);
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.generateFileList = generateFileList;
|
|
80
|
+
const runCommand = (command, args) => {
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
const process = (0, child_process_1.spawn)(command, args);
|
|
83
|
+
const output = [];
|
|
84
|
+
process.stdout.on('data', (data) => {
|
|
85
|
+
output.push(data.toString());
|
|
86
|
+
});
|
|
87
|
+
process.stderr.on('data', (data) => {
|
|
88
|
+
console.error(`stderr: ${data}`);
|
|
89
|
+
});
|
|
90
|
+
process.on('close', (code) => {
|
|
91
|
+
if (code !== 0) {
|
|
92
|
+
reject(new Error(`${command} process exited with code ${code}`));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
resolve(output.join('').trim().split('\n'));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
exports.runCommand = runCommand;
|
|
101
|
+
const promptUser = (question) => {
|
|
102
|
+
const rl = readline_1.default.createInterface({
|
|
103
|
+
input: process.stdin,
|
|
104
|
+
output: process.stdout
|
|
105
|
+
});
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
rl.question(question, (answer) => {
|
|
108
|
+
rl.close();
|
|
109
|
+
resolve(answer.trim());
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
exports.promptUser = promptUser;
|
|
114
|
+
const fetchModels = async () => {
|
|
115
|
+
try {
|
|
116
|
+
const modelsOutput = await (0, exports.runCommand)('cloving', ['models']);
|
|
117
|
+
return modelsOutput;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error('Error fetching models:', error.message);
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
exports.fetchModels = fetchModels;
|
|
125
|
+
const saveConfig = (config) => {
|
|
126
|
+
fs_1.default.writeFileSync(exports.CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
127
|
+
console.log(`Configuration saved to ${exports.CONFIG_PATH}`);
|
|
128
|
+
};
|
|
129
|
+
exports.saveConfig = saveConfig;
|
|
130
|
+
const getConfig = () => {
|
|
131
|
+
try {
|
|
132
|
+
if (fs_1.default.existsSync(exports.CONFIG_PATH)) {
|
|
133
|
+
const rawConfig = fs_1.default.readFileSync(exports.CONFIG_PATH, 'utf-8');
|
|
134
|
+
return JSON.parse(rawConfig);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
console.error('Error reading configuration:', err);
|
|
139
|
+
}
|
|
140
|
+
if (process.env.CLOVING_MODEL) {
|
|
141
|
+
return {
|
|
142
|
+
models: { [`${process.env.CLOVING_MODEL}`]: process.env.CLOVING_API_KEY ?? '' },
|
|
143
|
+
primaryModel: process.env.CLOVING_MODEL,
|
|
144
|
+
silent: false
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
return { models: {}, primaryModel: null, silent: false };
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
exports.getConfig = getConfig;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getGitDiff = exports.generateCommitMessagePrompt = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const generateCommitMessagePrompt = () => {
|
|
6
|
+
const diff = (0, child_process_1.execSync)('git diff').toString();
|
|
7
|
+
return `Generate a concise and meaningful commit message based on a diff.
|
|
8
|
+
|
|
9
|
+
Do not add any commentary or context to the message other than the commit message itself.
|
|
10
|
+
|
|
11
|
+
An example of the output for this should look like the following:
|
|
12
|
+
|
|
13
|
+
# Update dependencies and package versions
|
|
14
|
+
|
|
15
|
+
- Upgrade Ruby gems including aws-sdk, honeybadger, irb, and rubocop
|
|
16
|
+
- Update Node.js packages including esbuild, tinymce, and trix
|
|
17
|
+
- Bump TypeScript and ESLint related packages to latest versions
|
|
18
|
+
|
|
19
|
+
====
|
|
20
|
+
|
|
21
|
+
Here is the diff to help you write the commit message:
|
|
22
|
+
|
|
23
|
+
${diff}`;
|
|
24
|
+
};
|
|
25
|
+
exports.generateCommitMessagePrompt = generateCommitMessagePrompt;
|
|
26
|
+
const getGitDiff = () => {
|
|
27
|
+
try {
|
|
28
|
+
const masterBranchName = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD | sed \'s@^refs/remotes/origin/@@\'')
|
|
29
|
+
.toString()
|
|
30
|
+
.trim();
|
|
31
|
+
if (!masterBranchName) {
|
|
32
|
+
throw new Error('Error: Could not determine the default branch (main or master)');
|
|
33
|
+
}
|
|
34
|
+
const gitDiff = (0, child_process_1.execSync)(`git diff ${masterBranchName} --`).toString().trim();
|
|
35
|
+
return gitDiff;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('Error getting git diff:', error.message);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
exports.getGitDiff = getGitDiff;
|
|
@@ -0,0 +1,70 @@
|
|
|
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.getGitDiff = exports.generateCommitMessagePrompt = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const readline_1 = __importDefault(require("readline"));
|
|
9
|
+
const askQuestion = (question) => {
|
|
10
|
+
const rl = readline_1.default.createInterface({
|
|
11
|
+
input: process.stdin,
|
|
12
|
+
output: process.stdout
|
|
13
|
+
});
|
|
14
|
+
return new Promise(resolve => rl.question(question, answer => {
|
|
15
|
+
rl.close();
|
|
16
|
+
resolve(answer);
|
|
17
|
+
}));
|
|
18
|
+
};
|
|
19
|
+
const generateCommitMessagePrompt = () => {
|
|
20
|
+
const diff = (0, child_process_1.execSync)('git diff HEAD').toString();
|
|
21
|
+
return `Generate a concise and meaningful commit message based on a diff.
|
|
22
|
+
|
|
23
|
+
Do not add any commentary or context to the message other than the commit message itself.
|
|
24
|
+
|
|
25
|
+
An example of the output for this should look like the following:
|
|
26
|
+
|
|
27
|
+
\`\`\`plaintext
|
|
28
|
+
Update dependencies and package versions
|
|
29
|
+
|
|
30
|
+
- Upgrade Ruby gems including aws-sdk, honeybadger, irb, and rubocop
|
|
31
|
+
- Update Node.js packages including esbuild, tinymce, and trix
|
|
32
|
+
- Bump TypeScript and ESLint related packages to latest versions
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
Here is the diff to help you write the commit message:
|
|
36
|
+
|
|
37
|
+
${diff}`;
|
|
38
|
+
};
|
|
39
|
+
exports.generateCommitMessagePrompt = generateCommitMessagePrompt;
|
|
40
|
+
const getGitDiff = async () => {
|
|
41
|
+
try {
|
|
42
|
+
let mainBranchName;
|
|
43
|
+
mainBranchName = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD | sed \'s@^refs/remotes/origin/@@\'')
|
|
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();
|
|
63
|
+
return gitDiff;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Error getting git diff:', error.message);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
exports.getGitDiff = getGitDiff;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
|
|
4
|
+
// Function to estimate token count
|
|
5
|
+
const estimateTokens = async (text) => {
|
|
6
|
+
const charCount = text.length;
|
|
7
|
+
const tokenCount = Math.ceil(charCount / 4);
|
|
8
|
+
return tokenCount;
|
|
9
|
+
};
|
|
10
|
+
exports.estimateTokens = estimateTokens;
|
|
11
|
+
// Function to extract JSON metadata from the AI response
|
|
12
|
+
const extractJsonMetadata = (response) => {
|
|
13
|
+
let jsonString;
|
|
14
|
+
// Extract the ```json block from the response
|
|
15
|
+
const jsonBlockStart = response.indexOf('```json');
|
|
16
|
+
const jsonBlockEnd = response.indexOf('```', jsonBlockStart + 6);
|
|
17
|
+
if (jsonBlockStart !== -1 && jsonBlockEnd !== -1) {
|
|
18
|
+
jsonString = response.substring(jsonBlockStart + 6, jsonBlockEnd).trim();
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
jsonString = response;
|
|
22
|
+
}
|
|
23
|
+
// Remove any data before the first '{'
|
|
24
|
+
const jsonStartIndex = jsonString.indexOf('{');
|
|
25
|
+
if (jsonStartIndex !== -1) {
|
|
26
|
+
jsonString = jsonString.substring(jsonStartIndex);
|
|
27
|
+
}
|
|
28
|
+
// Remove any data after the last '}'
|
|
29
|
+
const jsonEndIndex = jsonString.lastIndexOf('}');
|
|
30
|
+
if (jsonEndIndex !== -1) {
|
|
31
|
+
jsonString = jsonString.substring(0, jsonEndIndex + 1);
|
|
32
|
+
}
|
|
33
|
+
return jsonString;
|
|
34
|
+
};
|
|
35
|
+
exports.extractJsonMetadata = extractJsonMetadata;
|
|
36
|
+
// Function to extract markdown from the AI response
|
|
37
|
+
const extractMarkdown = (response) => {
|
|
38
|
+
let markdownString;
|
|
39
|
+
// Extract the ```markdown block from the response
|
|
40
|
+
const markdownBlockStart = response.indexOf('```markdown');
|
|
41
|
+
const markdownBlockEnd = response.indexOf('```', markdownBlockStart + 11);
|
|
42
|
+
if (markdownBlockStart !== -1 && markdownBlockEnd !== -1) {
|
|
43
|
+
markdownString = response.substring(markdownBlockStart + 11, markdownBlockEnd).trim();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
markdownString = response;
|
|
47
|
+
}
|
|
48
|
+
const plaintextBlockStart = response.indexOf('```plaintext');
|
|
49
|
+
const plaintextBlockEnd = response.indexOf('```', plaintextBlockStart + 12);
|
|
50
|
+
if (plaintextBlockStart !== -1 && plaintextBlockEnd !== -1) {
|
|
51
|
+
markdownString = response.substring(plaintextBlockStart + 12, plaintextBlockEnd).trim();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
markdownString = response;
|
|
55
|
+
}
|
|
56
|
+
// Remove any data before the first '#'
|
|
57
|
+
const jsonStartIndex = markdownString.indexOf('#');
|
|
58
|
+
if (jsonStartIndex !== -1) {
|
|
59
|
+
markdownString = markdownString.substring(jsonStartIndex);
|
|
60
|
+
}
|
|
61
|
+
return markdownString;
|
|
62
|
+
};
|
|
63
|
+
exports.extractMarkdown = extractMarkdown;
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cloving",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"start": "node dist/index.js",
|
|
9
|
+
"watch": "tsc -w",
|
|
10
|
+
"check": "bash -c 'npx tsc --noEmit --skipLibCheck'",
|
|
11
|
+
"test": "bash -c jest",
|
|
12
|
+
"prepare": "husky"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"cloving": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"!**/test/**"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [],
|
|
22
|
+
"author": "Lucas Carlson",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"description": "",
|
|
25
|
+
"lint-staged": {
|
|
26
|
+
"*.ts": [
|
|
27
|
+
"yarn run check"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"lint-prepush": {
|
|
31
|
+
"base": "main",
|
|
32
|
+
"tasks": {
|
|
33
|
+
"*.ts": [
|
|
34
|
+
"yarn run check",
|
|
35
|
+
"yarn run test"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"axios": "^1.7.2",
|
|
41
|
+
"commander": "^12.1.0",
|
|
42
|
+
"eslint": "^9.6.0",
|
|
43
|
+
"eslint-config-prettier": "^9.1.0",
|
|
44
|
+
"eslint-config-raine": "^0.5.0",
|
|
45
|
+
"eslint-config-standard": "^17.1.0",
|
|
46
|
+
"eslint-plugin-import": "^2.29.1",
|
|
47
|
+
"eslint-plugin-jsdoc": "^48.5.2",
|
|
48
|
+
"eslint-plugin-n": "^17.9.0",
|
|
49
|
+
"eslint-plugin-promise": "^6.4.0",
|
|
50
|
+
"ignore": "^5.3.1",
|
|
51
|
+
"nock": "^13.5.4",
|
|
52
|
+
"vite": "^5.3.3",
|
|
53
|
+
"vite-node": "^2.0.1"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/jest": "^29.5.12",
|
|
57
|
+
"@types/node": "^20.14.10",
|
|
58
|
+
"husky": "^9.0.11",
|
|
59
|
+
"jest": "^29.7.0",
|
|
60
|
+
"lint-prepush": "^2.2.2",
|
|
61
|
+
"lint-staged": "^15.2.7",
|
|
62
|
+
"ts-jest": "^29.2.0",
|
|
63
|
+
"typescript": "^5.5.3"
|
|
64
|
+
}
|
|
65
|
+
}
|