@curdx/flow 2.0.0-beta.9 → 2.0.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/cli/registry.js CHANGED
@@ -9,58 +9,107 @@
9
9
  * or removing a plugin is a one-file change.
10
10
  *
11
11
  * Every consumer pulls what it needs via property access:
12
- * - install.js → marketplace + installSpec + hint (+ optional postInstall)
13
- * - uninstall.js → uninstallSpec
14
- * - upgrade.js → installSpec (for `claude plugin update`) + marketplaceId
15
- * - doctor.js → name + installSpec (for manual recovery hints)
12
+ * - install.js → marketplaceSource + installSpec + hint (+ optional postInstall)
13
+ * - uninstall.js → uninstallSpec + uninstallArgs + marketplaceId
14
+ * - upgrade.js → updateSpec + marketplaceId
15
+ * - doctor.js → id + installSpec (for health checks and recovery hints)
16
16
  */
17
17
 
18
18
  export const RECOMMENDED_PLUGINS = [
19
19
  {
20
20
  name: "pua",
21
- marketplace: "tanweai/pua",
22
- marketplaceId: "pua",
21
+ id: "pua@pua-skills",
22
+ marketplaceSource: "tanweai/pua",
23
+ marketplaceId: "pua-skills",
23
24
  installSpec: "pua@pua-skills",
24
25
  uninstallSpec: "pua@pua-skills",
26
+ updateSpec: "pua@pua-skills",
27
+ scope: "user",
25
28
  hint: "no-give-up + three red lines",
26
29
  },
27
30
  {
28
31
  name: "claude-mem",
29
- marketplace: "thedotmack/claude-mem",
32
+ id: "claude-mem@thedotmack",
33
+ marketplaceSource: "thedotmack/claude-mem",
30
34
  marketplaceId: "thedotmack",
31
35
  installSpec: "claude-mem@thedotmack",
32
36
  uninstallSpec: "claude-mem@thedotmack",
37
+ updateSpec: "claude-mem@thedotmack",
38
+ uninstallArgs: ["--keep-data"],
39
+ scope: "user",
33
40
  hint: "automatic cross-session memory",
34
41
  postInstall: "claude-mem-runtimes",
35
42
  },
36
43
  {
37
44
  name: "frontend-design",
38
- // Already in default marketplace claude-plugins-official, no add needed
39
- marketplace: null,
45
+ id: "frontend-design@claude-plugins-official",
46
+ marketplaceSource: "anthropics/claude-plugins-official",
40
47
  marketplaceId: "claude-plugins-official",
41
48
  installSpec: "frontend-design@claude-plugins-official",
42
49
  uninstallSpec: "frontend-design@claude-plugins-official",
50
+ updateSpec: "frontend-design@claude-plugins-official",
51
+ scope: "user",
43
52
  hint: "Anthropic official UI skill",
44
53
  },
45
54
  {
46
55
  name: "chrome-devtools-mcp",
47
- marketplace: "ChromeDevTools/chrome-devtools-mcp",
56
+ id: "chrome-devtools-mcp@chrome-devtools-plugins",
57
+ marketplaceSource: "ChromeDevTools/chrome-devtools-mcp",
48
58
  marketplaceId: "chrome-devtools-plugins",
49
59
  installSpec: "chrome-devtools-mcp@chrome-devtools-plugins",
50
60
  uninstallSpec: "chrome-devtools-mcp@chrome-devtools-plugins",
61
+ updateSpec: "chrome-devtools-mcp@chrome-devtools-plugins",
62
+ scope: "user",
51
63
  hint: "Chrome DevTools + Puppeteer (Google official)",
52
64
  },
53
65
  ];
54
66
 
67
+ export const REQUIRED_PLUGINS = [
68
+ {
69
+ name: "context7-plugin",
70
+ id: "context7-plugin@context7-marketplace",
71
+ marketplaceSource: "upstash/context7",
72
+ marketplaceId: "context7-marketplace",
73
+ installSpec: "context7-plugin@context7-marketplace",
74
+ uninstallSpec: "context7-plugin@context7-marketplace",
75
+ updateSpec: "context7-plugin@context7-marketplace",
76
+ scope: "user",
77
+ hint: "official Context7 plugin (MCP + skill + docs-researcher agent + /context7:docs)",
78
+ requiresConfig: true,
79
+ configType: "apiKey",
80
+ },
81
+ ];
82
+
83
+ /**
84
+ * MCP servers that curdx-flow depends on for its core discipline rules and
85
+ * still registers directly. Starting beta.12 these are registered at
86
+ * USER-LEVEL via `claude mcp add` instead of plugin.json bundling, so:
87
+ *
88
+ * - Tool names stay standard (mcp__sequential-thinking__*)
89
+ * — matching every agent's and knowledge doc's hardcoded references.
90
+ *
91
+ * Context7 is installed via Upstash's official Claude Code plugin instead
92
+ * of direct `claude mcp add`: context7-plugin@context7-marketplace includes
93
+ * the MCP server, skill, docs-researcher agent, and /context7:docs command.
94
+ */
95
+ export const BUNDLED_MCPS = [
96
+ {
97
+ name: "sequential-thinking",
98
+ command: "npx",
99
+ args: ["-y", "@modelcontextprotocol/server-sequential-thinking"],
100
+ purpose: "structured reasoning for design / review (L2 Mandatory Tool)",
101
+ preserveExisting: true,
102
+ },
103
+ ];
104
+
55
105
  /**
56
106
  * Marketplaces to refresh during `upgrade`. Derived from RECOMMENDED_PLUGINS
57
107
  * plus the curdx-flow marketplace itself.
58
108
  */
