@snapcommit/cli 1.2.1 → 2.0.1

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.
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ /**
3
+ * Cursor-style natural language Git assistant
4
+ * Shows preview, asks confirmation, handles errors automatically
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.executeCursorStyle = executeCursorStyle;
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const child_process_1 = require("child_process");
13
+ const readline_1 = __importDefault(require("readline"));
14
+ const git_1 = require("../utils/git");
15
+ const auth_1 = require("../lib/auth");
16
+ async function executeCursorStyle(userInput) {
17
+ if (!(0, git_1.isGitRepo)()) {
18
+ console.log(chalk_1.default.red('\n❌ Not a git repository\n'));
19
+ return;
20
+ }
21
+ // Get AI interpretation of the request
22
+ console.log(chalk_1.default.blue('\n✨ Understanding your request...\n'));
23
+ const plan = await interpretRequest(userInput);
24
+ if (!plan) {
25
+ console.log(chalk_1.default.red('❌ Could not understand the request\n'));
26
+ return;
27
+ }
28
+ // Show the execution plan
29
+ console.log(chalk_1.default.bold('📋 Here\'s what I\'ll do:\n'));
30
+ plan.actions.forEach((action, i) => {
31
+ console.log(chalk_1.default.gray(` ${i + 1}. `) + chalk_1.default.white(action.description));
32
+ });
33
+ console.log();
34
+ // If commit is involved, show the AI-generated message
35
+ if (plan.commitMessage) {
36
+ console.log(chalk_1.default.gray('📝 Commit message: ') + chalk_1.default.cyan(`"${plan.commitMessage}"`));
37
+ console.log();
38
+ }
39
+ // Ask for confirmation
40
+ const needsConfirm = plan.actions.some(a => a.requiresConfirmation);
41
+ if (needsConfirm) {
42
+ const confirmOptions = plan.commitMessage
43
+ ? chalk_1.default.yellow('Continue? (Y/n/e to edit message): ')
44
+ : chalk_1.default.yellow('Continue? (Y/n): ');
45
+ const answer = await askQuestion(confirmOptions);
46
+ if (answer.toLowerCase() === 'n') {
47
+ console.log(chalk_1.default.gray('\n✗ Cancelled\n'));
48
+ return;
49
+ }
50
+ if (answer.toLowerCase() === 'e' && plan.commitMessage) {
51
+ plan.commitMessage = await editMessage(plan.commitMessage);
52
+ }
53
+ }
54
+ // Execute the plan
55
+ console.log(chalk_1.default.blue('\n⚙️ Executing...\n'));
56
+ for (const action of plan.actions) {
57
+ try {
58
+ await executeAction(action, plan.commitMessage);
59
+ }
60
+ catch (error) {
61
+ // Try to auto-fix common errors
62
+ const fixed = await tryAutoFix(error, action);
63
+ if (!fixed) {
64
+ console.log(chalk_1.default.red(`\n❌ Error: ${error.message}\n`));
65
+ console.log(chalk_1.default.yellow('💡 Try: "undo last change" to revert\n'));
66
+ return;
67
+ }
68
+ }
69
+ }
70
+ console.log(chalk_1.default.green('\n✅ All done!\n'));
71
+ }
72
+ async function interpretRequest(userInput) {
73
+ // This will call the backend AI to interpret the request
74
+ // For now, let's handle some common patterns
75
+ const input = userInput.toLowerCase();
76
+ const actions = [];
77
+ let commitMessage;
78
+ // "commit and push" pattern
79
+ if (input.includes('commit') && input.includes('push')) {
80
+ // Generate commit message
81
+ const diff = await getChangeDiff();
82
+ if (diff) {
83
+ commitMessage = await generateCommitMessage(diff);
84
+ }
85
+ actions.push({
86
+ type: 'stage',
87
+ description: 'Stage all changes',
88
+ requiresConfirmation: false,
89
+ });
90
+ actions.push({
91
+ type: 'commit',
92
+ description: `Commit with message: "${commitMessage || 'Update'}"`,
93
+ requiresConfirmation: true,
94
+ requiresCommitMessage: true,
95
+ });
96
+ actions.push({
97
+ type: 'push',
98
+ description: 'Push to origin',
99
+ command: 'git push',
100
+ requiresConfirmation: true,
101
+ });
102
+ return {
103
+ actions,
104
+ commitMessage,
105
+ explanation: 'Stage all changes, commit with AI-generated message, and push to remote',
106
+ };
107
+ }
108
+ // "commit" only
109
+ if (input.includes('commit') && !input.includes('push')) {
110
+ const diff = await getChangeDiff();
111
+ if (diff) {
112
+ commitMessage = await generateCommitMessage(diff);
113
+ }
114
+ actions.push({
115
+ type: 'stage',
116
+ description: 'Stage all changes',
117
+ requiresConfirmation: false,
118
+ });
119
+ actions.push({
120
+ type: 'commit',
121
+ description: `Commit with message: "${commitMessage || 'Update'}"`,
122
+ requiresConfirmation: true,
123
+ requiresCommitMessage: true,
124
+ });
125
+ return {
126
+ actions,
127
+ commitMessage,
128
+ explanation: 'Stage and commit changes with AI-generated message',
129
+ };
130
+ }
131
+ // Let the AI handle complex requests
132
+ return await getAIInterpretation(userInput);
133
+ }
134
+ async function getChangeDiff() {
135
+ try {
136
+ (0, git_1.stageAllChanges)();
137
+ return (0, git_1.getGitDiff)(true);
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ async function generateCommitMessage(diff) {
144
+ const token = (0, auth_1.getToken)();
145
+ if (!token)
146
+ return 'Update changes';
147
+ try {
148
+ // Truncate large diffs
149
+ if (diff.length > 40000) {
150
+ diff = diff.substring(0, 40000);
151
+ }
152
+ const response = await fetch('https://snapcommit.dev/api/ai/commit', {
153
+ method: 'POST',
154
+ headers: { 'Content-Type': 'application/json' },
155
+ body: JSON.stringify({ diff, token }),
156
+ });
157
+ const data = await response.json();
158
+ return data.message || 'Update changes';
159
+ }
160
+ catch {
161
+ return 'Update changes';
162
+ }
163
+ }
164
+ async function getAIInterpretation(userInput) {
165
+ const token = (0, auth_1.getToken)();
166
+ if (!token)
167
+ return null;
168
+ try {
169
+ const currentBranch = (0, git_1.getCurrentBranch)();
170
+ const status = (0, git_1.getGitStatus)();
171
+ const response = await fetch('https://snapcommit.dev/api/ai/interpret', {
172
+ method: 'POST',
173
+ headers: { 'Content-Type': 'application/json' },
174
+ body: JSON.stringify({
175
+ userInput,
176
+ token,
177
+ context: {
178
+ currentBranch,
179
+ hasUncommittedChanges: status.unstaged > 0 || status.untracked > 0,
180
+ lastCommitHash: (0, child_process_1.execSync)('git log -1 --format=%H 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim(),
181
+ remoteBranch: (0, child_process_1.execSync)('git rev-parse --abbrev-ref @{upstream} 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim(),
182
+ },
183
+ }),
184
+ });
185
+ const data = await response.json();
186
+ // Convert AI intent to execution plan
187
+ // This is a simplified version - in production, this would be more sophisticated
188
+ return convertIntentToPlan(data.intent);
189
+ }
190
+ catch {
191
+ return null;
192
+ }
193
+ }
194
+ function convertIntentToPlan(intent) {
195
+ // Convert the AI intent format to our execution plan format
196
+ // This bridges the existing intent format with the new Cursor-style execution
197
+ if (!intent || !intent.gitCommands)
198
+ return null;
199
+ const actions = intent.gitCommands.map((cmd, i) => ({
200
+ type: inferActionType(cmd),
201
+ description: intent.explanation || `Execute: ${cmd}`,
202
+ command: cmd,
203
+ requiresConfirmation: intent.needsConfirmation || intent.riskLevel !== 'safe',
204
+ }));
205
+ return {
206
+ actions,
207
+ explanation: intent.explanation,
208
+ };
209
+ }
210
+ function inferActionType(command) {
211
+ if (command.includes('git add'))
212
+ return 'stage';
213
+ if (command.includes('git commit'))
214
+ return 'commit';
215
+ if (command.includes('git push'))
216
+ return 'push';
217
+ if (command.includes('git checkout') || command.includes('git switch'))
218
+ return 'checkout';
219
+ if (command.includes('git merge'))
220
+ return 'merge';
221
+ if (command.includes('git reset'))
222
+ return 'reset';
223
+ if (command.includes('git branch'))
224
+ return 'branch';
225
+ if (command.includes('git pull'))
226
+ return 'pull';
227
+ if (command.includes('git rebase'))
228
+ return 'rebase';
229
+ if (command.includes('git cherry-pick'))
230
+ return 'cherry-pick';
231
+ return 'stage';
232
+ }
233
+ async function executeAction(action, commitMessage) {
234
+ switch (action.type) {
235
+ case 'stage':
236
+ (0, git_1.stageAllChanges)();
237
+ console.log(chalk_1.default.green(' ✓ Staged all changes'));
238
+ break;
239
+ case 'commit':
240
+ if (commitMessage) {
241
+ (0, child_process_1.execSync)(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, { encoding: 'utf-8' });
242
+ console.log(chalk_1.default.green(` ✓ Committed`));
243
+ }
244
+ break;
245
+ case 'push':
246
+ (0, child_process_1.execSync)('git push', { encoding: 'utf-8', stdio: 'pipe' });
247
+ console.log(chalk_1.default.green(' ✓ Pushed to remote'));
248
+ break;
249
+ default:
250
+ if (action.command) {
251
+ (0, child_process_1.execSync)(action.command, { encoding: 'utf-8', stdio: 'pipe' });
252
+ console.log(chalk_1.default.green(` ✓ ${action.description}`));
253
+ }
254
+ break;
255
+ }
256
+ }
257
+ async function tryAutoFix(error, action) {
258
+ const errorMsg = error.message?.toLowerCase() || '';
259
+ // Handle "no remote configured"
260
+ if (errorMsg.includes('no configured push destination')) {
261
+ console.log(chalk_1.default.yellow('\n⚠️ No remote repository configured'));
262
+ console.log(chalk_1.default.gray(' You need to add a remote first\n'));
263
+ return false;
264
+ }
265
+ // Handle merge conflicts
266
+ if (errorMsg.includes('conflict')) {
267
+ console.log(chalk_1.default.yellow('\n⚠️ Merge conflict detected'));
268
+ console.log(chalk_1.default.gray(' Use: "resolve conflicts" to fix\n'));
269
+ return false;
270
+ }
271
+ // Handle auth failures
272
+ if (errorMsg.includes('authentication') || errorMsg.includes('permission denied')) {
273
+ console.log(chalk_1.default.yellow('\n⚠️ Authentication failed'));
274
+ console.log(chalk_1.default.gray(' Check your Git credentials\n'));
275
+ return false;
276
+ }
277
+ return false;
278
+ }
279
+ function askQuestion(query) {
280
+ const rl = readline_1.default.createInterface({
281
+ input: process.stdin,
282
+ output: process.stdout,
283
+ });
284
+ return new Promise((resolve) => {
285
+ rl.question(query, (answer) => {
286
+ rl.close();
287
+ resolve(answer);
288
+ });
289
+ });
290
+ }
291
+ async function editMessage(originalMessage) {
292
+ console.log(chalk_1.default.cyan('\n✏️ Edit commit message:\n'));
293
+ console.log(chalk_1.default.gray(' (Press Enter to keep, or type new message)\n'));
294
+ console.log(chalk_1.default.white(` Current: ${chalk_1.default.cyan(originalMessage)}\n`));
295
+ const newMessage = await askQuestion(chalk_1.default.yellow(' New message: '));
296
+ return newMessage.trim() || originalMessage;
297
+ }
@@ -1,97 +1,20 @@
1
1
  "use strict";
2
2
  /**
3
- * Natural language Git command handler
4
- * The magic command that understands what you want
3
+ * Natural language Git command handler - Cursor-style!
4
+ * Just talk, we'll handle everything
5
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
6
  var __importDefault = (this && this.__importDefault) || function (mod) {
40
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
41
8
  };
42
9
  Object.defineProperty(exports, "__esModule", { value: true });
43
10
  exports.naturalCommand = naturalCommand;
44
11
  const chalk_1 = __importDefault(require("chalk"));
45
- const child_process_1 = require("child_process");
46
- const readline_1 = __importDefault(require("readline"));
47
- const git_interpreter_1 = require("../ai/git-interpreter");
48
- const git_1 = require("../utils/git");
49
- const anthropic_client_1 = require("../ai/anthropic-client");
50
- const git_2 = require("../utils/git");
51
- const database_1 = require("../db/database");
52
- const analytics_1 = require("../utils/analytics");
53
- const manager_1 = require("../license/manager");
54
- const smart_solver_1 = require("../ai/smart-solver");
12
+ const cursor_style_1 = require("./cursor-style");
55
13
  const auth_1 = require("../lib/auth");
56
- function askQuestion(query) {
57
- const rl = readline_1.default.createInterface({
58
- input: process.stdin,
59
- output: process.stdout,
60
- });
61
- return new Promise(resolve => rl.question(query, ans => {
62
- rl.close();
63
- resolve(ans);
64
- }));
65
- }
66
- /**
67
- * Execute git commands with visual feedback
68
- */
69
- function executeGitCommands(commands, showCommands = true) {
70
- const results = [];
71
- for (const cmd of commands) {
72
- if (showCommands) {
73
- console.log(chalk_1.default.gray(` $ ${cmd}`));
74
- }
75
- try {
76
- const output = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8', stdio: 'pipe' });
77
- if (output.trim()) {
78
- if (showCommands) {
79
- console.log(chalk_1.default.gray(` ${output.trim().split('\n').join('\n ')}`));
80
- }
81
- results.push(output.trim());
82
- }
83
- }
84
- catch (error) {
85
- if (showCommands) {
86
- console.log(chalk_1.default.red(` Error: ${error.message}`));
87
- }
88
- return { success: false, output: results.join('\n'), error: error.message };
89
- }
90
- }
91
- return { success: true, output: results.join('\n') };
92
- }
14
+ const git_1 = require("../utils/git");
93
15
  /**
94
- * Main natural language command handler
16
+ * Main natural language command handler - Cursor-style!
17
+ * Now everything works like Cursor - just say what you want!
95
18
  */
