claudeup 4.0.1 → 4.2.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/package.json +1 -1
- package/src/data/cli-tools.js +7 -7
- package/src/data/cli-tools.ts +8 -9
- package/src/data/marketplaces.js +6 -2
- package/src/data/marketplaces.ts +10 -3
- package/src/prerunner/index.js +71 -7
- package/src/prerunner/index.ts +94 -6
- package/src/services/claude-settings.js +79 -12
- package/src/services/claude-settings.ts +101 -19
- package/src/types/index.ts +33 -0
- package/src/ui/renderers/cliToolRenderers.js +28 -7
- package/src/ui/renderers/cliToolRenderers.tsx +104 -30
- package/src/ui/renderers/pluginRenderers.js +1 -1
- package/src/ui/renderers/pluginRenderers.tsx +3 -5
- package/src/ui/renderers/profileRenderers.js +4 -4
- package/src/ui/renderers/profileRenderers.tsx +10 -12
- package/src/ui/screens/CliToolsScreen.js +152 -49
- package/src/ui/screens/CliToolsScreen.tsx +176 -51
- package/src/ui/screens/PluginsScreen.js +27 -0
- package/src/ui/screens/PluginsScreen.tsx +25 -0
package/package.json
CHANGED
package/src/data/cli-tools.js
CHANGED
|
@@ -11,15 +11,15 @@ export const cliTools = [
|
|
|
11
11
|
packageName: "claudeup",
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
|
-
name: "
|
|
15
|
-
displayName: "
|
|
16
|
-
description: "
|
|
17
|
-
installCommand: "npm install -g
|
|
18
|
-
checkCommand: "
|
|
19
|
-
website: "https://github.com/MadAppGang/
|
|
14
|
+
name: "mnemex",
|
|
15
|
+
displayName: "Mnemex",
|
|
16
|
+
description: "AST-aware code search with PageRank, callers/callees, and semantic embeddings",
|
|
17
|
+
installCommand: "npm install -g mnemex",
|
|
18
|
+
checkCommand: "mnemex --version",
|
|
19
|
+
website: "https://github.com/MadAppGang/mnemex",
|
|
20
20
|
category: "ai-coding",
|
|
21
21
|
packageManager: "npm",
|
|
22
|
-
packageName: "
|
|
22
|
+
packageName: "mnemex",
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
name: "claudish",
|
package/src/data/cli-tools.ts
CHANGED
|
@@ -18,23 +18,22 @@ export const cliTools: CliTool[] = [
|
|
|
18
18
|
"TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
19
19
|
installCommand: "npm install -g claudeup",
|
|
20
20
|
checkCommand: "claudeup --version",
|
|
21
|
-
website:
|
|
22
|
-
"https://github.com/MadAppGang/magus/tree/main/tools/claudeup",
|
|
21
|
+
website: "https://github.com/MadAppGang/magus/tree/main/tools/claudeup",
|
|
23
22
|
category: "ai-coding",
|
|
24
23
|
packageManager: "npm",
|
|
25
24
|
packageName: "claudeup",
|
|
26
25
|
},
|
|
27
26
|
{
|
|
28
|
-
name: "
|
|
29
|
-
displayName: "
|
|
27
|
+
name: "mnemex",
|
|
28
|
+
displayName: "Mnemex",
|
|
30
29
|
description:
|
|
31
|
-
"
|
|
32
|
-
installCommand: "npm install -g
|
|
33
|
-
checkCommand: "
|
|
34
|
-
website: "https://github.com/MadAppGang/
|
|
30
|
+
"AST-aware code search with PageRank, callers/callees, and semantic embeddings",
|
|
31
|
+
installCommand: "npm install -g mnemex",
|
|
32
|
+
checkCommand: "mnemex --version",
|
|
33
|
+
website: "https://github.com/MadAppGang/mnemex",
|
|
35
34
|
category: "ai-coding",
|
|
36
35
|
packageManager: "npm",
|
|
37
|
-
packageName: "
|
|
36
|
+
packageName: "mnemex",
|
|
38
37
|
},
|
|
39
38
|
{
|
|
40
39
|
name: "claudish",
|
package/src/data/marketplaces.js
CHANGED
|
@@ -70,7 +70,8 @@ export function getAllMarketplaces(localMarketplaces) {
|
|
|
70
70
|
const canonical = deprecatedMarketplaces[name];
|
|
71
71
|
if (canonical) {
|
|
72
72
|
// If canonical already in the map or in defaults, skip this entry
|
|
73
|
-
if (all.has(canonical) ||
|
|
73
|
+
if (all.has(canonical) ||
|
|
74
|
+
defaultMarketplaces.some((m) => m.name === canonical)) {
|
|
74
75
|
continue;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
@@ -85,7 +86,10 @@ export function getAllMarketplaces(localMarketplaces) {
|
|
|
85
86
|
name,
|
|
86
87
|
// Prefer default displayName over stale local clone data
|
|
87
88
|
displayName: defaultMp?.displayName || local.name || formatMarketplaceName(name),
|
|
88
|
-
source: {
|
|
89
|
+
source: {
|
|
90
|
+
source: "github",
|
|
91
|
+
repo: defaultMp?.source.repo || local.gitRepo || "",
|
|
92
|
+
},
|
|
89
93
|
description: defaultMp?.description || local.description || "",
|
|
90
94
|
official: defaultMp?.official ?? repo.toLowerCase().includes("anthropics/"),
|
|
91
95
|
featured: defaultMp?.featured,
|
package/src/data/marketplaces.ts
CHANGED
|
@@ -85,7 +85,10 @@ export function getAllMarketplaces(
|
|
|
85
85
|
const canonical = deprecatedMarketplaces[name];
|
|
86
86
|
if (canonical) {
|
|
87
87
|
// If canonical already in the map or in defaults, skip this entry
|
|
88
|
-
if (
|
|
88
|
+
if (
|
|
89
|
+
all.has(canonical) ||
|
|
90
|
+
defaultMarketplaces.some((m) => m.name === canonical)
|
|
91
|
+
) {
|
|
89
92
|
continue;
|
|
90
93
|
}
|
|
91
94
|
}
|
|
@@ -100,8 +103,12 @@ export function getAllMarketplaces(
|
|
|
100
103
|
all.set(name, {
|
|
101
104
|
name,
|
|
102
105
|
// Prefer default displayName over stale local clone data
|
|
103
|
-
displayName:
|
|
104
|
-
|
|
106
|
+
displayName:
|
|
107
|
+
defaultMp?.displayName || local.name || formatMarketplaceName(name),
|
|
108
|
+
source: {
|
|
109
|
+
source: "github" as const,
|
|
110
|
+
repo: defaultMp?.source.repo || local.gitRepo || "",
|
|
111
|
+
},
|
|
105
112
|
description: defaultMp?.description || local.description || "",
|
|
106
113
|
official:
|
|
107
114
|
defaultMp?.official ?? repo.toLowerCase().includes("anthropics/"),
|
package/src/prerunner/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import { UpdateCache } from "../services/update-cache.js";
|
|
5
5
|
import { getAvailablePlugins, clearMarketplaceCache, } from "../services/plugin-manager.js";
|
|
6
6
|
import { runClaude } from "../services/claude-runner.js";
|
|
7
|
-
import { recoverMarketplaceSettings, migrateMarketplaceRename, getGlobalEnabledPlugins, getEnabledPlugins, getLocalEnabledPlugins, saveGlobalInstalledPluginVersion, } from "../services/claude-settings.js";
|
|
7
|
+
import { recoverMarketplaceSettings, migrateMarketplaceRename, getGlobalEnabledPlugins, getEnabledPlugins, getLocalEnabledPlugins, saveGlobalInstalledPluginVersion, readGlobalSettings, writeGlobalSettings, } from "../services/claude-settings.js";
|
|
8
8
|
import { parsePluginId } from "../utils/string-utils.js";
|
|
9
9
|
import { defaultMarketplaces } from "../data/marketplaces.js";
|
|
10
10
|
import { updatePlugin, addMarketplace, isClaudeAvailable, } from "../services/claude-cli.js";
|
|
@@ -22,20 +22,26 @@ async function getReferencedMarketplaces(projectPath) {
|
|
|
22
22
|
for (const id of Object.keys(global))
|
|
23
23
|
allPluginIds.add(id);
|
|
24
24
|
}
|
|
25
|
-
catch {
|
|
25
|
+
catch {
|
|
26
|
+
/* skip if unreadable */
|
|
27
|
+
}
|
|
26
28
|
if (projectPath) {
|
|
27
29
|
try {
|
|
28
30
|
const project = await getEnabledPlugins(projectPath);
|
|
29
31
|
for (const id of Object.keys(project))
|
|
30
32
|
allPluginIds.add(id);
|
|
31
33
|
}
|
|
32
|
-
catch {
|
|
34
|
+
catch {
|
|
35
|
+
/* skip if unreadable */
|
|
36
|
+
}
|
|
33
37
|
try {
|
|
34
38
|
const local = await getLocalEnabledPlugins(projectPath);
|
|
35
39
|
for (const id of Object.keys(local))
|
|
36
40
|
allPluginIds.add(id);
|
|
37
41
|
}
|
|
38
|
-
catch {
|
|
42
|
+
catch {
|
|
43
|
+
/* skip if unreadable */
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
// Parse marketplace names from plugin IDs
|
|
41
47
|
for (const pluginId of allPluginIds) {
|
|
@@ -73,6 +79,57 @@ async function autoAddMissingMarketplaces(projectPath) {
|
|
|
73
79
|
}
|
|
74
80
|
return added;
|
|
75
81
|
}
|
|
82
|
+
const CONTINUITY_PLUGIN_SENTINEL = "tmux-claude-continuity";
|
|
83
|
+
const CONTINUITY_PLUGIN_SCRIPT = path.join(os.homedir(), ".tmux", "plugins", "tmux-claude-continuity", "scripts", "on_session_start.sh");
|
|
84
|
+
/**
|
|
85
|
+
* Ensure tmux-claude-continuity Claude Code hooks are present in global settings.
|
|
86
|
+
* If the tmux plugin is installed but the hooks are missing, they are appended.
|
|
87
|
+
* Returns a description of what was added, or null if nothing changed.
|
|
88
|
+
*/
|
|
89
|
+
async function ensureTmuxContinuityHooks() {
|
|
90
|
+
// Plugin not installed — nothing to do
|
|
91
|
+
if (!(await fs.pathExists(CONTINUITY_PLUGIN_SCRIPT))) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const settings = await readGlobalSettings();
|
|
95
|
+
// Check if hooks are already configured by looking for the sentinel string
|
|
96
|
+
const existingHooks = settings.hooks ?? {};
|
|
97
|
+
for (const groups of Object.values(existingHooks)) {
|
|
98
|
+
for (const group of groups) {
|
|
99
|
+
for (const hook of group.hooks) {
|
|
100
|
+
if (hook.command.includes(CONTINUITY_PLUGIN_SENTINEL)) {
|
|
101
|
+
return null; // Already configured
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Append hooks, preserving any existing entries in SessionStart and Stop
|
|
107
|
+
const sessionStartGroups = existingHooks["SessionStart"] ?? [];
|
|
108
|
+
const stopGroups = existingHooks["Stop"] ?? [];
|
|
109
|
+
sessionStartGroups.push({
|
|
110
|
+
hooks: [
|
|
111
|
+
{
|
|
112
|
+
type: "command",
|
|
113
|
+
command: "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_session_start.sh",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
stopGroups.push({
|
|
118
|
+
hooks: [
|
|
119
|
+
{
|
|
120
|
+
type: "command",
|
|
121
|
+
command: "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_stop.sh",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
settings.hooks = {
|
|
126
|
+
...existingHooks,
|
|
127
|
+
SessionStart: sessionStartGroups,
|
|
128
|
+
Stop: stopGroups,
|
|
129
|
+
};
|
|
130
|
+
await writeGlobalSettings(settings);
|
|
131
|
+
return "SessionStart + Stop hooks";
|
|
132
|
+
}
|
|
76
133
|
/**
|
|
77
134
|
* Prerun orchestration: Check for updates, apply them, then run claude
|
|
78
135
|
* @param claudeArgs - Arguments to pass to claude CLI
|
|
@@ -84,9 +141,11 @@ export async function prerunClaude(claudeArgs, options = {}) {
|
|
|
84
141
|
try {
|
|
85
142
|
// STEP 0: Migrate old marketplace names → magus (idempotent, no-ops if already migrated)
|
|
86
143
|
const migration = await migrateMarketplaceRename();
|
|
87
|
-
const migTotal = migration.projectMigrated +
|
|
88
|
-
|
|
89
|
-
|
|
144
|
+
const migTotal = migration.projectMigrated +
|
|
145
|
+
migration.globalMigrated +
|
|
146
|
+
migration.localMigrated +
|
|
147
|
+
migration.registryMigrated +
|
|
148
|
+
(migration.knownMarketplacesMigrated ? 1 : 0);
|
|
90
149
|
if (migTotal > 0) {
|
|
91
150
|
console.log(`✓ Migrated ${migTotal} plugin reference(s) → magus`);
|
|
92
151
|
}
|
|
@@ -99,6 +158,11 @@ export async function prerunClaude(claudeArgs, options = {}) {
|
|
|
99
158
|
console.log(`✓ Auto-added marketplace(s): ${addedMarketplaces.join(", ")}`);
|
|
100
159
|
}
|
|
101
160
|
}
|
|
161
|
+
// STEP 0.6: Ensure tmux-claude-continuity hooks are configured
|
|
162
|
+
const addedHooks = await ensureTmuxContinuityHooks();
|
|
163
|
+
if (addedHooks) {
|
|
164
|
+
console.log(`✓ Added tmux-claude-continuity hooks to ~/.claude/settings.json`);
|
|
165
|
+
}
|
|
102
166
|
// STEP 1: Check if we should update (time-based cache, or forced)
|
|
103
167
|
const shouldUpdate = options.force || (await cache.shouldCheckForUpdates());
|
|
104
168
|
if (options.force) {
|
package/src/prerunner/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
getEnabledPlugins,
|
|
15
15
|
getLocalEnabledPlugins,
|
|
16
16
|
saveGlobalInstalledPluginVersion,
|
|
17
|
+
readGlobalSettings,
|
|
18
|
+
writeGlobalSettings,
|
|
17
19
|
} from "../services/claude-settings.js";
|
|
18
20
|
import { parsePluginId } from "../utils/string-utils.js";
|
|
19
21
|
import { defaultMarketplaces } from "../data/marketplaces.js";
|
|
@@ -49,18 +51,24 @@ async function getReferencedMarketplaces(
|
|
|
49
51
|
try {
|
|
50
52
|
const global = await getGlobalEnabledPlugins();
|
|
51
53
|
for (const id of Object.keys(global)) allPluginIds.add(id);
|
|
52
|
-
} catch {
|
|
54
|
+
} catch {
|
|
55
|
+
/* skip if unreadable */
|
|
56
|
+
}
|
|
53
57
|
|
|
54
58
|
if (projectPath) {
|
|
55
59
|
try {
|
|
56
60
|
const project = await getEnabledPlugins(projectPath);
|
|
57
61
|
for (const id of Object.keys(project)) allPluginIds.add(id);
|
|
58
|
-
} catch {
|
|
62
|
+
} catch {
|
|
63
|
+
/* skip if unreadable */
|
|
64
|
+
}
|
|
59
65
|
|
|
60
66
|
try {
|
|
61
67
|
const local = await getLocalEnabledPlugins(projectPath);
|
|
62
68
|
for (const id of Object.keys(local)) allPluginIds.add(id);
|
|
63
|
-
} catch {
|
|
69
|
+
} catch {
|
|
70
|
+
/* skip if unreadable */
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
// Parse marketplace names from plugin IDs
|
|
@@ -108,6 +116,75 @@ async function autoAddMissingMarketplaces(
|
|
|
108
116
|
return added;
|
|
109
117
|
}
|
|
110
118
|
|
|
119
|
+
const CONTINUITY_PLUGIN_SENTINEL = "tmux-claude-continuity";
|
|
120
|
+
const CONTINUITY_PLUGIN_SCRIPT = path.join(
|
|
121
|
+
os.homedir(),
|
|
122
|
+
".tmux",
|
|
123
|
+
"plugins",
|
|
124
|
+
"tmux-claude-continuity",
|
|
125
|
+
"scripts",
|
|
126
|
+
"on_session_start.sh",
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Ensure tmux-claude-continuity Claude Code hooks are present in global settings.
|
|
131
|
+
* If the tmux plugin is installed but the hooks are missing, they are appended.
|
|
132
|
+
* Returns a description of what was added, or null if nothing changed.
|
|
133
|
+
*/
|
|
134
|
+
async function ensureTmuxContinuityHooks(): Promise<string | null> {
|
|
135
|
+
// Plugin not installed — nothing to do
|
|
136
|
+
if (!(await fs.pathExists(CONTINUITY_PLUGIN_SCRIPT))) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const settings = await readGlobalSettings();
|
|
141
|
+
|
|
142
|
+
// Check if hooks are already configured by looking for the sentinel string
|
|
143
|
+
const existingHooks = settings.hooks ?? {};
|
|
144
|
+
for (const groups of Object.values(existingHooks)) {
|
|
145
|
+
for (const group of groups) {
|
|
146
|
+
for (const hook of group.hooks) {
|
|
147
|
+
if (hook.command.includes(CONTINUITY_PLUGIN_SENTINEL)) {
|
|
148
|
+
return null; // Already configured
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Append hooks, preserving any existing entries in SessionStart and Stop
|
|
155
|
+
const sessionStartGroups = existingHooks["SessionStart"] ?? [];
|
|
156
|
+
const stopGroups = existingHooks["Stop"] ?? [];
|
|
157
|
+
|
|
158
|
+
sessionStartGroups.push({
|
|
159
|
+
hooks: [
|
|
160
|
+
{
|
|
161
|
+
type: "command",
|
|
162
|
+
command:
|
|
163
|
+
"bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_session_start.sh",
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
stopGroups.push({
|
|
169
|
+
hooks: [
|
|
170
|
+
{
|
|
171
|
+
type: "command",
|
|
172
|
+
command:
|
|
173
|
+
"bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_stop.sh",
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
settings.hooks = {
|
|
179
|
+
...existingHooks,
|
|
180
|
+
SessionStart: sessionStartGroups,
|
|
181
|
+
Stop: stopGroups,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
await writeGlobalSettings(settings);
|
|
185
|
+
return "SessionStart + Stop hooks";
|
|
186
|
+
}
|
|
187
|
+
|
|
111
188
|
/**
|
|
112
189
|
* Prerun orchestration: Check for updates, apply them, then run claude
|
|
113
190
|
* @param claudeArgs - Arguments to pass to claude CLI
|
|
@@ -123,9 +200,12 @@ export async function prerunClaude(
|
|
|
123
200
|
try {
|
|
124
201
|
// STEP 0: Migrate old marketplace names → magus (idempotent, no-ops if already migrated)
|
|
125
202
|
const migration = await migrateMarketplaceRename();
|
|
126
|
-
const migTotal =
|
|
127
|
-
|
|
128
|
-
|
|
203
|
+
const migTotal =
|
|
204
|
+
migration.projectMigrated +
|
|
205
|
+
migration.globalMigrated +
|
|
206
|
+
migration.localMigrated +
|
|
207
|
+
migration.registryMigrated +
|
|
208
|
+
(migration.knownMarketplacesMigrated ? 1 : 0);
|
|
129
209
|
if (migTotal > 0) {
|
|
130
210
|
console.log(`✓ Migrated ${migTotal} plugin reference(s) → magus`);
|
|
131
211
|
}
|
|
@@ -142,6 +222,14 @@ export async function prerunClaude(
|
|
|
142
222
|
}
|
|
143
223
|
}
|
|
144
224
|
|
|
225
|
+
// STEP 0.6: Ensure tmux-claude-continuity hooks are configured
|
|
226
|
+
const addedHooks = await ensureTmuxContinuityHooks();
|
|
227
|
+
if (addedHooks) {
|
|
228
|
+
console.log(
|
|
229
|
+
`✓ Added tmux-claude-continuity hooks to ~/.claude/settings.json`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
145
233
|
// STEP 1: Check if we should update (time-based cache, or forced)
|
|
146
234
|
const shouldUpdate = options.force || (await cache.shouldCheckForUpdates());
|
|
147
235
|
|
|
@@ -418,6 +418,8 @@ export async function getMarketplaceAutoUpdate(marketplaceName) {
|
|
|
418
418
|
// =============================================================================
|
|
419
419
|
const OLD_MARKETPLACE_NAMES = ["mag-claude-plugins", "MadAppGang-claude-code"];
|
|
420
420
|
const NEW_MARKETPLACE_NAME = "magus";
|
|
421
|
+
const NEW_MARKETPLACE_REPO = "MadAppGang/magus";
|
|
422
|
+
const OLD_MARKETPLACE_REPOS = ["MadAppGang/claude-code"];
|
|
421
423
|
/**
|
|
422
424
|
* Rename plugin keys in a Record from any old marketplace name to new.
|
|
423
425
|
* e.g., "frontend@mag-claude-plugins" → "frontend@magus"
|
|
@@ -468,11 +470,24 @@ function migrateSettingsObject(settings) {
|
|
|
468
470
|
const entry = settings.extraKnownMarketplaces[oldName];
|
|
469
471
|
delete settings.extraKnownMarketplaces[oldName];
|
|
470
472
|
if (!settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME]) {
|
|
471
|
-
settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] =
|
|
473
|
+
settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] = {
|
|
474
|
+
...entry,
|
|
475
|
+
source: {
|
|
476
|
+
...entry.source,
|
|
477
|
+
repo: NEW_MARKETPLACE_REPO,
|
|
478
|
+
},
|
|
479
|
+
};
|
|
472
480
|
}
|
|
473
481
|
total++;
|
|
474
482
|
}
|
|
475
483
|
}
|
|
484
|
+
// Fix stale repo URL on existing magus entry (e.g. key is "magus" but repo is still "MadAppGang/claude-code")
|
|
485
|
+
const magusEntry = settings.extraKnownMarketplaces?.[NEW_MARKETPLACE_NAME];
|
|
486
|
+
if (magusEntry?.source?.repo &&
|
|
487
|
+
OLD_MARKETPLACE_REPOS.includes(magusEntry.source.repo)) {
|
|
488
|
+
magusEntry.source.repo = NEW_MARKETPLACE_REPO;
|
|
489
|
+
total++;
|
|
490
|
+
}
|
|
476
491
|
return total;
|
|
477
492
|
}
|
|
478
493
|
/**
|
|
@@ -498,7 +513,9 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
498
513
|
result.projectMigrated = count;
|
|
499
514
|
}
|
|
500
515
|
}
|
|
501
|
-
catch {
|
|
516
|
+
catch {
|
|
517
|
+
/* skip if unreadable */
|
|
518
|
+
}
|
|
502
519
|
// 2. Global settings
|
|
503
520
|
try {
|
|
504
521
|
const settings = await readGlobalSettings();
|
|
@@ -508,7 +525,9 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
508
525
|
result.globalMigrated = count;
|
|
509
526
|
}
|
|
510
527
|
}
|
|
511
|
-
catch {
|
|
528
|
+
catch {
|
|
529
|
+
/* skip if unreadable */
|
|
530
|
+
}
|
|
512
531
|
// 3. Local settings (settings.local.json)
|
|
513
532
|
try {
|
|
514
533
|
const local = await readLocalSettings(projectPath);
|
|
@@ -528,7 +547,9 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
528
547
|
result.localMigrated = localCount;
|
|
529
548
|
}
|
|
530
549
|
}
|
|
531
|
-
catch {
|
|
550
|
+
catch {
|
|
551
|
+
/* skip if unreadable */
|
|
552
|
+
}
|
|
532
553
|
// 4. known_marketplaces.json — rename old keys + physical directory cleanup
|
|
533
554
|
const pluginsDir = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
|
|
534
555
|
const newDir = path.join(pluginsDir, NEW_MARKETPLACE_NAME);
|
|
@@ -553,8 +574,7 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
553
574
|
}
|
|
554
575
|
// Ensure installLocation doesn't reference old directory names
|
|
555
576
|
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);
|
|
577
|
+
known[NEW_MARKETPLACE_NAME].installLocation = known[NEW_MARKETPLACE_NAME].installLocation.replace(oldName, NEW_MARKETPLACE_NAME);
|
|
558
578
|
knownModified = true;
|
|
559
579
|
}
|
|
560
580
|
}
|
|
@@ -563,7 +583,9 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
563
583
|
result.knownMarketplacesMigrated = true;
|
|
564
584
|
}
|
|
565
585
|
}
|
|
566
|
-
catch {
|
|
586
|
+
catch {
|
|
587
|
+
/* skip if unreadable */
|
|
588
|
+
}
|
|
567
589
|
// 4b. Rename/remove old physical directories (runs even if keys were already migrated)
|
|
568
590
|
for (const oldName of OLD_MARKETPLACE_NAMES) {
|
|
569
591
|
const oldDir = path.join(pluginsDir, oldName);
|
|
@@ -578,24 +600,32 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
578
600
|
}
|
|
579
601
|
}
|
|
580
602
|
}
|
|
581
|
-
catch {
|
|
603
|
+
catch {
|
|
604
|
+
/* non-fatal: directory cleanup is best-effort */
|
|
605
|
+
}
|
|
582
606
|
}
|
|
583
607
|
// 4c. Update git remote URL in the marketplace clone (old → new repo)
|
|
584
608
|
try {
|
|
585
609
|
if (await fs.pathExists(path.join(newDir, ".git"))) {
|
|
586
610
|
const { execSync } = await import("node:child_process");
|
|
587
611
|
const remote = execSync("git remote get-url origin", {
|
|
588
|
-
cwd: newDir,
|
|
612
|
+
cwd: newDir,
|
|
613
|
+
encoding: "utf-8",
|
|
614
|
+
timeout: 5000,
|
|
589
615
|
}).trim();
|
|
590
616
|
if (remote.includes("claude-code") && remote.includes("MadAppGang")) {
|
|
591
617
|
const newRemote = remote.replace("claude-code", NEW_MARKETPLACE_NAME);
|
|
592
618
|
execSync(`git remote set-url origin "${newRemote}"`, {
|
|
593
|
-
cwd: newDir,
|
|
619
|
+
cwd: newDir,
|
|
620
|
+
encoding: "utf-8",
|
|
621
|
+
timeout: 5000,
|
|
594
622
|
});
|
|
595
623
|
}
|
|
596
624
|
}
|
|
597
625
|
}
|
|
598
|
-
catch {
|
|
626
|
+
catch {
|
|
627
|
+
/* non-fatal: git remote update is best-effort */
|
|
628
|
+
}
|
|
599
629
|
// 5. installed_plugins.json — rename plugin ID keys
|
|
600
630
|
try {
|
|
601
631
|
const registry = await readInstalledPluginsRegistry();
|
|
@@ -621,7 +651,44 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
621
651
|
result.registryMigrated = regCount;
|
|
622
652
|
}
|
|
623
653
|
}
|
|
624
|
-
catch {
|
|
654
|
+
catch {
|
|
655
|
+
/* skip if unreadable */
|
|
656
|
+
}
|
|
657
|
+
// 6. Scan all known project settings (derived from ~/.claude/projects/ directory names)
|
|
658
|
+
try {
|
|
659
|
+
const projectsDir = path.join(os.homedir(), ".claude", "projects");
|
|
660
|
+
if (await fs.pathExists(projectsDir)) {
|
|
661
|
+
const entries = await fs.readdir(projectsDir);
|
|
662
|
+
const seenPaths = new Set();
|
|
663
|
+
// Current project (from step 1) already handled — skip it
|
|
664
|
+
const currentProject = projectPath || process.cwd();
|
|
665
|
+
seenPaths.add(currentProject);
|
|
666
|
+
for (const entry of entries) {
|
|
667
|
+
// Directory names encode paths: -Users-jack-dev-foo → /Users/jack/dev/foo
|
|
668
|
+
const decoded = entry.replace(/^-/, "/").replace(/-/g, "/");
|
|
669
|
+
if (seenPaths.has(decoded))
|
|
670
|
+
continue;
|
|
671
|
+
seenPaths.add(decoded);
|
|
672
|
+
const settingsFile = path.join(decoded, ".claude", "settings.json");
|
|
673
|
+
try {
|
|
674
|
+
if (await fs.pathExists(settingsFile)) {
|
|
675
|
+
const raw = await fs.readJson(settingsFile);
|
|
676
|
+
const count = migrateSettingsObject(raw);
|
|
677
|
+
if (count > 0) {
|
|
678
|
+
await fs.writeJson(settingsFile, raw, { spaces: 2 });
|
|
679
|
+
result.projectMigrated += count;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
/* skip individual projects that fail */
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
catch {
|
|
690
|
+
/* non-fatal: cross-project scan is best-effort */
|
|
691
|
+
}
|
|
625
692
|
return result;
|
|
626
693
|
}
|
|
627
694
|
/**
|