claudeup 6.4.0 → 6.5.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": "6.4.0",
3
+ "version": "6.5.1",
4
4
  "description": "TUI tool for managing Claude Code plugins, MCPs, and configuration",
5
5
  "type": "module",
6
6
  "main": "src/main.tsx",
@@ -64,8 +64,8 @@
64
64
  "typescript": "^5.6.3"
65
65
  },
66
66
  "optionalDependencies": {
67
- "claudeup-darwin-arm64": "6.4.0",
68
- "claudeup-darwin-x64": "6.4.0",
69
- "claudeup-linux-x64": "6.4.0"
67
+ "claudeup-darwin-arm64": "6.5.1",
68
+ "claudeup-darwin-x64": "6.5.1",
69
+ "claudeup-linux-x64": "6.5.1"
70
70
  }
71
71
  }
@@ -62,7 +62,7 @@ function plan(over: Partial<UpdatePlan> = {}): UpdatePlan {
62
62
  scopes: [],
63
63
  },
64
64
  {
65
- pluginId: "seo@magus",
65
+ pluginId: "designer@magus",
66
66
  pinned: "latest",
67
67
  installed: "1.8.0",
68
68
  available: null,
@@ -182,7 +182,7 @@ describe("NO_COLOR", () => {
182
182
 
183
183
  test("a note rides along on the row it explains", () => {
184
184
  setColorEnabled(false);
185
- const row = planReport(plan()).find((l) => l.includes("seo@magus"))!;
185
+ const row = planReport(plan()).find((l) => l.includes("designer@magus"))!;
186
186
  expect(row).toContain("catalog unreachable");
187
187
  });
188
188
  });
@@ -117,20 +117,20 @@ describe("gapFillInstalledPluginVersions", () => {
117
117
  await fs.remove(initTmpHome).catch(() => {});
118
118
  });
119
119
 
120
- // Scenario 1: "The kanban bug" — project has newer version, global is stale
121
- it("bumps global when project has a newer version (the kanban bug)", async () => {
120
+ // Scenario 1: "The stale-global bug" — project has newer version, global is stale
121
+ it("bumps global when project has a newer version (the stale-global bug)", async () => {
122
122
  await writeGlobalSettings({
123
- installedPluginVersions: { "kanban@magus": "1.0.0" },
123
+ installedPluginVersions: { "designer@magus": "1.0.0" },
124
124
  });
125
125
  await writeProjectSettings({
126
- installedPluginVersions: { "kanban@magus": "1.3.0" },
126
+ installedPluginVersions: { "designer@magus": "1.3.0" },
127
127
  });
128
128
 
129
129
  const result = await gapFillInstalledPluginVersions(tmpProject);
130
130
 
131
131
  expect(result).toEqual([
132
132
  {
133
- pluginId: "kanban@magus",
133
+ pluginId: "designer@magus",
134
134
  oldVersion: "1.0.0",
135
135
  newVersion: "1.3.0",
136
136
  },
@@ -138,7 +138,7 @@ describe("gapFillInstalledPluginVersions", () => {
138
138
 
139
139
  // Verify the file was actually updated
140
140
  const global = await readGlobalSettingsFromDisk();
141
- expect((global as any).installedPluginVersions["kanban@magus"]).toBe(
141
+ expect((global as any).installedPluginVersions["designer@magus"]).toBe(
142
142
  "1.3.0",
143
143
  );
144
144
  });
@@ -146,10 +146,10 @@ describe("gapFillInstalledPluginVersions", () => {
146
146
  // Scenario 2: Global is already current — no changes needed
147
147
  it("returns empty array when global is already at the highest version", async () => {
148
148
  await writeGlobalSettings({
149
- installedPluginVersions: { "kanban@magus": "1.3.0" },
149
+ installedPluginVersions: { "designer@magus": "1.3.0" },
150
150
  });
151
151
  await writeProjectSettings({
152
- installedPluginVersions: { "kanban@magus": "1.3.0" },
152
+ installedPluginVersions: { "designer@magus": "1.3.0" },
153
153
  });
154
154
 
155
155
  const result = await gapFillInstalledPluginVersions(tmpProject);
@@ -158,7 +158,7 @@ describe("gapFillInstalledPluginVersions", () => {
158
158
 
159
159
  // Verify global was NOT rewritten (check original content is intact)
160
160
  const global = await readGlobalSettingsFromDisk();
161
- expect((global as any).installedPluginVersions["kanban@magus"]).toBe(
161
+ expect((global as any).installedPluginVersions["designer@magus"]).toBe(
162
162
  "1.3.0",
163
163
  );
164
164
  });
@@ -298,10 +298,10 @@ describe("gapFillInstalledPluginVersions", () => {
298
298
  // Scenario 9: Global version is higher than project — no downgrade
299
299
  it("does not downgrade global when global is higher than project", async () => {
300
300
  await writeGlobalSettings({
301
- installedPluginVersions: { "kanban@magus": "2.0.0" },
301
+ installedPluginVersions: { "designer@magus": "2.0.0" },
302
302
  });
303
303
  await writeProjectSettings({
304
- installedPluginVersions: { "kanban@magus": "1.0.0" },
304
+ installedPluginVersions: { "designer@magus": "1.0.0" },
305
305
  });
306
306
 
307
307
  const result = await gapFillInstalledPluginVersions(tmpProject);
@@ -310,7 +310,7 @@ describe("gapFillInstalledPluginVersions", () => {
310
310
 
311
311
  // Verify global was NOT downgraded
312
312
  const global = await readGlobalSettingsFromDisk();
313
- expect((global as any).installedPluginVersions["kanban@magus"]).toBe(
313
+ expect((global as any).installedPluginVersions["designer@magus"]).toBe(
314
314
  "2.0.0",
315
315
  );
316
316
  });
@@ -318,12 +318,12 @@ describe("gapFillInstalledPluginVersions", () => {
318
318
  // Scenario 10: Preserves unrelated settings fields during write
319
319
  it("preserves other global settings fields when bumping versions", async () => {
320
320
  await writeGlobalSettings({
321
- enabledPlugins: { "kanban@magus": true, "dev@magus": true },
321
+ enabledPlugins: { "designer@magus": true, "dev@magus": true },
322
322
  env: { SOME_VAR: "value" },
323
- installedPluginVersions: { "kanban@magus": "1.0.0" },
323
+ installedPluginVersions: { "designer@magus": "1.0.0" },
324
324
  });
325
325
  await writeProjectSettings({
326
- installedPluginVersions: { "kanban@magus": "2.0.0" },
326
+ installedPluginVersions: { "designer@magus": "2.0.0" },
327
327
  });
328
328
 
329
329
  const result = await gapFillInstalledPluginVersions(tmpProject);
@@ -333,11 +333,11 @@ describe("gapFillInstalledPluginVersions", () => {
333
333
  // Verify other fields are preserved
334
334
  const global = await readGlobalSettingsFromDisk();
335
335
  expect((global as any).enabledPlugins).toEqual({
336
- "kanban@magus": true,
336
+ "designer@magus": true,
337
337
  "dev@magus": true,
338
338
  });
339
339
  expect((global as any).env).toEqual({ SOME_VAR: "value" });
340
- expect((global as any).installedPluginVersions["kanban@magus"]).toBe(
340
+ expect((global as any).installedPluginVersions["designer@magus"]).toBe(
341
341
  "2.0.0",
342
342
  );
343
343
  });
@@ -345,24 +345,24 @@ describe("gapFillInstalledPluginVersions", () => {
345
345
  // Scenario 11: Invalid semver in GLOBAL scope gets overwritten
346
346
  it("overwrites corrupted global version when project has valid semver", async () => {
347
347
  await writeGlobalSettings({
348
- installedPluginVersions: { "kanban@magus": "not-a-version" },
348
+ installedPluginVersions: { "designer@magus": "not-a-version" },
349
349
  });
350
350
  await writeProjectSettings({
351
- installedPluginVersions: { "kanban@magus": "1.3.0" },
351
+ installedPluginVersions: { "designer@magus": "1.3.0" },
352
352
  });
353
353
 
354
354
  const result = await gapFillInstalledPluginVersions(tmpProject);
355
355
 
356
356
  expect(result).toEqual([
357
357
  {
358
- pluginId: "kanban@magus",
358
+ pluginId: "designer@magus",
359
359
  oldVersion: "not-a-version",
360
360
  newVersion: "1.3.0",
361
361
  },
362
362
  ]);
363
363
 
364
364
  const global = await readGlobalSettingsFromDisk();
365
- expect((global as any).installedPluginVersions["kanban@magus"]).toBe(
365
+ expect((global as any).installedPluginVersions["designer@magus"]).toBe(
366
366
  "1.3.0",
367
367
  );
368
368
  });
@@ -370,10 +370,10 @@ describe("gapFillInstalledPluginVersions", () => {
370
370
  // Scenario 12: Both global and project have invalid semver — no crash, no change
371
371
  it("does nothing when all versions are invalid semver", async () => {
372
372
  await writeGlobalSettings({
373
- installedPluginVersions: { "kanban@magus": "broken" },
373
+ installedPluginVersions: { "designer@magus": "broken" },
374
374
  });
375
375
  await writeProjectSettings({
376
- installedPluginVersions: { "kanban@magus": "also-broken" },
376
+ installedPluginVersions: { "designer@magus": "also-broken" },
377
377
  });
378
378
 
379
379
  const result = await gapFillInstalledPluginVersions(tmpProject);
@@ -382,7 +382,7 @@ describe("gapFillInstalledPluginVersions", () => {
382
382
 
383
383
  // Global unchanged
384
384
  const global = await readGlobalSettingsFromDisk();
385
- expect((global as any).installedPluginVersions["kanban@magus"]).toBe(
385
+ expect((global as any).installedPluginVersions["designer@magus"]).toBe(
386
386
  "broken",
387
387
  );
388
388
  });
@@ -72,7 +72,7 @@ const CONFIG = {
72
72
  agents: {
73
73
  "dev:architect": "smart",
74
74
  Explore: "cheap",
75
- "seo:editor": { model: "inherit" },
75
+ "dev:researcher": { model: "inherit" },
76
76
  },
77
77
  fallback: "normal",
78
78
  };
@@ -158,7 +158,7 @@ describe("claudeup hook agent-model", () => {
158
158
 
159
159
  test("`inherit` leaves the call alone", async () => {
160
160
  writeConfig(dir);
161
- const r = await runHook(agentPayload({ subagent_type: "seo:editor" }));
161
+ const r = await runHook(agentPayload({ subagent_type: "dev:researcher" }));
162
162
  expect(r.stdout).toBe("");
163
163
  });
164
164
 
@@ -317,7 +317,11 @@ describe("the agent table", () => {
317
317
  });
318
318
 
319
319
  test("sorted by grade in ROUTING order, then alphabetically inside it", () => {
320
- const rows = agentRows(fableAdvisor);
320
+ // No built-in preset pins an agent to `inherit`, so add one to prove it trails.
321
+ const rows = agentRows({
322
+ ...fableAdvisor,
323
+ agents: { ...fableAdvisor.agents, "x:pinned": { model: "inherit" } },
324
+ });
321
325
  const rank = (key: string) => {
322
326
  const index = (GRADES as readonly string[]).indexOf(key);
323
327
  return index === -1 ? GRADES.length : index;
@@ -361,7 +365,7 @@ describe("the agent table", () => {
361
365
  test("an inherit row sets nothing, so it reads as an em dash", () => {
362
366
  const config: ModelsConfig = {
363
367
  ...fableAdvisor,
364
- agents: { "seo:editor": { model: "inherit" } },
368
+ agents: { "dev:researcher": { model: "inherit" } },
365
369
  };
366
370
  const row = agentRows(config)[0];
367
371
  expect(row?.inherit).toBe(true);
@@ -570,8 +574,8 @@ describe("the colour key under the bars", () => {
570
574
  // NOT haiku: the presets no longer use it, and a key naming a colour that appears
571
575
  // nowhere on screen is a legend for a thing that is not there.
572
576
  expect(models).not.toContain("haiku");
573
- // `seo:editor` is pinned to `inherit`, so the key has to explain that grey.
574
- expect(models).toContain("inherit");
577
+ // Nor inherit: no preset pins an agent to it, so there is no grey to explain.
578
+ expect(models).not.toContain("inherit");
575
579
  expect(new Set(models).size).toBe(models.length);
576
580
  });
577
581
 
@@ -39,7 +39,7 @@ const base = (): ModelsConfig => ({
39
39
  agents: {
40
40
  "dev:architect": "smart",
41
41
  Explore: "cheap",
42
- "seo:editor": { model: "inherit" },
42
+ "dev:researcher": { model: "inherit" },
43
43
  },
44
44
  fallback: "normal",
45
45
  });
@@ -113,9 +113,9 @@ describe("validateModelsConfig", () => {
113
113
 
114
114
  test("rejects effort on `inherit`, which names no model to key it by", () => {
115
115
  const c = base();
116
- c.agents["seo:editor"] = { model: "inherit", effort: "low" } as never;
116
+ c.agents["dev:researcher"] = { model: "inherit", effort: "low" } as never;
117
117
  expect(validateModelsConfig(c).map((e) => e.path)).toEqual([
118
- "agents.seo:editor.effort",
118
+ "agents.dev:researcher.effort",
119
119
  ]);
120
120
  });
121
121
 
@@ -151,7 +151,7 @@ describe("resolveAgentRouting", () => {
151
151
  });
152
152
 
153
153
  test("a per-agent override beats the grade table", () => {
154
- expect(resolveAgentRouting(base(), "seo:editor")).toEqual({
154
+ expect(resolveAgentRouting(base(), "dev:researcher")).toEqual({
155
155
  model: "inherit",
156
156
  source: "agent-override",
157
157
  });
@@ -228,7 +228,7 @@ describe("evaluateAgentHook", () => {
228
228
 
229
229
  test("`inherit` is the opt-out and touches nothing", () => {
230
230
  expect(
231
- evaluateAgentHook(agentCall({ subagent_type: "seo:editor" }), base()),
231
+ evaluateAgentHook(agentCall({ subagent_type: "dev:researcher" }), base()),
232
232
  ).toEqual({
233
233
  kind: "passthrough",
234
234
  reason: "inherit",
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * An orphaned plugin that merely changed marketplace is not deprecated.
3
3
  *
4
- * Splitting `magus` into `magus` + `magus-marketing` left `seo@magus`,
5
- * `instantly@magus`, `video-editing@magus` and `nanobanana@magus` installed
6
- * under a namespace that no longer lists them. They rendered as bare
4
+ * Splitting `magus` into `magus` + `magus-marketing` left `video-editing@magus`
5
+ * and `nanobanana@magus` installed under a namespace that no longer lists them. They rendered as bare
7
6
  * "deprecated", whose only action is deletion — which drops a plugin that is
8
7
  * still published, just under a different id.
9
8
  */
@@ -45,19 +44,19 @@ describe("moved-marketplace detection", () => {
45
44
  test("finds a plugin that moved to a sibling marketplace", () => {
46
45
  const marketplaces = new Map([
47
46
  mp("magus", ["dev", "terminal"]),
48
- mp("magus-marketing", ["seo", "instantly", "video-editing"]),
47
+ mp("magus-marketing", ["image-generate", "video-editing"]),
49
48
  ]);
50
49
 
51
- expect(findPluginInOtherMarketplace("seo", "magus", marketplaces)).toBe(
52
- "magus-marketing",
53
- );
50
+ expect(
51
+ findPluginInOtherMarketplace("video-editing", "magus", marketplaces),
52
+ ).toBe("magus-marketing");
54
53
  });
55
54
 
56
55
  test("a genuinely retired plugin reports no destination", () => {
57
56
  // `conductor` was removed at magus v8.0.0 and republished nowhere.
58
57
  const marketplaces = new Map([
59
58
  mp("magus", ["dev"]),
60
- mp("magus-marketing", ["seo"]),
59
+ mp("magus-marketing", ["video-editing"]),
61
60
  ]);
62
61
 
63
62
  expect(
@@ -90,7 +90,7 @@ describe("fetchPluginContents", () => {
90
90
  stubTree([
91
91
  "plugins/dev/skills/frontend/design-system/SKILL.md",
92
92
  "plugins/dev/skills/backend/db-branching/SKILL.md",
93
- "plugins/seo/skills/audit/SKILL.md",
93
+ "plugins/designer/skills/ui-analyse/SKILL.md",
94
94
  ]);
95
95
 
96
96
  const skills = await fetchPluginContents("fake/multi", "./plugins/dev");
@@ -24,7 +24,7 @@ describe("buildAdoptedProfile", () => {
24
24
 
25
25
  test("skips disabled plugins", () => {
26
26
  const entry = buildAdoptedProfile({
27
- enabledPlugins: { "dev@magus": true, "gtd@magus": false },
27
+ enabledPlugins: { "dev@magus": true, "designer@magus": false },
28
28
  settings: {},
29
29
  mcpServers: {},
30
30
  now: NOW,
@@ -61,12 +61,12 @@ describe("buildProfileSettings", () => {
61
61
  test("explicitly disables union plugins the profile excludes", () => {
62
62
  const s = buildProfileSettings(
63
63
  closure({ plugins: { "designer@magus": "latest" } }),
64
- ["designer@magus", "terminal@magus", "gtd@magus"],
64
+ ["designer@magus", "terminal@magus", "madbench@magus"],
65
65
  );
66
66
  expect(s.enabledPlugins).toEqual({
67
67
  "designer@magus": true,
68
68
  "terminal@magus": false,
69
- "gtd@magus": false,
69
+ "madbench@magus": false,
70
70
  });
71
71
  });
72
72
 
@@ -91,12 +91,11 @@ describe("resolveExtends", () => {
91
91
  extends: "growth-marketer",
92
92
  };
93
93
  const merged = resolveExtends(entry);
94
- // seo/image-generate/video-editing ship on the magus-marketing channel and
94
+ // image-generate/video-editing ship on the magus-marketing channel and
95
95
  // pin it explicitly — they must NOT be rewritten to @magus.
96
- expect(merged.plugins!["seo@magus-marketing"]).toBe("latest");
97
96
  expect(merged.plugins!["image-generate@magus-marketing"]).toBe("latest");
98
97
  expect(merged.plugins!["video-editing@magus-marketing"]).toBe("latest");
99
- expect(merged.plugins!["seo@magus"]).toBeUndefined();
98
+ expect(merged.plugins!["image-generate@magus"]).toBeUndefined();
100
99
  // Bare names still default to @magus.
101
100
  expect(merged.plugins!["browser-use@magus"]).toBe("latest");
102
101
  });
@@ -303,8 +302,8 @@ describe("marketplaces derived from plugin ids", () => {
303
302
  name: "Mixed",
304
303
  plugins: {
305
304
  "dev@magus": "latest",
306
- "seo@magus-marketing": "latest",
307
- "autolinear@magus-alpha": "latest",
305
+ "image-generate@magus-marketing": "latest",
306
+ "feature-dev@claude-plugins-official": "latest",
308
307
  },
309
308
  },
310
309
  });
@@ -312,7 +311,9 @@ describe("marketplaces derived from plugin ids", () => {
312
311
  expect(c.marketplaces["magus-marketing"]?.repo).toBe(
313
312
  "MadAppGang/magus-marketing",
314
313
  );
315
- expect(c.marketplaces["magus-alpha"]?.repo).toBe("MadAppGang/magus-alpha");
314
+ expect(c.marketplaces["claude-plugins-official"]?.repo).toBe(
315
+ "anthropics/claude-plugins-official",
316
+ );
316
317
  });
317
318
 
318
319
  test("an explicit declaration is never overridden", async () => {
@@ -159,13 +159,13 @@ describe("per-project baselines", () => {
159
159
 
160
160
  test("saving one project preserves the others", async () => {
161
161
  await withSandbox(async () => {
162
- await saveSeenVersions("/proj/a", { "gtd@magus": "2.0.0" });
163
- await saveSeenVersions("/proj/b", { "gtd@magus": "2.0.1" });
162
+ await saveSeenVersions("/proj/a", { "designer@magus": "2.0.0" });
163
+ await saveSeenVersions("/proj/b", { "designer@magus": "2.0.1" });
164
164
  expect(await loadSeenVersions("/proj/a")).toEqual({
165
- "gtd@magus": "2.0.0",
165
+ "designer@magus": "2.0.0",
166
166
  });
167
167
  expect(await loadSeenVersions("/proj/b")).toEqual({
168
- "gtd@magus": "2.0.1",
168
+ "designer@magus": "2.0.1",
169
169
  });
170
170
  });
171
171
  });
@@ -21,7 +21,6 @@ export const BUILTIN_DEFAULTS: GitignoreManifest = {
21
21
  "ai-docs/sessions/",
22
22
  ".mnemex/",
23
23
  ".claudemem/",
24
- "gtd/sessions/",
25
24
  ".agents/",
26
25
  ],
27
26
  track: [
@@ -29,10 +29,6 @@ const RULE_REASONS = new Map<string, string>([
29
29
  key("ignore", ".claudemem/"),
30
30
  "Local claudemem indexes are generated from the repo and should not be committed.",
31
31
  ],
32
- [
33
- key("ignore", "gtd/sessions/"),
34
- "Local GTD session artifacts are personal workflow state.",
35
- ],
36
32
  [
37
33
  key("ignore", ".agents/"),
38
34
  "Local agent workspaces and runtime artifacts should stay out of team history.",
@@ -47,8 +47,7 @@ export const defaultMarketplaces: Marketplace[] = [
47
47
  source: "github",
48
48
  repo: "MadAppGang/magus-marketing",
49
49
  },
50
- description:
51
- "SEO, cold email outreach, AI image generation, and video editing plugins",
50
+ description: "AI image generation and video editing plugins",
52
51
  official: false,
53
52
  owned: true, // Ours: always listed even when uninstalled, sorted to the top
54
53
  // Not featured: a niche channel, so keep it collapsed by default.
@@ -56,7 +56,6 @@ export const DEFAULT_AGENT_GRADES: Record<string, Grade> = {
56
56
  "dev:stack-detector": "cheap",
57
57
  "dev:docs": "cheap",
58
58
  "terminal:tui-navigator": "cheap",
59
- "gtd:gtd-reviewer": "cheap",
60
59
  "statusline-setup": "cheap",
61
60
  };
62
61
 
@@ -115,16 +114,6 @@ export const WORKFLOWS: { name: string; agents: string[] }[] = [
115
114
  */
116
115
  export const VISION_AGENTS = ["designer:design-review", "designer:ui"] as const;
117
116
 
118
- /**
119
- * `seo:editor` is the one agent in this marketplace whose own frontmatter sets a model.
120
- * Routing it to `inherit` keeps that authoring decision visible and working instead of
121
- * silently overriding it — per-invocation beats frontmatter, so without this the author's
122
- * choice would vanish with no error.
123
- */
124
- const FRONTMATTER_RESPECTED: Record<string, { model: "inherit" }> = {
125
- "seo:editor": { model: "inherit" },
126
- };
127
-
128
117
  function preset(
129
118
  name: string,
130
119
  main: ModelsConfig["main"],
@@ -135,7 +124,7 @@ function preset(
135
124
  preset: name,
136
125
  main,
137
126
  grades,
138
- agents: { ...DEFAULT_AGENT_GRADES, ...FRONTMATTER_RESPECTED },
127
+ agents: { ...DEFAULT_AGENT_GRADES },
139
128
  fallback: "normal",
140
129
  };
141
130
  }
@@ -57,7 +57,6 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
57
57
  "browser-use",
58
58
  "designer",
59
59
  "terminal",
60
- "kanban",
61
60
  ],
62
61
  anthropicPlugins: [
63
62
  "claude-code-setup",
@@ -130,14 +129,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
130
129
  name: "Backend Forge",
131
130
  description: "API development, debugging, code quality, data workflows",
132
131
  icon: "★",
133
- magusPlugins: [
134
- "dev",
135
- "code-analysis",
136
- "terminal",
137
- "setup",
138
- "multimodel",
139
- "gtd",
140
- ],
132
+ magusPlugins: ["dev", "code-analysis", "terminal", "setup", "multimodel"],
141
133
  anthropicPlugins: [
142
134
  "feature-dev",
143
135
  "code-review",
@@ -171,7 +163,6 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
171
163
  "terminal",
172
164
  "setup",
173
165
  "multimodel",
174
- "gtd",
175
166
  "browser-use",
176
167
  ],
177
168
  anthropicPlugins: [
@@ -199,15 +190,13 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
199
190
  {
200
191
  id: "growth-marketer",
201
192
  name: "Growth Marketer",
202
- description:
203
- "SEO, website audits, content production, marketing automation",
193
+ description: "Website audits, content production, marketing automation",
204
194
  icon: "★",
205
195
  magusPlugins: [
206
196
  "dev",
207
197
  "code-analysis",
208
198
  "terminal",
209
199
  "setup",
210
- "seo@magus-marketing",
211
200
  "browser-use",
212
201
  "image-generate@magus-marketing",
213
202
  "video-editing@magus-marketing",
@@ -237,14 +226,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
237
226
  description:
238
227
  "Planning, code review, coordination, and broad repo visibility",
239
228
  icon: "★",
240
- magusPlugins: [
241
- "dev",
242
- "code-analysis",
243
- "terminal",
244
- "setup",
245
- "multimodel",
246
- "gtd",
247
- ],
229
+ magusPlugins: ["dev", "code-analysis", "terminal", "setup", "multimodel"],
248
230
  anthropicPlugins: [
249
231
  "code-review",
250
232
  "pr-review-toolkit",
@@ -14,7 +14,7 @@
14
14
  * `hook-named` 5/5 (subagent on claude-haiku-4-5-20251001) vs `nohook` 0/5 (inherited
15
15
  * claude-sonnet-5). One-sided Fisher p = 0.004.
16
16
  * 2. `hookEventName` is REQUIRED. The payload without it ran on all five passes and changed
17
- * nothing — so ALWAYS emit it. (Same defect ships in plugins/gtd's hook today.)
17
+ * nothing — so ALWAYS emit it.
18
18
  * 3. `permissionDecision: "allow"` is neither required nor harmful, so it is NOT emitted:
19
19
  * it would widen permission for the call in exchange for nothing.
20
20
  * 4. The value is SCHEMA-CHECKED. A non-alias string refused the spawn outright on all five
@@ -113,9 +113,8 @@ export interface PluginInfo {
113
113
  * For an orphaned plugin: another configured marketplace that publishes a
114
114
  * plugin of the same name — i.e. it did not disappear, it moved.
115
115
  *
116
- * Splitting `magus` into `magus` + `magus-marketing` left seo, instantly,
117
- * video-editing and image-generate installed under a namespace that no
118
- * longer lists them. Rendered as bare "deprecated", the only offered action
116
+ * Splitting `magus` into `magus` + `magus-marketing` left video-editing and
117
+ * image-generate installed under a namespace that no longer lists them. Rendered as bare "deprecated", the only offered action
119
118
  * was deletion, which silently drops a plugin that is still published.
120
119
  */
121
120
  movedTo?: string;
@@ -55,7 +55,7 @@ export function resolveExtends(
55
55
 
56
56
  const inheritedPlugins: Record<string, string | boolean> = {};
57
57
  for (const name of base.magusPlugins) {
58
- // Entries may pin a marketplace explicitly (e.g. "seo@magus-marketing")
58
+ // Entries may pin a marketplace explicitly (e.g. "image-generate@magus-marketing")
59
59
  // for plugins that ship on a channel other than magus. Bare names
60
60
  // still default to @magus.
61
61
  inheritedPlugins[name.includes("@") ? name : `${name}@magus`] = "latest";
@@ -158,7 +158,7 @@ async function resolveBins(
158
158
  /**
159
159
  * Add the marketplace every plugin id already names.
160
160
  *
161
- * A plugin id carries its marketplace — `dev@magus`, `seo@magus-marketing` —
161
+ * A plugin id carries its marketplace — `dev@magus`, `image-generate@magus-marketing` —
162
162
  * so requiring the manifest to ALSO declare that marketplace is redundant, and
163
163
  * forgetting to is silent: the plugin resolves against a marketplace that was
164
164
  * never registered. `extends` made this the default failure, because it
@@ -1200,7 +1200,7 @@ export function PluginsScreen() {
1200
1200
  // A plugin that moved marketplace is not deprecated — it is still
1201
1201
  // published under a new namespace. Offering only "remove" silently drops
1202
1202
  // something the user still wants (the magus → magus-marketing split left
1203
- // seo, instantly, video-editing and image-generate in this state).
1203
+ // video-editing and image-generate in this state).
1204
1204
  let migrateTo: string | undefined;
1205
1205
  if (plugin.movedTo) {
1206
1206
  const choice = await modal.select(