@ganakailabs/cloudeval-cli 0.25.0 → 0.26.1

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.
@@ -38,10 +38,10 @@ import {
38
38
  } from "./chunk-KBHRBGSX.js";
39
39
  import {
40
40
  Banner
41
- } from "./chunk-TISPT6EB.js";
41
+ } from "./chunk-M5XGS3ZV.js";
42
42
  import {
43
43
  CLI_VERSION
44
- } from "./chunk-LKVKOGVL.js";
44
+ } from "./chunk-UUXJKGRN.js";
45
45
  import {
46
46
  raisedButtonStyle,
47
47
  terminalTheme
@@ -3,8 +3,8 @@ import {
3
3
  bannerMetaColor,
4
4
  bannerSegmentColor,
5
5
  splitBannerLineSegments
6
- } from "./chunk-TISPT6EB.js";
7
- import "./chunk-LKVKOGVL.js";
6
+ } from "./chunk-M5XGS3ZV.js";
7
+ import "./chunk-UUXJKGRN.js";
8
8
  import "./chunk-ZDKRIOMB.js";
9
9
  export {
10
10
  Banner,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CLI_VERSION
3
- } from "./chunk-LKVKOGVL.js";
3
+ } from "./chunk-UUXJKGRN.js";
4
4
  import {
5
5
  shouldUseColor,
6
6
  terminalTheme
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var CLI_VERSION = "0.25.0";
2
+ var CLI_VERSION = "0.26.1";
3
3
 
4
4
  export {
5
5
  CLI_VERSION
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-LKVKOGVL.js";
39
+ } from "./chunk-UUXJKGRN.js";
40
40
 
41
41
  // src/runtime/prepareInk.ts
42
42
  import fs from "fs";
@@ -453,6 +453,8 @@ var cliCommands = [
453
453
  "--wait-timeout",
454
454
  "--poll-interval",
455
455
  "--no-ai-summary",
456
+ "--ai-summary-mode",
457
+ "--ai-summary-profile",
456
458
  "--ignore-dirty",
457
459
  "--output",
458
460
  "--format",
@@ -2398,6 +2400,10 @@ var parseGateConfig = (configText) => {
2398
2400
  if (!configText || !/^\s*ci\s*:/m.test(configText) || !/^\s*gates\s*:/m.test(configText)) {
2399
2401
  return void 0;
2400
2402
  }
2403
+ const stringValue2 = (key) => {
2404
+ const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([^\\s#]+)`, "m"));
2405
+ return match ? match[1].trim() : void 0;
2406
+ };
2401
2407
  const numberValue2 = (key) => {
2402
2408
  const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)`, "m"));
2403
2409
  return match ? Number(match[1]) : void 0;
@@ -2406,9 +2412,35 @@ var parseGateConfig = (configText) => {
2406
2412
  const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*(true|false)`, "im"));
2407
2413
  return match ? match[1].toLowerCase() === "true" : void 0;
2408
2414
  };
2415
+ const pillarScoreMins = {};
2416
+ const pillarBlock = configText.match(/^(\s*)pillars\s*:\s*$(?<body>(?:\n\s+[-\w]+\s*:\s*[0-9]+(?:\.[0-9]+)?\s*)+)/m);
2417
+ const pillarBody = pillarBlock?.groups?.body ?? "";
2418
+ for (const line of pillarBody.split("\n")) {
2419
+ const match = line.match(/^\s+([-\w]+)\s*:\s*([0-9]+(?:\.[0-9]+)?)/);
2420
+ if (match) {
2421
+ pillarScoreMins[match[1].replace(/-/g, "_").toLowerCase()] = Number(match[2]);
2422
+ }
2423
+ }
2424
+ for (const key of [
2425
+ "security",
2426
+ "reliability",
2427
+ "operational_excellence",
2428
+ "performance_efficiency",
2429
+ "cost_optimization"
2430
+ ]) {
2431
+ const value = numberValue2(`${key}_score_min`);
2432
+ if (value !== void 0) {
2433
+ pillarScoreMins[key] = value;
2434
+ }
2435
+ }
2436
+ const enforcement = stringValue2("enforcement")?.toLowerCase();
2409
2437
  return {
2438
+ enforcement: enforcement === "warn" ? "warn" : "required",
2410
2439
  overallScoreMin: numberValue2("overall_score_min") ?? 80,
2440
+ pillarScoreMin: numberValue2("pillar_score_min"),
2441
+ pillarScoreMins,
2411
2442
  failOnHighRisk: booleanValue2("fail_on_high_risk") ?? true,
2443
+ failOnValidationErrors: booleanValue2("fail_on_validation_errors") ?? true,
2412
2444
  maxMonthlyCost: numberValue2("max_monthly_cost")
2413
2445
  };
2414
2446
  };
@@ -2423,10 +2455,86 @@ var numberFrom = (...values) => {
2423
2455
  }
2424
2456
  return void 0;
2425
2457
  };
