@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,233 @@
1
+ "use strict";
2
+ /**
3
+ * Conflict resolution wizard
4
+ * Helps users resolve merge conflicts with AI assistance
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.conflictCommand = conflictCommand;
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const child_process_1 = require("child_process");
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const readline_1 = __importDefault(require("readline"));
15
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
16
+ const analytics_1 = require("../utils/analytics");
17
+ const anthropic = new sdk_1.default({
18
+ apiKey: process.env.ANTHROPIC_API_KEY || '',
19
+ });
20
+ function askQuestion(query) {
21
+ const rl = readline_1.default.createInterface({
22
+ input: process.stdin,
23
+ output: process.stdout,
24
+ });
25
+ return new Promise(resolve => rl.question(query, ans => {
26
+ rl.close();
27
+ resolve(ans);
28
+ }));
29
+ }
30
+ async function conflictCommand() {
31
+ console.log(chalk_1.default.blue.bold('\nšŸ”§ Conflict Resolution Wizard\n'));
32
+ // Check for conflicts
33
+ let conflictFiles;
34
+ try {
35
+ const output = (0, child_process_1.execSync)('git diff --name-only --diff-filter=U', { encoding: 'utf-8' });
36
+ conflictFiles = output.trim().split('\n').filter(Boolean);
37
+ }
38
+ catch {
39
+ conflictFiles = [];
40
+ }
41
+ if (conflictFiles.length === 0) {
42
+ console.log(chalk_1.default.green('āœ… No conflicts detected!\n'));
43
+ return;
44
+ }
45
+ console.log(chalk_1.default.yellow(`Found ${conflictFiles.length} file(s) with conflicts:\n`));
46
+ conflictFiles.forEach((file, i) => {
47
+ console.log(chalk_1.default.white(` ${i + 1}. ${file}`));
48
+ });
49
+ console.log();
50
+ // Offer AI assistance
51
+ console.log(chalk_1.default.blue('šŸ¤– I can help you resolve these conflicts with AI.\n'));
52
+ const choice = await askQuestion(chalk_1.default.yellow('Choose an option:\n') +
53
+ chalk_1.default.gray(' 1. AI-assisted resolution (recommended)\n') +
54
+ chalk_1.default.gray(' 2. Manual resolution (open in editor)\n') +
55
+ chalk_1.default.gray(' 3. Abort merge\n') +
56
+ chalk_1.default.yellow('Your choice (1/2/3): '));
57
+ if (choice === '3') {
58
+ console.log(chalk_1.default.yellow('\nāš ļø Aborting merge...\n'));
59
+ (0, child_process_1.execSync)('git merge --abort');
60
+ console.log(chalk_1.default.green('āœ… Merge aborted. You\'re back to a clean state.\n'));
61
+ return;
62
+ }
63
+ if (choice === '2') {
64
+ console.log(chalk_1.default.blue('\nšŸ“ Opening files for manual resolution...\n'));
65
+ console.log(chalk_1.default.gray('After resolving conflicts:'));
66
+ console.log(chalk_1.default.cyan(' git add <file>'));
67
+ console.log(chalk_1.default.cyan(' git commit'));
68
+ console.log();
69
+ return;
70
+ }
71
+ // AI-assisted resolution
72
+ for (const filePath of conflictFiles) {
73
+ await resolveConflictWithAI(filePath);
74
+ }
75
+ // Final commit
76
+ console.log(chalk_1.default.blue('\nāœ… All conflicts resolved!\n'));
77
+ const shouldCommit = await askQuestion(chalk_1.default.yellow('Commit the merge? (Y/n): '));
78
+ if (shouldCommit.toLowerCase() !== 'n') {
79
+ try {
80
+ (0, child_process_1.execSync)('git add -A');
81
+ (0, child_process_1.execSync)('git commit --no-edit');
82
+ console.log(chalk_1.default.green('\nāœ… Merge committed successfully!\n'));
83
+ (0, analytics_1.trackEvent)({ event: 'conflict_resolved', data: { filesCount: conflictFiles.length } });
84
+ }
85
+ catch (error) {
86
+ console.log(chalk_1.default.red(`\nāŒ Commit failed: ${error.message}\n`));
87
+ }
88
+ }
89
+ }
90
+ async function resolveConflictWithAI(filePath) {
91
+ console.log(chalk_1.default.blue(`\nšŸ” Analyzing ${chalk_1.default.white.bold(filePath)}...\n`));
92
+ // Read file content
93
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
94
+ // Extract conflict markers
95
+ const conflicts = extractConflicts(content);
96
+ console.log(chalk_1.default.yellow(`Found ${conflicts.length} conflict(s) in this file.\n`));
97
+ for (let i = 0; i < conflicts.length; i++) {
98
+ const conflict = conflicts[i];
99
+ console.log(chalk_1.default.white.bold(`Conflict ${i + 1}/${conflicts.length}:`));
100
+ console.log(chalk_1.default.gray('─'.repeat(50)));
101
+ console.log(chalk_1.default.green('<<<<<<< YOURS (current branch)'));
102
+ console.log(chalk_1.default.white(conflict.ours));
103
+ console.log(chalk_1.default.yellow('======='));
104
+ console.log(chalk_1.default.white(conflict.theirs));
105
+ console.log(chalk_1.default.blue('>>>>>>> THEIRS (incoming branch)'));
106
+ console.log(chalk_1.default.gray('─'.repeat(50)));
107
+ console.log();
108
+ // Get AI suggestion
109
+ console.log(chalk_1.default.blue('šŸ¤– Getting AI suggestion...\n'));
110
+ const suggestion = await getAISuggestion(conflict, filePath);
111
+ console.log(chalk_1.default.green('AI Suggestion:'));
112
+ console.log(chalk_1.default.white(suggestion.resolution));
113
+ console.log();
114
+ console.log(chalk_1.default.gray('Reasoning: ' + suggestion.explanation));
115
+ console.log();
116
+ const choice = await askQuestion(chalk_1.default.yellow('Choose:\n') +
117
+ chalk_1.default.gray(' 1. Use AI suggestion\n') +
118
+ chalk_1.default.gray(' 2. Keep yours\n') +
119
+ chalk_1.default.gray(' 3. Keep theirs\n') +
120
+ chalk_1.default.gray(' 4. Skip (manual edit)\n') +
121
+ chalk_1.default.yellow('Your choice (1/2/3/4): '));
122
+ let resolution;
123
+ if (choice === '1') {
124
+ resolution = suggestion.resolution;
125
+ }
126
+ else if (choice === '2') {
127
+ resolution = conflict.ours;
128
+ }
129
+ else if (choice === '3') {
130
+ resolution = conflict.theirs;
131
+ }
132
+ else {
133
+ console.log(chalk_1.default.yellow('Skipped. Resolve manually.\n'));
134
+ continue;
135
+ }
136
+ // Replace conflict in content
137
+ const conflictBlock = `<<<<<<< HEAD\n${conflict.ours}\n=======\n${conflict.theirs}\n>>>>>>> ${conflict.branch || 'MERGE_HEAD'}`;
138
+ const newContent = content.replace(conflictBlock, resolution);
139
+ fs_1.default.writeFileSync(filePath, newContent);
140
+ console.log(chalk_1.default.green('āœ… Resolved!\n'));
141
+ }
142
+ // Stage the file
143
+ (0, child_process_1.execSync)(`git add "${filePath}"`);
144
+ console.log(chalk_1.default.green(`āœ… Staged ${filePath}\n`));
145
+ }
146
+ function extractConflicts(content) {
147
+ const conflicts = [];
148
+ const lines = content.split('\n');
149
+ let inConflict = false;
150
+ let ours = [];
151
+ let theirs = [];
152
+ let inTheirs = false;
153
+ let branch;
154
+ for (const line of lines) {
155
+ if (line.startsWith('<<<<<<<')) {
156
+ inConflict = true;
157
+ ours = [];
158
+ theirs = [];
159
+ inTheirs = false;
160
+ }
161
+ else if (line.startsWith('=======')) {
162
+ inTheirs = true;
163
+ }
164
+ else if (line.startsWith('>>>>>>>')) {
165
+ inConflict = false;
166
+ branch = line.substring(8).trim();
167
+ conflicts.push({
168
+ ours: ours.join('\n'),
169
+ theirs: theirs.join('\n'),
170
+ branch,
171
+ });
172
+ }
173
+ else if (inConflict) {
174
+ if (inTheirs) {
175
+ theirs.push(line);
176
+ }
177
+ else {
178
+ ours.push(line);
179
+ }
180
+ }
181
+ }
182
+ return conflicts;
183
+ }
184
+ async function getAISuggestion(conflict, filePath) {
185
+ try {
186
+ const response = await anthropic.messages.create({
187
+ model: 'claude-sonnet-4-20250514',
188
+ max_tokens: 1000,
189
+ messages: [
190
+ {
191
+ role: 'user',
192
+ content: `You are an expert software engineer resolving a Git merge conflict.
193
+
194
+ File: ${filePath}
195
+
196
+ CURRENT BRANCH (ours):
197
+ \`\`\`
198
+ ${conflict.ours}
199
+ \`\`\`
200
+
201
+ INCOMING BRANCH (theirs):
202
+ \`\`\`
203
+ ${conflict.theirs}
204
+ \`\`\`
205
+
206
+ Analyze both versions and suggest the best resolution. Consider:
207
+ 1. Are they compatible? Can both changes be kept?
208
+ 2. Which version is more correct/better?
209
+ 3. Are there bugs or issues in either version?
210
+
211
+ Respond with JSON (no markdown, just JSON):
212
+ {
213
+ "resolution": "the merged code",
214
+ "explanation": "why this is the best resolution"
215
+ }`,
216
+ },
217
+ ],
218
+ });
219
+ const text = response.content[0];
220
+ if (text.type !== 'text') {
221
+ throw new Error('Unexpected response');
222
+ }
223
+ const result = JSON.parse(text.text);
224
+ return result;
225
+ }
226
+ catch (error) {
227
+ // Fallback: suggest keeping both
228
+ return {
229
+ resolution: `${conflict.ours}\n${conflict.theirs}`,
230
+ explanation: 'AI suggestion failed. Keeping both versions (you may need to clean up).',
231
+ };
232
+ }
233
+ }
@@ -0,0 +1,113 @@
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.doctorCommand = doctorCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const child_process_1 = require("child_process");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const path_1 = __importDefault(require("path"));
12
+ /**
13
+ * Doctor command - checks if everything is set up correctly
14
+ */
15
+ function doctorCommand() {
16
+ console.log(chalk_1.default.blue.bold('\nšŸ” SnapCommit Health Check\n'));
17
+ let allGood = true;
18
+ // Check 1: Git installed
19
+ console.log(chalk_1.default.white('Checking Git...'));
20
+ try {
21
+ const gitVersion = (0, child_process_1.execSync)('git --version', { encoding: 'utf-8' }).trim();
22
+ console.log(chalk_1.default.green(` āœ“ ${gitVersion}`));
23
+ }
24
+ catch {
25
+ console.log(chalk_1.default.red(' āœ— Git not found'));
26
+ console.log(chalk_1.default.gray(' Install: https://git-scm.com'));
27
+ allGood = false;
28
+ }
29
+ // Check 2: Node.js version
30
+ console.log(chalk_1.default.white('\nChecking Node.js...'));
31
+ const nodeVersion = process.version;
32
+ const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
33
+ if (majorVersion >= 18) {
34
+ console.log(chalk_1.default.green(` āœ“ ${nodeVersion} (good)`));
35
+ }
36
+ else {
37
+ console.log(chalk_1.default.yellow(` ⚠ ${nodeVersion} (recommend v18+)`));
38
+ }
39
+ // Check 3: API Keys
40
+ console.log(chalk_1.default.white('\nChecking API Keys...'));
41
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
42
+ if (anthropicKey && anthropicKey.startsWith('sk-ant')) {
43
+ console.log(chalk_1.default.green(' āœ“ ANTHROPIC_API_KEY set'));
44
+ }
45
+ else {
46
+ console.log(chalk_1.default.red(' āœ— ANTHROPIC_API_KEY missing or invalid'));
47
+ console.log(chalk_1.default.gray(' Get key: https://console.anthropic.com'));
48
+ allGood = false;
49
+ }
50
+ const geminiKey = process.env.GOOGLE_AI_API_KEY;
51
+ if (geminiKey && geminiKey.startsWith('AIza')) {
52
+ console.log(chalk_1.default.green(' āœ“ GOOGLE_AI_API_KEY set'));
53
+ }
54
+ else {
55
+ console.log(chalk_1.default.yellow(' ⚠ GOOGLE_AI_API_KEY missing'));
56
+ console.log(chalk_1.default.gray(' Optional for now, needed for autocomplete'));
57
+ }
58
+ // Check 4: Shell integration
59
+ console.log(chalk_1.default.white('\nChecking Shell Integration...'));
60
+ const shell = process.env.SHELL || '';
61
+ const homeDir = os_1.default.homedir();
62
+ let shellConfigPath = '';
63
+ if (shell.includes('zsh')) {
64
+ shellConfigPath = path_1.default.join(homeDir, '.zshrc');
65
+ }
66
+ else if (shell.includes('bash')) {
67
+ shellConfigPath = path_1.default.join(homeDir, '.bashrc');
68
+ }
69
+ if (shellConfigPath && fs_1.default.existsSync(shellConfigPath)) {
70
+ const content = fs_1.default.readFileSync(shellConfigPath, 'utf-8');
71
+ if (content.includes('SnapCommit Integration')) {
72
+ console.log(chalk_1.default.green(' āœ“ Shell integration installed'));
73
+ }
74
+ else {
75
+ console.log(chalk_1.default.yellow(' ⚠ Shell integration not installed'));
76
+ console.log(chalk_1.default.gray(' Run: snapcommit setup'));
77
+ }
78
+ }
79
+ else {
80
+ console.log(chalk_1.default.gray(' - Shell config not found'));
81
+ }
82
+ // Check 5: Database
83
+ console.log(chalk_1.default.white('\nChecking Database...'));
84
+ const dbPath = path_1.default.join(homeDir, '.builderos', 'builderos.db');
85
+ if (fs_1.default.existsSync(dbPath)) {
86
+ const stats = fs_1.default.statSync(dbPath);
87
+ const sizeKB = (stats.size / 1024).toFixed(2);
88
+ console.log(chalk_1.default.green(` āœ“ Database exists (${sizeKB} KB)`));
89
+ }
90
+ else {
91
+ console.log(chalk_1.default.gray(' - Database will be created on first use'));
92
+ }
93
+ // Check 6: Git repo (current directory)
94
+ console.log(chalk_1.default.white('\nChecking Current Directory...'));
95
+ try {
96
+ (0, child_process_1.execSync)('git rev-parse --git-dir', { stdio: 'ignore' });
97
+ const branch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
98
+ console.log(chalk_1.default.green(` āœ“ Git repository (${branch})`));
99
+ }
100
+ catch {
101
+ console.log(chalk_1.default.gray(' - Not in a git repository'));
102
+ }
103
+ // Summary
104
+ console.log();
105
+ if (allGood) {
106
+ console.log(chalk_1.default.green.bold('āœ… All systems go! SnapCommit is ready.\n'));
107
+ console.log(chalk_1.default.gray('Try: ') + chalk_1.default.cyan('snapcommit quick') + chalk_1.default.gray(' to make your first commit'));
108
+ }
109
+ else {
110
+ console.log(chalk_1.default.yellow.bold('āš ļø Some issues found. Fix them to get started.\n'));
111
+ }
112
+ console.log();
113
+ }
@@ -0,0 +1,311 @@
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.stashChanges = stashChanges;
7
+ exports.listStash = listStash;
8
+ exports.applyStash = applyStash;
9
+ exports.popStash = popStash;
10
+ exports.dropStash = dropStash;
11
+ exports.rebaseOnto = rebaseOnto;
12
+ exports.interactiveRebase = interactiveRebase;
13
+ exports.cherryPick = cherryPick;
14
+ exports.amendCommit = amendCommit;
15
+ exports.resetToCommit = resetToCommit;
16
+ exports.createBranch = createBranch;
17
+ exports.deleteBranch = deleteBranch;
18
+ exports.switchBranch = switchBranch;
19
+ exports.mergeBranch = mergeBranch;
20
+ exports.showLog = showLog;
21
+ exports.showDiff = showDiff;
22
+ const child_process_1 = require("child_process");
23
+ const chalk_1 = __importDefault(require("chalk"));
24
+ const readline_1 = __importDefault(require("readline"));
25
+ /**
26
+ * Stash current changes
27
+ */
28
+ async function stashChanges(message) {
29
+ try {
30
+ console.log(chalk_1.default.gray('\nšŸ’¾ Stashing changes...\n'));
31
+ if (message) {
32
+ (0, child_process_1.execSync)(`git stash push -m "${message}"`, { stdio: 'inherit' });
33
+ }
34
+ else {
35
+ (0, child_process_1.execSync)('git stash', { stdio: 'inherit' });
36
+ }
37
+ console.log(chalk_1.default.green('\nāœ… Changes stashed!\n'));
38
+ }
39
+ catch (error) {
40
+ console.log(chalk_1.default.red(`\nāŒ Failed to stash: ${error.message}\n`));
41
+ }
42
+ }
43
+ /**
44
+ * List stashed changes
45
+ */
46
+ function listStash() {
47
+ try {
48
+ console.log(chalk_1.default.bold('\nšŸ“¦ Stashed Changes:\n'));
49
+ (0, child_process_1.execSync)('git stash list', { stdio: 'inherit' });
50
+ console.log('');
51
+ }
52
+ catch (error) {
53
+ console.log(chalk_1.default.red(`\nāŒ Failed to list stash: ${error.message}\n`));
54
+ }
55
+ }
56
+ /**
57
+ * Apply stashed changes
58
+ */
59
+ async function applyStash(stashIndex) {
60
+ try {
61
+ console.log(chalk_1.default.gray('\nšŸ“‚ Applying stashed changes...\n'));
62
+ if (stashIndex !== undefined) {
63
+ (0, child_process_1.execSync)(`git stash apply stash@{${stashIndex}}`, { stdio: 'inherit' });
64
+ }
65
+ else {
66
+ (0, child_process_1.execSync)('git stash apply', { stdio: 'inherit' });
67
+ }
68
+ console.log(chalk_1.default.green('\nāœ… Stash applied!\n'));
69
+ }
70
+ catch (error) {
71
+ console.log(chalk_1.default.red(`\nāŒ Failed to apply stash: ${error.message}\n`));
72
+ }
73
+ }
74
+ /**
75
+ * Pop stashed changes (apply and remove)
76
+ */
77
+ async function popStash() {
78
+ try {
79
+ console.log(chalk_1.default.gray('\nšŸ“‚ Popping stashed changes...\n'));
80
+ (0, child_process_1.execSync)('git stash pop', { stdio: 'inherit' });
81
+ console.log(chalk_1.default.green('\nāœ… Stash popped!\n'));
82
+ }
83
+ catch (error) {
84
+ console.log(chalk_1.default.red(`\nāŒ Failed to pop stash: ${error.message}\n`));
85
+ }
86
+ }
87
+ /**
88
+ * Drop stashed changes
89
+ */
90
+ async function dropStash(stashIndex) {
91
+ try {
92
+ const confirmed = await confirmAction('This will permanently delete the stashed changes. Continue?');
93
+ if (!confirmed) {
94
+ console.log(chalk_1.default.gray('\n🚫 Cancelled\n'));
95
+ return;
96
+ }
97
+ console.log(chalk_1.default.gray('\nšŸ—‘ļø Dropping stash...\n'));
98
+ if (stashIndex !== undefined) {
99
+ (0, child_process_1.execSync)(`git stash drop stash@{${stashIndex}}`, { stdio: 'inherit' });
100
+ }
101
+ else {
102
+ (0, child_process_1.execSync)('git stash drop', { stdio: 'inherit' });
103
+ }
104
+ console.log(chalk_1.default.green('\nāœ… Stash dropped!\n'));
105
+ }
106
+ catch (error) {
107
+ console.log(chalk_1.default.red(`\nāŒ Failed to drop stash: ${error.message}\n`));
108
+ }
109
+ }
110
+ /**
111
+ * Rebase current branch onto another
112
+ */
113
+ async function rebaseOnto(targetBranch) {
114
+ try {
115
+ const confirmed = await confirmAction(`This will rebase your current branch onto ${targetBranch}. This rewrites history. Continue?`);
116
+ if (!confirmed) {
117
+ console.log(chalk_1.default.gray('\n🚫 Cancelled\n'));
118
+ return;
119
+ }
120
+ console.log(chalk_1.default.gray(`\nšŸ”„ Rebasing onto ${targetBranch}...\n`));
121
+ (0, child_process_1.execSync)(`git rebase ${targetBranch}`, { stdio: 'inherit' });
122
+ console.log(chalk_1.default.green('\nāœ… Rebase complete!\n'));
123
+ }
124
+ catch (error) {
125
+ console.log(chalk_1.default.red(`\nāŒ Rebase failed. You may need to resolve conflicts.\n`));
126
+ console.log(chalk_1.default.yellow('Run "git rebase --abort" to cancel, or resolve conflicts and "git rebase --continue"\n'));
127
+ }
128
+ }
129
+ /**
130
+ * Interactive rebase
131
+ */
132
+ async function interactiveRebase(commits = 5) {
133
+ try {
134
+ const confirmed = await confirmAction(`This will start an interactive rebase for the last ${commits} commits. Continue?`);
135
+ if (!confirmed) {
136
+ console.log(chalk_1.default.gray('\n🚫 Cancelled\n'));
137
+ return;
138
+ }
139
+ console.log(chalk_1.default.gray(`\nšŸ”„ Starting interactive rebase...\n`));
140
+ (0, child_process_1.execSync)(`git rebase -i HEAD~${commits}`, { stdio: 'inherit' });
141
+ console.log(chalk_1.default.green('\nāœ… Interactive rebase complete!\n'));
142
+ }
143
+ catch (error) {
144
+ console.log(chalk_1.default.red(`\nāŒ Interactive rebase failed or cancelled.\n`));
145
+ }
146
+ }
147
+ /**
148
+ * Cherry-pick a commit
149
+ */
150
+ async function cherryPick(commitHash) {
151
+ try {
152
+ console.log(chalk_1.default.gray(`\nšŸ’ Cherry-picking commit ${commitHash}...\n`));
153
+ (0, child_process_1.execSync)(`git cherry-pick ${commitHash}`, { stdio: 'inherit' });
154
+ console.log(chalk_1.default.green('\nāœ… Cherry-pick complete!\n'));
155
+ }
156
+ catch (error) {
157
+ console.log(chalk_1.default.red(`\nāŒ Cherry-pick failed. You may need to resolve conflicts.\n`));
158
+ console.log(chalk_1.default.yellow('Run "git cherry-pick --abort" to cancel, or resolve conflicts and "git cherry-pick --continue"\n'));
159
+ }
160
+ }
161
+ /**
162
+ * Amend last commit
163
+ */
164
+ async function amendCommit(message) {
165
+ try {
166
+ console.log(chalk_1.default.gray('\nāœļø Amending last commit...\n'));
167
+ if (message) {
168
+ (0, child_process_1.execSync)(`git commit --amend -m "${message}"`, { stdio: 'inherit' });
169
+ }
170
+ else {
171
+ (0, child_process_1.execSync)('git commit --amend --no-edit', { stdio: 'inherit' });
172
+ }
173
+ console.log(chalk_1.default.green('\nāœ… Commit amended!\n'));
174
+ }
175
+ catch (error) {
176
+ console.log(chalk_1.default.red(`\nāŒ Failed to amend commit: ${error.message}\n`));
177
+ }
178
+ }
179
+ /**
180
+ * Reset to a specific commit
181
+ */
182
+ async function resetToCommit(commitHash, mode = 'mixed') {
183
+ try {
184
+ const warnings = {
185
+ soft: 'This will undo commits but keep all changes staged.',
186
+ mixed: 'This will undo commits and unstage changes, but keep them in working directory.',
187
+ hard: 'āš ļø This will PERMANENTLY delete all changes! Cannot be undone!',
188
+ };
189
+ const confirmed = await confirmAction(`${warnings[mode]} Continue?`);
190
+ if (!confirmed) {
191
+ console.log(chalk_1.default.gray('\n🚫 Cancelled\n'));
192
+ return;
193
+ }
194
+ console.log(chalk_1.default.gray(`\nšŸ”„ Resetting to ${commitHash} (--${mode})...\n`));
195
+ (0, child_process_1.execSync)(`git reset --${mode} ${commitHash}`, { stdio: 'inherit' });
196
+ console.log(chalk_1.default.green('\nāœ… Reset complete!\n'));
197
+ }
198
+ catch (error) {
199
+ console.log(chalk_1.default.red(`\nāŒ Failed to reset: ${error.message}\n`));
200
+ }
201
+ }
202
+ /**
203
+ * Create and checkout a new branch
204
+ */
205
+ async function createBranch(branchName, fromBranch) {
206
+ try {
207
+ console.log(chalk_1.default.gray(`\n🌿 Creating branch ${branchName}...\n`));
208
+ if (fromBranch) {
209
+ (0, child_process_1.execSync)(`git checkout -b ${branchName} ${fromBranch}`, { stdio: 'inherit' });
210
+ }
211
+ else {
212
+ (0, child_process_1.execSync)(`git checkout -b ${branchName}`, { stdio: 'inherit' });
213
+ }
214
+ console.log(chalk_1.default.green(`\nāœ… Created and switched to ${branchName}!\n`));
215
+ }
216
+ catch (error) {
217
+ console.log(chalk_1.default.red(`\nāŒ Failed to create branch: ${error.message}\n`));
218
+ }
219
+ }
220
+ /**
221
+ * Delete a branch
222
+ */
223
+ async function deleteBranch(branchName, force = false) {
224
+ try {
225
+ const confirmed = await confirmAction(`This will delete the branch "${branchName}". Continue?`);
226
+ if (!confirmed) {
227
+ console.log(chalk_1.default.gray('\n🚫 Cancelled\n'));
228
+ return;
229
+ }
230
+ console.log(chalk_1.default.gray(`\nšŸ—‘ļø Deleting branch ${branchName}...\n`));
231
+ const flag = force ? '-D' : '-d';
232
+ (0, child_process_1.execSync)(`git branch ${flag} ${branchName}`, { stdio: 'inherit' });
233
+ console.log(chalk_1.default.green(`\nāœ… Branch ${branchName} deleted!\n`));
234
+ }
235
+ catch (error) {
236
+ console.log(chalk_1.default.red(`\nāŒ Failed to delete branch: ${error.message}\n`));
237
+ if (!force) {
238
+ console.log(chalk_1.default.yellow('šŸ’” Use force delete if branch has unmerged changes\n'));
239
+ }
240
+ }
241
+ }
242
+ /**
243
+ * Switch to a branch
244
+ */
245
+ async function switchBranch(branchName) {
246
+ try {
247
+ console.log(chalk_1.default.gray(`\nšŸ”„ Switching to ${branchName}...\n`));
248
+ (0, child_process_1.execSync)(`git checkout ${branchName}`, { stdio: 'inherit' });
249
+ console.log(chalk_1.default.green(`\nāœ… Switched to ${branchName}!\n`));
250
+ }
251
+ catch (error) {
252
+ console.log(chalk_1.default.red(`\nāŒ Failed to switch branch: ${error.message}\n`));
253
+ }
254
+ }
255
+ /**
256
+ * Merge a branch
257
+ */
258
+ async function mergeBranch(branchName, noFastForward = false) {
259
+ try {
260
+ console.log(chalk_1.default.gray(`\nšŸ”€ Merging ${branchName}...\n`));
261
+ const flag = noFastForward ? '--no-ff' : '';
262
+ (0, child_process_1.execSync)(`git merge ${flag} ${branchName}`, { stdio: 'inherit' });
263
+ console.log(chalk_1.default.green(`\nāœ… Merged ${branchName}!\n`));
264
+ }
265
+ catch (error) {
266
+ console.log(chalk_1.default.red(`\nāŒ Merge failed. You may need to resolve conflicts.\n`));
267
+ console.log(chalk_1.default.yellow('Run "git merge --abort" to cancel, or resolve conflicts and commit\n'));
268
+ }
269
+ }
270
+ /**
271
+ * Show commit log
272
+ */
273
+ function showLog(limit = 10) {
274
+ try {
275
+ console.log(chalk_1.default.bold(`\nšŸ“œ Last ${limit} Commits:\n`));
276
+ (0, child_process_1.execSync)(`git log --oneline --graph --decorate -${limit}`, { stdio: 'inherit' });
277
+ console.log('');
278
+ }
279
+ catch (error) {
280
+ console.log(chalk_1.default.red(`\nāŒ Failed to show log: ${error.message}\n`));
281
+ }
282
+ }
283
+ /**
284
+ * Show diff
285
+ */
286
+ function showDiff(cached = false) {
287
+ try {
288
+ const flag = cached ? '--cached' : '';
289
+ console.log(chalk_1.default.bold('\nšŸ“Š Changes:\n'));
290
+ (0, child_process_1.execSync)(`git diff ${flag}`, { stdio: 'inherit' });
291
+ console.log('');
292
+ }
293
+ catch (error) {
294
+ console.log(chalk_1.default.red(`\nāŒ Failed to show diff: ${error.message}\n`));
295
+ }
296
+ }
297
+ /**
298
+ * Helper: Confirm action
299
+ */
300
+ async function confirmAction(message) {
301
+ return new Promise((resolve) => {
302
+ const rl = readline_1.default.createInterface({
303
+ input: process.stdin,
304
+ output: process.stdout,
305
+ });
306
+ rl.question(chalk_1.default.yellow(`\nāš ļø ${message} (y/N): `), (answer) => {
307
+ rl.close();
308
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
309
+ });
310
+ });
311
+ }