@vedanshsharma/commit-gen 1.0.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/README.md +80 -0
- package/bin/commitgen.js +5 -0
- package/package.json +42 -0
- package/src/config.js +39 -0
- package/src/gemini.js +40 -0
- package/src/git.js +44 -0
- package/src/index.js +112 -0
- package/src/ollama.js +34 -0
- package/src/prompt.js +36 -0
- package/src/ui.js +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# 🚀 Commit Message Generator (commitgen)
|
|
2
|
+
|
|
3
|
+
A lightning-fast, offline-first CLI tool that reads your staged git diffs and automatically generates professional Conventional Commits using locally running LLMs (via Ollama).
|
|
4
|
+
|
|
5
|
+
*(Replace this text with a Demo GIF once recorded)*
|
|
6
|
+
|
|
7
|
+
## ✨ Why this tool?
|
|
8
|
+
|
|
9
|
+
- **100% Offline & Private:** Uses local LLMs. Your source code never leaves your machine.
|
|
10
|
+
- **Zero API Costs:** No OpenAI/Anthropic API keys needed.
|
|
11
|
+
- **Professional History:** Strictly adheres to the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
12
|
+
- **Lightning Fast:** Generates and commits in seconds without disrupting your terminal flow.
|
|
13
|
+
|
|
14
|
+
## 🛠 Prerequisites
|
|
15
|
+
|
|
16
|
+
1. **Node.js** (v18+ recommended for native `fetch`).
|
|
17
|
+
2. **Ollama** - You must have [Ollama](https://ollama.com/) installed and running locally.
|
|
18
|
+
3. **LLM Model** - Download your preferred model (default is `mistral`):
|
|
19
|
+
```bash
|
|
20
|
+
ollama pull mistral
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 📦 Installation
|
|
24
|
+
|
|
25
|
+
To install this tool globally on your machine so you can use it in any repository:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Clone the repository
|
|
29
|
+
git clone https://github.com/VSDeadShot/commit-gen.git
|
|
30
|
+
cd commit-gen
|
|
31
|
+
|
|
32
|
+
# Install dependencies and link globally
|
|
33
|
+
npm install
|
|
34
|
+
npm link
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 🚀 Usage
|
|
38
|
+
|
|
39
|
+
Whenever you have changes ready to commit:
|
|
40
|
+
|
|
41
|
+
1. Stage your files as usual:
|
|
42
|
+
```bash
|
|
43
|
+
git add <files>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
2. Run the CLI:
|
|
47
|
+
```bash
|
|
48
|
+
commitgen
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. The AI-generated commit message will be presented in a styled box. You can then:
|
|
52
|
+
- **Accept and Commit** instantly
|
|
53
|
+
- **Regenerate** if you want a different variation
|
|
54
|
+
- **Edit Manually** to tweak the generated message inline
|
|
55
|
+
- **Cancel** to abort without committing
|
|
56
|
+
|
|
57
|
+
### Command Options
|
|
58
|
+
|
|
59
|
+
- **Change Model**: Specify a different Ollama model (defaults to `mistral`).
|
|
60
|
+
```bash
|
|
61
|
+
commitgen --model llama3.2
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- **Dry Run**: Generate and preview the message without actually running `git commit`.
|
|
65
|
+
```bash
|
|
66
|
+
commitgen --dry-run
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **Gemini Fallback**: Use the Gemini API instead of local Ollama (requires the `GEMINI_API_KEY` environment variable).
|
|
70
|
+
```bash
|
|
71
|
+
commitgen --gemini
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 🏗 Tech Stack
|
|
75
|
+
|
|
76
|
+
- **JavaScript / Node.js ES Modules**
|
|
77
|
+
- **Commander.js** - CLI command orchestration
|
|
78
|
+
- **Inquirer.js (Classic)** - Interactive prompts
|
|
79
|
+
- **Chalk** - Premium terminal styling
|
|
80
|
+
- **Native Node Fetch & Child_Process** - Zero bloat system integration
|
package/bin/commitgen.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vedanshsharma/commit-gen",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI to generate Conventional Commits using local LLMs or Gemini",
|
|
5
|
+
"bin": {
|
|
6
|
+
"commitgen": "bin/commitgen.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "src/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/VSDeadShot/commit-gen.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"git",
|
|
18
|
+
"commit",
|
|
19
|
+
"cli",
|
|
20
|
+
"ai",
|
|
21
|
+
"ollama",
|
|
22
|
+
"conventional-commits"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"files": [
|
|
28
|
+
"bin",
|
|
29
|
+
"src",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/VSDeadShot/commit-gen/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/VSDeadShot/commit-gen#readme",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"chalk": "^5.6.2",
|
|
38
|
+
"clipboardy": "^5.3.1",
|
|
39
|
+
"commander": "^15.0.0",
|
|
40
|
+
"inquirer": "^14.0.2"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
const CONFIG_DIR = path.join(os.homedir(), '.commitgen');
|
|
6
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_CONFIG = {
|
|
9
|
+
model: 'mistral'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reads the configuration file or returns defaults if it doesn't exist.
|
|
14
|
+
*/
|
|
15
|
+
export async function getConfig() {
|
|
16
|
+
try {
|
|
17
|
+
const data = await fs.readFile(CONFIG_FILE, 'utf-8');
|
|
18
|
+
return JSON.parse(data);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
// If file doesn't exist or is invalid, return defaults
|
|
21
|
+
return DEFAULT_CONFIG;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Updates the configuration file with new values.
|
|
27
|
+
*/
|
|
28
|
+
export async function setConfig(newConfig) {
|
|
29
|
+
try {
|
|
30
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
31
|
+
|
|
32
|
+
const currentConfig = await getConfig();
|
|
33
|
+
const mergedConfig = { ...currentConfig, ...newConfig };
|
|
34
|
+
|
|
35
|
+
await fs.writeFile(CONFIG_FILE, JSON.stringify(mergedConfig, null, 2));
|
|
36
|
+
} catch (error) {
|
|
37
|
+
throw new Error('Failed to save configuration: ' + error.message);
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/gemini.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sends the prompt to the Gemini API and returns the generated commit message.
|
|
3
|
+
* @param {string} prompt The full prompt containing the diff and instructions
|
|
4
|
+
* @returns {Promise<string>} The generated commit message
|
|
5
|
+
*/
|
|
6
|
+
export async function generateCommitMessageGemini(prompt) {
|
|
7
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
throw new Error('GEMINI_API_KEY environment variable is not set.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: {
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
contents: [{
|
|
20
|
+
parts: [{ text: prompt }]
|
|
21
|
+
}]
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const errBody = await response.text();
|
|
27
|
+
throw new Error(`Gemini API returned status ${response.status}: ${errBody}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const data = await response.json();
|
|
31
|
+
|
|
32
|
+
if (!data.candidates || data.candidates.length === 0) {
|
|
33
|
+
throw new Error('Gemini API returned no candidates.');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return data.candidates[0].content.parts[0].text.trim();
|
|
37
|
+
} catch (error) {
|
|
38
|
+
throw new Error('Failed to generate commit message via Gemini: ' + error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/git.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import util from 'util';
|
|
3
|
+
|
|
4
|
+
const execFilePromise = util.promisify(execFile);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Checks if the current working directory is inside a git repository.
|
|
8
|
+
* @returns {Promise<boolean>}
|
|
9
|
+
*/
|
|
10
|
+
export async function isGitRepo() {
|
|
11
|
+
try {
|
|
12
|
+
await execFilePromise('git', ['rev-parse', '--is-inside-work-tree']);
|
|
13
|
+
return true;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Gets the currently staged git diff.
|
|
21
|
+
* @returns {Promise<string>} The diff output
|
|
22
|
+
*/
|
|
23
|
+
export async function getStagedDiff() {
|
|
24
|
+
try {
|
|
25
|
+
const { stdout } = await execFilePromise('git', ['diff', '--staged']);
|
|
26
|
+
return stdout.trim();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new Error('Failed to get staged diff: ' + error.message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Commits the staged changes with the provided message.
|
|
34
|
+
* @param {string} message The commit message
|
|
35
|
+
* @returns {Promise<string>} stdout from git commit
|
|
36
|
+
*/
|
|
37
|
+
export async function commitChanges(message) {
|
|
38
|
+
try {
|
|
39
|
+
const { stdout } = await execFilePromise('git', ['commit', '-m', message]);
|
|
40
|
+
return stdout.trim();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error('Failed to commit changes: ' + error.message);
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { isGitRepo, getStagedDiff, commitChanges } from './git.js';
|
|
4
|
+
import { buildPrompt } from './prompt.js';
|
|
5
|
+
import { generateCommitMessage } from './ollama.js';
|
|
6
|
+
import { generateCommitMessageGemini } from './gemini.js';
|
|
7
|
+
import { displayMessage, promptUserAction, promptManualEdit, promptConfigMenu } from './ui.js';
|
|
8
|
+
import { getConfig, setConfig } from './config.js';
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('commitgen')
|
|
14
|
+
.description('CLI to generate Conventional Commits using local LLMs')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
|
|
17
|
+
program.command('config')
|
|
18
|
+
.description('Configure default settings')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
try {
|
|
21
|
+
const config = await getConfig();
|
|
22
|
+
console.log(chalk.magenta.bold('\n⚙️ Commitgen Configuration\n'));
|
|
23
|
+
const answers = await promptConfigMenu(config.model);
|
|
24
|
+
await setConfig(answers);
|
|
25
|
+
console.log(chalk.green.bold('\n✅ Configuration saved successfully to ~/.commitgen/config.json!\n'));
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.log('\n' + chalk.red.bold('❌ Error: ') + error.message);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
program
|
|
33
|
+
.option('-m, --model <name>', 'Ollama model to use')
|
|
34
|
+
.option('--dry-run', "show generated message but don't commit")
|
|
35
|
+
.option('--gemini', "use Gemini API instead of local Ollama")
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
try {
|
|
38
|
+
const config = await getConfig();
|
|
39
|
+
const selectedModel = options.model || config.model;
|
|
40
|
+
|
|
41
|
+
// 1. Verify we are in a git repository
|
|
42
|
+
const isRepo = await isGitRepo();
|
|
43
|
+
if (!isRepo) {
|
|
44
|
+
console.log(chalk.red.bold('❌ Error:') + ' You are not inside a git repository.');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 2. Get staged changes
|
|
49
|
+
const diff = await getStagedDiff();
|
|
50
|
+
if (!diff) {
|
|
51
|
+
console.log(chalk.yellow('⚠️ No staged changes found.'));
|
|
52
|
+
console.log(chalk.gray('Run `git add <files>` and try again.'));
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 3. Main interactive loop
|
|
57
|
+
while (true) {
|
|
58
|
+
console.log(chalk.gray('\nAnalyzing staged diff and generating message...'));
|
|
59
|
+
|
|
60
|
+
const prompt = buildPrompt(diff);
|
|
61
|
+
|
|
62
|
+
let message;
|
|
63
|
+
if (options.gemini) {
|
|
64
|
+
if (!process.env.GEMINI_API_KEY) {
|
|
65
|
+
console.log(chalk.red.bold('\n❌ Error: ') + 'GEMINI_API_KEY environment variable is missing.');
|
|
66
|
+
console.log(chalk.gray('Set it using: $env:GEMINI_API_KEY="your_key" (PowerShell) or export GEMINI_API_KEY="your_key" (Mac/Linux)\n'));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
message = await generateCommitMessageGemini(prompt);
|
|
70
|
+
} else {
|
|
71
|
+
message = await generateCommitMessage(prompt, selectedModel);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
displayMessage(message);
|
|
75
|
+
|
|
76
|
+
if (options.dryRun) {
|
|
77
|
+
console.log(chalk.gray('Dry run complete. No changes were committed.\n'));
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const action = await promptUserAction();
|
|
82
|
+
|
|
83
|
+
if (action === 'accept') {
|
|
84
|
+
await commitChanges(message);
|
|
85
|
+
console.log(chalk.green.bold('\n✅ Successfully committed!'));
|
|
86
|
+
break;
|
|
87
|
+
} else if (action === 'regenerate') {
|
|
88
|
+
// Just continue the loop to regenerate
|
|
89
|
+
continue;
|
|
90
|
+
} else if (action === 'edit') {
|
|
91
|
+
const edited = await promptManualEdit(message);
|
|
92
|
+
if (edited && edited.trim() !== '') {
|
|
93
|
+
await commitChanges(edited.trim());
|
|
94
|
+
console.log(chalk.green.bold('\n✅ Successfully committed with your edits!'));
|
|
95
|
+
} else {
|
|
96
|
+
console.log(chalk.red('\n❌ Commit aborted: Message cannot be empty.'));
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
} else if (action === 'cancel') {
|
|
100
|
+
console.log(chalk.gray('\nProcess cancelled. No changes committed.'));
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.log('\n' + chalk.red.bold('❌ Error: ') + error.message);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export function run() {
|
|
111
|
+
program.parse(process.argv);
|
|
112
|
+
}
|
package/src/ollama.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sends the prompt to the local Ollama instance and returns the generated commit message.
|
|
3
|
+
* @param {string} prompt The full prompt containing the diff and instructions
|
|
4
|
+
* @param {string} model The Ollama model to use (default: 'mistral')
|
|
5
|
+
* @returns {Promise<string>} The generated commit message
|
|
6
|
+
*/
|
|
7
|
+
export async function generateCommitMessage(prompt, model = 'mistral') {
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetch('http://localhost:11434/api/generate', {
|
|
10
|
+
method: 'POST',
|
|
11
|
+
headers: {
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
},
|
|
14
|
+
body: JSON.stringify({
|
|
15
|
+
model: model,
|
|
16
|
+
prompt: prompt,
|
|
17
|
+
stream: false, // We want the full response at once
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new Error(`Ollama API returned status ${response.status}: ${response.statusText}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const data = await response.json();
|
|
26
|
+
return data.response.trim();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// Handle connection refused specifically to give a friendly error
|
|
29
|
+
if (error.code === 'ECONNREFUSED' || (error.cause && error.cause.code === 'ECONNREFUSED')) {
|
|
30
|
+
throw new Error('Could not connect to Ollama. Please make sure Ollama is running locally on port 11434.');
|
|
31
|
+
}
|
|
32
|
+
throw new Error('Failed to generate commit message: ' + error.message);
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/prompt.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const SYSTEM_PROMPT = `You are a git commit message generator. You follow the Conventional Commits
|
|
2
|
+
specification exactly. Analyze the provided git diff and generate ONE commit
|
|
3
|
+
message. Rules:
|
|
4
|
+
- Format: type(scope): description
|
|
5
|
+
- Type must be one of: feat, fix, refactor, docs, style, test, chore
|
|
6
|
+
- Scope is the main file or module changed (optional but preferred)
|
|
7
|
+
- Description is lowercase, present tense, under 72 characters, no period
|
|
8
|
+
- Return ONLY the commit message, nothing else, no explanation, no markdown
|
|
9
|
+
- Do NOT wrap the output in a code block.`;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Cleans and truncates the diff to ensure it fits within the context window.
|
|
13
|
+
* @param {string} diff The raw git diff
|
|
14
|
+
* @param {number} maxLength The maximum allowed length for the diff (default 3500)
|
|
15
|
+
* @returns {string} The processed diff
|
|
16
|
+
*/
|
|
17
|
+
export function truncateDiff(diff, maxLength = 3500) {
|
|
18
|
+
if (!diff) return '';
|
|
19
|
+
|
|
20
|
+
if (diff.length <= maxLength) {
|
|
21
|
+
return diff;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return diff.substring(0, maxLength) + '\n\n... [DIFF TRUNCATED]';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds the final prompt to be sent to the LLM.
|
|
29
|
+
* @param {string} rawDiff The raw git diff from the staged files
|
|
30
|
+
* @returns {string} The complete prompt
|
|
31
|
+
*/
|
|
32
|
+
export function buildPrompt(rawDiff) {
|
|
33
|
+
const diff = truncateDiff(rawDiff);
|
|
34
|
+
|
|
35
|
+
return `${SYSTEM_PROMPT}\n\nHere is the git diff:\n\`\`\`diff\n${diff}\n\`\`\`\n\nGenerate the commit message now:`;
|
|
36
|
+
}
|
package/src/ui.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Displays the generated commit message with a premium, clean aesthetic.
|
|
6
|
+
* @param {string} message The commit message to display
|
|
7
|
+
*/
|
|
8
|
+
export function displayMessage(message) {
|
|
9
|
+
console.log('\n' + chalk.magenta.bold('✨ Generated Commit Message ✨'));
|
|
10
|
+
console.log(chalk.gray('─────────────────────────────────────────────'));
|
|
11
|
+
console.log(chalk.cyanBright.bold(message));
|
|
12
|
+
console.log(chalk.gray('─────────────────────────────────────────────\n'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Prompts the user for their next action using a styled list.
|
|
17
|
+
* @returns {Promise<string>} The selected action ('accept', 'regenerate', 'edit', 'cancel')
|
|
18
|
+
*/
|
|
19
|
+
export async function promptUserAction() {
|
|
20
|
+
const { action } = await inquirer.prompt([
|
|
21
|
+
{
|
|
22
|
+
type: 'select',
|
|
23
|
+
name: 'action',
|
|
24
|
+
message: chalk.white.bold('What would you like to do?'),
|
|
25
|
+
choices: [
|
|
26
|
+
{ name: chalk.green('🚀 Accept and Commit'), value: 'accept' },
|
|
27
|
+
{ name: chalk.yellow('🔄 Regenerate Message'), value: 'regenerate' },
|
|
28
|
+
{ name: chalk.blue('✏️ Edit Manually'), value: 'edit' },
|
|
29
|
+
{ name: chalk.red('❌ Cancel Process'), value: 'cancel' }
|
|
30
|
+
],
|
|
31
|
+
prefix: chalk.magenta('❯') // Overrides the standard '?' prefix
|
|
32
|
+
}
|
|
33
|
+
]);
|
|
34
|
+
return action;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Prompts the user to edit the message manually inline.
|
|
39
|
+
* @param {string} currentMessage The default message to pre-fill
|
|
40
|
+
* @returns {Promise<string>} The edited message
|
|
41
|
+
*/
|
|
42
|
+
export async function promptManualEdit(currentMessage) {
|
|
43
|
+
const { editedMessage } = await inquirer.prompt([
|
|
44
|
+
{
|
|
45
|
+
type: 'input',
|
|
46
|
+
name: 'editedMessage',
|
|
47
|
+
message: chalk.white.bold('Edit your commit message:'),
|
|
48
|
+
default: currentMessage,
|
|
49
|
+
prefix: chalk.magenta('❯')
|
|
50
|
+
}
|
|
51
|
+
]);
|
|
52
|
+
return editedMessage;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Prompts the user to configure their default settings.
|
|
57
|
+
* @param {string} currentModel The current default model
|
|
58
|
+
* @returns {Promise<{model: string}>} The new config answers
|
|
59
|
+
*/
|
|
60
|
+
export async function promptConfigMenu(currentModel) {
|
|
61
|
+
return await inquirer.prompt([
|
|
62
|
+
{
|
|
63
|
+
type: 'input',
|
|
64
|
+
name: 'model',
|
|
65
|
+
message: chalk.white.bold('Enter your preferred default Ollama model:'),
|
|
66
|
+
default: currentModel,
|
|
67
|
+
prefix: chalk.magenta('❯')
|
|
68
|
+
}
|
|
69
|
+
]);
|
|
70
|
+
}
|