claudeup 4.10.0 → 4.10.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudeup",
3
- "version": "4.10.0",
3
+ "version": "4.10.2",
4
4
  "description": "TUI tool for managing Claude Code plugins, MCPs, and configuration",
5
5
  "type": "module",
6
6
  "main": "src/main.tsx",
@@ -53,9 +53,8 @@ async function execClaude(args, timeoutMs = 30000) {
53
53
  * Handles enabling + version tracking + cache copy in one shot.
54
54
  *
55
55
  * If the install fails because the plugin is "not found in marketplace",
56
- * triggers a marketplace update and retries once. This handles the case
57
- * where claudeup's remote listing (from GitHub) has newer plugins than
58
- * the local marketplace clone.
56
+ * recovers the marketplace registry (fixes stale "directory" sources),
57
+ * triggers a marketplace update, and retries once.
59
58
  */
60
59
  export async function installPlugin(pluginId, scope = "user") {
61
60
  try {
@@ -64,10 +63,13 @@ export async function installPlugin(pluginId, scope = "user") {
64
63
  catch (error) {
65
64
  const msg = error instanceof Error ? error.message : String(error);
66
65
  if (msg.includes("not found in marketplace")) {
67
- // Local marketplace clone is stale — update and retry
68
66
  const parts = pluginId.split("@");
69
67
  const marketplace = parts[1];
70
68
  if (marketplace) {
69
+ // Fix known_marketplaces.json first (stale "directory" → "github"),
70
+ // then update, then retry
71
+ const { recoverMarketplaceSettings } = await import("./claude-settings.js");
72
+ await recoverMarketplaceSettings();
71
73
  await updateMarketplace(marketplace);
72
74
  await execClaude([
73
75
  "plugin",
@@ -136,6 +138,8 @@ export async function updatePlugin(pluginId, scope = "user") {
136
138
  const parts = pluginId.split("@");
137
139
  const marketplace = parts[1];
138
140
  if (marketplace) {
141
+ const { recoverMarketplaceSettings } = await import("./claude-settings.js");
142
+ await recoverMarketplaceSettings();
139
143
  await updateMarketplace(marketplace);
140
144
  await execClaude(["plugin", "install", pluginId, "--scope", scope], 60000);
141
145
  return;
@@ -70,9 +70,8 @@ async function execClaude(args: string[], timeoutMs = 30000): Promise<string> {
70
70
  * Handles enabling + version tracking + cache copy in one shot.
71
71
  *
72
72
  * If the install fails because the plugin is "not found in marketplace",
73
- * triggers a marketplace update and retries once. This handles the case
74
- * where claudeup's remote listing (from GitHub) has newer plugins than
75
- * the local marketplace clone.
73
+ * recovers the marketplace registry (fixes stale "directory" sources),
74
+ * triggers a marketplace update, and retries once.
76
75
  */
77
76
  export async function installPlugin(
78
77
  pluginId: string,
@@ -84,10 +83,15 @@ export async function installPlugin(
84
83
  const msg =
85
84
  error instanceof Error ? error.message : String(error);
86
85
  if (msg.includes("not found in marketplace")) {
87
- // Local marketplace clone is stale — update and retry
88
86
  const parts = pluginId.split("@");
89
87
  const marketplace = parts[1];
90
88
  if (marketplace) {
89
+ // Fix known_marketplaces.json first (stale "directory" → "github"),
90
+ // then update, then retry
91
+ const { recoverMarketplaceSettings } = await import(
92
+ "./claude-settings.js"
93
+ );
94
+ await recoverMarketplaceSettings();
91
95
  await updateMarketplace(marketplace);
92
96
  await execClaude([
93
97
  "plugin",
@@ -173,6 +177,10 @@ export async function updatePlugin(
173
177
  const parts = pluginId.split("@");
174
178
  const marketplace = parts[1];
175
179
  if (marketplace) {
180
+ const { recoverMarketplaceSettings } = await import(
181
+ "./claude-settings.js"
182
+ );
183
+ await recoverMarketplaceSettings();
176
184
  await updateMarketplace(marketplace);
177
185
  await execClaude(
178
186
  ["plugin", "install", pluginId, "--scope", scope],
package/src/ui/App.js CHANGED
@@ -7,7 +7,7 @@ import { DimensionsProvider, useDimensions, } from "./state/DimensionsContext.js
7
7
  import { ModalContainer } from "./components/modals/index.js";
8
8
  import { PluginsScreen, McpScreen, McpRegistryScreen, SettingsScreen, CliToolsScreen, ModelSelectorScreen, ProfilesScreen, SkillsScreen, } from "./screens/index.js";
9
9
  import { repairAllMarketplaces } from "../services/local-marketplace.js";
10
- import { migrateMarketplaceRename } from "../services/claude-settings.js";
10
+ import { migrateMarketplaceRename, recoverMarketplaceSettings, } from "../services/claude-settings.js";
11
11
  import { checkForUpdates, getCurrentVersion, } from "../services/version-check.js";
12
12
  import { useKeyboardHandler } from "./hooks/useKeyboardHandler.js";
13
13
  import { ProgressBar } from "./components/layout/ProgressBar.js";
@@ -166,7 +166,7 @@ MCP Servers
166
166
  function UpdateBanner({ result }) {
167
167
  if (!result.updateAvailable)
168
168
  return null;
169
- return (_jsxs("box", { paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { bg: "yellow", fg: "black", children: _jsx("strong", { children: " UPDATE " }) }), _jsxs("text", { fg: "yellow", children: [" ", "v", result.currentVersion, " \u2192 v", result.latestVersion] }), _jsx("text", { fg: "gray", children: " Run: " }), _jsx("text", { fg: "cyan", children: "npm i -g claudeup" })] }));
169
+ return (_jsxs("box", { paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { bg: "yellow", fg: "black", children: _jsx("strong", { children: " UPDATE " }) }), _jsxs("text", { fg: "yellow", children: [" ", "v", result.currentVersion, " \u2192 v", result.latestVersion] }), _jsx("text", { fg: "gray", children: " Run: " }), _jsx("text", { fg: "cyan", children: "claudeup update" })] }));
170
170
  }
171
171
  /**
172
172
  * ProgressIndicator Component
@@ -179,6 +179,14 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
179
179
  const { state, dispatch } = useApp();
180
180
  const { progress } = state;
181
181
  const dimensions = useDimensions();
182
+ const [recoveryReport, setRecoveryReport] = useState(null);
183
+ // Auto-dismiss recovery banner after 5 seconds
184
+ useEffect(() => {
185
+ if (!recoveryReport)
186
+ return;
187
+ const timer = setTimeout(() => setRecoveryReport(null), 5000);
188
+ return () => clearTimeout(timer);
189
+ }, [recoveryReport]);
182
190
  // Auto-refresh marketplaces on startup
183
191
  useEffect(() => {
184
192
  const noRefresh = process.argv.includes("--no-refresh");
@@ -190,6 +198,34 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
190
198
  });
191
199
  // Migrate old marketplace names → magus (idempotent), then repair plugin.json files
192
200
  migrateMarketplaceRename().catch(() => { }); // non-blocking, best-effort
201
+ // Recover stale marketplace registry entries (e.g. "directory" → "github")
202
+ recoverMarketplaceSettings()
203
+ .then(async (recovery) => {
204
+ const parts = [];
205
+ if (recovery.reregistered.length > 0) {
206
+ // Update the marketplace clone now that the source is fixed
207
+ const { updateMarketplace } = await import("../services/claude-cli.js");
208
+ for (const mp of recovery.reregistered) {
209
+ try {
210
+ await updateMarketplace(mp);
211
+ parts.push(`${mp} refreshed`);
212
+ }
213
+ catch {
214
+ parts.push(`${mp} (update failed)`);
215
+ }
216
+ }
217
+ }
218
+ if (recovery.enabledAutoUpdate.length > 0) {
219
+ parts.push(`auto-update: ${recovery.enabledAutoUpdate.join(", ")}`);
220
+ }
221
+ if (recovery.removed.length > 0) {
222
+ parts.push(`removed: ${recovery.removed.join(", ")}`);
223
+ }
224
+ if (parts.length > 0) {
225
+ setRecoveryReport(parts.join(" | "));
226
+ }
227
+ })
228
+ .catch(() => { }); // non-fatal
193
229
  repairAllMarketplaces()
194
230
  .then(async () => {
195
231
  dispatch({ type: "HIDE_PROGRESS" });
@@ -199,7 +235,7 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
199
235
  dispatch({ type: "HIDE_PROGRESS" });
200
236
  });
201
237
  }, [dispatch]);
202
- return (_jsxs("box", { flexDirection: "column", height: dimensions.terminalHeight, children: [updateInfo?.updateAvailable && _jsx(UpdateBanner, { result: updateInfo }), showDebug && (_jsx("box", { paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { fg: "#888888", children: ["DEBUG: ", dimensions.terminalWidth, "x", dimensions.terminalHeight, " | content=", dimensions.contentHeight, " | screen=", state.currentRoute.screen] }) })), progress && _jsx(ProgressIndicator, { ...progress }), _jsx("box", { flexDirection: "column", height: dimensions.contentHeight, paddingLeft: 1, paddingRight: 1, children: _jsx(Router, {}) }), _jsx(GlobalKeyHandler, { onDebugToggle: onDebugToggle, onExit: onExit }), _jsx(ModalContainer, {})] }));
238
+ return (_jsxs("box", { flexDirection: "column", height: dimensions.terminalHeight, children: [updateInfo?.updateAvailable && _jsx(UpdateBanner, { result: updateInfo }), recoveryReport && (_jsx("box", { paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { fg: "green", children: ["\u2713 Fixed: ", recoveryReport] }) })), showDebug && (_jsx("box", { paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { fg: "#888888", children: ["DEBUG: ", dimensions.terminalWidth, "x", dimensions.terminalHeight, " | content=", dimensions.contentHeight, " | screen=", state.currentRoute.screen] }) })), progress && _jsx(ProgressIndicator, { ...progress }), _jsx("box", { flexDirection: "column", height: dimensions.contentHeight, paddingLeft: 1, paddingRight: 1, children: _jsx(Router, {}) }), _jsx(GlobalKeyHandler, { onDebugToggle: onDebugToggle, onExit: onExit }), _jsx(ModalContainer, {})] }));
203
239
  }
204
240
  function AppContent({ onExit }) {
205
241
  const { state } = useApp();
package/src/ui/App.tsx CHANGED
@@ -24,7 +24,10 @@ import {
24
24
  } from "./screens/index.js";
25
25
  import type { Screen } from "./state/types.js";
26
26
  import { repairAllMarketplaces } from "../services/local-marketplace.js";
27
- import { migrateMarketplaceRename } from "../services/claude-settings.js";
27
+ import {
28
+ migrateMarketplaceRename,
29
+ recoverMarketplaceSettings,
30
+ } from "../services/claude-settings.js";
28
31
  import {
29
32
  checkForUpdates,
30
33
  getCurrentVersion,
@@ -213,7 +216,7 @@ function UpdateBanner({ result }: { result: VersionCheckResult }) {
213
216
  v{result.currentVersion} → v{result.latestVersion}
214
217
  </text>
215
218
  <text fg="gray"> Run: </text>
216
- <text fg="cyan">npm i -g claudeup</text>
219
+ <text fg="cyan">claudeup update</text>
217
220
  </box>
218
221
  );
219
222
  }
@@ -258,6 +261,14 @@ function AppContentInner({
258
261
  const { state, dispatch } = useApp();
259
262
  const { progress } = state;
260
263
  const dimensions = useDimensions();
264
+ const [recoveryReport, setRecoveryReport] = useState<string | null>(null);
265
+
266
+ // Auto-dismiss recovery banner after 5 seconds
267
+ useEffect(() => {
268
+ if (!recoveryReport) return;
269
+ const timer = setTimeout(() => setRecoveryReport(null), 5000);
270
+ return () => clearTimeout(timer);
271
+ }, [recoveryReport]);
261
272
 
262
273
  // Auto-refresh marketplaces on startup
263
274
  useEffect(() => {
@@ -272,6 +283,40 @@ function AppContentInner({
272
283
  // Migrate old marketplace names → magus (idempotent), then repair plugin.json files
273
284
  migrateMarketplaceRename().catch(() => {}); // non-blocking, best-effort
274
285
 
286
+ // Recover stale marketplace registry entries (e.g. "directory" → "github")
287
+ recoverMarketplaceSettings()
288
+ .then(async (recovery) => {
289
+ const parts: string[] = [];
290
+ if (recovery.reregistered.length > 0) {
291
+ // Update the marketplace clone now that the source is fixed
292
+ const { updateMarketplace } = await import(
293
+ "../services/claude-cli.js"
294
+ );
295
+ for (const mp of recovery.reregistered) {
296
+ try {
297
+ await updateMarketplace(mp);
298
+ parts.push(`${mp} refreshed`);
299
+ } catch {
300
+ parts.push(`${mp} (update failed)`);
301
+ }
302
+ }
303
+ }
304
+ if (recovery.enabledAutoUpdate.length > 0) {
305
+ parts.push(
306
+ `auto-update: ${recovery.enabledAutoUpdate.join(", ")}`,
307
+ );
308
+ }
309
+ if (recovery.removed.length > 0) {
310
+ parts.push(
311
+ `removed: ${recovery.removed.join(", ")}`,
312
+ );
313
+ }
314
+ if (parts.length > 0) {
315
+ setRecoveryReport(parts.join(" | "));
316
+ }
317
+ })
318
+ .catch(() => {}); // non-fatal
319
+
275
320
  repairAllMarketplaces()
276
321
  .then(async () => {
277
322
  dispatch({ type: "HIDE_PROGRESS" });
@@ -285,6 +330,11 @@ function AppContentInner({
285
330
  return (
286
331
  <box flexDirection="column" height={dimensions.terminalHeight}>
287
332
  {updateInfo?.updateAvailable && <UpdateBanner result={updateInfo} />}
333
+ {recoveryReport && (
334
+ <box paddingLeft={1} paddingRight={1}>
335
+ <text fg="green">✓ Fixed: {recoveryReport}</text>
336
+ </box>
337
+ )}
288
338
  {showDebug && (
289
339
  <box paddingLeft={1} paddingRight={1}>
290
340
  <text fg="#888888">