@snapcommit/cli 3.3.1 → 3.4.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.
@@ -292,16 +292,90 @@ async function executeCommitWithAI(intent) {
292
292
  // Push if requested
293
293
  const shouldPush = intent.shouldPush || intent.gitCommands?.some((cmd) => cmd.includes('push'));
294
294
  if (shouldPush) {
295
+ // STEP 1: Pull first to avoid conflicts (like Cursor does!)
296
+ try {
297
+ const currentBranch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
298
+ const hasRemote = (0, child_process_1.execSync)(`git ls-remote --heads origin ${currentBranch}`, { encoding: 'utf-8' }).trim();
299
+ if (hasRemote) {
300
+ console.log(chalk_1.default.blue('⚡ Syncing with remote...'));
301
+ try {
302
+ (0, child_process_1.execSync)('git pull --rebase', { encoding: 'utf-8', stdio: 'pipe' });
303
+ console.log(chalk_1.default.green('✓ Synced'));
304
+ }
305
+ catch (pullError) {
306
+ // Handle rebase conflicts
307
+ if (pullError.message.includes('conflict')) {
308
+ console.log(chalk_1.default.yellow('\n⚠️ Merge conflicts detected - resolving...'));
309
+ // Try to auto-resolve conflicts
310
+ const resolved = await tryAdvancedConflictResolution();
311
+ if (resolved) {
312
+ console.log(chalk_1.default.green('✓ Conflicts resolved'));
313
+ }
314
+ else {
315
+ console.log(chalk_1.default.red('\n❌ Cannot auto-resolve conflicts'));
316
+ console.log(chalk_1.default.white('\nConflicted files:'));
317
+ const conflicts = (0, child_process_1.execSync)('git diff --name-only --diff-filter=U', { encoding: 'utf-8' });
318
+ conflicts.split('\n').filter(f => f.trim()).forEach(file => {
319
+ console.log(chalk_1.default.red(` ✗ ${file}`));
320
+ });
321
+ console.log(chalk_1.default.cyan('\nTo resolve manually:'));
322
+ console.log(chalk_1.default.gray(' 1. Fix conflicts in the files above'));
323
+ console.log(chalk_1.default.gray(' 2. Run: ') + chalk_1.default.cyan('git add <files>'));
324
+ console.log(chalk_1.default.gray(' 3. Run: ') + chalk_1.default.cyan('git rebase --continue'));
325
+ console.log(chalk_1.default.gray(' 4. Then try: ') + chalk_1.default.cyan('snap') + chalk_1.default.gray(' and ') + chalk_1.default.cyan('push changes\n'));
326
+ return;
327
+ }
328
+ }
329
+ else {
330
+ // Other pull errors
331
+ console.log(chalk_1.default.yellow(`⚠️ Sync issue: ${pullError.message}`));
332
+ }
333
+ }
334
+ }
335
+ }
336
+ catch {
337
+ // No remote or other issues - continue with push
338
+ }
339
+ // STEP 2: Now push
295
340
  try {
296
341
  (0, child_process_1.execSync)('git push', { encoding: 'utf-8', stdio: 'pipe' });
297
342
  console.log(chalk_1.default.green('✓ Pushed\n'));
298
343
  }
299
- catch (error) {
300
- if (error.message.includes('no configured push destination')) {
344
+ catch (pushError) {
345
+ const errorMsg = pushError.message.toLowerCase();
346
+ if (errorMsg.includes('no configured push destination')) {
301
347
  console.log(chalk_1.default.gray('✓ Committed locally (no remote)\n'));
302
348
  }
349
+ else if (errorMsg.includes('rejected') && errorMsg.includes('non-fast-forward')) {
350
+ // Need force push - but be safe!
351
+ console.log(chalk_1.default.yellow('\n⚠️ Remote has changes that would be overwritten'));
352
+ console.log(chalk_1.default.white('This requires force push, which can be dangerous.\n'));
353
+ const rlForce = await Promise.resolve().then(() => __importStar(require('readline')));
354
+ const rlForcePush = rlForce.createInterface({
355
+ input: process.stdin,
356
+ output: process.stdout,
357
+ });
358
+ const confirmForce = await new Promise((resolve) => {
359
+ rlForcePush.question(chalk_1.default.cyan('Force push? ') + chalk_1.default.gray('(yes/no): '), (ans) => {
360
+ setImmediate(() => rlForcePush.close());
361
+ resolve(ans.trim().toLowerCase());
362
+ });
363
+ });
364
+ if (confirmForce === 'yes') {
365
+ try {
366
+ (0, child_process_1.execSync)('git push --force-with-lease', { encoding: 'utf-8', stdio: 'pipe' });
367
+ console.log(chalk_1.default.green('✓ Force pushed (safely)\n'));
368
+ }
369
+ catch (forceError) {
370
+ console.log(chalk_1.default.red(`\n❌ Force push failed: ${forceError.message}\n`));
371
+ }
372
+ }
373
+ else {
374
+ console.log(chalk_1.default.gray('\n✗ Push cancelled\n'));
375
+ }
376
+ }
303
377
  else {
304
- console.log(chalk_1.default.yellow(`⚠️ Push failed: ${error.message}\n`));
378
+ console.log(chalk_1.default.yellow(`\n⚠️ Push failed: ${pushError.message}\n`));
305
379
  }
306
380
  }
307
381
  }
@@ -309,6 +383,46 @@ async function executeCommitWithAI(intent) {
309
383
  console.log();
310
384
  }
311
385
  }
386
+ /**
387
+ * Advanced conflict resolution - try multiple strategies
388
+ */
389
+ async function tryAdvancedConflictResolution() {
390
+ try {
391
+ // Strategy 1: Try accepting ours (our changes)
392
+ try {
393
+ (0, child_process_1.execSync)('git checkout --ours .', { encoding: 'utf-8', stdio: 'pipe' });
394
+ (0, child_process_1.execSync)('git add .', { encoding: 'utf-8', stdio: 'pipe' });
395
+ (0, child_process_1.execSync)('git rebase --continue', { encoding: 'utf-8', stdio: 'pipe' });
396
+ return true;
397
+ }
398
+ catch {
399
+ // Strategy 1 failed
400
+ }
401
+ // Strategy 2: Try accepting theirs (remote changes)
402
+ try {
403
+ (0, child_process_1.execSync)('git rebase --abort', { encoding: 'utf-8', stdio: 'pipe' });
404
+ (0, child_process_1.execSync)('git pull --rebase', { encoding: 'utf-8', stdio: 'pipe' });
405
+ (0, child_process_1.execSync)('git checkout --theirs .', { encoding: 'utf-8', stdio: 'pipe' });
406
+ (0, child_process_1.execSync)('git add .', { encoding: 'utf-8', stdio: 'pipe' });
407
+ (0, child_process_1.execSync)('git rebase --continue', { encoding: 'utf-8', stdio: 'pipe' });
408
+ return true;
409
+ }
410
+ catch {
411
+ // Strategy 2 failed
412
+ }
413
+ // Strategy 3: Abort and let user handle
414
+ try {
415
+ (0, child_process_1.execSync)('git rebase --abort', { encoding: 'utf-8', stdio: 'pipe' });
416
+ }
417
+ catch {
418
+ // Already aborted
419
+ }
420
+ return false;
421
+ }
422
+ catch {
423
+ return false;
424
+ }
425
+ }
312
426
  /**
313
427
  * Execute Git commands - Cursor-style (clean, fast, auto-fix)
314
428
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "3.3.1",
3
+ "version": "3.4.0",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {