magus-cli 7.11.4 → 7.11.6

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.
@@ -9,14 +9,8 @@ import {
9
9
  type PluginScope,
10
10
  addMarketplace as cliAddMarketplace,
11
11
  repairPlugin as cliRepairPlugin,
12
- isNotInstalledAnswer,
13
- uninstallPluginViaCli,
14
12
  } from "../../services/claude-cli.js";
15
- import {
16
- readInheritedRegistryRows,
17
- readSettings,
18
- setMcpEnvVar,
19
- } from "../../services/claude-settings.js";
13
+ import { readSettings, setMcpEnvVar } from "../../services/claude-settings.js";
20
14
  import { clearContentDriftCache } from "../../services/content-drift.js";
21
15
  import { missingPluginEnvVars } from "../../services/plugin-env-needs.js";
22
16
  import {
@@ -49,6 +43,7 @@ import {
49
43
  applyPlugins,
50
44
  installPluginInScope,
51
45
  pluginNeedsWork,
46
+ removeDeprecatedPlugin,
52
47
  uninstallPluginInScope,
53
48
  updatePluginInScope,
54
49
  } from "../../services/update-engine.js";
@@ -176,6 +171,14 @@ export function PluginsScreen() {
176
171
  const [requiredBy, setRequiredBy] = useState<
177
172
  Readonly<Record<string, readonly string[]>>
178
173
  >({});
174
+ /**
175
+ * Every plugin any profile lists — the same set `magus update` plans with,
176
+ * so a deprecated plugin only another profile keeps is a `remove` row here
177
+ * and a removal there.
178
+ */
179
+ const [listedByProfiles, setListedByProfiles] = useState<readonly string[]>(
180
+ [],
181
+ );
179
182
 
180
183
  // Fetch data (always fetches all scopes). Only the most recently started
181
184
  // load may touch the screen.
@@ -209,6 +212,7 @@ export function PluginsScreen() {
209
212
  setPins(active?.closure.plugins ?? null);
210
213
  setActiveProfileId(active?.profileId ?? null);
211
214
  setRequiredBy(required);
215
+ setListedByProfiles(active?.listedByProfiles ?? []);
212
216
 
213
217
  // Which catalogs answered, and which only appeared to. Read straight
214
218
  // after the fetch, while the record still describes this attempt.
@@ -300,9 +304,14 @@ export function PluginsScreen() {
300
304
  const statuses = useMemo(
301
305
  (): Map<string, PluginUpdateItem> =>
302
306
  shownPlugins
303
- ? classifyPluginRows(shownPlugins, pins ?? {}, requiredBy)
307
+ ? classifyPluginRows(
308
+ shownPlugins,
309
+ pins ?? {},
310
+ requiredBy,
311
+ listedByProfiles,
312
+ )
304
313
  : new Map(),
305
- [shownPlugins, pins, requiredBy],
314
+ [shownPlugins, pins, requiredBy, listedByProfiles],
306
315
  );
307
316
 
308
317
  /**
@@ -1265,39 +1274,16 @@ export function PluginsScreen() {
1265
1274
  return;
1266
1275
  }
1267
1276
 
1268
- // Errors are collected, not dropped: only the CLI's "is not installed in
1269
- // <scope> scope" answer is expected, and only for a scope with no row.
1270
- const problems: unknown[] = [];
1277
+ // An orphan the plan keeps rather than removes — it moved, another plugin
1278
+ // still requires it, or its marketplace was not read — and the user asked
1279
+ // for it gone. The engine uninstalls everywhere, drops it from every
1280
+ // profile and renders the active one again, so the row does not come back.
1271
1281
  try {
1272
- // Remove from all scopes — try all to clean up stale references
1273
- const scopes: PluginScope[] = ["user", "project", "local"];
1274
- for (const scope of scopes) {
1275
- try {
1276
- modal.loading(
1277
- `Removing ${plugin.name} from ${scope}…\nclaude plugin uninstall ${plugin.id} --scope ${scope}`,
1278
- );
1279
- await uninstallPluginViaCli(plugin.id, scope, state.projectPath);
1280
- } catch (error) {
1281
- if (!isNotInstalledAnswer(error)) problems.push(error);
1282
- }
1283
- }
1284
- // A folder of the same repository may hold the install this one only
1285
- // shows: the CLI removes it only when run from that folder.
1286
- const inherited = await readInheritedRegistryRows(
1282
+ modal.loading(`Removing ${plugin.name}…`);
1283
+ const { problems } = await removeDeprecatedPlugin(
1287
1284
  plugin.id,
1288
1285
  state.projectPath || process.cwd(),
1289
1286
  );
1290
- for (const row of inherited) {
1291
- if (!row.projectPath) continue;
1292
- try {
1293
- modal.loading(
1294
- `Removing ${plugin.name} from the repository's main folder, which this folder shares…\nclaude plugin uninstall ${plugin.id} --scope ${row.scope}`,
1295
- );
1296
- await uninstallPluginViaCli(plugin.id, row.scope, row.projectPath);
1297
- } catch (error) {
1298
- problems.push(error);
1299
- }
1300
- }
1301
1287
 
1302
1288
  modal.hideModal();
1303
1289
  fetchData();