@snapcommit/cli 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.
Files changed (40) hide show
  1. package/README.md +162 -0
  2. package/dist/ai/anthropic-client.js +92 -0
  3. package/dist/ai/commit-generator.js +200 -0
  4. package/dist/ai/gemini-client.js +201 -0
  5. package/dist/ai/git-interpreter.js +209 -0
  6. package/dist/ai/smart-solver.js +260 -0
  7. package/dist/auth/supabase-client.js +288 -0
  8. package/dist/commands/activate.js +108 -0
  9. package/dist/commands/commit.js +255 -0
  10. package/dist/commands/conflict.js +233 -0
  11. package/dist/commands/doctor.js +113 -0
  12. package/dist/commands/git-advanced.js +311 -0
  13. package/dist/commands/github-auth.js +193 -0
  14. package/dist/commands/login.js +11 -0
  15. package/dist/commands/natural.js +305 -0
  16. package/dist/commands/onboard.js +111 -0
  17. package/dist/commands/quick.js +173 -0
  18. package/dist/commands/setup.js +163 -0
  19. package/dist/commands/stats.js +128 -0
  20. package/dist/commands/uninstall.js +131 -0
  21. package/dist/db/database.js +99 -0
  22. package/dist/index.js +144 -0
  23. package/dist/lib/auth.js +171 -0
  24. package/dist/lib/github.js +280 -0
  25. package/dist/lib/multi-repo.js +276 -0
  26. package/dist/lib/supabase.js +153 -0
  27. package/dist/license/manager.js +203 -0
  28. package/dist/repl/index.js +185 -0
  29. package/dist/repl/interpreter.js +524 -0
  30. package/dist/utils/analytics.js +36 -0
  31. package/dist/utils/auth-storage.js +65 -0
  32. package/dist/utils/dopamine.js +211 -0
  33. package/dist/utils/errors.js +56 -0
  34. package/dist/utils/git.js +105 -0
  35. package/dist/utils/heatmap.js +265 -0
  36. package/dist/utils/rate-limit.js +68 -0
  37. package/dist/utils/retry.js +46 -0
  38. package/dist/utils/ui.js +189 -0
  39. package/dist/utils/version.js +81 -0
  40. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # SnapCommit
