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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Lucas Carlson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Cloving
2
+
3
+ Cloving is an innovative tool designed to seamlessly integrate AI into various aspects of the developer workflow. It aims to enhance productivity and code quality by leveraging AI for tasks such as generating commit messages, conducting code reviews, and creating unit tests.
4
+
5
+ ## Features
6
+
7
+ - **AI-powered Git commit message generation**
8
+ - **Automated code reviews with AI insights**
9
+ - **AI-assisted unit test generation**
10
+ - **Seamless integration with existing development workflows**
11
+ - **Customizable AI prompts for different tasks**
12
+
13
+ ## Prerequisites
14
+
15
+ - Node.js (version 20 or higher)
16
+ - Git
17
+ - API access to AI chat models (e.g., OpenAI GPT-4o, Claude, etc.)
18
+
19
+ ## Installation
20
+
21
+ 1. Clone this repository:
22
+ ```bash
23
+ git clone https://github.com/clovingai/cloving-cli.git
24
+ ```
25
+
26
+ 2. Navigate to the project directory:
27
+ ```bash
28
+ cd cloving-cli
29
+ ```
30
+
31
+ 3. Install dependencies:
32
+ ```bash
33
+ yarn install
34
+ ```
35
+
36
+ 4. Set up your AI chat model environment variables:
37
+ ```bash
38
+ export CLOVING_MODEL=your_preferred_model
39
+ export CLOVING_API_KEY=your_api_key
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ Cloving provides several commands to help integrate AI into your development workflow:
45
+
46
+ ### Generating Commit Messages
47
+
48
+ Run the following command in your Git repository:
49
+ ```bash
50
+ cloving commit
51
+ ```
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
+
54
+ ### Code Reviews
55
+
56
+ To get an AI-powered code review for your latest changes:
57
+ ```bash
58
+ cloving review
59
+ ```
60
+
61
+ ### Generating Unit Tests
62
+
63
+ To generate unit tests for a specific file or function:
64
+ ```bash
65
+ cloving unit-tests [file_path_to_test]
66
+ ```
67
+
68
+ ### Initial Setup
69
+
70
+ To set up Cloving in the current project, generating a `cloving.json` file that includes a GPT-generated project overview with details like the language, framework, and language version:
71
+ ```bash
72
+ cloving init
73
+ ```
74
+
75
+ ### Configuration
76
+
77
+ To configure Cloving with your API key and model:
78
+ ```bash
79
+ cloving config
80
+ ```
81
+
82
+ ### Listing Available Models
83
+
84
+ To list the available AI models:
85
+ ```bash
86
+ cloving models
87
+ ```
88
+
89
+ ## Command Line Options
90
+
91
+ Usage: `cloving [options] [command]`
92
+
93
+ Integrate AI into your development workflow for generating commit messages, code reviews, and unit tests.
94
+
95
+ Options:
96
+ - `-V, --version` output the version number
97
+ - `-h, --help` display help for command
98
+
99
+ Commands:
100
+ - `commit` Generate a commit message and commit the changes
101
+ - `unit-tests` Generate unit tests for the changes
102
+ - `models` List available models
103
+ - `review` Review the changes and propose
104
+ - `init` Setup Cloving in the current project
105
+ - `config` Configure Cloving with your API key and model
106
+ - `help [command]` Display help for command
107
+
108
+ ## Contributing
109
+
110
+ Contributions are welcome! Please feel free to submit a Pull Request.
111
+
112
+ ## License
113
+
114
+ This project is licensed under the [MIT License](LICENSE).
115
+
116
+ ## Acknowledgements
117
+
118
+ - This project is inspired by [aichat](https://github.com/sigoden/aichat) for interfacing with AI models.
119
+ - Special thanks to the open-source community for inspiration and support.
@@ -0,0 +1,55 @@
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 axios_1 = __importDefault(require("axios"));
7
+ class AbstractGPT {
8
+ constructor() {
9
+ const clovingModel = process.env.CLOVING_MODEL;
10
+ this.apiKey = process.env.CLOVING_API_KEY || '';
11
+ if (!clovingModel || !this.apiKey) {
12
+ throw new Error("CLOVING_MODEL and CLOVING_API_KEY environment variables must be set");
13
+ }
14
+ const [provider, model] = clovingModel.split(':');
15
+ this.provider = provider;
16
+ this.model = model;
17
+ }
18
+ getEndpoint() {
19
+ switch (this.provider) {
20
+ case 'openai':
21
+ return `https://api.openai.com/v1/engines/${this.model}/completions`;
22
+ case 'claude':
23
+ return `https://api.anthropic.com/v1/claude/${this.model}`;
24
+ case 'gpt4all':
25
+ return `https://api.gpt4all.io/v1/models/${this.model}/completions`;
26
+ default:
27
+ throw new Error(`Unsupported provider: ${this.provider}`);
28
+ }
29
+ }
30
+ async generateText(request) {
31
+ const endpoint = this.getEndpoint();
32
+ const response = await axios_1.default.post(endpoint, {
33
+ model: this.model,
34
+ prompt: request.prompt,
35
+ max_tokens: request.maxTokens || 100,
36
+ temperature: request.temperature || 0.7
37
+ }, {
38
+ headers: {
39
+ 'Authorization': `Bearer ${this.apiKey}`,
40
+ 'Content-Type': 'application/json'
41
+ }
42
+ });
43
+ switch (this.provider) {
44
+ case 'openai':
45
+ return response.data.choices[0].text;
46
+ case 'claude':
47
+ return response.data.completion;
48
+ case 'gpt4all':
49
+ return response.data.text;
50
+ default:
51
+ throw new Error(`Unsupported provider: ${this.provider}`);
52
+ }
53
+ }
54
+ }
55
+ exports.default = AbstractGPT;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClaudeAdapter = void 0;
4
+ class ClaudeAdapter {
5
+ constructor(model) {
6
+ this.model = model;
7
+ this.anthropicVersion = '2023-06-01';
8
+ }
9
+ static listSupportedModels() {
10
+ ClaudeAdapter.supportedModels.forEach(model => {
11
+ console.log(model);
12
+ });
13
+ }
14
+ getEndpoint() {
15
+ return `https://api.anthropic.com/v1/messages`;
16
+ }
17
+ getHeaders(apiKey) {
18
+ return {
19
+ 'x-api-key': apiKey,
20
+ 'Content-Type': 'application/json',
21
+ 'anthropic-version': this.anthropicVersion
22
+ };
23
+ }
24
+ getPayload(request) {
25
+ return {
26
+ model: this.model,
27
+ system: 'You are a computer programmer giving advice on how to write better code.',
28
+ messages: [
29
+ { role: 'user', content: request.prompt }
30
+ ],
31
+ max_tokens: request.maxTokens || 4096,
32
+ temperature: request.temperature || 0.7
33
+ };
34
+ }
35
+ extractResponse(data) {
36
+ try {
37
+ if (data && data.content && Array.isArray(data.content) && data.content.length > 0) {
38
+ return data.content.map((item) => item.text).join('\n');
39
+ }
40
+ else {
41
+ console.error('Unexpected response structure:', data);
42
+ throw new Error('Invalid response structure');
43
+ }
44
+ }
45
+ catch (error) {
46
+ console.error('Error extracting response:', error);
47
+ throw error;
48
+ }
49
+ }
50
+ }
51
+ exports.ClaudeAdapter = ClaudeAdapter;
52
+ ClaudeAdapter.supportedModels = [
53
+ 'claude:claude-3-5-sonnet-20240620',
54
+ 'claude:claude-3-opus-20240229',
55
+ 'claude:claude-3-sonnet-20240229',
56
+ 'claude:claude-3-haiku-20240307',
57
+ // Add more supported models here as needed
58
+ ];
59
+ // Example usage:
60
+ // ClaudeAdapter.listSupportedModels()
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GPT4AllAdapter = void 0;
4
+ class GPT4AllAdapter {
5
+ constructor(model) {
6
+ this.model = model;
7
+ }
8
+ getEndpoint() {
9
+ return `https://api.gpt4all.io/v1/models/${this.model}/completions`;
10
+ }
11
+ getHeaders(apiKey) {
12
+ return {
13
+ 'Authorization': `Bearer ${apiKey}`,
14
+ 'Content-Type': 'application/json'
15
+ };
16
+ }
17
+ getPayload(request) {
18
+ return {
19
+ model: this.model,
20
+ prompt: request.prompt,
21
+ max_tokens: request.maxTokens,
22
+ temperature: request.temperature || 0.7
23
+ };
24
+ }
25
+ extractResponse(data) {
26
+ return data.text;
27
+ }
28
+ }
29
+ exports.GPT4AllAdapter = GPT4AllAdapter;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,67 @@
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.OllamaAdapter = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class OllamaAdapter {
9
+ constructor(model) {
10
+ this.model = model;
11
+ }
12
+ static async listSupportedModels() {
13
+ try {
14
+ const endpoint = 'http://localhost:11434/api/tags';
15
+ const headers = {
16
+ 'Content-Type': 'application/json'
17
+ };
18
+ const response = await axios_1.default.get(endpoint, { headers });
19
+ const data = response.data;
20
+ if (data && Array.isArray(data.models)) {
21
+ OllamaAdapter.supportedModels = data.models.map((model) => `ollama:${model.name}`);
22
+ OllamaAdapter.supportedModels.forEach(model => {
23
+ console.log(model);
24
+ });
25
+ }
26
+ else {
27
+ console.error('Unexpected response structure:', data);
28
+ }
29
+ }
30
+ catch (error) {
31
+ // do nothing, no ollama server running
32
+ }
33
+ }
34
+ getEndpoint() {
35
+ return `http://localhost:11434/api/generate`;
36
+ }
37
+ getHeaders(apiKey) {
38
+ return {
39
+ 'Authorization': `Bearer ${apiKey}`,
40
+ 'Content-Type': 'application/json'
41
+ };
42
+ }
43
+ getPayload(request) {
44
+ return {
45
+ model: this.model,
46
+ prompt: request.prompt,
47
+ stream: request.stream ?? false
48
+ };
49
+ }
50
+ extractResponse(data) {
51
+ try {
52
+ if (data && typeof data === 'object' && 'response' in data) {
53
+ return data.response;
54
+ }
55
+ else {
56
+ console.error('Unexpected response structure:', data);
57
+ throw new Error('Invalid response structure');
58
+ }
59
+ }
60
+ catch (error) {
61
+ console.error('Error extracting response:', error);
62
+ throw error;
63
+ }
64
+ }
65
+ }
66
+ exports.OllamaAdapter = OllamaAdapter;
67
+ OllamaAdapter.supportedModels = [];
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenAIAdapter = void 0;
4
+ class OpenAIAdapter {
5
+ static listSupportedModels() {
6
+ OpenAIAdapter.supportedModels.forEach(model => {
7
+ console.log(model);
8
+ });
9
+ }
10
+ constructor(model) {
11
+ this.model = model;
12
+ }
13
+ getEndpoint() {
14
+ return `https://api.openai.com/v1/chat/completions`;
15
+ }
16
+ getHeaders(apiKey) {
17
+ return {
18
+ 'Authorization': `Bearer ${apiKey}`,
19
+ 'Content-Type': 'application/json'
20
+ };
21
+ }
22
+ getPayload(request) {
23
+ return {
24
+ model: this.model,
25
+ messages: [{ role: "user", content: request.prompt }],
26
+ max_tokens: request.maxTokens,
27
+ temperature: request.temperature || 0.7
28
+ };
29
+ }
30
+ extractResponse(data) {
31
+ return data.choices[0].message.content;
32
+ }
33
+ }
34
+ exports.OpenAIAdapter = OpenAIAdapter;
35
+ OpenAIAdapter.supportedModels = [
36
+ 'openai:gpt-4o',
37
+ 'openai:gpt-4-turbo',
38
+ 'openai:gpt-3.5-turbo',
39
+ 'openai:text-embedding-3-large',
40
+ // Add more supported models here as needed
41
+ ];
@@ -0,0 +1,116 @@
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 axios_1 = __importDefault(require("axios"));
7
+ const readline_1 = __importDefault(require("readline"));
8
+ const child_process_1 = require("child_process");
9
+ const process_1 = __importDefault(require("process"));
10
+ const claude_1 = require("./adapters/claude");
11
+ const openai_1 = require("./adapters/openai");
12
+ const gpt4all_1 = require("./adapters/gpt4all");
13
+ const ollama_1 = require("./adapters/ollama");
14
+ const command_utils_1 = require("../utils/command_utils");
15
+ class ClovingGPT {
16
+ constructor(options = { silent: false }) {
17
+ const config = (0, command_utils_1.getConfig)();
18
+ if (!config || !config.primaryModel || !config.models) {
19
+ throw new Error('No cloving configuration found. Please run `cloving config`');
20
+ }
21
+ const clovingModel = config.primaryModel;
22
+ this.apiKey = config.models[config?.primaryModel || ''].trim();
23
+ this.silent = options.silent;
24
+ const parts = clovingModel.split(':');
25
+ const model = parts.slice(1).join(':');
26
+ switch (parts[0]) {
27
+ case 'claude':
28
+ this.adapter = new claude_1.ClaudeAdapter(model);
29
+ break;
30
+ case 'openai':
31
+ this.adapter = new openai_1.OpenAIAdapter(model);
32
+ break;
33
+ case 'gpt4all':
34
+ this.adapter = new gpt4all_1.GPT4AllAdapter(model);
35
+ break;
36
+ case 'ollama':
37
+ this.adapter = new ollama_1.OllamaAdapter(model);
38
+ break;
39
+ default:
40
+ throw new Error(`Unsupported provider: ${parts[0]}`);
41
+ }
42
+ }
43
+ async askUserToConfirm(prompt, message) {
44
+ if (this.silent)
45
+ return true;
46
+ const rl = readline_1.default.createInterface({
47
+ input: process_1.default.stdin,
48
+ output: process_1.default.stdout
49
+ });
50
+ return new Promise((resolve) => {
51
+ rl.question(message, (answer) => {
52
+ rl.close();
53
+ answer = answer.trim().toLowerCase();
54
+ if (answer === 'y' || answer === '') {
55
+ const less = (0, child_process_1.spawn)('less', ['-R'], { stdio: ['pipe', process_1.default.stdout, process_1.default.stderr] });
56
+ less.stdin.write(prompt);
57
+ less.stdin.end();
58
+ less.on('close', (code) => {
59
+ if (code !== 0) {
60
+ console.error('less command exited with an error.');
61
+ resolve(false);
62
+ return;
63
+ }
64
+ const rlConfirm = readline_1.default.createInterface({
65
+ input: process_1.default.stdin,
66
+ output: process_1.default.stdout
67
+ });
68
+ rlConfirm.question('Do you still want to continue? [Yn]: ', (confirmAnswer) => {
69
+ rlConfirm.close();
70
+ confirmAnswer = confirmAnswer.trim().toLowerCase();
71
+ resolve(confirmAnswer === 'y' || confirmAnswer === '');
72
+ });
73
+ });
74
+ less.stdin.on('error', (err) => {
75
+ if (isNodeError(err) && err.code === 'EPIPE') {
76
+ resolve(true);
77
+ }
78
+ else {
79
+ console.error('Pipeline error:', err);
80
+ resolve(false);
81
+ }
82
+ });
83
+ }
84
+ else {
85
+ resolve(true);
86
+ }
87
+ });
88
+ });
89
+ }
90
+ async generateText(request) {
91
+ const endpoint = this.adapter.getEndpoint();
92
+ const payload = this.adapter.getPayload(request);
93
+ const headers = this.adapter.getHeaders(this.apiKey);
94
+ const tokenCount = Math.ceil(request.prompt.length / 4).toLocaleString();
95
+ const shouldContinue = await this.askUserToConfirm(request.prompt, `Do you want to review the ~${tokenCount} token prompt before sending it to ${endpoint}? [Yn]: `);
96
+ if (!shouldContinue) {
97
+ throw 'Operation cancelled by the user.';
98
+ }
99
+ try {
100
+ const response = await axios_1.default.post(endpoint, payload, { headers });
101
+ return this.adapter.extractResponse(response.data);
102
+ }
103
+ catch (error) {
104
+ let errorMessage = error instanceof Error ? error.message : 'connection error';
105
+ if (errorMessage === '') {
106
+ errorMessage = 'connection error';
107
+ }
108
+ console.error(`Error communicating with the GPT server (${this.adapter.getEndpoint()}):`, errorMessage);
109
+ throw error;
110
+ }
111
+ }
112
+ }
113
+ function isNodeError(error) {
114
+ return error instanceof Error && 'code' in error;
115
+ }
116
+ exports.default = ClovingGPT;
@@ -0,0 +1,47 @@
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 readline_1 = __importDefault(require("readline"));
7
+ const child_process_1 = require("child_process");
8
+ const git_utils_1 = require("../utils/git_utils");
9
+ const model_utils_1 = require("../utils/model_utils");
10
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
11
+ const analyze = async () => {
12
+ try {
13
+ // Define the prompt for analysis
14
+ const gitDiff = await (0, git_utils_1.getGitDiff)();
15
+ const model = (0, model_utils_1.getModel)();
16
+ const prompt = `Explain why the change is being made and document a description of these changes.
17
+ Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
18
+
19
+ ${gitDiff}`;
20
+ // Instantiate ClovingGPT and get the analysis
21
+ const gpt = new cloving_gpt_1.default();
22
+ const analysis = await gpt.generateText({ prompt });
23
+ // Print the analysis to the console
24
+ console.log(analysis);
25
+ // Prompt the user to copy the analysis to the clipboard
26
+ const rl = readline_1.default.createInterface({
27
+ input: process.stdin,
28
+ output: process.stdout
29
+ });
30
+ rl.question('Do you want to copy the analysis to the clipboard? [Y/n] ', (answer) => {
31
+ if (answer.toLowerCase() === 'y' || answer === '') {
32
+ try {
33
+ (0, child_process_1.execFileSync)('pbcopy', { input: analysis });
34
+ console.log('Analysis copied to clipboard');
35
+ }
36
+ catch (error) {
37
+ console.error('Error: pbcopy command not found. Unable to copy to clipboard.');
38
+ }
39
+ }
40
+ rl.close();
41
+ });
42
+ }
43
+ catch (error) {
44
+ console.error('Error during analysis:', error.message);
45
+ }
46
+ };
47
+ exports.default = analyze;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = (options) => {
4
+ if (options.verbose)
5
+ console.log('Verbose mode enabled');
6
+ console.log('Checking something...');
7
+ };
@@ -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 command_utils_1 = require("../utils/command_utils");
12
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
13
+ const generateAndCommitMessage = async (options) => {
14
+ options.silent = (0, command_utils_1.getConfig)().silent || 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,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ const command_utils_1 = require("../utils/command_utils");
5
+ const config = async () => {
6
+ let currentConfig = (0, command_utils_1.getConfig)();
7
+ // Ensure models is initialized properly
8
+ if (!currentConfig.models) {
9
+ currentConfig.models = {};
10
+ }
11
+ const models = await (0, command_utils_1.fetchModels)();
12
+ if (models.length === 0) {
13
+ console.error('No models available.');
14
+ throw new Error('No models available');
15
+ }
16
+ let continueConfig = true;
17
+ while (continueConfig) {
18
+ console.log('Available models:');
19
+ models.forEach((model, index) => console.log(`${index + 1}. ${model}`));
20
+ const modelIndex = parseInt(await (0, command_utils_1.promptUser)('Select a model by number: '), 10) - 1;
21
+ if (modelIndex < 0 || modelIndex >= models.length) {
22
+ console.error('Invalid selection.');
23
+ throw new Error('Invalid selection');
24
+ }
25
+ const selectedModel = models[modelIndex];
26
+ const apiKey = await (0, command_utils_1.promptUser)(`Enter your API key for ${selectedModel}: `);
27
+ currentConfig.models[selectedModel] = apiKey;
28
+ const setAsPrimary = await (0, command_utils_1.promptUser)('Do you want to set this model as the primary model? [Yn]: ');
29
+ if (setAsPrimary.toLowerCase() === 'y' || setAsPrimary === '') {
30
+ currentConfig.primaryModel = selectedModel;
31
+ }
32
+ const anotherModel = await (0, command_utils_1.promptUser)('Do you want to configure another model? [Yn]: ');
33
+ if (anotherModel.toLowerCase() !== 'y' && anotherModel !== '') {
34
+ continueConfig = false;
35
+ }
36
+ }
37
+ (0, command_utils_1.saveConfig)(currentConfig);
38
+ };
39
+ exports.config = config;
40
+ exports.default = exports.config;