@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.
- package/README.md +162 -0
- package/dist/ai/anthropic-client.js +92 -0
- package/dist/ai/commit-generator.js +200 -0
- package/dist/ai/gemini-client.js +201 -0
- package/dist/ai/git-interpreter.js +209 -0
- package/dist/ai/smart-solver.js +260 -0
- package/dist/auth/supabase-client.js +288 -0
- package/dist/commands/activate.js +108 -0
- package/dist/commands/commit.js +255 -0
- package/dist/commands/conflict.js +233 -0
- package/dist/commands/doctor.js +113 -0
- package/dist/commands/git-advanced.js +311 -0
- package/dist/commands/github-auth.js +193 -0
- package/dist/commands/login.js +11 -0
- package/dist/commands/natural.js +305 -0
- package/dist/commands/onboard.js +111 -0
- package/dist/commands/quick.js +173 -0
- package/dist/commands/setup.js +163 -0
- package/dist/commands/stats.js +128 -0
- package/dist/commands/uninstall.js +131 -0
- package/dist/db/database.js +99 -0
- package/dist/index.js +144 -0
- package/dist/lib/auth.js +171 -0
- package/dist/lib/github.js +280 -0
- package/dist/lib/multi-repo.js +276 -0
- package/dist/lib/supabase.js +153 -0
- package/dist/license/manager.js +203 -0
- package/dist/repl/index.js +185 -0
- package/dist/repl/interpreter.js +524 -0
- package/dist/utils/analytics.js +36 -0
- package/dist/utils/auth-storage.js +65 -0
- package/dist/utils/dopamine.js +211 -0
- package/dist/utils/errors.js +56 -0
- package/dist/utils/git.js +105 -0
- package/dist/utils/heatmap.js +265 -0
- package/dist/utils/rate-limit.js +68 -0
- package/dist/utils/retry.js +46 -0
- package/dist/utils/ui.js +189 -0
- package/dist/utils/version.js +81 -0
- 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
|
+
}
|