magus-cli 7.2.0 → 7.3.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.
@@ -996,9 +996,16 @@ export function PluginsScreen() {
996
996
  const handleUpdate = async () => {
997
997
  const item = selectableItems[pluginsState.selectedIndex];
998
998
  if (!item || item.kind !== "plugin") return;
999
- // The row said "update" or "stale — reinstall"; nothing else is work.
999
+ // The row said "update", "stale — reinstall" or "BROKEN"; nothing else is
1000
+ // work for this key. `install` stays out — that is the scope keys' and the
1001
+ // profile's business, and the detail panel never offers U for it.
1000
1002
  const status = statuses.get(item.plugin.id);
1001
- if (status?.action !== "update" && status?.action !== "repair") return;
1003
+ if (
1004
+ status?.action !== "update" &&
1005
+ status?.action !== "repair" &&
1006
+ status?.action !== "stale-load"
1007
+ )
1008
+ return;
1002
1009
 
1003
1010
  const plugin = item.plugin;
1004
1011
  const scope: PluginScope =
@@ -1010,9 +1017,13 @@ export function PluginsScreen() {
1010
1017
  const repairing = status.action === "repair";
1011
1018
 
1012
1019
  modal.loading(
1013
- repairing
1014
- ? `Repairing ${plugin.name}…\nclaude plugin uninstall ${plugin.id} --scope ${scope} && claude plugin install ${plugin.id} --scope ${scope}`
1015
- : `Updating ${plugin.name}…\nclaude plugin install ${plugin.id} --scope ${scope}`,
1020
+ // A broken plugin runs NO claude command at all, so naming one here would
1021
+ // misdescribe the work: the repair rewrites installed_plugins.json rows.
1022
+ status.action === "stale-load"
1023
+ ? `Repairing ${plugin.name}…\nmoving this repository's install records forward to v${status.target ?? "?"}`
1024
+ : repairing
1025
+ ? `Repairing ${plugin.name}…\nclaude plugin uninstall ${plugin.id} --scope ${scope} && claude plugin install ${plugin.id} --scope ${scope}`
1026
+ : `Updating ${plugin.name}…\nclaude plugin install ${plugin.id} --scope ${scope}`,
1016
1027
  );
1017
1028
  const reasons = noMoveReasons();
1018
1029
  try {
@@ -1069,24 +1080,38 @@ export function PluginsScreen() {
1069
1080
  const handleUpdateAll = async () => {
1070
1081
  if (pluginsState.plugins.status !== "success") return;
1071
1082
 
1072
- // Exactly the rows that say "update" or "stale — reinstall", each planned
1073
- // under its real pin. Content-stale plugins are included: they need
1074
- // repairing, and excluding them meant "update all" left the machine with
1075
- // silently outdated files.
1083
+ // Exactly the rows that say "update", "stale — reinstall" or "BROKEN — press
1084
+ // a to fix", each planned under its real pin. Content-stale plugins are
1085
+ // included: they need repairing, and excluding them meant "update all" left
1086
+ // the machine with silently outdated files. Broken (`stale-load`) rows are
1087
+ // included because the row itself now names this key — leaving them out
1088
+ // would make that hint a lie. `install` stays out: a plugin nothing has
1089
+ // installed is `s`'s business, per the note above.
1076
1090
  const items = [...statuses.values()]
1077
- .filter((s) => s.action === "update" || s.action === "repair")
1091
+ .filter(
1092
+ (s) =>
1093
+ s.action === "update" ||
1094
+ s.action === "repair" ||
1095
+ s.action === "stale-load",
1096
+ )
1078
1097
  .sort((a, b) => a.pluginId.localeCompare(b.pluginId));
1079
1098
  if (items.length === 0) return;
1080
1099
 
1081
1100
  const projectPath = state.projectPath || process.cwd();
1082
1101
  const total = items.filter(pluginNeedsWork).length;
1083
1102
 
1103
+ // A broken plugin is repaired in the registry, not updated, and the progress
1104
+ // line is the only place the user sees which one happened to which plugin.
1105
+ const actionOf = new Map(items.map((i) => [i.pluginId, i.action]));
1106
+
1084
1107
  let done = 0;
1085
1108
  const reasons = noMoveReasons();
1086
1109
  const reporter: ApplyReporter = {
1087
1110
  begin: (name) => {
1088
1111
  done++;
1089
- modal.loading(`Updating ${name} (${done}/${total})…`);
1112
+ const verb =
1113
+ actionOf.get(name) === "stale-load" ? "Repairing" : "Updating";
1114
+ modal.loading(`${verb} ${name} (${done}/${total})…`);
1090
1115
  },
1091
1116
  finish: reasons.reporter.finish,
1092
1117
  detach: (work) => work(),
@@ -1348,8 +1373,16 @@ export function PluginsScreen() {
1348
1373
  // "cannot tell rendered as fine" bug.
1349
1374
  const unknownCount = counts.unknown;
1350
1375
  const installCount = counts.install;
1351
- const subtitle = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} updates` : ""}${installCount > 0 ? ` │ ${installCount} to install` : ""}${unknownCount > 0 ? ` │ ${unknownCount} unverified` : ""}`;
1352
- const searchPlaceholder = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} ⬆` : ""}${installCount > 0 ? ` │ ${installCount} +` : ""}${unknownCount > 0 ? ` │ ${unknownCount} ?` : ""} │ / to search`;
1376
+ // Broken: installed at the right version, loading an older one. Its own
1377
+ // number, never folded into `updateCount` or into silence — a clean header
1378
+ // over a session running a version nobody installed is the whole defect.
1379
+ //
1380
+ // "broken", not "loading older". The old wording described the mechanism, so
1381
+ // a reader had to already know what "loads" versus "installed" means here
1382
+ // before the count told them anything. `a` clears these.
1383
+ const brokenCount = counts["stale-load"];
1384
+ const subtitle = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} updates` : ""}${installCount > 0 ? ` │ ${installCount} to install` : ""}${brokenCount > 0 ? ` │ ${brokenCount} broken` : ""}${unknownCount > 0 ? ` │ ${unknownCount} unverified` : ""}`;
1385
+ const searchPlaceholder = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} ⬆` : ""}${installCount > 0 ? ` │ ${installCount} +` : ""}${brokenCount > 0 ? ` │ ${brokenCount} ✗` : ""}${unknownCount > 0 ? ` │ ${unknownCount} ?` : ""} │ / to search`;
1353
1386
 
1354
1387
  const catalogNotice = buildCatalogNotice({
1355
1388
  failures: catalogFailures,