@curdx/flow 7.1.1 → 7.1.3

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/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import * as p9 from "@clack/prompts";
5
- import { defineCommand, runMain } from "citty";
4
+ import * as p10 from "@clack/prompts";
5
+ import { defineCommand as defineCommand2, runMain } from "citty";
6
6
 
7
7
  // src/ui/language.ts
8
8
  import * as p from "@clack/prompts";
@@ -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",
@@ -75,7 +84,17 @@ var messages = {
75
84
  "claudeMd.unchanged": "CLAUDE.md \u5DF2\u662F\u6700\u65B0",
76
85
  "claudeMd.removed": "\u5DF2\u4ECE CLAUDE.md \u79FB\u9664 @curdx/flow \u533A\u5757",
77
86
  "claudeMd.skipped": "\u5DF2\u8DF3\u8FC7 CLAUDE.md \u540C\u6B65\uFF08--no-claude-md\uFF09",
78
- "claudeMd.failed": "CLAUDE.md \u540C\u6B65\u5931\u8D25\uFF1A{error}"
87
+ "claudeMd.failed": "CLAUDE.md \u540C\u6B65\u5931\u8D25\uFF1A{error}",
88
+ "analyze.description": "\u89E3\u6790 Claude Code session jsonl + curdx-flow errors.jsonl\uFF0C\u8F93\u51FA markdown \u62A5\u544A",
89
+ "analyze.helpSummary": "analyze \u2014 \u672C\u5730\u89C2\u6D4B\uFF1A\u5408\u5E76 ~/.claude/projects/<cwd-encoded>/<sessionId>.jsonl \u4E0E ~/.claude/curdx-flow/errors.jsonl\uFF0C\u6E32\u67D3 7 \u6BB5 markdown\uFF08hook \u5931\u8D25 / slash command \u9891\u6B21 / subagent \u8C03\u5EA6 / spec \u6F0F\u6597 / hook \u65F6\u5EF6 P50/P95/P99 / schema \u6F02\u79FB / parentUuid \u94FE\u5B8C\u6574\u6027\uFF09",
90
+ "analyze.flags.json": "\u8F93\u51FA JSON \u800C\u975E markdown\uFF08CI \u53CB\u597D\uFF1B\u4E0E --out \u4E92\u65A5\u8BED\u4E49\uFF1AJSON \u4E0D\u5199\u6587\u4EF6\uFF09",
91
+ "analyze.flags.out": "\u628A markdown \u62A5\u544A\u5199\u5165\u6307\u5B9A\u6587\u4EF6\uFF08\u7F3A\u7701\u5199\u5230 stdout\uFF09",
92
+ "analyze.flags.limit": "\u8868\u683C\u6BB5 Top-N \u622A\u65AD\uFF08\u9ED8\u8BA4 10\uFF1B\u4F20 0 \u89C6\u4E3A\u65E0\u610F\u4E49\u56DE\u843D 10\uFF09",
93
+ "analyze.flags.since": "\u53EA\u7EDF\u8BA1\u76F8\u5BF9\u7A97\u53E3\u5185\u7684\u4E8B\u4EF6\uFF0C\u4F8B\u5982 `7d` / `24h` / `30m`\uFF08\u9ED8\u8BA4\u5168\u91CF\uFF1B--since \u4E0E\u589E\u91CF offset \u7F13\u5B58\u5171\u5B58\uFF0C\u7F13\u5B58\u547D\u4E2D\u65F6\u76F4\u63A5 replay \u4E0A\u6B21\u62A5\u544A\uFF09",
94
+ "analyze.flags.project": "\u6307\u5B9A ~/.claude/projects/ \u4E0B\u7684 encoded-cwd \u5B50\u76EE\u5F55\uFF1B\u7F3A\u7701\u6309\u5F53\u524D git \u4ED3\u5E93\u6839\u76EE\u5F55\u63A8\u65AD\uFF08\u975E git \u76EE\u5F55\u9000\u5316\u4E3A\u7A7A\u62A5\u544A + warning\uFF09",
95
+ "analyze.flags.includePrompts": "\u5173\u95ED\u9ED8\u8BA4 redact\uFF0C\u628A\u539F\u59CB prompt \u6587\u672C\u900F\u51FA\uFF08\u4EC5\u672C\u5730\u8C03\u8BD5\uFF0C\u8C28\u614E\u4F7F\u7528\uFF1BD-9 \u767D\u540D\u5355\u9ED8\u8BA4\u88C1\u526A\u6240\u6709\u672A\u5728\u767D\u540D\u5355\u5B57\u6BB5\uFF09",
96
+ "analyze.warning.noProject": "\u672A\u80FD\u4ECE cwd \u63A8\u65AD\u5230 ~/.claude/projects/ \u5B50\u76EE\u5F55\uFF08\u975E git \u4ED3\u5E93\u6216\u65E0\u5BF9\u5E94 session\uFF09\uFF1B\u8F93\u51FA\u7A7A\u62A5\u544A\uFF0C\u8BF7\u7528 --project \u663E\u5F0F\u6307\u5B9A",
97
+ "analyze.warning.schemaFallback": "plugins/curdx-flow/schemas/transcript-events.json \u672A\u627E\u5230\uFF0C\u56DE\u843D\u5230\u5185\u7F6E\u6700\u5C0F\u767D\u540D\u5355\u2014\u2014schema \u6F02\u79FB\u8BCA\u65AD\u53EF\u80FD\u6F0F\u62A5"
79
98
  };
80
99
  var zh_default = messages;
81
100
 