59
109
  export const MARKETPLACES_TO_REFRESH = [
60
110
  "curdx-flow-marketplace",
61
- ...RECOMMENDED_PLUGINS
62
- .filter((p) => p.marketplaceId && p.marketplaceId !== "claude-plugins-official")
63
- .map((p) => p.marketplaceId),
111
+ ...REQUIRED_PLUGINS.map((p) => p.marketplaceId),
112
+ ...RECOMMENDED_PLUGINS.map((p) => p.marketplaceId),
64
113
  ];
65
114
 
66
115
  /**
@@ -69,5 +118,6 @@ export const MARKETPLACES_TO_REFRESH = [
69
118
  */
70
119
  export const PLUGINS_TO_UPDATE = [
71
120
  "curdx-flow@curdx-flow-marketplace",
72
- ...RECOMMENDED_PLUGINS.map((p) => p.installSpec),
121
+ ...REQUIRED_PLUGINS.map((p) => p.updateSpec),
122
+ ...RECOMMENDED_PLUGINS.map((p) => p.updateSpec),
73
123
  ];
package/cli/uninstall.js CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { existsSync, lstatSync, unlinkSync, rmSync, readlinkSync } from "node:fs";
6
6
  import { join } from "node:path";
7
+ import { homedir } from "node:os";
7
8
 
8
9
  import {
9
10
  color,
@@ -15,14 +16,24 @@ import {
15
16
  listPlugins,
16
17
  } from "./utils.js";
17
18
  import { removeGlobalProtocols, GLOBAL_CLAUDE_MD } from "./protocols.js";
18
- import { RECOMMENDED_PLUGINS } from "./registry.js";
19
+ import { REQUIRED_PLUGINS, RECOMMENDED_PLUGINS, BUNDLED_MCPS } from "./registry.js";
19
20
 
20
- const HOME = process.env.HOME || "";
21
+ const HOME = homedir();
21
22
 
22
23
  // Pull uninstall-relevant subset from the single registry. See registry.js.
23
- const RECOMMENDED = RECOMMENDED_PLUGINS.map(({ name, uninstallSpec }) => ({
24
+ const RECOMMENDED = RECOMMENDED_PLUGINS.map(({ name, uninstallSpec, uninstallArgs, marketplaceId, scope }) => ({
24
25
  name,
25
26
  uninstallSpec,
27
+ uninstallArgs: uninstallArgs || [],
28
+ marketplaceId,
29
+ scope,
30
+ }));
31
+ const REQUIRED = REQUIRED_PLUGINS.map(({ name, uninstallSpec, uninstallArgs, marketplaceId, scope }) => ({
32
+ name,
33
+ uninstallSpec,
34
+ uninstallArgs: uninstallArgs || [],
35
+ marketplaceId,
36
+ scope,
26
37
  }));
27
38
 
28
39
  // Symlinks created by install.js (only cleaned with --purge)
@@ -67,7 +78,7 @@ export async function uninstall(args = []) {
67
78
  } else {
68
79
  const r = await run(
69
80
  "claude",
70
- ["plugin", "uninstall", "curdx-flow@curdx-flow-marketplace"],
81
+ ["plugin", "uninstall", "--scope", "user", "curdx-flow@curdx-flow-marketplace"],
71
82
  { silent: true }
72
83
  );
73
84
  if (r.code === 0) {
@@ -113,10 +124,10 @@ export async function uninstall(args = []) {
113
124
  for (const name of toRemove) {
114
125
  const rec = presentRecs.find((r) => r.name === name);
115
126
  log.blank();
116
- console.log(` ${color.cyan("")} Uninstalling ${color.bold(rec.name)}...`);
127
+ console.log(` ${color.cyan("")} Uninstalling ${color.bold(rec.name)}...`);
117
128
  const r = await run(
118
129
  "claude",
119
- ["plugin", "uninstall", rec.uninstallSpec],
130
+ ["plugin", "uninstall", "--scope", rec.scope, ...rec.uninstallArgs, rec.uninstallSpec],
120
131
  { silent: true }
121
132
  );
122
133
  if (r.code === 0) {
@@ -130,9 +141,71 @@ export async function uninstall(args = []) {
130
141
  }
131
142
  }
132
143
 
144
+ // ---------- Step 4.5: optionally remove user-level MCPs ----------
145
+ // Starting beta.12, the install command registers context7 +
146
+ // sequential-thinking at user-level (not plugin-bundled). Ask before
147
+ // removing because the user may have customised args (e.g. --api-key)
148
+ // or still be using these MCPs outside curdx-flow.
149
+ log.blank();
150
+ log.info("Required MCP servers (context7, sequential-thinking)");
151
+ if (keepRecommended || yes) {
152
+ log.info(
153
+ color.dim("--yes or --keep-recommended: keeping user-level MCPs (remove manually with `claude mcp remove <name>`)")
154
+ );
155
+ } else {
156
+ const removeMcps = await confirm(
157
+ `Remove user-level MCPs registered by install (${BUNDLED_MCPS.map((m) => m.name).join(", ")})? ${color.dim("(keeps them if other tools depend on them)")}`,
158
+ false
159
+ );
160
+ if (removeMcps) {
161
+ for (const mcp of BUNDLED_MCPS) {
162
+ const r = await run("claude", ["mcp", "remove", mcp.name], {
163
+ silent: true,
164
+ });
165
+ if (r.code === 0) {
166
+ log.ok(` ${mcp.name.padEnd(22)} removed`);
167
+ } else {
168
+ log.info(` ${mcp.name.padEnd(22)} ${color.dim("not present or already removed")}`);
169
+ }
170
+ }
171
+ } else {
172
+ log.info("Keeping user-level MCPs");
173
+ }
174
+ }
175
+
176
+ // ---------- Step 4.75: uninstall required companion plugins ----------
177
+ log.blank();
178
+ log.info("Required companion plugins");
179
+ if (yes) {
180
+ log.info(
181
+ color.dim("--yes mode: keeping required companion plugins (use --purge to remove them)")
182
+ );
183
+ } else {
184
+ const removeRequired = await confirm(
185
+ `Remove required companion plugins (${REQUIRED.map((p) => p.name).join(", ")})? ${color.dim("(keeps shared tools available if other workflows depend on them)")}`,
186
+ false
187
+ );
188
+ if (removeRequired) {
189
+ for (const plugin of REQUIRED) {
190
+ const r = await run(
191
+ "claude",
192
+ ["plugin", "uninstall", "--scope", plugin.scope, ...plugin.uninstallArgs, plugin.uninstallSpec],
193
+ { silent: true }
194
+ );
195
+ if (r.code === 0) {
196
+ log.ok(` ${plugin.name.padEnd(22)} uninstalled`);
197
+ } else {
198
+ log.info(` ${plugin.name.padEnd(22)} ${color.dim("not present or already removed")}`);
199
+ }
200
+ }
201
+ } else {
202
+ log.info("Keeping required companion plugins");
203
+ }
204
+ }
205
+
133
206
  // ---------- Step 5: cleanup symlinks (only with --purge) ----------
134
207
  log.blank();
135
- log.step(3, 4, "Runtime symlinks");
208
+ log.step(3, 4, "Runtime symlinks and marketplaces");
136
209
  if (!purge) {
137
210
  log.info(
138
211
  color.dim("Keeping ~/.local/bin/bun, ~/.local/bin/uv (use --purge to remove)")
@@ -141,6 +214,27 @@ export async function uninstall(args = []) {
141
214
  color.dim("Reason: these bun/uv binaries may be used by other tools — confirm before deleting")
142
215
  );
143
216
  } else {
217
+ const marketplaceIds = [
218
+ ...new Set(
219
+ RECOMMENDED
220
+ .concat(REQUIRED)
221
+ .map((r) => r.marketplaceId)
222
+ .filter((id) => id && id !== "claude-plugins-official")
223
+ ),
224
+ ];
225
+ for (const marketplaceId of marketplaceIds) {
226
+ const r = await run(
227
+ "claude",
228
+ ["plugin", "marketplace", "remove", marketplaceId],
229
+ { silent: true }
230
+ );
231
+ if (r.code === 0) {
232
+ log.ok(`Removed marketplace ${marketplaceId}`);
233
+ } else if (!r.stderr.includes("not found")) {
234
+ log.warn(`Failed to remove marketplace ${marketplaceId}: ${r.stderr.trim().split("\n").pop()}`);
235
+ }
236
+ }
237
+
144
238
  for (const link of MANAGED_SYMLINKS) {
145
239
  if (!existsSync(link) && !isBrokenSymlink(link)) {
146
240
  continue;
package/cli/upgrade.js CHANGED
@@ -47,7 +47,7 @@ export async function upgrade(args = []) {
47
47
  continue;
48
48
  }
49
49
 
50
- const r = await run("claude", ["plugin", "update", spec], { silent: true });
50
+ const r = await run("claude", ["plugin", "update", "--scope", "user", spec], { silent: true });
51
51
  if (r.code === 0) {
52
52
  const updated = r.stdout.includes("updated from");
53
53
  if (updated) {