magus-cli 7.5.3 → 7.5.4

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.
Files changed (59) hide show
  1. package/package.json +4 -4
  2. package/src/__tests__/blackbox-convergence.test.ts +1 -2
  3. package/src/__tests__/blackbox-parity.test.ts +16 -14
  4. package/src/__tests__/blackbox-plugin-state.test.ts +13 -15
  5. package/src/__tests__/cli-apply-seams.test.ts +3 -3
  6. package/src/__tests__/cli-profile-prompts.test.ts +1165 -0
  7. package/src/__tests__/content-drift.test.ts +0 -1
  8. package/src/__tests__/files-missing-scope.test.ts +235 -0
  9. package/src/__tests__/{enabled-not-installed.test.ts → files-missing.test.ts} +32 -26
  10. package/src/__tests__/format-update-outcome.test.ts +367 -0
  11. package/src/__tests__/installed-version-scope.test.tsx +425 -0
  12. package/src/__tests__/mate-availability.test.ts +11 -13
  13. package/src/__tests__/plugin-contents.test.ts +0 -1
  14. package/src/__tests__/plugin-detail-scopes.test.tsx +270 -0
  15. package/src/__tests__/plugin-row-text.test.ts +18 -21
  16. package/src/__tests__/profile-command.test.ts +2 -2
  17. package/src/__tests__/profile-migration-offer.test.ts +5 -4
  18. package/src/__tests__/profile-skills.test.ts +3 -3
  19. package/src/__tests__/profile-startup-nested.test.ts +329 -0
  20. package/src/__tests__/profile-startup-old-format.test.ts +390 -0
  21. package/src/__tests__/profile-state.test.ts +27 -22
  22. package/src/__tests__/scope-action.test.ts +35 -38
  23. package/src/__tests__/scope-row-lookup.test.ts +126 -0
  24. package/src/__tests__/select-restore.test.ts +209 -0
  25. package/src/__tests__/stale-load-repair.test.ts +1 -2
  26. package/src/__tests__/update-apply.test.ts +18 -2
  27. package/src/__tests__/update-null-readback.test.ts +300 -0
  28. package/src/__tests__/update-plan-parity.test.ts +249 -0
  29. package/src/__tests__/update-plan.test.ts +66 -67
  30. package/src/__tests__/version-unknown-state.test.ts +38 -39
  31. package/src/cli/bootstrap.ts +239 -66
  32. package/src/cli/doctor.ts +4 -3
  33. package/src/cli/install.ts +83 -27
  34. package/src/cli/prompt.ts +7 -3
  35. package/src/cli/router.ts +1 -0
  36. package/src/cli/select.ts +168 -0
  37. package/src/cli/update-view.ts +118 -6
  38. package/src/cli/update.ts +6 -4
  39. package/src/services/claude-settings.ts +28 -0
  40. package/src/services/manifest.ts +31 -0
  41. package/src/services/mate-availability.ts +23 -29
  42. package/src/services/plugin-manager.ts +247 -105
  43. package/src/services/profile-absorb.ts +10 -7
  44. package/src/services/profile-activate.ts +39 -18
  45. package/src/services/profile-startup.ts +160 -4
  46. package/src/services/profile-state.ts +7 -6
  47. package/src/services/tracked-live-files.ts +24 -3
  48. package/src/services/update-engine.ts +28 -10
  49. package/src/services/update-plan.ts +78 -94
  50. package/src/ui/App.tsx +7 -2
  51. package/src/ui/adapters/pluginsAdapter.ts +1 -1
  52. package/src/ui/components/primitives/ScopeSquares.tsx +10 -5
  53. package/src/ui/hooks/useProfileStartupModal.ts +87 -7
  54. package/src/ui/keymap.ts +1 -1
  55. package/src/ui/renderers/pluginRenderers.tsx +39 -45
  56. package/src/ui/renderers/pluginRowText.ts +6 -2
  57. package/src/ui/renderers/pluginStatusText.ts +4 -3
  58. package/src/ui/screens/PluginsScreen.tsx +28 -41
  59. package/src/cli/tui-prompt.tsx +0 -143
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Spec 7.5.4 v3 — REQ-4: at each scope a plugin is installed or not, and the list row
3
+ * and the detail pane say the same thing.
4
+ *
5
+ * Written blind: from the spec and the exported signatures and interfaces only. Both
6
+ * surfaces are rendered into a character frame and read the way a user reads them.
7
+ *
8
+ * The item's `status` is `null` throughout: `PluginUpdateItem` is not part of the
9
+ * contract this test was given, and the spec derives every scope's display from its
10
+ * `ScopeStatus` alone.
11
+ */
12
+ import { describe, expect, test } from "bun:test";
13
+ import { testRender } from "@opentui/react/test-utils";
14
+ import { type ReactNode, act } from "react";
15
+ import type { PluginInfo, ScopeStatus } from "../services/plugin-manager.js";
16
+ import type { PluginPluginItem } from "../ui/adapters/pluginsAdapter.js";
17
+ import {
18
+ renderPluginDetail,
19
+ renderPluginRow,
20
+ } from "../ui/renderers/pluginRenderers.js";
21
+
22
+ /** Newer than any scope's version, so an "update available" claim would be plausible. */
23
+ const CATALOG_VERSION = "0.9.0";
24
+
25
+ interface Scopes {
26
+ user?: ScopeStatus;
27
+ project?: ScopeStatus;
28
+ local?: ScopeStatus;
29
+ }
30
+
31
+ const SCOPE_NAMES = ["User", "Project", "Local"] as const;
32
+ type ScopeName = (typeof SCOPE_NAMES)[number];
33
+ const CHIP: Record<ScopeName, string> = { User: "u", Project: "p", Local: "l" };
34
+
35
+ function pluginItem(scopes: Scopes): PluginPluginItem {
36
+ // The version that loads here: undefined unless some scope has a real install.
37
+ const loaded = [scopes.user, scopes.project, scopes.local].find(
38
+ (s) => s?.installed === true && s.version !== undefined,
39
+ );
40
+ const plugin: PluginInfo = {
41
+ id: "scopeprobe@magus",
42
+ name: "scopeprobe",
43
+ version: CATALOG_VERSION,
44
+ description: "Probe plugin for scope rendering.",
45
+ marketplace: "magus",
46
+ marketplaceDisplay: "Magus",
47
+ installedVersion: loaded?.version,
48
+ userScope: scopes.user,
49
+ projectScope: scopes.project,
50
+ localScope: scopes.local,
51
+ };
52
+ return {
53
+ id: `plugin:${plugin.id}`,
54
+ kind: "plugin",
55
+ label: plugin.name,
56
+ plugin,
57
+ isExpandable: false,
58
+ isExpanded: false,
59
+ status: null,
60
+ };
61
+ }
62
+
63
+ async function frameOf(
64
+ node: ReactNode,
65
+ width: number,
66
+ height: number,
67
+ ): Promise<string[]> {
68
+ const { captureCharFrame, renderOnce, renderer } = await testRender(
69
+ <box flexDirection="column">{node}</box>,
70
+ { width, height },
71
+ );
72
+ await act(async () => {
73
+ await renderOnce();
74
+ await renderOnce();
75
+ });
76
+ const lines = captureCharFrame().split("\n");
77
+ await act(async () => renderer.destroy());
78
+ return lines;
79
+ }
80
+
81
+ function detailFrame(item: PluginPluginItem): Promise<string[]> {
82
+ return frameOf(renderPluginDetail(item, new Set<string>()), 120, 80);
83
+ }
84
+
85
+ function rowFrame(
86
+ item: PluginPluginItem,
87
+ isSelected: boolean,
88
+ ): Promise<string[]> {
89
+ return frameOf(renderPluginRow(item, 0, isSelected), 120, 3);
90
+ }
91
+
92
+ /** The `User` / `Project` / `Local` line under `Scopes:`. */
93
+ function scopeLine(frame: string[], name: ScopeName): string {
94
+ const header = frame.findIndex((l) => l.includes("Scopes:"));
95
+ if (header === -1) {
96
+ throw new Error(
97
+ `no "Scopes:" section in the detail pane:\n${frame.join("\n")}`,
98
+ );
99
+ }
100
+ const pattern = new RegExp(`\\b${name}\\b`);
101
+ const line = frame.slice(header + 1).find((l) => pattern.test(l));
102
+ if (line === undefined) {
103
+ throw new Error(`no ${name} line under "Scopes:":\n${frame.join("\n")}`);
104
+ }
105
+ return line;
106
+ }
107
+
108
+ function dotOf(line: string): string | undefined {
109
+ return /[●○]/.exec(line)?.[0];
110
+ }
111
+
112
+ const V = "0.8.1";
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Detail pane, one scope at a time (REQ-4a..d on the Project line)
116
+ // ---------------------------------------------------------------------------
117
+
118
+ describe("REQ-4 the detail pane's Project line", () => {
119
+ test("REQ-4a installed with a version: ● and v0.8.1, not files missing", async () => {
120
+ const frame = await detailFrame(
121
+ pluginItem({ project: { installed: true, version: V } }),
122
+ );
123
+
124
+ const line = scopeLine(frame, "Project");
125
+ expect(dotOf(line)).toBe("●");
126
+ expect(line).toContain(`v${V}`);
127
+ expect(line).not.toContain("files missing");
128
+ });
129
+
130
+ test("REQ-4b not installed but a version recorded: ○, no 0.8.1, and never the word installed", async () => {
131
+ const frame = await detailFrame(
132
+ pluginItem({ project: { installed: false, version: V } }),
133
+ );
134
+
135
+ const line = scopeLine(frame, "Project");
136
+ expect(dotOf(line)).toBe("○");
137
+ expect(line).not.toContain(V);
138
+ expect(line).not.toMatch(/\binstalled\b/i);
139
+ });
140
+
141
+ test("REQ-4c installed with no version: ● and files missing", async () => {
142
+ const frame = await detailFrame(
143
+ pluginItem({ project: { installed: true } }),
144
+ );
145
+
146
+ const line = scopeLine(frame, "Project");
147
+ expect(dotOf(line)).toBe("●");
148
+ expect(line).toContain("files missing");
149
+ });
150
+
151
+ test("REQ-4d { installed: false } with no version: ○ and none of version, files missing, installed", async () => {
152
+ const frame = await detailFrame(
153
+ pluginItem({ project: { installed: false } }),
154
+ );
155
+
156
+ const line = scopeLine(frame, "Project");
157
+ expect(dotOf(line)).toBe("○");
158
+ expect(line).not.toContain(V);
159
+ expect(line).not.toContain("files missing");
160
+ expect(line).not.toMatch(/\binstalled\b/i);
161
+ });
162
+
163
+ test("REQ-4d an undefined scope: ○ and none of version, files missing, installed", async () => {
164
+ const frame = await detailFrame(pluginItem({}));
165
+
166
+ const line = scopeLine(frame, "Project");
167
+ expect(dotOf(line)).toBe("○");
168
+ expect(line).not.toContain(V);
169
+ expect(line).not.toContain("files missing");
170
+ expect(line).not.toMatch(/\binstalled\b/i);
171
+ });
172
+ });
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // Structure and vocabulary of every scope line (REQ-4 layout, REQ-4e)
176
+ // ---------------------------------------------------------------------------
177
+
178
+ const STATES: Array<[label: string, status: ScopeStatus | undefined]> = [
179
+ ["installed with version", { installed: true, version: V }],
180
+ ["not installed, version recorded", { installed: false, version: V }],
181
+ ["installed, no version", { installed: true }],
182
+ ["not installed, no version", { installed: false }],
183
+ ["undefined", undefined],
184
+ ];
185
+
186
+ describe("REQ-4 every scope line is chip, dot, name — and never says enabled", () => {
187
+ for (const [label, status] of STATES) {
188
+ test(`REQ-4 / REQ-4e scope lines for "${label}" at every scope`, async () => {
189
+ for (const scope of SCOPE_NAMES) {
190
+ const scopes: Scopes = {};
191
+ if (scope === "User") scopes.user = status;
192
+ if (scope === "Project") scopes.project = status;
193
+ if (scope === "Local") scopes.local = status;
194
+
195
+ const frame = await detailFrame(pluginItem(scopes));
196
+
197
+ for (const name of SCOPE_NAMES) {
198
+ const line = scopeLine(frame, name);
199
+ const layout = new RegExp(
200
+ `(?:^|[^A-Za-z0-9])${CHIP[name]}[^A-Za-z0-9●○]*[●○]\\s*${name}\\b`,
201
+ );
202
+ expect({ scope, name, line, layout: layout.test(line) }).toEqual({
203
+ scope,
204
+ name,
205
+ line,
206
+ layout: true,
207
+ });
208
+ expect({ scope, name, line, enabled: /enabled/i.test(line) }).toEqual(
209
+ { scope, name, line, enabled: false },
210
+ );
211
+ }
212
+ }
213
+ });
214
+ }
215
+ });
216
+
217
+ // ---------------------------------------------------------------------------
218
+ // The list row agrees with the detail pane (REQ-4f)
219
+ // ---------------------------------------------------------------------------
220
+
221
+ describe("REQ-4f the list row's squares equal the detail pane's dots", () => {
222
+ const combos: Array<[boolean, boolean, boolean]> = [];
223
+ for (const u of [false, true]) {
224
+ for (const p of [false, true]) {
225
+ for (const l of [false, true]) combos.push([u, p, l]);
226
+ }
227
+ }
228
+
229
+ for (const isSelected of [false, true]) {
230
+ for (const [u, p, l] of combos) {
231
+ const name = `user=${u} project=${p} local=${l}`;
232
+ test(`REQ-4f ${name}, isSelected=${isSelected}`, async () => {
233
+ const item = pluginItem({
234
+ user: { installed: u, version: V },
235
+ project: { installed: p, version: V },
236
+ local: { installed: l, version: V },
237
+ });
238
+ const expectedSquares = [u, p, l].map((on) => (on ? "■" : "□"));
239
+ const expectedDots = [u, p, l].map((on) => (on ? "●" : "○"));
240
+
241
+ const row = await rowFrame(item, isSelected);
242
+ const detail = await detailFrame(item);
243
+
244
+ const squares = row.join("\n").match(/[■□]/g) ?? [];
245
+ const dots = SCOPE_NAMES.map((scope) =>
246
+ dotOf(scopeLine(detail, scope)),
247
+ );
248
+ expect(squares).toEqual(expectedSquares);
249
+ expect(dots).toEqual(expectedDots);
250
+ expect(squares.map((s) => (s === "■" ? "●" : "○"))).toEqual(dots);
251
+ });
252
+ }
253
+ }
254
+ });
255
+
256
+ // ---------------------------------------------------------------------------
257
+ // No phantom version claim (REQ-4g)
258
+ // ---------------------------------------------------------------------------
259
+
260
+ describe("REQ-4g a recorded-but-not-installed version is not presented as installed", () => {
261
+ test("REQ-4g projectScope { installed: false, version: 0.8.1 } alone: no UPDATE AVAILABLE, no 0.8.1 installed", async () => {
262
+ const item = pluginItem({ project: { installed: false, version: V } });
263
+ expect(item.plugin.installedVersion).toBeUndefined();
264
+
265
+ const frame = (await detailFrame(item)).join("\n");
266
+
267
+ expect(frame).not.toMatch(/update available/i);
268
+ expect(frame).not.toContain(`${V} installed`);
269
+ });
270
+ });
@@ -47,7 +47,6 @@ function info(over: Partial<PluginInfo> & { id: string }): PluginInfo {
47
47
  description: "",
48
48
  marketplace,
49
49
  marketplaceDisplay: "",
50
- enabled: true,
51
50
  author: { name: "Anthropic" },
52
51
  ...over,
53
52
  } as PluginInfo;
