@ganakailabs/cloudeval-cli 0.24.4 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  } from "./chunk-KBHRBGSX.js";
37
37
  import {
38
38
  CLI_VERSION
39
- } from "./chunk-YEKKVQCN.js";
39
+ } from "./chunk-LKVKOGVL.js";
40
40
 
41
41
  // src/runtime/prepareInk.ts
42
42
  import fs from "fs";
@@ -135,9 +135,9 @@ ensureInkRuntimeEnvironment();
135
135
  // src/cli.tsx
136
136
  import React from "react";
137
137
  import { Command } from "commander";
138
- import { promises as fs12 } from "fs";
138
+ import { promises as fs13 } from "fs";
139
139
  import os6 from "os";
140
- import path9 from "path";
140
+ import path10 from "path";
141
141
 
142
142
  // src/shellCompletion.ts
143
143
  var normalizeCompletionShell = (shell) => {
@@ -437,6 +437,33 @@ var cliCommands = [
437
437
  "reports rules"
438
438
  ]
439
439
  },
440
+ {
441
+ name: "review",
442
+ description: "Sync a GitHub-backed CloudEval project from the pushed commit and evaluate report gates",
443
+ domain: "reports",
444
+ options: [
445
+ ...authOptions,
446
+ "--project",
447
+ "--repo",
448
+ "--ref",
449
+ "--commit-sha",
450
+ "--source-root",
451
+ "--config",
452
+ "--no-wait",
453
+ "--wait-timeout",
454
+ "--poll-interval",
455
+ "--no-ai-summary",
456
+ "--ignore-dirty",
457
+ "--output",
458
+ "--format",
459
+ "--quiet",
460
+ "--progress",
461
+ "--model",
462
+ "--profile",
463
+ "--help"
464
+ ],
465
+ workflows: ["github review", "pr gate", "shift-left review"]
466
+ },
440
467
  {
441
468
  name: "recipes",
442
469
  description: "CloudEval reusable recipes and agent skills",
@@ -1436,10 +1463,10 @@ var writeFormattedOutput = async (input) => {
1436
1463
  process.stdout.write(text);
1437
1464
  };
1438
1465
  var writePrivateOutputFile = async (output, text) => {
1439
- const fs13 = await import("fs/promises");
1440
- await fs13.writeFile(output, text, { encoding: "utf8", mode: 384 });
1466
+ const fs14 = await import("fs/promises");
1467
+ await fs14.writeFile(output, text, { encoding: "utf8", mode: 384 });
1441
1468
  if (process.platform !== "win32") {
1442
- await fs13.chmod(output, 384);
1469
+ await fs14.chmod(output, 384);
1443
1470
  }
1444
1471
  };
1445
1472
 
@@ -1787,8 +1814,8 @@ var writeReport = async (report, options, tuiDefault) => {
1787
1814
  const textFormat = format === "text" || format === "table" ? "summary" : format;
1788
1815
  const text = serializeReportOutput(report, { format: textFormat, mode });
1789
1816
  if (options.output) {
1790
- const fs13 = await import("fs/promises");
1791
- await fs13.writeFile(options.output, text, "utf8");
1817
+ const fs14 = await import("fs/promises");
1818
+ await fs14.writeFile(options.output, text, "utf8");
1792
1819
  return;
1793
1820
  }
1794
1821
  process.stdout.write(text);
@@ -1823,16 +1850,16 @@ var writeDownloadPayload = async (input) => {
1823
1850
  );
1824
1851
  return [];
1825
1852
  }
1826
- const fs13 = await import("fs/promises");
1827
- const path10 = await import("path");
1828
- await fs13.mkdir(path10.dirname(input.output), { recursive: true });
1853
+ const fs14 = await import("fs/promises");
1854
+ const path11 = await import("path");
1855
+ await fs14.mkdir(path11.dirname(input.output), { recursive: true });
1829
1856
  const text = formatOutput({
1830
1857
  command: input.command,
1831
1858
  data: input.payload,
1832
1859
  format: input.format,
1833
1860
  frontendUrl: input.frontendUrl
1834
1861
  });
1835
- await fs13.writeFile(input.output, text, "utf8");
1862
+ await fs14.writeFile(input.output, text, "utf8");
1836
1863
  return [input.output];
1837
1864
  };
