cloving 0.1.7 → 0.1.9

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 CHANGED
@@ -1,5 +1,3 @@
1
- Here's the updated README.md incorporating the latest changes:
2
-
3
1
  # Cloving
4
2
 
5
3
  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.
@@ -23,7 +23,7 @@ class ClaudeAdapter {
23
23
  }
24
24
  getPayload(request) {
25
25
  return {
26
- model: this.model,
26
+ model: this.model.replace(':', '-'),
27
27
  system: 'You are a computer programmer giving advice on how to write better code.',
28
28
  messages: [
29
29
  { role: 'user', content: request.prompt }
@@ -50,10 +50,10 @@ class ClaudeAdapter {
50
50
  }
51
51
  exports.ClaudeAdapter = ClaudeAdapter;
52
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',
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
57
  // Add more supported models here as needed
58
58
  ];
59
59
  // Example usage:
@@ -21,7 +21,7 @@ class OpenAIAdapter {
21
21
  }
22
22
  getPayload(request) {
23
23
  return {
24
- model: this.model,
24
+ model: this.model.replace(':', '-'),
25
25
  messages: [{ role: "user", content: request.prompt }],
26
26
  max_tokens: request.maxTokens,
27
27
  temperature: request.temperature || 0.7
@@ -33,9 +33,9 @@ class OpenAIAdapter {
33
33
  }
34
34
  exports.OpenAIAdapter = OpenAIAdapter;
35
35
  OpenAIAdapter.supportedModels = [
36
- 'openai:gpt-4o',
37
- 'openai:gpt-4-turbo',
38
- 'openai:gpt-3.5-turbo',
39
- 'openai:text-embedding-3-large',
36
+ 'openai:gpt:4o',
37
+ 'openai:gpt:4-turbo',
38
+ 'openai:gpt:3.5-turbo',
39
+ 'openai:text-embedding:3-large',
40
40
  // Add more supported models here as needed
41
41
  ];
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const axios_1 = __importDefault(require("axios"));
7
- const readline_1 = __importDefault(require("readline"));
8
7
  const child_process_1 = require("child_process");
9
8
  const process_1 = __importDefault(require("process"));
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
10
  const claude_1 = require("./adapters/claude");
11
11
  const openai_1 = require("./adapters/openai");
12
12
  const gpt4all_1 = require("./adapters/gpt4all");
@@ -22,7 +22,7 @@ class ClovingGPT {
22
22
  }
23
23
  const primaryModel = (0, config_utils_1.getPrimaryModel)(partialModel);
24
24
  if (!primaryModel) {
25
- console.log('No primary model found in the configuration. Please run: cloving config');
25
+ console.log('No AI API models found in the cloving configuration. Please run: cloving config');
26
26
  process_1.default.exit(1);
27
27
  }
28
28
  const { provider, model, config: modelConfig } = primaryModel;
@@ -45,68 +45,60 @@ class ClovingGPT {
45
45
  throw new Error(`Unsupported provider: ${provider}`);
46
46
  }
47
47
  }
48
- async askUserToConfirm(prompt, message) {
48
+ async reviewPrompt(prompt, endpoint) {
49
49
  if (this.silent)
50
- return true;
51
- const rl = readline_1.default.createInterface({
52
- input: process_1.default.stdin,
53
- output: process_1.default.stdout
54
- });
55
- return new Promise((resolve) => {
56
- rl.question(message, (answer) => {
57
- rl.close();
58
- answer = answer.trim().toLowerCase();
59
- if (answer === 'y' || answer === '') {
60
- const less = (0, child_process_1.spawn)('less', ['-R'], { stdio: ['pipe', process_1.default.stdout, process_1.default.stderr] });
61
- less.stdin.write(prompt);
62
- less.stdin.end();
63
- less.on('close', (code) => {
64
- if (code !== 0) {
65
- console.error('less command exited with an error');
66
- resolve(false);
67
- return;
68
- }
69
- const rlConfirm = readline_1.default.createInterface({
70
- input: process_1.default.stdin,
71
- output: process_1.default.stdout
72
- });
73
- rlConfirm.question('Do you still want to continue? [Yn]: ', (confirmAnswer) => {
74
- rlConfirm.close();
75
- confirmAnswer = confirmAnswer.trim().toLowerCase();
76
- if (confirmAnswer !== 'y' && confirmAnswer !== '') {
77
- console.error('User cancelled the operation');
78
- process_1.default.exit(1);
79
- }
80
- else {
81
- resolve(true);
82
- }
83
- });
84
- });
85
- less.stdin.on('error', (err) => {
86
- if (isNodeError(err) && err.code === 'EPIPE') {
87
- resolve(true);
88
- }
89
- else {
90
- console.error('Pipeline error:', err);
91
- resolve(false);
50
+ return;
51
+ const tokenCount = Math.ceil(prompt.length / 4).toLocaleString();
52
+ const { reviewPrompt } = await inquirer_1.default.prompt([
53
+ {
54
+ type: 'confirm',
55
+ name: 'reviewPrompt',
56
+ message: `Do you want to review the ~${tokenCount} token prompt before sending it to ${endpoint}?`,
57
+ default: true
58
+ }
59
+ ]);
60
+ if (reviewPrompt) {
61
+ const less = (0, child_process_1.spawn)('less', ['-R'], { stdio: ['pipe', process_1.default.stdout, process_1.default.stderr] });
62
+ less.stdin.write(prompt);
63
+ less.stdin.end();
64
+ await new Promise((resolve, reject) => {
65
+ less.on('close', async (code) => {
66
+ if (code !== 0) {
67
+ console.error('less command exited with an error');
68
+ reject(new Error('Less command failed'));
69
+ return;
70
+ }
71
+ const { confirmContinue } = await inquirer_1.default.prompt([
72
+ {
73
+ type: 'confirm',
74
+ name: 'confirmContinue',
75
+ message: 'Do you want to continue?',
76
+ default: true
92
77
  }
93
- });
94
- }
95
- else {
96
- resolve(true);
97
- }
78
+ ]);
79
+ if (!confirmContinue) {
80
+ console.error('User cancelled the operation');
81
+ process_1.default.exit(1);
82
+ }
83
+ resolve();
84
+ });
85
+ less.stdin.on('error', (err) => {
86
+ if (isNodeError(err) && err.code === 'EPIPE') {
87
+ resolve();
88
+ }
89
+ else {
90
+ console.error('Pipeline error:', err);
91
+ reject(err);
92
+ }
93
+ });
98
94
  });
99
- });
95
+ }
100
96
  }
101
97
  async generateText(request) {
102
98
  const endpoint = this.adapter.getEndpoint();
103
99
  const payload = this.adapter.getPayload(request);
104
100
  const headers = this.adapter.getHeaders(this.apiKey);
105
- const tokenCount = Math.ceil(request.prompt.length / 4).toLocaleString();
106
- const shouldContinue = await this.askUserToConfirm(request.prompt, `Do you want to review the ~${tokenCount} token prompt before sending it to ${endpoint}? [Yn]: `);
107
- if (!shouldContinue) {
108
- throw 'Operation cancelled by the user.';
109
- }
101
+ await this.reviewPrompt(request.prompt, endpoint);
110
102
  try {
111
103
  const response = await axios_1.default.post(endpoint, payload, { headers });
112
104
  return this.adapter.extractResponse(response.data);
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.config = void 0;
4
7
  const config_utils_1 = require("../utils/config_utils");
5
8
  const command_utils_1 = require("../utils/command_utils");
9
+ const inquirer_1 = __importDefault(require("inquirer"));
6
10
  const config = async () => {
7
11
  let currentConfig = (0, config_utils_1.getConfig)({});
8
- // Ensure models is initialized properly
12
+ // Ensure models are initialized properly
9
13
  if (!currentConfig.models) {
10
14
  currentConfig.models = {};
11
15
  }
@@ -16,32 +20,92 @@ const config = async () => {
16
20
  }
17
21
  let continueConfig = true;
18
22
  while (continueConfig) {
19
- console.log('Available models:');
20
- models.forEach((model, index) => console.log(`${index + 1}. ${model}`));
21
- const modelIndex = parseInt(await (0, command_utils_1.promptUser)('Select a model by number: '), 10) - 1;
22
- if (isNaN(modelIndex) || modelIndex < 0 || modelIndex >= models.length) {
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}: `);
23
+ const providers = [...new Set(models.map(model => model.split(':')[0]))];
24
+ const { selectedProvider } = await inquirer_1.default.prompt([
25
+ {
26
+ type: 'list',
27
+ name: 'selectedProvider',
28
+ message: 'Select a provider:',
29
+ choices: providers.map(provider => ({ name: provider, value: provider })),
30
+ },
31
+ ]);
32
+ const providerModels = models.filter(model => model.startsWith(selectedProvider));
33
+ const modelCategories = [...new Set(providerModels.map(model => model.split(':')[1]))];
34
+ const { selectedCategory } = await inquirer_1.default.prompt([
35
+ {
36
+ type: 'list',
37
+ name: 'selectedCategory',
38
+ message: 'Select a category:',
39
+ choices: modelCategories.map(category => ({ name: category, value: category })),
40
+ },
41
+ ]);
42
+ const specificModels = providerModels.filter(model => model.split(':')[1] === selectedCategory);
43
+ const { modelIndex } = await inquirer_1.default.prompt([
44
+ {
45
+ type: 'list',
46
+ name: 'modelIndex',
47
+ message: 'Select an AI model you\'d like to use:',
48
+ choices: specificModels.map((model, index) => ({ name: model, value: index })),
49
+ pageSize: specificModels.length,
50
+ },
51
+ ]);
52
+ const selectedModel = specificModels[modelIndex];
53
+ const { apiKey } = await inquirer_1.default.prompt([
54
+ {
55
+ type: 'input',
56
+ name: 'apiKey',
57
+ message: `Enter your API key for ${selectedModel}: `,
58
+ },
59
+ ]);
27
60
  const [provider, ...modelParts] = selectedModel.split(':');
28
61
  const model = modelParts.join(':');
29
62
  if (!currentConfig.models[provider]) {
30
63
  currentConfig.models[provider] = {};
31
64
  }
32
- const setAsPrimary = await (0, command_utils_1.promptUser)('Do you want to use this model as a primary model? [Yn]: ');
33
- const primary = setAsPrimary.toLowerCase() === 'y' || setAsPrimary === '';
34
- const priorityNumber = await (0, command_utils_1.promptUser)('Enter the priority for this model (0-100, higher priority will be used as long as the prompt fits the context token limit): [' + (primary ? 100 : 0) + ']');
35
- const priority = primary ? 100 : parseInt(priorityNumber, 10);
36
- const silentResponse = await (0, command_utils_1.promptUser)('Do you want to review all prompts before they are sent to this model? [Yn]: ');
37
- const silent = !(silentResponse.toLowerCase() === 'y' || silentResponse === '');
38
- const trustResponse = await (0, command_utils_1.promptUser)('Do you trust this model with sensitive information? [Yn]: ');
39
- const trust = trustResponse.toLowerCase() === 'y' || trustResponse === '';
65
+ const { primary } = await inquirer_1.default.prompt([
66
+ {
67
+ type: 'confirm',
68
+ name: 'primary',
69
+ message: 'Set this model as the primary? Primary models are used by default if your prompt fits within its context window. You can configure backup models for larger prompts.',
70
+ default: true,
71
+ },
72
+ ]);
73
+ const { priority } = await inquirer_1.default.prompt([
74
+ {
75
+ type: 'input',
76
+ name: 'priority',
77
+ message: 'Enter the priority for this model (0-100). Higher priority AI APIs are chosen as long as the prompt fits its context window:',
78
+ default: primary ? '100' : '0',
79
+ validate: (input) => {
80
+ const num = parseInt(input, 10);
81
+ if (isNaN(num) || num < 0 || num > 100) {
82
+ return 'Please enter a valid number between 0 and 100.';
83
+ }
84
+ return true;
85
+ },
86
+ },
87
+ ]);
88
+ const { review } = await inquirer_1.default.prompt([
89
+ {
90
+ type: 'confirm',
91
+ name: 'review',
92
+ message: `Do you want to review all prompts before they are sent to ${selectedModel}?`,
93
+ default: true,
94
+ },
95
+ ]);
96
+ const { trust } = await inquirer_1.default.prompt([
97
+ {
98
+ type: 'confirm',
99
+ name: 'trust',
100
+ message: `Do you trust ${selectedModel} with sensitive information?`,
101
+ default: false,
102
+ },
103
+ ]);
40
104
  const modelConfig = {
41
105
  apiKey,
42
106
  primary,
43
- priority,
44
- silent,
107
+ priority: parseInt(priority, 10),
108
+ silent: !review,
45
109
  trust,
46
110
  };
47
111
  currentConfig.models[provider][model] = modelConfig;
@@ -55,10 +119,15 @@ const config = async () => {
55
119
  }
56
120
  }
57
121
  }
58
- const anotherModel = await (0, command_utils_1.promptUser)('Do you want to configure another model? [Yn]: ');
59
- if (anotherModel.toLowerCase() !== 'y' && anotherModel !== '') {
60
- continueConfig = false;
61
- }
122
+ const { anotherModel } = await inquirer_1.default.prompt([
123
+ {
124
+ type: 'confirm',
125
+ name: 'anotherModel',
126
+ message: 'Do you want to configure another model?',
127
+ default: true,
128
+ },
129
+ ]);
130
+ continueConfig = anotherModel;
62
131
  }
63
132
  (0, config_utils_1.saveConfig)(currentConfig);
64
133
  };
@@ -0,0 +1,187 @@
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 copy_paste_1 = __importDefault(require("copy-paste"));
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const cli_highlight_1 = __importDefault(require("cli-highlight"));
9
+ const command_utils_1 = require("../../utils/command_utils");
10
+ const config_utils_1 = require("../../utils/config_utils");
11
+ const string_utils_1 = require("../../utils/string_utils");
12
+ const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const generateCodePrompt = (prompt, files, contextFiles, previousCode) => {
16
+ const specialFileContents = (0, command_utils_1.collectSpecialFileContents)();
17
+ const specialFiles = Object.keys(specialFileContents).map((file) => `### Contents of ${file}\n\n${JSON.stringify(specialFileContents[file], null, 2)}\n\n`).join('\n');
18
+ const contextFileContents = Object.keys(contextFiles).map((file) => `### Contents of ${file}\n\n${contextFiles[file]}\n\n`).join('\n');
19
+ let promptText = `Here is a description of my app:
20
+
21
+ ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
22
+
23
+ ${specialFiles}
24
+
25
+ ${contextFileContents}
26
+
27
+ ### Directory structure
28
+
29
+ ${files.join('\n')}
30
+
31
+ `;
32
+ if (previousCode) {
33
+ promptText += `### Previously generated code
34
+
35
+ ${previousCode}
36
+
37
+ `;
38
+ }
39
+ promptText += `### Request
40
+
41
+ I would like to generate code that does the following: ${prompt}
42
+
43
+ Please generate the code and include filenames with paths to the code files mentioned and do not be lazy and ask me to keep the existing code or show things like previous code remains unchanged, always include existing code in the response.`;
44
+ return promptText;
45
+ };
46
+ const generateExplainCodePrompt = (prompt) => {
47
+ return `${prompt}
48
+
49
+ Please briefly explain how this code works.`;
50
+ };
51
+ const generateCode = async (gpt, prompt, allSrcFiles, contextFiles, previousCode) => {
52
+ const codePrompt = generateCodePrompt(prompt, allSrcFiles, contextFiles, previousCode);
53
+ return await gpt.generateText({ prompt: codePrompt });
54
+ };
55
+ const displayGeneratedCode = (rawCodeCommand) => {
56
+ (0, string_utils_1.parseMarkdownInstructions)(rawCodeCommand).map(code => {
57
+ if (code.trim().startsWith('```')) {
58
+ const lines = code.split('\n');
59
+ const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
60
+ console.log(lines[0]);
61
+ console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
62
+ console.log(lines.slice(-1)[0]);
63
+ }
64
+ else {
65
+ console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
66
+ }
67
+ });
68
+ };
69
+ const handleUserAction = async (gpt, rawCodeCommand, prompt, allSrcFiles, contextFiles) => {
70
+ const { action } = await inquirer_1.default.prompt([
71
+ {
72
+ type: 'list',
73
+ name: 'action',
74
+ message: 'What would you like to do?',
75
+ choices: [
76
+ { name: 'Revise', value: 'revise' },
77
+ { name: 'Explain', value: 'explain' },
78
+ { name: 'Copy Source Code to Clipboard', value: 'copySource' },
79
+ { name: 'Copy Entire Response to Clipboard', value: 'copyAll' },
80
+ { name: 'Cancel', value: 'cancel' },
81
+ ],
82
+ },
83
+ ]);
84
+ switch (action) {
85
+ case 'revise':
86
+ const { newPrompt } = await inquirer_1.default.prompt([
87
+ {
88
+ type: 'input',
89
+ name: 'newPrompt',
90
+ message: 'Enter your revised prompt:',
91
+ },
92
+ ]);
93
+ const newRawCodeCommand = await generateCode(gpt, newPrompt, allSrcFiles, contextFiles, rawCodeCommand);
94
+ displayGeneratedCode(newRawCodeCommand);
95
+ await handleUserAction(gpt, newRawCodeCommand, newPrompt, allSrcFiles, contextFiles);
96
+ break;
97
+ case 'explain':
98
+ const explainPrompt = generateExplainCodePrompt(rawCodeCommand);
99
+ const explainCodeCommand = await gpt.generateText({ prompt: explainPrompt });
100
+ console.log((0, cli_highlight_1.default)(explainCodeCommand, { language: 'markdown' }));
101
+ break;
102
+ case 'copySource':
103
+ const sourceCode = (0, string_utils_1.extractMarkdown)(rawCodeCommand);
104
+ copy_paste_1.default.copy(sourceCode, () => {
105
+ console.log('Source code to clipboard');
106
+ });
107
+ break;
108
+ case 'copyAll':
109
+ copy_paste_1.default.copy(rawCodeCommand, () => {
110
+ console.log('Entire response copied to clipboard');
111
+ });
112
+ break;
113
+ case 'cancel':
114
+ console.log('Operation cancelled.');
115
+ break;
116
+ }
117
+ };
118
+ const code = async (options) => {
119
+ let { prompt, files } = options;
120
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
121
+ const gpt = new cloving_gpt_1.default(options);
122
+ const allSrcFiles = await (0, config_utils_1.getAllFiles)(options, true);
123
+ const contextFiles = {};
124
+ try {
125
+ if (!prompt) {
126
+ const { userPrompt } = await inquirer_1.default.prompt([
127
+ {
128
+ type: 'input',
129
+ name: 'userPrompt',
130
+ message: 'What would you like to build:',
131
+ },
132
+ ]);
133
+ prompt = userPrompt;
134
+ }
135
+ if (files && files.length > 0) {
136
+ // Check if the files exist
137
+ const nonExistentFiles = files.filter(file => !fs_1.default.existsSync(file));
138
+ if (nonExistentFiles.length > 0) {
139
+ throw new Error(`The following files do not exist: ${nonExistentFiles.join(', ')}`);
140
+ }
141
+ // Read the content of the provided files
142
+ for (const file of files) {
143
+ const content = fs_1.default.readFileSync(file, 'utf-8');
144
+ contextFiles[file] = content;
145
+ }
146
+ }
147
+ else {
148
+ // Prompt for additional context files if no files were passed in options
149
+ let includeMoreFiles = true;
150
+ while (includeMoreFiles) {
151
+ const { contextFile } = await inquirer_1.default.prompt([
152
+ {
153
+ type: 'input',
154
+ name: 'contextFile',
155
+ message: 'Relative path of an existing file you would like to include as context in the prompt [optional]:',
156
+ },
157
+ ]);
158
+ if (contextFile) {
159
+ const filePath = path_1.default.resolve(contextFile);
160
+ if (fs_1.default.existsSync(filePath)) {
161
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
162
+ contextFiles[filePath] = content;
163
+ }
164
+ else {
165
+ console.log(`File ${contextFile} does not exist.`);
166
+ }
167
+ }
168
+ const { includeMore } = await inquirer_1.default.prompt([
169
+ {
170
+ type: 'confirm',
171
+ name: 'includeMore',
172
+ message: 'Do you want to include any other files?',
173
+ default: false,
174
+ },
175
+ ]);
176
+ includeMoreFiles = includeMore;
177
+ }
178
+ }
179
+ const rawCodeCommand = await generateCode(gpt, prompt, allSrcFiles, contextFiles);
180
+ displayGeneratedCode(rawCodeCommand);
181
+ await handleUserAction(gpt, rawCodeCommand, prompt, allSrcFiles, contextFiles);
182
+ }
183
+ catch (error) {
184
+ console.error('Could not generate code', error);
185
+ }
186
+ };
187
+ exports.default = code;
@@ -6,16 +6,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const fs_1 = __importDefault(require("fs"));
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const child_process_1 = require("child_process");
9
- const git_utils_1 = require("../../utils/git_utils");
10
9
  const string_utils_1 = require("../../utils/string_utils");
11
10
  const config_utils_1 = require("../../utils/config_utils");
12
11
  const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
13
- const generateAndCommitMessage = async (options) => {
12
+ const generateCommitMessagePrompt = (diff) => {
13
+ return `Generate a concise and meaningful commit message based on a diff.
14
+
15
+ Do not add any commentary or context to the message other than the commit message itself.
16
+
17
+ An example of the output for this should look like the following:
18
+
19
+ \`\`\`plaintext
20
+ Update dependencies and package versions
21
+
22
+ - Upgrade Ruby gems including aws-sdk, honeybadger, irb, and rubocop
23
+ - Update Node.js packages including esbuild, tinymce, and trix
24
+ - Bump TypeScript and ESLint related packages to latest versions
25
+ \`\`\`
26
+
27
+ Here is the diff to help you write the commit message:
28
+
29
+ ${diff}`;
30
+ };
31
+ const commit = async (options) => {
14
32
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
15
33
  const gpt = new cloving_gpt_1.default(options);
16
34
  try {
35
+ // Get the git diff
36
+ const diff = (0, child_process_1.execSync)('git diff HEAD').toString().trim();
37
+ // Check if the diff is blank
38
+ if (!diff) {
39
+ console.error('No changes to commit.');
40
+ return;
41
+ }
17
42
  // Generate the prompt for commit message
18
- const prompt = (0, git_utils_1.generateCommitMessagePrompt)();
43
+ const prompt = generateCommitMessagePrompt(diff);
19
44
  // Instantiate ClovingGPT and get the commit message
20
45
  const rawCommitMessage = await gpt.generateText({ prompt });
21
46
  // Clean the commit message using extractMarkdown
@@ -38,7 +63,7 @@ const generateAndCommitMessage = async (options) => {
38
63
  });
39
64
  }
40
65
  catch (error) {
41
- console.error('Could not generate commit message');
66
+ console.error('Could not generate commit message:', error);
42
67
  }
43
68
  };
44
- exports.default = generateAndCommitMessage;
69
+ exports.default = commit;
@@ -5,12 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const child_process_1 = require("child_process");
7
7
  const cli_highlight_1 = __importDefault(require("cli-highlight"));
8
+ const inquirer_1 = __importDefault(require("inquirer"));
8
9
  const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
9
10
  const git_utils_1 = require("../../utils/git_utils");
10
11
  const string_utils_1 = require("../../utils/string_utils");
11
12
  const config_utils_1 = require("../../utils/config_utils");
12
13
  const string_utils_2 = require("../../utils/string_utils");
13
- const command_utils_1 = require("../../utils/command_utils");
14
14
  const review = async (options) => {
15
15
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
16
16
  const gpt = new cloving_gpt_1.default(options);
@@ -39,8 +39,15 @@ ${gitDiff}`;
39
39
  }
40
40
  });
41
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 === '') {
42
+ const { copyToClipboard } = await inquirer_1.default.prompt([
43
+ {
44
+ type: 'confirm',
45
+ name: 'copyToClipboard',
46
+ message: 'Do you want to copy the analysis to the clipboard?',
47
+ default: true
48
+ }
49
+ ]);
50
+ if (copyToClipboard) {
44
51
  try {
45
52
  (0, child_process_1.execFileSync)('pbcopy', { input: markdown });
46
53
  console.log('Analysis copied to clipboard');