2
+
3
+ **Snap. Commit. Track.**
4
+
5
+ Instant AI commits. Beautiful progress tracking. Never write commit messages again.
6
+
7
+ [![npm version](https://badge.fury.io/js/snapcommit.svg)](https://www.npmjs.com/package/snapcommit)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ## What is SnapCommit?
11
+
12
+ SnapCommit is a CLI tool that makes Git & GitHub effortless:
13
+
14
+ 1. **⚡ Instant AI Commits** - Professional messages in one command (`snap`)
15
+ 2. **🌍 Natural Language** - Just say what you want: "create a PR", "merge main"
16
+ 3. **📊 Beautiful Stats** - Track your coding journey with gorgeous analytics
17
+ 4. **🔥 Streak Tracking** - Gamification to keep you motivated
18
+ 5. **🚀 Universal** - Works everywhere (any IDE, any terminal, any OS)
19
+
20
+ ## Quick Start
21
+
22
+ ```bash
23
+ # Install globally
24
+ npm install -g snapcommit
25
+
26
+ # Set up (adds aliases)
27
+ snapcommit setup
28
+
29
+ # Make your first commit
30
+ snap # That's it!
31
+ ```
32
+
33
+ ## Installation
34
+
35
+ ### Option 1: npm (Recommended)
36
+
37
+ ```bash
38
+ npm install -g snapcommit
39
+ ```
40
+
41
+ ### Option 2: Install Scripts
42
+
43
+ **macOS / Linux:**
44
+ ```bash
45
+ curl -fsSL https://snapcommit.dev/install.sh | bash
46
+ ```
47
+
48
+ **Windows (PowerShell):**
49
+ ```powershell
50
+ iwr https://snapcommit.dev/install.ps1 | iex
51
+ ```
52
+
53
+ ## Setup
54
+
55
+ After installation, run setup to integrate with your shell:
56
+
57
+ ```bash
58
+ snapcommit setup
59
+ ```
60
+
61
+ This adds `snap` and `sc` aliases. Restart your terminal and you're ready!
62
+
63
+ ## Usage
64
+
65
+ ### Interactive Mode
66
+
67
+ Type `snap` to enter interactive mode:
68
+
69
+ ```bash
70
+ $ snap
71
+ ⚡ SnapCommit Interactive Mode
72
+
73
+ snap> commit my authentication work
74
+ ✅ feat(auth): implement user authentication system
75
+
76
+ snap> create a PR
77
+ ✅ PR #42 created
78
+
79
+ snap> check ci
80
+ ✅ All checks passed
81
+ ```
82
+
83
+ ### Just Talk to It 💬
84
+
85
+ SnapCommit is **Cursor for Git** - no commands to memorize. Just describe what you want:
86
+
87
+ ```bash
88
+ snap> commit my authentication work
89
+ ✅ feat(auth): implement user authentication system
90
+
91
+ snap> I messed up, go back to how it was before
92
+ ✅ Reverted last commit
93
+
94
+ snap> open a pull request for this feature
95
+ ✅ PR #42 created
96
+
97
+ snap> did the tests pass?
98
+ ✅ All CI checks passed
99
+ ```
100
+
101
+ **That's the whole point** - talk naturally, like you would to a teammate. SnapCommit figures out what you mean.
102
+
103
+ ## Authentication
104
+
105
+ **SnapCommit includes built-in AI models!** No API keys needed.
106
+
107
+ 1. **Sign up at:** https://snapcommit.dev/login
108
+ 2. **Subscribe:** $9.99/month or $100/year
109
+ 3. **Get your token:** From your dashboard
110
+ 4. **Authenticate CLI:** Enter token when prompted
111
+
112
+ After installing, the CLI will guide you through authentication on first use.
113
+
114
+ ## Features
115
+
116
+ - ✅ **AI Commit Messages** - Claude 4.5 Sonnet generates perfect commits
117
+ - ✅ **Natural Language Git** - No more memorizing commands
118
+ - ✅ **GitHub Integration** - PRs, CI checks, workflows, releases
119
+ - ✅ **Progress Tracking** - Streaks, heatmaps, milestones
120
+ - ✅ **Multi-Repo Support** - Switch between projects seamlessly
121
+ - ✅ **Conflict Resolution** - AI-guided merge conflict solving
122
+ - ✅ **Smart Error Handling** - Auto-retry with intelligent fixes
123
+ - ✅ **Privacy First** - Local-first, minimal data sent to AI
124
+ - ✅ **Cross-Platform** - macOS, Linux, Windows
125
+
126
+ ## Pricing
127
+
128
+ - **Monthly:** $9.99/month - Unlimited AI commits, all features
129
+ - **Yearly:** $100/year - Save 17% (best value!)
130
+
131
+ ## Supported Platforms
132
+
133
+ - ✅ macOS (zsh, bash, fish)
134
+ - ✅ Linux (zsh, bash, fish)
135
+ - ✅ Windows (PowerShell, Git Bash, WSL)
136
+ - ✅ Any terminal, any IDE
137
+
138
+ ## Requirements
139
+
140
+ - Node.js 18+
141
+ - Git installed
142
+ - API keys (we'll help you get them)
143
+
144
+ ## Documentation
145
+
146
+ Full docs at [snapcommit.dev/docs](https://snapcommit.dev/docs)
147
+
148
+ ## Support
149
+
150
+ - 🐦 [Follow on X/Twitter](https://x.com/Arjun06061)
151
+ - 📧 [Email support](mailto:support@snapcommit.dev)
152
+ - 💡 [Suggestions & Feedback](https://x.com/Arjun06061)
153
+
154
+ ## License
155
+
156
+ MIT © SnapCommit Team
157
+
158
+ ---
159
+
160
+ **Made with ❤️ for developers who want to focus on building, not git messages.**
161
+
162
+ [Website](https://snapcommit.dev) • [X/Twitter](https://x.com/Arjun06061)
@@ -0,0 +1,92 @@
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.generateCommitMessage = generateCommitMessage;
7
+ exports.analyzeCommand = analyzeCommand;
8
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
9
+ const retry_1 = require("../utils/retry");
10
+ const client = new sdk_1.default({
11
+ apiKey: process.env.ANTHROPIC_API_KEY,
12
+ });
13
+ async function generateCommitMessage(diff) {
14
+ try {
15
+ // Try Claude first, fallback to Gemini if it fails
16
+ const message = await (0, retry_1.retryWithBackoff)(() => client.messages.create({
17
+ model: 'claude-sonnet-4-20250514',
18
+ max_tokens: 500,
19
+ temperature: 0.3,
20
+ messages: [
21
+ {
22
+ role: 'user',
23
+ content: `You are an expert software engineer writing commit messages.
24
+
25
+ Analyze this git diff and generate a professional conventional commit message.
26
+
27
+ Rules:
28
+ 1. Use conventional commits format: type(scope): message
29
+ 2. Types: feat, fix, docs, style, refactor, test, chore
30
+ 3. Keep first line under 72 characters
31
+ 4. Be specific and clear
32
+ 5. If multiple changes, use the most significant one
33
+ 6. Don't mention file names unless critical
34
+ 7. Focus on WHAT changed and WHY, not HOW
35
+
36
+ Git Diff:
37
+ \`\`\`
38
+ ${diff}
39
+ \`\`\`
40
+
41
+ Reply with ONLY the commit message, nothing else.`,
42
+ },
43
+ ],
44
+ }), {
45
+ maxAttempts: 3,
46
+ initialDelay: 1000,
47
+ maxDelay: 5000,
48
+ });
49
+ const content = message.content[0];
50
+ if (content.type === 'text') {
51
+ return content.text.trim();
52
+ }
53
+ throw new Error('Unexpected response format from Claude');
54
+ }
55
+ catch (error) {
56
+ throw new Error(`Claude API error: ${error.message}`);
57
+ }
58
+ }
59
+ async function analyzeCommand(naturalLanguage) {
60
+ try {
61
+ const message = await client.messages.create({
62
+ model: 'claude-sonnet-4-20250514',
63
+ max_tokens: 200,
64
+ temperature: 0.2,
65
+ messages: [
66
+ {
67
+ role: 'user',
68
+ content: `You are a terminal command expert. Convert this natural language request into a precise terminal command.
69
+
70
+ Request: "${naturalLanguage}"
71
+
72
+ Rules:
73
+ 1. Reply with ONLY the command, nothing else
74
+ 2. No explanations, no markdown, no backticks
75
+ 3. If multiple commands needed, separate with &&
76
+ 4. Use common Unix commands (works on Mac/Linux)
77
+ 5. Be safe - no destructive commands without confirmation
78
+
79
+ Reply with the command:`,
80
+ },
81
+ ],
82
+ });
83
+ const content = message.content[0];
84
+ if (content.type === 'text') {
85
+ return content.text.trim();
86
+ }
87
+ throw new Error('Unexpected response format from Claude');
88
+ }
89
+ catch (error) {
90
+ throw new Error(`Claude API error: ${error.message}`);
91
+ }
92
+ }
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ /**
3
+ * Enhanced commit message generator
4
+ * Generates multiple options for user to choose from
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.generateCommitOptions = generateCommitOptions;
44
+ exports.displayCommitOptions = displayCommitOptions;
45
+ exports.editCommitMessage = editCommitMessage;
46
+ const anthropic_client_1 = require("./anthropic-client");
47
+ const gemini_client_1 = require("./gemini-client");
48
+ const chalk_1 = __importDefault(require("chalk"));
49
+ /**
50
+ * Generate multiple commit message options
51
+ * Gives user choice of styles
52
+ */
53
+ async function generateCommitOptions(diff) {
54
+ try {
55
+ // Generate 3 different styles in parallel
56
+ const [detailed, concise, conventional] = await Promise.all([
57
+ generateDetailedCommit(diff),
58
+ generateConciseCommit(diff),
59
+ generateConventionalCommit(diff),
60
+ ]);
61
+ return [
62
+ {
63
+ message: conventional,
64
+ description: 'Conventional Commits format (recommended)',
65
+ style: 'conventional',
66
+ },
67
+ {
68
+ message: detailed,
69
+ description: 'Detailed with full context',
70
+ style: 'detailed',
71
+ },
72
+ {
73
+ message: concise,
74
+ description: 'Short and simple',
75
+ style: 'concise',
76
+ },
77
+ ];
78
+ }
79
+ catch (error) {
80
+ // Fallback to single generation
81
+ const message = await (0, anthropic_client_1.generateCommitMessage)(diff);
82
+ return [{
83
+ message,
84
+ description: 'AI-generated',
85
+ style: 'conventional',
86
+ }];
87
+ }
88
+ }
89
+ /**
90
+ * Generate detailed commit message
91
+ */
92
+ async function generateDetailedCommit(diff) {
93
+ const prompt = `You are an expert software engineer writing a detailed commit message.
94
+
95
+ Analyze this git diff and generate a comprehensive conventional commit message.
96
+
97
+ Requirements:
98
+ 1. Use conventional commits format: type(scope): subject
99
+ 2. Include a detailed body explaining WHAT changed and WHY
100
+ 3. List key changes as bullet points
101
+ 4. Be specific and thorough
102
+ 5. Keep subject line under 72 characters
103
+
104
+ Git Diff:
105
+ \`\`\`
106
+ ${diff}
107
+ \`\`\`
108
+
109
+ Reply with ONLY the commit message (subject + body), nothing else.`;
110
+ try {
111
+ return await (0, anthropic_client_1.generateCommitMessage)(diff);
112
+ }
113
+ catch {
114
+ return await (0, gemini_client_1.generateCommitMessageGemini)(diff);
115
+ }
116
+ }
117
+ /**
118
+ * Generate concise commit message
119
+ */
120
+ async function generateConciseCommit(diff) {
121
+ const prompt = `You are an expert software engineer writing a concise commit message.
122
+
123
+ Analyze this git diff and generate a SHORT conventional commit message.
124
+
125
+ Requirements:
126
+ 1. Use conventional commits format: type(scope): subject
127
+ 2. Keep it to ONE line only (subject only, no body)
128
+ 3. Be specific but brief
129
+ 4. Under 72 characters total
130
+
131
+ Git Diff:
132
+ \`\`\`
133
+ ${diff}
134
+ \`\`\`
135
+
136
+ Reply with ONLY the one-line commit message, nothing else.`;
137
+ try {
138
+ // For concise, we need a different AI call with specific instructions
139
+ const message = await (0, anthropic_client_1.generateCommitMessage)(diff);
140
+ // Extract just the first line
141
+ return message.split('\n')[0];
142
+ }
143
+ catch {
144
+ const message = await (0, gemini_client_1.generateCommitMessageGemini)(diff);
145
+ return message.split('\n')[0];
146
+ }
147
+ }
148
+ /**
149
+ * Generate conventional commit message (our standard)
150
+ */
151
+ async function generateConventionalCommit(diff) {
152
+ return await (0, anthropic_client_1.generateCommitMessage)(diff);
153
+ }
154
+ /**
155
+ * Display commit options to user
156
+ */
157
+ function displayCommitOptions(options) {
158
+ console.log(chalk_1.default.white.bold('\n✨ AI-Generated Commit Messages:\n'));
159
+ options.forEach((option, index) => {
160
+ const number = index + 1;
161
+ const badge = index === 0 ? chalk_1.default.green(' (Recommended)') : '';
162
+ console.log(chalk_1.default.cyan.bold(`Option ${number}:`) + chalk_1.default.gray(` ${option.description}`) + badge);
163
+ console.log();
164
+ // Display the commit message with nice formatting
165
+ const lines = option.message.split('\n');
166
+ lines.forEach((line, i) => {
167
+ if (i === 0) {
168
+ // Subject line - bold
169
+ console.log(chalk_1.default.white.bold(` ${line}`));
170
+ }
171
+ else if (line.trim()) {
172
+ // Body lines - gray
173
+ console.log(chalk_1.default.gray(` ${line}`));
174
+ }
175
+ else {
176
+ console.log();
177
+ }
178
+ });
179
+ console.log();
180
+ });
181
+ }
182
+ /**
183
+ * Let user edit a commit message
184
+ */
185
+ async function editCommitMessage(original) {
186
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
187
+ console.log(chalk_1.default.yellow('\n📝 Edit your commit message:'));
188
+ console.log(chalk_1.default.gray('Current:'));
189
+ console.log(chalk_1.default.white(` ${original}\n`));
190
+ const rl = readline.createInterface({
191
+ input: process.stdin,
192
+ output: process.stdout,
193
+ });
194
+ return new Promise((resolve) => {
195
+ rl.question(chalk_1.default.cyan('New message: '), (answer) => {
196
+ rl.close();
197
+ resolve(answer.trim() || original);
198
+ });
199
+ });
200
+ }
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ /**
3
+ * Google Gemini AI Client (Fallback)
4
+ * Used when Claude is unavailable or for cost optimization
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.generateCommitMessageGemini = generateCommitMessageGemini;
8
+ exports.interpretGitCommandGemini = interpretGitCommandGemini;
9
+ exports.needsClarificationGemini = needsClarificationGemini;
10
+ exports.solveGitProblemGemini = solveGitProblemGemini;
11
+ const generative_ai_1 = require("@google/generative-ai");
12
+ const retry_1 = require("../utils/retry");
13
+ const genAI = new generative_ai_1.GoogleGenerativeAI(process.env.GOOGLE_AI_API_KEY || '');
14
+ async function generateCommitMessageGemini(diff) {
15
+ try {
16
+ const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp' });
17
+ const prompt = `You are an expert software engineer writing commit messages.
18
+
19
+ Analyze this git diff and generate a professional conventional commit message.
20
+
21
+ Rules:
22
+ 1. Use conventional commits format: type(scope): message
23
+ 2. Types: feat, fix, docs, style, refactor, test, chore
24
+ 3. Keep first line under 72 characters
25
+ 4. Be specific and clear
26
+ 5. If multiple changes, use the most significant one
27
+ 6. Don't mention file names unless critical
28
+ 7. Focus on WHAT changed and WHY, not HOW
29
+
30
+ Git Diff:
31
+ \`\`\`
32
+ ${diff}
33
+ \`\`\`
34
+
35
+ Reply with ONLY the commit message, nothing else.`;
36
+ const result = await (0, retry_1.retryWithBackoff)(() => model.generateContent(prompt), {
37
+ maxAttempts: 3,
38
+ initialDelay: 1000,
39
+ maxDelay: 5000,
40
+ });
41
+ const response = await result.response;
42
+ const text = response.text();
43
+ if (!text) {
44
+ throw new Error('Empty response from Gemini');
45
+ }
46
+ return text.trim();
47
+ }
48
+ catch (error) {
49
+ throw new Error(`Gemini API error: ${error.message}`);
50
+ }
51
+ }
52
+ async function interpretGitCommandGemini(userInput, context) {
53
+ try {
54
+ const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp' });
55
+ const prompt = `You are a Git expert assistant. Interpret the user's natural language request into a structured Git operation.
56
+
57
+ User request: "${userInput}"
58
+
59
+ Current context:
60
+ - Branch: ${context.currentBranch}
61
+ - Uncommitted changes: ${context.hasUncommittedChanges}
62
+ - Last commit: ${context.lastCommitHash || 'none'}
63
+
64
+ Respond with a JSON object (and ONLY JSON, no markdown):
65
+ {
66
+ "action": "commit|revert|merge|branch|reset|stash|push|pull|rebase|cherry-pick|etc",
67
+ "target": "specific target if any (branch, commit, file)",
68
+ "options": {"key": "value"},
69
+ "confidence": 0.95,
70
+ "explanation": "This will undo your last commit but keep all your changes staged",
71
+ "gitCommands": ["git reset --soft HEAD~1", "git status"],
72
+ "riskLevel": "safe|medium|high",
73
+ "needsConfirmation": true,
74
+ "educationalTip": "The --soft flag keeps your changes staged. Use --hard to discard changes entirely (dangerous!)"
75
+ }
76
+
77
+ Risk levels:
78
+ - safe: Read-only, undo-able (status, log, diff)
79
+ - medium: Changes state but recoverable (commit, stash, branch)
80
+ - high: Destructive, affects remote, or hard to undo (push --force, reset --hard, delete branch)
81
+
82
+ Respond with ONLY the JSON object, no other text.`;
83
+ const result = await model.generateContent(prompt);
84
+ const response = await result.response;
85
+ const text = response.text();
86
+ // Clean up markdown if present
87
+ let jsonText = text.trim();
88
+ if (jsonText.startsWith('```json')) {
89
+ jsonText = jsonText.replace(/```json\n?/, '').replace(/```\n?$/, '');
90
+ }
91
+ else if (jsonText.startsWith('```')) {
92
+ jsonText = jsonText.replace(/```\n?/, '').replace(/```\n?$/, '');
93
+ }
94
+ const intent = JSON.parse(jsonText);
95
+ // Validate
96
+ if (!intent.action || !intent.explanation || !intent.gitCommands) {
97
+ throw new Error('Invalid intent structure from Gemini');
98
+ }
99
+ return intent;
100
+ }
101
+ catch (error) {
102
+ throw new Error(`Gemini API error: ${error.message}`);
103
+ }
104
+ }
105
+ /**
106
+ * Check if user input needs clarification
107
+ */
108
+ async function needsClarificationGemini(userInput) {
109
+ try {
110
+ const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp' });
111
+ const prompt = `You are a helpful Git assistant. Determine if this user request is ambiguous and requires clarification.
112
+
113
+ User request: "${userInput}"
114
+
115
+ Return a JSON object:
116
+ {
117
+ "needsClarification": true|false,
118
+ "options": [
119
+ { "label": "Description of option 1", "value": "clarified command 1" },
120
+ { "label": "Description of option 2", "value": "clarified command 2" }
121
+ ]
122
+ }
123
+
124
+ Examples of ambiguous requests:
125
+ - "undo that" → could mean: undo last commit, undo last change, undo last push
126
+ - "go back" → could mean: go back 1 commit, discard changes, switch branch
127
+ - "clean up" → could mean: clean untracked files, delete merged branches, remove stale changes
128
+
129
+ If the request is clear and unambiguous, return: { "needsClarification": false }
130
+
131
+ Respond with ONLY the JSON object, no other text.`;
132
+ const result = await model.generateContent(prompt);
133
+ const response = await result.response;
134
+ let text = response.text().trim();
135
+ // Clean up markdown
136
+ if (text.startsWith('```json')) {
137
+ text = text.replace(/```json\n?/, '').replace(/```\n?$/, '');
138
+ }
139
+ else if (text.startsWith('```')) {
140
+ text = text.replace(/```\n?/, '').replace(/```\n?$/, '');
141
+ }
142
+ return JSON.parse(text);
143
+ }
144
+ catch (error) {
145
+ // If clarification check fails, assume no clarification needed
146
+ return { needsClarification: false };
147
+ }
148
+ }
149
+ /**
150
+ * Try to solve a Git problem using AI
151
+ */
152
+ async function solveGitProblemGemini(userInput, context) {
153
+ try {
154
+ const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp' });
155
+ const prompt = `You are an expert Git problem solver. A user encountered an issue.
156
+
157
+ User's problem: "${userInput}"
158
+
159
+ Current context:
160
+ - Branch: ${context.currentBranch}
161
+ - Git status:
162
+ \`\`\`
163
+ ${context.gitStatus}
164
+ \`\`\`
165
+ ${context.recentError ? `- Recent error: ${context.recentError}` : ''}
166
+
167
+ Your goal is to provide a solution. Return a JSON object:
168
+ {
169
+ "success": true,
170
+ "message": "A clear explanation of what went wrong and how you fixed it",
171
+ "gitCommands": ["git command 1", "git command 2"]
172
+ }
173
+
174
+ OR if you cannot solve it:
175
+ {
176
+ "success": false,
177
+ "message": "Explanation of why you couldn't solve it or what more information is needed"
178
+ }
179
+
180
+ Prioritize safe, non-destructive commands. If a destructive command is necessary, clearly state its implications in the message.
181
+
182
+ Respond with ONLY the JSON object, no other text.`;
183
+ const result = await model.generateContent(prompt);
184
+ const response = await result.response;
185
+ let text = response.text().trim();
186
+ // Clean up markdown
187
+ if (text.startsWith('```json')) {
188
+ text = text.replace(/```json\n?/, '').replace(/```\n?$/, '');
189
+ }
190
+ else if (text.startsWith('```')) {
191
+ text = text.replace(/```\n?/, '').replace(/```\n?$/, '');
192
+ }
193
+ return JSON.parse(text);
194
+ }
195
+ catch (error) {
196
+ return {
197
+ success: false,
198
+ message: `AI problem solver failed: ${error.message}`,
199
+ };
200
+ }
201
+ }