@@ -58,7 +57,7 @@ const CODE_REVIEW = info({
58
57
  id: "code-review@claude-plugins-official",
59
58
  version: null, // the official catalog publishes no version for it
60
59
  installedVersion: "3ea32df27be7",
61
- projectScope: { enabled: true, version: "3ea32df27be7" },
60
+ projectScope: { installed: true, version: "3ea32df27be7" },
62
61
  });
63
62
 
64
63
  /** A registry "0.0.0" — the value the old row hid every status behind. */
@@ -66,7 +65,7 @@ const ZERO = info({
66
65
  id: "zero-version@claude-plugins-official",
67
66
  version: null,
68
67
  installedVersion: "0.0.0",
69
- projectScope: { enabled: true, version: "0.0.0" },
68
+ projectScope: { installed: true, version: "0.0.0" },
70
69
  });
71
70
 
72
71
  /** Other listed ids, so each marketplace demonstrably was read. */
@@ -147,7 +146,7 @@ describe("pluginRowText — orphans say what the CLI says (F9)", () => {
147
146
  version: "1.8.0",
148
147
  installedVersion: "1.8.0",
149
148
  isOrphaned: true,
150
- projectScope: { enabled: true, version: "1.8.0" },
149
+ projectScope: { installed: true, version: "1.8.0" },
151
150
  });
