claude-autopm 2.7.0 → 2.8.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.
@@ -351,33 +351,142 @@ async function issueEdit(argv) {
351
351
  * @param {Object} argv - Command arguments
352
352
  */
353
353
  async function issueSync(argv) {
354
- const spinner = ora(`Syncing issue: #${argv.number}`).start();
354
+ const provider = argv.provider || 'github';
355
+ const spinner = ora(`Syncing issue: #${argv.number} (${provider})`).start();
355
356
 
356
357
  try {
357
- const issueService = new IssueService();
358
+ let providerInstance;
359
+ let issueService;
360
+
361
+ // Load provider based on --provider flag
362
+ if (provider === 'azure') {
363
+ // Load Azure DevOps provider
364
+ const AzureDevOpsProvider = require('../../providers/AzureDevOpsProvider');
365
+ providerInstance = new AzureDevOpsProvider({
366
+ token: process.env.AZURE_DEVOPS_PAT,
367
+ organization: process.env.AZURE_DEVOPS_ORG,
368
+ project: process.env.AZURE_DEVOPS_PROJECT
369
+ });
358
370
 
359
- // Check if issue exists
360
- const issue = await issueService.getLocalIssue(argv.number);
371
+ await providerInstance.authenticate();
372
+ issueService = new IssueService({ provider: providerInstance });
361
373
 
362
- // TODO: Implement provider integration
363
- // For now, just show a message
364
- spinner.info(chalk.yellow('Provider sync not yet implemented'));
374
+ let result;
365
375
 
366
- console.log(chalk.yellow(`\n⚠️ GitHub/Azure sync feature coming soon!\n`));
376
+ if (argv.push) {
377
+ // Push to Azure
378
+ spinner.text = 'Pushing to Azure DevOps...';
379
+ result = await issueService.syncToAzure(argv.number, { detectConflicts: true });
380
+ } else if (argv.pull && argv.azure) {
381
+ // Pull from Azure
382
+ spinner.text = 'Pulling from Azure DevOps...';
383
+ result = await issueService.syncFromAzure(argv.azure, { detectConflicts: true });
384
+ } else {
385
+ // Default: bidirectional sync
386
+ spinner.text = 'Bidirectional sync...';
387
+ result = await issueService.syncBidirectionalAzure(argv.number, { conflictStrategy: 'detect' });
388
+ }
367
389
 
368
- console.log(chalk.dim('This feature will:'));
369
- console.log(chalk.dim(' - Create GitHub/Azure issue if not exists'));
370
- console.log(chalk.dim(' - Update existing issue'));
371
- console.log(chalk.dim(' - Sync issue status and comments\n'));
390
+ if (!result.success && result.conflict) {
391
+ spinner.warn(chalk.yellow('Conflict detected'));
372
392
 
373
- console.log(chalk.bold('For now, you can:'));
374
- console.log(` ${chalk.cyan('1.')} View issue: ${chalk.yellow('autopm issue show ' + argv.number)}`);
375
- console.log(` ${chalk.cyan('2.')} Check status: ${chalk.yellow('autopm issue status ' + argv.number)}\n`);
393
+ console.log(chalk.yellow(`\n⚠️ Sync Conflict Detected!\n`));
394
+ console.log(chalk.bold('Conflict Details:'));
395
+ console.log(` Local newer: ${result.conflict.localNewer}`);
396
+ console.log(` Remote newer: ${result.conflict.remoteNewer}\n`);
397
+
398
+ console.log(chalk.bold('Resolution Options:'));
399
+ console.log(` ${chalk.cyan('1.')} Use local: ${chalk.yellow('autopm issue sync-resolve ' + argv.number + ' --provider azure --strategy local')}`);
400
+ console.log(` ${chalk.cyan('2.')} Use remote: ${chalk.yellow('autopm issue sync-resolve ' + argv.number + ' --provider azure --strategy remote')}`);
401
+ console.log(` ${chalk.cyan('3.')} Use newest: ${chalk.yellow('autopm issue sync-resolve ' + argv.number + ' --provider azure --strategy newest')}\n`);
402
+ } else {
403
+ spinner.succeed(chalk.green('Sync complete'));
404
+
405
+ console.log(chalk.green(`\n✅ Issue #${argv.number} synced successfully!\n`));
406
+ console.log(chalk.bold('Sync Details:'));
407
+ console.log(` Provider: Azure DevOps`);
408
+ console.log(` Action: ${result.action || 'synced'}`);
409
+ if (result.workItemId) {
410
+ console.log(` Work Item #: ${result.workItemId}`);
411
+ }
412
+ if (result.direction) {
413
+ console.log(` Direction: ${result.direction}`);
414
+ }
415
+ console.log();
416
+ }
417
+
418
+ } else {
419
+ // Load GitHub provider (default)
420
+ const GitHubProvider = require('../../providers/GitHubProvider');
421
+ providerInstance = new GitHubProvider({
422
+ token: process.env.GITHUB_TOKEN,
423
+ owner: process.env.GITHUB_OWNER || process.env.GITHUB_USER,
424
+ repo: process.env.GITHUB_REPO
425
+ });
426
+
427
+ await providerInstance.authenticate();
428
+ issueService = new IssueService({ provider: providerInstance });
429
+
430
+ let result;
431
+
432
+ if (argv.push) {
433
+ // Push to GitHub
434
+ spinner.text = 'Pushing to GitHub...';
435
+ result = await issueService.syncToGitHub(argv.number, { detectConflicts: true });
436
+ } else if (argv.pull && argv.github) {
437
+ // Pull from GitHub
438
+ spinner.text = 'Pulling from GitHub...';
439
+ result = await issueService.syncFromGitHub(argv.github, { detectConflicts: true });
440
+ } else {
441
+ // Default: bidirectional sync
442
+ spinner.text = 'Bidirectional sync...';
443
+ result = await issueService.syncBidirectional(argv.number, { conflictStrategy: 'detect' });
444
+ }
445
+
446
+ if (!result.success && result.conflict) {
447
+ spinner.warn(chalk.yellow('Conflict detected'));
448
+
449
+ console.log(chalk.yellow(`\n⚠️ Sync Conflict Detected!\n`));
450
+ console.log(chalk.bold('Conflict Details:'));
451
+ console.log(` Local newer: ${result.conflict.localNewer}`);
452
+ console.log(` Remote newer: ${result.conflict.remoteNewer}`);
453
+ console.log(` Fields: ${result.conflict.conflictFields.join(', ')}\n`);
454
+
455
+ console.log(chalk.bold('Resolution Options:'));
456
+ console.log(` ${chalk.cyan('1.')} Use local: ${chalk.yellow('autopm issue sync-resolve ' + argv.number + ' --strategy local')}`);
457
+ console.log(` ${chalk.cyan('2.')} Use remote: ${chalk.yellow('autopm issue sync-resolve ' + argv.number + ' --strategy remote')}`);
458
+ console.log(` ${chalk.cyan('3.')} Use newest: ${chalk.yellow('autopm issue sync-resolve ' + argv.number + ' --strategy newest')}\n`);
459
+ } else {
460
+ spinner.succeed(chalk.green('Sync complete'));
461
+
462
+ console.log(chalk.green(`\n✅ Issue #${argv.number} synced successfully!\n`));
463
+ console.log(chalk.bold('Sync Details:'));
464
+ console.log(` Provider: GitHub`);
465
+ console.log(` Action: ${result.action || 'synced'}`);
466
+ if (result.githubNumber) {
467
+ console.log(` GitHub #: ${result.githubNumber}`);
468
+ }
469
+ if (result.direction) {
470
+ console.log(` Direction: ${result.direction}`);
471
+ }
472
+ console.log();
473
+ }
474
+ }
376
475
 
377
476
  } catch (error) {
378
477
  spinner.fail(chalk.red('Failed to sync issue'));
379
478
 
380
- if (error.message.includes('not found')) {
479
+ if (error.message.includes('GITHUB_TOKEN')) {
480
+ console.error(chalk.red(`\n❌ GitHub token not configured`));
481
+ console.error(chalk.yellow('Set: export GITHUB_TOKEN=your_token'));
482
+ console.error(chalk.yellow('Set: export GITHUB_OWNER=username'));
483
+ console.error(chalk.yellow('Set: export GITHUB_REPO=repository\n'));
484
+ } else if (error.message.includes('AZURE_DEVOPS_PAT')) {
485
+ console.error(chalk.red(`\n❌ Azure DevOps token not configured`));
486
+ console.error(chalk.yellow('Set: export AZURE_DEVOPS_PAT=your_pat_token'));
487
+ console.error(chalk.yellow('Set: export AZURE_DEVOPS_ORG=your_organization'));
488
+ console.error(chalk.yellow('Set: export AZURE_DEVOPS_PROJECT=your_project\n'));
489
+ } else if (error.message.includes('not found')) {
381
490
  console.error(chalk.red(`\nError: ${error.message}`));
382
491
  } else {
383
492
  console.error(chalk.red(`\nError: ${error.message}`));
@@ -385,6 +494,180 @@ async function issueSync(argv) {
385
494
  }
386
495
  }
387
496
 
497
+ /**
498
+ * Check sync status of an issue
499
+ * @param {Object} argv - Command arguments
500
+ */
501
+ async function issueSyncStatus(argv) {
502
+ const provider = argv.provider || 'github';
503
+ const spinner = ora(`Checking sync status: #${argv.number} (${provider})`).start();
504
+
505
+ try {
506
+ let providerInstance;
507
+ let issueService;
508
+
509
+ if (provider === 'azure') {
510
+ // Load Azure DevOps provider
511
+ const AzureDevOpsProvider = require('../../providers/AzureDevOpsProvider');
512
+ providerInstance = new AzureDevOpsProvider({
513
+ token: process.env.AZURE_DEVOPS_PAT,
514
+ organization: process.env.AZURE_DEVOPS_ORG,
515
+ project: process.env.AZURE_DEVOPS_PROJECT
516
+ });
517
+
518
+ await providerInstance.authenticate();
519
+ issueService = new IssueService({ provider: providerInstance });
520
+ const status = await issueService.getAzureSyncStatus(argv.number);
521
+
522
+ spinner.succeed(chalk.green('Status retrieved'));
523
+
524
+ console.log('\n' + chalk.bold('🔄 Sync Status (Azure DevOps)') + '\n');
525
+ console.log(chalk.gray('─'.repeat(50)) + '\n');
526
+
527
+ console.log(chalk.bold('Issue:'));
528
+ console.log(` Local #: ${status.localNumber}`);
529
+ console.log(` Work Item #: ${status.workItemId || 'Not synced'}`);
530
+ console.log(` Status: ${status.synced ? chalk.green('✓ Synced') : chalk.yellow('⚠ Out of sync')}`);
531
+
532
+ if (status.lastSync) {
533
+ console.log(` Last Sync: ${new Date(status.lastSync).toLocaleString()}`);
534
+ }
535
+
536
+ console.log('\n' + chalk.gray('─'.repeat(50)) + '\n');
537
+
538
+ if (!status.synced) {
539
+ console.log(chalk.yellow('💡 Tip: Run sync to update:'));
540
+ console.log(` ${chalk.cyan('autopm issue sync ' + argv.number + ' --provider azure')}\n`);
541
+ }
542
+
543
+ } else {
544
+ // Load GitHub provider
545
+ const GitHubProvider = require('../../providers/GitHubProvider');
546
+ providerInstance = new GitHubProvider({
547
+ token: process.env.GITHUB_TOKEN,
548
+ owner: process.env.GITHUB_OWNER || process.env.GITHUB_USER,
549
+ repo: process.env.GITHUB_REPO
550
+ });
551
+
552
+ await providerInstance.authenticate();
553
+ issueService = new IssueService({ provider: providerInstance });
554
+ const status = await issueService.getSyncStatus(argv.number);
555
+
556
+ spinner.succeed(chalk.green('Status retrieved'));
557
+
558
+ console.log('\n' + chalk.bold('🔄 Sync Status') + '\n');
559
+ console.log(chalk.gray('─'.repeat(50)) + '\n');
560
+
561
+ console.log(chalk.bold('Issue:'));
562
+ console.log(` Local #: ${status.localNumber}`);
563
+ console.log(` GitHub #: ${status.githubNumber || 'Not synced'}`);
564
+ console.log(` Status: ${status.synced ? chalk.green('✓ Synced') : chalk.yellow('⚠ Out of sync')}`);
565
+
566
+ if (status.lastSync) {
567
+ console.log(` Last Sync: ${new Date(status.lastSync).toLocaleString()}`);
568
+ }
569
+
570
+ console.log('\n' + chalk.gray('─'.repeat(50)) + '\n');
571
+
572
+ if (!status.synced) {
573
+ console.log(chalk.yellow('💡 Tip: Run sync to update:'));
574
+ console.log(` ${chalk.cyan('autopm issue sync ' + argv.number)}\n`);
575
+ }
576
+ }
577
+
578
+ } catch (error) {
579
+ spinner.fail(chalk.red('Failed to check status'));
580
+ console.error(chalk.red(`\nError: ${error.message}`));
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Resolve sync conflict
586
+ * @param {Object} argv - Command arguments
587
+ */
588
+ async function issueSyncResolve(argv) {
589
+ const provider = argv.provider || 'github';
590
+ const spinner = ora(`Resolving conflict: #${argv.number} (${provider})`).start();
591
+
592
+ try {
593
+ let providerInstance;
594
+ let issueService;
595
+
596
+ if (provider === 'azure') {
597
+ // Load Azure DevOps provider
598
+ const AzureDevOpsProvider = require('../../providers/AzureDevOpsProvider');
599
+ providerInstance = new AzureDevOpsProvider({
600
+ token: process.env.AZURE_DEVOPS_PAT,
601
+ organization: process.env.AZURE_DEVOPS_ORG,
602
+ project: process.env.AZURE_DEVOPS_PROJECT
603
+ });
604
+
605
+ await providerInstance.authenticate();
606
+ issueService = new IssueService({ provider: providerInstance });
607
+ const result = await issueService.resolveAzureConflict(argv.number, argv.strategy);
608
+
609
+ if (result.resolved) {
610
+ spinner.succeed(chalk.green('Conflict resolved'));
611
+
612
+ console.log(chalk.green(`\n✅ Conflict resolved using "${result.appliedStrategy}" strategy\n`));
613
+ console.log(chalk.bold('Result:'));
614
+ console.log(` Provider: Azure DevOps`);
615
+ console.log(` Action: ${result.result.action || 'resolved'}`);
616
+ if (result.result.workItemId) {
617
+ console.log(` Work Item #: ${result.result.workItemId}`);
618
+ }
619
+ console.log();
620
+ } else {
621
+ spinner.info(chalk.yellow('Manual resolution required'));
622
+
623
+ console.log(chalk.yellow(`\n⚠️ Manual resolution required\n`));
624
+ console.log(chalk.bold('Available strategies:'));
625
+ console.log(` ${chalk.cyan('local')} - Use local version`);
626
+ console.log(` ${chalk.cyan('remote')} - Use remote (Azure DevOps) version`);
627
+ console.log(` ${chalk.cyan('newest')} - Use most recently updated\n`);
628
+ }
629
+
630
+ } else {
631
+ // Load GitHub provider (default)
632
+ const GitHubProvider = require('../../providers/GitHubProvider');
633
+ providerInstance = new GitHubProvider({
634
+ token: process.env.GITHUB_TOKEN,
635
+ owner: process.env.GITHUB_OWNER || process.env.GITHUB_USER,
636
+ repo: process.env.GITHUB_REPO
637
+ });
638
+
639
+ await providerInstance.authenticate();
640
+ issueService = new IssueService({ provider: providerInstance });
641
+ const result = await issueService.resolveConflict(argv.number, argv.strategy);
642
+
643
+ if (result.resolved) {
644
+ spinner.succeed(chalk.green('Conflict resolved'));
645
+
646
+ console.log(chalk.green(`\n✅ Conflict resolved using "${result.appliedStrategy}" strategy\n`));
647
+ console.log(chalk.bold('Result:'));
648
+ console.log(` Provider: GitHub`);
649
+ console.log(` Action: ${result.result.action || 'resolved'}`);
650
+ if (result.result.githubNumber) {
651
+ console.log(` GitHub #: ${result.result.githubNumber}`);
652
+ }
653
+ console.log();
654
+ } else {
655
+ spinner.info(chalk.yellow('Manual resolution required'));
656
+
657
+ console.log(chalk.yellow(`\n⚠️ Manual resolution required\n`));
658
+ console.log(chalk.bold('Available strategies:'));
659
+ console.log(` ${chalk.cyan('local')} - Use local version`);
660
+ console.log(` ${chalk.cyan('remote')} - Use remote (GitHub) version`);
661
+ console.log(` ${chalk.cyan('newest')} - Use most recently updated\n`);
662
+ }
663
+ }
664
+
665
+ } catch (error) {
666
+ spinner.fail(chalk.red('Failed to resolve conflict'));
667
+ console.error(chalk.red(`\nError: ${error.message}`));
668
+ }
669
+ }
670
+
388
671
  /**
389
672
  * Command builder - registers all subcommands
390
673
  * @param {Object} yargs - Yargs instance
@@ -467,6 +750,12 @@ function builder(yargs) {
467
750
  describe: 'Issue number',
468
751
  type: 'number'
469
752
  })
753
+ .option('provider', {
754
+ describe: 'Provider to sync with',
755
+ type: 'string',
756
+ choices: ['github', 'azure'],
757
+ default: 'github'
758
+ })
470
759
  .option('push', {
471
760
  describe: 'Push local changes to provider',
472
761
  type: 'boolean',
@@ -477,12 +766,59 @@ function builder(yargs) {
477
766
  type: 'boolean',
478
767
  default: false
479
768
  })
480
- .example('autopm issue sync 123', 'Sync issue #123 with provider')
481
- .example('autopm issue sync 123 --push', 'Push local changes')
482
- .example('autopm issue sync 123 --pull', 'Pull remote updates');
769
+ .example('autopm issue sync 123', 'Sync issue #123 with GitHub (default)')
770
+ .example('autopm issue sync 123 --provider azure', 'Sync with Azure DevOps')
771
+ .example('autopm issue sync 123 --push', 'Push local changes to GitHub')
772
+ .example('autopm issue sync 123 --provider azure --push', 'Push to Azure DevOps');
483
773
  },
484
774
  issueSync
485
775
  )
776
+ .command(
777
+ 'sync-status <number>',
778
+ 'Check sync status for issue',
779
+ (yargs) => {
780
+ return yargs
781
+ .positional('number', {
782
+ describe: 'Issue number',
783
+ type: 'number'
784
+ })
785
+ .option('provider', {
786
+ describe: 'Provider to check status with',
787
+ type: 'string',
788
+ choices: ['github', 'azure'],
789
+ default: 'github'
790
+ })
791
+ .example('autopm issue sync-status 123', 'Check GitHub sync status (default)')
792
+ .example('autopm issue sync-status 123 --provider azure', 'Check Azure DevOps sync status');
793
+ },
794
+ issueSyncStatus
795
+ )
796
+ .command(
797
+ 'sync-resolve <number>',
798
+ 'Resolve sync conflict',
799
+ (yargs) => {
800
+ return yargs
801
+ .positional('number', {
802
+ describe: 'Issue number',
803
+ type: 'number'
804
+ })
805
+ .option('provider', {
806
+ describe: 'Provider to resolve conflict with',
807
+ type: 'string',
808
+ choices: ['github', 'azure'],
809
+ default: 'github'
810
+ })
811
+ .option('strategy', {
812
+ describe: 'Resolution strategy',
813
+ type: 'string',
814
+ choices: ['local', 'remote', 'newest', 'manual'],
815
+ demandOption: true
816
+ })
817
+ .example('autopm issue sync-resolve 123 --strategy newest', 'Use newest version (GitHub)')
818
+ .example('autopm issue sync-resolve 123 --provider azure --strategy local', 'Use local version (Azure)');
819
+ },
820
+ issueSyncResolve
821
+ )
486
822
  .demandCommand(1, 'You must specify an issue command')
487
823
  .strictCommands()
488
824
  .help();
@@ -506,6 +842,8 @@ module.exports = {
506
842
  console.log(' status <number> Check issue status');
507
843
  console.log(' edit <number> Edit issue in editor');
508
844
  console.log(' sync <number> Sync with GitHub/Azure');
845
+ console.log(' sync-status <number> Check sync status');
846
+ console.log(' sync-resolve <number> Resolve sync conflict');
509
847
  console.log('\nUse: autopm issue <command> --help for more info\n');
510
848
  }
511
849
  },
@@ -515,6 +853,8 @@ module.exports = {
515
853
  close: issueClose,
516
854
  status: issueStatus,
517
855
  edit: issueEdit,
518
- sync: issueSync
856
+ sync: issueSync,
857
+ syncStatus: issueSyncStatus,
858
+ syncResolve: issueSyncResolve
519
859
  }
520
860
  };