@curdx/flow 7.1.1 → 7.1.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to `@curdx/flow` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/) and the project follows [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## 7.1.2 — 2026-05-04
6
+
7
+ ### Added
8
+
9
+ - **Auto-detect-and-install Bun when installing `claude-mem`.** The `claude-mem` plugin's runtime hooks shell out to `node scripts/bun-runner.js` which requires Bun on `PATH` or `~/.bun/bin/bun(.exe)` — Windows users without Bun previously hit `Error: Bun not found` at every Claude Code session start. The installer now runs `ensureBun()` as a `prereqCheck` before installing `claude-mem`: detects Bun via `which`/`where` plus the standard fallback paths (mirrors `bun-runner.js`'s discovery order), and if missing prompts `Auto-install Bun now? (default: No)`. On accept, runs the official installer (`curl -fsSL https://bun.sh/install | bash` on macOS/Linux, `powershell -c "irm bun.sh/install.ps1 | iex"` on Windows). On decline, the installer surfaces a `skip` row for `claude-mem` and continues with the rest of the bundle — other packages are unaffected. Existing users with Bun already installed (e.g. all macOS users who manually ran the installer earlier) see zero behavior change.
10
+ - New module: `src/runner/ensureBun.ts` (~75 LoC) — exports `findBun()` and `ensureBun(t)`.
11
+ - `src/registry/plugins/claude-mem.ts` declares `prereqCheck: (t) => ensureBun(t)` (one-line wire-in via the existing `Pkg.prereqCheck` contract that `chrome-devtools-mcp` already uses).
12
+ - i18n: 9 new `bun.*` keys in `src/i18n/{zh,en}.ts`.
13
+
5
14
  ## 7.1.1 — 2026-05-04
6
15
 
7
16
  ### Fixed
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import * as p9 from "@clack/prompts";
4
+ import * as p10 from "@clack/prompts";
5
5
  import { defineCommand, runMain } from "citty";
6
6
 
7
7
  // src/ui/language.ts
@@ -66,6 +66,15 @@ var messages = {
66
66
  "context7.dashboardHint": "\u53EF\u5728 https://context7.com/dashboard \u521B\u5EFA API Key",
67
67
  "chrome.prereqNode": "\u9700\u8981 Node.js >= 20.19\uFF0C\u5F53\u524D\u7248\u672C {current}",
68
68
  "chrome.prereqChrome": "\u9700\u8981\u672C\u673A\u5DF2\u5B89\u88C5 Chrome\uFF08chrome-devtools-mcp \u4F1A\u8C03\u7528\u672C\u5730\u6D4F\u89C8\u5668\uFF09",
69
+ "bun.missing": "\u672A\u68C0\u6D4B\u5230 Bun\uFF0Cclaude-mem \u7684\u540E\u53F0 worker \u4F9D\u8D56 Bun \u8FD0\u884C\u3002",
70
+ "bun.installerSource": "Bun \u5B89\u88C5\u811A\u672C\u6765\u6E90\uFF1Ahttps://bun.sh\uFF08macOS/Linux \u7528 curl\uFF0CWindows \u7528 powershell irm\uFF09\u3002",
71
+ "bun.confirmInstall": "\u662F\u5426\u73B0\u5728\u81EA\u52A8\u5B89\u88C5 Bun\uFF1F\uFF08\u9ED8\u8BA4\u5426\u2014\u2014\u9009\u5426\u5C06\u8DF3\u8FC7 claude-mem\uFF0C\u5176\u4ED6\u63D2\u4EF6\u7EE7\u7EED\u5B89\u88C5\uFF09",
72
+ "bun.declined": "\u5DF2\u8DF3\u8FC7 Bun \u5B89\u88C5\uFF1Bclaude-mem \u9700\u8981 Bun\uFF0C\u672C\u6B21\u4E0D\u5B89\u88C5\u3002\u624B\u52A8\u88C5\u597D Bun \u540E\u53EF\u91CD\u8DD1 install\u3002",
73
+ "bun.installing": "\u6B63\u5728\u4E0B\u8F7D\u5E76\u5B89\u88C5 Bun\uFF08\u9996\u6B21\u7EA6 60MB\uFF09\u2026",
74
+ "bun.installed": "Bun \u5B89\u88C5\u5B8C\u6210\u3002",
75
+ "bun.installFailedTitle": "Bun \u5B89\u88C5\u5931\u8D25",
76
+ "bun.installFailedReason": "Bun \u5B89\u88C5\u5931\u8D25\uFF1A{error}",
77
+ "bun.installedButNotFound": "Bun \u5B89\u88C5\u811A\u672C\u58F0\u660E\u6210\u529F\uFF0C\u4F46\u672A\u5728\u5DF2\u77E5\u8DEF\u5F84\u627E\u5230 bun \u53EF\u6267\u884C\u6587\u4EF6\u2014\u2014\u8BF7\u624B\u52A8\u786E\u8BA4\u540E\u91CD\u8DD1\u3002",
69
78
  "reinstall.uninstalling": "\u5148\u5378\u8F7D\u65E7\u7248\u672C\u2026",
70
79
  "reinstall.installing": "\u5B89\u88C5\u65B0\u7248\u672C\u2026",
71
80
  "state.checking": "\u68C0\u67E5\u5DF2\u5B89\u88C5\u72B6\u6001\u2026\uFF08claude plugin list / mcp list\uFF09",
@@ -138,6 +147,15 @@ var messages2 = {
138
147
  "context7.dashboardHint": "Get a key at https://context7.com/dashboard",
139
148
  "chrome.prereqNode": "Requires Node.js >= 20.19 (current: {current})",
140
149
  "chrome.prereqChrome": "Requires Chrome installed locally (chrome-devtools-mcp drives the local browser)",
150
+ "bun.missing": "Bun runtime not found \u2014 claude-mem's background worker requires Bun.",
151
+ "bun.installerSource": "Bun installer source: https://bun.sh (curl on macOS/Linux, PowerShell irm on Windows).",
152
+ "bun.confirmInstall": "Auto-install Bun now? (default: No \u2014 declining will skip claude-mem; other packages continue)",
153
+ "bun.declined": "Bun install declined; claude-mem requires Bun and will be skipped this run. Install Bun manually and re-run install.",
154
+ "bun.installing": "Downloading and installing Bun (~60 MB on first run)\u2026",
155
+ "bun.installed": "Bun installed.",
156
+ "bun.installFailedTitle": "Bun install failed",
157
+ "bun.installFailedReason": "Bun install failed: {error}",
158
+ "bun.installedButNotFound": "Bun installer reported success but bun was not found at any known path \u2014 verify manually and re-run.",
141
159
  "reinstall.uninstalling": "Uninstalling old version\u2026",
142
160
  "reinstall.installing": "Installing new version\u2026",
143
161
  "state.checking": "Checking installed state\u2026 (claude plugin list / mcp list)",
@@ -196,10 +214,10 @@ async function initLanguage(override) {
196
214
  }
197
215
 
198
216
  // src/ui/menu.ts
199
- import * as p8 from "@clack/prompts";
217
+ import * as p9 from "@clack/prompts";
200
218
 
201
219
  // src/flows/install.ts
202
- import * as p4 from "@clack/prompts";
220
+ import * as p5 from "@clack/prompts";
203
221
  import pc from "picocolors";
204
222
 
205
223
  // src/runner/state.ts
@@ -218,15 +236,15 @@ async function run(cmd, args) {
218
236
  stderr: result.stderr
219
237
  };
220
238
  }
221
- async function runStreaming(cmd, args, log5) {
222
- log5.message(`$ ${cmd} ${args.join(" ")}`);
239
+ async function runStreaming(cmd, args, log6) {
240
+ log6.message(`$ ${cmd} ${args.join(" ")}`);
223
241
  const proc = x(cmd, args, { throwOnError: false });
224
242
  let stdout = "";
225
243
  for await (const line of proc) {
226
244
  const trimmed = line.replace(/\r?\n$/, "");
227
245
  if (trimmed.length > 0) {
228
246
  stdout += trimmed + "\n";
229
- log5.message(trimmed);
247
+ log6.message(trimmed);
230
248
  }
231
249
  }
232
250
  const finished = await proc;
@@ -269,15 +287,15 @@ async function listPlugins(force = false) {
269
287
  }
270
288
  try {
271
289
  const arr = JSON.parse(res.stdout);
272
- pluginCache = arr.map((p10) => {
273
- const [name = p10.id, marketplace = ""] = p10.id.split("@");
290
+ pluginCache = arr.map((p11) => {
291
+ const [name = p11.id, marketplace = ""] = p11.id.split("@");
274
292
  return {
275
- id: p10.id,
293
+ id: p11.id,
276
294
  name,
277
295
  marketplace,
278
- version: p10.version,
279
- scope: p10.scope,
280
- enabled: p10.enabled
296
+ version: p11.version,
297
+ scope: p11.scope,
298
+ enabled: p11.enabled
281
299
  };
282
300
  });
283
301
  } catch {
@@ -323,7 +341,7 @@ async function listMcp(force = false) {
323
341
  }
324
342
  async function isPluginInstalled(id) {
325
343
  const list = await listPlugins();
326
- return list.some((p10) => p10.id === id);
344
+ return list.some((p11) => p11.id === id);
327
345
  }
328
346
  async function isMarketplaceAdded(name) {
329
347
  const list = await listMarketplaces();
@@ -335,7 +353,7 @@ async function isMcpInstalled(name) {
335
353
  }
336
354
  async function findPlugin(id) {
337
355
  const list = await listPlugins();
338
- return list.find((p10) => p10.id === id);
356
+ return list.find((p11) => p11.id === id);
339
357
  }
340
358
  var marketplaceJsonCache = /* @__PURE__ */ new Map();
341
359
  function marketplaceDir(name) {
@@ -357,7 +375,7 @@ async function readMarketplaceJson(name) {
357
375
  async function getMarketplacePluginVersion(marketplaceName, pluginName) {
358
376
  const m = await readMarketplaceJson(marketplaceName);
359
377
  if (!m?.plugins) return null;
360
- const entry = m.plugins.find((p10) => p10.name === pluginName);
378
+ const entry = m.plugins.find((p11) => p11.name === pluginName);
361
379
  return entry?.version ?? null;
362
380
  }
363
381
  var REFRESH_TTL_MS = 60 * 60 * 1e3;
@@ -478,8 +496,8 @@ var pua = {
478
496
  marketplaces: () => [MARKETPLACE_NAME],
479
497
  isInstalled: () => isPluginInstalled(PLUGIN_ID),
480
498
  installedVersion: async () => {
481
- const p10 = await findPlugin(PLUGIN_ID);
482
- const v = p10?.version;
499
+ const p11 = await findPlugin(PLUGIN_ID);
500
+ const v = p11?.version;
483
501
  return v && v !== "unknown" ? v : null;
484
502
  },
485
503
  latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME, PLUGIN_NAME),
@@ -492,6 +510,72 @@ var pua = {
492
510
  };
493
511
  var pua_default = pua;
494
512
 
513
+ // src/runner/ensureBun.ts
514
+ import { existsSync } from "fs";
515
+ import { homedir } from "os";
516
+ import path2 from "path";
517
+ import * as p2 from "@clack/prompts";
518
+ var IS_WINDOWS = process.platform === "win32";
519
+ function fallbackPaths() {
520
+ const home = homedir();
521
+ if (IS_WINDOWS) return [path2.join(home, ".bun", "bin", "bun.exe")];
522
+ return [
523
+ path2.join(home, ".bun", "bin", "bun"),
524
+ "/usr/local/bin/bun",
525
+ "/opt/homebrew/bin/bun",
526
+ "/home/linuxbrew/.linuxbrew/bin/bun"
527
+ ];
528
+ }
529
+ async function findBun() {
530
+ const probe = IS_WINDOWS ? await run("where", ["bun"]) : await run("which", ["bun"]);
531
+ if (probe.exitCode === 0 && probe.stdout.trim()) {
532
+ const lines = probe.stdout.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
533
+ if (IS_WINDOWS) {
534
+ const cmd = lines.find((l) => l.toLowerCase().endsWith("bun.cmd") || l.toLowerCase().endsWith("bun.exe"));
535
+ if (cmd) return cmd;
536
+ }
537
+ if (lines[0]) return lines[0];
538
+ }
539
+ for (const candidate of fallbackPaths()) {
540
+ if (existsSync(candidate)) return candidate;
541
+ }
542
+ return null;
543
+ }
544
+ async function runInstaller() {
545
+ const result = IS_WINDOWS ? await run("powershell", ["-NoProfile", "-Command", "irm bun.sh/install.ps1 | iex"]) : await run("bash", ["-lc", "curl -fsSL https://bun.sh/install | bash"]);
546
+ if (result.exitCode !== 0) {
547
+ const detail = (result.stderr || result.stdout || "").trim().slice(0, 500);
548
+ return { ok: false, error: detail || `exit ${result.exitCode}` };
549
+ }
550
+ return { ok: true };
551
+ }
552
+ async function ensureBun(t2) {
553
+ const found = await findBun();
554
+ if (found) return { ok: true };
555
+ p2.log.warn(t2("bun.missing"));
556
+ p2.log.info(t2("bun.installerSource"));
557
+ const ans = await p2.confirm({
558
+ message: t2("bun.confirmInstall"),
559
+ initialValue: false
560
+ });
561
+ if (p2.isCancel(ans) || ans === false) {
562
+ return { ok: false, reason: t2("bun.declined") };
563
+ }
564
+ const sp = p2.spinner();
565
+ sp.start(t2("bun.installing"));
566
+ const result = await runInstaller();
567
+ if (!result.ok) {
568
+ sp.stop(t2("bun.installFailedTitle"));
569
+ return { ok: false, reason: t2("bun.installFailedReason", { error: result.error }) };
570
+ }
571
+ sp.stop(t2("bun.installed"));
572
+ const reFound = await findBun();
573
+ if (!reFound) {
574
+ return { ok: false, reason: t2("bun.installedButNotFound") };
575
+ }
576
+ return { ok: true };
577
+ }
578
+
495
579
  // src/registry/plugins/claude-mem.ts
496
580
  var PLUGIN_ID2 = "claude-mem@thedotmack";
497
581
  var PLUGIN_NAME2 = "claude-mem";
@@ -505,10 +589,11 @@ var claudeMem = {
505
589
  slashNamespace: "/claude-mem:*",
506
590
  whenToUse: 'for cross-session memory search ("did we solve this before?"), phased planning (`make-plan`), or phased execution (`do`).',
507
591
  marketplaces: () => [MARKETPLACE_NAME2],
592
+ prereqCheck: (t2) => ensureBun(t2),
508
593
  isInstalled: () => isPluginInstalled(PLUGIN_ID2),
509
594
  installedVersion: async () => {
510
- const p10 = await findPlugin(PLUGIN_ID2);
511
- const v = p10?.version;
595
+ const p11 = await findPlugin(PLUGIN_ID2);
596
+ const v = p11?.version;
512
597
  return v && v !== "unknown" ? v : null;
513
598
  },
514
599
  latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME2, PLUGIN_NAME2),
@@ -522,32 +607,32 @@ var claudeMem = {
522
607
  var claude_mem_default = claudeMem;
523
608
 
524
609
  // src/registry/plugins/chrome-devtools-mcp.ts
525
- import { existsSync } from "fs";
526
- import path2 from "path";
610
+ import { existsSync as existsSync2 } from "fs";
611
+ import path3 from "path";
527
612
  var PLUGIN_ID3 = "chrome-devtools-mcp@chrome-devtools-plugins";
528
613
  var MARKETPLACE_NAME3 = "chrome-devtools-plugins";
529
614
  var MARKETPLACE_SOURCE3 = "ChromeDevTools/chrome-devtools-mcp";
530
615
  async function checkChrome() {
531
- if (process.env.CHROME_PATH && existsSync(process.env.CHROME_PATH)) return true;
616
+ if (process.env.CHROME_PATH && existsSync2(process.env.CHROME_PATH)) return true;
532
617
  if (process.platform === "win32") {
533
618
  const suffixes = [
534
- path2.join("Google", "Chrome SxS", "Application", "chrome.exe"),
535
- path2.join("Google", "Chrome", "Application", "chrome.exe")
619
+ path3.join("Google", "Chrome SxS", "Application", "chrome.exe"),
620
+ path3.join("Google", "Chrome", "Application", "chrome.exe")
536
621
  ];
537
622
  const prefixes = [
538
623
  process.env.LOCALAPPDATA,
539
624
  process.env.PROGRAMFILES,
540
625
  process.env["PROGRAMFILES(X86)"]
541
- ].filter((p10) => Boolean(p10));
626
+ ].filter((p11) => Boolean(p11));
542
627
  for (const prefix of prefixes) {
543
628
  for (const suffix of suffixes) {
544
- if (existsSync(path2.join(prefix, suffix))) return true;
629
+ if (existsSync2(path3.join(prefix, suffix))) return true;
545
630
  }
546
631
  }
547
632
  return false;
548
633
  }
549
634
  if (process.platform === "darwin") {
550
- return existsSync("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
635
+ return existsSync2("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
551
636
  }
552
637
  const [viaPath, viaPathChromium] = await Promise.all([
553
638
  run("which", ["google-chrome"]),
@@ -613,8 +698,8 @@ var curdxFlow = {
613
698
  marketplaces: () => [MARKETPLACE_NAME4],
614
699
  isInstalled: () => isPluginInstalled(PLUGIN_ID5),
615
700
  installedVersion: async () => {
616
- const p10 = await findPlugin(PLUGIN_ID5);
617
- const v = p10?.version;
701
+ const p11 = await findPlugin(PLUGIN_ID5);
702
+ const v = p11?.version;
618
703
  return v && v !== "unknown" ? v : null;
619
704
  },
620
705
  latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME4, PLUGIN_NAME3),
@@ -667,7 +752,7 @@ var sequentialThinking = {
667
752
  var sequential_thinking_default = sequentialThinking;
668
753
 
669
754
  // src/registry/mcps/context7.ts
670
- import * as p2 from "@clack/prompts";
755
+ import * as p3 from "@clack/prompts";
671
756
  var MCP_NAME2 = "context7";
672
757
  var URL = "https://mcp.context7.com/mcp";
673
758
  var context7 = {
@@ -678,14 +763,14 @@ var context7 = {
678
763
  whenToUse: "for any library / SDK / framework / API / Claude Code docs lookup. Use instead of web search.",
679
764
  isInstalled: () => isMcpInstalled(MCP_NAME2),
680
765
  configPrompts: async ({ t: t2 }) => {
681
- p2.note(`${t2("context7.dashboardHint")}
766
+ p3.note(`${t2("context7.dashboardHint")}
682
767
  ${t2("context7.keyWarning")}`, "context7");
683
- const key = await p2.text({
768
+ const key = await p3.text({
684
769
  message: t2("context7.askKey"),
685
770
  placeholder: t2("context7.keyPlaceholder"),
686
771
  defaultValue: ""
687
772
  });
688
- if (p2.isCancel(key)) return null;
773
+ if (p3.isCancel(key)) return null;
689
774
  const trimmed = String(key ?? "").trim();
690
775
  const out = {};
691
776
  if (trimmed) out["CONTEXT7_API_KEY"] = trimmed;
@@ -729,19 +814,19 @@ var PKGS = [
729
814
  context7_default
730
815
  ];
731
816
  function findPkg(id) {
732
- return PKGS.find((p10) => p10.id === id);
817
+ return PKGS.find((p11) => p11.id === id);
733
818
  }
734
819
 
735
820
  // src/runner/claudeMd.ts
736
821
  import { promises as fs2 } from "fs";
737
- import path3 from "path";
822
+ import path4 from "path";
738
823
  import os2 from "os";
739
- import * as p3 from "@clack/prompts";
824
+ import * as p4 from "@clack/prompts";
740
825
  var BEGIN_MARKER = "<!-- BEGIN @curdx/flow v1 -->";
741
826
  var END_MARKER = "<!-- END @curdx/flow v1 -->";
742
827
  var BLOCK_RE = /<!-- BEGIN @curdx\/flow v\d+[^>]*-->[\s\S]*?<!-- END @curdx\/flow v\d+ -->/;
743
828
  function claudeMdPath() {
744
- return path3.join(os2.homedir(), ".claude", "CLAUDE.md");
829
+ return path4.join(os2.homedir(), ".claude", "CLAUDE.md");
745
830
  }
746
831
  function buildCombinationPatterns(ids) {
747
832
  const has = (k) => ids.has(k);
@@ -917,7 +1002,7 @@ async function syncClaudeMd(opts) {
917
1002
  if (next === existing) {
918
1003
  return { status: "unchanged", path: file };
919
1004
  }
920
- await fs2.mkdir(path3.dirname(file), { recursive: true });
1005
+ await fs2.mkdir(path4.dirname(file), { recursive: true });
921
1006
  const tmp = `${file}.tmp.${process.pid}`;
922
1007
  await fs2.writeFile(tmp, next, "utf8");
923
1008
  await fs2.rename(tmp, file);
@@ -931,10 +1016,10 @@ async function syncClaudeMd(opts) {
931
1016
  }
932
1017
  async function syncFromState(opts) {
933
1018
  if (opts?.skip) {
934
- p3.log.info(t("claudeMd.skipped"));
1019
+ p4.log.info(t("claudeMd.skipped"));
935
1020
  return;
936
1021
  }
937
- const sp = p3.spinner();
1022
+ const sp = p4.spinner();
938
1023
  sp.start(t("claudeMd.syncing"));
939
1024
  const r = await syncClaudeMd();
940
1025
  switch (r.status) {
@@ -989,7 +1074,7 @@ async function selectInteractive(states) {
989
1074
  const optionalPkgs = PKGS.filter((pkg) => !pkg.required);
990
1075
  if (requiredPkgs.length > 0) {
991
1076
  const lines = requiredPkgs.map((pkg) => ` ${stateLabel(pkg, states.get(pkg.id))}`);
992
- p4.note(lines.join("\n"), t("install.requiredHeader"));
1077
+ p5.note(lines.join("\n"), t("install.requiredHeader"));
993
1078
  }
994
1079
  const options = optionalPkgs.map((pkg) => {
995
1080
  const s = states.get(pkg.id);
@@ -999,13 +1084,13 @@ async function selectInteractive(states) {
999
1084
  const s = states.get(pkg.id);
1000
1085
  return s.kind === "not_installed" || s.kind === "update_available";
1001
1086
  }).map((pkg) => pkg.id);
1002
- const picked = await p4.multiselect({
1087
+ const picked = await p5.multiselect({
1003
1088
  message: t("install.selectPrompt"),
1004
1089
  options,
1005
1090
  initialValues,
1006
1091
  required: false
1007
1092
  });
1008
- if (p4.isCancel(picked)) return null;
1093
+ if (p5.isCancel(picked)) return null;
1009
1094
  const userPicked = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
1010
1095
  const requiredAuto = requiredPkgs.filter((pkg) => states.get(pkg.id)?.kind !== "up_to_date");
1011
1096
  return [...requiredAuto, ...userPicked];
@@ -1017,7 +1102,7 @@ function selectFromIds(opts) {
1017
1102
  for (const id of opts.ids) {
1018
1103
  const pkg = findPkg(id);
1019
1104
  if (pkg) found.push(pkg);
1020
- else p4.log.warn(`Unknown id: ${id}`);
1105
+ else p5.log.warn(`Unknown id: ${id}`);
1021
1106
  }
1022
1107
  return found;
1023
1108
  }
@@ -1029,11 +1114,11 @@ async function runOne(pkg, state, opts) {
1029
1114
  mode = "update";
1030
1115
  } else {
1031
1116
  if (!opts.yes) {
1032
- const ans = await p4.confirm({
1117
+ const ans = await p5.confirm({
1033
1118
  message: t("install.confirmReinstall", { name: pkg.name }),
1034
1119
  initialValue: false
1035
1120
  });
1036
- if (p4.isCancel(ans) || ans === false) {
1121
+ if (p5.isCancel(ans) || ans === false) {
1037
1122
  return { id: pkg.id, status: "skip", message: t("install.skippedReinstall", { name: pkg.name }) };
1038
1123
  }
1039
1124
  }
@@ -1042,7 +1127,7 @@ async function runOne(pkg, state, opts) {
1042
1127
  if (pkg.prereqCheck) {
1043
1128
  const r = await pkg.prereqCheck(t);
1044
1129
  if (!r.ok) {
1045
- p4.log.warn(t("install.prereqFail", { name: pkg.name, reason: r.reason }));
1130
+ p5.log.warn(t("install.prereqFail", { name: pkg.name, reason: r.reason }));
1046
1131
  return { id: pkg.id, status: "skip", message: r.reason };
1047
1132
  }
1048
1133
  }
@@ -1057,30 +1142,30 @@ async function runOne(pkg, state, opts) {
1057
1142
  if (mode === "update" && state.kind === "update_available") {
1058
1143
  titleVars["version"] = state.latest;
1059
1144
  }
1060
- const log5 = p4.taskLog({ title: t(titleKey, titleVars) });
1145
+ const log6 = p5.taskLog({ title: t(titleKey, titleVars) });
1061
1146
  try {
1062
1147
  if (mode === "reinstall") {
1063
- log5.message(t("reinstall.uninstalling"));
1064
- await pkg.uninstall({ log: log5, config, t });
1065
- log5.message(t("reinstall.installing"));
1066
- await pkg.install({ log: log5, config, t });
1148
+ log6.message(t("reinstall.uninstalling"));
1149
+ await pkg.uninstall({ log: log6, config, t });
1150
+ log6.message(t("reinstall.installing"));
1151
+ await pkg.install({ log: log6, config, t });
1067
1152
  } else if (mode === "update") {
1068
1153
  if (pkg.update) {
1069
- await pkg.update({ log: log5, config, t });
1154
+ await pkg.update({ log: log6, config, t });
1070
1155
  } else {
1071
- log5.message(t("reinstall.uninstalling"));
1072
- await pkg.uninstall({ log: log5, config, t });
1073
- log5.message(t("reinstall.installing"));
1074
- await pkg.install({ log: log5, config, t });
1156
+ log6.message(t("reinstall.uninstalling"));
1157
+ await pkg.uninstall({ log: log6, config, t });
1158
+ log6.message(t("reinstall.installing"));
1159
+ await pkg.install({ log: log6, config, t });
1075
1160
  }
1076
1161
  } else {
1077
- await pkg.install({ log: log5, config, t });
1162
+ await pkg.install({ log: log6, config, t });
1078
1163
  }
1079
- log5.success(t("install.success", { name: pkg.name }));
1164
+ log6.success(t("install.success", { name: pkg.name }));
1080
1165
  return { id: pkg.id, status: "ok" };
1081
1166
  } catch (err) {
1082
1167
  const msg = err instanceof Error ? err.message : String(err);
1083
- log5.error(`${t("install.failed", { name: pkg.name })}
1168
+ log6.error(`${t("install.failed", { name: pkg.name })}
1084
1169
  ${msg}`);
1085
1170
  return { id: pkg.id, status: "fail", message: msg };
1086
1171
  }
@@ -1098,7 +1183,7 @@ function summarize(results) {
1098
1183
  ...skip.map((r) => ` ${pc.yellow("-")} ${r.id}${r.message ? pc.dim(` (${r.message})`) : ""}`),
1099
1184
  ...fail.map((r) => ` ${pc.red("\u2717")} ${r.id}${r.message ? pc.dim(` (${r.message.split("\n")[0]})`) : ""}`)
1100
1185
  ];
1101
- p4.note(lines.join("\n"), t("install.summaryTitle"));
1186
+ p5.note(lines.join("\n"), t("install.summaryTitle"));
1102
1187
  }
1103
1188
  async function maybeRefreshMarketplaces(opts) {
1104
1189
  if (opts.noRefresh) return;
@@ -1107,7 +1192,7 @@ async function maybeRefreshMarketplaces(opts) {
1107
1192
  if (pkg.marketplaces) for (const n of pkg.marketplaces()) names.add(n);
1108
1193
  }
1109
1194
  if (names.size === 0) return;
1110
- const sp = p4.spinner();
1195
+ const sp = p5.spinner();
1111
1196
  sp.start(t("marketplace.refreshing"));
1112
1197
  const refreshed = await refreshMarketplaces([...names]);
1113
1198
  sp.stop(
@@ -1121,7 +1206,7 @@ async function installFlow(opts = {}) {
1121
1206
  const explicit = opts.all || opts.ids && opts.ids.length > 0;
1122
1207
  const candidates = explicit ? selectFromIds(opts) : [...PKGS];
1123
1208
  if (candidates.length === 0) {
1124
- p4.log.info(t("install.nothingSelected"));
1209
+ p5.log.info(t("install.nothingSelected"));
1125
1210
  return;
1126
1211
  }
1127
1212
  if (opts.ids && opts.ids.length > 0) {
@@ -1132,7 +1217,7 @@ async function installFlow(opts = {}) {
1132
1217
  }
1133
1218
  }
1134
1219
  const stateMap = /* @__PURE__ */ new Map();
1135
- const sp = p4.spinner();
1220
+ const sp = p5.spinner();
1136
1221
  sp.start(t("state.checking"));
1137
1222
  try {
1138
1223
  await Promise.all([listPlugins(), listMcp()]);
@@ -1160,13 +1245,13 @@ async function installFlow(opts = {}) {
1160
1245
  const picked = await selectInteractive(stateMap);
1161
1246
  if (picked === null) {
1162
1247
  userCancelled = true;
1163
- p4.cancel(t("app.cancelled"));
1248
+ p5.cancel(t("app.cancelled"));
1164
1249
  return;
1165
1250
  }
1166
1251
  targets = picked;
1167
1252
  }
1168
1253
  if (targets.length === 0) {
1169
- p4.log.info(t("install.nothingSelected"));
1254
+ p5.log.info(t("install.nothingSelected"));
1170
1255
  return;
1171
1256
  }
1172
1257
  const results = [];
@@ -1183,14 +1268,14 @@ async function installFlow(opts = {}) {
1183
1268
  }
1184
1269
 
1185
1270
  // src/flows/uninstall.ts
1186
- import * as p5 from "@clack/prompts";
1271
+ import * as p6 from "@clack/prompts";
1187
1272
  import pc2 from "picocolors";
1188
1273
  async function getInstalled() {
1189
1274
  const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
1190
1275
  return states.filter((s) => s.installed).map((s) => s.pkg);
1191
1276
  }
1192
1277
  async function probeInstalled() {
1193
- const sp = p5.spinner();
1278
+ const sp = p6.spinner();
1194
1279
  sp.start(t("state.checking"));
1195
1280
  try {
1196
1281
  await Promise.all([listPlugins(), listMcp()]);
@@ -1212,21 +1297,21 @@ async function uninstallFlow(opts = {}) {
1212
1297
  for (const id of opts.ids) {
1213
1298
  const pkg = findPkg(id);
1214
1299
  if (!pkg) {
1215
- p5.log.warn(`Unknown id: ${id}`);
1300
+ p6.log.warn(`Unknown id: ${id}`);
1216
1301
  continue;
1217
1302
  }
1218
1303
  if (!installed.some((x2) => x2.id === pkg.id)) {
1219
- p5.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1304
+ p6.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1220
1305
  continue;
1221
1306
  }
1222
1307
  targets.push(pkg);
1223
1308
  }
1224
1309
  } else {
1225
1310
  if (installed.length === 0) {
1226
- p5.log.info(t("uninstall.noneInstalled"));
1311
+ p6.log.info(t("uninstall.noneInstalled"));
1227
1312
  return;
1228
1313
  }
1229
- const picked = await p5.multiselect({
1314
+ const picked = await p6.multiselect({
1230
1315
  message: t("uninstall.selectPrompt"),
1231
1316
  options: installed.map((pkg) => ({
1232
1317
  value: pkg.id,
@@ -1235,45 +1320,45 @@ async function uninstallFlow(opts = {}) {
1235
1320
  })),
1236
1321
  required: false
1237
1322
  });
1238
- if (p5.isCancel(picked)) {
1323
+ if (p6.isCancel(picked)) {
1239
1324
  userCancelled = true;
1240
- p5.cancel(t("app.cancelled"));
1325
+ p6.cancel(t("app.cancelled"));
1241
1326
  return;
1242
1327
  }
1243
1328
  targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
1244
1329
  }
1245
1330
  if (targets.length === 0) {
1246
- p5.log.info(t("install.nothingSelected"));
1331
+ p6.log.info(t("install.nothingSelected"));
1247
1332
  return;
1248
1333
  }
1249
1334
  if (!opts.yes) {
1250
- const ok2 = await p5.confirm({
1335
+ const ok2 = await p6.confirm({
1251
1336
  message: t("uninstall.confirm", { count: targets.length }),
1252
1337
  initialValue: false
1253
1338
  });
1254
- if (p5.isCancel(ok2) || ok2 === false) {
1339
+ if (p6.isCancel(ok2) || ok2 === false) {
1255
1340
  userCancelled = true;
1256
- p5.cancel(t("app.cancelled"));
1341
+ p6.cancel(t("app.cancelled"));
1257
1342
  return;
1258
1343
  }
1259
1344
  }
1260
1345
  const results = [];
1261
1346
  for (const pkg of targets) {
1262
- const log5 = p5.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
1347
+ const log6 = p6.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
1263
1348
  try {
1264
- await pkg.uninstall({ log: log5, config: {}, t });
1265
- log5.success(t("uninstall.success", { name: pkg.name }));
1349
+ await pkg.uninstall({ log: log6, config: {}, t });
1350
+ log6.success(t("uninstall.success", { name: pkg.name }));
1266
1351
  results.push({ id: pkg.id, status: "ok" });
1267
1352
  } catch (err) {
1268
1353
  const msg = err instanceof Error ? err.message : String(err);
1269
- log5.error(`${t("uninstall.failed", { name: pkg.name })}
1354
+ log6.error(`${t("uninstall.failed", { name: pkg.name })}
1270
1355
  ${msg}`);
1271
1356
  results.push({ id: pkg.id, status: "fail", message: msg });
1272
1357
  }
1273
1358
  }
1274
1359
  const ok = results.filter((r) => r.status === "ok").length;
1275
1360
  const fail = results.filter((r) => r.status === "fail").length;
1276
- p5.note(
1361
+ p6.note(
1277
1362
  [
1278
1363
  pc2.green(t("install.summaryOk", { count: ok })),
1279
1364
  pc2.red(t("install.summaryFail", { count: fail }))
@@ -1288,14 +1373,14 @@ ${msg}`);
1288
1373
  }
1289
1374
 
1290
1375
  // src/flows/update.ts
1291
- import * as p6 from "@clack/prompts";
1376
+ import * as p7 from "@clack/prompts";
1292
1377
  import pc3 from "picocolors";
1293
1378
  async function getInstalled2() {
1294
1379
  const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
1295
1380
  return states.filter((s) => s.installed).map((s) => s.pkg);
1296
1381
  }
1297
1382
  async function probeInstalled2() {
1298
- const sp = p6.spinner();
1383
+ const sp = p7.spinner();
1299
1384
  sp.start(t("state.checking"));
1300
1385
  try {
1301
1386
  await Promise.all([listPlugins(), listMcp()]);
@@ -1312,7 +1397,7 @@ async function updateFlow(opts = {}) {
1312
1397
  try {
1313
1398
  const installed = await probeInstalled2();
1314
1399
  if (installed.length === 0) {
1315
- p6.log.info(t("update.noneInstalled"));
1400
+ p7.log.info(t("update.noneInstalled"));
1316
1401
  return;
1317
1402
  }
1318
1403
  let targets;
@@ -1323,17 +1408,17 @@ async function updateFlow(opts = {}) {
1323
1408
  for (const id of opts.ids) {
1324
1409
  const pkg = findPkg(id);
1325
1410
  if (!pkg) {
1326
- p6.log.warn(`Unknown id: ${id}`);
1411
+ p7.log.warn(`Unknown id: ${id}`);
1327
1412
  continue;
1328
1413
  }
1329
1414
  if (!installed.some((x2) => x2.id === pkg.id)) {
1330
- p6.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1415
+ p7.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1331
1416
  continue;
1332
1417
  }
1333
1418
  targets.push(pkg);
1334
1419
  }
1335
1420
  } else {
1336
- const picked = await p6.multiselect({
1421
+ const picked = await p7.multiselect({
1337
1422
  message: t("update.selectPrompt"),
1338
1423
  options: installed.map((pkg) => ({
1339
1424
  value: pkg.id,
@@ -1342,42 +1427,42 @@ async function updateFlow(opts = {}) {
1342
1427
  })),
1343
1428
  required: false
1344
1429
  });
1345
- if (p6.isCancel(picked)) {
1430
+ if (p7.isCancel(picked)) {
1346
1431
  userCancelled = true;
1347
- p6.cancel(t("app.cancelled"));
1432
+ p7.cancel(t("app.cancelled"));
1348
1433
  return;
1349
1434
  }
1350
1435
  targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
1351
1436
  }
1352
1437
  if (targets.length === 0) {
1353
- p6.log.info(t("install.nothingSelected"));
1438
+ p7.log.info(t("install.nothingSelected"));
1354
1439
  return;
1355
1440
  }
1356
1441
  const results = [];
1357
1442
  for (const pkg of targets) {
1358
1443
  if (pkg.id === "sequential-thinking") {
1359
- p6.log.info(t("update.mcpAutoNote", { name: pkg.name }));
1444
+ p7.log.info(t("update.mcpAutoNote", { name: pkg.name }));
1360
1445
  results.push({ id: pkg.id, status: "noop" });
1361
1446
  continue;
1362
1447
  }
1363
1448
  if (pkg.id === "context7") {
1364
- p6.log.info(t("update.context7Note"));
1449
+ p7.log.info(t("update.context7Note"));
1365
1450
  results.push({ id: pkg.id, status: "noop" });
1366
1451
  continue;
1367
1452
  }
1368
- const log5 = p6.taskLog({ title: t("update.starting", { name: pkg.name }) });
1453
+ const log6 = p7.taskLog({ title: t("update.starting", { name: pkg.name }) });
1369
1454
  try {
1370
1455
  if (pkg.update) {
1371
- await pkg.update({ log: log5, config: {}, t });
1456
+ await pkg.update({ log: log6, config: {}, t });
1372
1457
  } else {
1373
- await pkg.uninstall({ log: log5, config: {}, t });
1374
- await pkg.install({ log: log5, config: {}, t });
1458
+ await pkg.uninstall({ log: log6, config: {}, t });
1459
+ await pkg.install({ log: log6, config: {}, t });
1375
1460
  }
1376
- log5.success(t("update.success", { name: pkg.name }));
1461
+ log6.success(t("update.success", { name: pkg.name }));
1377
1462
  results.push({ id: pkg.id, status: "ok" });
1378
1463
  } catch (err) {
1379
1464
  const msg = err instanceof Error ? err.message : String(err);
1380
- log5.error(`${t("update.failed", { name: pkg.name })}
1465
+ log6.error(`${t("update.failed", { name: pkg.name })}
1381
1466
  ${msg}`);
1382
1467
  results.push({ id: pkg.id, status: "fail", message: msg });
1383
1468
  }
@@ -1385,7 +1470,7 @@ ${msg}`);
1385
1470
  const ok = results.filter((r) => r.status === "ok").length;
1386
1471
  const fail = results.filter((r) => r.status === "fail").length;
1387
1472
  const noop = results.filter((r) => r.status === "noop").length;
1388
- p6.note(
1473
+ p7.note(
1389
1474
  [
1390
1475
  pc3.green(t("install.summaryOk", { count: ok })),
1391
1476
  pc3.red(t("install.summaryFail", { count: fail })),
@@ -1401,7 +1486,7 @@ ${msg}`);
1401
1486
  }
1402
1487
 
1403
1488
  // src/flows/status.ts
1404
- import * as p7 from "@clack/prompts";
1489
+ import * as p8 from "@clack/prompts";
1405
1490
  import pc4 from "picocolors";
1406
1491
  async function statusFlow(opts = {}) {
1407
1492
  const states = await Promise.all(
@@ -1434,12 +1519,12 @@ async function statusFlow(opts = {}) {
1434
1519
  const rows = states.map(
1435
1520
  (s) => `${s.name.padEnd(nameW)} ${s.type.padEnd(typeW)} ${s.installed ? pc4.green(`\u2713 ${t("pkg.installed")}`) : pc4.yellow(`\u2717 ${t("pkg.notInstalled")}`)}`
1436
1521
  );
1437
- p7.note([header, sep, ...rows].join("\n"), t("status.title"));
1522
+ p8.note([header, sep, ...rows].join("\n"), t("status.title"));
1438
1523
  }
1439
1524
 
1440
1525
  // src/ui/menu.ts
1441
1526
  async function mainMenu() {
1442
- const action = await p8.select({
1527
+ const action = await p9.select({
1443
1528
  message: t("menu.title"),
1444
1529
  options: [
1445
1530
  { value: "install", label: t("menu.install") },
@@ -1449,8 +1534,8 @@ async function mainMenu() {
1449
1534
  { value: "exit", label: t("menu.exit") }
1450
1535
  ]
1451
1536
  });
1452
- if (p8.isCancel(action) || action === "exit") {
1453
- p8.cancel(t("app.cancelled"));
1537
+ if (p9.isCancel(action) || action === "exit") {
1538
+ p9.cancel(t("app.cancelled"));
1454
1539
  return;
1455
1540
  }
1456
1541
  switch (action) {
@@ -1495,7 +1580,7 @@ var installCmd = defineCommand({
1495
1580
  },
1496
1581
  async run({ args }) {
1497
1582
  await initLanguage(parseLang(args.lang));
1498
- p9.intro(t("app.intro"));
1583
+ p10.intro(t("app.intro"));
1499
1584
  const ids = collectPositional(args);
1500
1585
  await installFlow({
1501
1586
  ids,
@@ -1504,7 +1589,7 @@ var installCmd = defineCommand({
1504
1589
  noRefresh: Boolean(args["no-refresh"]),
1505
1590
  noClaudeMd: noClaudeMdFromArgs(args)
1506
1591
  });
1507
- p9.outro(t("app.outro"));
1592
+ p10.outro(t("app.outro"));
1508
1593
  }
1509
1594
  });
1510
1595
  var uninstallCmd = defineCommand({
@@ -1516,14 +1601,14 @@ var uninstallCmd = defineCommand({
1516
1601
  },
1517
1602
  async run({ args }) {
1518
1603
  await initLanguage(parseLang(args.lang));
1519
- p9.intro(t("app.intro"));
1604
+ p10.intro(t("app.intro"));
1520
1605
  const ids = collectPositional(args);
1521
1606
  await uninstallFlow({
1522
1607
  ids,
1523
1608
  yes: Boolean(args.yes),
1524
1609
  noClaudeMd: noClaudeMdFromArgs(args)
1525
1610
  });
1526
- p9.outro(t("app.outro"));
1611
+ p10.outro(t("app.outro"));
1527
1612
  }
1528
1613
  });
1529
1614
  var updateCmd = defineCommand({
@@ -1535,14 +1620,14 @@ var updateCmd = defineCommand({
1535
1620
  },
1536
1621
  async run({ args }) {
1537
1622
  await initLanguage(parseLang(args.lang));
1538
- p9.intro(t("app.intro"));
1623
+ p10.intro(t("app.intro"));
1539
1624
  const ids = collectPositional(args);
1540
1625
  await updateFlow({
1541
1626
  ids,
1542
1627
  all: Boolean(args.all),
1543
1628
  noClaudeMd: noClaudeMdFromArgs(args)
1544
1629
  });
1545
- p9.outro(t("app.outro"));
1630
+ p10.outro(t("app.outro"));
1546
1631
  }
1547
1632
  });
1548
1633
  var statusCmd = defineCommand({
@@ -1553,9 +1638,9 @@ var statusCmd = defineCommand({
1553
1638
  },
1554
1639
  async run({ args }) {
1555
1640
  await initLanguage(parseLang(args.lang));
1556
- if (!args.json) p9.intro(t("app.intro"));
1641
+ if (!args.json) p10.intro(t("app.intro"));
1557
1642
  await statusFlow({ json: Boolean(args.json) });
1558
- if (!args.json) p9.outro(t("app.outro"));
1643
+ if (!args.json) p10.outro(t("app.outro"));
1559
1644
  }
1560
1645
  });
1561
1646
  var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "status"]);
@@ -1597,9 +1682,9 @@ async function runInteractive(argv2) {
1597
1682
  else if (argv2[i]?.startsWith("--lang=")) lang = parseLang(argv2[i].slice("--lang=".length));
1598
1683
  }
1599
1684
  await initLanguage(lang);
1600
- p9.intro(t("app.intro"));
1685
+ p10.intro(t("app.intro"));
1601
1686
  await mainMenu();
1602
- p9.outro(t("app.outro"));
1687
+ p10.outro(t("app.outro"));
1603
1688
  }
1604
1689
  var argv = process.argv.slice(2);
1605
1690
  var first = firstNonFlag(argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curdx/flow",
3
- "version": "7.1.1",
3
+ "version": "7.1.2",
4
4
  "description": "Interactive installer for Claude Code plugins and MCP servers",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.mjs",