@@ -138,6 +157,15 @@ var messages2 = {
138
157
  "context7.dashboardHint": "Get a key at https://context7.com/dashboard",
139
158
  "chrome.prereqNode": "Requires Node.js >= 20.19 (current: {current})",
140
159
  "chrome.prereqChrome": "Requires Chrome installed locally (chrome-devtools-mcp drives the local browser)",
160
+ "bun.missing": "Bun runtime not found \u2014 claude-mem's background worker requires Bun.",
161
+ "bun.installerSource": "Bun installer source: https://bun.sh (curl on macOS/Linux, PowerShell irm on Windows).",
162
+ "bun.confirmInstall": "Auto-install Bun now? (default: No \u2014 declining will skip claude-mem; other packages continue)",
163
+ "bun.declined": "Bun install declined; claude-mem requires Bun and will be skipped this run. Install Bun manually and re-run install.",
164
+ "bun.installing": "Downloading and installing Bun (~60 MB on first run)\u2026",
165
+ "bun.installed": "Bun installed.",
166
+ "bun.installFailedTitle": "Bun install failed",
167
+ "bun.installFailedReason": "Bun install failed: {error}",
168
+ "bun.installedButNotFound": "Bun installer reported success but bun was not found at any known path \u2014 verify manually and re-run.",
141
169
  "reinstall.uninstalling": "Uninstalling old version\u2026",
142
170
  "reinstall.installing": "Installing new version\u2026",
143
171
  "state.checking": "Checking installed state\u2026 (claude plugin list / mcp list)",
@@ -147,7 +175,17 @@ var messages2 = {
147
175
  "claudeMd.unchanged": "CLAUDE.md already up to date",
148
176
  "claudeMd.removed": "Removed @curdx/flow block from CLAUDE.md",
149
177
  "claudeMd.skipped": "Skipped CLAUDE.md sync (--no-claude-md)",
150
- "claudeMd.failed": "CLAUDE.md sync failed: {error}"
178
+ "claudeMd.failed": "CLAUDE.md sync failed: {error}",
179
+ "analyze.description": "Analyze Claude Code session jsonl + curdx-flow errors.jsonl, output markdown report",
180
+ "analyze.helpSummary": "analyze \u2014 local observability: merge ~/.claude/projects/<cwd-encoded>/<sessionId>.jsonl with ~/.claude/curdx-flow/errors.jsonl into 7 markdown sections (hook failures / slash command frequency / subagent dispatch / spec funnel / hook duration P50/P95/P99 / schema drift / parentUuid chain integrity)",
181
+ "analyze.flags.json": "Emit JSON instead of markdown (CI-friendly; --out is ignored when --json is set)",
182
+ "analyze.flags.out": "Write the markdown report to the given file path (defaults to stdout)",
183
+ "analyze.flags.limit": "Top-N truncation for tabular sections (default 10; 0 falls back to 10)",
184
+ "analyze.flags.since": "Only count events within a relative window such as `7d` / `24h` / `30m` (default: full history; --since coexists with the incremental offset cache, replays the last report when cache hits)",
185
+ "analyze.flags.project": "Pin to a specific encoded-cwd directory under ~/.claude/projects/; defaults to inferring from the current git repository root (non-git directories degrade to an empty report with a warning)",
186
+ "analyze.flags.includePrompts": "Skip default redaction and pass raw prompt text through (local debugging only; D-9 white-list redacts every non-whitelisted field by default)",
187
+ "analyze.warning.noProject": "Could not infer a ~/.claude/projects/ subdirectory from cwd (not a git repo or no matching session); emitting an empty report \u2014 pass --project to override",
188
+ "analyze.warning.schemaFallback": "plugins/curdx-flow/schemas/transcript-events.json not found, falling back to the builtin minimal whitelist \u2014 schema drift diagnostics may underreport"
151
189
  };
152
190
  var en_default = messages2;
153
191
 
@@ -196,10 +234,10 @@ async function initLanguage(override) {
196
234
  }
197
235
 
198
236
  // src/ui/menu.ts
199
- import * as p8 from "@clack/prompts";
237
+ import * as p9 from "@clack/prompts";
200
238
 
201
239
  // src/flows/install.ts
202
- import * as p4 from "@clack/prompts";
240
+ import * as p5 from "@clack/prompts";
203
241
  import pc from "picocolors";
204
242
 
205
243
  // src/runner/state.ts
@@ -218,15 +256,15 @@ async function run(cmd, args) {
218
256
  stderr: result.stderr
219
257
  };
220
258
  }