152
151
  const row = rows([seo]).get(seo.id) as PluginPluginItem;
153
152
  expect(cliNote([seo], seo.id)).toBe(
@@ -165,7 +164,7 @@ describe("pluginRowText — orphans say what the CLI says (F9)", () => {
165
164
  version: "2.0.0",
166
165
  installedVersion: "2.0.0",
167
166
  isOrphaned: true,
168
- projectScope: { enabled: true, version: "2.0.0" },
167
+ projectScope: { installed: true, version: "2.0.0" },
169
168
  });
170
169
  const row = rows([gone]).get(gone.id) as PluginPluginItem;
171
170
  expect(pluginRowText(row).text).toBe(
@@ -181,7 +180,7 @@ describe("pluginRowText — orphans say what the CLI says (F9)", () => {
181
180
  id: "seo@magus",
182
181
  installedVersion: "1.8.0",
183
182
  isOrphaned: true,
184
- projectScope: { enabled: true, version: "1.8.0" },
183
+ projectScope: { installed: true, version: "1.8.0" },
185
184
  });
186
185
  const { text } = pluginRowText(rows([seo]).get(seo.id) as PluginPluginItem);
187
186
  expect(text).not.toContain("deprecated");
@@ -195,7 +194,7 @@ describe("pluginRowText — history badges combine with the state", () => {
195
194
  id: "terminal@magus",
196
195
  version: "3.3.1",
197
196
  installedVersion: "3.0.1",
198
- projectScope: { enabled: true, version: "3.0.1" },
197
+ projectScope: { installed: true, version: "3.0.1" },
199
198
  author: { name: "MadAppGang" },
200
199
  ...over,
201
200
  });
@@ -220,7 +219,7 @@ describe("pluginRowText — history badges combine with the state", () => {
220
219
  recentlyInstalled: true,
221
220
  version: "0.0.0",
222
221
  installedVersion: "0.0.0",
223
- projectScope: { enabled: true, version: "0.0.0" },
222
+ projectScope: { installed: true, version: "0.0.0" },
224
223
  });
225
224
  const row = rows([p]).get(p.id) as PluginPluginItem;
226
225
  expect(pluginRowText(row).text).toBe(" new");
@@ -263,7 +262,7 @@ describe("pluginRowText — rows with no state claim none", () => {
263
262
  });
264
263
  });
