feat: improve AI commit experience and remove message length limits
- Remove 72-char limit on AI-generated commit messages (no more ... truncation) - Add [Y/n] confirmation prompt when using local heuristics (no AI provider) - Default to Y (press Enter to accept) - Clearly warns user about local heuristics vs AI - Bypass with --yes flag for automation - Enhanced transparency: users now know when AI is used vs local pattern matching - Safer commits when API keys are not configured - Bumped version to 0.6.6
This commit is contained in:
@@ -1,5 +1,21 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.6.6] - 2025-10-26
|
||||||
|
|
||||||
|
### 🔧 Improvements
|
||||||
|
- **Removed commit message length restriction**: AI can now generate commit messages of any length (no more `...` truncation at 72 chars)
|
||||||
|
- **Added confirmation prompt for local heuristics**: When no AI provider is configured, the tool now asks for confirmation `[Y/n]` before committing
|
||||||
|
- Default is `Y` (just press Enter to accept)
|
||||||
|
- Warning message clearly indicates "local heuristics" instead of AI
|
||||||
|
- Can be bypassed with `--yes` flag for automation
|
||||||
|
- **Better user awareness**: Users are now informed when local heuristics are used instead of AI
|
||||||
|
|
||||||
|
### 📚 User Experience
|
||||||
|
- More transparent about when AI is used vs. local pattern matching
|
||||||
|
- Safer commits when API keys are not configured
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [0.6.5] - 2025-10-20
|
## [0.6.5] - 2025-10-20
|
||||||
|
|
||||||
### 🔧 Command Behavior Updated
|
### 🔧 Command Behavior Updated
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
# 🚀 GIMS Quick Reference
|
|
||||||
|
|
||||||
## Single-Letter Workflow Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
g s # Status - Enhanced git status with AI insights
|
|
||||||
g i # Init - Initialize a new Git repository
|
|
||||||
g p # Preview - See what will be committed
|
|
||||||
g l # Local - AI commit locally
|
|
||||||
g o # Online - AI commit + push
|
|
||||||
g ls # List - Short commit history
|
|
||||||
g ll # Large List - Detailed commit history
|
|
||||||
g h # History - Alias for list
|
|
||||||
g a # Amend - Merge changes into previous commit (keeps message)
|
|
||||||
g u # Undo - Undo last commit
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Choose your AI provider (one-time setup)
|
|
||||||
g setup --api-key gemini # 🚀 Recommended: Fast & free
|
|
||||||
g setup --api-key openai # 💎 High quality
|
|
||||||
g setup --api-key groq # ⚡ Ultra fast
|
|
||||||
|
|
||||||
# Or run full setup wizard
|
|
||||||
g setup
|
|
||||||
```
|
|
||||||
|
|
||||||
## Essential Workflow
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Check what's changed
|
|
||||||
g s
|
|
||||||
|
|
||||||
# 2. Commit with AI (choose one)
|
|
||||||
g int # Interactive mode (guided)
|
|
||||||
g o # One-command: commit + push
|
|
||||||
g l # Local commit only
|
|
||||||
|
|
||||||
# 3. View history
|
|
||||||
g ls # Recent commits (short)
|
|
||||||
g ll # Recent commits (detailed)
|
|
||||||
g h # Same as g ls
|
|
||||||
```
|
|
||||||
|
|
||||||
## Default AI Models
|
|
||||||
|
|
||||||
- **Gemini**: `gemini-2.5-flash` (Fast, free, recommended)
|
|
||||||
- **OpenAI**: `gpt-5` (Latest GPT model)
|
|
||||||
- **Groq**: `groq/compound` (Ultra-fast inference)
|
|
||||||
|
|
||||||
## Pro Tips
|
|
||||||
|
|
||||||
```bash
|
|
||||||
g sg --multiple # Get 3 AI suggestions
|
|
||||||
g p # Preview before committing
|
|
||||||
g a # Amend: merge changes to previous commit (keeps message)
|
|
||||||
g a --edit # Amend with new AI-generated message
|
|
||||||
g sync --rebase # Smart sync with rebase
|
|
||||||
g stash # Stash with AI description
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
```bash
|
|
||||||
g config --list # View all settings
|
|
||||||
g config --set conventional=true # Enable conventional commits
|
|
||||||
g config --set autoStage=true # Auto-stage changes
|
|
||||||
g config --set provider=gemini # Set AI provider
|
|
||||||
```
|
|
||||||
|
|
||||||
## Help
|
|
||||||
|
|
||||||
```bash
|
|
||||||
g --help # All commands
|
|
||||||
g q # This quick reference
|
|
||||||
```
|
|
||||||
+75
-6
@@ -90,7 +90,31 @@ async function safeLog() {
|
|||||||
|
|
||||||
|
|
||||||
async function generateCommitMessage(rawDiff, options = {}) {
|
async function generateCommitMessage(rawDiff, options = {}) {
|
||||||
return await aiProvider.generateCommitMessage(rawDiff, options);
|
const result = await aiProvider.generateCommitMessage(rawDiff, options);
|
||||||
|
// Return both message and whether local heuristics were used
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmCommit(message, isLocalHeuristic) {
|
||||||
|
if (!isLocalHeuristic) return true; // No confirmation needed for AI-generated messages
|
||||||
|
|
||||||
|
const readline = require('readline');
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(color.yellow('\n⚠️ No AI provider configured - using local heuristics'));
|
||||||
|
console.log(`Suggested commit: "${message}"`);
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
rl.question('Proceed with this commit? [Y/n]: ', (answer) => {
|
||||||
|
rl.close();
|
||||||
|
const trimmed = answer.trim().toLowerCase();
|
||||||
|
// Default to 'yes' if empty (just Enter pressed)
|
||||||
|
resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveCommit(input) {
|
async function resolveCommit(input) {
|
||||||
@@ -392,11 +416,19 @@ program.command('suggest').alias('sg')
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (opts.progressIndicators) Progress.start('🤖 Analyzing changes');
|
if (opts.progressIndicators) Progress.start('🤖 Analyzing changes');
|
||||||
const msg = await generateCommitMessage(rawDiff, opts);
|
const result = await generateCommitMessage(rawDiff, opts);
|
||||||
if (opts.progressIndicators) Progress.stop('');
|
if (opts.progressIndicators) Progress.stop('');
|
||||||
|
|
||||||
|
const msg = result.message || result; // Handle both old and new format
|
||||||
|
const usedLocal = result.usedLocal || false;
|
||||||
|
|
||||||
|
// Warn if using local heuristics
|
||||||
|
if (usedLocal) {
|
||||||
|
console.log(color.yellow('⚠️ No AI provider configured - using local heuristics'));
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.json) {
|
if (opts.json) {
|
||||||
const out = { message: msg };
|
const out = { message: msg, usedLocalHeuristics: usedLocal };
|
||||||
console.log(JSON.stringify(out));
|
console.log(JSON.stringify(out));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -446,8 +478,11 @@ program.command('local').alias('l')
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opts.progressIndicators) Progress.start('🤖 Generating commit message');
|
if (opts.progressIndicators) Progress.start('🤖 Generating commit message');
|
||||||
const msg = await generateCommitMessage(rawDiff, opts);
|
const result = await generateCommitMessage(rawDiff, opts);
|
||||||
if (opts.progressIndicators) Progress.stop('');
|
if (opts.progressIndicators) Progress.stop('');
|
||||||
|
|
||||||
|
const msg = result.message || result; // Handle both old and new format
|
||||||
|
const usedLocal = result.usedLocal || false;
|
||||||
|
|
||||||
if (opts.dryRun) {
|
if (opts.dryRun) {
|
||||||
console.log(color.yellow('[dry-run] Would commit with message:'));
|
console.log(color.yellow('[dry-run] Would commit with message:'));
|
||||||
@@ -455,6 +490,15 @@ program.command('local').alias('l')
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ask for confirmation if using local heuristics (unless --yes flag is set)
|
||||||
|
if (usedLocal && !opts.yes) {
|
||||||
|
const confirmed = await confirmCommit(msg, true);
|
||||||
|
if (!confirmed) {
|
||||||
|
Progress.info('Commit cancelled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.amend) {
|
if (opts.amend) {
|
||||||
await git.raw(['commit', '--amend', '-m', msg]);
|
await git.raw(['commit', '--amend', '-m', msg]);
|
||||||
Progress.success(`Amended commit: "${msg}"`);
|
Progress.success(`Amended commit: "${msg}"`);
|
||||||
@@ -496,8 +540,11 @@ program.command('online').alias('o')
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opts.progressIndicators) Progress.start('🤖 Generating commit message');
|
if (opts.progressIndicators) Progress.start('🤖 Generating commit message');
|
||||||
const msg = await generateCommitMessage(rawDiff, opts);
|
const result = await generateCommitMessage(rawDiff, opts);
|
||||||
if (opts.progressIndicators) Progress.stop('');
|
if (opts.progressIndicators) Progress.stop('');
|
||||||
|
|
||||||
|
const msg = result.message || result; // Handle both old and new format
|
||||||
|
const usedLocal = result.usedLocal || false;
|
||||||
|
|
||||||
if (opts.dryRun) {
|
if (opts.dryRun) {
|
||||||
console.log(color.yellow('[dry-run] Would commit & push with message:'));
|
console.log(color.yellow('[dry-run] Would commit & push with message:'));
|
||||||
@@ -505,6 +552,15 @@ program.command('online').alias('o')
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ask for confirmation if using local heuristics (unless --yes flag is set)
|
||||||
|
if (usedLocal && !opts.yes) {
|
||||||
|
const confirmed = await confirmCommit(msg, true);
|
||||||
|
if (!confirmed) {
|
||||||
|
Progress.info('Commit cancelled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Progress.info('Committing changes...');
|
Progress.info('Committing changes...');
|
||||||
if (opts.amend) {
|
if (opts.amend) {
|
||||||
await git.raw(['commit', '--amend', '-m', msg]);
|
await git.raw(['commit', '--amend', '-m', msg]);
|
||||||
@@ -723,10 +779,23 @@ program.command('amend').alias('a')
|
|||||||
|
|
||||||
if (cmdOptions.edit) {
|
if (cmdOptions.edit) {
|
||||||
// Generate new message for amend
|
// Generate new message for amend
|
||||||
|
const opts = getOpts();
|
||||||
Progress.start('🤖 Generating updated commit message');
|
Progress.start('🤖 Generating updated commit message');
|
||||||
const newMessage = await generateCommitMessage(rawDiff, getOpts());
|
const result = await generateCommitMessage(rawDiff, opts);
|
||||||
Progress.stop('');
|
Progress.stop('');
|
||||||
|
|
||||||
|
const newMessage = result.message || result; // Handle both old and new format
|
||||||
|
const usedLocal = result.usedLocal || false;
|
||||||
|
|
||||||
|
// Ask for confirmation if using local heuristics (unless --yes flag is set)
|
||||||
|
if (usedLocal && !opts.yes) {
|
||||||
|
const confirmed = await confirmCommit(newMessage, true);
|
||||||
|
if (!confirmed) {
|
||||||
|
Progress.info('Amend cancelled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await git.raw(['commit', '--amend', '-m', newMessage]);
|
await git.raw(['commit', '--amend', '-m', newMessage]);
|
||||||
Progress.success(`Amended commit: "${newMessage}"`);
|
Progress.success(`Amended commit: "${newMessage}"`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+10
-9
@@ -46,7 +46,7 @@ class AIProviderManager {
|
|||||||
return this.cache.get(cacheKey);
|
return this.cache.get(cacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCache(cacheKey, result) {
|
setCache(cacheKey, result, usedLocal = false) {
|
||||||
if (!this.config.cacheEnabled) return;
|
if (!this.config.cacheEnabled) return;
|
||||||
|
|
||||||
if (this.cache.size >= this.maxCacheSize) {
|
if (this.cache.size >= this.maxCacheSize) {
|
||||||
@@ -56,6 +56,7 @@ class AIProviderManager {
|
|||||||
|
|
||||||
this.cache.set(cacheKey, {
|
this.cache.set(cacheKey, {
|
||||||
result,
|
result,
|
||||||
|
usedLocal,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -129,7 +130,7 @@ class AIProviderManager {
|
|||||||
const cached = this.getFromCache(cacheKey);
|
const cached = this.getFromCache(cacheKey);
|
||||||
if (cached && Date.now() - cached.timestamp < 3600000) { // 1 hour cache
|
if (cached && Date.now() - cached.timestamp < 3600000) { // 1 hour cache
|
||||||
if (verbose) Progress.info('Using cached result');
|
if (verbose) Progress.info('Using cached result');
|
||||||
return cached.result;
|
return { message: cached.result, usedLocal: cached.usedLocal || false };
|
||||||
}
|
}
|
||||||
|
|
||||||
const providerChain = this.buildProviderChain(preferredProvider);
|
const providerChain = this.buildProviderChain(preferredProvider);
|
||||||
@@ -140,16 +141,16 @@ class AIProviderManager {
|
|||||||
|
|
||||||
if (provider === 'local') {
|
if (provider === 'local') {
|
||||||
const result = await this.generateLocalHeuristic(diff, options);
|
const result = await this.generateLocalHeuristic(diff, options);
|
||||||
this.setCache(cacheKey, result);
|
this.setCache(cacheKey, result, true);
|
||||||
return result;
|
return { message: result, usedLocal: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
const prompt = this.buildPrompt(diff, { conventional, body });
|
const prompt = this.buildPrompt(diff, { conventional, body });
|
||||||
const result = await this.generateWithProvider(provider, prompt, options);
|
const result = await this.generateWithProvider(provider, prompt, options);
|
||||||
const cleaned = this.cleanCommitMessage(result, { body });
|
const cleaned = this.cleanCommitMessage(result, { body });
|
||||||
|
|
||||||
this.setCache(cacheKey, cleaned);
|
this.setCache(cacheKey, cleaned, false);
|
||||||
return cleaned;
|
return { message: cleaned, usedLocal: false };
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (verbose) Progress.warning(`${provider} failed: ${error.message}`);
|
if (verbose) Progress.warning(`${provider} failed: ${error.message}`);
|
||||||
@@ -159,8 +160,8 @@ class AIProviderManager {
|
|||||||
|
|
||||||
// Final fallback
|
// Final fallback
|
||||||
const fallback = 'Update project files';
|
const fallback = 'Update project files';
|
||||||
this.setCache(cacheKey, fallback);
|
this.setCache(cacheKey, fallback, true);
|
||||||
return fallback;
|
return { message: fallback, usedLocal: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
buildProviderChain(preferred) {
|
buildProviderChain(preferred) {
|
||||||
@@ -213,7 +214,7 @@ class AIProviderManager {
|
|||||||
let subject = (lines[0] || '').replace(/\s{2,}/g, ' ').replace(/[\s:,.!;]+$/g, '').trim();
|
let subject = (lines[0] || '').replace(/\s{2,}/g, ' ').replace(/[\s:,.!;]+$/g, '').trim();
|
||||||
|
|
||||||
if (subject.length === 0) subject = 'Update project code';
|
if (subject.length === 0) subject = 'Update project code';
|
||||||
if (subject.length > 72) subject = subject.substring(0, 69) + '...';
|
// No length restriction - allow AI to generate full commit messages
|
||||||
|
|
||||||
if (!options.body) return subject;
|
if (!options.body) return subject;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gims",
|
"name": "gims",
|
||||||
"version": "0.6.5",
|
"version": "0.6.6",
|
||||||
"description": "Git Made Simple – AI‑powered git helper using Gemini / OpenAI",
|
"description": "Git Made Simple – AI‑powered git helper using Gemini / OpenAI",
|
||||||
"author": "S41R4J",
|
"author": "S41R4J",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
Reference in New Issue
Block a user