1838
1865
  var resolveMachineFormat = (requested) => {
@@ -1981,14 +2008,14 @@ var registerReportsCommand = (program2, deps) => {
1981
2008
  );
1982
2009
  const data = reportTypes.length === 1 ? payload[reportTypes[0] === "architecture" ? "waf" : reportTypes[0]] : payload;
1983
2010
  if (options.output && reportTypes.length > 1) {
1984
- const fs13 = await import("fs/promises");
1985
- const path10 = await import("path");
1986
- const stat = await fs13.stat(options.output).catch(() => void 0);
1987
- if (stat?.isDirectory() || !path10.extname(options.output)) {
1988
- await fs13.mkdir(options.output, { recursive: true });
2011
+ const fs14 = await import("fs/promises");
2012
+ const path11 = await import("path");
2013
+ const stat = await fs14.stat(options.output).catch(() => void 0);
2014
+ if (stat?.isDirectory() || !path11.extname(options.output)) {
2015
+ await fs14.mkdir(options.output, { recursive: true });
1989
2016
  const files = [];
1990
2017
  for (const [key, value] of Object.entries(payload)) {
1991
- const file = path10.join(options.output, `${projectId}-${key}-report.json`);
2018
+ const file = path11.join(options.output, `${projectId}-${key}-report.json`);
1992
2019
  files.push(
1993
2020
  ...await writeDownloadPayload({
1994
2021
  command: "reports download",
@@ -2217,9 +2244,546 @@ var registerReportsCommand = (program2, deps) => {
2217
2244
  );
2218
2245
  };
2219
2246
 
2247
+ // src/reviewCommand.ts
2248
+ import fs2 from "fs/promises";
2249
+ import path2 from "path";
2250
+ import { spawn } from "child_process";
2251
+
2252
+ // src/apiClient.ts
2253
+ var responseErrorMessage = async (response) => {
2254
+ const text = await response.text();
2255
+ if (!text) {
2256
+ return `request failed with status ${response.status}`;
2257
+ }
2258
+ try {
2259
+ const payload = JSON.parse(text);
2260
+ const detail = payload?.detail ?? payload?.message ?? payload?.error;
2261
+ if (typeof detail === "string") {
2262
+ return detail;
2263
+ }
2264
+ if (detail) {
2265
+ return JSON.stringify(detail);
2266
+ }
2267
+ } catch {
2268
+ }
2269
+ return text;
2270
+ };
2271
+ var fetchCloudEvalJson = async ({
2272
+ baseUrl,
2273
+ authToken,
2274
+ path: path11,
2275
+ method = "GET",
2276
+ query = {},
2277
+ body,
2278
+ idempotencyKey
2279
+ }) => {
2280
+ const url = new URL(`${normalizeApiBase(baseUrl)}${path11}`);
2281
+ for (const [key, value] of Object.entries(query)) {
2282
+ if (value !== void 0 && value !== null && value !== "") {
2283
+ url.searchParams.set(key, String(value));
2284
+ }
2285
+ }
2286
+ const response = await fetch(url, {
2287
+ method,
2288
+ headers: {
2289
+ ...getCLIHeaders(authToken),
2290
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
2291
+ },
2292
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
2293
+ });
2294
+ if (!response.ok) {
2295
+ throw new Error(await responseErrorMessage(response));
2296
+ }
2297
+ return await response.json();
2298
+ };
2299
+
2300
+ // src/reviewCommand.ts
2301
+ var DIRTY_REVIEW_MESSAGE = "Reviews pushed commits only. Add --ignore-dirty to review HEAD anyway.";
2302
+ var runGit = async (cwd, args) => {
2303
+ const child = spawn("git", args, {
2304
+ cwd,
2305
+ stdio: ["ignore", "pipe", "pipe"]
2306
+ });
2307
+ const stdout = [];
2308
+ child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
2309
+ const exitCode = await new Promise(
2310
+ (resolve) => child.on("exit", resolve)
2311
+ );
2312
+ return {
2313
+ ok: exitCode === 0,
2314
+ stdout: Buffer.concat(stdout).toString("utf8").trim()
2315
+ };
2316
+ };
2317
+ var normalizeGithubRepo = (value) => {
2318
+ const text = String(value ?? "").trim();
2319
+ if (!text) return void 0;
2320
+ const withoutGit = text.replace(/\.git$/i, "");
2321
+ const httpsMatch = withoutGit.match(/github\.com[:/]([^/\s]+)\/([^/\s]+)$/i);
2322
+ if (httpsMatch) {
2323
+ return `${httpsMatch[1]}/${httpsMatch[2]}`;
2324
+ }
2325
+ if (/^[^/\s]+\/[^/\s]+$/.test(withoutGit)) {
2326
+ return withoutGit;
2327
+ }
2328
+ return void 0;
2329
+ };
2330
+ var resolveGitMetadata = async (cwd, options) => {
2331
+ const status = await runGit(cwd, ["status", "--porcelain"]);
2332
+ const dirty = status.ok && status.stdout.length > 0;
2333
+ const remote = options.repo ? { ok: true, stdout: options.repo } : await runGit(cwd, ["remote", "get-url", "origin"]);
2334
+ const ref = options.ref ? options.ref : (await runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])).stdout;
2335
+ const sha = options.commitSha ? options.commitSha : (await runGit(cwd, ["rev-parse", "HEAD"])).stdout;
2336
+ return {
2337
+ repo: normalizeGithubRepo(remote.stdout),
2338
+ ref: ref && ref !== "HEAD" ? ref : void 0,
2339
+ commitSha: sha || void 0,
2340
+ dirty
2341
+ };
2342
+ };
2343
+ var asRecord = (value) => value && typeof value === "object" ? value : {};
2344
+ var sourceOf = (project) => asRecord(project.iac_source ?? project.iacSource);
2345
+ var resolveProjectId = async ({
2346
+ baseUrl,
2347
+ token,
2348
+ requestedProjectId,
2349
+ repo,
2350
+ ref,
2351
+ sourceRoot
2352
+ }) => {
2353
+ if (requestedProjectId) {
2354
+ return requestedProjectId;
2355
+ }
2356
+ if (!repo) {
2357
+ throw new Error("Provide --repo or run inside a GitHub-backed Git repository.");
2358
+ }
2359
+ const projects = await fetchCloudEvalJson({
2360
+ baseUrl,
2361
+ authToken: token,
2362
+ path: "/projects"
2363
+ });
2364
+ const matches = projects.filter((project) => {
2365
+ const record = asRecord(project);
2366
+ const source = sourceOf(record);
2367
+ if (source.type !== "github") return false;
2368
+ if (source.repo_full_name !== repo) return false;
2369
+ if (sourceRoot !== void 0 && String(source.source_root ?? "") !== sourceRoot) {
2370
+ return false;
2371
+ }
2372
+ if (ref && source.ref && source.ref !== ref) {
2373
+ return false;
2374
+ }
2375
+ return true;
2376
+ });
2377
+ if (matches.length === 1) {
2378
+ return String(asRecord(matches[0]).id);
2379
+ }
2380
+ if (matches.length > 1) {
2381
+ throw new Error(
2382
+ `Multiple CloudEval projects match ${repo}. Pass --project to choose one.`
2383
+ );
2384
+ }
2385
+ throw new Error(
2386
+ `No CloudEval GitHub project matched ${repo}. Create one in CloudEval or pass --project.`
2387
+ );
2388
+ };
2389
+ var readConfigText = async (cwd, options) => {
2390
+ const configPath = options.config ?? path2.join(cwd, ".cloudeval", "config.yaml");
2391
+ try {
2392
+ return await fs2.readFile(path2.resolve(cwd, configPath), "utf8");
2393
+ } catch {
2394
+ return void 0;
2395
+ }
2396
+ };
2397
+ var parseGateConfig = (configText) => {
2398
+ if (!configText || !/^\s*ci\s*:/m.test(configText) || !/^\s*gates\s*:/m.test(configText)) {
2399
+ return void 0;
2400
+ }
2401
+ const numberValue2 = (key) => {
2402
+ const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)`, "m"));
2403
+ return match ? Number(match[1]) : void 0;
2404
+ };
2405
+ const booleanValue2 = (key) => {
2406
+ const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*(true|false)`, "im"));
2407
+ return match ? match[1].toLowerCase() === "true" : void 0;
2408
+ };
2409
+ return {
2410
+ overallScoreMin: numberValue2("overall_score_min") ?? 80,
2411
+ failOnHighRisk: booleanValue2("fail_on_high_risk") ?? true,
2412
+ maxMonthlyCost: numberValue2("max_monthly_cost")
2413
+ };
2414
+ };
2415
+ var numberFrom = (...values) => {
2416
+ for (const value of values) {
2417
+ if (typeof value === "number" && Number.isFinite(value)) {
2418
+ return value;
2419
+ }
2420
+ if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) {
2421
+ return Number(value);
2422
+ }
2423
+ }
2424
+ return void 0;
2425
+ };
2426
+ var evaluateGate = ({
2427
+ configText,
2428
+ waf,
2429
+ cost
2430
+ }) => {
2431
+ const gateConfig = parseGateConfig(configText);
2432
+ const overallScore = numberFrom(
2433
+ waf?.parsed?.score?.overall,
2434
+ waf?.parsed?.overall_score,
2435
+ waf?.raw?.score
2436
+ );
2437
+ const highRisk = numberFrom(
2438
+ waf?.parsed?.counts?.highRisk,
2439
+ waf?.parsed?.counts?.high_count,
2440
+ waf?.parsed?.highRisk
2441
+ );
2442
+ const monthlyCost = numberFrom(
2443
+ cost?.parsed?.totalSpend?.amount,
2444
+ cost?.parsed?.total_spend?.amount,
2445
+ cost?.raw?.total
2446
+ );
2447
+ if (!gateConfig) {
2448
+ return {
2449
+ status: "warn",
2450
+ reason: "ci.gates is not configured in .cloudeval/config.yaml.",
2451
+ overallScore,
2452
+ highRisk,
2453
+ monthlyCost
2454
+ };
2455
+ }
2456
+ const failures = [];
2457
+ if (overallScore !== void 0 && overallScore < gateConfig.overallScoreMin) {
2458
+ failures.push(
2459
+ `overall score ${overallScore} is below ${gateConfig.overallScoreMin}`
2460
+ );
2461
+ }
2462
+ if (gateConfig.failOnHighRisk && highRisk !== void 0 && highRisk > 0) {
2463
+ failures.push(`${highRisk} high-risk architecture findings`);
2464
+ }
2465
+ if (gateConfig.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost) {
2466
+ failures.push(`monthly cost ${monthlyCost} exceeds ${gateConfig.maxMonthlyCost}`);
2467
+ }
2468
+ return {
2469
+ status: failures.length ? "fail" : "pass",
2470
+ failures,
2471
+ thresholds: gateConfig,
2472
+ overallScore,
2473
+ highRisk,
2474
+ monthlyCost
2475
+ };
2476
+ };
2477
+ var safeFetch = async (input) => {
2478
+ try {
2479
+ return await fetchCloudEvalJson(input);
2480
+ } catch {
2481
+ return void 0;
2482
+ }
2483
+ };
2484
+ var parsePositiveInteger = (value, flagName, fallback) => {
2485
+ if (value === void 0 || value === "") {
2486
+ return fallback;
2487
+ }
2488
+ const parsed = Number(value);
2489
+ if (!Number.isFinite(parsed) || parsed <= 0) {
2490
+ throw new Error(`${flagName} must be a positive number of milliseconds.`);
2491
+ }
2492
+ return Math.floor(parsed);
2493
+ };
2494
+ var extractJobId2 = (value) => {
2495
+ const record = asRecord(value);
2496
+ const job = asRecord(record.job);
2497
+ const candidates = [
2498
+ record.job_id,
2499
+ record.jobId,
2500
+ record.id,
2501
+ job.job_id,
2502
+ job.jobId,
2503
+ job.id
2504
+ ];
2505
+ return candidates.find(
2506
+ (candidate) => typeof candidate === "string" && candidate.trim().length > 0
2507
+ );
2508
+ };
2509
+ var isTerminalJobStatus2 = (value) => {
2510
+ const status = String(asRecord(value).status ?? "").toUpperCase();
2511
+ return [
2512
+ "COMPLETED",
2513
+ "SUCCEEDED",
2514
+ "SUCCESS",
2515
+ "FAILED",
2516
+ "CANCELLED",
2517
+ "CANCELED",
2518
+ "ERROR"
2519
+ ].includes(status);
2520
+ };
2521
+ var isFailedJobStatus = (value) => {
2522
+ const status = String(asRecord(value).status ?? "").toUpperCase();
2523
+ return ["FAILED", "CANCELLED", "CANCELED", "ERROR"].includes(status);
2524
+ };
2525
+ var waitForJob = async ({
2526
+ baseUrl,
2527
+ token,
2528
+ userId,
2529
+ jobId,
2530
+ pollIntervalMs,
2531
+ waitTimeoutMs
2532
+ }) => {
2533
+ const startedAt = Date.now();
2534
+ let lastStatus;
2535
+ for (; ; ) {
2536
+ const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
2537
+ lastStatus = await fetchCloudEvalJson({
2538
+ baseUrl,
2539
+ authToken: token,
2540
+ path: `/jobs/${encodeURIComponent(jobId)}${query}`
2541
+ });
2542
+ const status = String(lastStatus.status ?? "unknown");
2543
+ process.stderr.write(`github sync job ${jobId}: ${status}
2544
+ `);
2545
+ if (isTerminalJobStatus2(lastStatus)) {
2546
+ if (isFailedJobStatus(lastStatus)) {
2547
+ throw new Error(`GitHub sync job ${jobId} finished with status ${status}.`);
2548
+ }
2549
+ return lastStatus;
2550
+ }
2551
+ if (Date.now() - startedAt > waitTimeoutMs) {
2552
+ throw new Error(`Timed out waiting for GitHub sync job ${jobId}.`);
2553
+ }
2554
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
2555
+ }
2556
+ };
2557
+ var fetchProjectById = async ({
2558
+ baseUrl,
2559
+ token,
2560
+ projectId
2561
+ }) => {
2562
+ const projects = await safeFetch({
2563
+ baseUrl,
2564
+ authToken: token,
2565
+ path: "/projects"
2566
+ });
2567
+ return projects?.map(asRecord).find((project) => project.id === projectId);
2568
+ };
2569
+ var buildAiSummaryPrompt = (data) => [
2570
+ "Write a concise CloudEval pull request review summary in Markdown.",
2571
+ "Focus on gate status, Well-Architected posture, cost posture, and security/operational risks.",
2572
+ "Keep it under 160 words. Do not invent facts not present below.",
2573
+ "",
2574
+ `Project: ${data.projectId}`,
2575
+ `Repository: ${data.repo ?? "unknown"}`,
2576
+ `Ref: ${data.ref ?? "unknown"}`,
2577
+ `Commit: ${data.commitSha ?? "unknown"}`,
2578
+ `Gate: ${String(data.gate?.status ?? "unknown").toUpperCase()}`,
2579
+ `Well-Architected score: ${data.gate?.overallScore ?? "unknown"}`,
2580
+ `High-risk findings: ${data.gate?.highRisk ?? "unknown"}`,
2581
+ `Monthly cost: ${data.gate?.monthlyCost ?? "unknown"}`,
2582
+ Array.isArray(data.gate?.failures) && data.gate.failures.length ? `Gate failures: ${data.gate.failures.join("; ")}` : "Gate failures: none reported"
2583
+ ].join("\n");
2584
+ var generateAiSummary = async ({
2585
+ baseUrl,
2586
+ token,
2587
+ user,
2588
+ project,
2589
+ model,
2590
+ data
2591
+ }) => {
2592
+ const core = await import("./dist-CFLR5FML.js");
2593
+ const threadId = `review-${data.projectId}-${Date.now()}`;
2594
+ let markdown = "";
2595
+ for await (const chunk of core.streamChat({
2596
+ baseUrl,
2597
+ authToken: token,
2598
+ message: buildAiSummaryPrompt(data),
2599
+ threadId,
2600
+ user: {
2601
+ id: String(project?.user_id ?? user?.id ?? "cli-user"),
2602
+ name: String(user?.full_name ?? user?.name ?? user?.email ?? "CloudEval CI")
2603
+ },
2604
+ project: project ? {
2605
+ id: String(project.id ?? data.projectId),
2606
+ name: String(project.name ?? data.projectId),
2607
+ user_id: typeof project.user_id === "string" ? project.user_id : void 0,
2608
+ cloud_provider: typeof project.cloud_provider === "string" ? project.cloud_provider : void 0,
2609
+ type: typeof project.type === "string" ? project.type : void 0,
2610
+ connection_ids: Array.isArray(project.connection_ids) ? project.connection_ids : void 0
2611
+ } : {
2612
+ id: String(data.projectId),
2613
+ name: String(data.projectId)
2614
+ },
2615
+ settings: {
2616
+ mode: "ask",
2617
+ ...model ? { model } : {}
2618
+ },
2619
+ completeAfterResponse: true,
2620
+ responseCompletionGraceMs: 250,
2621
+ streamIdleTimeoutMs: 3e4
2622
+ })) {
2623
+ const content = chunk?.content;
2624
+ if (chunk.type === "responding" && typeof content === "string") {
2625
+ markdown += content;
2626
+ }
2627
+ }
2628
+ return {
2629
+ enabled: true,
2630
+ mode: "ask",
2631
+ ...model ? { model } : {},
2632
+ markdown: markdown.trim(),
2633
+ threadId
2634
+ };
2635
+ };
2636
+ var buildMarkdownSummary = (data) => {
2637
+ const gateStatus = String(data.gate?.status ?? "unknown").toUpperCase();
2638
+ const score = data.gate?.overallScore ?? "unknown";
2639
+ const cost = data.gate?.monthlyCost ?? "unknown";
2640
+ const lines = [
2641
+ "### CloudEval review",
2642
+ "",
2643
+ `- **Project:** \`${data.projectId}\``,
2644
+ `- **Repository:** \`${data.repo ?? "unknown"}\``,
2645
+ `- **Ref:** \`${data.ref ?? "unknown"}\``,
2646
+ `- **Commit:** \`${String(data.commitSha ?? "unknown").slice(0, 12)}\``,
2647
+ `- **Gate:** ${gateStatus}`,
2648
+ `- **Well-Architected score:** ${score}`,
2649
+ `- **Monthly cost:** ${cost}`
2650
+ ];
2651
+ if (data.aiSummary?.markdown) {
2652
+ lines.push("", "## AI summary", "", data.aiSummary.markdown);
2653
+ }
2654
+ return lines.join("\n");
2655
+ };
2656
+ var registerReviewCommand = (program2, deps) => {
2657
+ const command = addAuthOptions(
2658
+ program2.command("review").description("Review the current GitHub-backed project from a pushed commit"),
2659
+ deps.defaultBaseUrl
2660
+ ).option("--project <id>", "CloudEval project id. If omitted, resolve by GitHub repo metadata.").option("--repo <owner/repo>", "GitHub repository. Defaults to git origin.").option("--ref <name>", "Git branch/ref. Defaults to current branch.").option("--commit-sha <sha>", "Commit SHA to sync/review. Defaults to local HEAD.").option("--source-root <path>", "GitHub source root used by the CloudEval project.").option("--config <path>", "Path to .cloudeval/config.yaml for gate thresholds.").option("--no-wait", "Submit GitHub sync and return without waiting for analysis.").option("--wait-timeout <ms>", "Maximum time to wait for GitHub sync.", "900000").option("--poll-interval <ms>", "Polling interval while waiting for GitHub sync.", "5000").option("--no-ai-summary", "Skip the AI-written review summary.").option("--ignore-dirty", "Review HEAD even if the local working tree has uncommitted changes.", false).option("--output <dir>", "Write review.json and review.md into a directory.").option("--quiet", "Accepted for CI parity; review output stays machine-readable.", false).option("--progress <mode>", "Accepted for CI parity; review does not stream progress.", "none").option("--model <model>", "Accepted for CI parity with ask/agent modes.").option("--format <format>", "Output format: text, json, ndjson, markdown", "text");
2661
+ command.action(async (options, actionCommand) => {
2662
+ try {
2663
+ const cwd = process.cwd();
2664
+ const git = await resolveGitMetadata(cwd, options);
2665
+ if (git.dirty && !options.ignoreDirty) {
2666
+ throw new Error(DIRTY_REVIEW_MESSAGE);
2667
+ }
2668
+ const context = await resolveAuthContext(options, actionCommand, deps);
2669
+ const repo = normalizeGithubRepo(options.repo) ?? git.repo;
2670
+ const ref = options.ref ?? git.ref;
2671
+ const commitSha = options.commitSha ?? git.commitSha;
2672
+ const sourceRoot = options.sourceRoot;
2673
+ const projectId = await resolveProjectId({
2674
+ baseUrl: context.baseUrl,
2675
+ token: context.token,
2676
+ requestedProjectId: options.project,
2677
+ repo,
2678
+ ref,
2679
+ sourceRoot
2680
+ });
2681
+ const sync = await fetchCloudEvalJson({
2682
+ baseUrl: context.baseUrl,
2683
+ authToken: context.token,
2684
+ path: `/projects/${projectId}/github/sync`,
2685
+ method: "POST",
2686
+ body: commitSha ? { commit_sha: commitSha } : {},
2687
+ idempotencyKey: `cloudeval-review-${projectId}-${commitSha ?? "head"}`
2688
+ });
2689
+ const finalStatus = options.wait === false ? void 0 : extractJobId2(sync) ? await waitForJob({
2690
+ baseUrl: context.baseUrl,
2691
+ token: context.token,
2692
+ userId: context.user?.id,
2693
+ jobId: extractJobId2(sync),
2694
+ pollIntervalMs: parsePositiveInteger(
2695
+ options.pollInterval,
2696
+ "--poll-interval",
2697
+ 5e3
2698
+ ),
2699
+ waitTimeoutMs: parsePositiveInteger(
2700
+ options.waitTimeout,
2701
+ "--wait-timeout",
2702
+ 9e5
2703
+ )
2704
+ }) : void 0;
2705
+ const [cost, waf, configText] = await Promise.all([
2706
+ safeFetch({
2707
+ baseUrl: context.baseUrl,
2708
+ authToken: context.token,
2709
+ path: `/cost-reports/${projectId}/full`
2710
+ }),
2711
+ safeFetch({
2712
+ baseUrl: context.baseUrl,
2713
+ authToken: context.token,
2714
+ path: `/well-architected-reports/${projectId}/full`
2715
+ }),
2716
+ readConfigText(cwd, options)
2717
+ ]);
2718
+ const project = await fetchProjectById({
2719
+ baseUrl: context.baseUrl,
2720
+ token: context.token,
2721
+ projectId
2722
+ });
2723
+ const data = {
2724
+ projectId,
2725
+ repo,
2726
+ ref,
2727
+ commitSha,
2728
+ sourceRoot,
2729
+ sync: finalStatus ? { ...asRecord(sync), finalStatus } : sync,
2730
+ reports: {
2731
+ cost,
2732
+ waf
2733
+ },
2734
+ gate: evaluateGate({ configText, waf, cost })
2735
+ };
2736
+ if (options.aiSummary !== false) {
2737
+ try {
2738
+ data.aiSummary = await generateAiSummary({
2739
+ baseUrl: context.baseUrl,
2740
+ token: context.token,
2741
+ user: context.user,
2742
+ project,
2743
+ model: options.model,
2744
+ data
2745
+ });
2746
+ } catch (error) {
2747
+ data.aiSummary = {
2748
+ enabled: true,
2749
+ status: "failed",
2750
+ error: error?.message ?? "AI summary failed"
2751
+ };
2752
+ }
2753
+ } else {
2754
+ data.aiSummary = { enabled: false };
2755
+ }
2756
+ const summaryMarkdown = buildMarkdownSummary(data);
2757
+ const filesWritten = [];
2758
+ if (options.output) {
2759
+ const outputDir = path2.resolve(options.output);
2760
+ await fs2.mkdir(outputDir, { recursive: true });
2761
+ const jsonPath = path2.join(outputDir, "review.json");
2762
+ const markdownPath = path2.join(outputDir, "review.md");
2763
+ await fs2.writeFile(jsonPath, JSON.stringify(data, null, 2), "utf8");
2764
+ await fs2.writeFile(markdownPath, summaryMarkdown, "utf8");
2765
+ filesWritten.push(jsonPath, markdownPath);
2766
+ }
2767
+ await writeFormattedOutput({
2768
+ command: "review",
2769
+ data: { ...data, summaryMarkdown },
2770
+ format: options.format,
2771
+ filesWritten
2772
+ });
2773
+ if (data.gate.status === "fail") {
2774
+ process.exit(1);
2775
+ }
2776
+ process.exit(0);
2777
+ } catch (error) {
2778
+ console.error(error?.message ?? "Review failed");
2779
+ process.exit(1);
2780
+ }
2781
+ });
2782
+ };
2783
+
2220
2784
  // src/recipesCommand.ts
2221
2785
  import { randomUUID } from "crypto";
2222
- import fs2 from "fs/promises";
2786
+ import fs3 from "fs/promises";
2223
2787
 
2224
2788
  // src/askProgress.ts
2225
2789
  var ASK_PROGRESS_MODES = /* @__PURE__ */ new Set(["auto", "stderr", "ndjson", "none"]);
@@ -3455,7 +4019,7 @@ var writeRecipeList = async (options) => {
3455
4019
  if (format === "table" || format === "text") {
3456
4020
  const text = renderRecipesTable();
3457
4021
  if (options.output) {
3458
- await fs2.writeFile(options.output, text, "utf8");
4022
+ await fs3.writeFile(options.output, text, "utf8");
3459
4023
  return;
3460
4024
  }
3461
4025
  process.stdout.write(text);
@@ -3464,7 +4028,7 @@ var writeRecipeList = async (options) => {
3464
4028
  if (format === "markdown") {
3465
4029
  const text = renderRecipesMarkdown();
3466
4030
  if (options.output) {
3467
- await fs2.writeFile(options.output, text, "utf8");
4031
+ await fs3.writeFile(options.output, text, "utf8");
3468
4032
  return;
3469
4033
  }
3470
4034
  process.stdout.write(text);
@@ -3482,7 +4046,7 @@ var writeRecipeShow = async (recipe, options) => {
3482
4046
  if (format === "markdown" || format === "text") {
3483
4047
  const text = renderRecipeMarkdown(recipe);
3484
4048
  if (options.output) {
3485
- await fs2.writeFile(options.output, text, "utf8");
4049
+ await fs3.writeFile(options.output, text, "utf8");
3486
4050
  return;
3487
4051
  }
3488
4052
  process.stdout.write(text);
@@ -3677,15 +4241,15 @@ var registerRecipesCommand = (program2, deps) => {
3677
4241
  };
3678
4242
 
3679
4243
  // src/skillsCommand.ts
3680
- import fs4 from "fs/promises";
4244
+ import fs5 from "fs/promises";
3681
4245
 
3682
4246
  // src/skills/catalog.ts
3683
- import fs3 from "fs/promises";
4247
+ import fs4 from "fs/promises";
3684
4248
  import fsSync from "fs";
3685
- import path2 from "path";
4249
+ import path3 from "path";
3686
4250
  import { fileURLToPath as fileURLToPath2 } from "url";
3687
- var repoRoot = path2.resolve(fileURLToPath2(new URL("../../../../", import.meta.url)));
3688
- var defaultSkillsPath = path2.join(repoRoot, "skills");
4251
+ var repoRoot = path3.resolve(fileURLToPath2(new URL("../../../../", import.meta.url)));
4252
+ var defaultSkillsPath = path3.join(repoRoot, "skills");
3689
4253
  var requiredSkillSections = [
3690
4254
  "## WHEN",
3691
4255
  "## DO NOT USE FOR",
@@ -3773,7 +4337,7 @@ var normalizeSkillId = (id) => {
3773
4337
  return metadataById.has(prefixed) ? prefixed : normalized;
3774
4338
  };
3775
4339
  var getSkillsPath = () => process.env.CLOUDEVAL_SKILLS_PATH ?? defaultSkillsPath;
3776
- var skillFilePath = (id) => path2.join(getSkillsPath(), id, "SKILL.md");
4340
+ var skillFilePath = (id) => path3.join(getSkillsPath(), id, "SKILL.md");
3777
4341
  var fileExists = (filePath) => {
3778
4342
  try {
3779
4343
  return fsSync.statSync(filePath).isFile();
@@ -3785,7 +4349,7 @@ var readSkillFile = async (id) => {
3785
4349
  const filePath = skillFilePath(id);
3786
4350
  if (fileExists(filePath)) {
3787
4351
  return {
3788
- content: await fs3.readFile(filePath, "utf8"),
4352
+ content: await fs4.readFile(filePath, "utf8"),
3789
4353
  path: filePath,
3790
4354
  source: "filesystem"
3791
4355
  };
@@ -3982,7 +4546,7 @@ var writeSkillList = async (options) => {
3982
4546
  if (format === "table" || format === "text") {
3983
4547
  const text = renderSkillsTable(skills);
3984
4548
  if (options.output) {
3985
- await fs4.writeFile(options.output, text, "utf8");
4549
+ await fs5.writeFile(options.output, text, "utf8");
3986
4550
  return;
3987
4551
  }
3988
4552
  process.stdout.write(text);
@@ -3991,7 +4555,7 @@ var writeSkillList = async (options) => {
3991
4555
  if (format === "markdown") {
3992
4556
  const text = renderSkillsMarkdown(skills);
3993
4557
  if (options.output) {
3994
- await fs4.writeFile(options.output, text, "utf8");
4558
+ await fs5.writeFile(options.output, text, "utf8");
3995
4559
  return;
3996
4560
  }
3997
4561
  process.stdout.write(text);
@@ -4014,7 +4578,7 @@ var writeSkillShow = async (id, options) => {
4014
4578
  const format = options.format ?? "markdown";
4015
4579
  if (format === "markdown" || format === "text") {
4016
4580
  if (options.output) {
4017
- await fs4.writeFile(options.output, skill.content, "utf8");
4581
+ await fs5.writeFile(options.output, skill.content, "utf8");
4018
4582
  return;
4019
4583
  }
4020
4584
  process.stdout.write(skill.content);
@@ -4069,7 +4633,7 @@ var writeSkillDoctor = async (options) => {
4069
4633
  ""
4070
4634
  ].filter(Boolean).join("\n");
4071
4635
  if (options.output) {
4072
- await fs4.writeFile(options.output, `${text}
4636
+ await fs5.writeFile(options.output, `${text}
4073
4637
  `, "utf8");
4074
4638
  return;
4075
4639
  }
@@ -4101,7 +4665,7 @@ var registerSkillsCommand = (program2) => {
4101
4665
  const text = `${skillsPath}
4102
4666
  `;
4103
4667
  if (options.output) {
4104
- await fs4.writeFile(options.output, text, "utf8");
4668
+ await fs5.writeFile(options.output, text, "utf8");
4105
4669
  return;
4106
4670
  }
4107
4671
  process.stdout.write(text);
@@ -4274,10 +4838,10 @@ var registerOpenCommand = (program2, deps) => {
4274
4838
  };
4275
4839
 
4276
4840
  // src/projectsCommand.ts
4277
- import path3 from "path";
4278
- import fs5 from "fs/promises";
4841
+ import path4 from "path";
4842
+ import fs6 from "fs/promises";
4279
4843
  import os from "os";
4280
- import { spawn } from "child_process";
4844
+ import { spawn as spawn2 } from "child_process";
4281
4845
 
4282
4846
  // src/projectDiagramImage.ts
4283
4847
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
@@ -4434,50 +4998,6 @@ var downloadProjectDiagramImage = async (input) => {
4434
4998
  };
4435
4999
  };
4436
5000
 
4437
- // src/apiClient.ts
4438
- var responseErrorMessage = async (response) => {
4439
- const text = await response.text();
4440
- if (!text) {
4441
- return `request failed with status ${response.status}`;
4442
- }
4443
- try {
4444
- const payload = JSON.parse(text);
4445
- const detail = payload?.detail ?? payload?.message ?? payload?.error;
4446
- if (typeof detail === "string") {
4447
- return detail;
4448
- }
4449
- if (detail) {
4450
- return JSON.stringify(detail);
4451
- }
4452
- } catch {
4453
- }
4454
- return text;
4455
- };
4456
- var fetchCloudEvalJson = async ({
4457
- baseUrl,
4458
- authToken,
4459
- path: path10,
4460
- method = "GET",
4461
- query = {},
4462
- body
4463
- }) => {
4464
- const url = new URL(`${normalizeApiBase(baseUrl)}${path10}`);
4465
- for (const [key, value] of Object.entries(query)) {
4466
- if (value !== void 0 && value !== null && value !== "") {
4467
- url.searchParams.set(key, String(value));
4468
- }
4469
- }
4470
- const response = await fetch(url, {
4471
- method,
4472
- headers: getCLIHeaders(authToken),
4473
- ...body === void 0 ? {} : { body: JSON.stringify(body) }
4474
- });
4475
- if (!response.ok) {
4476
- throw new Error(await responseErrorMessage(response));
4477
- }
4478
- return await response.json();
4479
- };
4480
-
4481
5001
  // src/graphClient.ts
4482
5002
  var normalizeGraphInsightFocus = (focus) => {
4483
5003
  const normalized = String(focus ?? "overview").trim().toLowerCase();
@@ -4637,7 +5157,7 @@ var writeProjectListOutput = async ({
4637
5157
  });
4638
5158
  }
4639
5159
  if (options.output) {
4640
- await fs5.writeFile(options.output, text, "utf8");
5160
+ await fs6.writeFile(options.output, text, "utf8");
4641
5161
  return;
4642
5162
  }
4643
5163
  process.stdout.write(text);
@@ -4646,10 +5166,10 @@ var fileBlob = async (filePath) => {
4646
5166
  if (!filePath) {
4647
5167
  return void 0;
4648
5168
  }
4649
- const bytes = await fs5.readFile(filePath);
5169
+ const bytes = await fs6.readFile(filePath);
4650
5170
  return {
4651
5171
  blob: new Blob([bytes], { type: "application/json" }),
4652
- name: path3.basename(filePath)
5172
+ name: path4.basename(filePath)
4653
5173
  };
4654
5174
  };
4655
5175
  var normalizeWorkspacePath = (value) => value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/^\.\//, "").split("/").filter((part) => part && part !== ".").join("/");
@@ -4687,7 +5207,7 @@ var generateWorkspaceConfig = (entry, parameters, sourceEntry) => {
4687
5207
  ].filter((line) => line.length > 0).join("\n");
4688
5208
  };
4689
5209
  var runCommand = (command, args) => new Promise((resolve, reject) => {
4690
- const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
5210
+ const child = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"] });
4691
5211
  const stdout = [];
4692
5212
  const stderr = [];
4693
5213
  child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
@@ -4711,9 +5231,9 @@ var runCommand = (command, args) => new Promise((resolve, reject) => {
4711
5231
  });
4712
5232
  var isBicepPath = (filePath) => /\.bicep$/i.test(filePath);
4713
5233
  var compiledBicepPathFor = (entry) => {
4714
- const parsed = path3.posix.parse(entry);
5234
+ const parsed = path4.posix.parse(entry);
4715
5235
  return normalizeWorkspacePath(
4716
- path3.posix.join(
5236
+ path4.posix.join(
4717
5237
  ".cloudeval/template-cache/compiled",
4718
5238
  parsed.dir,
4719
5239
  `${parsed.name}.json`
@@ -4721,18 +5241,18 @@ var compiledBicepPathFor = (entry) => {
4721
5241
  );
4722
5242
  };
4723
5243
  var compileBicepEntry = async (root, entry) => {
4724
- const tempDir = await fs5.mkdtemp(path3.join(os.tmpdir(), "cloudeval-bicep-"));
4725
- const outputPath = path3.join(tempDir, "compiled.json");
5244
+ const tempDir = await fs6.mkdtemp(path4.join(os.tmpdir(), "cloudeval-bicep-"));
5245
+ const outputPath = path4.join(tempDir, "compiled.json");
4726
5246
  try {
4727
5247
  await runCommand("az", [
4728
5248
  "bicep",
4729
5249
  "build",
4730
5250
  "--file",
4731
- path3.join(root, entry),
5251
+ path4.join(root, entry),
4732
5252
  "--outfile",
4733
5253
  outputPath
4734
5254
  ]);
4735
- const bytes = await fs5.readFile(outputPath);
5255
+ const bytes = await fs6.readFile(outputPath);
4736
5256
  return {
4737
5257
  path: compiledBicepPathFor(entry),
4738
5258
  blob: new Blob([bytes], { type: "application/json" })
@@ -4746,16 +5266,16 @@ var compileBicepEntry = async (root, entry) => {
4746
5266
  }
4747
5267
  throw new Error(`Failed to compile Bicep workspace entry '${entry}': ${message}`);
4748
5268
  } finally {
4749
- await fs5.rm(tempDir, { recursive: true, force: true });
5269
+ await fs6.rm(tempDir, { recursive: true, force: true });
4750
5270
  }
4751
5271
  };
4752
5272
  var collectWorkspacePaths = async (root) => {
4753
5273
  const paths = [];
4754
5274
  const visit = async (directory) => {
4755
- const entries = await fs5.readdir(directory, { withFileTypes: true });
5275
+ const entries = await fs6.readdir(directory, { withFileTypes: true });
4756
5276
  for (const entry of entries) {
4757
- const absolute = path3.join(directory, entry.name);
4758
- const relative = normalizeWorkspacePath(path3.relative(root, absolute));
5277
+ const absolute = path4.join(directory, entry.name);
5278
+ const relative = normalizeWorkspacePath(path4.relative(root, absolute));
4759
5279
  if (!relative || isIgnoredWorkspacePath(relative)) {
4760
5280
  continue;
4761
5281
  }
@@ -4822,8 +5342,8 @@ var resolveWorkspaceParameters = (paths, explicitParameters, config) => {
4822
5342
  return findFirstPath(paths, ["azuredeploy.parameters.json", "parameters.json"]);
4823
5343
  };
4824
5344
  var collectWorkspaceFiles = async (workspaceDir, options) => {
4825
- const root = path3.resolve(workspaceDir);
4826
- const stat = await fs5.stat(root).catch(() => void 0);
5345
+ const root = path4.resolve(workspaceDir);
5346
+ const stat = await fs6.stat(root).catch(() => void 0);
4827
5347
  if (!stat?.isDirectory()) {
4828
5348
  throw new Error(`--workspace-dir '${workspaceDir}' is not a directory.`);
4829
5349
  }
@@ -4831,7 +5351,7 @@ var collectWorkspaceFiles = async (workspaceDir, options) => {
4831
5351
  const existingConfigPath = paths.find(
4832
5352
  (filePath) => filePath.toLowerCase() === ".cloudeval/config.yaml"
4833
5353
  );
4834
- const config = existingConfigPath ? readWorkspaceConfig(await fs5.readFile(path3.join(root, existingConfigPath), "utf8")) : void 0;
5354
+ const config = existingConfigPath ? readWorkspaceConfig(await fs6.readFile(path4.join(root, existingConfigPath), "utf8")) : void 0;
4835
5355
  const entry = detectWorkspaceEntry(paths, options.workspaceEntry, config);
4836
5356
  const parameters = resolveWorkspaceParameters(paths, options.workspaceParameters, config);
4837
5357
  const compiledEntry = isBicepPath(entry) ? await compileBicepEntry(root, entry) : void 0;
@@ -4853,7 +5373,7 @@ var collectWorkspaceFiles = async (workspaceDir, options) => {
4853
5373
  });
4854
5374
  continue;
4855
5375
  }
4856
- const bytes = await fs5.readFile(path3.join(root, relativePath));
5376
+ const bytes = await fs6.readFile(path4.join(root, relativePath));
4857
5377
  files.push({
4858
5378
  path: relativePath,
4859
5379
  blob: new Blob([bytes], {
@@ -4915,9 +5435,9 @@ var appendOptionValue = (value, previous = []) => [
4915
5435
  value
4916
5436
  ];
4917
5437
  var writeDiagramImageHeaders = async (outputPath, headers) => {
4918
- await fs5.mkdir(path3.dirname(outputPath), { recursive: true });
5438
+ await fs6.mkdir(path4.dirname(outputPath), { recursive: true });
4919
5439
  const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
4920
- await fs5.writeFile(outputPath, `${text}
5440
+ await fs6.writeFile(outputPath, `${text}
4921
5441
  `, "utf8");
4922
5442
  };
4923
5443
  var listProjectsForContext = async (core, context) => {
@@ -5080,10 +5600,10 @@ var configureDiagramExportCommand = (command, deps) => addAuthOptions(command, d
5080
5600
  publicGraph,
5081
5601
  syncVersion: options.syncVersion
5082
5602
  });
5083
- const outputPath = path3.resolve(options.output);
5084
- const headersOutputPath = options.headersOutput ? path3.resolve(options.headersOutput) : void 0;
5085
- await fs5.mkdir(path3.dirname(outputPath), { recursive: true });
5086
- await fs5.writeFile(outputPath, result.bytes);
5603
+ const outputPath = path4.resolve(options.output);
5604
+ const headersOutputPath = options.headersOutput ? path4.resolve(options.headersOutput) : void 0;
5605
+ await fs6.mkdir(path4.dirname(outputPath), { recursive: true });
5606
+ await fs6.writeFile(outputPath, result.bytes);
5087
5607
  const filesWritten = [outputPath];
5088
5608
  if (headersOutputPath) {
5089
5609
  await writeDiagramImageHeaders(headersOutputPath, result.headers);
@@ -5217,7 +5737,7 @@ var registerProjectsCommand = (program2, deps) => {
5217
5737
  const parameters = await fileBlob(options.parametersFile);
5218
5738
  const workspace = options.workspaceDir ? await collectWorkspaceFiles(options.workspaceDir, options) : void 0;
5219
5739
  const cloudSync = resolveAzureCloudSyncInput(options);
5220
- const inferredName = options.name || (options.workspaceDir ? path3.basename(path3.resolve(options.workspaceDir)) : void 0) || (cloudSync ? "Cloud sync" : void 0) || (options.templateFile ? path3.basename(options.templateFile, path3.extname(options.templateFile)) : void 0);
5740
+ const inferredName = options.name || (options.workspaceDir ? path4.basename(path4.resolve(options.workspaceDir)) : void 0) || (cloudSync ? "Cloud sync" : void 0) || (options.templateFile ? path4.basename(options.templateFile, path4.extname(options.templateFile)) : void 0);
5221
5741
  const result = await core.createQuickProject({
5222
5742
  baseUrl: context.baseUrl,
5223
5743
  authToken: context.token,
@@ -5321,8 +5841,8 @@ var writeConnectionsListOutput = async ({
5321
5841
  if (format === "text") {
5322
5842
  const text = renderConnectionsListText(data);
5323
5843
  if (options.output) {
5324
- const fs13 = await import("fs/promises");
5325
- await fs13.writeFile(options.output, text, "utf8");
5844
+ const fs14 = await import("fs/promises");
5845
+ await fs14.writeFile(options.output, text, "utf8");
5326
5846
  return;
5327
5847
  }
5328
5848
  process.stdout.write(text);
@@ -5656,8 +6176,8 @@ var write = async (command, data, options, frontendUrl) => {
5656
6176
  const text = renderBillingText(command, data);
5657
6177
  if (text) {
5658
6178
  if (options.output) {
5659
- const fs13 = await import("fs/promises");
5660
- await fs13.writeFile(options.output, text, "utf8");
6179
+ const fs14 = await import("fs/promises");
6180
+ await fs14.writeFile(options.output, text, "utf8");
5661
6181
  return;
5662
6182
  }
5663
6183
  process.stdout.write(text);
@@ -5926,14 +6446,14 @@ var registerBillingCommands = (program2, deps) => {
5926
6446
  };
5927
6447
 
5928
6448
  // src/mcpCommand.ts
5929
- import fs8 from "fs/promises";
5930
- import path5 from "path";
6449
+ import fs9 from "fs/promises";
6450
+ import path6 from "path";
5931
6451
  import { randomUUID as randomUUID2 } from "crypto";
5932
6452
 
5933
6453
  // src/mcpSetupCommand.ts
5934
- import fs6 from "fs/promises";
6454
+ import fs7 from "fs/promises";
5935
6455
  import os2 from "os";
5936
- import path4 from "path";
6456
+ import path5 from "path";
5937
6457
  var MCP_SETUP_CLIENTS = ["codex", "claude", "cursor", "vscode", "generic"];
5938
6458
  var CLIENTS = new Set(MCP_SETUP_CLIENTS);
5939
6459
  var TOOLSETS = /* @__PURE__ */ new Set([
@@ -5959,7 +6479,7 @@ var normalizeMcpSetupToolset = (value) => {
5959
6479
  };
5960
6480
  var defaultConfigPath = (client) => {
5961
6481
  if (client === "claude") {
5962
- return path4.join(
6482
+ return path5.join(
5963
6483
  os2.homedir(),
5964
6484
  "Library",
5965
6485
  "Application Support",
@@ -5968,10 +6488,10 @@ var defaultConfigPath = (client) => {
5968
6488
  );
5969
6489
  }
5970
6490
  if (client === "cursor") {
5971
- return path4.join(os2.homedir(), ".cursor", "mcp.json");
6491
+ return path5.join(os2.homedir(), ".cursor", "mcp.json");
5972
6492
  }
5973
6493
  if (client === "vscode") {
5974
- return path4.join(process.cwd(), ".vscode", "mcp.json");
6494
+ return path5.join(process.cwd(), ".vscode", "mcp.json");
5975
6495
  }
5976
6496
  return void 0;
5977
6497
  };
@@ -6081,7 +6601,7 @@ var formatMcpClientSetupText = (setup, options = {}) => {
6081
6601
  };
6082
6602
  var readJsonObject = async (filePath) => {
6083
6603
  try {
6084
- const raw = await fs6.readFile(filePath, "utf8");
6604
+ const raw = await fs7.readFile(filePath, "utf8");
6085
6605
  const parsed = JSON.parse(raw);
6086
6606
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
6087
6607
  } catch (error) {
@@ -6106,8 +6626,8 @@ var writeMcpClientConfig = async (setup) => {
6106
6626
  cloudeval: nextServer
6107
6627
  }
6108
6628
  };
6109
- await fs6.mkdir(path4.dirname(setup.configPath), { recursive: true });
6110
- await fs6.writeFile(setup.configPath, `${JSON.stringify(next, null, 2)}
6629
+ await fs7.mkdir(path5.dirname(setup.configPath), { recursive: true });
6630
+ await fs7.writeFile(setup.configPath, `${JSON.stringify(next, null, 2)}
6111
6631
  `, {
6112
6632
  encoding: "utf8",
6113
6633
  mode: 384
@@ -6116,9 +6636,9 @@ var writeMcpClientConfig = async (setup) => {
6116
6636
  };
6117
6637
 
6118
6638
  // src/templateValidationClient.ts
6119
- import fs7 from "fs/promises";
6639
+ import fs8 from "fs/promises";
6120
6640
  var readJsonFile = async (filePath) => {
6121
- const text = await fs7.readFile(filePath, "utf8");
6641
+ const text = await fs8.readFile(filePath, "utf8");
6122
6642
  try {
6123
6643
  return JSON.parse(text);
6124
6644
  } catch (error) {
@@ -6169,7 +6689,7 @@ var stringField = (value, field) => {
6169
6689
  const raw = value?.[field];
6170
6690
  return typeof raw === "string" && raw.trim() ? raw : void 0;
6171
6691
  };
6172
- var extractJobId2 = (value) => {
6692
+ var extractJobId3 = (value) => {
6173
6693
  const record = recordValue(value);
6174
6694
  const job = recordValue(record?.job);
6175
6695
  const data = recordValue(record?.data);
@@ -6177,7 +6697,7 @@ var extractJobId2 = (value) => {
6177
6697
  return stringField(job, "job_id") ?? stringField(job, "jobId") ?? stringField(record, "job_id") ?? stringField(record, "jobId") ?? stringField(dataJob, "job_id") ?? stringField(dataJob, "jobId");
6178
6698
  };
6179
6699
  var normalizedStatus = (value) => String(recordValue(value)?.status ?? "").trim().toLowerCase();
6180
- var isTerminalJobStatus2 = (value) => [
6700
+ var isTerminalJobStatus3 = (value) => [
6181
6701
  "completed",
6182
6702
  "succeeded",
6183
6703
  "failed",
@@ -6326,7 +6846,7 @@ var getTemplateValidationJobResult = async (input) => fetchCloudEvalJson({
6326
6846
  query: { user_id: input.userId }
6327
6847
  });
6328
6848
  var waitForTemplateValidationResult = async (input) => {
6329
- const jobId = extractJobId2(input.submitted);
6849
+ const jobId = extractJobId3(input.submitted);
6330
6850
  if (!jobId) {
6331
6851
  return input.submitted;
6332
6852
  }
@@ -6336,7 +6856,7 @@ var waitForTemplateValidationResult = async (input) => {
6336
6856
  let status;
6337
6857
  for (; ; ) {
6338
6858
  status = await getTemplateValidationJobStatus({ ...input, jobId });
6339
- if (isTerminalJobStatus2(status)) {
6859
+ if (isTerminalJobStatus3(status)) {
6340
6860
  break;
6341
6861
  }
6342
6862
  if (Date.now() >= deadline) {
@@ -8507,12 +9027,12 @@ var pickReportDownloadPayload2 = (value, view) => {
8507
9027
  }
8508
9028
  return value;
8509
9029
  };
8510
- var extractJobId3 = (value) => {
9030
+ var extractJobId4 = (value) => {
8511
9031
  if (!value || typeof value !== "object") return void 0;
8512
9032
  const record = value;
8513
9033
  return record.job_id ?? record.id ?? record.job?.job_id ?? record.job?.id ?? record.data?.job_id ?? record.data?.job?.job_id;
8514
9034
  };
8515
- var isTerminalJobStatus3 = (value) => {
9035
+ var isTerminalJobStatus4 = (value) => {
8516
9036
  if (!value || typeof value !== "object") return true;
8517
9037
  const status = String(
8518
9038
  value.status ?? ""
@@ -8528,9 +9048,9 @@ var isTerminalJobStatus3 = (value) => {
8528
9048
  };
8529
9049
  var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8530
9050
  var writeHeaderFile = async (outputPath, headers) => {
8531
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
9051
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
8532
9052
  const text = Object.entries(headers).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}: ${value}`).join("\n");
8533
- await fs8.writeFile(outputPath, `${text}
9053
+ await fs9.writeFile(outputPath, `${text}
8534
9054
  `, "utf8");
8535
9055
  };
8536
9056
  var resolveInvocationConfig = async (serverOptions, args) => {
@@ -8587,15 +9107,8 @@ var resolveProject2 = async (config, args, auth) => {
8587
9107
  const requestedProjectId = stringValue(args.projectId) ?? config.defaultProjectId;
8588
9108
  const userId = auth.user?.id;
8589
9109
  if (!userId) {
8590
- if (requestedProjectId) {
8591
- return {
8592
- id: requestedProjectId,
8593
- name: "Selected Project",
8594
- cloud_provider: "azure"
8595
- };
8596
- }
8597
9110
  throw new Error(
8598
- "Could not determine the authenticated user. Provide projectId."
9111
+ "Could not determine the authenticated user. Run `cloudeval login` and retry."
8599
9112
  );
8600
9113
  }
8601
9114
  const projects = await auth.core.getProjects(
@@ -8604,12 +9117,15 @@ var resolveProject2 = async (config, args, auth) => {
8604
9117
  userId
8605
9118
  );
8606
9119
  if (requestedProjectId) {
8607
- return projects.find((project) => project.id === requestedProjectId) ?? {
8608
- id: requestedProjectId,
8609
- name: "Selected Project",
8610
- user_id: userId,
8611
- cloud_provider: "azure"
8612
- };
9120
+ const match = projects.find(
9121
+ (project) => project.id === requestedProjectId
9122
+ );
9123
+ if (!match) {
9124
+ throw new Error(
9125
+ `Project ${requestedProjectId} was not found for authenticated user ${userId}. Run \`cloudeval projects list\` to choose a visible project.`
9126
+ );
9127
+ }
9128
+ return match;
8613
9129
  }
8614
9130
  const selected = projects.find((project) => project.name === "Playground") ?? projects[0];
8615
9131
  if (selected) {
@@ -8693,7 +9209,7 @@ var assertModelAvailable = async (config, token) => {
8693
9209
  }
8694
9210
  };
8695
9211
  var waitForReportJobs2 = async (input) => {
8696
- const jobIds = input.submitted.map(extractJobId3).filter(Boolean);
9212
+ const jobIds = input.submitted.map(extractJobId4).filter(Boolean);
8697
9213
  if (!jobIds.length) {
8698
9214
  return input.submitted;
8699
9215
  }
@@ -8707,7 +9223,7 @@ var waitForReportJobs2 = async (input) => {
8707
9223
  userId: input.userId,
8708
9224
  jobId
8709
9225
  });
8710
- if (isTerminalJobStatus3(lastStatus)) {
9226
+ if (isTerminalJobStatus4(lastStatus)) {
8711
9227
  break;
8712
9228
  }
8713
9229
  await sleep3(input.pollIntervalMs);
@@ -8776,13 +9292,13 @@ var downloadReports = async (config, args, auth) => {
8776
9292
  const filesWritten = [];
8777
9293
  if (outputPath) {
8778
9294
  if (reportTypes.length > 1) {
8779
- const stat = await fs8.stat(outputPath).catch(() => void 0);
8780
- const outputIsDirectory = stat?.isDirectory() || !path5.extname(outputPath);
9295
+ const stat = await fs9.stat(outputPath).catch(() => void 0);
9296
+ const outputIsDirectory = stat?.isDirectory() || !path6.extname(outputPath);
8781
9297
  if (outputIsDirectory) {
8782
- await fs8.mkdir(outputPath, { recursive: true });
9298
+ await fs9.mkdir(outputPath, { recursive: true });
8783
9299
  for (const [key, value] of Object.entries(payload)) {
8784
- const file = path5.join(outputPath, `${projectId}-${key}-report.json`);
8785
- await fs8.writeFile(
9300
+ const file = path6.join(outputPath, `${projectId}-${key}-report.json`);
9301
+ await fs9.writeFile(
8786
9302
  file,
8787
9303
  `${JSON.stringify(value, null, 2)}
8788
9304
  `,
@@ -8791,8 +9307,8 @@ var downloadReports = async (config, args, auth) => {
8791
9307
  filesWritten.push(file);
8792
9308
  }
8793
9309
  } else {
8794
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8795
- await fs8.writeFile(
9310
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
9311
+ await fs9.writeFile(
8796
9312
  outputPath,
8797
9313
  `${JSON.stringify(data, null, 2)}
8798
9314
  `,
@@ -8801,8 +9317,8 @@ var downloadReports = async (config, args, auth) => {
8801
9317
  filesWritten.push(outputPath);
8802
9318
  }
8803
9319
  } else {
8804
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
8805
- await fs8.writeFile(
9320
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
9321
+ await fs9.writeFile(
8806
9322
  outputPath,
8807
9323
  `${JSON.stringify(data, null, 2)}
8808
9324
  `,
@@ -9112,7 +9628,7 @@ var buildToolHandlers = (serverOptions) => {
9112
9628
  if (!rawOutputPath) {
9113
9629
  throw new Error("outputPath is required.");
9114
9630
  }
9115
- const outputPath = path5.resolve(rawOutputPath);
9631
+ const outputPath = path6.resolve(rawOutputPath);
9116
9632
  const publicGraph = booleanValue(args.public) ?? false;
9117
9633
  const auth = publicGraph ? void 0 : await resolveAuth(config, { requireUser: true });
9118
9634
  const layout = normalizeProjectDiagramImageLayout(stringValue(args.layout));
@@ -9133,11 +9649,11 @@ var buildToolHandlers = (serverOptions) => {
9133
9649
  publicGraph,
9134
9650
  syncVersion: stringValue(args.syncVersion)
9135
9651
  });
9136
- await fs8.mkdir(path5.dirname(outputPath), { recursive: true });
9137
- await fs8.writeFile(outputPath, result.bytes);
9652
+ await fs9.mkdir(path6.dirname(outputPath), { recursive: true });
9653
+ await fs9.writeFile(outputPath, result.bytes);
9138
9654
  const filesWritten = [outputPath];
9139
9655
  const rawHeadersOutputPath = stringValue(args.headersOutputPath);
9140
- const headersOutputPath = rawHeadersOutputPath ? path5.resolve(rawHeadersOutputPath) : void 0;
9656
+ const headersOutputPath = rawHeadersOutputPath ? path6.resolve(rawHeadersOutputPath) : void 0;
9141
9657
  if (headersOutputPath) {
9142
9658
  await writeHeaderFile(headersOutputPath, result.headers);
9143
9659
  filesWritten.push(headersOutputPath);
@@ -10009,7 +10525,7 @@ var buildToolHandlers = (serverOptions) => {
10009
10525
  projectId,
10010
10526
  type,
10011
10527
  submitted,
10012
- jobs: submitted.map(extractJobId3).filter(Boolean),
10528
+ jobs: submitted.map(extractJobId4).filter(Boolean),
10013
10529
  finalStatuses
10014
10530
  },
10015
10531
  frontendUrl: reportsFrontendUrl(config, { projectId, type })
@@ -10862,7 +11378,7 @@ var registerMcpCommand = (program2, deps) => {
10862
11378
  note
10863
11379
  });
10864
11380
  if (options.output) {
10865
- await fs8.writeFile(options.output, text, "utf8");
11381
+ await fs9.writeFile(options.output, text, "utf8");
10866
11382
  } else {
10867
11383
  process.stdout.write(text);
10868
11384
  }
@@ -11054,23 +11570,23 @@ Discovery:
11054
11570
  };
11055
11571
 
11056
11572
  // src/credentialsCommand.ts
11057
- var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
11573
+ var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
11058
11574
  var arrayFromPayload = (payload, key) => {
11059
- const record = asRecord(payload);
11575
+ const record = asRecord2(payload);
11060
11576
  const value = record[key] ?? record.data;
11061
11577
  return Array.isArray(value) ? value.filter((item) => item && typeof item === "object") : [];
11062
11578
  };
11063
11579
  var credentialFromPayload = (payload) => {
11064
- const record = asRecord(payload);
11065
- return asRecord(record.credential ?? record.data ?? record);
11580
+ const record = asRecord2(payload);
11581
+ return asRecord2(record.credential ?? record.data ?? record);
11066
11582
  };
11067
11583
  var secretFromPayload = (payload) => {
11068
- const record = asRecord(payload);
11584
+ const record = asRecord2(payload);
11069
11585
  const value = record.access_key ?? record.accessKey ?? record.secret;
11070
11586
  return typeof value === "string" ? value : void 0;
11071
11587
  };
11072
11588
  var projectIdFromPayload = (payload, fallback) => {
11073
- const record = asRecord(payload);
11589
+ const record = asRecord2(payload);
11074
11590
  const credential = credentialFromPayload(payload);
11075
11591
  const value = record.project_id ?? record.projectId ?? credential.project_id ?? credential.projectId ?? (Array.isArray(credential.project_ids) ? credential.project_ids[0] : void 0) ?? fallback;
11076
11592
  return typeof value === "string" ? value : void 0;
@@ -11104,7 +11620,7 @@ var writeCredentialOutput = async (input) => {
11104
11620
  return;
11105
11621
  }
11106
11622
  if (input.format === "text" || !input.format) {
11107
- const record = asRecord(input.data);
11623
+ const record = asRecord2(input.data);
11108
11624
  const credentials = arrayFromPayload(input.data, "credentials").length > 0 ? arrayFromPayload(input.data, "credentials") : record.credential ? [credentialFromPayload(input.data)] : arrayFromPayload(input.data, "templates");
11109
11625
  if (credentials.length) {
11110
11626
  process.stdout.write(formatTextTable(formatCredentialTextRows(credentials)));
@@ -11228,9 +11744,9 @@ import { randomUUID as randomUUID4 } from "crypto";
11228
11744
  // src/localHooks.ts
11229
11745
  import { exec } from "child_process";
11230
11746
  import { randomUUID as randomUUID3 } from "crypto";
11231
- import fs9 from "fs/promises";
11747
+ import fs10 from "fs/promises";
11232
11748
  import os4 from "os";
11233
- import path6 from "path";
11749
+ import path7 from "path";
11234
11750
  var normalizeHooks = (config, event) => {
11235
11751
  if (config.hooks?.enabled !== true) {
11236
11752
  return [];
@@ -11241,11 +11757,11 @@ var normalizeHooks = (config, event) => {
11241
11757
  ) : [];
11242
11758
  };
11243
11759
  var writeHookPayload = async (input, hook) => {
11244
- const filePath = path6.join(
11760
+ const filePath = path7.join(
11245
11761
  os4.tmpdir(),
11246
11762
  `cloudeval-hook-${process.pid}-${randomUUID3()}.json`
11247
11763
  );
11248
- await fs9.writeFile(
11764
+ await fs10.writeFile(
11249
11765
  filePath,
11250
11766
  JSON.stringify(
11251
11767
  {
@@ -11321,7 +11837,7 @@ var runLocalHooks = async (input) => {
11321
11837
  throw error;
11322
11838
  }
11323
11839
  } finally {
11324
- await fs9.rm(payloadPath, { force: true }).catch(() => void 0);
11840
+ await fs10.rm(payloadPath, { force: true }).catch(() => void 0);
11325
11841
  }
11326
11842
  }
11327
11843
  return warnings;
@@ -11633,7 +12149,7 @@ var registerAgentsCommand = (program2, deps) => {
11633
12149
 
11634
12150
  // src/validateCommand.ts
11635
12151
  var addCommon5 = (command, deps) => addAuthOptions(command, deps.defaultBaseUrl).requiredOption("--template-file <path>", "Cloud template JSON file").option("--parameters-file <path>", "Optional parameters JSON file").option("--format <format>", "Output format: text, json, ndjson, markdown", "text").option("--output <file>", "Output file");
11636
- var parsePositiveInteger = (value, optionName = "--max-results") => {
12152
+ var parsePositiveInteger2 = (value, optionName = "--max-results") => {
11637
12153
  if (!value) {
11638
12154
  return void 0;
11639
12155
  }
@@ -11667,7 +12183,7 @@ var registerValidateCommand = (program2, deps) => {
11667
12183
  category: options.category,
11668
12184
  pillar: options.pillar,
11669
12185
  minSeverity: options.minSeverity,
11670
- maxResults: parsePositiveInteger(options.maxResults),
12186
+ maxResults: parsePositiveInteger2(options.maxResults),
11671
12187
  projectId: options.project,
11672
12188
  saveReport: options.saveReport
11673
12189
  });
@@ -11676,11 +12192,11 @@ var registerValidateCommand = (program2, deps) => {
11676
12192
  authToken: context.token,
11677
12193
  userId: context.user.id,
11678
12194
  submitted,
11679
- pollIntervalMs: parsePositiveInteger(
12195
+ pollIntervalMs: parsePositiveInteger2(
11680
12196
  options.pollInterval,
11681
12197
  "--poll-interval"
11682
12198
  ),
11683
- waitTimeoutMs: parsePositiveInteger(
12199
+ waitTimeoutMs: parsePositiveInteger2(
11684
12200
  options.waitTimeout,
11685
12201
  "--wait-timeout"
11686
12202
  )
@@ -11740,11 +12256,11 @@ var registerValidateCommand = (program2, deps) => {
11740
12256
  authToken: context.token,
11741
12257
  userId: context.user.id,
11742
12258
  submitted,
11743
- pollIntervalMs: parsePositiveInteger(
12259
+ pollIntervalMs: parsePositiveInteger2(
11744
12260
  options.pollInterval,
11745
12261
  "--poll-interval"
11746
12262
  ),
11747
- waitTimeoutMs: parsePositiveInteger(
12263
+ waitTimeoutMs: parsePositiveInteger2(
11748
12264
  options.waitTimeout,
11749
12265
  "--wait-timeout"
11750
12266
  )
@@ -11865,10 +12381,10 @@ var registerConfigCommand = (program2) => {
11865
12381
  const profile = resolveProfile(options, command);
11866
12382
  const current = await loadCliConfig(profile);
11867
12383
  const next = writeCliConfigValue(current, key, value);
11868
- const path10 = await saveCliConfig(next, profile);
12384
+ const path11 = await saveCliConfig(next, profile);
11869
12385
  await writeFormattedOutput({
11870
12386
  command: "config set",
11871
- data: { profile, path: path10, config: next },
12387
+ data: { profile, path: path11, config: next },
11872
12388
  format: options.format,
11873
12389
  output: options.output
11874
12390
  });
@@ -11879,10 +12395,10 @@ var registerConfigCommand = (program2) => {
11879
12395
  const profile = resolveProfile(options, command);
11880
12396
  const current = await loadCliConfig(profile);
11881
12397
  const next = unsetCliConfigValue(current, key);
11882
- const path10 = await saveCliConfig(next, profile);
12398
+ const path11 = await saveCliConfig(next, profile);
11883
12399
  await writeFormattedOutput({
11884
12400
  command: "config unset",
11885
- data: { profile, path: path10, config: next },
12401
+ data: { profile, path: path11, config: next },
11886
12402
  format: options.format,
11887
12403
  output: options.output
11888
12404
  });
@@ -12179,8 +12695,8 @@ var writeModelsListOutput = async (input) => {
12179
12695
  defaultModel: input.defaultModel
12180
12696
  });
12181
12697
  if (input.options.output) {
12182
- const fs13 = await import("fs/promises");
12183
- await fs13.writeFile(input.options.output, text, "utf8");
12698
+ const fs14 = await import("fs/promises");
12699
+ await fs14.writeFile(input.options.output, text, "utf8");
12184
12700
  return;
12185
12701
  }
12186
12702
  process.stdout.write(text);
@@ -12246,10 +12762,10 @@ var registerModelsCommand = (program2, deps) => {
12246
12762
  const profile = options.profile || getActiveConfigProfile(command);
12247
12763
  const config = await loadCliConfig(profile);
12248
12764
  const next = { ...config, model };
12249
- const path10 = await saveCliConfig(next, profile);
12765
+ const path11 = await saveCliConfig(next, profile);
12250
12766
  await writeFormattedOutput({
12251
12767
  command: "models default set",
12252
- data: { profile, path: path10, model },
12768
+ data: { profile, path: path11, model },
12253
12769
  format: options.format,
12254
12770
  output: options.output
12255
12771
  });
@@ -12292,8 +12808,8 @@ var writeSessionTableOutput = async (command, data, options) => {
12292
12808
  if (format === "text") {
12293
12809
  const text = renderSessionsTable(data);
12294
12810
  if (options.output) {
12295
- const fs13 = await import("fs/promises");
12296
- await fs13.writeFile(options.output, text, "utf8");
12811
+ const fs14 = await import("fs/promises");
12812
+ await fs14.writeFile(options.output, text, "utf8");
12297
12813
  return;
12298
12814
  }
12299
12815
  process.stdout.write(text);
@@ -12465,12 +12981,12 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
12465
12981
  rl.close();
12466
12982
  }
12467
12983
  }
12468
- const path10 = await saveCliConfig(next, profile);
12984
+ const path11 = await saveCliConfig(next, profile);
12469
12985
  await writeFormattedOutput({
12470
12986
  command: "setup",
12471
12987
  data: {
12472
12988
  profile,
12473
- path: path10,
12989
+ path: path11,
12474
12990
  config: next,
12475
12991
  nextSteps: [
12476
12992
  "Run `cloudeval auth status` to inspect authentication.",
@@ -12485,9 +13001,9 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
12485
13001
  };
12486
13002
 
12487
13003
  // src/updateCommand.ts
12488
- import { spawn as spawn2 } from "child_process";
12489
- import fs10 from "fs/promises";
12490
- import path7 from "path";
13004
+ import { spawn as spawn3 } from "child_process";
13005
+ import fs11 from "fs/promises";
13006
+ import path8 from "path";
12491
13007
  import { createInterface as createInterface2 } from "readline/promises";
12492
13008
  var DEFAULT_LATEST_RELEASE_URL = "https://api.github.com/repos/ganakailabs/cloudeval-cli/releases/latest";
12493
13009
  var DEFAULT_INSTALLER_URL = "https://cli.cloudeval.ai/install.sh";
@@ -12604,7 +13120,7 @@ var runInstaller = async ({
12604
13120
  installerUrl,
12605
13121
  targetTag,
12606
13122
  fetchImpl = fetch,
12607
- spawnImpl = spawn2,
13123
+ spawnImpl = spawn3,
12608
13124
  output = process.stderr,
12609
13125
  platform = process.platform,
12610
13126
  env = process.env,
@@ -12626,12 +13142,12 @@ var runInstaller = async ({
12626
13142
  const installerScript = await response.text();
12627
13143
  if (usePowerShellInstaller) {
12628
13144
  const configDir = getCloudevalConfigDir();
12629
- await fs10.mkdir(configDir, { recursive: true, mode: 448 });
12630
- const scriptPath = path7.join(
12631
- await fs10.mkdtemp(path7.join(configDir, "installer-")),
13145
+ await fs11.mkdir(configDir, { recursive: true, mode: 448 });
13146
+ const scriptPath = path8.join(
13147
+ await fs11.mkdtemp(path8.join(configDir, "installer-")),
12632
13148
  "install.ps1"
12633
13149
  );
12634
- await fs10.writeFile(scriptPath, installerScript, "utf8");
13150
+ await fs11.writeFile(scriptPath, installerScript, "utf8");
12635
13151
  const child2 = spawnImpl(
12636
13152
  "pwsh",
12637
13153
  [
@@ -12659,7 +13175,7 @@ var runInstaller = async ({
12659
13175
  );
12660
13176
  });
12661
13177
  child2.once("close", (code) => {
12662
- void fs10.rm(path7.dirname(scriptPath), { recursive: true, force: true });
13178
+ void fs11.rm(path8.dirname(scriptPath), { recursive: true, force: true });
12663
13179
  if (code === 0) {
12664
13180
  resolve();
12665
13181
  return;
@@ -12699,10 +13215,10 @@ var runInstaller = async ({
12699
13215
  child.stdin.end(installerScript);
12700
13216
  });
12701
13217
  };
12702
- var getUpdateCachePath = () => path7.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
13218
+ var getUpdateCachePath = () => path8.join(getCloudevalConfigDir(), UPDATE_CACHE_FILE);
12703
13219
  var readCache = async (cachePath) => {
12704
13220
  try {
12705
- const parsed = JSON.parse(await fs10.readFile(cachePath, "utf8"));
13221
+ const parsed = JSON.parse(await fs11.readFile(cachePath, "utf8"));
12706
13222
  if (parsed && typeof parsed === "object" && typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string" && typeof parsed.latestTag === "string") {
12707
13223
  return parsed;
12708
13224
  }
@@ -12714,8 +13230,8 @@ var readCache = async (cachePath) => {
12714
13230
  return void 0;
12715
13231
  };
12716
13232
  var writeCache = async (cachePath, status) => {
12717
- await fs10.mkdir(path7.dirname(cachePath), { recursive: true, mode: 448 });
12718
- await fs10.writeFile(
13233
+ await fs11.mkdir(path8.dirname(cachePath), { recursive: true, mode: 448 });
13234
+ await fs11.writeFile(
12719
13235
  cachePath,
12720
13236
  `${JSON.stringify(
12721
13237
  {
@@ -12927,7 +13443,7 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
12927
13443
  if (options.format === "text" || !options.format) {
12928
13444
  const text = formatUpdateStatusText(result);
12929
13445
  if (options.output) {
12930
- await fs10.writeFile(options.output, text, "utf8");
13446
+ await fs11.writeFile(options.output, text, "utf8");
12931
13447
  return;
12932
13448
  }
12933
13449
  process.stdout.write(text);
@@ -12943,13 +13459,13 @@ var registerUpdateCommand = (program2, registerOptions = {}) => {
12943
13459
  };
12944
13460
 
12945
13461
  // src/uninstallCommand.ts
12946
- import fs11 from "fs/promises";
13462
+ import fs12 from "fs/promises";
12947
13463
  import os5 from "os";
12948
- import path8 from "path";
13464
+ import path9 from "path";
12949
13465
  import { createInterface as createInterface3 } from "readline/promises";
12950
13466
  var pathExists = async (candidate) => {
12951
13467
  try {
12952
- await fs11.lstat(candidate);
13468
+ await fs12.lstat(candidate);
12953
13469
  return true;
12954
13470
  } catch (error) {
12955
13471
  if (error?.code === "ENOENT") {
@@ -12958,12 +13474,12 @@ var pathExists = async (candidate) => {
12958
13474
  throw error;
12959
13475
  }
12960
13476
  };
12961
- var installerBinDir = (home) => path8.join(home, ".local", "bin");
13477
+ var installerBinDir = (home) => path9.join(home, ".local", "bin");
12962
13478
  var completionPaths = (home) => [
12963
- path8.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
12964
- path8.join(home, ".zsh", "completions", "_cloudeval"),
12965
- path8.join(home, ".config", "fish", "completions", "cloudeval.fish"),
12966
- path8.join(home, ".config", "powershell", "cloudeval-completion.ps1")
13479
+ path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
13480
+ path9.join(home, ".zsh", "completions", "_cloudeval"),
13481
+ path9.join(home, ".config", "fish", "completions", "cloudeval.fish"),
13482
+ path9.join(home, ".config", "powershell", "cloudeval-completion.ps1")
12967
13483
  ];
12968
13484
  var installerArtifactTargets = (home, platform) => {
12969
13485
  const binDir = installerBinDir(home);
@@ -12971,37 +13487,37 @@ var installerArtifactTargets = (home, platform) => {
12971
13487
  const targets = [
12972
13488
  {
12973
13489
  label: "cloudeval binary",
12974
- path: path8.join(binDir, executableName),
13490
+ path: path9.join(binDir, executableName),
12975
13491
  kind: "file",
12976
13492
  status: "missing"
12977
13493
  },
12978
13494
  {
12979
13495
  label: "cloudeval binary",
12980
- path: path8.join(binDir, "cloudeval"),
13496
+ path: path9.join(binDir, "cloudeval"),
12981
13497
  kind: "file",
12982
13498
  status: "missing"
12983
13499
  },
12984
13500
  {
12985
13501
  label: "eva alias",
12986
- path: path8.join(binDir, "eva"),
13502
+ path: path9.join(binDir, "eva"),
12987
13503
  kind: "file",
12988
13504
  status: "missing"
12989
13505
  },
12990
13506
  {
12991
13507
  label: "cloud alias",
12992
- path: path8.join(binDir, "cloud"),
13508
+ path: path9.join(binDir, "cloud"),
12993
13509
  kind: "file",
12994
13510
  status: "missing"
12995
13511
  },
12996
13512
  {
12997
13513
  label: "Ink runtime asset",
12998
- path: path8.join(binDir, "yoga.wasm"),
13514
+ path: path9.join(binDir, "yoga.wasm"),
12999
13515
  kind: "file",
13000
13516
  status: "missing"
13001
13517
  },
13002
13518
  {
13003
13519
  label: "license notices",
13004
- path: path8.join(home, ".local", "share", "cloudeval", "licenses"),
13520
+ path: path9.join(home, ".local", "share", "cloudeval", "licenses"),
13005
13521
  kind: "directory",
13006
13522
  status: "missing"
13007
13523
  },
@@ -13017,11 +13533,11 @@ var installerArtifactTargets = (home, platform) => {
13017
13533
  );
13018
13534
  };
13019
13535
  var shellProfilePaths = (home) => [
13020
- path8.join(home, ".bashrc"),
13021
- path8.join(home, ".bash_profile"),
13022
- path8.join(home, ".zshrc"),
13023
- path8.join(home, ".profile"),
13024
- path8.join(home, ".config", "fish", "config.fish")
13536
+ path9.join(home, ".bashrc"),
13537
+ path9.join(home, ".bash_profile"),
13538
+ path9.join(home, ".zshrc"),
13539
+ path9.join(home, ".profile"),
13540
+ path9.join(home, ".config", "fish", "config.fish")
13025
13541
  ];
13026
13542
  var removeInstallerPathSnippet = (content, binDir) => {
13027
13543
  const exportLine = `export PATH="${binDir}:$PATH"`;
@@ -13048,7 +13564,7 @@ var removeTarget = async (target, dryRun) => {
13048
13564
  if (dryRun) {
13049
13565
  return { ...target, status: "would_remove" };
13050
13566
  }
13051
- await fs11.rm(target.path, { recursive: target.kind === "directory", force: true });
13567
+ await fs12.rm(target.path, { recursive: target.kind === "directory", force: true });
13052
13568
  return { ...target, status: "removed" };
13053
13569
  };
13054
13570
  var updateShellProfile = async (profilePath, home, dryRun) => {
@@ -13060,7 +13576,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
13060
13576
  status: "missing"
13061
13577
  };
13062
13578
  }
13063
- const content = await fs11.readFile(profilePath, "utf8");
13579
+ const content = await fs12.readFile(profilePath, "utf8");
13064
13580
  const updated = removeInstallerPathSnippet(content, installerBinDir(home));
13065
13581
  if (updated === void 0) {
13066
13582
  return {
@@ -13078,7 +13594,7 @@ var updateShellProfile = async (profilePath, home, dryRun) => {
13078
13594
  status: "would_update"
13079
13595
  };
13080
13596
  }
13081
- await fs11.writeFile(profilePath, updated, "utf8");
13597
+ await fs12.writeFile(profilePath, updated, "utf8");
13082
13598
  return {
13083
13599
  label: "shell profile PATH entry",
13084
13600
  path: profilePath,
@@ -13131,7 +13647,7 @@ var handleUninstallCommand = async (options, deps = {}) => {
13131
13647
  }
13132
13648
  const configTarget = {
13133
13649
  label: "config",
13134
- path: path8.join(home, ".config", "cloudeval"),
13650
+ path: path9.join(home, ".config", "cloudeval"),
13135
13651
  kind: "directory",
13136
13652
  status: "kept"
13137
13653
  };
@@ -13193,7 +13709,7 @@ var registerUninstallCommand = (program2) => {
13193
13709
  if (options.format === "text" || !options.format) {
13194
13710
  const text = formatUninstallResultText(result);
13195
13711
  if (options.output) {
13196
- await fs11.writeFile(options.output, text, "utf8");
13712
+ await fs12.writeFile(options.output, text, "utf8");
13197
13713
  return;
13198
13714
  }
13199
13715
  process.stdout.write(text);
@@ -13314,6 +13830,7 @@ var resolveLoginOnboardingMode = (options) => {
13314
13830
  import { jsx } from "react/jsx-runtime";
13315
13831
  var DEFAULT_BASE_URL = getDefaultBaseUrl();
13316
13832
  var ASK_STREAM_IDLE_TIMEOUT_MS2 = 9e4;
13833
+ var AGENT_STREAM_IDLE_TIMEOUT_MS = 18e4;
13317
13834
  var LEGACY_API_KEY_MESSAGE = "API key auth was renamed in beta. Use --access-key or CLOUDEVAL_ACCESS_KEY.";
13318
13835
  var STREAM_OUTPUT_NODES3 = /* @__PURE__ */ new Set([
13319
13836
  "generate_response",
@@ -13339,21 +13856,21 @@ var completionScriptPath = (shell) => {
13339
13856
  const home = os6.homedir();
13340
13857
  switch (shell) {
13341
13858
  case "bash":
13342
- return path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
13859
+ return path10.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
13343
13860
  case "zsh":
13344
- return path9.join(home, ".zsh", "completions", "_cloudeval");
13861
+ return path10.join(home, ".zsh", "completions", "_cloudeval");
13345
13862
  case "fish":
13346
- return path9.join(home, ".config", "fish", "completions", "cloudeval.fish");
13863
+ return path10.join(home, ".config", "fish", "completions", "cloudeval.fish");
13347
13864
  case "powershell":
13348
- return path9.join(home, ".config", "powershell", "cloudeval-completion.ps1");
13865
+ return path10.join(home, ".config", "powershell", "cloudeval-completion.ps1");
13349
13866
  }
13350
13867
  };
13351
13868
  var ZSH_FPATH_MARKER = "CloudEval CLI completions";
13352
13869
  var ensureZshCompletionFpath = async () => {
13353
- const zshrc = path9.join(os6.homedir(), ".zshrc");
13870
+ const zshrc = path10.join(os6.homedir(), ".zshrc");
13354
13871
  let existing = "";
13355
13872
  try {
13356
- existing = await fs12.readFile(zshrc, "utf8");
13873
+ existing = await fs13.readFile(zshrc, "utf8");
13357
13874
  } catch {
13358
13875
  existing = "";
13359
13876
  }
@@ -13364,12 +13881,12 @@ var ensureZshCompletionFpath = async () => {
13364
13881
  # ${ZSH_FPATH_MARKER}
13365
13882
  fpath=("$HOME/.zsh/completions" $fpath)
13366
13883
  `;
13367
- await fs12.appendFile(zshrc, snippet, "utf8");
13884
+ await fs13.appendFile(zshrc, snippet, "utf8");
13368
13885
  };
13369
13886
  var installCompletionScript = async (shell, binaryName) => {
13370
13887
  const scriptPath = completionScriptPath(shell);
13371
- await fs12.mkdir(path9.dirname(scriptPath), { recursive: true });
13372
- await fs12.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
13888
+ await fs13.mkdir(path10.dirname(scriptPath), { recursive: true });
13889
+ await fs13.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
13373
13890
  if (shell === "zsh") {
13374
13891
  await ensureZshCompletionFpath();
13375
13892
  }
@@ -13377,7 +13894,7 @@ var installCompletionScript = async (shell, binaryName) => {
13377
13894
  };
13378
13895
  var uninstallCompletionScript = async (shell) => {
13379
13896
  const scriptPath = completionScriptPath(shell);
13380
- await fs12.rm(scriptPath, { force: true });
13897
+ await fs13.rm(scriptPath, { force: true });
13381
13898
  return scriptPath;
13382
13899
  };
13383
13900
  var runInteractiveLoginOnboarding = async (baseUrl, token) => {
@@ -13946,6 +14463,12 @@ registerReportsCommand(program, {
13946
14463
  resolveBaseUrl,
13947
14464
  readStdinValue
13948
14465
  });
14466
+ registerReviewCommand(program, {
14467
+ defaultBaseUrl: DEFAULT_BASE_URL,
14468
+ resolveBaseUrl,
14469
+ readStdinValue,
14470
+ isHeadlessEnvironment
14471
+ });
13949
14472
  registerRecipesCommand(program, {
13950
14473
  defaultBaseUrl: DEFAULT_BASE_URL,
13951
14474
  resolveBaseUrl,
@@ -14102,7 +14625,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
14102
14625
  const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
14103
14626
  const [{ render }, { App }] = await Promise.all([
14104
14627
  import("ink"),
14105
- import("./App-NGGAERHH.js")
14628
+ import("./App-H46FRLWK.js")
14106
14629
  ]);
14107
14630
  const baseUrl = await resolveBaseUrl(options, command);
14108
14631
  assertSecureBaseUrl(baseUrl);
@@ -14160,7 +14683,7 @@ program.command("chat").description("Start an interactive chat session").option(
14160
14683
  const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
14161
14684
  const [{ render }, { App }] = await Promise.all([
14162
14685
  import("ink"),
14163
- import("./App-NGGAERHH.js")
14686
+ import("./App-H46FRLWK.js")
14164
14687
  ]);
14165
14688
  const baseUrl = await resolveBaseUrl(options, command);
14166
14689
  assertSecureBaseUrl(baseUrl);
@@ -14273,7 +14796,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
14273
14796
  });
14274
14797
  }
14275
14798
  try {
14276
- const fs13 = await import("fs");
14799
+ const fs14 = await import("fs");
14277
14800
  const fsPromises = await import("fs/promises");
14278
14801
  const { randomUUID: randomUUID5 } = await import("crypto");
14279
14802
  const core = await import("./dist-CFLR5FML.js");
@@ -14281,8 +14804,6 @@ program.command("ask").alias("agent").description("Ask a single question or run
14281
14804
  streamChat,
14282
14805
  reduceChunk,
14283
14806
  getAuthToken,
14284
- getProjects,
14285
- ensurePlaygroundProject,
14286
14807
  checkUserStatus,
14287
14808
  extractEmailFromToken,
14288
14809
  initialChatState,
@@ -14381,68 +14902,39 @@ program.command("ask").alias("agent").description("Ask a single question or run
14381
14902
  step: "project",
14382
14903
  message: selectedProjectId ? `Using project ${selectedProjectId}` : "Resolving project"
14383
14904
  });
14384
- let project;
14385
14905
  let authenticatedUserId;
14386
- if (selectedProjectId) {
14387
- verboseLog("Using provided project ID:", selectedProjectId);
14388
- try {
14389
- const userStatus = await checkUserStatus(baseUrl, token);
14390
- getActiveCliTelemetry()?.setUserProperties(userStatus.user || {});
14391
- authenticatedUserId = userStatus.user?.id;
14392
- } catch {
14393
- }
14394
- project = {
14395
- id: selectedProjectId,
14396
- name: "Selected Project",
14397
- user_id: authenticatedUserId,
14398
- cloud_provider: "azure"
14399
- };
14400
- } else {
14401
- verboseLog("No project ID provided, attempting to fetch user projects");
14402
- try {
14403
- verboseLog("Checking user status", { baseUrl });
14404
- const userStatus = await checkUserStatus(baseUrl, token);
14405
- getActiveCliTelemetry()?.setUserProperties(userStatus.user || {});
14406
- authenticatedUserId = userStatus.user?.id;
14407
- verboseLog("User status:", {
14408
- hasUser: !!userStatus.user,
14409
- userId: userStatus.user?.id,
14410
- onboardingCompleted: userStatus.onboardingCompleted
14411
- });
14412
- if (authenticatedUserId) {
14413
- verboseLog("Fetching projects for user", { userId: authenticatedUserId });
14414
- const projects = await getProjects(baseUrl, token, authenticatedUserId);
14415
- verboseLog("Projects fetched:", { count: projects.length, names: projects.map((p) => p.name) });
14416
- const playgroundProject = projects.find((p) => p.name === "Playground");
14417
- if (playgroundProject) {
14418
- project = playgroundProject;
14419
- } else if (userStatus.user?.email) {
14420
- verboseLog("Playground project missing; running shared onboarding repair");
14421
- project = await ensurePlaygroundProject(baseUrl, token, {
14422
- id: authenticatedUserId,
14423
- email: userStatus.user.email,
14424
- full_name: userStatus.user.full_name,
14425
- name: userStatus.user.name
14426
- });
14427
- } else {
14428
- project = projects[0] || void 0;
14429
- }
14430
- verboseLog("Selected project:", project ? { id: project.id, name: project.name } : "none");
14431
- }
14432
- } catch (error) {
14433
- verboseLog("Failed to fetch projects, using default:", {
14434
- message: error.message,
14435
- stack: error.stack
14436
- });
14437
- }
14438
- if (!project) {
14439
- process.stderr.write(
14440
- "No project is available for this account. Run `cloudeval chat` to complete onboarding, then retry."
14441
- );
14442
- process.stderr.write("\n");
14443
- await exitCli(1, new Error("no_project_available"));
14444
- }
14906
+ let authenticatedUser;
14907
+ try {
14908
+ const userStatus = await checkUserStatus(baseUrl, token);
14909
+ getActiveCliTelemetry()?.setUserProperties(userStatus.user || {});
14910
+ authenticatedUserId = userStatus.user?.id;
14911
+ authenticatedUser = userStatus.user;
14912
+ verboseLog("User status:", {
14913
+ hasUser: !!userStatus.user,
14914
+ userId: userStatus.user?.id,
14915
+ onboardingCompleted: userStatus.onboardingCompleted
14916
+ });
14917
+ } catch (error) {
14918
+ verboseLog("Failed to check user status before project resolve:", {
14919
+ message: error.message
14920
+ });
14921
+ }
14922
+ const { resolveAskProject } = await import("./resolveAskProject-NK435I56.js");
14923
+ let project;
14924
+ try {
14925
+ project = await resolveAskProject({
14926
+ baseUrl,
14927
+ token,
14928
+ selectedProjectId,
14929
+ authenticatedUserId,
14930
+ authenticatedUser
14931
+ });
14932
+ } catch (error) {
14933
+ progressWriter.clear();
14934
+ console.error(error?.message ?? "Failed to resolve project");
14935
+ await exitCli(1, error);
14445
14936
  }
14937
+ verboseLog("Selected project:", { id: project.id, name: project.name });
14446
14938
  let userName = "You";
14447
14939
  try {
14448
14940
  const email = extractEmailFromToken(token);
@@ -14469,11 +14961,11 @@ program.command("ask").alias("agent").description("Ask a single question or run
14469
14961
  console.error(`[${commandName}] Thread ID: ${threadId}`);
14470
14962
  }
14471
14963
  if (streamTextOutput && options.output) {
14472
- fileOutputStream = fs13.createWriteStream(options.output, { encoding: "utf-8" });
14964
+ fileOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
14473
14965
  outputStream = fileOutputStream;
14474
14966
  }
14475
14967
  if (ndjsonOutput && options.output) {
14476
- ndjsonOutputStream = fs13.createWriteStream(options.output, { encoding: "utf-8" });
14968
+ ndjsonOutputStream = fs14.createWriteStream(options.output, { encoding: "utf-8" });
14477
14969
  }
14478
14970
  const writeAskDataEvent = (event) => {
14479
14971
  const line = `${JSON.stringify(event)}
@@ -14575,7 +15067,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
14575
15067
  debug: options.debug,
14576
15068
  completeAfterResponse: true,
14577
15069
  responseCompletionGraceMs: 5e3,
14578
- streamIdleTimeoutMs: ASK_STREAM_IDLE_TIMEOUT_MS2,
15070
+ streamIdleTimeoutMs: selectedMode === "agent" ? AGENT_STREAM_IDLE_TIMEOUT_MS : ASK_STREAM_IDLE_TIMEOUT_MS2,
14579
15071
  hitlResume
14580
15072
  })) {
14581
15073
  totalChunkCount++;
@@ -14754,7 +15246,24 @@ Error: ${errorMsg}
14754
15246
  throw error;
14755
15247
  }
14756
15248
  const finalMessage = [...chatState.messages].reverse().find((m) => m.role === "assistant");
14757
- const finalResponse = collapseRepeatedAssistantText3(finalMessage?.content || responseText || "");
15249
+ let finalResponse = collapseRepeatedAssistantText3(
15250
+ finalMessage?.content || responseText || ""
15251
+ );
15252
+ if (!finalResponse.trim() && chatState.threadId) {
15253
+ const { fetchLastAssistantContent } = await import("./fetchLastAssistantContent-RH6RMSQO.js");
15254
+ const persisted = await fetchLastAssistantContent({
15255
+ baseUrl,
15256
+ authToken: token,
15257
+ threadId: chatState.threadId,
15258
+ normalizeApiBase: normalizeApiBase2
15259
+ });
15260
+ if (persisted) {
15261
+ finalResponse = collapseRepeatedAssistantText3(persisted);
15262
+ verboseLog("Recovered final response from thread history", {
15263
+ length: finalResponse.length
15264
+ });
15265
+ }
15266
+ }
14758
15267
  if (!finalResponse.trim()) {
14759
15268
  const noResponseMessage = `No final response returned by CloudEval (last stream status: ${chatState.status ?? "unknown"}). Retry with --verbose or --format ndjson to inspect stream progress.`;
14760
15269
  progressWriter.clear();
@@ -14928,7 +15437,7 @@ Error: ${errorMsg}
14928
15437
  program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
14929
15438
  const { render } = await import("ink");
14930
15439
  const BannerPreview = React.lazy(async () => ({
14931
- default: (await import("./Banner-6POGUWTT.js")).Banner
15440
+ default: (await import("./Banner-7X2VHUVH.js")).Banner
14932
15441
  }));
14933
15442
  render(
14934
15443
  /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })