claudeup 3.3.1 → 3.5.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.
- package/README.md +1 -1
- package/package.json +4 -4
- package/src/data/cli-tools.js +1 -1
- package/src/data/cli-tools.ts +1 -1
- package/src/data/marketplaces.js +44 -12
- package/src/data/marketplaces.ts +48 -12
- package/src/prerunner/index.js +9 -1
- package/src/prerunner/index.ts +10 -1
- package/src/services/claude-settings.js +197 -3
- package/src/services/claude-settings.ts +217 -3
- package/src/services/plugin-mcp-config.js +2 -2
- package/src/services/plugin-mcp-config.ts +2 -2
- package/src/services/update-cache.ts +1 -1
- package/src/ui/App.js +4 -2
- package/src/ui/App.tsx +5 -2
- package/src/ui/screens/PluginsScreen.js +1 -1
- package/src/ui/screens/PluginsScreen.tsx +1 -1
- package/src/utils/string-utils.js +1 -1
- package/src/utils/string-utils.ts +1 -1
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ Releases are automated via GitHub Actions using [npm Trusted Publishing](https:/
|
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
2. **Configure Trusted Publisher on npmjs.com** (already done):
|
|
102
|
-
- Repository: `MadAppGang/
|
|
102
|
+
- Repository: `MadAppGang/magus`
|
|
103
103
|
- Workflow: `claudeup-release.yml`
|
|
104
104
|
|
|
105
105
|
### Releasing New Versions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudeup",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.tsx",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/MadAppGang/
|
|
37
|
+
"url": "https://github.com/MadAppGang/magus"
|
|
38
38
|
},
|
|
39
39
|
"bugs": {
|
|
40
|
-
"url": "https://github.com/MadAppGang/
|
|
40
|
+
"url": "https://github.com/MadAppGang/magus/issues"
|
|
41
41
|
},
|
|
42
|
-
"homepage": "https://github.com/MadAppGang/
|
|
42
|
+
"homepage": "https://github.com/MadAppGang/magus/tree/main/tools/claudeup#readme",
|
|
43
43
|
"engines": {
|
|
44
44
|
"bun": ">=1.0.0"
|
|
45
45
|
},
|
package/src/data/cli-tools.js
CHANGED
|
@@ -5,7 +5,7 @@ export const cliTools = [
|
|
|
5
5
|
description: "TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
6
6
|
installCommand: "npm install -g claudeup",
|
|
7
7
|
checkCommand: "claudeup --version",
|
|
8
|
-
website: "https://github.com/MadAppGang/
|
|
8
|
+
website: "https://github.com/MadAppGang/magus/tree/main/tools/claudeup",
|
|
9
9
|
category: "ai-coding",
|
|
10
10
|
packageManager: "npm",
|
|
11
11
|
packageName: "claudeup",
|
package/src/data/cli-tools.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const cliTools: CliTool[] = [
|
|
|
19
19
|
installCommand: "npm install -g claudeup",
|
|
20
20
|
checkCommand: "claudeup --version",
|
|
21
21
|
website:
|
|
22
|
-
"https://github.com/MadAppGang/
|
|
22
|
+
"https://github.com/MadAppGang/magus/tree/main/tools/claudeup",
|
|
23
23
|
category: "ai-coding",
|
|
24
24
|
packageManager: "npm",
|
|
25
25
|
packageName: "claudeup",
|
package/src/data/marketplaces.js
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import { formatMarketplaceName } from "../utils/string-utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* Known repo aliases — GitHub redirects the old repo to the new one,
|
|
4
|
+
* so these should be treated as identical for deduplication.
|
|
5
|
+
*/
|
|
6
|
+
const REPO_ALIASES = {
|
|
7
|
+
"madappgang/claude-code": "madappgang/magus",
|
|
8
|
+
};
|
|
9
|
+
/** Normalize a repo string through known aliases. */
|
|
10
|
+
function normalizeRepo(repo) {
|
|
11
|
+
const lower = repo.toLowerCase();
|
|
12
|
+
return REPO_ALIASES[lower] || lower;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Deprecated marketplace names that should be hidden if the canonical
|
|
16
|
+
* marketplace is already present. Maps deprecated name → canonical name.
|
|
17
|
+
*/
|
|
18
|
+
export const deprecatedMarketplaces = {
|
|
19
|
+
"mag-claude-plugins": "magus",
|
|
20
|
+
};
|
|
2
21
|
export const defaultMarketplaces = [
|
|
3
22
|
{
|
|
4
|
-
name: "
|
|
5
|
-
displayName: "
|
|
23
|
+
name: "magus",
|
|
24
|
+
displayName: "Magus",
|
|
6
25
|
source: {
|
|
7
26
|
source: "github",
|
|
8
|
-
repo: "MadAppGang/
|
|
27
|
+
repo: "MadAppGang/magus",
|
|
9
28
|
},
|
|
10
29
|
description: "Professional plugins for frontend, backend, and code analysis",
|
|
11
30
|
official: false,
|
|
@@ -37,23 +56,36 @@ export function getMarketplaceByName(name) {
|
|
|
37
56
|
}
|
|
38
57
|
// Get all available marketplaces from local cache + hardcoded defaults
|
|
39
58
|
// Local cache is primary source of truth, defaults are fallback
|
|
40
|
-
// Deduplicates by repo URL to avoid showing same marketplace twice
|
|
59
|
+
// Deduplicates by normalized repo URL to avoid showing same marketplace twice
|
|
41
60
|
export function getAllMarketplaces(localMarketplaces) {
|
|
42
61
|
const all = new Map();
|
|
43
62
|
const seenRepos = new Set();
|
|
44
63
|
// Primary source: local cache (what's actually cloned)
|
|
45
64
|
if (localMarketplaces) {
|
|
46
65
|
for (const [name, local] of localMarketplaces) {
|
|
47
|
-
const repo = local.gitRepo || "";
|
|
66
|
+
const repo = normalizeRepo(local.gitRepo || "");
|
|
67
|
+
// Skip deprecated marketplaces if their canonical replacement exists
|
|
68
|
+
// (or will be added from defaults)
|
|
69
|
+
const canonical = deprecatedMarketplaces[name];
|
|
70
|
+
if (canonical) {
|
|
71
|
+
// If canonical already in the map or in defaults, skip this entry
|
|
72
|
+
if (all.has(canonical) || defaultMarketplaces.some((m) => m.name === canonical)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Skip if another marketplace already claimed this repo URL
|
|
77
|
+
if (repo && seenRepos.has(repo))
|
|
78
|
+
continue;
|
|
48
79
|
if (repo)
|
|
49
|
-
seenRepos.add(repo
|
|
80
|
+
seenRepos.add(repo);
|
|
50
81
|
// Check if this marketplace has defaults (for official/featured flags)
|
|
51
82
|
const defaultMp = defaultMarketplaces.find((m) => m.name === name);
|
|
52
83
|
all.set(name, {
|
|
53
84
|
name,
|
|
54
|
-
displayName
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
// Prefer default displayName over stale local clone data
|
|
86
|
+
displayName: defaultMp?.displayName || local.name || formatMarketplaceName(name),
|
|
87
|
+
source: { source: "github", repo: defaultMp?.source.repo || local.gitRepo || "" },
|
|
88
|
+
description: defaultMp?.description || local.description || "",
|
|
57
89
|
official: defaultMp?.official ?? repo.toLowerCase().includes("anthropics/"),
|
|
58
90
|
featured: defaultMp?.featured,
|
|
59
91
|
});
|
|
@@ -61,16 +93,16 @@ export function getAllMarketplaces(localMarketplaces) {
|
|
|
61
93
|
}
|
|
62
94
|
// Fallback: hardcoded defaults (only if their repo isn't already represented)
|
|
63
95
|
for (const mp of defaultMarketplaces) {
|
|
64
|
-
const repo = mp.source.repo
|
|
96
|
+
const repo = normalizeRepo(mp.source.repo || "");
|
|
65
97
|
if (!all.has(mp.name) && !seenRepos.has(repo)) {
|
|
66
98
|
all.set(mp.name, mp);
|
|
67
99
|
if (repo)
|
|
68
100
|
seenRepos.add(repo);
|
|
69
101
|
}
|
|
70
102
|
}
|
|
71
|
-
// Sort:
|
|
103
|
+
// Sort: Magus first, then alphabetically
|
|
72
104
|
return Array.from(all.values()).sort((a, b) => {
|
|
73
|
-
// MadAppGang always first
|
|
105
|
+
// Magus (MadAppGang) always first
|
|
74
106
|
const aIsMag = a.source.repo?.toLowerCase().includes("madappgang/");
|
|
75
107
|
const bIsMag = b.source.repo?.toLowerCase().includes("madappgang/");
|
|
76
108
|
if (aIsMag && !bIsMag)
|
package/src/data/marketplaces.ts
CHANGED
|
@@ -2,13 +2,35 @@ import type { Marketplace } from "../types/index.js";
|
|
|
2
2
|
import type { LocalMarketplace } from "../services/local-marketplace.js";
|
|
3
3
|
import { formatMarketplaceName } from "../utils/string-utils.js";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Known repo aliases — GitHub redirects the old repo to the new one,
|
|
7
|
+
* so these should be treated as identical for deduplication.
|
|
8
|
+
*/
|
|
9
|
+
const REPO_ALIASES: Record<string, string> = {
|
|
10
|
+
"madappgang/claude-code": "madappgang/magus",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Normalize a repo string through known aliases. */
|
|
14
|
+
function normalizeRepo(repo: string): string {
|
|
15
|
+
const lower = repo.toLowerCase();
|
|
16
|
+
return REPO_ALIASES[lower] || lower;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Deprecated marketplace names that should be hidden if the canonical
|
|
21
|
+
* marketplace is already present. Maps deprecated name → canonical name.
|
|
22
|
+
*/
|
|
23
|
+
export const deprecatedMarketplaces: Record<string, string> = {
|
|
24
|
+
"mag-claude-plugins": "magus",
|
|
25
|
+
};
|
|
26
|
+
|
|
5
27
|
export const defaultMarketplaces: Marketplace[] = [
|
|
6
28
|
{
|
|
7
|
-
name: "
|
|
8
|
-
displayName: "
|
|
29
|
+
name: "magus",
|
|
30
|
+
displayName: "Magus",
|
|
9
31
|
source: {
|
|
10
32
|
source: "github",
|
|
11
|
-
repo: "MadAppGang/
|
|
33
|
+
repo: "MadAppGang/magus",
|
|
12
34
|
},
|
|
13
35
|
description:
|
|
14
36
|
"Professional plugins for frontend, backend, and code analysis",
|
|
@@ -45,7 +67,7 @@ export function getMarketplaceByName(name: string): Marketplace | undefined {
|
|
|
45
67
|
|
|
46
68
|
// Get all available marketplaces from local cache + hardcoded defaults
|
|
47
69
|
// Local cache is primary source of truth, defaults are fallback
|
|
48
|
-
// Deduplicates by repo URL to avoid showing same marketplace twice
|
|
70
|
+
// Deduplicates by normalized repo URL to avoid showing same marketplace twice
|
|
49
71
|
export function getAllMarketplaces(
|
|
50
72
|
localMarketplaces?: Map<string, LocalMarketplace>,
|
|
51
73
|
): Marketplace[] {
|
|
@@ -55,17 +77,31 @@ export function getAllMarketplaces(
|
|
|
55
77
|
// Primary source: local cache (what's actually cloned)
|
|
56
78
|
if (localMarketplaces) {
|
|
57
79
|
for (const [name, local] of localMarketplaces) {
|
|
58
|
-
const repo = local.gitRepo || "";
|
|
59
|
-
|
|
80
|
+
const repo = normalizeRepo(local.gitRepo || "");
|
|
81
|
+
|
|
82
|
+
// Skip deprecated marketplaces if their canonical replacement exists
|
|
83
|
+
// (or will be added from defaults)
|
|
84
|
+
const canonical = deprecatedMarketplaces[name];
|
|
85
|
+
if (canonical) {
|
|
86
|
+
// If canonical already in the map or in defaults, skip this entry
|
|
87
|
+
if (all.has(canonical) || defaultMarketplaces.some((m) => m.name === canonical)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Skip if another marketplace already claimed this repo URL
|
|
93
|
+
if (repo && seenRepos.has(repo)) continue;
|
|
94
|
+
if (repo) seenRepos.add(repo);
|
|
60
95
|
|
|
61
96
|
// Check if this marketplace has defaults (for official/featured flags)
|
|
62
97
|
const defaultMp = defaultMarketplaces.find((m) => m.name === name);
|
|
63
98
|
|
|
64
99
|
all.set(name, {
|
|
65
100
|
name,
|
|
66
|
-
displayName
|
|
67
|
-
|
|
68
|
-
|
|
101
|
+
// Prefer default displayName over stale local clone data
|
|
102
|
+
displayName: defaultMp?.displayName || local.name || formatMarketplaceName(name),
|
|
103
|
+
source: { source: "github" as const, repo: defaultMp?.source.repo || local.gitRepo || "" },
|
|
104
|
+
description: defaultMp?.description || local.description || "",
|
|
69
105
|
official:
|
|
70
106
|
defaultMp?.official ?? repo.toLowerCase().includes("anthropics/"),
|
|
71
107
|
featured: defaultMp?.featured,
|
|
@@ -75,16 +111,16 @@ export function getAllMarketplaces(
|
|
|
75
111
|
|
|
76
112
|
// Fallback: hardcoded defaults (only if their repo isn't already represented)
|
|
77
113
|
for (const mp of defaultMarketplaces) {
|
|
78
|
-
const repo = mp.source.repo
|
|
114
|
+
const repo = normalizeRepo(mp.source.repo || "");
|
|
79
115
|
if (!all.has(mp.name) && !seenRepos.has(repo)) {
|
|
80
116
|
all.set(mp.name, mp);
|
|
81
117
|
if (repo) seenRepos.add(repo);
|
|
82
118
|
}
|
|
83
119
|
}
|
|
84
120
|
|
|
85
|
-
// Sort:
|
|
121
|
+
// Sort: Magus first, then alphabetically
|
|
86
122
|
return Array.from(all.values()).sort((a, b) => {
|
|
87
|
-
// MadAppGang always first
|
|
123
|
+
// Magus (MadAppGang) always first
|
|
88
124
|
const aIsMag = a.source.repo?.toLowerCase().includes("madappgang/");
|
|
89
125
|
const bIsMag = b.source.repo?.toLowerCase().includes("madappgang/");
|
|
90
126
|
if (aIsMag && !bIsMag) return -1;
|
package/src/prerunner/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UpdateCache } from "../services/update-cache.js";
|
|
2
2
|
import { getAvailablePlugins, clearMarketplaceCache, saveInstalledPluginVersion, } from "../services/plugin-manager.js";
|
|
3
3
|
import { runClaude } from "../services/claude-runner.js";
|
|
4
|
-
import { recoverMarketplaceSettings } from "../services/claude-settings.js";
|
|
4
|
+
import { recoverMarketplaceSettings, migrateMarketplaceRename } from "../services/claude-settings.js";
|
|
5
5
|
/**
|
|
6
6
|
* Prerun orchestration: Check for updates, apply them, then run claude
|
|
7
7
|
* @param claudeArgs - Arguments to pass to claude CLI
|
|
@@ -11,6 +11,14 @@ import { recoverMarketplaceSettings } from "../services/claude-settings.js";
|
|
|
11
11
|
export async function prerunClaude(claudeArgs, options = {}) {
|
|
12
12
|
const cache = new UpdateCache();
|
|
13
13
|
try {
|
|
14
|
+
// STEP 0: Migrate mag-claude-plugins → magus (idempotent, no-ops if already migrated)
|
|
15
|
+
const migration = await migrateMarketplaceRename();
|
|
16
|
+
const migTotal = migration.projectMigrated + migration.globalMigrated
|
|
17
|
+
+ migration.localMigrated + migration.registryMigrated
|
|
18
|
+
+ (migration.knownMarketplacesMigrated ? 1 : 0);
|
|
19
|
+
if (migTotal > 0) {
|
|
20
|
+
console.log(`✓ Migrated ${migTotal} plugin reference(s) from mag-claude-plugins → magus`);
|
|
21
|
+
}
|
|
14
22
|
// STEP 1: Check if we should update (time-based cache, or forced)
|
|
15
23
|
const shouldUpdate = options.force || (await cache.shouldCheckForUpdates());
|
|
16
24
|
if (options.force) {
|
package/src/prerunner/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
saveInstalledPluginVersion,
|
|
6
6
|
} from "../services/plugin-manager.js";
|
|
7
7
|
import { runClaude } from "../services/claude-runner.js";
|
|
8
|
-
import { recoverMarketplaceSettings } from "../services/claude-settings.js";
|
|
8
|
+
import { recoverMarketplaceSettings, migrateMarketplaceRename } from "../services/claude-settings.js";
|
|
9
9
|
|
|
10
10
|
export interface PrerunOptions {
|
|
11
11
|
force?: boolean; // Bypass cache and force update check
|
|
@@ -24,6 +24,15 @@ export async function prerunClaude(
|
|
|
24
24
|
const cache = new UpdateCache();
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
+
// STEP 0: Migrate mag-claude-plugins → magus (idempotent, no-ops if already migrated)
|
|
28
|
+
const migration = await migrateMarketplaceRename();
|
|
29
|
+
const migTotal = migration.projectMigrated + migration.globalMigrated
|
|
30
|
+
+ migration.localMigrated + migration.registryMigrated
|
|
31
|
+
+ (migration.knownMarketplacesMigrated ? 1 : 0);
|
|
32
|
+
if (migTotal > 0) {
|
|
33
|
+
console.log(`✓ Migrated ${migTotal} plugin reference(s) from mag-claude-plugins → magus`);
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
// STEP 1: Check if we should update (time-based cache, or forced)
|
|
28
37
|
const shouldUpdate = options.force || (await cache.shouldCheckForUpdates());
|
|
29
38
|
|
|
@@ -413,8 +413,202 @@ export async function getMarketplaceAutoUpdate(marketplaceName) {
|
|
|
413
413
|
const known = await readKnownMarketplaces();
|
|
414
414
|
return known[marketplaceName]?.autoUpdate;
|
|
415
415
|
}
|
|
416
|
+
// =============================================================================
|
|
417
|
+
// MARKETPLACE RENAME MIGRATION: mag-claude-plugins → magus
|
|
418
|
+
// =============================================================================
|
|
419
|
+
const OLD_MARKETPLACE_NAME = "mag-claude-plugins";
|
|
420
|
+
const NEW_MARKETPLACE_NAME = "magus";
|
|
416
421
|
/**
|
|
417
|
-
*
|
|
422
|
+
* Rename plugin keys in a Record from old marketplace to new.
|
|
423
|
+
* e.g., "frontend@mag-claude-plugins" → "frontend@magus"
|
|
424
|
+
* Returns [migratedRecord, count] — count=0 means no changes.
|
|
425
|
+
*/
|
|
426
|
+
function migratePluginKeys(record) {
|
|
427
|
+
if (!record)
|
|
428
|
+
return [{}, 0];
|
|
429
|
+
const migrated = {};
|
|
430
|
+
let count = 0;
|
|
431
|
+
for (const [key, value] of Object.entries(record)) {
|
|
432
|
+
if (key.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
|
|
433
|
+
const pluginName = key.slice(0, key.lastIndexOf("@"));
|
|
434
|
+
migrated[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = value;
|
|
435
|
+
count++;
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
migrated[key] = value;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return [migrated, count];
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Migrate a single settings object in-place.
|
|
445
|
+
* Returns number of keys renamed.
|
|
446
|
+
*/
|
|
447
|
+
function migrateSettingsObject(settings) {
|
|
448
|
+
let total = 0;
|
|
449
|
+
const [ep, epCount] = migratePluginKeys(settings.enabledPlugins);
|
|
450
|
+
if (epCount > 0) {
|
|
451
|
+
settings.enabledPlugins = ep;
|
|
452
|
+
total += epCount;
|
|
453
|
+
}
|
|
454
|
+
const [iv, ivCount] = migratePluginKeys(settings.installedPluginVersions);
|
|
455
|
+
if (ivCount > 0) {
|
|
456
|
+
settings.installedPluginVersions = iv;
|
|
457
|
+
total += ivCount;
|
|
458
|
+
}
|
|
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
|
+
}
|
|
466
|
+
return total;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Migrate all settings from mag-claude-plugins to magus.
|
|
470
|
+
* Safe to call multiple times — no-ops if already migrated.
|
|
471
|
+
* Runs across project settings, global settings, local settings,
|
|
472
|
+
* known_marketplaces.json, and installed_plugins.json.
|
|
473
|
+
*/
|
|
474
|
+
export async function migrateMarketplaceRename(projectPath) {
|
|
475
|
+
const result = {
|
|
476
|
+
projectMigrated: 0,
|
|
477
|
+
globalMigrated: 0,
|
|
478
|
+
localMigrated: 0,
|
|
479
|
+
knownMarketplacesMigrated: false,
|
|
480
|
+
registryMigrated: 0,
|
|
481
|
+
};
|
|
482
|
+
// 1. Project settings
|
|
483
|
+
try {
|
|
484
|
+
const settings = await readSettings(projectPath);
|
|
485
|
+
const count = migrateSettingsObject(settings);
|
|
486
|
+
if (count > 0) {
|
|
487
|
+
await writeSettings(settings, projectPath);
|
|
488
|
+
result.projectMigrated = count;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
catch { /* skip if unreadable */ }
|
|
492
|
+
// 2. Global settings
|
|
493
|
+
try {
|
|
494
|
+
const settings = await readGlobalSettings();
|
|
495
|
+
const count = migrateSettingsObject(settings);
|
|
496
|
+
if (count > 0) {
|
|
497
|
+
await writeGlobalSettings(settings);
|
|
498
|
+
result.globalMigrated = count;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
catch { /* skip if unreadable */ }
|
|
502
|
+
// 3. Local settings (settings.local.json)
|
|
503
|
+
try {
|
|
504
|
+
const local = await readLocalSettings(projectPath);
|
|
505
|
+
let localCount = 0;
|
|
506
|
+
const [ep, epCount] = migratePluginKeys(local.enabledPlugins);
|
|
507
|
+
if (epCount > 0) {
|
|
508
|
+
local.enabledPlugins = ep;
|
|
509
|
+
localCount += epCount;
|
|
510
|
+
}
|
|
511
|
+
const [iv, ivCount] = migratePluginKeys(local.installedPluginVersions);
|
|
512
|
+
if (ivCount > 0) {
|
|
513
|
+
local.installedPluginVersions = iv;
|
|
514
|
+
localCount += ivCount;
|
|
515
|
+
}
|
|
516
|
+
if (localCount > 0) {
|
|
517
|
+
await writeLocalSettings(local, projectPath);
|
|
518
|
+
result.localMigrated = localCount;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
catch { /* skip if unreadable */ }
|
|
522
|
+
// 4. known_marketplaces.json — rename the key + physical directory cleanup
|
|
523
|
+
const pluginsDir = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
|
|
524
|
+
const oldDir = path.join(pluginsDir, OLD_MARKETPLACE_NAME);
|
|
525
|
+
const newDir = path.join(pluginsDir, NEW_MARKETPLACE_NAME);
|
|
526
|
+
try {
|
|
527
|
+
const known = await readKnownMarketplaces();
|
|
528
|
+
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
|
+
};
|
|
540
|
+
}
|
|
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
|
+
}
|
|
550
|
+
if (knownModified) {
|
|
551
|
+
await writeKnownMarketplaces(known);
|
|
552
|
+
result.knownMarketplacesMigrated = true;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
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);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
catch { /* non-fatal: directory cleanup is best-effort */ }
|
|
569
|
+
// 4c. Update git remote URL in the marketplace clone (old → new repo)
|
|
570
|
+
try {
|
|
571
|
+
const marketplaceDir = await fs.pathExists(newDir) ? newDir : oldDir;
|
|
572
|
+
if (await fs.pathExists(path.join(marketplaceDir, ".git"))) {
|
|
573
|
+
const { execSync } = await import("node:child_process");
|
|
574
|
+
const remote = execSync("git remote get-url origin", {
|
|
575
|
+
cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
|
|
576
|
+
}).trim();
|
|
577
|
+
if (remote.includes("claude-code") && remote.includes("MadAppGang")) {
|
|
578
|
+
const newRemote = remote.replace("claude-code", NEW_MARKETPLACE_NAME);
|
|
579
|
+
execSync(`git remote set-url origin "${newRemote}"`, {
|
|
580
|
+
cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
catch { /* non-fatal: git remote update is best-effort */ }
|
|
586
|
+
// 5. installed_plugins.json — rename plugin ID keys
|
|
587
|
+
try {
|
|
588
|
+
const registry = await readInstalledPluginsRegistry();
|
|
589
|
+
let regCount = 0;
|
|
590
|
+
const newPlugins = {};
|
|
591
|
+
for (const [pluginId, entries] of Object.entries(registry.plugins)) {
|
|
592
|
+
if (pluginId.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
|
|
593
|
+
const pluginName = pluginId.slice(0, pluginId.lastIndexOf("@"));
|
|
594
|
+
newPlugins[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = entries;
|
|
595
|
+
regCount++;
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
newPlugins[pluginId] = entries;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (regCount > 0) {
|
|
602
|
+
registry.plugins = newPlugins;
|
|
603
|
+
await writeInstalledPluginsRegistry(registry);
|
|
604
|
+
result.registryMigrated = regCount;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
catch { /* skip if unreadable */ }
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Check if a marketplace is from Magus (MadAppGang)
|
|
418
612
|
*/
|
|
419
613
|
function isMadAppGangMarketplace(entry) {
|
|
420
614
|
const repo = entry.source?.repo?.toLowerCase() || "";
|
|
@@ -422,7 +616,7 @@ function isMadAppGangMarketplace(entry) {
|
|
|
422
616
|
}
|
|
423
617
|
/**
|
|
424
618
|
* Recover/sync marketplace settings:
|
|
425
|
-
* - Enable autoUpdate for
|
|
619
|
+
* - Enable autoUpdate for Magus marketplaces that don't have it set
|
|
426
620
|
* - Remove entries for marketplaces whose installLocation no longer exists
|
|
427
621
|
*/
|
|
428
622
|
export async function recoverMarketplaceSettings() {
|
|
@@ -439,7 +633,7 @@ export async function recoverMarketplaceSettings() {
|
|
|
439
633
|
result.removed.push(name);
|
|
440
634
|
continue;
|
|
441
635
|
}
|
|
442
|
-
// Enable autoUpdate if not set - only for MadAppGang marketplaces
|
|
636
|
+
// Enable autoUpdate if not set - only for Magus (MadAppGang) marketplaces
|
|
443
637
|
if (entry.autoUpdate === undefined && isMadAppGangMarketplace(entry)) {
|
|
444
638
|
entry.autoUpdate = true;
|
|
445
639
|
result.enabledAutoUpdate.push(name);
|
|
@@ -629,8 +629,222 @@ export interface MarketplaceRecoveryResult {
|
|
|
629
629
|
removed: string[];
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
+
// =============================================================================
|
|
633
|
+
// MARKETPLACE RENAME MIGRATION: mag-claude-plugins → magus
|
|
634
|
+
// =============================================================================
|
|
635
|
+
|
|
636
|
+
const OLD_MARKETPLACE_NAME = "mag-claude-plugins";
|
|
637
|
+
const NEW_MARKETPLACE_NAME = "magus";
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Rename plugin keys in a Record from old marketplace to new.
|
|
641
|
+
* e.g., "frontend@mag-claude-plugins" → "frontend@magus"
|
|
642
|
+
* Returns [migratedRecord, count] — count=0 means no changes.
|
|
643
|
+
*/
|
|
644
|
+
function migratePluginKeys<T>(
|
|
645
|
+
record: Record<string, T> | undefined,
|
|
646
|
+
): [Record<string, T>, number] {
|
|
647
|
+
if (!record) return [{}, 0];
|
|
648
|
+
const migrated: Record<string, T> = {};
|
|
649
|
+
let count = 0;
|
|
650
|
+
for (const [key, value] of Object.entries(record)) {
|
|
651
|
+
if (key.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
|
|
652
|
+
const pluginName = key.slice(0, key.lastIndexOf("@"));
|
|
653
|
+
migrated[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = value;
|
|
654
|
+
count++;
|
|
655
|
+
} else {
|
|
656
|
+
migrated[key] = value;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return [migrated, count];
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Migrate a single settings object in-place.
|
|
664
|
+
* Returns number of keys renamed.
|
|
665
|
+
*/
|
|
666
|
+
function migrateSettingsObject(settings: ClaudeSettings): number {
|
|
667
|
+
let total = 0;
|
|
668
|
+
|
|
669
|
+
const [ep, epCount] = migratePluginKeys(settings.enabledPlugins);
|
|
670
|
+
if (epCount > 0) {
|
|
671
|
+
settings.enabledPlugins = ep;
|
|
672
|
+
total += epCount;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const [iv, ivCount] = migratePluginKeys(settings.installedPluginVersions);
|
|
676
|
+
if (ivCount > 0) {
|
|
677
|
+
settings.installedPluginVersions = iv;
|
|
678
|
+
total += ivCount;
|
|
679
|
+
}
|
|
680
|
+
|
|
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
|
+
}
|
|
688
|
+
|
|
689
|
+
return total;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
export interface MigrationResult {
|
|
693
|
+
projectMigrated: number;
|
|
694
|
+
globalMigrated: number;
|
|
695
|
+
localMigrated: number;
|
|
696
|
+
knownMarketplacesMigrated: boolean;
|
|
697
|
+
registryMigrated: number;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Migrate all settings from mag-claude-plugins to magus.
|
|
702
|
+
* Safe to call multiple times — no-ops if already migrated.
|
|
703
|
+
* Runs across project settings, global settings, local settings,
|
|
704
|
+
* known_marketplaces.json, and installed_plugins.json.
|
|
705
|
+
*/
|
|
706
|
+
export async function migrateMarketplaceRename(
|
|
707
|
+
projectPath?: string,
|
|
708
|
+
): Promise<MigrationResult> {
|
|
709
|
+
const result: MigrationResult = {
|
|
710
|
+
projectMigrated: 0,
|
|
711
|
+
globalMigrated: 0,
|
|
712
|
+
localMigrated: 0,
|
|
713
|
+
knownMarketplacesMigrated: false,
|
|
714
|
+
registryMigrated: 0,
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
// 1. Project settings
|
|
718
|
+
try {
|
|
719
|
+
const settings = await readSettings(projectPath);
|
|
720
|
+
const count = migrateSettingsObject(settings);
|
|
721
|
+
if (count > 0) {
|
|
722
|
+
await writeSettings(settings, projectPath);
|
|
723
|
+
result.projectMigrated = count;
|
|
724
|
+
}
|
|
725
|
+
} catch { /* skip if unreadable */ }
|
|
726
|
+
|
|
727
|
+
// 2. Global settings
|
|
728
|
+
try {
|
|
729
|
+
const settings = await readGlobalSettings();
|
|
730
|
+
const count = migrateSettingsObject(settings);
|
|
731
|
+
if (count > 0) {
|
|
732
|
+
await writeGlobalSettings(settings);
|
|
733
|
+
result.globalMigrated = count;
|
|
734
|
+
}
|
|
735
|
+
} catch { /* skip if unreadable */ }
|
|
736
|
+
|
|
737
|
+
// 3. Local settings (settings.local.json)
|
|
738
|
+
try {
|
|
739
|
+
const local = await readLocalSettings(projectPath);
|
|
740
|
+
let localCount = 0;
|
|
741
|
+
const [ep, epCount] = migratePluginKeys(local.enabledPlugins);
|
|
742
|
+
if (epCount > 0) { local.enabledPlugins = ep; localCount += epCount; }
|
|
743
|
+
const [iv, ivCount] = migratePluginKeys(local.installedPluginVersions);
|
|
744
|
+
if (ivCount > 0) { local.installedPluginVersions = iv; localCount += ivCount; }
|
|
745
|
+
if (localCount > 0) {
|
|
746
|
+
await writeLocalSettings(local, projectPath);
|
|
747
|
+
result.localMigrated = localCount;
|
|
748
|
+
}
|
|
749
|
+
} catch { /* skip if unreadable */ }
|
|
750
|
+
|
|
751
|
+
// 4. known_marketplaces.json — rename the key + physical directory cleanup
|
|
752
|
+
const pluginsDir = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
|
|
753
|
+
const oldDir = path.join(pluginsDir, OLD_MARKETPLACE_NAME);
|
|
754
|
+
const newDir = path.join(pluginsDir, NEW_MARKETPLACE_NAME);
|
|
755
|
+
|
|
756
|
+
try {
|
|
757
|
+
const known = await readKnownMarketplaces();
|
|
758
|
+
let knownModified = false;
|
|
759
|
+
|
|
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
|
+
};
|
|
772
|
+
}
|
|
773
|
+
delete known[OLD_MARKETPLACE_NAME];
|
|
774
|
+
knownModified = true;
|
|
775
|
+
}
|
|
776
|
+
|
|
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;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (knownModified) {
|
|
788
|
+
await writeKnownMarketplaces(known);
|
|
789
|
+
result.knownMarketplacesMigrated = true;
|
|
790
|
+
}
|
|
791
|
+
} catch { /* skip if unreadable */ }
|
|
792
|
+
|
|
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);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
} catch { /* non-fatal: directory cleanup is best-effort */ }
|
|
804
|
+
|
|
805
|
+
// 4c. Update git remote URL in the marketplace clone (old → new repo)
|
|
806
|
+
try {
|
|
807
|
+
const marketplaceDir = await fs.pathExists(newDir) ? newDir : oldDir;
|
|
808
|
+
if (await fs.pathExists(path.join(marketplaceDir, ".git"))) {
|
|
809
|
+
const { execSync } = await import("node:child_process");
|
|
810
|
+
const remote = execSync("git remote get-url origin", {
|
|
811
|
+
cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
|
|
812
|
+
}).trim();
|
|
813
|
+
if (remote.includes("claude-code") && remote.includes("MadAppGang")) {
|
|
814
|
+
const newRemote = remote.replace("claude-code", NEW_MARKETPLACE_NAME);
|
|
815
|
+
execSync(`git remote set-url origin "${newRemote}"`, {
|
|
816
|
+
cwd: marketplaceDir, encoding: "utf-8", timeout: 5000,
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
} catch { /* non-fatal: git remote update is best-effort */ }
|
|
821
|
+
|
|
822
|
+
// 5. installed_plugins.json — rename plugin ID keys
|
|
823
|
+
try {
|
|
824
|
+
const registry = await readInstalledPluginsRegistry();
|
|
825
|
+
let regCount = 0;
|
|
826
|
+
const newPlugins: typeof registry.plugins = {};
|
|
827
|
+
for (const [pluginId, entries] of Object.entries(registry.plugins)) {
|
|
828
|
+
if (pluginId.endsWith(`@${OLD_MARKETPLACE_NAME}`)) {
|
|
829
|
+
const pluginName = pluginId.slice(0, pluginId.lastIndexOf("@"));
|
|
830
|
+
newPlugins[`${pluginName}@${NEW_MARKETPLACE_NAME}`] = entries;
|
|
831
|
+
regCount++;
|
|
832
|
+
} else {
|
|
833
|
+
newPlugins[pluginId] = entries;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (regCount > 0) {
|
|
837
|
+
registry.plugins = newPlugins;
|
|
838
|
+
await writeInstalledPluginsRegistry(registry);
|
|
839
|
+
result.registryMigrated = regCount;
|
|
840
|
+
}
|
|
841
|
+
} catch { /* skip if unreadable */ }
|
|
842
|
+
|
|
843
|
+
return result;
|
|
844
|
+
}
|
|
845
|
+
|
|
632
846
|
/**
|
|
633
|
-
* Check if a marketplace is from MadAppGang
|
|
847
|
+
* Check if a marketplace is from Magus (MadAppGang)
|
|
634
848
|
*/
|
|
635
849
|
function isMadAppGangMarketplace(entry: KnownMarketplaceEntry): boolean {
|
|
636
850
|
const repo = entry.source?.repo?.toLowerCase() || "";
|
|
@@ -639,7 +853,7 @@ function isMadAppGangMarketplace(entry: KnownMarketplaceEntry): boolean {
|
|
|
639
853
|
|
|
640
854
|
/**
|
|
641
855
|
* Recover/sync marketplace settings:
|
|
642
|
-
* - Enable autoUpdate for
|
|
856
|
+
* - Enable autoUpdate for Magus marketplaces that don't have it set
|
|
643
857
|
* - Remove entries for marketplaces whose installLocation no longer exists
|
|
644
858
|
*/
|
|
645
859
|
export async function recoverMarketplaceSettings(): Promise<MarketplaceRecoveryResult> {
|
|
@@ -661,7 +875,7 @@ export async function recoverMarketplaceSettings(): Promise<MarketplaceRecoveryR
|
|
|
661
875
|
continue;
|
|
662
876
|
}
|
|
663
877
|
|
|
664
|
-
// Enable autoUpdate if not set - only for MadAppGang marketplaces
|
|
878
|
+
// Enable autoUpdate if not set - only for Magus (MadAppGang) marketplaces
|
|
665
879
|
if (entry.autoUpdate === undefined && isMadAppGangMarketplace(entry)) {
|
|
666
880
|
entry.autoUpdate = true;
|
|
667
881
|
result.enabledAutoUpdate.push(name);
|
|
@@ -43,7 +43,7 @@ function extractAllEnvVarReferences(obj) {
|
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* Get the path to a plugin's mcp-config.json in the marketplace cache
|
|
46
|
-
* @param marketplaceName - Name of the marketplace (e.g., "
|
|
46
|
+
* @param marketplaceName - Name of the marketplace (e.g., "magus")
|
|
47
47
|
* @param pluginSource - Plugin source path from marketplace.json (e.g., "./plugins/frontend")
|
|
48
48
|
* @returns Full path to mcp-config.json or undefined if not found
|
|
49
49
|
*/
|
|
@@ -98,7 +98,7 @@ function generateLabel(varName) {
|
|
|
98
98
|
* Get all environment variable requirements for a plugin
|
|
99
99
|
* Extracts ${VAR_NAME} patterns from the plugin's mcp-config.json
|
|
100
100
|
*
|
|
101
|
-
* @param marketplaceName - Name of the marketplace (e.g., "
|
|
101
|
+
* @param marketplaceName - Name of the marketplace (e.g., "magus")
|
|
102
102
|
* @param pluginSource - Plugin source path from marketplace.json (e.g., "./plugins/frontend")
|
|
103
103
|
* @returns Array of required env vars with metadata
|
|
104
104
|
*/
|
|
@@ -74,7 +74,7 @@ interface McpServerConfig {
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Get the path to a plugin's mcp-config.json in the marketplace cache
|
|
77
|
-
* @param marketplaceName - Name of the marketplace (e.g., "
|
|
77
|
+
* @param marketplaceName - Name of the marketplace (e.g., "magus")
|
|
78
78
|
* @param pluginSource - Plugin source path from marketplace.json (e.g., "./plugins/frontend")
|
|
79
79
|
* @returns Full path to mcp-config.json or undefined if not found
|
|
80
80
|
*/
|
|
@@ -142,7 +142,7 @@ function generateLabel(varName: string): string {
|
|
|
142
142
|
* Get all environment variable requirements for a plugin
|
|
143
143
|
* Extracts ${VAR_NAME} patterns from the plugin's mcp-config.json
|
|
144
144
|
*
|
|
145
|
-
* @param marketplaceName - Name of the marketplace (e.g., "
|
|
145
|
+
* @param marketplaceName - Name of the marketplace (e.g., "magus")
|
|
146
146
|
* @param pluginSource - Plugin source path from marketplace.json (e.g., "./plugins/frontend")
|
|
147
147
|
* @returns Array of required env vars with metadata
|
|
148
148
|
*/
|
|
@@ -7,7 +7,7 @@ const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
|
7
7
|
export interface UpdateCacheData {
|
|
8
8
|
lastUpdateCheck: string; // ISO timestamp
|
|
9
9
|
lastUpdateResult: {
|
|
10
|
-
updated: string[]; // ["frontend@
|
|
10
|
+
updated: string[]; // ["frontend@magus v3.13.0", ...]
|
|
11
11
|
failed: string[]; // Marketplaces that failed
|
|
12
12
|
autoUpdated?: Array<{
|
|
13
13
|
pluginId: string;
|
package/src/ui/App.js
CHANGED
|
@@ -7,6 +7,7 @@ import { DimensionsProvider, useDimensions, } from "./state/DimensionsContext.js
|
|
|
7
7
|
import { ModalContainer } from "./components/modals/index.js";
|
|
8
8
|
import { PluginsScreen, McpScreen, McpRegistryScreen, StatusLineScreen, EnvVarsScreen, CliToolsScreen, ModelSelectorScreen, } from "./screens/index.js";
|
|
9
9
|
import { repairAllMarketplaces } from "../services/local-marketplace.js";
|
|
10
|
+
import { migrateMarketplaceRename } from "../services/claude-settings.js";
|
|
10
11
|
import { checkForUpdates, getCurrentVersion, } from "../services/version-check.js";
|
|
11
12
|
import { useKeyboardHandler } from "./hooks/useKeyboardHandler.js";
|
|
12
13
|
import { ProgressBar } from "./components/layout/ProgressBar.js";
|
|
@@ -181,8 +182,9 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
|
|
|
181
182
|
type: "SHOW_PROGRESS",
|
|
182
183
|
state: { message: "Scanning marketplaces..." },
|
|
183
184
|
});
|
|
184
|
-
//
|
|
185
|
-
|
|
185
|
+
// Migrate mag-claude-plugins → magus (idempotent), then repair plugin.json files
|
|
186
|
+
migrateMarketplaceRename()
|
|
187
|
+
.catch(() => { }); // non-blocking, best-effort
|
|
186
188
|
repairAllMarketplaces()
|
|
187
189
|
.then(async () => {
|
|
188
190
|
dispatch({ type: "HIDE_PROGRESS" });
|
package/src/ui/App.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "./screens/index.js";
|
|
24
24
|
import type { Screen } from "./state/types.js";
|
|
25
25
|
import { repairAllMarketplaces } from "../services/local-marketplace.js";
|
|
26
|
+
import { migrateMarketplaceRename } from "../services/claude-settings.js";
|
|
26
27
|
import {
|
|
27
28
|
checkForUpdates,
|
|
28
29
|
getCurrentVersion,
|
|
@@ -264,8 +265,10 @@ function AppContentInner({
|
|
|
264
265
|
state: { message: "Scanning marketplaces..." },
|
|
265
266
|
});
|
|
266
267
|
|
|
267
|
-
//
|
|
268
|
-
|
|
268
|
+
// Migrate mag-claude-plugins → magus (idempotent), then repair plugin.json files
|
|
269
|
+
migrateMarketplaceRename()
|
|
270
|
+
.catch(() => {}); // non-blocking, best-effort
|
|
271
|
+
|
|
269
272
|
repairAllMarketplaces()
|
|
270
273
|
.then(async () => {
|
|
271
274
|
dispatch({ type: "HIDE_PROGRESS" });
|
|
@@ -265,7 +265,7 @@ export function PluginsScreen() {
|
|
|
265
265
|
await modal.message("Add Marketplace", "To add a marketplace, run this command in your terminal:\n\n" +
|
|
266
266
|
" claude marketplace add owner/repo\n\n" +
|
|
267
267
|
"Examples:\n" +
|
|
268
|
-
" claude marketplace add MadAppGang/
|
|
268
|
+
" claude marketplace add MadAppGang/magus\n" +
|
|
269
269
|
" claude marketplace add anthropics/claude-plugins-official\n\n" +
|
|
270
270
|
"Auto-update is enabled by default for new marketplaces.\n\n" +
|
|
271
271
|
"After adding, refresh claudeup with 'r' to see the new marketplace.", "info");
|
|
@@ -344,7 +344,7 @@ export function PluginsScreen() {
|
|
|
344
344
|
"To add a marketplace, run this command in your terminal:\n\n" +
|
|
345
345
|
" claude marketplace add owner/repo\n\n" +
|
|
346
346
|
"Examples:\n" +
|
|
347
|
-
" claude marketplace add MadAppGang/
|
|
347
|
+
" claude marketplace add MadAppGang/magus\n" +
|
|
348
348
|
" claude marketplace add anthropics/claude-plugins-official\n\n" +
|
|
349
349
|
"Auto-update is enabled by default for new marketplaces.\n\n" +
|
|
350
350
|
"After adding, refresh claudeup with 'r' to see the new marketplace.",
|