96
19
  async function naturalCommand(userInput) {
97
20
  // Ensure authentication first
@@ -106,325 +29,6 @@ async function naturalCommand(userInput) {
106
29
  console.log(chalk_1.default.gray(' Navigate to a git repository first\n'));
107
30
  return;
108
31
  }
109
- // Get context
110
- const currentBranch = (0, git_1.getCurrentBranch)();
111
- const status = (0, git_1.getGitStatus)();
112
- const hasUncommittedChanges = status.staged > 0 || status.unstaged > 0;
113
- let lastCommitHash;
114
- try {
115
- lastCommitHash = (0, child_process_1.execSync)('git log -1 --format=%H', { encoding: 'utf-8' }).trim();
116
- }
117
- catch {
118
- lastCommitHash = undefined;
119
- }
120
- // Check if we need clarification
121
- const clarification = await (0, smart_solver_1.needsClarification)(userInput);
122
- if (clarification.needsClarification && clarification.options) {
123
- console.log(chalk_1.default.yellow('\n🤔 I need more information:\n'));
124
- clarification.options.forEach((option, i) => {
125
- console.log(chalk_1.default.white(` ${i + 1}. ${option.label}`));
126
- });
127
- console.log();
128
- const answer = await askQuestion(chalk_1.default.yellow(`Which one? (1-${clarification.options.length}): `));
129
- const choice = parseInt(answer) - 1;
130
- if (choice >= 0 && choice < clarification.options.length) {
131
- const selectedOption = clarification.options[choice];
132
- userInput = selectedOption.value; // Use the clarified intent
133
- }
134
- else {
135
- console.log(chalk_1.default.gray('\nInvalid choice. Cancelled.\n'));
136
- return;
137
- }
138
- }
139
- console.log(chalk_1.default.blue('\n✨ Understanding your request...\n'));
140
- // Show spinner
141
- const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
142
- let i = 0;
143
- const interval = setInterval(() => {
144
- process.stdout.write(chalk_1.default.blue(`\r${spinner[i++ % spinner.length]} Thinking...`));
145
- }, 100);
146
- // Interpret user input
147
- let intent;
148
- try {
149
- intent = await (0, git_interpreter_1.interpretGitCommand)(userInput, {
150
- currentBranch,
151
- hasUncommittedChanges,
152
- lastCommitHash,
153
- });
154
- }
155
- catch (error) {
156
- clearInterval(interval);
157
- process.stdout.write('\r' + ' '.repeat(20) + '\r');
158
- // Try smart solver as ultimate fallback
159
- console.log(chalk_1.default.yellow('🧠 Using advanced problem solver...\n'));
160
- const gitStatusOutput = (0, child_process_1.execSync)('git status', { encoding: 'utf-8' });
161
- const result = await (0, smart_solver_1.solveGitProblem)(userInput, {
162
- currentBranch,
163
- gitStatus: gitStatusOutput,
164
- recentError: error.message,
165
- });
166
- if (result.success) {
167
- await showDopamineStats();
168
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_SUCCESS });
169
- }
170
- else {
171
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_ERROR });
172
- }
173
- return;
174
- }
175
- clearInterval(interval);
176
- process.stdout.write('\r' + ' '.repeat(20) + '\r');
177
- // Check if this is a GitHub operation
178
- const intentType = intent.type || 'git';
179
- if (intentType === 'github') {
180
- // Handle GitHub operations
181
- await handleGitHubOperation(intent);
182
- return;
183
- }
184
- // Show intent (for Git operations) - simplified
185
- console.log(chalk_1.default.cyan(`✨ ${intent.explanation}`));
186
- // Risk warning
187
- if (intent.riskLevel === 'high') {
188
- console.log(chalk_1.default.red('⚠️ Destructive operation!'));
189
- }
190
- // Confirmation
191
- if (intent.needsConfirmation) {
192
- const answer = await askQuestion(chalk_1.default.yellow('Continue? (Y/n): '));
193
- if (answer.toLowerCase() === 'n') {
194
- console.log(chalk_1.default.gray('\nCancelled.\n'));
195
- return;
196
- }
197
- }
198
- // Special handling for commit (needs AI message generation)
199
- if (intent.action === 'commit') {
200
- await handleCommitWithAI();
201
- return;
202
- }
203
- // Execute git commands
204
- const result = executeGitCommands(intent.gitCommands, false);
205
- if (result.success) {
206
- console.log(chalk_1.default.green(`✅ Done!\n`));
207
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_SUCCESS });
208
- }
209
- else {
210
- // First approach failed - try smart solver
211
- console.log(chalk_1.default.yellow(`\n⚠️ Standard approach failed. Trying advanced solver...\n`));
212
- const gitStatusOutput = (0, child_process_1.execSync)('git status', { encoding: 'utf-8' });
213
- const smartResult = await (0, smart_solver_1.solveGitProblem)(userInput, {
214
- currentBranch,
215
- gitStatus: gitStatusOutput,
216
- recentError: result.error,
217
- });
218
- if (smartResult.success) {
219
- await showDopamineStats();
220
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_SUCCESS });
221
- }
222
- else {
223
- console.log(chalk_1.default.red(`❌ Could not solve the problem: ${smartResult.error}\n`));
224
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_ERROR });
225
- }
226
- }
227
- }
228
- /**
229
- * Handle commit with AI message generation
230
- */
231
- async function handleCommitWithAI() {
232
- const { getGitDiff, stageAllChanges } = await Promise.resolve().then(() => __importStar(require('../utils/git')));
233
- console.log(chalk_1.default.blue('📟 Running Git commands:\n'));
234
- // Stage all changes
235
- console.log(chalk_1.default.gray(' $ git add -A'));
236
- stageAllChanges();
237
- console.log(chalk_1.default.gray(' ✓ Staged all changes\n'));
238
- // Get diff
239
- let diff;
240
- try {
241
- diff = getGitDiff();
242
- if (diff.length > 40000) {
243
- diff = diff.substring(0, 40000);
244
- }
245
- }
246
- catch (error) {
247
- console.log(chalk_1.default.red(' ❌ Could not get diff\n'));
248
- return;
249
- }
250
- // Generate AI commit message
251
- console.log(chalk_1.default.gray(' ✨ Generating commit message with AI...'));
252
- let message;
253
- try {
254
- message = await (0, anthropic_client_1.generateCommitMessage)(diff);
255
- console.log(chalk_1.default.gray(` ✓ Generated: "${message.split('\n')[0]}"\n`));
256
- }
257
- catch (error) {
258
- console.log(chalk_1.default.red(' ❌ AI failed\n'));
259
- (0, analytics_1.trackEvent)({ event: 'commit_error' });
260
- return;
261
- }
262
- // Commit
263
- console.log(chalk_1.default.gray(` $ git commit -m "${message.split('\n')[0]}"`));
264
- try {
265
- const hash = (0, git_2.commitWithMessage)(message);
266
- const stats = (0, git_2.getCommitStats)(hash);
267
- console.log(chalk_1.default.gray(` ✓ Committed ${hash.substring(0, 7)}\n`));
268
- console.log(chalk_1.default.green.bold('✅ Success! Committed your changes\n'));
269
- console.log(chalk_1.default.gray(` Message: ${message.split('\n')[0]}`));
270
- console.log(chalk_1.default.gray(` Files: ${stats.files} | +${stats.insertions} -${stats.deletions}\n`));
271
- // Track
272
- (0, manager_1.trackCommit)();
273
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.COMMIT_SUCCESS });
274
- // Log to database
275
- (0, database_1.logCommit)({
276
- message,
277
- hash,
278
- files_changed: stats.files,
279
- insertions: stats.insertions,
280
- deletions: stats.deletions,
281
- timestamp: Date.now(),
282
- });
283
- // Dopamine
284
- await showDopamineStats();
285
- }
286
- catch (error) {
287
- console.log(chalk_1.default.red(` ❌ Commit failed: ${error.message}\n`));
288
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.COMMIT_ERROR });
289
- }
290
- }
291
- /**
292
- * Show dopamine-inducing stats
293
- */
294
- async function showDopamineStats() {
295
- const { displayQuickDopamine } = await Promise.resolve().then(() => __importStar(require('../utils/dopamine')));
296
- displayQuickDopamine();
297
- }
298
- /**
299
- * Handle GitHub operations (PRs, CI checks, issues)
300
- */
301
- async function handleGitHubOperation(intent) {
302
- // Import GitHub connection utilities
303
- const { getGitHubToken, isGitHubConnected } = await Promise.resolve().then(() => __importStar(require('./github-connect')));
304
- const { Octokit } = await Promise.resolve().then(() => __importStar(require('@octokit/rest')));
305
- // Check if GitHub is connected
306
- if (!isGitHubConnected()) {
307
- console.log(chalk_1.default.red('❌ GitHub not connected!\n'));
308
- console.log(chalk_1.default.yellow('To connect GitHub:'));
309
- console.log(chalk_1.default.cyan(' Run: ') + chalk_1.default.white('snap github connect'));
310
- console.log(chalk_1.default.gray('\nYou\'ll need a GitHub Personal Access Token'));
311
- console.log(chalk_1.default.gray('Get one at: https://github.com/settings/tokens\n'));
312
- return;
313
- }
314
- const githubToken = getGitHubToken();
315
- if (!githubToken) {
316
- console.log(chalk_1.default.red('❌ GitHub token not found\n'));
317
- return;
318
- }
319
- const octokit = new Octokit({ auth: githubToken });
320
- console.log(chalk_1.default.green(`✨ ${intent.explanation}\n`));
321
- // Show confirmation if needed
322
- if (intent.needsConfirmation) {
323
- const answer = await askQuestion(chalk_1.default.yellow('Continue? (Y/n): '));
324
- if (answer.toLowerCase() === 'n') {
325
- console.log(chalk_1.default.gray('\nCancelled.\n'));
326
- return;
327
- }
328
- }
329
- // Get repo info from git remote
330
- let owner, repo;
331
- try {
332
- const remote = (0, child_process_1.execSync)('git remote get-url origin', { encoding: 'utf-8' }).trim();
333
- const match = remote.match(/github\.com[/:]([\w-]+)\/([\w-]+?)(\.git)?$/);
334
- if (!match) {
335
- console.log(chalk_1.default.red('❌ Not a GitHub repository\n'));
336
- return;
337
- }
338
- owner = match[1];
339
- repo = match[2];
340
- }
341
- catch {
342
- console.log(chalk_1.default.red('❌ Not a GitHub repository\n'));
343
- return;
344
- }
345
- console.log(chalk_1.default.blue('🔄 Executing GitHub operation...\n'));
346
- try {
347
- if (intent.action === 'pr_create') {
348
- // Create PR using Octokit
349
- const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
350
- const { data: pr } = await octokit.pulls.create({
351
- owner,
352
- repo,
353
- title: intent.options?.title || `PR from ${currentBranch}`,
354
- body: intent.options?.body || '',
355
- head: intent.options?.head || currentBranch,
356
- base: intent.options?.base || 'main',
357
- });
358
- console.log(chalk_1.default.green.bold('✅ Pull Request Created!\n'));
359
- console.log(chalk_1.default.white(` Title: ${pr.title}`));
360
- console.log(chalk_1.default.white(` Number: #${pr.number}`));
361
- console.log(chalk_1.default.white(` URL: ${chalk_1.default.cyan(pr.html_url)}\n`));
362
- }
363
- else if (intent.action === 'pr_list') {
364
- // List PRs using Octokit
365
- const state = intent.options?.state || 'open';
366
- const { data: prs } = await octokit.pulls.list({
367
- owner,
368
- repo,
369
- state: state,
370
- per_page: 20,
371
- });
372
- if (prs.length === 0) {
373
- console.log(chalk_1.default.yellow(`No ${state} pull requests found.\n`));
374
- return;
375
- }
376
- console.log(chalk_1.default.green.bold(`📋 ${state.toUpperCase()} Pull Requests:\n`));
377
- prs.forEach((pr) => {
378
- console.log(chalk_1.default.white(` #${pr.number} - ${pr.title}`));
379
- console.log(chalk_1.default.gray(` ${pr.head.ref} → ${pr.base.ref} by @${pr.user?.login}`));
380
- console.log(chalk_1.default.cyan(` ${pr.html_url}\n`));
381
- });
382
- }
383
- else if (intent.action === 'ci_check') {
384
- // Check CI status using Octokit
385
- const ref = intent.options?.ref || (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
386
- // Get commit status checks
387
- const { data: statusData } = await octokit.repos.getCombinedStatusForRef({
388
- owner,
389
- repo,
390
- ref,
391
- });
392
- // Get workflow runs
393
- const { data: workflowRuns } = await octokit.actions.listWorkflowRunsForRepo({
394
- owner,
395
- repo,
396
- per_page: 10,
397
- });
398
- // Filter workflow runs for this ref
399
- const relevantRuns = workflowRuns.workflow_runs.filter(run => run.head_branch === ref || run.head_sha === ref);
400
- const stateColor = statusData.state === 'success' ? chalk_1.default.green :
401
- statusData.state === 'pending' ? chalk_1.default.yellow : chalk_1.default.red;
402
- console.log(stateColor.bold(`CI Status: ${statusData.state.toUpperCase()}\n`));
403
- if (statusData.statuses.length > 0) {
404
- console.log(chalk_1.default.white('Status Checks:'));
405
- statusData.statuses.forEach((status) => {
406
- const icon = status.state === 'success' ? '✅' : status.state === 'pending' ? '⏳' : '❌';
407
- console.log(chalk_1.default.gray(` ${icon} ${status.context}: ${status.description}`));
408
- });
409
- console.log();
410
- }
411
- if (relevantRuns.length > 0) {
412
- console.log(chalk_1.default.white('Workflows:'));
413
- relevantRuns.forEach((workflow) => {
414
- const icon = workflow.conclusion === 'success' ? '✅' :
415
- workflow.status === 'in_progress' ? '⏳' : '❌';
416
- console.log(chalk_1.default.gray(` ${icon} ${workflow.name}: ${workflow.conclusion || workflow.status}`));
417
- });
418
- console.log();
419
- }
420
- }
421
- else {
422
- console.log(chalk_1.default.yellow(`⚠️ GitHub operation "${intent.action}" not yet implemented\n`));
423
- }
424
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_SUCCESS });
425
- }
426
- catch (error) {
427
- console.log(chalk_1.default.red(`❌ ${error.message}\n`));
428
- (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_ERROR });
429
- }
32
+ // Use the new Cursor-style execution!
33
+ await (0, cursor_style_1.executeCursorStyle)(userInput);
430
34
  }
package/dist/repl.js CHANGED
@@ -41,10 +41,7 @@ const readline_1 = __importDefault(require("readline"));
41
41
  const chalk_1 = __importDefault(require("chalk"));
42
42
  const auth_1 = require("./lib/auth");
43
43
  const natural_1 = require("./commands/natural");
44
- const quick_1 = require("./commands/quick");
45
- const stats_1 = require("./commands/stats");
46
44
  const github_connect_1 = require("./commands/github-connect");
47
- const repo_manager_1 = require("./utils/repo-manager");
48
45
  const version_1 = require("./utils/version");
49
46
  /**
50
47
  * Start SnapCommit REPL (Read-Eval-Print-Loop)
@@ -70,8 +67,9 @@ async function startREPL() {
70
67
  console.log(chalk_1.default.yellow.bold('║ 🚀 UPDATE AVAILABLE! ║'));
71
68
  console.log(chalk_1.default.yellow.bold('╚════════════════════════════════════════╝'));
72
69
  console.log(chalk_1.default.white(` Current: ${chalk_1.default.red(updateInfo.currentVersion)} → Latest: ${chalk_1.default.green(updateInfo.latestVersion)}\n`));
73
- console.log(chalk_1.default.cyan.bold(' Update now: ') + chalk_1.default.white('Type ') + chalk_1.default.cyan.bold('update') + chalk_1.default.white(' to upgrade'));
74
- console.log(chalk_1.default.gray(' (Faster AI, better UX, bug fixes!)\n'));
70
+ console.log(chalk_1.default.cyan.bold(' To update: ') + chalk_1.default.white('Just type ') + chalk_1.default.cyan.bold('update') + chalk_1.default.white(' in the CLI below'));
71
+ console.log(chalk_1.default.gray(' (Faster AI, Cursor-style UX, better experience!)\n'));
72
+ console.log(chalk_1.default.white(' Example: ') + chalk_1.default.cyan('snap> update\n'));
75
73
  }
76
74
  }).catch((err) => {
77
75
  // Silent fail - don't interrupt user experience
@@ -83,15 +81,15 @@ async function startREPL() {
83
81
  console.log(chalk_1.default.green(`✅ GitHub connected as ${chalk_1.default.bold('@' + githubConfig?.username)}\n`));
84
82
  }
85
83
  console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
86
- console.log(chalk_1.default.bold('💡 What can SnapCommit do?\n'));
87
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Natural language Git: ') + chalk_1.default.cyan('"undo my last commit"'));
88
- console.log(chalk_1.default.gray('') + chalk_1.default.white('Quick AI commits: ') + chalk_1.default.cyan('quick') + chalk_1.default.gray(' (fast, no prompts)'));
89
- console.log(chalk_1.default.gray('') + chalk_1.default.white('Interactive commits: ') + chalk_1.default.cyan('commit') + chalk_1.default.gray(' (choose & edit)'));
90
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Switch repos: ') + chalk_1.default.cyan('cd /path/to/repo') + chalk_1.default.gray(' or ') + chalk_1.default.cyan('repos'));
84
+ console.log(chalk_1.default.bold('💡 Just talk to me! I understand natural language:\n'));
85
+ console.log(chalk_1.default.gray(' Git: ') + chalk_1.default.cyan('"commit and push changes"'));
86
+ console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('"switch to main and merge feature-x"'));
87
+ console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('"undo my last commit"'));
91
88
  if (githubConnected) {
92
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('GitHub operations: ') + chalk_1.default.cyan('"create a PR", "check CI"'));
89
+ console.log(chalk_1.default.gray(' GitHub: ') + chalk_1.default.cyan('"create a PR to main"'));
90
+ console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('"check CI status"'));
93
91
  }
94
- console.log(chalk_1.default.gray(' ') + chalk_1.default.white('Exit SnapCommit: ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' or ') + chalk_1.default.cyan('quit\n'));
92
+ console.log(chalk_1.default.gray(' Other: ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' • ') + chalk_1.default.cyan('help') + chalk_1.default.gray(' ') + chalk_1.default.cyan('update\n'));
95
93
  // Show GitHub setup reminder if not connected
96
94
  if (!githubConnected) {
97
95
  console.log(chalk_1.default.yellow.bold('┌─────────────────────────────────────────┐'));
@@ -125,60 +123,7 @@ async function startREPL() {
125
123
  rl.close();
126
124
  process.exit(0);
127
125
  }
128
- // Built-in commands
129
- if (line === 'quick' || line === 'q') {
130
- await (0, quick_1.quickCommand)();
131
- rl.prompt();
132
- return;
133
- }
134
- if (line === 'commit' || line === 'c') {
135
- const { commitCommand } = await Promise.resolve().then(() => __importStar(require('./commands/commit')));
136
- rl.pause(); // Pause REPL readline to avoid conflicts
137
- await commitCommand();
138
- rl.resume(); // Resume REPL readline
139
- rl.prompt();
140
- return;
141
- }
142
- if (line === 'stats' || line === 's') {
143
- (0, stats_1.statsCommand)();
144
- rl.prompt();
145
- return;
146
- }
147
- // cd command - navigate to different repo
148
- if (line.startsWith('cd ')) {
149
- const targetPath = line.substring(3).trim();
150
- const result = (0, repo_manager_1.changeToRepo)(targetPath);
151
- if (result.success) {
152
- console.log(chalk_1.default.green(`\n✅ Switched to: ${chalk_1.default.bold(result.repoName)}`));
153
- console.log(chalk_1.default.gray(` Path: ${result.repoPath}\n`));
154
- }
155
- else {
156
- console.log(chalk_1.default.red(`\n❌ ${result.error}`));
157
- console.log(chalk_1.default.gray(' Navigate to a valid git repository\n'));
158
- }
159
- rl.prompt();
160
- return;
161
- }
162
- // repos command - show recent repos
163
- if (line === 'repos' || line === 'repo') {
164
- const repos = (0, repo_manager_1.getRecentRepos)();
165
- if (repos.length === 0) {
166
- console.log(chalk_1.default.yellow('\n📁 No recent repositories\n'));
167
- console.log(chalk_1.default.gray(' Use ') + chalk_1.default.cyan('cd /path/to/repo') + chalk_1.default.gray(' to navigate to a repo\n'));
168
- }
169
- else {
170
- console.log(chalk_1.default.bold('\n📁 Recent Repositories:\n'));
171
- repos.slice(0, 10).forEach((repo, index) => {
172
- const isCurrent = repo.path === process.cwd();
173
- const prefix = isCurrent ? chalk_1.default.green('→ ') : ' ';
174
- console.log(prefix + chalk_1.default.cyan(`${index + 1}. ${repo.name}`) + chalk_1.default.gray(` (${repo.useCount} uses)`));
175
- console.log(' ' + chalk_1.default.gray(repo.path));
176
- });
177
- console.log(chalk_1.default.gray('\n Use ') + chalk_1.default.cyan('cd <path>') + chalk_1.default.gray(' to switch repos\n'));
178
- }
179
- rl.prompt();
180
- return;
181
- }
126
+ // All commands are now natural language - removed special commands!
182
127
  // Update command - update SnapCommit to latest version
183
128
  if (line === 'update' || line === 'upgrade') {
184
129
  console.log(chalk_1.default.cyan('\n⚡ Updating SnapCommit to latest version...\n'));
@@ -212,15 +157,26 @@ async function startREPL() {
212
157
  return;
213
158
  }
214
159
  if (line === 'help' || line === 'h') {
215
- console.log(chalk_1.default.bold('\n💡 SnapCommit Commands:\n'));
216
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('quick') + chalk_1.default.gray(' - Quick AI commit (fast, no prompts)'));
217
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('commit') + chalk_1.default.gray(' - Interactive commit (choose & edit AI message)'));
218
- console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('update') + chalk_1.default.gray(' - Update SnapCommit to latest version'));
219
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('cd /path/to/repo') + chalk_1.default.gray(' - Switch to a different repository'));
220
- console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('repos') + chalk_1.default.gray(' - Show recent repositories'));
221
- console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('stats') + chalk_1.default.gray(' - Show your coding stats'));
222
- console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('exit/quit') + chalk_1.default.gray(' - Exit SnapCommit'));
223
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Or just type what you want: ') + chalk_1.default.cyan('"create a new branch called feature-x"\n'));
160
+ console.log(chalk_1.default.bold.cyan('\n╔════════════════════════════════════════╗'));
161
+ console.log(chalk_1.default.bold.cyan('║ SnapCommit - Your AI Git Assistant ║'));
162
+ console.log(chalk_1.default.bold.cyan('╚════════════════════════════════════════╝\n'));
163
+ console.log(chalk_1.default.bold('🎯 Just talk to me in plain English!\n'));
164
+ console.log(chalk_1.default.white.bold('Git Operations:\n'));
165
+ console.log(chalk_1.default.cyan(' "commit and push my changes"'));
166
+ console.log(chalk_1.default.cyan(' "switch to main branch"'));
167
+ console.log(chalk_1.default.cyan(' "create a new branch called feature-x"'));
168
+ console.log(chalk_1.default.cyan(' "undo my last commit"'));
169
+ console.log(chalk_1.default.cyan(' "merge feature-x into main"'));
170
+ console.log(chalk_1.default.cyan(' "show me what changed"\n'));
171
+ console.log(chalk_1.default.white.bold('GitHub Operations:\n'));
172
+ console.log(chalk_1.default.cyan(' "create a PR to main"'));
173
+ console.log(chalk_1.default.cyan(' "check CI status"'));
174
+ console.log(chalk_1.default.cyan(' "list my pull requests"'));
175
+ console.log(chalk_1.default.cyan(' "merge PR #123"\n'));
176
+ console.log(chalk_1.default.white.bold('System Commands:\n'));
177
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('update') + chalk_1.default.gray(' - Update to latest version'));
178
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('exit/quit') + chalk_1.default.gray(' - Exit SnapCommit\n'));
179
+ console.log(chalk_1.default.gray('💡 I\'ll show you what I\'ll do before executing anything!\n'));
224
180
  rl.prompt();
225
181
  return;
226
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "1.2.1",
3
+ "version": "2.0.1",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {