@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
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ /**
3
+ * Natural Language Git Command Interpreter
4
+ * Uses Claude to understand user intent and map to Git operations
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.interpretGitCommand = interpretGitCommand;
44
+ exports.suggestNextActions = suggestNextActions;
45
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
46
+ const retry_1 = require("../utils/retry");
47
+ const client = new sdk_1.default({
48
+ apiKey: process.env.ANTHROPIC_API_KEY || '',
49
+ });
50
+ async function interpretGitCommand(userInput, context) {
51
+ const prompt = `You are a Git expert assistant. Interpret the user's natural language request into a structured Git operation.
52
+
53
+ User request: "${userInput}"
54
+
55
+ Current context:
56
+ - Branch: ${context.currentBranch}
57
+ - Uncommitted changes: ${context.hasUncommittedChanges}
58
+ - Last commit: ${context.lastCommitHash || 'none'}
59
+ - Remote: ${context.remoteBranch || 'none'}
60
+
61
+ Respond with a JSON object (and ONLY JSON, no markdown):
62
+ {
63
+ "action": "commit|revert|merge|branch|reset|stash|push|pull|rebase|cherry-pick|etc",
64
+ "target": "specific target if any (branch, commit, file)",
65
+ "options": {"key": "value"},
66
+ "confidence": 0.95,
67
+ "explanation": "This will undo your last commit but keep all your changes staged",
68
+ "gitCommands": ["git reset --soft HEAD~1", "git status"],
69
+ "riskLevel": "safe|medium|high",
70
+ "needsConfirmation": true,
71
+ "educationalTip": "The --soft flag keeps your changes staged. Use --hard to discard changes entirely (dangerous!)"
72
+ }
73
+
74
+ Risk levels:
75
+ - safe: Read-only, undo-able (status, log, diff)
76
+ - medium: Changes state but recoverable (commit, stash, branch)
77
+ - high: Destructive, affects remote, or hard to undo (push --force, reset --hard, delete branch)
78
+
79
+ Common patterns:
80
+ - "commit" / "save" / "commit this" → action: "commit"
81
+ - "undo" / "go back" → action: "reset" (if uncommitted) or "revert" (if committed)
82
+ - "push" / "send" / "upload" → action: "push"
83
+ - "merge X" → action: "merge", target: "X"
84
+ - "create branch X" → action: "branch", target: "X"
85
+ - "switch to X" → action: "checkout", target: "X"
86
+ - "delete branch X" → action: "delete_branch", target: "X"
87
+
88
+ Be smart about context:
89
+ - If user says "undo that" and last action was commit → revert
90
+ - If user says "merge" without target → suggest merging current branch to main
91
+ - If user says "fix conflicts" → action: "resolve_conflicts"
92
+
93
+ Respond with ONLY the JSON object, no other text.`;
94
+ try {
95
+ const response = await (0, retry_1.retryWithBackoff)(() => client.messages.create({
96
+ model: 'claude-sonnet-4-20250514',
97
+ max_tokens: 1000,
98
+ messages: [{ role: 'user', content: prompt }],
99
+ }));
100
+ const text = response.content[0];
101
+ if (text.type !== 'text') {
102
+ throw new Error('Unexpected response type');
103
+ }
104
+ // Parse JSON response
105
+ const intent = JSON.parse(text.text.trim());
106
+ // Validate
107
+ if (!intent.action || !intent.explanation || !intent.gitCommands) {
108
+ throw new Error('Invalid intent structure');
109
+ }
110
+ return intent;
111
+ }
112
+ catch (error) {
113
+ // Try Gemini fallback
114
+ try {
115
+ const { interpretGitCommandGemini } = await Promise.resolve().then(() => __importStar(require('./gemini-client')));
116
+ return await interpretGitCommandGemini(userInput, context);
117
+ }
118
+ catch (geminiError) {
119
+ // Ultimate fallback: simple pattern matching
120
+ return getFallbackIntent(userInput, context);
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Fallback intent parser for common commands (if AI fails)
126
+ */
127
+ function getFallbackIntent(userInput, context) {
128
+ const input = userInput.toLowerCase().trim();
129
+ // Common patterns
130
+ if (input.includes('commit') || input === 'save' || input === 'snap') {
131
+ return {
132
+ action: 'commit',
133
+ confidence: 0.9,
134
+ explanation: 'This will commit all your changes with an AI-generated message',
135
+ gitCommands: ['git add -A', 'git commit -m "[AI generated]"'],
136
+ riskLevel: 'medium',
137
+ needsConfirmation: false,
138
+ educationalTip: 'git add -A stages all changes (new, modified, deleted files)',
139
+ };
140
+ }
141
+ if (input.includes('undo') || input.includes('go back')) {
142
+ if (context.hasUncommittedChanges) {
143
+ return {
144
+ action: 'reset',
145
+ confidence: 0.8,
146
+ explanation: 'This will discard all uncommitted changes',
147
+ gitCommands: ['git reset --hard HEAD'],
148
+ riskLevel: 'high',
149
+ needsConfirmation: true,
150
+ educationalTip: '⚠️ --hard permanently deletes changes. Use --soft to keep changes.',
151
+ };
152
+ }
153
+ else {
154
+ return {
155
+ action: 'revert',
156
+ confidence: 0.8,
157
+ explanation: 'This will revert your last commit',
158
+ gitCommands: ['git revert HEAD'],
159
+ riskLevel: 'medium',
160
+ needsConfirmation: true,
161
+ educationalTip: 'git revert creates a new commit that undoes changes (safe for pushed commits)',
162
+ };
163
+ }
164
+ }
165
+ if (input.includes('push') || input.includes('send')) {
166
+ return {
167
+ action: 'push',
168
+ confidence: 0.9,
169
+ explanation: `This will push your commits to remote (${context.currentBranch})`,
170
+ gitCommands: [`git push origin ${context.currentBranch}`],
171
+ riskLevel: 'medium',
172
+ needsConfirmation: false,
173
+ educationalTip: 'git push uploads your local commits to GitHub/remote',
174
+ };
175
+ }
176
+ // Default: show help
177
+ return {
178
+ action: 'help',
179
+ confidence: 0.5,
180
+ explanation: 'I\'m not sure what you want to do. Try being more specific.',
181
+ gitCommands: ['git status'],
182
+ riskLevel: 'safe',
183
+ needsConfirmation: false,
184
+ educationalTip: 'Try commands like: "commit", "push", "undo", "merge", "create branch"',
185
+ };
186
+ }
187
+ /**
188
+ * Generate suggestions for next actions
189
+ */
190
+ function suggestNextActions(lastAction, context) {
191
+ const suggestions = [];
192
+ if (lastAction === 'commit') {
193
+ suggestions.push('push - Send to remote');
194
+ suggestions.push('amend - Fix the commit');
195
+ suggestions.push('undo - Undo the commit');
196
+ }
197
+ if (lastAction === 'push') {
198
+ suggestions.push('create PR - Open pull request');
199
+ if (context.currentBranch !== 'main') {
200
+ suggestions.push('merge to main - Merge this branch');
201
+ }
202
+ }
203
+ if (context.hasUncommittedChanges) {
204
+ suggestions.push('commit - Save changes');
205
+ suggestions.push('stash - Temporarily save changes');
206
+ suggestions.push('discard - Throw away changes');
207
+ }
208
+ return suggestions;
209
+ }
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ /**
3
+ * Smart Git Problem Solver
4
+ * Handles ANY Git problem, no matter how complex
5
+ * Self-healing retry system with multiple strategies
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.solveGitProblem = solveGitProblem;
12
+ exports.needsClarification = needsClarification;
13
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
14
+ const child_process_1 = require("child_process");
15
+ const chalk_1 = __importDefault(require("chalk"));
16
+ const retry_1 = require("../utils/retry");
17
+ const client = new sdk_1.default({
18
+ apiKey: process.env.ANTHROPIC_API_KEY || '',
19
+ });
20
+ /**
21
+ * Intelligently solve ANY Git problem
22
+ * Tries multiple strategies until one succeeds
23
+ */
24
+ async function solveGitProblem(problemDescription, context, maxAttempts = 5) {
25
+ console.log(chalk_1.default.blue('🧠 Analyzing your problem...\n'));
26
+ // Get multiple solution strategies from AI
27
+ const strategies = await getSolutionStrategies(problemDescription, context);
28
+ if (strategies.length === 0) {
29
+ return {
30
+ success: false,
31
+ error: 'Could not find a solution. Please describe the problem differently.',
32
+ };
33
+ }
34
+ // Show the strategies to the user
35
+ console.log(chalk_1.default.white.bold('Possible solutions:\n'));
36
+ strategies.forEach((strategy, i) => {
37
+ const riskColor = strategy.riskLevel === 'safe' ? chalk_1.default.green :
38
+ strategy.riskLevel === 'medium' ? chalk_1.default.yellow :
39
+ chalk_1.default.red;
40
+ console.log(chalk_1.default.gray(`${i + 1}. `) + chalk_1.default.white(strategy.approach));
41
+ console.log(chalk_1.default.gray(` ${strategy.explanation}`));
42
+ console.log(riskColor(` Risk: ${strategy.riskLevel}`) + chalk_1.default.gray(` | Confidence: ${(strategy.confidence * 100).toFixed(0)}%`));
43
+ console.log();
44
+ });
45
+ // Try each strategy in order of confidence
46
+ const sortedStrategies = strategies.sort((a, b) => b.confidence - a.confidence);
47
+ for (let i = 0; i < Math.min(sortedStrategies.length, maxAttempts); i++) {
48
+ const strategy = sortedStrategies[i];
49
+ console.log(chalk_1.default.blue(`\n🔧 Trying approach ${i + 1}: ${strategy.approach}`));
50
+ console.log(chalk_1.default.gray('─'.repeat(60)));
51
+ // Execute the strategy
52
+ const result = await executeStrategy(strategy);
53
+ if (result.success) {
54
+ console.log(chalk_1.default.green.bold('\n✅ Success!'));
55
+ console.log(chalk_1.default.gray(` ${strategy.explanation}\n`));
56
+ return {
57
+ success: true,
58
+ strategy,
59
+ output: result.output,
60
+ attemptNumber: i + 1,
61
+ };
62
+ }
63
+ else {
64
+ console.log(chalk_1.default.red(`❌ Approach ${i + 1} failed: ${result.error}`));
65
+ // If we have more strategies, try to learn from this error
66
+ if (i < sortedStrategies.length - 1) {
67
+ console.log(chalk_1.default.yellow('💡 Trying a different approach...\n'));
68
+ // Ask AI for alternative based on this error
69
+ const alternative = await getAlternativeStrategy(problemDescription, context, strategy, result.error || 'Unknown error');
70
+ if (alternative) {
71
+ sortedStrategies.splice(i + 1, 0, alternative);
72
+ }
73
+ }
74
+ }
75
+ }
76
+ // All strategies failed
77
+ console.log(chalk_1.default.red.bold('\n❌ Could not solve the problem automatically.\n'));
78
+ console.log(chalk_1.default.yellow('💡 Suggestions:'));
79
+ console.log(chalk_1.default.gray(' • Try describing the problem differently'));
80
+ console.log(chalk_1.default.gray(' • Check if you have uncommitted changes (git status)'));
81
+ console.log(chalk_1.default.gray(' • Try "snapcommit doctor" to check your Git setup'));
82
+ console.log();
83
+ return {
84
+ success: false,
85
+ error: 'All solution strategies failed. Manual intervention may be required.',
86
+ };
87
+ }
88
+ /**
89
+ * Get multiple solution strategies from AI
90
+ */
91
+ async function getSolutionStrategies(problemDescription, context) {
92
+ try {
93
+ const prompt = `You are an expert Git problem solver. A user has described a Git problem and you need to provide multiple solution strategies.
94
+
95
+ User's problem: "${problemDescription}"
96
+
97
+ Context:
98
+ - Current branch: ${context.currentBranch || 'unknown'}
99
+ - Git status: ${context.gitStatus || 'unknown'}
100
+ - Recent error: ${context.recentError || 'none'}
101
+
102
+ Provide 3-5 solution strategies, ordered by safety and likelihood of success.
103
+
104
+ For each strategy, provide:
105
+ 1. A brief approach name
106
+ 2. The exact Git commands to run
107
+ 3. An explanation of what it does
108
+ 4. Risk level (safe/medium/high)
109
+ 5. Confidence level (0-1)
110
+
111
+ Respond with a JSON array (and ONLY JSON, no markdown):
112
+ [
113
+ {
114
+ "approach": "Soft reset to undo commit",
115
+ "commands": ["git reset --soft HEAD~1"],
116
+ "explanation": "Undoes the last commit but keeps your changes staged",
117
+ "riskLevel": "safe",
118
+ "confidence": 0.95
119
+ },
120
+ ...
121
+ ]
122
+
123
+ Rules:
124
+ - Start with the safest approach
125
+ - Include progressively more aggressive strategies
126
+ - Always check for uncommitted changes first if relevant
127
+ - Prefer stashing over discarding changes
128
+ - For destructive operations, warn the user`;
129
+ const response = await (0, retry_1.retryWithBackoff)(() => client.messages.create({
130
+ model: 'claude-sonnet-4-20250514',
131
+ max_tokens: 2000,
132
+ messages: [{ role: 'user', content: prompt }],
133
+ }));
134
+ const text = response.content[0];
135
+ if (text.type !== 'text') {
136
+ throw new Error('Unexpected response type');
137
+ }
138
+ // Parse JSON response
139
+ const strategies = JSON.parse(text.text.trim());
140
+ return strategies;
141
+ }
142
+ catch (error) {
143
+ console.error('AI strategy generation failed:', error.message);
144
+ return [];
145
+ }
146
+ }
147
+ /**
148
+ * Get an alternative strategy after a failure
149
+ */
150
+ async function getAlternativeStrategy(originalProblem, context, failedStrategy, errorMessage) {
151
+ try {
152
+ const prompt = `A Git solution strategy failed. Suggest an alternative approach.
153
+
154
+ Original problem: "${originalProblem}"
155
+ Failed approach: "${failedStrategy.approach}"
156
+ Commands that failed: ${failedStrategy.commands.join(', ')}
157
+ Error message: "${errorMessage}"
158
+
159
+ Suggest ONE alternative strategy that might work better.
160
+
161
+ Respond with JSON (no markdown):
162
+ {
163
+ "approach": "Alternative approach name",
164
+ "commands": ["git", "commands"],
165
+ "explanation": "Why this might work",
166
+ "riskLevel": "safe|medium|high",
167
+ "confidence": 0.8
168
+ }`;
169
+ const response = await (0, retry_1.retryWithBackoff)(() => client.messages.create({
170
+ model: 'claude-sonnet-4-20250514',
171
+ max_tokens: 1000,
172
+ messages: [{ role: 'user', content: prompt }],
173
+ }));
174
+ const text = response.content[0];
175
+ if (text.type !== 'text')
176
+ return null;
177
+ const alternative = JSON.parse(text.text.trim());
178
+ return alternative;
179
+ }
180
+ catch (error) {
181
+ return null;
182
+ }
183
+ }
184
+ /**
185
+ * Execute a solution strategy
186
+ */
187
+ async function executeStrategy(strategy) {
188
+ try {
189
+ const outputs = [];
190
+ for (const command of strategy.commands) {
191
+ console.log(chalk_1.default.gray(` $ ${command}`));
192
+ try {
193
+ const output = (0, child_process_1.execSync)(command, {
194
+ encoding: 'utf-8',
195
+ stdio: 'pipe',
196
+ });
197
+ if (output.trim()) {
198
+ console.log(chalk_1.default.gray(` ${output.trim()}`));
199
+ outputs.push(output.trim());
200
+ }
201
+ }
202
+ catch (cmdError) {
203
+ // Command failed
204
+ throw new Error(cmdError.stderr?.trim() || cmdError.message);
205
+ }
206
+ }
207
+ return {
208
+ success: true,
209
+ output: outputs.join('\n'),
210
+ };
211
+ }
212
+ catch (error) {
213
+ return {
214
+ success: false,
215
+ error: error.message,
216
+ };
217
+ }
218
+ }
219
+ /**
220
+ * Ask clarifying questions if the problem is ambiguous
221
+ */
222
+ async function needsClarification(problemDescription) {
223
+ try {
224
+ const prompt = `Analyze this Git problem description and determine if you need more information.
225
+
226
+ Problem: "${problemDescription}"
227
+
228
+ If the description is ambiguous or could mean multiple things, provide clarifying questions or options.
229
+
230
+ Respond with JSON (no markdown):
231
+ {
232
+ "needsClarification": true,
233
+ "questions": ["What specific state do you want to undo?"],
234
+ "options": [
235
+ {"label": "Undo last commit (keep changes)", "value": "reset_soft"},
236
+ {"label": "Undo last commit (discard changes)", "value": "reset_hard"},
237
+ {"label": "Undo all uncommitted changes", "value": "restore_all"}
238
+ ]
239
+ }
240
+
241
+ Or if clear:
242
+ {
243
+ "needsClarification": false
244
+ }`;
245
+ const response = await (0, retry_1.retryWithBackoff)(() => client.messages.create({
246
+ model: 'claude-sonnet-4-20250514',
247
+ max_tokens: 500,
248
+ messages: [{ role: 'user', content: prompt }],
249
+ }));
250
+ const text = response.content[0];
251
+ if (text.type !== 'text') {
252
+ return { needsClarification: false };
253
+ }
254
+ const result = JSON.parse(text.text.trim());
255
+ return result;
256
+ }
257
+ catch (error) {
258
+ return { needsClarification: false };
259
+ }
260
+ }