claudeup 4.10.0 → 4.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudeup",
3
- "version": "4.10.0",
3
+ "version": "4.10.1",
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";
@@ -179,6 +179,7 @@ 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);
182
183
  // Auto-refresh marketplaces on startup
183
184
  useEffect(() => {
184
185
  const noRefresh = process.argv.includes("--no-refresh");
@@ -190,6 +191,35 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
190
191
  });
191
192
  // Migrate old marketplace names → magus (idempotent), then repair plugin.json files
192
193
  migrateMarketplaceRename().catch(() => { }); // non-blocking, best-effort
194
+ // Recover stale marketplace registry entries (e.g. "directory" → "github")
195
+ recoverMarketplaceSettings()
196
+ .then(async (recovery) => {
197
+ const msgs = [];
198
+ if (recovery.reregistered.length > 0) {
199
+ msgs.push(`Re-registered as GitHub: ${recovery.reregistered.join(", ")}`);
200
+ // Update the marketplace clone now that the source is fixed
201
+ const { updateMarketplace } = await import("../services/claude-cli.js");
202
+ for (const mp of recovery.reregistered) {
203
+ try {
204
+ await updateMarketplace(mp);
205
+ msgs.push(`Updated marketplace: ${mp}`);
206
+ }
207
+ catch {
208
+ msgs.push(`Failed to update: ${mp}`);
209
+ }
210
+ }
211
+ }
212
+ if (recovery.enabledAutoUpdate.length > 0) {
213
+ msgs.push(`Enabled auto-update: ${recovery.enabledAutoUpdate.join(", ")}`);
214
+ }
215
+ if (recovery.removed.length > 0) {
216
+ msgs.push(`Removed stale: ${recovery.removed.join(", ")}`);
217
+ }
218
+ if (msgs.length > 0) {
219
+ setRecoveryReport(msgs.join("\n"));
220
+ }
221
+ })
222
+ .catch(() => { }); // non-fatal
193
223
  repairAllMarketplaces()
194
224
  .then(async () => {
195
225
  dispatch({ type: "HIDE_PROGRESS" });
@@ -199,7 +229,7 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
199
229
  dispatch({ type: "HIDE_PROGRESS" });
200
230
  });
201
231
  }, [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, {})] }));
232
+ return (_jsxs("box", { flexDirection: "column", height: dimensions.terminalHeight, children: [updateInfo?.updateAvailable && _jsx(UpdateBanner, { result: updateInfo }), recoveryReport && (_jsxs("box", { paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { bg: "green", fg: "black", children: _jsx("strong", { children: " RECOVERED " }) }), _jsxs("text", { fg: "green", children: [" ", recoveryReport.split("\n").join(" | ")] })] })), 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
233
  }
204
234
  function AppContent({ onExit }) {
205
235
  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,
@@ -258,6 +261,7 @@ 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);
261
265
 
262
266
  // Auto-refresh marketplaces on startup
263
267
  useEffect(() => {
@@ -272,6 +276,43 @@ function AppContentInner({
272
276
  // Migrate old marketplace names → magus (idempotent), then repair plugin.json files
273
277
  migrateMarketplaceRename().catch(() => {}); // non-blocking, best-effort
274
278
 
279
+ // Recover stale marketplace registry entries (e.g. "directory" → "github")
280
+ recoverMarketplaceSettings()
281
+ .then(async (recovery) => {
282
+ const msgs: string[] = [];
283
+ if (recovery.reregistered.length > 0) {
284
+ msgs.push(
285
+ `Re-registered as GitHub: ${recovery.reregistered.join(", ")}`,
286
+ );
287
+ // Update the marketplace clone now that the source is fixed
288
+ const { updateMarketplace } = await import(
289
+ "../services/claude-cli.js"
290
+ );
291
+ for (const mp of recovery.reregistered) {
292
+ try {
293
+ await updateMarketplace(mp);
294
+ msgs.push(`Updated marketplace: ${mp}`);
295
+ } catch {
296
+ msgs.push(`Failed to update: ${mp}`);
297
+ }
298
+ }
299
+ }
300
+ if (recovery.enabledAutoUpdate.length > 0) {
301
+ msgs.push(
302
+ `Enabled auto-update: ${recovery.enabledAutoUpdate.join(", ")}`,
303
+ );
304
+ }
305
+ if (recovery.removed.length > 0) {
306
+ msgs.push(
307
+ `Removed stale: ${recovery.removed.join(", ")}`,
308
+ );
309
+ }
310
+ if (msgs.length > 0) {
311
+ setRecoveryReport(msgs.join("\n"));
312
+ }
313
+ })
314
+ .catch(() => {}); // non-fatal
315
+
275
316
  repairAllMarketplaces()
276
317
  .then(async () => {
277
318
  dispatch({ type: "HIDE_PROGRESS" });
@@ -285,6 +326,14 @@ function AppContentInner({
285
326
  return (
286
327
  <box flexDirection="column" height={dimensions.terminalHeight}>
287
328
  {updateInfo?.updateAvailable && <UpdateBanner result={updateInfo} />}
329
+ {recoveryReport && (
330
+ <box paddingLeft={1} paddingRight={1}>
331
+ <text bg="green" fg="black">
332
+ <strong> RECOVERED </strong>
333
+ </text>
334
+ <text fg="green"> {recoveryReport.split("\n").join(" | ")}</text>
335
+ </box>
336
+ )}
288
337
  {showDebug && (
289
338
  <box paddingLeft={1} paddingRight={1}>
290
339
  <text fg="#888888">