2458
+ var normalizeKey = (value) => String(value ?? "").trim().replace(/[\s-]+/g, "_").toLowerCase();
2459
+ var firstRecord = (...values) => {
2460
+ for (const value of values) {
2461
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2462
+ return value;
2463
+ }
2464
+ }
2465
+ return void 0;
2466
+ };
2467
+ var extractPillars = (waf) => {
2468
+ const fromArray = waf?.parsed?.score?.pillars;
2469
+ if (Array.isArray(fromArray)) {
2470
+ return fromArray.map((pillar) => {
2471
+ const record = asRecord(pillar);
2472
+ const id = normalizeKey(record.id ?? record.name ?? record.label);
2473
+ const score = numberFrom(record.score, record.value);
2474
+ if (!id || score === void 0) return void 0;
2475
+ return {
2476
+ id,
2477
+ label: String(record.label ?? record.name ?? id),
2478
+ score,
2479
+ passed: numberFrom(record.passed, record.passed_rules),
2480
+ warned: numberFrom(record.warned, record.warning_rules),
2481
+ failed: numberFrom(record.failed, record.failed_rules)
2482
+ };
2483
+ }).filter((pillar) => pillar !== void 0);
2484
+ }
2485
+ const scores = firstRecord(
2486
+ waf?.parsed?.pillar_scores,
2487
+ waf?.parsed?.score?.pillar_scores,
2488
+ waf?.raw?.pillar_scores
2489
+ );
2490
+ if (!scores) return [];
2491
+ return Object.entries(scores).map(([id, score]) => ({
2492
+ id: normalizeKey(id),
2493
+ label: id,
2494
+ score: numberFrom(score)
2495
+ })).filter((pillar) => pillar.score !== void 0);
2496
+ };
2497
+ var extractValidation = ({
2498
+ waf,
2499
+ preload,
2500
+ project
2501
+ }) => {
2502
+ const psRuleSummary = firstRecord(
2503
+ waf?.parsed?.validation_summary,
2504
+ waf?.raw?.validation_summary,
2505
+ waf?.validation_summary
2506
+ );
2507
+ const rules = Array.isArray(waf?.parsed?.rules) ? waf?.parsed?.rules : [];
2508
+ const failedRules = rules.filter(
2509
+ (rule) => ["fail", "failed", "error"].includes(String(rule?.status ?? rule?.outcome ?? "").toLowerCase())
2510
+ );
2511
+ const unitSummary = firstRecord(
2512
+ preload?.latest_payloads?.unit_tests?.summary,
2513
+ preload?.reports?.unit_tests?.metrics,
2514
+ project?.status?.unit_tests
2515
+ );
2516
+ return {
2517
+ psRule: {
2518
+ total: numberFrom(psRuleSummary?.total_rules, psRuleSummary?.totalRules, rules.length),
2519
+ passed: numberFrom(psRuleSummary?.passed_rules, psRuleSummary?.passedRules),
2520
+ failed: numberFrom(psRuleSummary?.failed_rules, psRuleSummary?.failedRules, failedRules.length),
2521
+ errors: numberFrom(psRuleSummary?.error_rules, psRuleSummary?.errorRules)
2522
+ },
2523
+ unitTests: {
2524
+ status: unitSummary?.status,
2525
+ total: numberFrom(unitSummary?.total_tests, unitSummary?.totalTests),
2526
+ passed: numberFrom(unitSummary?.passed_tests, unitSummary?.passedTests),
2527
+ failed: numberFrom(unitSummary?.failed_tests, unitSummary?.failedTests),
2528
+ skipped: numberFrom(unitSummary?.skipped_tests, unitSummary?.skippedTests)
2529
+ }
2530
+ };
2531
+ };
2426
2532
  var evaluateGate = ({
2427
2533
  configText,
2428
2534
  waf,
2429
- cost
2535
+ cost,
2536
+ preload,
2537
+ project
2430
2538
  }) => {
2431
2539
  const gateConfig = parseGateConfig(configText);
2432
2540
  const overallScore = numberFrom(
@@ -2444,13 +2552,61 @@ var evaluateGate = ({
2444
2552
  cost?.parsed?.total_spend?.amount,
2445
2553
  cost?.raw?.total
2446
2554
  );
2555
+ const currency = String(
2556
+ cost?.parsed?.totalSpend?.currency ?? cost?.parsed?.total_spend?.currency ?? cost?.raw?.currency ?? ""
2557
+ ) || void 0;
2558
+ const savings = numberFrom(
2559
+ cost?.parsed?.estimatedSavings?.amount,
2560
+ cost?.parsed?.estimated_savings?.amount
2561
+ );
2562
+ const pillars = extractPillars(waf).map((pillar) => {
2563
+ const threshold = gateConfig?.pillarScoreMins[pillar.id] ?? gateConfig?.pillarScoreMin;
2564
+ return {
2565
+ ...pillar,
2566
+ threshold,
2567
+ status: threshold !== void 0 && pillar.score < threshold ? "fail" : "pass"
2568
+ };
2569
+ });
2570
+ const validation = extractValidation({ waf, preload, project });
2571
+ const wellArchitected = {
2572
+ overall: {
2573
+ score: overallScore,
2574
+ threshold: gateConfig?.overallScoreMin,
2575
+ status: gateConfig && overallScore !== void 0 && overallScore < gateConfig.overallScoreMin ? "fail" : "pass"
2576
+ },
2577
+ pillars,
2578
+ risks: {
2579
+ high: highRisk,
2580
+ medium: numberFrom(waf?.parsed?.counts?.mediumRisk, waf?.parsed?.counts?.medium_count),
2581
+ critical: numberFrom(waf?.parsed?.counts?.criticalRisk, waf?.parsed?.counts?.critical_count)
2582
+ },
2583
+ topFindings: Array.isArray(waf?.parsed?.rules) ? waf.parsed.rules.slice(0, 5) : []
2584
+ };
2585
+ const costSummary = {
2586
+ monthly: {
2587
+ amount: monthlyCost,
2588
+ currency,
2589
+ threshold: gateConfig?.maxMonthlyCost,
2590
+ status: gateConfig?.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost ? "fail" : "pass"
2591
+ },
2592
+ estimatedSavings: {
2593
+ amount: savings,
2594
+ currency
2595
+ },
2596
+ topServices: Array.isArray(cost?.parsed?.serviceGroups) ? cost.parsed.serviceGroups.slice(0, 5) : [],
2597
+ recommendations: Array.isArray(cost?.parsed?.recommendations) ? cost.parsed.recommendations.slice(0, 5) : []
2598
+ };
2447
2599
  if (!gateConfig) {
2448
2600
  return {
2449
2601
  status: "warn",
2450
2602
  reason: "ci.gates is not configured in .cloudeval/config.yaml.",
2603
+ enforcement: "warn",
2451
2604
  overallScore,
2452
2605
  highRisk,
2453
- monthlyCost
2606
+ monthlyCost,
2607
+ wellArchitected,
2608
+ cost: costSummary,
2609
+ validation
2454
2610
  };
2455
2611
  }
2456
2612
  const failures = [];
@@ -2462,16 +2618,34 @@ var evaluateGate = ({
2462
2618
  if (gateConfig.failOnHighRisk && highRisk !== void 0 && highRisk > 0) {
2463
2619
  failures.push(`${highRisk} high-risk architecture findings`);
2464
2620
  }
2621
+ for (const pillar of pillars) {
2622
+ if (pillar.status === "fail") {
2623
+ failures.push(`${pillar.label} score ${pillar.score} is below ${pillar.threshold}`);
2624
+ }
2625
+ }
2626
+ const failedPsRules = validation.psRule.failed ?? 0;
2627
+ const failedUnitTests = validation.unitTests.failed ?? 0;
2628
+ if (gateConfig.failOnValidationErrors && (failedPsRules > 0 || failedUnitTests > 0)) {
2629
+ failures.push(
2630
+ `validation has ${failedPsRules} failed PSRule checks and ${failedUnitTests} failed unit tests`
2631
+ );
2632
+ }
2465
2633
  if (gateConfig.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost) {
2466
2634
  failures.push(`monthly cost ${monthlyCost} exceeds ${gateConfig.maxMonthlyCost}`);
2467
2635
  }
2636
+ const wouldFail = failures.length > 0;
2468
2637
  return {
2469
- status: failures.length ? "fail" : "pass",
2638
+ status: wouldFail && gateConfig.enforcement === "required" ? "fail" : wouldFail ? "warn" : "pass",
2639
+ enforcement: gateConfig.enforcement,
2640
+ wouldFail,
2470
2641
  failures,
2471
2642
  thresholds: gateConfig,
2472
2643
  overallScore,
2473
2644
  highRisk,
2474
- monthlyCost
2645
+ monthlyCost,
2646
+ wellArchitected,
2647
+ cost: costSummary,
2648
+ validation
2475
2649
  };
2476
2650
  };
2477
2651
  var safeFetch = async (input) => {
@@ -2526,6 +2700,7 @@ var waitForJob = async ({
2526
2700
  baseUrl,
2527
2701
  token,
2528
2702
  userId,
2703
+ projectId,
2529
2704
  jobId,
2530
2705
  pollIntervalMs,
2531
2706
  waitTimeoutMs
@@ -2533,11 +2708,14 @@ var waitForJob = async ({
2533
2708
  const startedAt = Date.now();
2534
2709
  let lastStatus;
2535
2710
  for (; ; ) {
2536
- const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
2711
+ const query = new URLSearchParams({ project_id: projectId });
2712
+ if (userId) {
2713
+ query.set("user_id", userId);
2714
+ }
2537
2715
  lastStatus = await fetchCloudEvalJson({
2538
2716
  baseUrl,
2539
2717
  authToken: token,
2540
- path: `/jobs/${encodeURIComponent(jobId)}${query}`
2718
+ path: `/jobs/${encodeURIComponent(jobId)}?${query.toString()}`
2541
2719
  });
2542
2720
  const status = String(lastStatus.status ?? "unknown");
2543
2721
  process.stderr.write(`github sync job ${jobId}: ${status}
@@ -2579,6 +2757,7 @@ var buildAiSummaryPrompt = (data) => [
2579
2757
  `Well-Architected score: ${data.gate?.overallScore ?? "unknown"}`,
2580
2758
  `High-risk findings: ${data.gate?.highRisk ?? "unknown"}`,
2581
2759
  `Monthly cost: ${data.gate?.monthlyCost ?? "unknown"}`,
2760
+ `Validation: PSRule failed ${data.gate?.validation?.psRule?.failed ?? "unknown"}, unit tests failed ${data.gate?.validation?.unitTests?.failed ?? "unknown"}`,
2582
2761
  Array.isArray(data.gate?.failures) && data.gate.failures.length ? `Gate failures: ${data.gate.failures.join("; ")}` : "Gate failures: none reported"
2583
2762
  ].join("\n");
2584
2763
  var generateAiSummary = async ({
@@ -2587,6 +2766,8 @@ var generateAiSummary = async ({
2587
2766
  user,
2588
2767
  project,
2589
2768
  model,
2769
+ mode,
2770
+ agentProfileId,
2590
2771
  data
2591
2772
  }) => {
2592
2773
  const core = await import("./dist-CFLR5FML.js");
@@ -2613,9 +2794,10 @@ var generateAiSummary = async ({
2613
2794
  name: String(data.projectId)
2614
2795
  },
2615
2796
  settings: {
2616
- mode: "ask",
2797
+ mode,
2617
2798
  ...model ? { model } : {}
2618
2799
  },
2800
+ ...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
2619
2801
  completeAfterResponse: true,
2620
2802
  responseCompletionGraceMs: 250,
2621
2803
  streamIdleTimeoutMs: 3e4
@@ -2627,7 +2809,8 @@ var generateAiSummary = async ({
2627
2809
  }
2628
2810
  return {
2629
2811
  enabled: true,
2630
- mode: "ask",
2812
+ mode,
2813
+ ...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
2631
2814
  ...model ? { model } : {},
2632
2815
  markdown: markdown.trim(),
2633
2816
  threadId
@@ -2636,7 +2819,11 @@ var generateAiSummary = async ({
2636
2819
  var buildMarkdownSummary = (data) => {
2637
2820
  const gateStatus = String(data.gate?.status ?? "unknown").toUpperCase();
2638
2821
  const score = data.gate?.overallScore ?? "unknown";
2639
- const cost = data.gate?.monthlyCost ?? "unknown";
2822
+ const cost = data.gate?.cost?.monthly;
2823
+ const validation = data.gate?.validation;
2824
+ const pillarLines = Array.isArray(data.gate?.wellArchitected?.pillars) ? data.gate.wellArchitected.pillars.map(
2825
+ (pillar) => ` - ${pillar.label}: ${pillar.score}${pillar.threshold !== void 0 ? ` (min ${pillar.threshold})` : ""} - ${String(pillar.status ?? "unknown").toUpperCase()}`
2826
+ ) : [];
2640
2827
  const lines = [
2641
2828
  "### CloudEval review",
2642
2829
  "",
@@ -2646,8 +2833,31 @@ var buildMarkdownSummary = (data) => {
2646
2833
  `- **Commit:** \`${String(data.commitSha ?? "unknown").slice(0, 12)}\``,
2647
2834
  `- **Gate:** ${gateStatus}`,
2648
2835
  `- **Well-Architected score:** ${score}`,
2649
- `- **Monthly cost:** ${cost}`
2836
+ `- **Cost:** ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}`.trim(),
2837
+ `- **Validation:** PSRule ${validation?.psRule?.failed ?? "unknown"} failed, unit tests ${validation?.unitTests?.failed ?? "unknown"} failed`
2650
2838
  ];
2839
+ if (Array.isArray(data.gate?.failures) && data.gate.failures.length) {
2840
+ lines.push("", "#### Gate failures", "", ...data.gate.failures.map((failure) => `- ${failure}`));
2841
+ }
2842
+ if (pillarLines.length) {
2843
+ lines.push("", "#### Well-Architected drilldown", "", ...pillarLines);
2844
+ }
2845
+ if (cost?.amount !== void 0 || cost?.threshold !== void 0) {
2846
+ lines.push(
2847
+ "",
2848
+ "#### Cost drilldown",
2849
+ "",
2850
+ `- Cost: ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}${cost?.threshold !== void 0 ? ` (max ${cost.threshold})` : ""}`.trim()
2851
+ );
2852
+ }
2853
+ if (validation) {
2854
+ lines.push(
2855
+ "",
2856
+ "#### Validation drilldown",
2857
+ "",
2858
+ `- Validation: PSRule ${validation.psRule?.failed ?? "unknown"} failed, unit tests ${validation.unitTests?.failed ?? "unknown"} failed`
2859
+ );
2860
+ }
2651
2861
  if (data.aiSummary?.markdown) {
2652
2862
  lines.push("", "## AI summary", "", data.aiSummary.markdown);
2653
2863
  }
@@ -2657,7 +2867,7 @@ var registerReviewCommand = (program2, deps) => {
2657
2867
  const command = addAuthOptions(
2658
2868
  program2.command("review").description("Review the current GitHub-backed project from a pushed commit"),
2659
2869
  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");
2870
+ ).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("--ai-summary-mode <mode>", "AI summary mode: ask or agent.", "ask").option("--ai-summary-profile <profile-id>", "Agent Profile id when --ai-summary-mode agent is used.", "architecture").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
2871
  command.action(async (options, actionCommand) => {
2662
2872
  try {
2663
2873
  const cwd = process.cwd();
@@ -2690,6 +2900,7 @@ var registerReviewCommand = (program2, deps) => {
2690
2900
  baseUrl: context.baseUrl,
2691
2901
  token: context.token,
2692
2902
  userId: context.user?.id,
2903
+ projectId,
2693
2904
  jobId: extractJobId2(sync),
2694
2905
  pollIntervalMs: parsePositiveInteger(
2695
2906
  options.pollInterval,
@@ -2720,6 +2931,11 @@ var registerReviewCommand = (program2, deps) => {
2720
2931
  token: context.token,
2721
2932
  projectId
2722
2933
  });
2934
+ const preload = context.user?.id ? await safeFetch({
2935
+ baseUrl: context.baseUrl,
2936
+ authToken: context.token,
2937
+ path: `/reports/preload/${encodeURIComponent(projectId)}?user_id=${encodeURIComponent(context.user.id)}&include_payload=true`
2938
+ }) : void 0;
2723
2939
  const data = {
2724
2940
  projectId,
2725
2941
  repo,
@@ -2729,18 +2945,25 @@ var registerReviewCommand = (program2, deps) => {
2729
2945
  sync: finalStatus ? { ...asRecord(sync), finalStatus } : sync,
2730
2946
  reports: {
2731
2947
  cost,
2732
- waf
2948
+ waf,
2949
+ preload
2733
2950
  },
2734
- gate: evaluateGate({ configText, waf, cost })
2951
+ gate: evaluateGate({ configText, waf, cost, preload, project })
2735
2952
  };
2736
2953
  if (options.aiSummary !== false) {
2737
2954
  try {
2955
+ const aiSummaryMode = String(options.aiSummaryMode ?? "ask").toLowerCase();
2956
+ if (!["ask", "agent"].includes(aiSummaryMode)) {
2957
+ throw new Error("--ai-summary-mode must be ask or agent.");
2958
+ }
2738
2959
  data.aiSummary = await generateAiSummary({
2739
2960
  baseUrl: context.baseUrl,
2740
2961
  token: context.token,
2741
2962
  user: context.user,
2742
2963
  project,
2743
2964
  model: options.model,
2965
+ mode: aiSummaryMode,
2966
+ agentProfileId: options.aiSummaryProfile,
2744
2967
  data
2745
2968
  });
2746
2969
  } catch (error) {
@@ -14625,7 +14848,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
14625
14848
  const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
14626
14849
  const [{ render }, { App }] = await Promise.all([
14627
14850
  import("ink"),
14628
- import("./App-H46FRLWK.js")
14851
+ import("./App-3VDZ4SKF.js")
14629
14852
  ]);
14630
14853
  const baseUrl = await resolveBaseUrl(options, command);
14631
14854
  assertSecureBaseUrl(baseUrl);
@@ -14683,7 +14906,7 @@ program.command("chat").description("Start an interactive chat session").option(
14683
14906
  const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
14684
14907
  const [{ render }, { App }] = await Promise.all([
14685
14908
  import("ink"),
14686
- import("./App-H46FRLWK.js")
14909
+ import("./App-3VDZ4SKF.js")
14687
14910
  ]);
14688
14911
  const baseUrl = await resolveBaseUrl(options, command);
14689
14912
  assertSecureBaseUrl(baseUrl);
@@ -15437,7 +15660,7 @@ Error: ${errorMsg}
15437
15660
  program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
15438
15661
  const { render } = await import("ink");
15439
15662
  const BannerPreview = React.lazy(async () => ({
15440
- default: (await import("./Banner-7X2VHUVH.js")).Banner
15663
+ default: (await import("./Banner-7BP5U7P4.js")).Banner
15441
15664
  }));
15442
15665
  render(
15443
15666
  /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganakailabs/cloudeval-cli",
3
- "version": "0.25.0",
3
+ "version": "0.26.1",
4
4
  "license": "LicenseRef-CloudEval-CLI",
5
5
  "type": "module",
6
6
  "description": "CloudEval CLI for cloud architecture, cost, report, automation, and MCP workflows.",
package/sbom.spdx.json CHANGED
@@ -14,7 +14,7 @@
14
14
  {
15
15
  "SPDXID": "SPDXRef-Package-CloudEval-CLI",
16
16
  "name": "CloudEval CLI",
17
- "versionInfo": "0.25.0",
17
+ "versionInfo": "0.26.1",
18
18
  "downloadLocation": "https://github.com/ganakailabs/cloudeval-cli",
19
19
  "filesAnalyzed": false,
20
20
  "licenseConcluded": "LicenseRef-CloudEval-CLI",