@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/CHANGELOG.md +31 -0
- package/README.md +475 -39
- package/README.zh-CN.md +503 -0
- package/dist/analyze-4DE3HVCA.mjs +794 -0
- package/dist/index.mjs +271 -126
- package/package.json +4 -2
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
|
|
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
|
|
237
|
+
import * as p9 from "@clack/prompts";
|
|
200
238
|
|
|
201
239
|
// src/flows/install.ts
|
|
202
|
-
import * as
|
|
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,
|
|
222
|
-
|
|
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
|
-
|
|
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((
|
|
273
|
-
const [name =
|
|
310
|
+
pluginCache = arr.map((p11) => {
|
|
311
|
+
const [name = p11.id, marketplace = ""] = p11.id.split("@");
|
|
274
312
|
return {
|
|
275
|
-
id:
|
|
313
|
+
id: p11.id,
|
|
276
314
|
name,
|
|
277
315
|
marketplace,
|
|
278
|
-
version:
|
|
279
|
-
scope:
|
|
280
|
-
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((
|
|
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((
|
|
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((
|
|
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
|
|
482
|
-
const v =
|
|
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
|
|
511
|
-
const v =
|
|
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
|
|
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 &&
|
|
636
|
+
if (process.env.CHROME_PATH && existsSync2(process.env.CHROME_PATH)) return true;
|
|
532
637
|
if (process.platform === "win32") {
|
|
533
638
|
const suffixes = [
|
|
534
|
-
|
|
535
|
-
|
|
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((
|
|
646
|
+
].filter((p11) => Boolean(p11));
|
|
542
647
|
for (const prefix of prefixes) {
|
|
543
648
|
for (const suffix of suffixes) {
|
|
544
|
-
if (
|
|
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
|
|
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
|
|
617
|
-
const v =
|
|
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
|
|
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
|
-
|
|
786
|
+
p3.note(`${t2("context7.dashboardHint")}
|
|
682
787
|
${t2("context7.keyWarning")}`, "context7");
|
|
683
|
-
const key = await
|
|
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 (
|
|
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((
|
|
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
|
|
842
|
+
import path4 from "path";
|
|
738
843
|
import os2 from "os";
|
|
739
|
-
import * as
|
|
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
|
|
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(
|
|
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
|
-
|
|
1039
|
+
p4.log.info(t("claudeMd.skipped"));
|
|
935
1040
|
return;
|
|
936
1041
|
}
|
|
937
|
-
const sp =
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
1137
|
+
const ans = await p5.confirm({
|
|
1033
1138
|
message: t("install.confirmReinstall", { name: pkg.name }),
|
|
1034
1139
|
initialValue: false
|
|
1035
1140
|
});
|
|
1036
|
-
if (
|
|
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
|
-
|
|
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
|
|
1165
|
+
const log6 = p5.taskLog({ title: t(titleKey, titleVars) });
|
|
1061
1166
|
try {
|
|
1062
1167
|
if (mode === "reinstall") {
|
|
1063
|
-
|
|
1064
|
-
await pkg.uninstall({ log:
|
|
1065
|
-
|
|
1066
|
-
await pkg.install({ log:
|
|
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:
|
|
1174
|
+
await pkg.update({ log: log6, config, t });
|
|
1070
1175
|
} else {
|
|
1071
|
-
|
|
1072
|
-
await pkg.uninstall({ log:
|
|
1073
|
-
|
|
1074
|
-
await pkg.install({ log:
|
|
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:
|
|
1182
|
+
await pkg.install({ log: log6, config, t });
|
|
1078
1183
|
}
|
|
1079
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
1268
|
+
p5.cancel(t("app.cancelled"));
|
|
1164
1269
|
return;
|
|
1165
1270
|
}
|
|
1166
1271
|
targets = picked;
|
|
1167
1272
|
}
|
|
1168
1273
|
if (targets.length === 0) {
|
|
1169
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1320
|
+
p6.log.warn(`Unknown id: ${id}`);
|
|
1216
1321
|
continue;
|
|
1217
1322
|
}
|
|
1218
1323
|
if (!installed.some((x2) => x2.id === pkg.id)) {
|
|
1219
|
-
|
|
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
|
-
|
|
1331
|
+
p6.log.info(t("uninstall.noneInstalled"));
|
|
1227
1332
|
return;
|
|
1228
1333
|
}
|
|
1229
|
-
const picked = await
|
|
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 (
|
|
1343
|
+
if (p6.isCancel(picked)) {
|
|
1239
1344
|
userCancelled = true;
|
|
1240
|
-
|
|
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
|
-
|
|
1351
|
+
p6.log.info(t("install.nothingSelected"));
|
|
1247
1352
|
return;
|
|
1248
1353
|
}
|
|
1249
1354
|
if (!opts.yes) {
|
|
1250
|
-
const ok2 = await
|
|
1355
|
+
const ok2 = await p6.confirm({
|
|
1251
1356
|
message: t("uninstall.confirm", { count: targets.length }),
|
|
1252
1357
|
initialValue: false
|
|
1253
1358
|
});
|
|
1254
|
-
if (
|
|
1359
|
+
if (p6.isCancel(ok2) || ok2 === false) {
|
|
1255
1360
|
userCancelled = true;
|
|
1256
|
-
|
|
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
|
|
1367
|
+
const log6 = p6.taskLog({ title: t("uninstall.starting", { name: pkg.name }) });
|
|
1263
1368
|
try {
|
|
1264
|
-
await pkg.uninstall({ log:
|
|
1265
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1431
|
+
p7.log.warn(`Unknown id: ${id}`);
|
|
1327
1432
|
continue;
|
|
1328
1433
|
}
|
|
1329
1434
|
if (!installed.some((x2) => x2.id === pkg.id)) {
|
|
1330
|
-
|
|
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
|
|
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 (
|
|
1450
|
+
if (p7.isCancel(picked)) {
|
|
1346
1451
|
userCancelled = true;
|
|
1347
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1469
|
+
p7.log.info(t("update.context7Note"));
|
|
1365
1470
|
results.push({ id: pkg.id, status: "noop" });
|
|
1366
1471
|
continue;
|
|
1367
1472
|
}
|
|
1368
|
-
const
|
|
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:
|
|
1476
|
+
await pkg.update({ log: log6, config: {}, t });
|
|
1372
1477
|
} else {
|
|
1373
|
-
await pkg.uninstall({ log:
|
|
1374
|
-
await pkg.install({ log:
|
|
1478
|
+
await pkg.uninstall({ log: log6, config: {}, t });
|
|
1479
|
+
await pkg.install({ log: log6, config: {}, t });
|
|
1375
1480
|
}
|
|
1376
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1453
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1651
|
+
p10.outro(t("app.outro"));
|
|
1508
1652
|
}
|
|
1509
1653
|
});
|
|
1510
|
-
var uninstallCmd =
|
|
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
|
-
|
|
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
|
-
|
|
1670
|
+
p10.outro(t("app.outro"));
|
|
1527
1671
|
}
|
|
1528
1672
|
});
|
|
1529
|
-
var updateCmd =
|
|
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
|
-
|
|
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
|
-
|
|
1689
|
+
p10.outro(t("app.outro"));
|
|
1546
1690
|
}
|
|
1547
1691
|
});
|
|
1548
|
-
var statusCmd =
|
|
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)
|
|
1700
|
+
if (!args.json) p10.intro(t("app.intro"));
|
|
1557
1701
|
await statusFlow({ json: Boolean(args.json) });
|
|
1558
|
-
if (!args.json)
|
|
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 =
|
|
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
|
-
|
|
1745
|
+
p10.intro(t("app.intro"));
|
|
1601
1746
|
await mainMenu();
|
|
1602
|
-
|
|
1747
|
+
p10.outro(t("app.outro"));
|
|
1603
1748
|
}
|
|
1604
1749
|
var argv = process.argv.slice(2);
|
|
1605
1750
|
var first = firstNonFlag(argv);
|