magus-cli 7.5.3 → 7.6.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 +4 -4
- package/src/__tests__/blackbox-convergence.test.ts +1 -2
- package/src/__tests__/blackbox-parity.test.ts +16 -14
- package/src/__tests__/blackbox-plugin-state.test.ts +13 -15
- package/src/__tests__/cli-apply-seams.test.ts +3 -3
- package/src/__tests__/cli-profile-prompts.test.ts +1165 -0
- package/src/__tests__/content-drift.test.ts +0 -1
- package/src/__tests__/files-missing-scope.test.ts +235 -0
- package/src/__tests__/{enabled-not-installed.test.ts → files-missing.test.ts} +32 -26
- package/src/__tests__/format-update-outcome.test.ts +367 -0
- package/src/__tests__/installed-version-scope.test.tsx +425 -0
- package/src/__tests__/key-focus-guard.test.tsx +260 -0
- package/src/__tests__/mate-availability.test.ts +11 -13
- package/src/__tests__/plugin-contents.test.ts +0 -1
- package/src/__tests__/plugin-detail-scopes.test.tsx +270 -0
- package/src/__tests__/plugin-row-text.test.ts +18 -21
- package/src/__tests__/profile-command.test.ts +2 -2
- package/src/__tests__/profile-migration-offer.test.ts +5 -4
- package/src/__tests__/profile-name-on-save.test.ts +443 -0
- package/src/__tests__/profile-skills.test.ts +3 -3
- package/src/__tests__/profile-startup-nested.test.ts +329 -0
- package/src/__tests__/profile-startup-old-format.test.ts +390 -0
- package/src/__tests__/profile-startup.test.ts +1 -1
- package/src/__tests__/profile-state.test.ts +27 -22
- package/src/__tests__/prompt-eof.test.ts +245 -0
- package/src/__tests__/scope-action.test.ts +35 -38
- package/src/__tests__/scope-row-lookup.test.ts +126 -0
- package/src/__tests__/select-restore.test.ts +209 -0
- package/src/__tests__/stale-load-repair.test.ts +1 -2
- package/src/__tests__/update-apply.test.ts +18 -2
- package/src/__tests__/update-null-readback.test.ts +300 -0
- package/src/__tests__/update-plan-parity.test.ts +249 -0
- package/src/__tests__/update-plan.test.ts +66 -67
- package/src/__tests__/version-unknown-state.test.ts +38 -39
- package/src/cli/bootstrap.ts +271 -66
- package/src/cli/doctor.ts +4 -3
- package/src/cli/install.ts +83 -27
- package/src/cli/prompt.ts +19 -5
- package/src/cli/router.ts +1 -0
- package/src/cli/select.ts +168 -0
- package/src/cli/update-view.ts +120 -6
- package/src/cli/update.ts +6 -4
- package/src/services/claude-settings.ts +28 -0
- package/src/services/manifest.ts +31 -0
- package/src/services/mate-availability.ts +23 -29
- package/src/services/plugin-manager.ts +247 -105
- package/src/services/profile-absorb.ts +10 -7
- package/src/services/profile-activate.ts +41 -19
- package/src/services/profile-adopt.ts +9 -2
- package/src/services/profile-startup.ts +206 -5
- package/src/services/profile-state.ts +8 -7
- package/src/services/tracked-live-files.ts +24 -3
- package/src/services/update-engine.ts +28 -10
- package/src/services/update-plan.ts +78 -94
- package/src/ui/App.tsx +96 -56
- package/src/ui/adapters/pluginsAdapter.ts +1 -1
- package/src/ui/components/modals/ModalContainer.tsx +3 -0
- package/src/ui/components/primitives/ScopeSquares.tsx +10 -5
- package/src/ui/hooks/useGitignoreModal.ts +21 -3
- package/src/ui/hooks/useProfileStartupModal.ts +214 -15
- package/src/ui/hooks/useStaleLoadModal.ts +5 -1
- package/src/ui/input/KeyRouter.tsx +43 -2
- package/src/ui/keymap.ts +1 -1
- package/src/ui/renderers/pluginRenderers.tsx +39 -45
- package/src/ui/renderers/pluginRowText.ts +6 -2
- package/src/ui/renderers/pluginStatusText.ts +4 -3
- package/src/ui/screens/PluginsScreen.tsx +28 -41
- package/src/cli/tui-prompt.tsx +0 -143
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magus-cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.6.0",
|
|
4
4
|
"description": "TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.tsx",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"typescript": "^5.6.3"
|
|
66
66
|
},
|
|
67
67
|
"optionalDependencies": {
|
|
68
|
-
"magus-cli-darwin-arm64": "7.
|
|
69
|
-
"magus-cli-darwin-x64": "7.
|
|
70
|
-
"magus-cli-linux-x64": "7.
|
|
68
|
+
"magus-cli-darwin-arm64": "7.6.0",
|
|
69
|
+
"magus-cli-darwin-x64": "7.6.0",
|
|
70
|
+
"magus-cli-linux-x64": "7.6.0"
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -220,9 +220,8 @@ function designer(installed: string): PluginInfo {
|
|
|
220
220
|
description: "",
|
|
221
221
|
marketplace: "magus",
|
|
222
222
|
marketplaceDisplay: "magus",
|
|
223
|
-
enabled: true,
|
|
224
223
|
installedVersion: installed,
|
|
225
|
-
projectScope: {
|
|
224
|
+
projectScope: { installed: true, version: installed },
|
|
226
225
|
};
|
|
227
226
|
}
|
|
228
227
|
|
|
@@ -96,11 +96,10 @@ function row(
|
|
|
96
96
|
description: "",
|
|
97
97
|
marketplace: mp,
|
|
98
98
|
marketplaceDisplay: mp,
|
|
99
|
-
enabled: installed !== null,
|
|
100
99
|
...(installed !== null
|
|
101
100
|
? {
|
|
102
101
|
installedVersion: installed,
|
|
103
|
-
projectScope: {
|
|
102
|
+
projectScope: { installed: true, version: installed },
|
|
104
103
|
}
|
|
105
104
|
: {}),
|
|
106
105
|
...extra,
|
|
@@ -141,9 +140,8 @@ function makeCatalog(): PluginInfo[] {
|
|
|
141
140
|
row("pinned-undeliverable@magus", "3.0.0", "1.0.0"),
|
|
142
141
|
// _synthetic TUI-only rows
|
|
143
142
|
row("installed-undeclared@magus", "1.0.0", "1.0.0"),
|
|
144
|
-
row("
|
|
145
|
-
|
|
146
|
-
userScope: { enabled: true },
|
|
143
|
+
row("files-missing@magus", "1.0.0", null, {
|
|
144
|
+
userScope: { installed: true },
|
|
147
145
|
}),
|
|
148
146
|
row("browse-only@magus", "1.0.0", null),
|
|
149
147
|
];
|
|
@@ -541,23 +539,25 @@ describe("R7 parity: one fixture through the CLI path and the TUI path", () => {
|
|
|
541
539
|
expect(p.tuiRows.has("gone@magus")).toBe(false);
|
|
542
540
|
});
|
|
543
541
|
|
|
544
|
-
test("TUI-only rows: installed
|
|
542
|
+
test("TUI-only rows: installed rows, files missing included, get a status, browse-only rows get none", async () => {
|
|
545
543
|
const p = await runBothPaths();
|
|
546
544
|
|
|
547
545
|
expect(p.tuiRows.get("installed-undeclared@magus")?.status?.action).toBe(
|
|
548
546
|
"current",
|
|
549
547
|
);
|
|
550
|
-
const
|
|
548
|
+
const filesMissing = p.tuiRows.get("files-missing@magus")?.status;
|
|
551
549
|
expect({
|
|
552
|
-
action:
|
|
553
|
-
reason:
|
|
550
|
+
action: filesMissing?.action,
|
|
551
|
+
reason: filesMissing?.reason,
|
|
554
552
|
}).toEqual({
|
|
555
553
|
action: "install",
|
|
556
|
-
reason: "
|
|
554
|
+
reason: "files-missing",
|
|
557
555
|
});
|
|
558
|
-
const
|
|
559
|
-
if (!
|
|
560
|
-
expect(pluginRowText(
|
|
556
|
+
const filesMissingRow = p.tuiRows.get("files-missing@magus");
|
|
557
|
+
if (!filesMissingRow) throw new Error("no files-missing row");
|
|
558
|
+
expect(pluginRowText(filesMissingRow).text).not.toContain(
|
|
559
|
+
"profile declares it",
|
|
560
|
+
);
|
|
561
561
|
expect(p.tuiRows.get("browse-only@magus")?.status).toBeNull();
|
|
562
562
|
expect(p.tuiRows.get("seo@magus-marketing")?.status).toBeNull();
|
|
563
563
|
});
|
|
@@ -573,7 +573,9 @@ describe("R7 parity: one fixture through the CLI path and the TUI path", () => {
|
|
|
573
573
|
|
|
574
574
|
const cliCounts = countPluginStates(cliItems);
|
|
575
575
|
expect(countPluginStates(tuiItems)).toEqual(cliCounts);
|
|
576
|
-
|
|
576
|
+
// bunjs (declared, not installed) and files-missing (installed, no files).
|
|
577
|
+
// The second was TUI-only until the CLI planned every installed plugin too.
|
|
578
|
+
expect(cliCounts.install).toBe(2);
|
|
577
579
|
expect(cliCounts.repair).toBe(1); // madbench
|
|
578
580
|
// Was 10. Seven of those rows are `current` now, and each for the same
|
|
579
581
|
// reason: nothing could be compared, so nothing is known to be behind.
|
|
@@ -35,11 +35,10 @@ function info(
|
|
|
35
35
|
description: "",
|
|
36
36
|
marketplace: at >= 0 ? id.slice(at + 1) : "",
|
|
37
37
|
marketplaceDisplay: at >= 0 ? id.slice(at + 1) : "",
|
|
38
|
-
enabled: installed !== null,
|
|
39
38
|
...(installed !== null
|
|
40
39
|
? {
|
|
41
40
|
installedVersion: installed,
|
|
42
|
-
projectScope: {
|
|
41
|
+
projectScope: { installed: true, version: installed },
|
|
43
42
|
}
|
|
44
43
|
: {}),
|
|
45
44
|
...extra,
|
|
@@ -273,23 +272,22 @@ describe("planPluginUpdates is classifyPlugin over every pin, sorted", () => {
|
|
|
273
272
|
describe("pinForRow / classifyPluginRows", () => {
|
|
274
273
|
const declared = info("dev@magus", "1.0.0", "1.0.0");
|
|
275
274
|
const installedOnly = info("extra@magus", "1.0.0", "1.0.0");
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
userScope: { enabled: true },
|
|
275
|
+
const filesMissing = info("en@magus", "1.0.0", null, {
|
|
276
|
+
userScope: { installed: true },
|
|
279
277
|
});
|
|
280
278
|
const browseOnly = info("browse@magus", "1.0.0", null);
|
|
281
279
|
const pins = { "dev@magus": "1.0.0" };
|
|
282
280
|
|
|
283
|
-
test("closure pin wins; installed
|
|
281
|
+
test("closure pin wins; installed rows, files missing included, are judged against latest; browse-only has no pin", () => {
|
|
284
282
|
expect(pinForRow(declared, pins)).toBe("1.0.0");
|
|
285
283
|
expect(pinForRow(installedOnly, pins)).toBe("latest");
|
|
286
|
-
expect(pinForRow(
|
|
284
|
+
expect(pinForRow(filesMissing, pins)).toBe("latest");
|
|
287
285
|
expect(pinForRow(browseOnly, pins)).toBeNull();
|
|
288
286
|
});
|
|
289
287
|
|
|
290
288
|
test("classifyPluginRows has one status per pinned row and none for browse-only rows", () => {
|
|
291
289
|
const rows = classifyPluginRows(
|
|
292
|
-
[declared, installedOnly,
|
|
290
|
+
[declared, installedOnly, filesMissing, browseOnly],
|
|
293
291
|
pins,
|
|
294
292
|
);
|
|
295
293
|
|
|
@@ -299,7 +297,7 @@ describe("pinForRow / classifyPluginRows", () => {
|
|
|
299
297
|
"extra@magus",
|
|
300
298
|
]);
|
|
301
299
|
expect(rows.get("en@magus")?.action).toBe("install");
|
|
302
|
-
expect(rows.get("en@magus")?.reason).toBe("
|
|
300
|
+
expect(rows.get("en@magus")?.reason).toBe("files-missing");
|
|
303
301
|
});
|
|
304
302
|
});
|
|
305
303
|
|
|
@@ -450,22 +448,22 @@ describe("pluginStatusText — the TUI suffix contract (§8.7)", () => {
|
|
|
450
448
|
).toBe(" stale — reinstall (files moved)");
|
|
451
449
|
});
|
|
452
450
|
|
|
453
|
-
test("install: absent claims the profile declares it;
|
|
451
|
+
test("install: absent claims the profile declares it; files-missing shows its note instead", () => {
|
|
454
452
|
expect(
|
|
455
453
|
pluginStatusText(
|
|
456
454
|
item({ action: "install", reason: "absent", installed: null }),
|
|
457
455
|
).suffix,
|
|
458
456
|
).toBe(" not installed — profile declares it");
|
|
459
|
-
const
|
|
457
|
+
const filesMissing = pluginStatusText(
|
|
460
458
|
item({
|
|
461
459
|
action: "install",
|
|
462
|
-
reason: "
|
|
460
|
+
reason: "files-missing",
|
|
463
461
|
installed: null,
|
|
464
|
-
note: "
|
|
462
|
+
note: "files missing",
|
|
465
463
|
}),
|
|
466
464
|
).suffix;
|
|
467
|
-
expect(
|
|
468
|
-
expect(
|
|
465
|
+
expect(filesMissing).toBe(" files missing");
|
|
466
|
+
expect(filesMissing).not.toContain("profile declares");
|
|
469
467
|
});
|
|
470
468
|
|
|
471
469
|
test("current: empty, or ' (<note>)' for pin-undeliverable", () => {
|
|
@@ -115,7 +115,7 @@ describe("the apply meter's denominator", () => {
|
|
|
115
115
|
item({ pluginId: "b", action: "unknown" }),
|
|
116
116
|
item({ pluginId: "c", action: "update", scopes: ["project"] }),
|
|
117
117
|
item({ pluginId: "d", action: "update", scopes: [] }),
|
|
118
|
-
item({ pluginId: "e", action: "install", scopes: [] }),
|
|
118
|
+
item({ pluginId: "e", action: "install", reason: "absent", scopes: [] }),
|
|
119
119
|
item({ pluginId: "f", action: "repair", scopes: ["user", "project"] }),
|
|
120
120
|
];
|
|
121
121
|
|
|
@@ -128,8 +128,8 @@ describe("the apply meter's denominator", () => {
|
|
|
128
128
|
.map((e) => e.slice("begin:".length));
|
|
129
129
|
|
|
130
130
|
expect(actual).toEqual(predicted);
|
|
131
|
-
// And specifically: an install with no scopes IS work (it goes
|
|
132
|
-
// project scope), while an update with no scopes is not.
|
|
131
|
+
// And specifically: an `absent` install with no scopes IS work (it goes
|
|
132
|
+
// in at project scope), while an update with no scopes is not.
|
|
133
133
|
expect(predicted).toEqual(["c", "e", "f"]);
|
|
134
134
|
});
|
|
135
135
|
|