221
- async function runStreaming(cmd, args, log5) {
222
- log5.message(`$ ${cmd} ${args.join(" ")}`);
259
+ async function runStreaming(cmd, args, log6) {
260
+ log6.message(`$ ${cmd} ${args.join(" ")}`);
223
261
  const proc = x(cmd, args, { throwOnError: false });
224
262
  let stdout = "";
225
263
  for await (const line of proc) {
226
264
  const trimmed = line.replace(/\r?\n$/, "");
227
265
  if (trimmed.length > 0) {
228
266
  stdout += trimmed + "\n";
229
- log5.message(trimmed);
267
+ log6.message(trimmed);
230
268
  }
231
269
  }
232
270
  const finished = await proc;
@@ -269,15 +307,15 @@ async function listPlugins(force = false) {
269
307
  }
270
308
  try {
271
309
  const arr = JSON.parse(res.stdout);
272
- pluginCache = arr.map((p10) => {
273
- const [name = p10.id, marketplace = ""] = p10.id.split("@");
310
+ pluginCache = arr.map((p11) => {
311
+ const [name = p11.id, marketplace = ""] = p11.id.split("@");
274
312
  return {
275
- id: p10.id,
313
+ id: p11.id,
276
314
  name,
277
315
  marketplace,
278
- version: p10.version,
279
- scope: p10.scope,
280
- enabled: p10.enabled
316
+ version: p11.version,
317
+ scope: p11.scope,
318
+ enabled: p11.enabled
281
319
  };
282
320
  });
283
321
  } catch {
@@ -323,7 +361,7 @@ async function listMcp(force = false) {
323
361
  }
324
362
  async function isPluginInstalled(id) {
325
363
  const list = await listPlugins();
326
- return list.some((p10) => p10.id === id);
364
+ return list.some((p11) => p11.id === id);
327
365
  }
328
366
  async function isMarketplaceAdded(name) {
329
367
  const list = await listMarketplaces();
@@ -335,7 +373,7 @@ async function isMcpInstalled(name) {
335
373
  }
336
374
  async function findPlugin(id) {
337
375
  const list = await listPlugins();
338
- return list.find((p10) => p10.id === id);
376
+ return list.find((p11) => p11.id === id);
339
377
  }
340
378
  var marketplaceJsonCache = /* @__PURE__ */ new Map();
341
379
  function marketplaceDir(name) {
@@ -357,7 +395,7 @@ async function readMarketplaceJson(name) {
357
395
  async function getMarketplacePluginVersion(marketplaceName, pluginName) {
358
396
  const m = await readMarketplaceJson(marketplaceName);
359
397
  if (!m?.plugins) return null;
360
- const entry = m.plugins.find((p10) => p10.name === pluginName);
398
+ const entry = m.plugins.find((p11) => p11.name === pluginName);
361
399
  return entry?.version ?? null;
362
400
  }
363
401
  var REFRESH_TTL_MS = 60 * 60 * 1e3;
@@ -478,8 +516,8 @@ var pua = {
478
516
  marketplaces: () => [MARKETPLACE_NAME],
479
517
  isInstalled: () => isPluginInstalled(PLUGIN_ID),
480
518
  installedVersion: async () => {
481
- const p10 = await findPlugin(PLUGIN_ID);
482
- const v = p10?.version;
519
+ const p11 = await findPlugin(PLUGIN_ID);
520
+ const v = p11?.version;
483
521
  return v && v !== "unknown" ? v : null;
484
522
  },
485
523
  latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME, PLUGIN_NAME),
@@ -492,6 +530,72 @@ var pua = {
492
530
  };
493
531
  var pua_default = pua;
494
532
 
533
+ // src/runner/ensureBun.ts
534
+ import { existsSync } from "fs";
535
+ import { homedir } from "os";
536
+ import path2 from "path";
537
+ import * as p2 from "@clack/prompts";
538
+ var IS_WINDOWS = process.platform === "win32";
539
+ function fallbackPaths() {
540
+ const home = homedir();
541
+ if (IS_WINDOWS) return [path2.join(home, ".bun", "bin", "bun.exe")];
542
+ return [
543
+ path2.join(home, ".bun", "bin", "bun"),
544
+ "/usr/local/bin/bun",
545
+ "/opt/homebrew/bin/bun",
546
+ "/home/linuxbrew/.linuxbrew/bin/bun"
547
+ ];
548
+ }
549
+ async function findBun() {
550
+ const probe = IS_WINDOWS ? await run("where", ["bun"]) : await run("which", ["bun"]);
551
+ if (probe.exitCode === 0 && probe.stdout.trim()) {
552
+ const lines = probe.stdout.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
553
+ if (IS_WINDOWS) {
554
+ const cmd = lines.find((l) => l.toLowerCase().endsWith("bun.cmd") || l.toLowerCase().endsWith("bun.exe"));
555
+ if (cmd) return cmd;
556
+ }
557
+ if (lines[0]) return lines[0];
558
+ }
559
+ for (const candidate of fallbackPaths()) {
560
+ if (existsSync(candidate)) return candidate;
561
+ }
562
+ return null;
563
+ }
564
+ async function runInstaller() {
565
+ 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"]);
566
+ if (result.exitCode !== 0) {
567
+ const detail = (result.stderr || result.stdout || "").trim().slice(0, 500);
568
+ return { ok: false, error: detail || `exit ${result.exitCode}` };
569
+ }
570
+ return { ok: true };
571
+ }
572
+ async function ensureBun(t2) {
573
+ const found = await findBun();
574
+ if (found) return { ok: true };
575
+ p2.log.warn(t2("bun.missing"));
576
+ p2.log.info(t2("bun.installerSource"));
577
+ const ans = await p2.confirm({
578
+ message: t2("bun.confirmInstall"),
579
+ initialValue: false
580
+ });
581
+ if (p2.isCancel(ans) || ans === false) {
582
+ return { ok: false, reason: t2("bun.declined") };
583
+ }
584
+ const sp = p2.spinner();
585
+ sp.start(t2("bun.installing"));
586
+ const result = await runInstaller();
587
+ if (!result.ok) {
588
+ sp.stop(t2("bun.installFailedTitle"));
589
+ return { ok: false, reason: t2("bun.installFailedReason", { error: result.error }) };
590
+ }
591
+ sp.stop(t2("bun.installed"));
592
+ const reFound = await findBun();
593
+ if (!reFound) {
594
+ return { ok: false, reason: t2("bun.installedButNotFound") };
595
+ }
596
+ return { ok: true };
597
+ }
598
+
495
599
  // src/registry/plugins/claude-mem.ts
496
600
  var PLUGIN_ID2 = "claude-mem@thedotmack";
497
601
  var PLUGIN_NAME2 = "claude-mem";
@@ -505,10 +609,11 @@ var claudeMem = {
505
609
  slashNamespace: "/claude-mem:*",
506
610
  whenToUse: 'for cross-session memory search ("did we solve this before?"), phased planning (`make-plan`), or phased execution (`do`).',
507
611
  marketplaces: () => [MARKETPLACE_NAME2],
612
+ prereqCheck: (t2) => ensureBun(t2),
508
613
  isInstalled: () => isPluginInstalled(PLUGIN_ID2),
509
614
  installedVersion: async () => {
510
- const p10 = await findPlugin(PLUGIN_ID2);
511
- const v = p10?.version;
615
+ const p11 = await findPlugin(PLUGIN_ID2);
616
+ const v = p11?.version;
512
617
  return v && v !== "unknown" ? v : null;
513
618
  },
514
619
  latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME2, PLUGIN_NAME2),
@@ -522,32 +627,32 @@ var claudeMem = {
522
627
  var claude_mem_default = claudeMem;
523
628
 
524
629
  // src/registry/plugins/chrome-devtools-mcp.ts
525
- import { existsSync } from "fs";
526
- import path2 from "path";
630
+ import { existsSync as existsSync2 } from "fs";
631
+ import path3 from "path";
527
632
  var PLUGIN_ID3 = "chrome-devtools-mcp@chrome-devtools-plugins";
528
633
  var MARKETPLACE_NAME3 = "chrome-devtools-plugins";
529
634
  var MARKETPLACE_SOURCE3 = "ChromeDevTools/chrome-devtools-mcp";
530
635
  async function checkChrome() {
531
- if (process.env.CHROME_PATH && existsSync(process.env.CHROME_PATH)) return true;
636
+ if (process.env.CHROME_PATH && existsSync2(process.env.CHROME_PATH)) return true;
532
637
  if (process.platform === "win32") {
533
638
  const suffixes = [
534
- path2.join("Google", "Chrome SxS", "Application", "chrome.exe"),
535
- path2.join("Google", "Chrome", "Application", "chrome.exe")
639
+ path3.join("Google", "Chrome SxS", "Application", "chrome.exe"),
640
+ path3.join("Google", "Chrome", "Application", "chrome.exe")
536
641
  ];
537
642
  const prefixes = [
538
643
  process.env.LOCALAPPDATA,
539
644
  process.env.PROGRAMFILES,
540
645
  process.env["PROGRAMFILES(X86)"]
541
- ].filter((p10) => Boolean(p10));
646
+ ].filter((p11) => Boolean(p11));
542
647
  for (const prefix of prefixes) {
543
648
  for (const suffix of suffixes) {
544
- if (existsSync(path2.join(prefix, suffix))) return true;
649
+ if (existsSync2(path3.join(prefix, suffix))) return true;
545
650
  }
546
651
  }
547
652
  return false;
548
653
  }
549
654
  if (process.platform === "darwin") {
550
- return existsSync("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
655
+ return existsSync2("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
551
656
  }
552
657
  const [viaPath, viaPathChromium] = await Promise.all([
553
658
  run("which", ["google-chrome"]),
@@ -613,8 +718,8 @@ var curdxFlow = {
613
718
  marketplaces: () => [MARKETPLACE_NAME4],
614
719
  isInstalled: () => isPluginInstalled(PLUGIN_ID5),
615
720
  installedVersion: async () => {
616
- const p10 = await findPlugin(PLUGIN_ID5);
617
- const v = p10?.version;
721
+ const p11 = await findPlugin(PLUGIN_ID5);
722
+ const v = p11?.version;
618
723
  return v && v !== "unknown" ? v : null;
619
724
  },
620
725
  latestVersion: () => getMarketplacePluginVersion(MARKETPLACE_NAME4, PLUGIN_NAME3),
@@ -667,7 +772,7 @@ var sequentialThinking = {
667
772
  var sequential_thinking_default = sequentialThinking;
668
773
 
669
774
  // src/registry/mcps/context7.ts
670
- import * as p2 from "@clack/prompts";
775
+ import * as p3 from "@clack/prompts";
671
776
  var MCP_NAME2 = "context7";
672
777
  var URL = "https://mcp.context7.com/mcp";
673
778
  var context7 = {
@@ -678,14 +783,14 @@ var context7 = {
678
783
  whenToUse: "for any library / SDK / framework / API / Claude Code docs lookup. Use instead of web search.",
679
784
  isInstalled: () => isMcpInstalled(MCP_NAME2),
680
785
  configPrompts: async ({ t: t2 }) => {
681
- p2.note(`${t2("context7.dashboardHint")}
786
+ p3.note(`${t2("context7.dashboardHint")}
682
787
  ${t2("context7.keyWarning")}`, "context7");
683
- const key = await p2.text({
788
+ const key = await p3.text({
684
789
  message: t2("context7.askKey"),
685
790
  placeholder: t2("context7.keyPlaceholder"),
686
791
  defaultValue: ""
687
792
  });
688
- if (p2.isCancel(key)) return null;
793
+ if (p3.isCancel(key)) return null;
689
794
  const trimmed = String(key ?? "").trim();
690
795
  const out = {};
691
796
  if (trimmed) out["CONTEXT7_API_KEY"] = trimmed;
@@ -729,19 +834,19 @@ var PKGS = [
729
834
  context7_default
730
835
  ];
731
836
  function findPkg(id) {
732
- return PKGS.find((p10) => p10.id === id);
837
+ return PKGS.find((p11) => p11.id === id);
733
838
  }
734
839
 
735
840
  // src/runner/claudeMd.ts
736
841
  import { promises as fs2 } from "fs";
737
- import path3 from "path";
842
+ import path4 from "path";
738
843
  import os2 from "os";
739
- import * as p3 from "@clack/prompts";
844
+ import * as p4 from "@clack/prompts";
740
845
  var BEGIN_MARKER = "<!-- BEGIN @curdx/flow v1 -->";
741
846
  var END_MARKER = "<!-- END @curdx/flow v1 -->";
742
847
  var BLOCK_RE = /<!-- BEGIN @curdx\/flow v\d+[^>]*-->[\s\S]*?<!-- END @curdx\/flow v\d+ -->/;
743
848
  function claudeMdPath() {
744
- return path3.join(os2.homedir(), ".claude", "CLAUDE.md");
849
+ return path4.join(os2.homedir(), ".claude", "CLAUDE.md");
745
850
  }
746
851
  function buildCombinationPatterns(ids) {
747
852
  const has = (k) => ids.has(k);
@@ -917,7 +1022,7 @@ async function syncClaudeMd(opts) {
917
1022
  if (next === existing) {
918
1023
  return { status: "unchanged", path: file };
919
1024
  }
920
- await fs2.mkdir(path3.dirname(file), { recursive: true });
1025
+ await fs2.mkdir(path4.dirname(file), { recursive: true });
921
1026
  const tmp = `${file}.tmp.${process.pid}`;
922
1027
  await fs2.writeFile(tmp, next, "utf8");
923
1028
  await fs2.rename(tmp, file);
@@ -931,10 +1036,10 @@ async function syncClaudeMd(opts) {
931
1036
  }
932
1037
  async function syncFromState(opts) {
933
1038
  if (opts?.skip) {
934
- p3.log.info(t("claudeMd.skipped"));
1039
+ p4.log.info(t("claudeMd.skipped"));
935
1040
  return;
936
1041
  }
937
- const sp = p3.spinner();
1042
+ const sp = p4.spinner();
938
1043
  sp.start(t("claudeMd.syncing"));
939
1044
  const r = await syncClaudeMd();
940
1045
  switch (r.status) {
@@ -989,7 +1094,7 @@ async function selectInteractive(states) {
989
1094
  const optionalPkgs = PKGS.filter((pkg) => !pkg.required);
990
1095
  if (requiredPkgs.length > 0) {
991
1096
  const lines = requiredPkgs.map((pkg) => ` ${stateLabel(pkg, states.get(pkg.id))}`);
992
- p4.note(lines.join("\n"), t("install.requiredHeader"));
1097
+ p5.note(lines.join("\n"), t("install.requiredHeader"));
993
1098
  }
994
1099
  const options = optionalPkgs.map((pkg) => {
995
1100
  const s = states.get(pkg.id);
@@ -999,13 +1104,13 @@ async function selectInteractive(states) {
999
1104
  const s = states.get(pkg.id);
1000
1105
  return s.kind === "not_installed" || s.kind === "update_available";
1001
1106
  }).map((pkg) => pkg.id);
1002
- const picked = await p4.multiselect({
1107
+ const picked = await p5.multiselect({
1003
1108
  message: t("install.selectPrompt"),
1004
1109
  options,
1005
1110
  initialValues,
1006
1111
  required: false
1007
1112
  });
1008
- if (p4.isCancel(picked)) return null;
1113
+ if (p5.isCancel(picked)) return null;
1009
1114
  const userPicked = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
1010
1115
  const requiredAuto = requiredPkgs.filter((pkg) => states.get(pkg.id)?.kind !== "up_to_date");
1011
1116
  return [...requiredAuto, ...userPicked];
@@ -1017,7 +1122,7 @@ function selectFromIds(opts) {
1017
1122
  for (const id of opts.ids) {
1018
1123
  const pkg = findPkg(id);
1019
1124
  if (pkg) found.push(pkg);
1020
- else p4.log.warn(`Unknown id: ${id}`);
1125
+ else p5.log.warn(`Unknown id: ${id}`);
1021
1126
  }
1022
1127
  return found;
1023
1128
  }
@@ -1029,11 +1134,11 @@ async function runOne(pkg, state, opts) {
1029
1134
  mode = "update";
1030
1135
  } else {
1031
1136
  if (!opts.yes) {
1032
- const ans = await p4.confirm({
1137
+ const ans = await p5.confirm({
1033
1138
  message: t("install.confirmReinstall", { name: pkg.name }),
1034
1139
  initialValue: false
1035
1140
  });
1036
- if (p4.isCancel(ans) || ans === false) {
1141
+ if (p5.isCancel(ans) || ans === false) {
1037
1142
  return { id: pkg.id, status: "skip", message: t("install.skippedReinstall", { name: pkg.name }) };
1038
1143
  }
1039
1144
  }
@@ -1042,7 +1147,7 @@ async function runOne(pkg, state, opts) {
1042
1147
  if (pkg.prereqCheck) {
1043
1148
  const r = await pkg.prereqCheck(t);
1044
1149
  if (!r.ok) {
1045
- p4.log.warn(t("install.prereqFail", { name: pkg.name, reason: r.reason }));
1150
+ p5.log.warn(t("install.prereqFail", { name: pkg.name, reason: r.reason }));
1046
1151
  return { id: pkg.id, status: "skip", message: r.reason };
1047
1152
  }
1048
1153
  }
@@ -1057,30 +1162,30 @@ async function runOne(pkg, state, opts) {
1057
1162
  if (mode === "update" && state.kind === "update_available") {
1058
1163
  titleVars["version"] = state.latest;
1059
1164
  }
1060
- const log5 = p4.taskLog({ title: t(titleKey, titleVars) });
1165
+ const log6 = p5.taskLog({ title: t(titleKey, titleVars) });
1061
1166
  try {
1062
1167
  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 });
1168
+ log6.message(t("reinstall.uninstalling"));
1169
+ await pkg.uninstall({ log: log6, config, t });
1170
+ log6.message(t("reinstall.installing"));
1171
+ await pkg.install({ log: log6, config, t });
1067
1172
  } else if (mode === "update") {
1068
1173
  if (pkg.update) {
1069
- await pkg.update({ log: log5, config, t });
1174
+ await pkg.update({ log: log6, config, t });
1070
1175
  } 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 });
1176
+ log6.message(t("reinstall.uninstalling"));
1177
+ await pkg.uninstall({ log: log6, config, t });
1178
+ log6.message(t("reinstall.installing"));
1179
+ await pkg.install({ log: log6, config, t });
1075
1180
  }
1076
1181
  } else {
1077
- await pkg.install({ log: log5, config, t });
1182
+ await pkg.install({ log: log6, config, t });
1078
1183
  }
1079
- log5.success(t("install.success", { name: pkg.name }));
1184
+ log6.success(t("install.success", { name: pkg.name }));
1080
1185
  return { id: pkg.id, status: "ok" };
1081
1186
  } catch (err) {
1082
1187
  const msg = err instanceof Error ? err.message : String(err);
1083
- log5.error(`${t("install.failed", { name: pkg.name })}
1188
+ log6.error(`${t("install.failed", { name: pkg.name })}
1084
1189
  ${msg}`);
1085
1190
  return { id: pkg.id, status: "fail", message: msg };
1086
1191
  }
@@ -1098,7 +1203,7 @@ function summarize(results) {
1098
1203
  ...skip.map((r) => ` ${pc.yellow("-")} ${r.id}${r.message ? pc.dim(` (${r.message})`) : ""}`),
1099
1204
  ...fail.map((r) => ` ${pc.red("\u2717")} ${r.id}${r.message ? pc.dim(` (${r.message.split("\n")[0]})`) : ""}`)
1100
1205
  ];
1101
- p4.note(lines.join("\n"), t("install.summaryTitle"));
1206
+ p5.note(lines.join("\n"), t("install.summaryTitle"));
1102
1207
  }
1103
1208
  async function maybeRefreshMarketplaces(opts) {
1104
1209
  if (opts.noRefresh) return;
@@ -1107,7 +1212,7 @@ async function maybeRefreshMarketplaces(opts) {
1107
1212
  if (pkg.marketplaces) for (const n of pkg.marketplaces()) names.add(n);
1108
1213
  }
1109
1214
  if (names.size === 0) return;
1110
- const sp = p4.spinner();
1215
+ const sp = p5.spinner();
1111
1216
  sp.start(t("marketplace.refreshing"));
1112
1217
  const refreshed = await refreshMarketplaces([...names]);
1113
1218
  sp.stop(
@@ -1121,7 +1226,7 @@ async function installFlow(opts = {}) {
1121
1226
  const explicit = opts.all || opts.ids && opts.ids.length > 0;
1122
1227
  const candidates = explicit ? selectFromIds(opts) : [...PKGS];
1123
1228
  if (candidates.length === 0) {
1124
- p4.log.info(t("install.nothingSelected"));
1229
+ p5.log.info(t("install.nothingSelected"));
1125
1230
  return;
1126
1231
  }
1127
1232
  if (opts.ids && opts.ids.length > 0) {
@@ -1132,7 +1237,7 @@ async function installFlow(opts = {}) {
1132
1237
  }
1133
1238
  }
1134
1239
  const stateMap = /* @__PURE__ */ new Map();
1135
- const sp = p4.spinner();
1240
+ const sp = p5.spinner();
1136
1241
  sp.start(t("state.checking"));
1137
1242
  try {
1138
1243
  await Promise.all([listPlugins(), listMcp()]);
@@ -1160,13 +1265,13 @@ async function installFlow(opts = {}) {
1160
1265
  const picked = await selectInteractive(stateMap);
1161
1266
  if (picked === null) {
1162
1267
  userCancelled = true;
1163
- p4.cancel(t("app.cancelled"));
1268
+ p5.cancel(t("app.cancelled"));
1164
1269
  return;
1165
1270
  }
1166
1271
  targets = picked;
1167
1272
  }
1168
1273
  if (targets.length === 0) {
1169
- p4.log.info(t("install.nothingSelected"));
1274
+ p5.log.info(t("install.nothingSelected"));
1170
1275
  return;
1171
1276
  }
1172
1277
  const results = [];
@@ -1183,14 +1288,14 @@ async function installFlow(opts = {}) {
1183
1288
  }
1184
1289
 
1185
1290
  // src/flows/uninstall.ts
1186
- import * as p5 from "@clack/prompts";
1291
+ import * as p6 from "@clack/prompts";
1187
1292
  import pc2 from "picocolors";
1188
1293
  async function getInstalled() {
1189
1294
  const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
1190
1295
  return states.filter((s) => s.installed).map((s) => s.pkg);
1191
1296
  }
1192
1297
  async function probeInstalled() {
1193
- const sp = p5.spinner();
1298
+ const sp = p6.spinner();
1194
1299
  sp.start(t("state.checking"));
1195
1300
  try {
1196
1301
  await Promise.all([listPlugins(), listMcp()]);
@@ -1212,21 +1317,21 @@ async function uninstallFlow(opts = {}) {
1212
1317
  for (const id of opts.ids) {
1213
1318
  const pkg = findPkg(id);
1214
1319
  if (!pkg) {
1215
- p5.log.warn(`Unknown id: ${id}`);
1320
+ p6.log.warn(`Unknown id: ${id}`);
1216
1321
  continue;
1217
1322
  }
1218
1323
  if (!installed.some((x2) => x2.id === pkg.id)) {
1219
- p5.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1324
+ p6.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1220
1325
  continue;
1221
1326
  }
1222
1327
  targets.push(pkg);
1223
1328
  }
1224
1329
  } else {
1225
1330
  if (installed.length === 0) {
1226
- p5.log.info(t("uninstall.noneInstalled"));
1331
+ p6.log.info(t("uninstall.noneInstalled"));
1227
1332
  return;
1228
1333
  }
1229
- const picked = await p5.multiselect({
1334
+ const picked = await p6.multiselect({
1230
1335
  message: t("uninstall.selectPrompt"),
1231
1336
  options: installed.map((pkg) => ({
1232
1337
  value: pkg.id,
@@ -1235,45 +1340,45 @@ async function uninstallFlow(opts = {}) {
1235
1340
  })),
1236
1341
  required: false
1237
1342
  });
1238
- if (p5.isCancel(picked)) {
1343
+ if (p6.isCancel(picked)) {
1239
1344
  userCancelled = true;
1240
- p5.cancel(t("app.cancelled"));
1345
+ p6.cancel(t("app.cancelled"));
1241
1346
  return;
1242
1347
  }
1243
1348
  targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
1244
1349
  }
1245
1350
  if (targets.length === 0) {
1246
- p5.log.info(t("install.nothingSelected"));
1351
+ p6.log.info(t("install.nothingSelected"));
1247
1352
  return;
1248
1353
  }
1249
1354
  if (!opts.yes) {
1250
- const ok2 = await p5.confirm({
1355
+ const ok2 = await p6.confirm({
1251
1356
  message: t("uninstall.confirm", { count: targets.length }),
1252
1357
  initialValue: false
1253
1358
  });
1254
- if (p5.isCancel(ok2) || ok2 === false) {
1359
+ if (p6.isCancel(ok2) || ok2 === false) {
1255
1360
  userCancelled = true;
1256
- p5.cancel(t("app.cancelled"));
1361
+ p6.cancel(t("app.cancelled"));
1257
1362
  return;
1258
1363
  }
1259
1364
  }
1260
1365
  const results = [];
1261
1366
  for (const pkg of targets) {
1262
- const log5 = p5.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
1367
+ const log6 = p6.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
1263
1368
  try {
1264
- await pkg.uninstall({ log: log5, config: {}, t });
1265
- log5.success(t("uninstall.success", { name: pkg.name }));
1369
+ await pkg.uninstall({ log: log6, config: {}, t });
1370
+ log6.success(t("uninstall.success", { name: pkg.name }));
1266
1371
  results.push({ id: pkg.id, status: "ok" });
1267
1372
  } catch (err) {
1268
1373
  const msg = err instanceof Error ? err.message : String(err);
1269
- log5.error(`${t("uninstall.failed", { name: pkg.name })}
1374
+ log6.error(`${t("uninstall.failed", { name: pkg.name })}
1270
1375
  ${msg}`);
1271
1376
  results.push({ id: pkg.id, status: "fail", message: msg });
1272
1377
  }
1273
1378
  }
1274
1379
  const ok = results.filter((r) => r.status === "ok").length;
1275
1380
  const fail = results.filter((r) => r.status === "fail").length;
1276
- p5.note(
1381
+ p6.note(
1277
1382
  [
1278
1383
  pc2.green(t("install.summaryOk", { count: ok })),
1279
1384
  pc2.red(t("install.summaryFail", { count: fail }))
@@ -1288,14 +1393,14 @@ ${msg}`);
1288
1393
  }
1289
1394
 
1290
1395
  // src/flows/update.ts
1291
- import * as p6 from "@clack/prompts";
1396
+ import * as p7 from "@clack/prompts";
1292
1397
  import pc3 from "picocolors";
1293
1398
  async function getInstalled2() {
1294
1399
  const states = await Promise.all(PKGS.map(async (pkg) => ({ pkg, installed: await pkg.isInstalled() })));
1295
1400
  return states.filter((s) => s.installed).map((s) => s.pkg);
1296
1401
  }
1297
1402
  async function probeInstalled2() {
1298
- const sp = p6.spinner();
1403
+ const sp = p7.spinner();
1299
1404
  sp.start(t("state.checking"));
1300
1405
  try {
1301
1406
  await Promise.all([listPlugins(), listMcp()]);
@@ -1312,7 +1417,7 @@ async function updateFlow(opts = {}) {
1312
1417
  try {
1313
1418
  const installed = await probeInstalled2();
1314
1419
  if (installed.length === 0) {
1315
- p6.log.info(t("update.noneInstalled"));
1420
+ p7.log.info(t("update.noneInstalled"));
1316
1421
  return;
1317
1422
  }
1318
1423
  let targets;
@@ -1323,17 +1428,17 @@ async function updateFlow(opts = {}) {
1323
1428
  for (const id of opts.ids) {
1324
1429
  const pkg = findPkg(id);
1325
1430
  if (!pkg) {
1326
- p6.log.warn(`Unknown id: ${id}`);
1431
+ p7.log.warn(`Unknown id: ${id}`);
1327
1432
  continue;
1328
1433
  }
1329
1434
  if (!installed.some((x2) => x2.id === pkg.id)) {
1330
- p6.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1435
+ p7.log.warn(`${pkg.name}: ${t("pkg.notInstalled")}`);
1331
1436
  continue;
1332
1437
  }
1333
1438
  targets.push(pkg);
1334
1439
  }
1335
1440
  } else {
1336
- const picked = await p6.multiselect({
1441
+ const picked = await p7.multiselect({
1337
1442
  message: t("update.selectPrompt"),
1338
1443
  options: installed.map((pkg) => ({
1339
1444
  value: pkg.id,
@@ -1342,42 +1447,42 @@ async function updateFlow(opts = {}) {
1342
1447
  })),
1343
1448
  required: false
1344
1449
  });
1345
- if (p6.isCancel(picked)) {
1450
+ if (p7.isCancel(picked)) {
1346
1451
  userCancelled = true;
1347
- p6.cancel(t("app.cancelled"));
1452
+ p7.cancel(t("app.cancelled"));
1348
1453
  return;
1349
1454
  }
1350
1455
  targets = picked.map((id) => findPkg(id)).filter((x2) => Boolean(x2));
1351
1456
  }
1352
1457
  if (targets.length === 0) {
1353
- p6.log.info(t("install.nothingSelected"));
1458
+ p7.log.info(t("install.nothingSelected"));
1354
1459
  return;
1355
1460
  }
1356
1461
  const results = [];
1357
1462
  for (const pkg of targets) {
1358
1463
  if (pkg.id === "sequential-thinking") {
1359
- p6.log.info(t("update.mcpAutoNote", { name: pkg.name }));
1464
+ p7.log.info(t("update.mcpAutoNote", { name: pkg.name }));
1360
1465
  results.push({ id: pkg.id, status: "noop" });
1361
1466
  continue;
1362
1467
  }
1363
1468
  if (pkg.id === "context7") {
1364
- p6.log.info(t("update.context7Note"));
1469
+ p7.log.info(t("update.context7Note"));
1365
1470
  results.push({ id: pkg.id, status: "noop" });
1366
1471
  continue;
1367
1472
  }
1368
- const log5 = p6.taskLog({ title: t("update.starting", { name: pkg.name }) });
1473
+ const log6 = p7.taskLog({ title: t("update.starting", { name: pkg.name }) });
1369
1474
  try {
1370
1475
  if (pkg.update) {
1371
- await pkg.update({ log: log5, config: {}, t });
1476
+ await pkg.update({ log: log6, config: {}, t });
1372
1477
  } else {
1373
- await pkg.uninstall({ log: log5, config: {}, t });
1374
- await pkg.install({ log: log5, config: {}, t });
1478
+ await pkg.uninstall({ log: log6, config: {}, t });
1479
+ await pkg.install({ log: log6, config: {}, t });
1375
1480
  }
1376
- log5.success(t("update.success", { name: pkg.name }));
1481
+ log6.success(t("update.success", { name: pkg.name }));
1377
1482
  results.push({ id: pkg.id, status: "ok" });
1378
1483
  } catch (err) {
1379
1484
  const msg = err instanceof Error ? err.message : String(err);
1380
- log5.error(`${t("update.failed", { name: pkg.name })}
1485
+ log6.error(`${t("update.failed", { name: pkg.name })}
1381
1486
  ${msg}`);
1382
1487
  results.push({ id: pkg.id, status: "fail", message: msg });
1383
1488
  }
@@ -1385,7 +1490,7 @@ ${msg}`);
1385
1490
  const ok = results.filter((r) => r.status === "ok").length;
1386
1491
  const fail = results.filter((r) => r.status === "fail").length;
1387
1492
  const noop = results.filter((r) => r.status === "noop").length;
1388
- p6.note(
1493
+ p7.note(
1389
1494
  [
1390
1495
  pc3.green(t("install.summaryOk", { count: ok })),
1391
1496
  pc3.red(t("install.summaryFail", { count: fail })),
@@ -1401,7 +1506,7 @@ ${msg}`);
1401
1506
  }
1402
1507
 
1403
1508
  // src/flows/status.ts
1404
- import * as p7 from "@clack/prompts";
1509
+ import * as p8 from "@clack/prompts";
1405
1510
  import pc4 from "picocolors";
1406
1511
  async function statusFlow(opts = {}) {
1407
1512
  const states = await Promise.all(
@@ -1434,12 +1539,12 @@ async function statusFlow(opts = {}) {
1434
1539
  const rows = states.map(
1435
1540
  (s) => `${s.name.padEnd(nameW)} ${s.type.padEnd(typeW)} ${s.installed ? pc4.green(`\u2713 ${t("pkg.installed")}`) : pc4.yellow(`\u2717 ${t("pkg.notInstalled")}`)}`
1436
1541
  );
1437
- p7.note([header, sep, ...rows].join("\n"), t("status.title"));
1542
+ p8.note([header, sep, ...rows].join("\n"), t("status.title"));
1438
1543
  }
1439
1544
 
1440
1545
  // src/ui/menu.ts
1441
1546
  async function mainMenu() {
1442
- const action = await p8.select({
1547
+ const action = await p9.select({
1443
1548
  message: t("menu.title"),
1444
1549
  options: [
1445
1550
  { value: "install", label: t("menu.install") },
@@ -1449,8 +1554,8 @@ async function mainMenu() {
1449
1554
  { value: "exit", label: t("menu.exit") }
1450
1555
  ]
1451
1556
  });
1452
- if (p8.isCancel(action) || action === "exit") {
1453
- p8.cancel(t("app.cancelled"));
1557
+ if (p9.isCancel(action) || action === "exit") {
1558
+ p9.cancel(t("app.cancelled"));
1454
1559
  return;
1455
1560
  }
1456
1561
  switch (action) {
@@ -1469,6 +1574,45 @@ async function mainMenu() {
1469
1574
  }
1470
1575
  }
1471
1576
 
1577
+ // src/flows/analyze.ts
1578
+ import { defineCommand } from "citty";
1579
+ var analyzeCmd = defineCommand({
1580
+ meta: {
1581
+ name: "analyze",
1582
+ description: "Analyze Claude Code session jsonl + curdx-flow errors.jsonl into a markdown report"
1583
+ },
1584
+ args: {
1585
+ out: {
1586
+ type: "string",
1587
+ description: "Output file path (default: stdout)"
1588
+ },
1589
+ json: {
1590
+ type: "boolean",
1591
+ description: "Emit JSON instead of markdown"
1592
+ },
1593
+ limit: {
1594
+ type: "string",
1595
+ description: "Top-N truncation for tabular sections (default: 10)"
1596
+ },
1597
+ "include-prompts": {
1598
+ type: "boolean",
1599
+ description: "Skip prompt redaction (D-9 white-list passthrough disabled \u2014 local debugging only)"
1600
+ }
1601
+ },
1602
+ async run({ args }) {
1603
+ const limitRaw = args.limit;
1604
+ const limit = typeof limitRaw === "string" && limitRaw.length > 0 ? Number(limitRaw) : void 0;
1605
+ const { runAnalyze } = await import("./analyze-4DE3HVCA.mjs");
1606
+ await runAnalyze({
1607
+ out: typeof args.out === "string" ? args.out : void 0,
1608
+ json: Boolean(args.json),
1609
+ limit: Number.isFinite(limit) ? limit : void 0,
1610
+ includePrompts: Boolean(args["include-prompts"])
1611
+ });
1612
+ }
1613
+ });
1614
+ var analyze_default = analyzeCmd;
1615
+
1472
1616
  // src/index.ts
1473
1617
  function parseLang(v) {
1474
1618
  return v === "zh" || v === "en" ? v : void 0;
@@ -1484,7 +1628,7 @@ var sharedArgs = {
1484
1628
  description: "Skip syncing the @curdx/flow block in ~/.claude/CLAUDE.md"
1485
1629
  }
1486
1630
  };
1487
- var installCmd = defineCommand({
1631
+ var installCmd = defineCommand2({
1488
1632
  meta: { name: "install", description: "Install, reinstall, or update plugins / MCP servers" },
1489
1633
  args: {
1490
1634
  ...sharedArgs,
@@ -1495,7 +1639,7 @@ var installCmd = defineCommand({
1495
1639
  },
1496
1640
  async run({ args }) {
1497
1641
  await initLanguage(parseLang(args.lang));
1498
- p9.intro(t("app.intro"));
1642
+ p10.intro(t("app.intro"));
1499
1643
  const ids = collectPositional(args);
1500
1644
  await installFlow({
1501
1645
  ids,
@@ -1504,10 +1648,10 @@ var installCmd = defineCommand({
1504
1648
  noRefresh: Boolean(args["no-refresh"]),
1505
1649
  noClaudeMd: noClaudeMdFromArgs(args)
1506
1650
  });
1507
- p9.outro(t("app.outro"));
1651
+ p10.outro(t("app.outro"));
1508
1652
  }
1509
1653
  });
1510
- var uninstallCmd = defineCommand({
1654
+ var uninstallCmd = defineCommand2({
1511
1655
  meta: { name: "uninstall", description: "Uninstall installed plugins / MCP servers" },
1512
1656
  args: {
1513
1657
  ...sharedArgs,
@@ -1516,17 +1660,17 @@ var uninstallCmd = defineCommand({
1516
1660
  },
1517
1661
  async run({ args }) {
1518
1662
  await initLanguage(parseLang(args.lang));
1519
- p9.intro(t("app.intro"));
1663
+ p10.intro(t("app.intro"));
1520
1664
  const ids = collectPositional(args);
1521
1665
  await uninstallFlow({
1522
1666
  ids,
1523
1667
  yes: Boolean(args.yes),
1524
1668
  noClaudeMd: noClaudeMdFromArgs(args)
1525
1669
  });
1526
- p9.outro(t("app.outro"));
1670
+ p10.outro(t("app.outro"));
1527
1671
  }
1528
1672
  });
1529
- var updateCmd = defineCommand({
1673
+ var updateCmd = defineCommand2({
1530
1674
  meta: { name: "update", description: "Update installed plugins" },
1531
1675
  args: {
1532
1676
  ...sharedArgs,
@@ -1535,17 +1679,17 @@ var updateCmd = defineCommand({
1535
1679
  },
1536
1680
  async run({ args }) {
1537
1681
  await initLanguage(parseLang(args.lang));
1538
- p9.intro(t("app.intro"));
1682
+ p10.intro(t("app.intro"));
1539
1683
  const ids = collectPositional(args);
1540
1684
  await updateFlow({
1541
1685
  ids,
1542
1686
  all: Boolean(args.all),
1543
1687
  noClaudeMd: noClaudeMdFromArgs(args)
1544
1688
  });
1545
- p9.outro(t("app.outro"));
1689
+ p10.outro(t("app.outro"));
1546
1690
  }
1547
1691
  });
1548
- var statusCmd = defineCommand({
1692
+ var statusCmd = defineCommand2({
1549
1693
  meta: { name: "status", description: "Show install status" },
1550
1694
  args: {
1551
1695
  ...sharedArgs,
@@ -1553,13 +1697,13 @@ var statusCmd = defineCommand({
1553
1697
  },
1554
1698
  async run({ args }) {
1555
1699
  await initLanguage(parseLang(args.lang));
1556
- if (!args.json) p9.intro(t("app.intro"));
1700
+ if (!args.json) p10.intro(t("app.intro"));
1557
1701
  await statusFlow({ json: Boolean(args.json) });
1558
- if (!args.json) p9.outro(t("app.outro"));
1702
+ if (!args.json) p10.outro(t("app.outro"));
1559
1703
  }
1560
1704
  });
1561
- var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "status"]);
1562
- var root = defineCommand({
1705
+ var SUBCOMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "update", "status", "analyze"]);
1706
+ var root = defineCommand2({
1563
1707
  meta: {
1564
1708
  name: "@curdx/flow",
1565
1709
  version: "3.3.2",
@@ -1570,7 +1714,8 @@ var root = defineCommand({
1570
1714
  install: installCmd,
1571
1715
  uninstall: uninstallCmd,
1572
1716
  update: updateCmd,
1573
- status: statusCmd
1717
+ status: statusCmd,
1718
+ analyze: analyze_default
1574
1719
  }
1575
1720
  // No root run() — citty 0.1.6 calls parent.run AFTER a matching subcommand,
1576
1721
  // which would render the menu after a subcommand finishes. We dispatch the
@@ -1597,9 +1742,9 @@ async function runInteractive(argv2) {
1597
1742
  else if (argv2[i]?.startsWith("--lang=")) lang = parseLang(argv2[i].slice("--lang=".length));
1598
1743
  }
1599
1744
  await initLanguage(lang);
1600
- p9.intro(t("app.intro"));
1745
+ p10.intro(t("app.intro"));
1601
1746
  await mainMenu();
1602
- p9.outro(t("app.outro"));
1747
+ p10.outro(t("app.outro"));
1603
1748
  }
1604
1749
  var argv = process.argv.slice(2);
1605
1750
  var first = firstNonFlag(argv);