@ganakailabs/cloudeval-cli 0.25.0 → 0.26.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/{App-H46FRLWK.js → App-XNVJF2ON.js} +2 -2
- package/dist/{Banner-7X2VHUVH.js → Banner-2FNO54OA.js} +2 -2
- package/dist/{chunk-LKVKOGVL.js → chunk-IAXSLM2G.js} +1 -1
- package/dist/{chunk-TISPT6EB.js → chunk-U5TI24XX.js} +1 -1
- package/dist/cli.js +233 -15
- package/package.json +1 -1
- package/sbom.spdx.json +1 -1
|
@@ -38,10 +38,10 @@ import {
|
|
|
38
38
|
} from "./chunk-KBHRBGSX.js";
|
|
39
39
|
import {
|
|
40
40
|
Banner
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-U5TI24XX.js";
|
|
42
42
|
import {
|
|
43
43
|
CLI_VERSION
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-IAXSLM2G.js";
|
|
45
45
|
import {
|
|
46
46
|
raisedButtonStyle,
|
|
47
47
|
terminalTheme
|
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-
|
|
39
|
+
} from "./chunk-IAXSLM2G.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:
|
|
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) => {
|
|
@@ -2579,6 +2753,7 @@ var buildAiSummaryPrompt = (data) => [
|
|
|
2579
2753
|
`Well-Architected score: ${data.gate?.overallScore ?? "unknown"}`,
|
|
2580
2754
|
`High-risk findings: ${data.gate?.highRisk ?? "unknown"}`,
|
|
2581
2755
|
`Monthly cost: ${data.gate?.monthlyCost ?? "unknown"}`,
|
|
2756
|
+
`Validation: PSRule failed ${data.gate?.validation?.psRule?.failed ?? "unknown"}, unit tests failed ${data.gate?.validation?.unitTests?.failed ?? "unknown"}`,
|
|
2582
2757
|
Array.isArray(data.gate?.failures) && data.gate.failures.length ? `Gate failures: ${data.gate.failures.join("; ")}` : "Gate failures: none reported"
|
|
2583
2758
|
].join("\n");
|
|
2584
2759
|
var generateAiSummary = async ({
|
|
@@ -2587,6 +2762,8 @@ var generateAiSummary = async ({
|
|
|
2587
2762
|
user,
|
|
2588
2763
|
project,
|
|
2589
2764
|
model,
|
|
2765
|
+
mode,
|
|
2766
|
+
agentProfileId,
|
|
2590
2767
|
data
|
|
2591
2768
|
}) => {
|
|
2592
2769
|
const core = await import("./dist-CFLR5FML.js");
|
|
@@ -2613,9 +2790,10 @@ var generateAiSummary = async ({
|
|
|
2613
2790
|
name: String(data.projectId)
|
|
2614
2791
|
},
|
|
2615
2792
|
settings: {
|
|
2616
|
-
mode
|
|
2793
|
+
mode,
|
|
2617
2794
|
...model ? { model } : {}
|
|
2618
2795
|
},
|
|
2796
|
+
...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
|
|
2619
2797
|
completeAfterResponse: true,
|
|
2620
2798
|
responseCompletionGraceMs: 250,
|
|
2621
2799
|
streamIdleTimeoutMs: 3e4
|
|
@@ -2627,7 +2805,8 @@ var generateAiSummary = async ({
|
|
|
2627
2805
|
}
|
|
2628
2806
|
return {
|
|
2629
2807
|
enabled: true,
|
|
2630
|
-
mode
|
|
2808
|
+
mode,
|
|
2809
|
+
...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
|
|
2631
2810
|
...model ? { model } : {},
|
|
2632
2811
|
markdown: markdown.trim(),
|
|
2633
2812
|
threadId
|
|
@@ -2636,7 +2815,11 @@ var generateAiSummary = async ({
|
|
|
2636
2815
|
var buildMarkdownSummary = (data) => {
|
|
2637
2816
|
const gateStatus = String(data.gate?.status ?? "unknown").toUpperCase();
|
|
2638
2817
|
const score = data.gate?.overallScore ?? "unknown";
|
|
2639
|
-
const cost = data.gate?.
|
|
2818
|
+
const cost = data.gate?.cost?.monthly;
|
|
2819
|
+
const validation = data.gate?.validation;
|
|
2820
|
+
const pillarLines = Array.isArray(data.gate?.wellArchitected?.pillars) ? data.gate.wellArchitected.pillars.map(
|
|
2821
|
+
(pillar) => ` - ${pillar.label}: ${pillar.score}${pillar.threshold !== void 0 ? ` (min ${pillar.threshold})` : ""} - ${String(pillar.status ?? "unknown").toUpperCase()}`
|
|
2822
|
+
) : [];
|
|
2640
2823
|
const lines = [
|
|
2641
2824
|
"### CloudEval review",
|
|
2642
2825
|
"",
|
|
@@ -2646,8 +2829,31 @@ var buildMarkdownSummary = (data) => {
|
|
|
2646
2829
|
`- **Commit:** \`${String(data.commitSha ?? "unknown").slice(0, 12)}\``,
|
|
2647
2830
|
`- **Gate:** ${gateStatus}`,
|
|
2648
2831
|
`- **Well-Architected score:** ${score}`,
|
|
2649
|
-
`- **
|
|
2832
|
+
`- **Cost:** ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}`.trim(),
|
|
2833
|
+
`- **Validation:** PSRule ${validation?.psRule?.failed ?? "unknown"} failed, unit tests ${validation?.unitTests?.failed ?? "unknown"} failed`
|
|
2650
2834
|
];
|
|
2835
|
+
if (Array.isArray(data.gate?.failures) && data.gate.failures.length) {
|
|
2836
|
+
lines.push("", "#### Gate failures", "", ...data.gate.failures.map((failure) => `- ${failure}`));
|
|
2837
|
+
}
|
|
2838
|
+
if (pillarLines.length) {
|
|
2839
|
+
lines.push("", "#### Well-Architected drilldown", "", ...pillarLines);
|
|
2840
|
+
}
|
|
2841
|
+
if (cost?.amount !== void 0 || cost?.threshold !== void 0) {
|
|
2842
|
+
lines.push(
|
|
2843
|
+
"",
|
|
2844
|
+
"#### Cost drilldown",
|
|
2845
|
+
"",
|
|
2846
|
+
`- Cost: ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}${cost?.threshold !== void 0 ? ` (max ${cost.threshold})` : ""}`.trim()
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
if (validation) {
|
|
2850
|
+
lines.push(
|
|
2851
|
+
"",
|
|
2852
|
+
"#### Validation drilldown",
|
|
2853
|
+
"",
|
|
2854
|
+
`- Validation: PSRule ${validation.psRule?.failed ?? "unknown"} failed, unit tests ${validation.unitTests?.failed ?? "unknown"} failed`
|
|
2855
|
+
);
|
|
2856
|
+
}
|
|
2651
2857
|
if (data.aiSummary?.markdown) {
|
|
2652
2858
|
lines.push("", "## AI summary", "", data.aiSummary.markdown);
|
|
2653
2859
|
}
|
|
@@ -2657,7 +2863,7 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
2657
2863
|
const command = addAuthOptions(
|
|
2658
2864
|
program2.command("review").description("Review the current GitHub-backed project from a pushed commit"),
|
|
2659
2865
|
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");
|
|
2866
|
+
).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
2867
|
command.action(async (options, actionCommand) => {
|
|
2662
2868
|
try {
|
|
2663
2869
|
const cwd = process.cwd();
|
|
@@ -2720,6 +2926,11 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
2720
2926
|
token: context.token,
|
|
2721
2927
|
projectId
|
|
2722
2928
|
});
|
|
2929
|
+
const preload = context.user?.id ? await safeFetch({
|
|
2930
|
+
baseUrl: context.baseUrl,
|
|
2931
|
+
authToken: context.token,
|
|
2932
|
+
path: `/reports/preload/${encodeURIComponent(projectId)}?user_id=${encodeURIComponent(context.user.id)}&include_payload=true`
|
|
2933
|
+
}) : void 0;
|
|
2723
2934
|
const data = {
|
|
2724
2935
|
projectId,
|
|
2725
2936
|
repo,
|
|
@@ -2729,18 +2940,25 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
2729
2940
|
sync: finalStatus ? { ...asRecord(sync), finalStatus } : sync,
|
|
2730
2941
|
reports: {
|
|
2731
2942
|
cost,
|
|
2732
|
-
waf
|
|
2943
|
+
waf,
|
|
2944
|
+
preload
|
|
2733
2945
|
},
|
|
2734
|
-
gate: evaluateGate({ configText, waf, cost })
|
|
2946
|
+
gate: evaluateGate({ configText, waf, cost, preload, project })
|
|
2735
2947
|
};
|
|
2736
2948
|
if (options.aiSummary !== false) {
|
|
2737
2949
|
try {
|
|
2950
|
+
const aiSummaryMode = String(options.aiSummaryMode ?? "ask").toLowerCase();
|
|
2951
|
+
if (!["ask", "agent"].includes(aiSummaryMode)) {
|
|
2952
|
+
throw new Error("--ai-summary-mode must be ask or agent.");
|
|
2953
|
+
}
|
|
2738
2954
|
data.aiSummary = await generateAiSummary({
|
|
2739
2955
|
baseUrl: context.baseUrl,
|
|
2740
2956
|
token: context.token,
|
|
2741
2957
|
user: context.user,
|
|
2742
2958
|
project,
|
|
2743
2959
|
model: options.model,
|
|
2960
|
+
mode: aiSummaryMode,
|
|
2961
|
+
agentProfileId: options.aiSummaryProfile,
|
|
2744
2962
|
data
|
|
2745
2963
|
});
|
|
2746
2964
|
} catch (error) {
|
|
@@ -14625,7 +14843,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
|
|
|
14625
14843
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14626
14844
|
const [{ render }, { App }] = await Promise.all([
|
|
14627
14845
|
import("ink"),
|
|
14628
|
-
import("./App-
|
|
14846
|
+
import("./App-XNVJF2ON.js")
|
|
14629
14847
|
]);
|
|
14630
14848
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14631
14849
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -14683,7 +14901,7 @@ program.command("chat").description("Start an interactive chat session").option(
|
|
|
14683
14901
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14684
14902
|
const [{ render }, { App }] = await Promise.all([
|
|
14685
14903
|
import("ink"),
|
|
14686
|
-
import("./App-
|
|
14904
|
+
import("./App-XNVJF2ON.js")
|
|
14687
14905
|
]);
|
|
14688
14906
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14689
14907
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -15437,7 +15655,7 @@ Error: ${errorMsg}
|
|
|
15437
15655
|
program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
|
|
15438
15656
|
const { render } = await import("ink");
|
|
15439
15657
|
const BannerPreview = React.lazy(async () => ({
|
|
15440
|
-
default: (await import("./Banner-
|
|
15658
|
+
default: (await import("./Banner-2FNO54OA.js")).Banner
|
|
15441
15659
|
}));
|
|
15442
15660
|
render(
|
|
15443
15661
|
/* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })
|
package/package.json
CHANGED
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.
|
|
17
|
+
"versionInfo": "0.26.0",
|
|
18
18
|
"downloadLocation": "https://github.com/ganakailabs/cloudeval-cli",
|
|
19
19
|
"filesAnalyzed": false,
|
|
20
20
|
"licenseConcluded": "LicenseRef-CloudEval-CLI",
|