265
264
 
266
- describe("pluginStatusText — absent is not enabled-not-installed (LOW 6)", () => {
265
+ describe("pluginStatusText — absent is not files-missing (LOW 6)", () => {
267
266
  const status = (over: Partial<PluginUpdateItem>): PluginUpdateItem => ({
268
267
  pluginId: "x@magus",
269
268
  pinned: "latest",
@@ -285,32 +284,30 @@ describe("pluginStatusText — absent is not enabled-not-installed (LOW 6)", ()
285
284
  });
286
285
  });
287
286
 
288
- test("enabled-not-installed shows its note and never claims a declaration", () => {
287
+ test("files-missing shows its note and never claims a declaration", () => {
289
288
  const got = pluginStatusText(
290
289
  status({
291
- reason: "enabled-not-installed",
292
- note: "enabled in settings, but no files on disk",
290
+ reason: "files-missing",
291
+ note: "files missing",
293
292
  }),
294
293
  );
295
294
  expect(got).toEqual({
296
- suffix: " not installed (enabled in settings, but no files on disk)",
295
+ suffix: " files missing",
297
296
  tone: "danger",
298
297
  });
299
298
  expect(got.suffix).not.toContain("profile");
300
299
  });
301
300
 
302
- test("an undeclared enabled-but-not-installed ROW gets the enabled text", () => {
301
+ test("an undeclared files-missing ROW gets the files-missing text", () => {
303
302
  const broken = info({
304
303
  id: "kanban@magus",
305
304
  version: "1.6.2",
306
305
  author: { name: "MadAppGang" },
307
- localScope: { enabled: true },
306
+ localScope: { installed: true },
308
307
  });
309
308
  const row = rows([broken]).get(broken.id) as PluginPluginItem;
310
- expect(row.status?.reason).toBe("enabled-not-installed");
311
- expect(pluginRowText(row).text).toBe(
312
- " not installed (enabled in settings, but no files on disk)",
313
- );
309
+ expect(row.status?.reason).toBe("files-missing");
310
+ expect(pluginRowText(row).text).toBe(" files missing");
314
311
  });
315
312
 
316
313
  test("null status says nothing", () => {
@@ -357,7 +354,7 @@ describe("pluginRowText — a broken plugin says so, and names the key", () => {
357
354
  version: "0.5.0",
358
355
  installedVersion: "0.5.0",
359
356
  loadedVersion: "0.3.0",
360
- projectScope: { enabled: true, version: "0.5.0" },
357
+ projectScope: { installed: true, version: "0.5.0" },
361
358
  author: { name: "MadAppGang" },
362
359
  });
363
360
 
@@ -399,7 +396,7 @@ describe("pluginRowText — a broken plugin says so, and names the key", () => {
399
396
  version: "0.5.0",
400
397
  installedVersion: "0.5.0",
401
398
  loadedVersion: "0.5.0",
402
- projectScope: { enabled: true, version: "0.5.0" },
399
+ projectScope: { installed: true, version: "0.5.0" },
403
400
  author: { name: "MadAppGang" },
404
401
  });
405
402
  const row = rows([healthy]).get("madbench@magus");
@@ -136,7 +136,7 @@ describe("profile switch", () => {
136
136
  expect((await fs.readJson(live)).enabledPlugins["terminal@magus"]).toBe(
137
137
  true,
138
138
  );
139
- expect(logs.some((l) => l.includes("kept at"))).toBe(false);
139
+ expect(logs.some((l) => l.includes("Backed up"))).toBe(false);
140
140
  expect(await fs.pathExists(join(dir, ".claude", ".magus-backups"))).toBe(
141
141
  false,
142
142
  );
@@ -147,7 +147,7 @@ describe("profile switch", () => {
147
147
  await switchProfile("frontend", dir);
148
148
  await switchProfile("backend", dir);
149
149
  await switchProfile("frontend", dir);
150
- expect(logs.some((l) => l.includes("kept at"))).toBe(false);
150
+ expect(logs.some((l) => l.includes("Backed up"))).toBe(false);
151
151
  expect(await fs.pathExists(join(dir, ".claude", ".magus-backups"))).toBe(
152
152
  false,
153
153
  );
@@ -6,10 +6,11 @@
6
6
  * update` carry on with a profile "for this run only" — resolving against a
7
7
  * manifest the project was not running. Two worlds live at once.
8
8
  *
9
- * The interactive path mounts a real OpenTUI renderer and cannot run here; what
10
- * IS testable is every path that must refuse, which is the behaviour that
11
- * changed. `selectDialog` returns null without a TTY, so a non-TTY run takes
12
- * the refusal branch by construction — which is also what CI does.
9
+ * The interactive path reads a numbered answer from a terminal and cannot run
10
+ * here; what IS testable is every path that must refuse, which is the
11
+ * behaviour that changed. Without a TTY the gate refuses before it asks, so a
12
+ * non-TTY run takes the refusal branch by construction — which is also what
13
+ * CI does.
13
14
  */
14
15
 
15
16
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
@@ -17,7 +17,7 @@ import {
17
17
  activationNotes,
18
18
  recordRefetchedSkills,
19
19
  trackFetchedSkills,
20
- trackedGeneratedWarnings,
20
+ trackedGeneratedNotices,
21
21
  updatableSkills,
22
22
  } from "../services/profile-activate.js";
23
23
  import {
@@ -361,7 +361,7 @@ describe("older layout: committed custom skills the seeding displaced come back"
361
361
  });
362
362
 
363
363
  expect(prep.lines).toContain(
364
- 'Converted profile "team" from the older layout to real files',
364
+ 'Updated profile "team" to the current profile format',
365
365
  );
366
366
  expect(
367
367
  git(
@@ -465,7 +465,7 @@ describe("older layout: committed custom skills the seeding displaced come back"
465
465
  expect(git(project, "init", "-q").status).toBe(0);
466
466
  expect(git(project, "add", "-f", ".claude/skills/alpha").status).toBe(0);
467
467
  expect(git(project, "commit", "-q", "-m", "oops").status).toBe(0);
468
- expect(await trackedGeneratedWarnings(project)).toEqual([]);
468
+ expect(await trackedGeneratedNotices(project)).toEqual([]);
469
469
  // A teammate drops alpha from the profile. Were it still magus's, the
470
470
  // reconcile would delete the team's committed folder.
471
471
  const pulled = manifest();