@ganakailabs/cloudeval-cli 0.30.2 → 0.30.4
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/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +2 -1
- package/dist/{App-5LBIATTG.js → App-AN3ELGIY.js} +2 -2
- package/dist/{Banner-LJ5AITDD.js → Banner-XD5GCTUQ.js} +2 -2
- package/dist/{chunk-6WVS2D2U.js → chunk-NOR7UT66.js} +1 -1
- package/dist/{chunk-XGKZ2VPZ.js → chunk-RVZOUNMP.js} +1 -1
- package/dist/cli.js +385 -40
- package/package.json +2 -1
- package/sbom.spdx.json +17 -1
package/README.md
CHANGED
|
@@ -117,7 +117,7 @@ jobs:
|
|
|
117
117
|
--non-interactive
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
Public example: [passing baseline PR #6](https://github.com/ganakailabs/cloudeval-azure-arm-review-example/pull/6) in [`ganakailabs/cloudeval-azure-arm-review-example`](https://github.com/ganakailabs/cloudeval-azure-arm-review-example).
|
|
120
|
+
Public example: [passing baseline PR #6](https://github.com/ganakailabs/cloudeval-azure-arm-review-example/pull/6) in [`ganakailabs/cloudeval-azure-arm-review-example`](https://github.com/ganakailabs/cloudeval-azure-arm-review-example). Review comments show a merge-gate table, CloudEval report badges, a visible AI summary, a folded detailed AI reviewer note, a compact Well-Architected radar/table drilldown, and cost Mermaid charts grouped for quick scanning.
|
|
121
121
|
|
|
122
122
|
### MCP For Codex, Cursor, Claude, VS Code
|
|
123
123
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -14,7 +14,7 @@ This notice is not a substitute for legal review before public or enterprise dis
|
|
|
14
14
|
| --- | ---: |
|
|
15
15
|
| (MIT OR CC0-1.0) | 2 |
|
|
16
16
|
| 0BSD | 1 |
|
|
17
|
-
| Apache-2.0 |
|
|
17
|
+
| Apache-2.0 | 51 |
|
|
18
18
|
| BSD-3-Clause | 3 |
|
|
19
19
|
| ISC | 12 |
|
|
20
20
|
| MIT | 172 |
|
|
@@ -226,6 +226,7 @@ This notice is not a substitute for legal review before public or enterprise dis
|
|
|
226
226
|
| semver | 7.7.3 | ISC | GitHub Inc. | https://github.com/npm/node-semver#readme |
|
|
227
227
|
| shell-quote | 1.8.4 | MIT | James Halliday | https://github.com/ljharb/shell-quote |
|
|
228
228
|
| signal-exit | 3.0.7 | ISC | Ben Coe | https://github.com/tapjs/signal-exit |
|
|
229
|
+
| signalstory | 0.1.0 | Apache-2.0 | NOASSERTION | NOASSERTION |
|
|
229
230
|
| skin-tone | 2.0.0 | MIT | Sindre Sorhus | https://github.com/sindresorhus/skin-tone#readme |
|
|
230
231
|
| slice-ansi | 5.0.0 | MIT | NOASSERTION | https://github.com/chalk/slice-ansi#readme |
|
|
231
232
|
| slice-ansi | 6.0.0 | MIT | NOASSERTION | https://github.com/chalk/slice-ansi#readme |
|
|
@@ -38,10 +38,10 @@ import {
|
|
|
38
38
|
} from "./chunk-NXM4JEOB.js";
|
|
39
39
|
import {
|
|
40
40
|
Banner
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-NOR7UT66.js";
|
|
42
42
|
import {
|
|
43
43
|
CLI_VERSION
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-RVZOUNMP.js";
|
|
45
45
|
import {
|
|
46
46
|
raisedButtonStyle,
|
|
47
47
|
terminalTheme
|
package/dist/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
} from "./chunk-NXM4JEOB.js";
|
|
40
40
|
import {
|
|
41
41
|
CLI_VERSION
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-RVZOUNMP.js";
|
|
43
43
|
|
|
44
44
|
// src/runtime/prepareInk.ts
|
|
45
45
|
import fs from "fs";
|
|
@@ -2541,6 +2541,85 @@ var fetchCloudEvalJson = async ({
|
|
|
2541
2541
|
return await response.json();
|
|
2542
2542
|
};
|
|
2543
2543
|
|
|
2544
|
+
// src/signalstoryReviewAdapter.ts
|
|
2545
|
+
import {
|
|
2546
|
+
createSignalStoryEngine,
|
|
2547
|
+
renderPlainText
|
|
2548
|
+
} from "signalstory/core";
|
|
2549
|
+
import { renderGithubSummary } from "signalstory/markdown";
|
|
2550
|
+
var renderSignalStoryPlainText = (parts = []) => renderPlainText(parts);
|
|
2551
|
+
var REVIEW_FALLBACK_RULE_PACK = {
|
|
2552
|
+
id: "cloudeval-review-fallback",
|
|
2553
|
+
rules: [
|
|
2554
|
+
{
|
|
2555
|
+
id: "review-fallback",
|
|
2556
|
+
when: { signal: "gateStatus", exists: true },
|
|
2557
|
+
story: {
|
|
2558
|
+
id: "review-fallback",
|
|
2559
|
+
severity: "high",
|
|
2560
|
+
icon: "git-pull-request",
|
|
2561
|
+
priority: 100,
|
|
2562
|
+
sentence: [
|
|
2563
|
+
{ text: "CloudEval review completed with " },
|
|
2564
|
+
{ path: "gateStatus", marks: ["bold"] },
|
|
2565
|
+
{ text: ". Well-Architected posture is " },
|
|
2566
|
+
{ path: "score", marks: ["bold"] },
|
|
2567
|
+
{ text: " (" },
|
|
2568
|
+
{ path: "rating", marks: ["bold"] },
|
|
2569
|
+
{ text: "), validation has " },
|
|
2570
|
+
{ path: "failedTests", suffix: " failed unit tests", marks: ["bold"] },
|
|
2571
|
+
{ text: ", policy checks are " },
|
|
2572
|
+
{ path: "policyStatus", marks: ["bold"] },
|
|
2573
|
+
{ text: ", and monthly cost is " },
|
|
2574
|
+
{ path: "monthlyCost", marks: ["bold"] },
|
|
2575
|
+
{ text: ". Prioritize " },
|
|
2576
|
+
{ text: "failed validation checks", marks: ["bold"] },
|
|
2577
|
+
{ text: " and " },
|
|
2578
|
+
{ path: "weakestPillar", marks: ["bold"] },
|
|
2579
|
+
{ text: " first." }
|
|
2580
|
+
],
|
|
2581
|
+
rationale: [
|
|
2582
|
+
{
|
|
2583
|
+
text: "Failed validation, weak architecture pillars, and cost over budget are the highest-signal remediation inputs before merge."
|
|
2584
|
+
}
|
|
2585
|
+
],
|
|
2586
|
+
action: { label: "Fix failed validation checks and rerun the review." }
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
]
|
|
2590
|
+
};
|
|
2591
|
+
var buildSignalStoryReviewFallback = (input) => {
|
|
2592
|
+
const engine = createSignalStoryEngine({
|
|
2593
|
+
rulePacks: [REVIEW_FALLBACK_RULE_PACK]
|
|
2594
|
+
});
|
|
2595
|
+
const stories = engine.generate({ signals: input });
|
|
2596
|
+
const primary = stories[0];
|
|
2597
|
+
if (!primary) {
|
|
2598
|
+
return null;
|
|
2599
|
+
}
|
|
2600
|
+
const shortSummary = renderSignalStoryPlainText(primary.sentence);
|
|
2601
|
+
const detailsMarkdown = [
|
|
2602
|
+
`**Main risk**
|
|
2603
|
+
${renderSignalStoryPlainText(primary.sentence)}`,
|
|
2604
|
+
`**Why it matters**
|
|
2605
|
+
${renderSignalStoryPlainText(primary.rationale ?? [])}`,
|
|
2606
|
+
`**Recommended actions**
|
|
2607
|
+
${primary.action?.label ?? "Rerun the review after remediation."}`,
|
|
2608
|
+
"**Evidence used**\n**Gate status**, **Well-Architected score**, **validation totals**, **policy totals**, and **monthly cost**."
|
|
2609
|
+
].join("\n\n");
|
|
2610
|
+
return {
|
|
2611
|
+
enabled: true,
|
|
2612
|
+
status: "fallback",
|
|
2613
|
+
fallbackUsed: true,
|
|
2614
|
+
warnings: [],
|
|
2615
|
+
shortSummary,
|
|
2616
|
+
detailsMarkdown,
|
|
2617
|
+
markdown: renderGithubSummary(stories, {
|
|
2618
|
+
title: "CloudEval review summary"
|
|
2619
|
+
})
|
|
2620
|
+
};
|
|
2621
|
+
};
|
|
2622
|
+
|
|
2544
2623
|
// src/reviewCommand.ts
|
|
2545
2624
|
var DIRTY_REVIEW_MESSAGE = "Reviews pushed commits only. Add --ignore-dirty to review HEAD anyway.";
|
|
2546
2625
|
var runGit = async (cwd, args) => {
|
|
@@ -3167,22 +3246,211 @@ var compactCostRowsForChart = (rows, totalAmount, currency, options = {}) => {
|
|
|
3167
3246
|
return result;
|
|
3168
3247
|
};
|
|
3169
3248
|
var markdownLink = (label, url) => url ? `[${label}](${url})` : label;
|
|
3170
|
-
var
|
|
3249
|
+
var shieldSegment = (value) => encodeURIComponent(value.trim().replace(/-/g, "--").replace(/_/g, "__"));
|
|
3250
|
+
var badgeLink = ({
|
|
3251
|
+
label,
|
|
3252
|
+
message,
|
|
3253
|
+
color,
|
|
3254
|
+
url
|
|
3255
|
+
}) => {
|
|
3256
|
+
if (!url) {
|
|
3257
|
+
return void 0;
|
|
3258
|
+
}
|
|
3259
|
+
return `[}-${shieldSegment(message)}-${color}?style=flat-square)](${url})`;
|
|
3260
|
+
};
|
|
3261
|
+
var openInCloudEvalBadges = (links) => {
|
|
3171
3262
|
if (!links) {
|
|
3172
3263
|
return [];
|
|
3173
3264
|
}
|
|
3174
3265
|
const reports = asRecord(links.reports);
|
|
3175
3266
|
const downloads = asRecord(links.downloads);
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3267
|
+
return [
|
|
3268
|
+
badgeLink({
|
|
3269
|
+
label: "Project",
|
|
3270
|
+
message: "preview",
|
|
3271
|
+
color: "2563eb",
|
|
3272
|
+
url: typeof links.project === "string" ? links.project : void 0
|
|
3273
|
+
}),
|
|
3274
|
+
badgeLink({
|
|
3275
|
+
label: "Report",
|
|
3276
|
+
message: "architecture",
|
|
3277
|
+
color: "16a34a",
|
|
3278
|
+
url: typeof reports.architecture === "string" ? reports.architecture : void 0
|
|
3279
|
+
}),
|
|
3280
|
+
badgeLink({
|
|
3281
|
+
label: "Cost",
|
|
3282
|
+
message: "drilldown",
|
|
3283
|
+
color: "0f766e",
|
|
3284
|
+
url: typeof reports.cost === "string" ? reports.cost : void 0
|
|
3285
|
+
}),
|
|
3286
|
+
badgeLink({
|
|
3287
|
+
label: "Validation",
|
|
3288
|
+
message: "details",
|
|
3289
|
+
color: "d97706",
|
|
3290
|
+
url: typeof reports.validation === "string" ? reports.validation : void 0
|
|
3291
|
+
}),
|
|
3292
|
+
badgeLink({
|
|
3293
|
+
label: "PDF",
|
|
3294
|
+
message: "download",
|
|
3295
|
+
color: "7c3aed",
|
|
3296
|
+
url: typeof downloads.pdf === "string" ? downloads.pdf : void 0
|
|
3297
|
+
}),
|
|
3298
|
+
badgeLink({
|
|
3299
|
+
label: "Workflow",
|
|
3300
|
+
message: "run",
|
|
3301
|
+
color: "475569",
|
|
3302
|
+
url: typeof links.workflowRun === "string" ? links.workflowRun : void 0
|
|
3303
|
+
}),
|
|
3304
|
+
badgeLink({
|
|
3305
|
+
label: "Artifacts",
|
|
3306
|
+
message: "review",
|
|
3307
|
+
color: "475569",
|
|
3308
|
+
url: typeof downloads.reviewArtifacts === "string" ? downloads.reviewArtifacts : void 0
|
|
3309
|
+
})
|
|
3310
|
+
].filter((entry) => Boolean(entry));
|
|
3311
|
+
};
|
|
3312
|
+
var signalTableCell = (summaryLine) => {
|
|
3313
|
+
const match = summaryLine.match(/^(\S+)\s+[^:]+:\s*(.+)$/);
|
|
3314
|
+
if (!match) {
|
|
3315
|
+
return compactMarkdownCell(summaryLine);
|
|
3316
|
+
}
|
|
3317
|
+
return `${match[1]} **${compactMarkdownCell(match[2])}**`;
|
|
3318
|
+
};
|
|
3319
|
+
var reviewDecisionLine = ({
|
|
3320
|
+
gateStatus,
|
|
3321
|
+
score,
|
|
3322
|
+
rating
|
|
3323
|
+
}) => {
|
|
3324
|
+
if (gateStatus === "FAIL") {
|
|
3325
|
+
return `${statusIcon(gateStatus)} **FAIL** - configured gates failed. Do not merge until the action queue is resolved and CloudEval is rerun.`;
|
|
3326
|
+
}
|
|
3327
|
+
if (gateStatus === "WARN") {
|
|
3328
|
+
return `${statusIcon(gateStatus)} **WARN** - configured gates are warning-only or non-blocking for this run. Review the action queue before merge.`;
|
|
3329
|
+
}
|
|
3330
|
+
if (rating === "CRITICAL" || rating === "POOR") {
|
|
3331
|
+
return `${statusIcon(gateStatus)} **PASS** - configured gates passed, but observed Well-Architected posture is **${formatScore(score)} (${rating})**. Tighten gate thresholds if this posture should block pull requests.`;
|
|
3332
|
+
}
|
|
3333
|
+
return `${statusIcon(gateStatus)} **PASS** - configured gates passed for this review. Use the drilldowns below to keep the posture improving.`;
|
|
3334
|
+
};
|
|
3335
|
+
var strongestPillarRisk = (pillars) => pillars.map((pillar) => ({
|
|
3336
|
+
label: String(pillar.label ?? pillar.id ?? "Well-Architected pillar"),
|
|
3337
|
+
score: numberFrom(pillar.score)
|
|
3338
|
+
})).filter((pillar) => pillar.score !== void 0).sort((left, right) => left.score - right.score).map((pillar) => ({
|
|
3339
|
+
...pillar,
|
|
3340
|
+
rating: scoreRating(pillar.score)
|
|
3341
|
+
}))[0];
|
|
3342
|
+
var reviewActionItems = ({
|
|
3343
|
+
data,
|
|
3344
|
+
pillars,
|
|
3345
|
+
cost,
|
|
3346
|
+
validation
|
|
3347
|
+
}) => {
|
|
3348
|
+
const endpointActions = Array.isArray(data.aiSummary?.recommendedActions) ? data.aiSummary.recommendedActions.map((action) => compactMarkdownCell(action, "")).filter(Boolean) : [];
|
|
3349
|
+
const failedUnitTests = numberFrom(validation?.unitTests?.failed) ?? 0;
|
|
3350
|
+
const failedPolicyChecks = numberFrom(validation?.policyChecks?.failed) ?? 0;
|
|
3351
|
+
const weakest = strongestPillarRisk(pillars);
|
|
3352
|
+
const currentCost = numberFrom(cost?.amount);
|
|
3353
|
+
const savings = numberFrom(data.gate?.cost?.estimatedSavings?.amount);
|
|
3354
|
+
const currency = cost?.currency ?? data.gate?.cost?.estimatedSavings?.currency;
|
|
3355
|
+
const actions = [...endpointActions];
|
|
3356
|
+
if (failedUnitTests > 0 || failedPolicyChecks > 0) {
|
|
3357
|
+
const parts = [];
|
|
3358
|
+
if (failedUnitTests > 0) {
|
|
3359
|
+
parts.push(`${displayNumber(failedUnitTests)} failed unit tests`);
|
|
3360
|
+
}
|
|
3361
|
+
if (failedPolicyChecks > 0) {
|
|
3362
|
+
parts.push(`${displayNumber(failedPolicyChecks)} failed policy checks`);
|
|
3363
|
+
}
|
|
3364
|
+
actions.push(
|
|
3365
|
+
`**Fix validation failures** - resolve ${joinReadableList(parts)} and rerun CloudEval review.`
|
|
3366
|
+
);
|
|
3367
|
+
}
|
|
3368
|
+
if (weakest) {
|
|
3369
|
+
actions.push(
|
|
3370
|
+
`**Prioritize ${weakest.label}** - weakest pillar is **${formatScore(weakest.score)} (${weakest.rating ?? "UNKNOWN"})**.`
|
|
3371
|
+
);
|
|
3372
|
+
}
|
|
3373
|
+
if (currentCost !== void 0) {
|
|
3374
|
+
const savingsText = savings !== void 0 && savings > 0 ? `; review the estimated **${formatMonthlyMoney(savings, currency)}** savings` : "";
|
|
3375
|
+
actions.push(
|
|
3376
|
+
`**Review cost drivers** - current monthly cost is **${formatMonthlyMoney(currentCost, currency)}**${savingsText}.`
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
if (Array.isArray(data.gate?.failures)) {
|
|
3380
|
+
for (const failure of data.gate.failures) {
|
|
3381
|
+
actions.push(`**Address gate failure** - ${compactMarkdownCell(failure)}.`);
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
actions.push("**Rerun CloudEval** - confirm the updated gate, reports, and PR comment after remediation.");
|
|
3385
|
+
const unique2 = [];
|
|
3386
|
+
for (const action of actions) {
|
|
3387
|
+
if (!unique2.includes(action)) {
|
|
3388
|
+
unique2.push(action);
|
|
3389
|
+
}
|
|
3390
|
+
if (unique2.length >= 3) {
|
|
3391
|
+
break;
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
return unique2.map((action, index) => `${index + 1}. ${action}`);
|
|
3395
|
+
};
|
|
3396
|
+
var mermaidAxisId = (label) => {
|
|
3397
|
+
const normalized = normalizeKey(label).replace(/[^a-z0-9_]/g, "_");
|
|
3398
|
+
return normalized || "pillar";
|
|
3399
|
+
};
|
|
3400
|
+
var compactMermaidAxisLabel = (label) => {
|
|
3401
|
+
const compactLabels = {
|
|
3402
|
+
security: "Security",
|
|
3403
|
+
reliability: "Reliability",
|
|
3404
|
+
cost_optimization: "Cost",
|
|
3405
|
+
operational_excellence: "Ops",
|
|
3406
|
+
performance_efficiency: "Performance"
|
|
3407
|
+
};
|
|
3408
|
+
const normalized = normalizeKey(label);
|
|
3409
|
+
if (compactLabels[normalized]) {
|
|
3410
|
+
return compactLabels[normalized];
|
|
3411
|
+
}
|
|
3412
|
+
const cleaned = label.trim().replace(/\s+/g, " ");
|
|
3413
|
+
if (cleaned.length <= 16) {
|
|
3414
|
+
return cleaned;
|
|
3415
|
+
}
|
|
3416
|
+
const words = cleaned.split(" ");
|
|
3417
|
+
if (words.length > 1) {
|
|
3418
|
+
return words.slice(0, 2).map((word) => word.length > 8 ? word.slice(0, 8) : word).join(" ");
|
|
3419
|
+
}
|
|
3420
|
+
return cleaned.slice(0, 16);
|
|
3421
|
+
};
|
|
3422
|
+
var wellArchitectedRadarLines = (pillars) => {
|
|
3423
|
+
const scored = pillars.map((pillar) => {
|
|
3424
|
+
const label = String(pillar.label ?? pillar.id ?? "Pillar");
|
|
3425
|
+
const score = numberFrom(pillar.score);
|
|
3426
|
+
return score === void 0 ? void 0 : {
|
|
3427
|
+
id: mermaidAxisId(label),
|
|
3428
|
+
label,
|
|
3429
|
+
score
|
|
3430
|
+
};
|
|
3431
|
+
}).filter(
|
|
3432
|
+
(pillar) => pillar !== void 0
|
|
3433
|
+
);
|
|
3434
|
+
if (scored.length < 3) {
|
|
3435
|
+
return [];
|
|
3436
|
+
}
|
|
3437
|
+
return [
|
|
3438
|
+
"```mermaid",
|
|
3439
|
+
"radar-beta",
|
|
3440
|
+
" title Well-Architected posture",
|
|
3441
|
+
` axis ${scored.map(
|
|
3442
|
+
(pillar) => `${pillar.id}["${mermaidLabel(compactMermaidAxisLabel(pillar.label))}"]`
|
|
3443
|
+
).join(", ")}`,
|
|
3444
|
+
` curve current["Current"]{${scored.map((pillar) => trimNumber(pillar.score, 3)).join(", ")}}`,
|
|
3445
|
+
" showLegend false",
|
|
3446
|
+
" max 100",
|
|
3447
|
+
" min 0",
|
|
3448
|
+
" graticule polygon",
|
|
3449
|
+
" ticks 4",
|
|
3450
|
+
"```",
|
|
3451
|
+
"",
|
|
3452
|
+
"_If GitHub does not render Mermaid radar charts yet, use the table below as the fallback._"
|
|
3453
|
+
];
|
|
3186
3454
|
};
|
|
3187
3455
|
var monthlyCostImpactLines = (currentAmount, savingsAmount, currency) => {
|
|
3188
3456
|
const current = numberFrom(currentAmount);
|
|
@@ -3665,7 +3933,7 @@ var renderAiSummarySections = (shortSummary, detailsMarkdown) => {
|
|
|
3665
3933
|
lines.push(
|
|
3666
3934
|
"",
|
|
3667
3935
|
"<details>",
|
|
3668
|
-
"<summary>AI
|
|
3936
|
+
"<summary><strong>Detailed AI reviewer note - evidence, reasoning, and next actions</strong></summary>",
|
|
3669
3937
|
"",
|
|
3670
3938
|
detailsMarkdown.trim(),
|
|
3671
3939
|
"",
|
|
@@ -3698,7 +3966,7 @@ var reviewSurface = () => {
|
|
|
3698
3966
|
const ref = String(process.env.GITHUB_REF ?? "").toLowerCase();
|
|
3699
3967
|
return event.startsWith("pull_request") || ref.startsWith("refs/pull/") ? "pull_request" : "local_review";
|
|
3700
3968
|
};
|
|
3701
|
-
var buildReviewSummaryPayload = (data) => ({
|
|
3969
|
+
var buildReviewSummaryPayload = (data, preferences = {}) => ({
|
|
3702
3970
|
source: process.env.GITHUB_ACTIONS === "true" ? "github_action" : "cli",
|
|
3703
3971
|
surface: reviewSurface(),
|
|
3704
3972
|
project: data.project ?? { id: data.projectId },
|
|
@@ -3717,7 +3985,12 @@ var buildReviewSummaryPayload = (data) => ({
|
|
|
3717
3985
|
validation: data.gate?.validation ?? {},
|
|
3718
3986
|
policy: data.gate?.validation?.policy ?? data.gate?.policy ?? {},
|
|
3719
3987
|
architecture_signals: data.gate?.architecture ?? {},
|
|
3720
|
-
changed_files: data.changedFiles ?? []
|
|
3988
|
+
changed_files: data.changedFiles ?? [],
|
|
3989
|
+
ai_preferences: {
|
|
3990
|
+
mode: preferences.mode ?? "ask",
|
|
3991
|
+
...preferences.agentProfileId ? { agent_profile_id: preferences.agentProfileId } : {},
|
|
3992
|
+
...preferences.model ? { model: preferences.model } : {}
|
|
3993
|
+
}
|
|
3721
3994
|
});
|
|
3722
3995
|
var deterministicAiSummary = (data, error) => {
|
|
3723
3996
|
const score = data.gate?.wellArchitected?.overall?.score ?? data.gate?.overallScore;
|
|
@@ -3732,6 +4005,27 @@ var deterministicAiSummary = (data, error) => {
|
|
|
3732
4005
|
)[0] : void 0;
|
|
3733
4006
|
const weakestPillarLabel = weakestPillar?.label ?? weakestPillar?.id ?? "the weakest Well-Architected pillar";
|
|
3734
4007
|
const highRisk = numberFrom(data.gate?.wellArchitected?.risks?.high) ?? 0;
|
|
4008
|
+
const signalStorySummary = buildSignalStoryReviewFallback({
|
|
4009
|
+
gateStatus: String(data.gate?.status ?? "UNKNOWN").toUpperCase(),
|
|
4010
|
+
score: formatScore(score),
|
|
4011
|
+
rating,
|
|
4012
|
+
failedTests,
|
|
4013
|
+
policyStatus,
|
|
4014
|
+
monthlyCost: formatMonthlyMoney(cost?.amount, cost?.currency),
|
|
4015
|
+
weakestPillar: weakestPillarLabel
|
|
4016
|
+
});
|
|
4017
|
+
if (signalStorySummary) {
|
|
4018
|
+
const signalStoryShortSummary = String(signalStorySummary.shortSummary ?? "").trim();
|
|
4019
|
+
const signalStoryDetailsMarkdown = String(signalStorySummary.detailsMarkdown ?? "").trim();
|
|
4020
|
+
return {
|
|
4021
|
+
...signalStorySummary,
|
|
4022
|
+
warnings: error ? [`Review summary endpoint failed: ${error}`] : [],
|
|
4023
|
+
markdown: renderAiSummarySections(
|
|
4024
|
+
signalStoryShortSummary,
|
|
4025
|
+
signalStoryDetailsMarkdown
|
|
4026
|
+
)
|
|
4027
|
+
};
|
|
4028
|
+
}
|
|
3735
4029
|
const summary = [
|
|
3736
4030
|
`CloudEval review completed with **${String(data.gate?.status ?? "UNKNOWN").toUpperCase()}**.`,
|
|
3737
4031
|
`Well-Architected posture is **${formatScore(score)} (${rating})**, validation has **${displayNumber(failedTests)} failed unit tests**, policy checks are **${policyStatus}**, and monthly cost is **${formatMonthlyMoney(cost?.amount, cost?.currency)}**.`,
|
|
@@ -3758,7 +4052,11 @@ Fix **${displayNumber(failedTests)} failed unit tests**, address **${weakestPill
|
|
|
3758
4052
|
};
|
|
3759
4053
|
var generateAiSummary = async (input) => {
|
|
3760
4054
|
try {
|
|
3761
|
-
const payload = buildReviewSummaryPayload(input.data
|
|
4055
|
+
const payload = buildReviewSummaryPayload(input.data, {
|
|
4056
|
+
model: input.model,
|
|
4057
|
+
mode: input.mode,
|
|
4058
|
+
agentProfileId: input.agentProfileId
|
|
4059
|
+
});
|
|
3762
4060
|
const response = await fetchCloudEvalJson({
|
|
3763
4061
|
baseUrl: input.baseUrl,
|
|
3764
4062
|
authToken: input.token,
|
|
@@ -3808,7 +4106,8 @@ var buildMarkdownSummary = (data) => {
|
|
|
3808
4106
|
const repository = String(data.repo ?? "unknown repository");
|
|
3809
4107
|
const ref = String(data.ref ?? "unknown ref");
|
|
3810
4108
|
const commit = String(data.commitSha ?? "unknown").slice(0, 12);
|
|
3811
|
-
const
|
|
4109
|
+
const pillars = Array.isArray(data.gate?.wellArchitected?.pillars) ? data.gate.wellArchitected.pillars : [];
|
|
4110
|
+
const pillarLines = pillars.length ? pillars.map((pillar) => {
|
|
3812
4111
|
const rating = scoreRating(pillar.score);
|
|
3813
4112
|
return `| ${pillar.label} | **${formatScore(pillar.score)}** | ${scoreRatingIcon(rating)} ${rating ?? "UNKNOWN"} |`;
|
|
3814
4113
|
}) : [];
|
|
@@ -3834,47 +4133,89 @@ var buildMarkdownSummary = (data) => {
|
|
|
3834
4133
|
);
|
|
3835
4134
|
const costPieRows = namedResourceCosts.length ? positiveResourceCosts : costServices.filter((service) => service.amount > 0);
|
|
3836
4135
|
const costPieTitle = namedResourceCosts.length ? "Monthly cost by resource" : "Monthly cost by service";
|
|
3837
|
-
const
|
|
4136
|
+
const linkBadges = openInCloudEvalBadges(data.links);
|
|
3838
4137
|
const architectureLines = architectureSignalLines({
|
|
3839
4138
|
architecture,
|
|
3840
4139
|
costServices,
|
|
3841
4140
|
costCurrency: cost?.currency,
|
|
3842
4141
|
highRiskFindings: data.gate?.wellArchitected?.risks?.high,
|
|
3843
|
-
pillars
|
|
4142
|
+
pillars
|
|
3844
4143
|
});
|
|
3845
4144
|
const validationRows = validationFailureRows(validation);
|
|
3846
4145
|
const overallRating = scoreRating(score);
|
|
4146
|
+
const actionLines = reviewActionItems({
|
|
4147
|
+
data,
|
|
4148
|
+
pillars,
|
|
4149
|
+
cost,
|
|
4150
|
+
validation
|
|
4151
|
+
});
|
|
4152
|
+
const radarLines = wellArchitectedRadarLines(pillars);
|
|
3847
4153
|
const lines = [
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
4154
|
+
"## CloudEval infrastructure review",
|
|
4155
|
+
"",
|
|
4156
|
+
"| Signal | Result |",
|
|
4157
|
+
"| --- | --- |",
|
|
4158
|
+
`| Merge gate | ${statusIcon(data.gate?.status)} **${gateStatus}** |`,
|
|
4159
|
+
`| Observed posture | ${scoreRatingIcon(overallRating)} **${formatScore(score)} (${overallRating ?? "UNKNOWN"})** |`,
|
|
4160
|
+
`| Validation | ${signalTableCell(validationSummaryLine(validation))} |`,
|
|
4161
|
+
`| Policy | ${signalTableCell(policySummaryLine(validation))} |`,
|
|
4162
|
+
`| Cost | ${signalTableCell(costSummaryLine(cost))} |`
|
|
4163
|
+
];
|
|
4164
|
+
if (linkBadges.length) {
|
|
4165
|
+
lines.push("", "### Links", "", linkBadges.join(" "));
|
|
4166
|
+
}
|
|
4167
|
+
lines.push(
|
|
4168
|
+
"",
|
|
4169
|
+
"### Decision",
|
|
4170
|
+
"",
|
|
4171
|
+
reviewDecisionLine({ gateStatus, score, rating: overallRating }),
|
|
3853
4172
|
"",
|
|
3854
|
-
"
|
|
4173
|
+
"<details>",
|
|
4174
|
+
"<summary><strong>Source</strong></summary>",
|
|
3855
4175
|
"",
|
|
3856
4176
|
`- **CloudEval project**: ${projectDisplay}`,
|
|
3857
4177
|
`- **Repository**: \`${repository}\``,
|
|
3858
4178
|
`- **Ref**: \`${ref}\``,
|
|
3859
|
-
`- **Commit**: \`${commit}
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
}
|
|
4179
|
+
`- **Commit**: \`${commit}\``,
|
|
4180
|
+
"",
|
|
4181
|
+
"</details>"
|
|
4182
|
+
);
|
|
3864
4183
|
if (data.aiSummary?.markdown) {
|
|
3865
|
-
lines.push("", "
|
|
4184
|
+
lines.push("", "### AI summary", "", data.aiSummary.markdown);
|
|
4185
|
+
}
|
|
4186
|
+
if (actionLines.length) {
|
|
4187
|
+
lines.push(
|
|
4188
|
+
"",
|
|
4189
|
+
"<details open>",
|
|
4190
|
+
`<summary><strong>Action queue - ${actionLines.length} recommended fixes</strong></summary>`,
|
|
4191
|
+
"",
|
|
4192
|
+
...actionLines,
|
|
4193
|
+
"",
|
|
4194
|
+
"</details>"
|
|
4195
|
+
);
|
|
3866
4196
|
}
|
|
3867
4197
|
if (Array.isArray(data.gate?.failures) && data.gate.failures.length) {
|
|
3868
|
-
lines.push(
|
|
4198
|
+
lines.push(
|
|
4199
|
+
"",
|
|
4200
|
+
"<details>",
|
|
4201
|
+
"<summary><strong>Gate failures</strong></summary>",
|
|
4202
|
+
"",
|
|
4203
|
+
...data.gate.failures.map((failure) => `- ${failure}`),
|
|
4204
|
+
"",
|
|
4205
|
+
"</details>"
|
|
4206
|
+
);
|
|
3869
4207
|
}
|
|
3870
4208
|
if (pillarLines.length) {
|
|
3871
4209
|
lines.push(
|
|
3872
4210
|
"",
|
|
3873
4211
|
"<details>",
|
|
3874
|
-
"<summary>Well-Architected drilldown</summary>",
|
|
4212
|
+
"<summary><strong>Well-Architected drilldown</strong></summary>",
|
|
3875
4213
|
"",
|
|
3876
4214
|
...riskLines,
|
|
3877
4215
|
"",
|
|
4216
|
+
...radarLines.length ? ["**Radar (compact labels)**", "", ...radarLines, ""] : [],
|
|
4217
|
+
"**Scores**",
|
|
4218
|
+
"",
|
|
3878
4219
|
"| Pillar | Score | Rating |",
|
|
3879
4220
|
"| --- | ---: | --- |",
|
|
3880
4221
|
...pillarLines,
|
|
@@ -3890,14 +4231,18 @@ var buildMarkdownSummary = (data) => {
|
|
|
3890
4231
|
cost?.currency ?? data.gate?.cost?.estimatedSavings?.currency
|
|
3891
4232
|
);
|
|
3892
4233
|
if (impactLines.length) {
|
|
3893
|
-
costLines.push(...impactLines);
|
|
4234
|
+
costLines.push("**Cost impact**", "", ...impactLines);
|
|
3894
4235
|
} else if (data.gate?.cost?.estimatedSavings?.amount !== void 0) {
|
|
3895
4236
|
costLines.push(
|
|
4237
|
+
"**Cost impact**",
|
|
4238
|
+
"",
|
|
3896
4239
|
`- Estimated savings: **${formatMonthlyMoney(data.gate.cost.estimatedSavings.amount, data.gate.cost.estimatedSavings.currency)}**`
|
|
3897
4240
|
);
|
|
3898
4241
|
}
|
|
3899
4242
|
if (costPieRows.length) {
|
|
3900
4243
|
costLines.push(
|
|
4244
|
+
"",
|
|
4245
|
+
"**Cost split**",
|
|
3901
4246
|
"",
|
|
3902
4247
|
"```mermaid",
|
|
3903
4248
|
`pie title ${costPieTitle}`,
|
|
@@ -3921,7 +4266,7 @@ var buildMarkdownSummary = (data) => {
|
|
|
3921
4266
|
lines.push(
|
|
3922
4267
|
"",
|
|
3923
4268
|
"<details>",
|
|
3924
|
-
"<summary>Cost drilldown</summary>",
|
|
4269
|
+
"<summary><strong>Cost drilldown</strong></summary>",
|
|
3925
4270
|
"",
|
|
3926
4271
|
...costLines,
|
|
3927
4272
|
"",
|
|
@@ -3933,7 +4278,7 @@ var buildMarkdownSummary = (data) => {
|
|
|
3933
4278
|
lines.push(
|
|
3934
4279
|
"",
|
|
3935
4280
|
"<details>",
|
|
3936
|
-
`<summary>${validationRows.length ? "Validation failures" : "Validation details"}</summary>`,
|
|
4281
|
+
`<summary><strong>${validationRows.length ? "Validation failures" : "Validation details"}</strong></summary>`,
|
|
3937
4282
|
"",
|
|
3938
4283
|
...validationDetailLines(validation),
|
|
3939
4284
|
...validationRows.length ? [
|
|
@@ -3950,7 +4295,7 @@ var buildMarkdownSummary = (data) => {
|
|
|
3950
4295
|
lines.push(
|
|
3951
4296
|
"",
|
|
3952
4297
|
"<details>",
|
|
3953
|
-
"<summary>Architecture signals</summary>",
|
|
4298
|
+
"<summary><strong>Architecture signals</strong></summary>",
|
|
3954
4299
|
"",
|
|
3955
4300
|
...architectureLines,
|
|
3956
4301
|
"",
|
|
@@ -16869,7 +17214,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option("--b
|
|
|
16869
17214
|
const { assertSecureBaseUrl } = await import("./dist-6LEMVXIY.js");
|
|
16870
17215
|
const [{ render }, { App }] = await Promise.all([
|
|
16871
17216
|
import("ink"),
|
|
16872
|
-
import("./App-
|
|
17217
|
+
import("./App-AN3ELGIY.js")
|
|
16873
17218
|
]);
|
|
16874
17219
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
16875
17220
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -16930,7 +17275,7 @@ program.command("chat").description("Start an interactive chat session").option(
|
|
|
16930
17275
|
const { assertSecureBaseUrl } = await import("./dist-6LEMVXIY.js");
|
|
16931
17276
|
const [{ render }, { App }] = await Promise.all([
|
|
16932
17277
|
import("ink"),
|
|
16933
|
-
import("./App-
|
|
17278
|
+
import("./App-AN3ELGIY.js")
|
|
16934
17279
|
]);
|
|
16935
17280
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
16936
17281
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -17722,7 +18067,7 @@ Error: ${errorMsg}
|
|
|
17722
18067
|
program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
|
|
17723
18068
|
const { render } = await import("ink");
|
|
17724
18069
|
const BannerPreview = React.lazy(async () => ({
|
|
17725
|
-
default: (await import("./Banner-
|
|
18070
|
+
default: (await import("./Banner-XD5GCTUQ.js")).Banner
|
|
17726
18071
|
}));
|
|
17727
18072
|
render(
|
|
17728
18073
|
/* @__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.30.
|
|
3
|
+
"version": "0.30.4",
|
|
4
4
|
"license": "LicenseRef-CloudEval-CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Review Cloud infra-as-code and live environments from CLI, CI, and MCP agents.",
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
"marked-terminal": "^7.3.0",
|
|
87
87
|
"react": "^18.3.1",
|
|
88
88
|
"react-devtools-core": "^4.28.5",
|
|
89
|
+
"signalstory": "https://github.com/ganakailabs/signalstory/archive/refs/tags/v0.1.0.tar.gz",
|
|
89
90
|
"sql.js": "^1.14.1"
|
|
90
91
|
},
|
|
91
92
|
"devDependencies": {
|
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.30.
|
|
17
|
+
"versionInfo": "0.30.4",
|
|
18
18
|
"downloadLocation": "https://github.com/ganakailabs/cloudeval-cli",
|
|
19
19
|
"filesAnalyzed": false,
|
|
20
20
|
"licenseConcluded": "LicenseRef-CloudEval-CLI",
|
|
@@ -2254,6 +2254,17 @@
|
|
|
2254
2254
|
"copyrightText": "Ben Coe",
|
|
2255
2255
|
"summary": "when you want to fire an event no matter how a process exits."
|
|
2256
2256
|
},
|
|
2257
|
+
{
|
|
2258
|
+
"SPDXID": "SPDXRef-Package-signalstory-0.1.0",
|
|
2259
|
+
"name": "signalstory",
|
|
2260
|
+
"versionInfo": "0.1.0",
|
|
2261
|
+
"downloadLocation": "NOASSERTION",
|
|
2262
|
+
"filesAnalyzed": false,
|
|
2263
|
+
"licenseConcluded": "Apache-2.0",
|
|
2264
|
+
"licenseDeclared": "Apache-2.0",
|
|
2265
|
+
"copyrightText": "NOASSERTION",
|
|
2266
|
+
"summary": "Composable signal-to-story generation for evidence-grounded product, ops, and review narratives."
|
|
2267
|
+
},
|
|
2257
2268
|
{
|
|
2258
2269
|
"SPDXID": "SPDXRef-Package-skin-tone-2.0.0",
|
|
2259
2270
|
"name": "skin-tone",
|
|
@@ -3678,6 +3689,11 @@
|
|
|
3678
3689
|
"relationshipType": "DEPENDS_ON",
|
|
3679
3690
|
"relatedSpdxElement": "SPDXRef-Package-signal-exit-3.0.7"
|
|
3680
3691
|
},
|
|
3692
|
+
{
|
|
3693
|
+
"spdxElementId": "SPDXRef-Package-CloudEval-CLI",
|
|
3694
|
+
"relationshipType": "DEPENDS_ON",
|
|
3695
|
+
"relatedSpdxElement": "SPDXRef-Package-signalstory-0.1.0"
|
|
3696
|
+
},
|
|
3681
3697
|
{
|
|
3682
3698
|
"spdxElementId": "SPDXRef-Package-CloudEval-CLI",
|
|
3683
3699
|
"relationshipType": "DEPENDS_ON",
|