claudeup 3.5.0 → 3.6.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.
@@ -414,13 +414,14 @@ export async function getMarketplaceAutoUpdate(marketplaceName) {
414
414
  return known[marketplaceName]?.autoUpdate;
415
415
  }
416
416
  // =============================================================================
417
- // MARKETPLACE RENAME MIGRATION: mag-claude-plugins → magus
417
+ // MARKETPLACE RENAME MIGRATION: mag-claude-plugins / MadAppGang-claude-code → magus
418
418
  // =============================================================================
419
- const OLD_MARKETPLACE_NAME = "mag-claude-plugins";
419
+ const OLD_MARKETPLACE_NAMES = ["mag-claude-plugins", "MadAppGang-claude-code"];
420
420
  const NEW_MARKETPLACE_NAME = "magus";
421
421
  /**
422
- * Rename plugin keys in a Record from old marketplace to new.
422
+ * Rename plugin keys in a Record from any old marketplace name to new.
423
423
  * e.g., "frontend@mag-claude-plugins" → "frontend@magus"
424
+ * "dev@MadAppGang-claude-code" → "dev@magus"
424
425
  * Returns [migratedRecord, count] — count=0 means no changes.
425
426
  */
426
427
  function migratePluginKeys(record) {
@@ -429,9 +430,14 @@ function migratePluginKeys(record) {
429
430
  const migrated = {};
430
431
  let count = 0;
431
432
  for (const [key, value] of Object.entries(record)) {
432
- if (key.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
433
+ const oldName = OLD_MARKETPLACE_NAMES.find((n) => key.endsWith(`@${n}`));
434
+ if (oldName) {
433
435
  const pluginName = key.slice(0, key.lastIndexOf("@"));
434
- migrated[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = value;
436
+ const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
437
+ // Don't overwrite if canonical key already exists
438
+ if (!record[newKey]) {
439
+ migrated[newKey] = value;
440
+ }
435
441
  count++;
436
442
  }
437
443
  else {
@@ -456,12 +462,16 @@ function migrateSettingsObject(settings) {
456
462
  settings.installedPluginVersions = iv;
457
463
  total += ivCount;
458
464
  }
459
- // Migrate extraKnownMarketplaces key
460
- if (settings.extraKnownMarketplaces?.[OLD_MARKETPLACE_NAME]) {
461
- const entry = settings.extraKnownMarketplaces[OLD_MARKETPLACE_NAME];
462
- delete settings.extraKnownMarketplaces[OLD_MARKETPLACE_NAME];
463
- settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] = entry;
464
- total++;
465
+ // Migrate extraKnownMarketplaces keys
466
+ for (const oldName of OLD_MARKETPLACE_NAMES) {
467
+ if (settings.extraKnownMarketplaces?.[oldName]) {
468
+ const entry = settings.extraKnownMarketplaces[oldName];
469
+ delete settings.extraKnownMarketplaces[oldName];
470
+ if (!settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME]) {
471
+ settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] = entry;
472
+ }
473
+ total++;
474
+ }
465
475
  }
466
476
  return total;
467
477
  }
@@ -519,33 +529,34 @@ export async function migrateMarketplaceRename(projectPath) {
519
529
  }
520
530
  }
521
531
  catch { /* skip if unreadable */ }
522
- // 4. known_marketplaces.json — rename the key + physical directory cleanup
532
+ // 4. known_marketplaces.json — rename old keys + physical directory cleanup
523
533
  const pluginsDir = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
524
- const oldDir = path.join(pluginsDir, OLD_MARKETPLACE_NAME);
525
534
  const newDir = path.join(pluginsDir, NEW_MARKETPLACE_NAME);
526
535
  try {
527
536
  const known = await readKnownMarketplaces();
528
537
  let knownModified = false;
529
- if (known[OLD_MARKETPLACE_NAME]) {
530
- const oldEntry = known[OLD_MARKETPLACE_NAME];
531
- // If canonical entry already exists, just delete the old one
532
- if (!known[NEW_MARKETPLACE_NAME]) {
533
- known[NEW_MARKETPLACE_NAME] = {
534
- ...oldEntry,
535
- source: {
536
- ...oldEntry.source,
537
- repo: "MadAppGang/magus",
538
- },
539
- };
538
+ for (const oldName of OLD_MARKETPLACE_NAMES) {
539
+ if (known[oldName]) {
540
+ const oldEntry = known[oldName];
541
+ // If canonical entry doesn't exist yet, create it
542
+ if (!known[NEW_MARKETPLACE_NAME]) {
543
+ known[NEW_MARKETPLACE_NAME] = {
544
+ ...oldEntry,
545
+ source: {
546
+ ...oldEntry.source,
547
+ repo: "MadAppGang/magus",
548
+ },
549
+ };
550
+ }
551
+ delete known[oldName];
552
+ knownModified = true;
553
+ }
554
+ // Ensure installLocation doesn't reference old directory names
555
+ if (known[NEW_MARKETPLACE_NAME]?.installLocation?.includes(oldName)) {
556
+ known[NEW_MARKETPLACE_NAME].installLocation =
557
+ known[NEW_MARKETPLACE_NAME].installLocation.replace(oldName, NEW_MARKETPLACE_NAME);
558
+ knownModified = true;
540
559
  }
541
- delete known[OLD_MARKETPLACE_NAME];
542
- knownModified = true;
543
- }
544
- // Ensure installLocation points to new directory name
545
- if (known[NEW_MARKETPLACE_NAME]?.installLocation?.includes(OLD_MARKETPLACE_NAME)) {
546
- known[NEW_MARKETPLACE_NAME].installLocation =
547
- known[NEW_MARKETPLACE_NAME].installLocation.replace(OLD_MARKETPLACE_NAME, NEW_MARKETPLACE_NAME);
548
- knownModified = true;
549
560
  }
550
561
  if (knownModified) {
551
562
  await writeKnownMarketplaces(known);
@@ -553,31 +564,33 @@ export async function migrateMarketplaceRename(projectPath) {
553
564
  }
554
565
  }
555
566
  catch { /* skip if unreadable */ }
556
- // 4b. Rename/remove the old physical directory (runs even if key was already migrated)
557
- try {
558
- if (await fs.pathExists(oldDir)) {
559
- if (!(await fs.pathExists(newDir))) {
560
- await fs.rename(oldDir, newDir);
561
- }
562
- else {
563
- // Both exist — remove the old one (magus dir is canonical)
564
- await fs.remove(oldDir);
567
+ // 4b. Rename/remove old physical directories (runs even if keys were already migrated)
568
+ for (const oldName of OLD_MARKETPLACE_NAMES) {
569
+ const oldDir = path.join(pluginsDir, oldName);
570
+ try {
571
+ if (await fs.pathExists(oldDir)) {
572
+ if (!(await fs.pathExists(newDir))) {
573
+ await fs.rename(oldDir, newDir);
574
+ }
575
+ else {
576
+ // Both exist — remove the old one (magus dir is canonical)
577
+ await fs.remove(oldDir);
578
+ }
565
579
  }
566
580
  }
581
+ catch { /* non-fatal: directory cleanup is best-effort */ }
567
582
  }
568
- catch { /* non-fatal: directory cleanup is best-effort */ }
569
583
  // 4c. Update git remote URL in the marketplace clone (old → new repo)
570
584
  try {
571
- const marketplaceDir = await fs.pathExists(newDir) ? newDir : oldDir;
572
- if (await fs.pathExists(path.join(marketplaceDir, ".git"))) {
585
+ if (await fs.pathExists(path.join(newDir, ".git"))) {
573
586
  const { execSync } = await import("node:child_process");
574
587
  const remote = execSync("git remote get-url origin", {
575
- cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
588
+ cwd: newDir, encoding: "utf-8", timeout: 5000,
576
589
  }).trim();
577
590
  if (remote.includes("claude-code") && remote.includes("MadAppGang")) {
578
591
  const newRemote = remote.replace("claude-code", NEW_MARKETPLACE_NAME);
579
592
  execSync(`git remote set-url origin "${newRemote}"`, {
580
- cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
593
+ cwd: newDir, encoding: "utf-8", timeout: 5000,
581
594
  });
582
595
  }
583
596
  }
@@ -589,9 +602,13 @@ export async function migrateMarketplaceRename(projectPath) {
589
602
  let regCount = 0;
590
603
  const newPlugins = {};
591
604
  for (const [pluginId, entries] of Object.entries(registry.plugins)) {
592
- if (pluginId.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
605
+ const oldName = OLD_MARKETPLACE_NAMES.find((n) => pluginId.endsWith(`@${n}`));
606
+ if (oldName) {
593
607
  const pluginName = pluginId.slice(0, pluginId.lastIndexOf("@"));
594
- newPlugins[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = entries;
608
+ const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
609
+ if (!newPlugins[newKey]) {
610
+ newPlugins[newKey] = entries;
611
+ }
595
612
  regCount++;
596
613
  }
597
614
  else {
@@ -630,15 +630,16 @@ export interface MarketplaceRecoveryResult {
630
630
  }
631
631
 
632
632
  // =============================================================================
633
- // MARKETPLACE RENAME MIGRATION: mag-claude-plugins → magus
633
+ // MARKETPLACE RENAME MIGRATION: mag-claude-plugins / MadAppGang-claude-code → magus
634
634
  // =============================================================================
635
635
 
636
- const OLD_MARKETPLACE_NAME = "mag-claude-plugins";
636
+ const OLD_MARKETPLACE_NAMES = ["mag-claude-plugins", "MadAppGang-claude-code"];
637
637
  const NEW_MARKETPLACE_NAME = "magus";
638
638
 
639
639
  /**
640
- * Rename plugin keys in a Record from old marketplace to new.
640
+ * Rename plugin keys in a Record from any old marketplace name to new.
641
641
  * e.g., "frontend@mag-claude-plugins" → "frontend@magus"
642
+ * "dev@MadAppGang-claude-code" → "dev@magus"
642
643
  * Returns [migratedRecord, count] — count=0 means no changes.
643
644
  */
644
645
  function migratePluginKeys<T>(
@@ -648,9 +649,14 @@ function migratePluginKeys<T>(
648
649
  const migrated: Record<string, T> = {};
649
650
  let count = 0;
650
651
  for (const [key, value] of Object.entries(record)) {
651
- if (key.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
652
+ const oldName = OLD_MARKETPLACE_NAMES.find((n) => key.endsWith(`@${n}`));
653
+ if (oldName) {
652
654
  const pluginName = key.slice(0, key.lastIndexOf("@"));
653
- migrated[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = value;
655
+ const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
656
+ // Don't overwrite if canonical key already exists
657
+ if (!record[newKey]) {
658
+ migrated[newKey] = value;
659
+ }
654
660
  count++;
655
661
  } else {
656
662
  migrated[key] = value;
@@ -678,12 +684,16 @@ function migrateSettingsObject(settings: ClaudeSettings): number {
678
684
  total += ivCount;
679
685
  }
680
686
 
681
- // Migrate extraKnownMarketplaces key
682
- if (settings.extraKnownMarketplaces?.[OLD_MARKETPLACE_NAME]) {
683
- const entry = settings.extraKnownMarketplaces[OLD_MARKETPLACE_NAME];
684
- delete settings.extraKnownMarketplaces[OLD_MARKETPLACE_NAME];
685
- settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] = entry;
686
- total++;
687
+ // Migrate extraKnownMarketplaces keys
688
+ for (const oldName of OLD_MARKETPLACE_NAMES) {
689
+ if (settings.extraKnownMarketplaces?.[oldName]) {
690
+ const entry = settings.extraKnownMarketplaces[oldName];
691
+ delete settings.extraKnownMarketplaces[oldName];
692
+ if (!settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME]) {
693
+ settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] = entry;
694
+ }
695
+ total++;
696
+ }
687
697
  }
688
698
 
689
699
  return total;
@@ -748,40 +758,40 @@ export async function migrateMarketplaceRename(
748
758
  }
749
759
  } catch { /* skip if unreadable */ }
750
760
 
751
- // 4. known_marketplaces.json — rename the key + physical directory cleanup
761
+ // 4. known_marketplaces.json — rename old keys + physical directory cleanup
752
762
  const pluginsDir = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
753
- const oldDir = path.join(pluginsDir, OLD_MARKETPLACE_NAME);
754
763
  const newDir = path.join(pluginsDir, NEW_MARKETPLACE_NAME);
755
764
 
756
765
  try {
757
766
  const known = await readKnownMarketplaces();
758
767
  let knownModified = false;
759
768
 
760
- if (known[OLD_MARKETPLACE_NAME]) {
761
- const oldEntry = known[OLD_MARKETPLACE_NAME];
762
-
763
- // If canonical entry already exists, just delete the old one
764
- if (!known[NEW_MARKETPLACE_NAME]) {
765
- known[NEW_MARKETPLACE_NAME] = {
766
- ...oldEntry,
767
- source: {
768
- ...oldEntry.source,
769
- repo: "MadAppGang/magus",
770
- },
771
- };
769
+ for (const oldName of OLD_MARKETPLACE_NAMES) {
770
+ if (known[oldName]) {
771
+ const oldEntry = known[oldName];
772
+ // If canonical entry doesn't exist yet, create it
773
+ if (!known[NEW_MARKETPLACE_NAME]) {
774
+ known[NEW_MARKETPLACE_NAME] = {
775
+ ...oldEntry,
776
+ source: {
777
+ ...oldEntry.source,
778
+ repo: "MadAppGang/magus",
779
+ },
780
+ };
781
+ }
782
+ delete known[oldName];
783
+ knownModified = true;
772
784
  }
773
- delete known[OLD_MARKETPLACE_NAME];
774
- knownModified = true;
775
- }
776
785
 
777
- // Ensure installLocation points to new directory name
778
- if (known[NEW_MARKETPLACE_NAME]?.installLocation?.includes(OLD_MARKETPLACE_NAME)) {
779
- known[NEW_MARKETPLACE_NAME].installLocation =
780
- known[NEW_MARKETPLACE_NAME].installLocation.replace(
781
- OLD_MARKETPLACE_NAME,
782
- NEW_MARKETPLACE_NAME,
783
- );
784
- knownModified = true;
786
+ // Ensure installLocation doesn't reference old directory names
787
+ if (known[NEW_MARKETPLACE_NAME]?.installLocation?.includes(oldName)) {
788
+ known[NEW_MARKETPLACE_NAME].installLocation =
789
+ known[NEW_MARKETPLACE_NAME].installLocation.replace(
790
+ oldName,
791
+ NEW_MARKETPLACE_NAME,
792
+ );
793
+ knownModified = true;
794
+ }
785
795
  }
786
796
 
787
797
  if (knownModified) {
@@ -790,30 +800,32 @@ export async function migrateMarketplaceRename(
790
800
  }
791
801
  } catch { /* skip if unreadable */ }
792
802
 
793
- // 4b. Rename/remove the old physical directory (runs even if key was already migrated)
794
- try {
795
- if (await fs.pathExists(oldDir)) {
796
- if (!(await fs.pathExists(newDir))) {
797
- await fs.rename(oldDir, newDir);
798
- } else {
799
- // Both exist — remove the old one (magus dir is canonical)
800
- await fs.remove(oldDir);
803
+ // 4b. Rename/remove old physical directories (runs even if keys were already migrated)
804
+ for (const oldName of OLD_MARKETPLACE_NAMES) {
805
+ const oldDir = path.join(pluginsDir, oldName);
806
+ try {
807
+ if (await fs.pathExists(oldDir)) {
808
+ if (!(await fs.pathExists(newDir))) {
809
+ await fs.rename(oldDir, newDir);
810
+ } else {
811
+ // Both exist — remove the old one (magus dir is canonical)
812
+ await fs.remove(oldDir);
813
+ }
801
814
  }
802
- }
803
- } catch { /* non-fatal: directory cleanup is best-effort */ }
815
+ } catch { /* non-fatal: directory cleanup is best-effort */ }
816
+ }
804
817
 
805
818
  // 4c. Update git remote URL in the marketplace clone (old → new repo)
806
819
  try {
807
- const marketplaceDir = await fs.pathExists(newDir) ? newDir : oldDir;
808
- if (await fs.pathExists(path.join(marketplaceDir, ".git"))) {
820
+ if (await fs.pathExists(path.join(newDir, ".git"))) {
809
821
  const { execSync } = await import("node:child_process");
810
822
  const remote = execSync("git remote get-url origin", {
811
- cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
823
+ cwd: newDir, encoding: "utf-8", timeout: 5000,
812
824
  }).trim();
813
825
  if (remote.includes("claude-code") && remote.includes("MadAppGang")) {
814
826
  const newRemote = remote.replace("claude-code", NEW_MARKETPLACE_NAME);
815
827
  execSync(`git remote set-url origin "${newRemote}"`, {
816
- cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
828
+ cwd: newDir, encoding: "utf-8", timeout: 5000,
817
829
  });
818
830
  }
819
831
  }
@@ -825,9 +837,13 @@ export async function migrateMarketplaceRename(
825
837
  let regCount = 0;
826
838
  const newPlugins: typeof registry.plugins = {};
827
839
  for (const [pluginId, entries] of Object.entries(registry.plugins)) {
828
- if (pluginId.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
840
+ const oldName = OLD_MARKETPLACE_NAMES.find((n) => pluginId.endsWith(`@${n}`));
841
+ if (oldName) {
829
842
  const pluginName = pluginId.slice(0, pluginId.lastIndexOf("@"));
830
- newPlugins[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = entries;
843
+ const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
844
+ if (!newPlugins[newKey]) {
845
+ newPlugins[newKey] = entries;
846
+ }
831
847
  regCount++;
832
848
  } else {
833
849
  newPlugins[pluginId] = entries;
package/src/ui/App.js CHANGED
@@ -182,7 +182,7 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
182
182
  type: "SHOW_PROGRESS",
183
183
  state: { message: "Scanning marketplaces..." },
184
184
  });
185
- // Migrate mag-claude-plugins → magus (idempotent), then repair plugin.json files
185
+ // Migrate old marketplace names → magus (idempotent), then repair plugin.json files
186
186
  migrateMarketplaceRename()
187
187
  .catch(() => { }); // non-blocking, best-effort
188
188
  repairAllMarketplaces()
package/src/ui/App.tsx CHANGED
@@ -265,7 +265,7 @@ function AppContentInner({
265
265
  state: { message: "Scanning marketplaces..." },
266
266
  });
267
267
 
268
- // Migrate mag-claude-plugins → magus (idempotent), then repair plugin.json files
268
+ // Migrate old marketplace names → magus (idempotent), then repair plugin.json files
269
269
  migrateMarketplaceRename()
270
270
  .catch(() => {}); // non-blocking, best-effort
271
271
 
@@ -8,8 +8,9 @@ import { CategoryHeader } from "../components/CategoryHeader.js";
8
8
  import { ScrollableList } from "../components/ScrollableList.js";
9
9
  import { fuzzyFilter, highlightMatches } from "../../utils/fuzzy-search.js";
10
10
  import { getAllMarketplaces } from "../../data/marketplaces.js";
11
- import { getAvailablePlugins, refreshAllMarketplaces, clearMarketplaceCache, saveInstalledPluginVersion, removeInstalledPluginVersion, getLocalMarketplacesInfo, } from "../../services/plugin-manager.js";
12
- import { enablePlugin, enableGlobalPlugin, enableLocalPlugin, saveGlobalInstalledPluginVersion, removeGlobalInstalledPluginVersion, saveLocalInstalledPluginVersion, removeLocalInstalledPluginVersion, setMcpEnvVar, getMcpEnvVars, } from "../../services/claude-settings.js";
11
+ import { getAvailablePlugins, refreshAllMarketplaces, clearMarketplaceCache, getLocalMarketplacesInfo, } from "../../services/plugin-manager.js";
12
+ import { setMcpEnvVar, getMcpEnvVars, } from "../../services/claude-settings.js";
13
+ import { installPlugin as cliInstallPlugin, uninstallPlugin as cliUninstallPlugin, updatePlugin as cliUpdatePlugin, } from "../../services/claude-cli.js";
13
14
  import { getPluginEnvRequirements, getPluginSourcePath, } from "../../services/plugin-mcp-config.js";
14
15
  export function PluginsScreen() {
15
16
  const { state, dispatch } = useApp();
@@ -263,10 +264,10 @@ export function PluginsScreen() {
263
264
  };
264
265
  const handleShowAddMarketplaceInstructions = async () => {
265
266
  await modal.message("Add Marketplace", "To add a marketplace, run this command in your terminal:\n\n" +
266
- " claude marketplace add owner/repo\n\n" +
267
+ " claude plugin marketplace add owner/repo\n\n" +
267
268
  "Examples:\n" +
268
- " claude marketplace add MadAppGang/magus\n" +
269
- " claude marketplace add anthropics/claude-plugins-official\n\n" +
269
+ " claude plugin marketplace add MadAppGang/magus\n" +
270
+ " claude plugin marketplace add anthropics/claude-plugins-official\n\n" +
270
271
  "Auto-update is enabled by default for new marketplaces.\n\n" +
271
272
  "After adding, refresh claudeup with 'r' to see the new marketplace.", "info");
272
273
  };
@@ -377,7 +378,7 @@ export function PluginsScreen() {
377
378
  else {
378
379
  // Show add marketplace instructions
379
380
  await modal.message(`Add ${mp.displayName}?`, `To add this marketplace, run in your terminal:\n\n` +
380
- ` claude marketplace add ${mp.source.repo || mp.name}\n\n` +
381
+ ` claude plugin marketplace add ${mp.source.repo || mp.name}\n\n` +
381
382
  `Auto-update is enabled by default.\n\n` +
382
383
  `After adding, refresh claudeup with 'r' to see it.`, "info");
383
384
  }
@@ -454,40 +455,18 @@ export function PluginsScreen() {
454
455
  : `Uninstalling from ${scopeLabel}`;
455
456
  modal.loading(`${actionLabel}...`);
456
457
  try {
458
+ const scope = scopeValue;
457
459
  if (action === "uninstall") {
458
- // Uninstall from this scope
459
- if (scopeValue === "user") {
460
- await enableGlobalPlugin(plugin.id, false);
461
- await removeGlobalInstalledPluginVersion(plugin.id);
462
- }
463
- else if (scopeValue === "project") {
464
- await enablePlugin(plugin.id, false, state.projectPath);
465
- await removeInstalledPluginVersion(plugin.id, state.projectPath);
466
- }
467
- else {
468
- await enableLocalPlugin(plugin.id, false, state.projectPath);
469
- await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
470
- }
460
+ await cliUninstallPlugin(plugin.id, scope);
461
+ }
462
+ else if (action === "update") {
463
+ await cliUpdatePlugin(plugin.id, scope);
471
464
  }
472
465
  else {
473
- // Install or update (both save the latest version)
474
- if (scopeValue === "user") {
475
- await enableGlobalPlugin(plugin.id, true);
476
- await saveGlobalInstalledPluginVersion(plugin.id, latestVersion);
477
- }
478
- else if (scopeValue === "project") {
479
- await enablePlugin(plugin.id, true, state.projectPath);
480
- await saveInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
481
- }
482
- else {
483
- await enableLocalPlugin(plugin.id, true, state.projectPath);
484
- await saveLocalInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
485
- }
466
+ await cliInstallPlugin(plugin.id, scope);
486
467
  // On fresh install, prompt for MCP server env vars if needed
487
- if (action === "install") {
488
- modal.hideModal();
489
- await collectPluginEnvVars(plugin.name, plugin.marketplace);
490
- }
468
+ modal.hideModal();
469
+ await collectPluginEnvVars(plugin.name, plugin.marketplace);
491
470
  }
492
471
  if (action !== "install") {
493
472
  modal.hideModal();
@@ -505,16 +484,10 @@ export function PluginsScreen() {
505
484
  if (!item || item.type !== "plugin" || !item.plugin?.hasUpdate)
506
485
  return;
507
486
  const plugin = item.plugin;
508
- const isGlobal = pluginsState.scope === "global";
487
+ const scope = pluginsState.scope === "global" ? "user" : "project";
509
488
  modal.loading(`Updating ${plugin.name}...`);
510
489
  try {
511
- const versionToSave = plugin.version || "0.0.0";
512
- if (isGlobal) {
513
- await saveGlobalInstalledPluginVersion(plugin.id, versionToSave);
514
- }
515
- else {
516
- await saveInstalledPluginVersion(plugin.id, versionToSave, state.projectPath);
517
- }
490
+ await cliUpdatePlugin(plugin.id, scope);
518
491
  modal.hideModal();
519
492
  fetchData();
520
493
  }
@@ -529,17 +502,11 @@ export function PluginsScreen() {
529
502
  const updatable = pluginsState.plugins.data.filter((p) => p.hasUpdate);
530
503
  if (updatable.length === 0)
531
504
  return;
532
- const isGlobal = pluginsState.scope === "global";
505
+ const scope = pluginsState.scope === "global" ? "user" : "project";
533
506
  modal.loading(`Updating ${updatable.length} plugin(s)...`);
534
507
  try {
535
508
  for (const plugin of updatable) {
536
- const versionToSave = plugin.version || "0.0.0";
537
- if (isGlobal) {
538
- await saveGlobalInstalledPluginVersion(plugin.id, versionToSave);
539
- }
540
- else {
541
- await saveInstalledPluginVersion(plugin.id, versionToSave, state.projectPath);
542
- }
509
+ await cliUpdatePlugin(plugin.id, scope);
543
510
  }
544
511
  modal.hideModal();
545
512
  fetchData();
@@ -589,39 +556,16 @@ export function PluginsScreen() {
589
556
  modal.loading(`${actionLabel}...`);
590
557
  try {
591
558
  if (action === "uninstall") {
592
- // Uninstall from this scope
593
- if (scope === "user") {
594
- await enableGlobalPlugin(plugin.id, false);
595
- await removeGlobalInstalledPluginVersion(plugin.id);
596
- }
597
- else if (scope === "project") {
598
- await enablePlugin(plugin.id, false, state.projectPath);
599
- await removeInstalledPluginVersion(plugin.id, state.projectPath);
600
- }
601
- else {
602
- await enableLocalPlugin(plugin.id, false, state.projectPath);
603
- await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
604
- }
559
+ await cliUninstallPlugin(plugin.id, scope);
560
+ }
561
+ else if (action === "update") {
562
+ await cliUpdatePlugin(plugin.id, scope);
605
563
  }
606
564
  else {
607
- // Install or update to this scope (both save the latest version)
608
- if (scope === "user") {
609
- await enableGlobalPlugin(plugin.id, true);
610
- await saveGlobalInstalledPluginVersion(plugin.id, latestVersion);
611
- }
612
- else if (scope === "project") {
613
- await enablePlugin(plugin.id, true, state.projectPath);
614
- await saveInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
615
- }
616
- else {
617
- await enableLocalPlugin(plugin.id, true, state.projectPath);
618
- await saveLocalInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
619
- }
565
+ await cliInstallPlugin(plugin.id, scope);
620
566
  // On fresh install, prompt for MCP server env vars if needed
621
- if (action === "install") {
622
- modal.hideModal();
623
- await collectPluginEnvVars(plugin.name, plugin.marketplace);
624
- }
567
+ modal.hideModal();
568
+ await collectPluginEnvVars(plugin.name, plugin.marketplace);
625
569
  }
626
570
  if (action !== "install") {
627
571
  modal.hideModal();
@@ -667,19 +611,7 @@ export function PluginsScreen() {
667
611
  return; // Cancelled
668
612
  modal.loading(`Uninstalling ${plugin.name}...`);
669
613
  try {
670
- if (scopeValue === "user") {
671
- await enableGlobalPlugin(plugin.id, false);
672
- await removeGlobalInstalledPluginVersion(plugin.id);
673
- }
674
- else if (scopeValue === "project") {
675
- await enablePlugin(plugin.id, false, state.projectPath);
676
- await removeInstalledPluginVersion(plugin.id, state.projectPath);
677
- }
678
- else {
679
- // local scope
680
- await enableLocalPlugin(plugin.id, false, state.projectPath);
681
- await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
682
- }
614
+ await cliUninstallPlugin(plugin.id, scopeValue);
683
615
  modal.hideModal();
684
616
  fetchData();
685
617
  }