@probelabs/visor 0.1.71 → 0.1.73
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/ai-review-service.d.ts +1 -0
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts +92 -3
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/index.js +3958 -78
- package/dist/output-formatters.d.ts +2 -0
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/sdk/{check-execution-engine-TBF6LPYH.mjs → check-execution-engine-75SSXUBP.mjs} +2 -2
- package/dist/sdk/{chunk-745RDQD3.mjs → chunk-NINHE4RF.mjs} +304 -35
- package/dist/sdk/chunk-NINHE4RF.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +99 -60
- package/dist/sdk/sdk.d.ts +99 -60
- package/dist/sdk/sdk.js +302 -33
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +1 -1
- package/package.json +3 -3
- package/dist/sdk/chunk-745RDQD3.mjs.map +0 -1
- /package/dist/sdk/{check-execution-engine-TBF6LPYH.mjs.map → check-execution-engine-75SSXUBP.mjs.map} +0 -0
package/dist/sdk/sdk.js
CHANGED
|
@@ -1069,6 +1069,10 @@ ${schemaString}`);
|
|
|
1069
1069
|
// We don't want the agent to modify files
|
|
1070
1070
|
debug: this.config.debug || false
|
|
1071
1071
|
};
|
|
1072
|
+
if (this.config.mcpServers && Object.keys(this.config.mcpServers).length > 0) {
|
|
1073
|
+
options.enableMcp = true;
|
|
1074
|
+
options.mcpConfig = { mcpServers: this.config.mcpServers };
|
|
1075
|
+
}
|
|
1072
1076
|
if (this.config.provider) {
|
|
1073
1077
|
const providerOverride = this.config.provider === "claude-code" || this.config.provider === "bedrock" ? "anthropic" : this.config.provider === "anthropic" || this.config.provider === "openai" || this.config.provider === "google" ? this.config.provider : void 0;
|
|
1074
1078
|
if (providerOverride) {
|
|
@@ -1536,7 +1540,7 @@ var init_reviewer = __esm({
|
|
|
1536
1540
|
if (config && checks && checks.length > 0) {
|
|
1537
1541
|
const { CheckExecutionEngine: CheckExecutionEngine2 } = await Promise.resolve().then(() => (init_check_execution_engine(), check_execution_engine_exports));
|
|
1538
1542
|
const engine = new CheckExecutionEngine2();
|
|
1539
|
-
const
|
|
1543
|
+
const { results } = await engine.executeGroupedChecks(
|
|
1540
1544
|
prInfo,
|
|
1541
1545
|
checks,
|
|
1542
1546
|
void 0,
|
|
@@ -1544,7 +1548,7 @@ var init_reviewer = __esm({
|
|
|
1544
1548
|
void 0,
|
|
1545
1549
|
debug
|
|
1546
1550
|
);
|
|
1547
|
-
return
|
|
1551
|
+
return results;
|
|
1548
1552
|
}
|
|
1549
1553
|
throw new Error(
|
|
1550
1554
|
"No configuration provided. Please create a .visor.yaml file with check definitions. Built-in prompts have been removed - all checks must be explicitly configured."
|
|
@@ -2686,15 +2690,13 @@ var init_ai_check_provider = __esm({
|
|
|
2686
2690
|
Object.assign(mcpServers, config.ai.mcpServers);
|
|
2687
2691
|
}
|
|
2688
2692
|
if (Object.keys(mcpServers).length > 0) {
|
|
2693
|
+
aiConfig.mcpServers = mcpServers;
|
|
2689
2694
|
const mcpConfig = { mcpServers };
|
|
2690
2695
|
const mcpTools = await this.setupMcpTools(mcpConfig);
|
|
2691
|
-
if (
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
`\u{1F527} Debug: AI check configured with ${mcpTools.length} MCP tools from ${Object.keys(mcpServers).length} servers`
|
|
2696
|
-
);
|
|
2697
|
-
}
|
|
2696
|
+
if (aiConfig.debug) {
|
|
2697
|
+
console.error(
|
|
2698
|
+
`\u{1F527} Debug: AI check MCP configured with ${Object.keys(mcpServers).length} servers; discovered ${mcpTools.length} tools`
|
|
2699
|
+
);
|
|
2698
2700
|
}
|
|
2699
2701
|
}
|
|
2700
2702
|
const processedPrompt = await this.processPrompt(
|
|
@@ -6014,6 +6016,7 @@ var init_check_execution_engine = __esm({
|
|
|
6014
6016
|
config;
|
|
6015
6017
|
webhookContext;
|
|
6016
6018
|
routingSandbox;
|
|
6019
|
+
executionStats = /* @__PURE__ */ new Map();
|
|
6017
6020
|
constructor(workingDirectory) {
|
|
6018
6021
|
this.workingDirectory = workingDirectory || process.cwd();
|
|
6019
6022
|
this.gitAnalyzer = new GitRepositoryAnalyzer(this.workingDirectory);
|
|
@@ -6552,12 +6555,14 @@ ${expr}
|
|
|
6552
6555
|
apiCallDetails: reviewSummary.debug.apiCallDetails
|
|
6553
6556
|
};
|
|
6554
6557
|
}
|
|
6558
|
+
const executionStatistics = this.buildExecutionStatistics();
|
|
6555
6559
|
return {
|
|
6556
6560
|
repositoryInfo,
|
|
6557
6561
|
reviewSummary,
|
|
6558
6562
|
executionTime,
|
|
6559
6563
|
timestamp,
|
|
6560
6564
|
checksExecuted: filteredChecks,
|
|
6565
|
+
executionStatistics,
|
|
6561
6566
|
debug: debugInfo
|
|
6562
6567
|
};
|
|
6563
6568
|
} catch (error) {
|
|
@@ -6750,7 +6755,7 @@ ${expr}
|
|
|
6750
6755
|
});
|
|
6751
6756
|
}
|
|
6752
6757
|
/**
|
|
6753
|
-
* Execute review checks and return grouped results for new architecture
|
|
6758
|
+
* Execute review checks and return grouped results with statistics for new architecture
|
|
6754
6759
|
*/
|
|
6755
6760
|
async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter) {
|
|
6756
6761
|
const logFn = outputFormat === "json" || outputFormat === "sarif" ? debug ? console.error : () => {
|
|
@@ -6780,7 +6785,10 @@ ${expr}
|
|
|
6780
6785
|
checks = tagFilteredChecks;
|
|
6781
6786
|
if (checks.length === 0) {
|
|
6782
6787
|
logger.warn("\u26A0\uFE0F No checks remain after tag filtering");
|
|
6783
|
-
return {
|
|
6788
|
+
return {
|
|
6789
|
+
results: {},
|
|
6790
|
+
statistics: this.buildExecutionStatistics()
|
|
6791
|
+
};
|
|
6784
6792
|
}
|
|
6785
6793
|
if (!config?.checks) {
|
|
6786
6794
|
throw new Error("Config with check definitions required for grouped execution");
|
|
@@ -6820,9 +6828,15 @@ ${expr}
|
|
|
6820
6828
|
);
|
|
6821
6829
|
const groupedResults = {};
|
|
6822
6830
|
groupedResults[checkResult.group] = [checkResult];
|
|
6823
|
-
return
|
|
6831
|
+
return {
|
|
6832
|
+
results: groupedResults,
|
|
6833
|
+
statistics: this.buildExecutionStatistics()
|
|
6834
|
+
};
|
|
6824
6835
|
}
|
|
6825
|
-
return {
|
|
6836
|
+
return {
|
|
6837
|
+
results: {},
|
|
6838
|
+
statistics: this.buildExecutionStatistics()
|
|
6839
|
+
};
|
|
6826
6840
|
}
|
|
6827
6841
|
/**
|
|
6828
6842
|
* Execute single check and return grouped result
|
|
@@ -6868,7 +6882,7 @@ ${expr}
|
|
|
6868
6882
|
};
|
|
6869
6883
|
}
|
|
6870
6884
|
/**
|
|
6871
|
-
* Execute multiple checks with dependency awareness - return grouped results
|
|
6885
|
+
* Execute multiple checks with dependency awareness - return grouped results with statistics
|
|
6872
6886
|
*/
|
|
6873
6887
|
async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
|
|
6874
6888
|
const reviewSummary = await this.executeDependencyAwareChecks(
|
|
@@ -6881,7 +6895,17 @@ ${expr}
|
|
|
6881
6895
|
maxParallelism,
|
|
6882
6896
|
failFast
|
|
6883
6897
|
);
|
|
6884
|
-
|
|
6898
|
+
const executionStatistics = this.buildExecutionStatistics();
|
|
6899
|
+
const groupedResults = await this.convertReviewSummaryToGroupedResults(
|
|
6900
|
+
reviewSummary,
|
|
6901
|
+
checks,
|
|
6902
|
+
config,
|
|
6903
|
+
prInfo
|
|
6904
|
+
);
|
|
6905
|
+
return {
|
|
6906
|
+
results: groupedResults,
|
|
6907
|
+
statistics: executionStatistics
|
|
6908
|
+
};
|
|
6885
6909
|
}
|
|
6886
6910
|
/**
|
|
6887
6911
|
* Convert ReviewSummary to GroupedCheckResults
|
|
@@ -7144,9 +7168,9 @@ ${expr}
|
|
|
7144
7168
|
let shouldStopExecution = false;
|
|
7145
7169
|
let completedChecksCount = 0;
|
|
7146
7170
|
const totalChecksCount = stats.totalChecks;
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7171
|
+
for (const checkName of checks) {
|
|
7172
|
+
this.initializeCheckStats(checkName);
|
|
7173
|
+
}
|
|
7150
7174
|
for (let levelIndex = 0; levelIndex < dependencyGraph.executionOrder.length && !shouldStopExecution; levelIndex++) {
|
|
7151
7175
|
const executionGroup = dependencyGraph.executionOrder[levelIndex];
|
|
7152
7176
|
const checksInLevel = executionGroup.parallel;
|
|
@@ -7276,11 +7300,13 @@ ${expr}
|
|
|
7276
7300
|
}
|
|
7277
7301
|
let finalResult;
|
|
7278
7302
|
if (isForEachDependent && forEachParentName) {
|
|
7303
|
+
this.recordForEachPreview(checkName, forEachItems);
|
|
7279
7304
|
if (debug) {
|
|
7280
7305
|
log2(
|
|
7281
7306
|
`\u{1F504} Debug: Check "${checkName}" depends on forEach check "${forEachParentName}", executing ${forEachItems.length} times`
|
|
7282
7307
|
);
|
|
7283
7308
|
}
|
|
7309
|
+
logger.info(` Processing ${forEachItems.length} items...`);
|
|
7284
7310
|
const allIssues = [];
|
|
7285
7311
|
const allOutputs = [];
|
|
7286
7312
|
const aggregatedContents = [];
|
|
@@ -7337,6 +7363,7 @@ ${expr}
|
|
|
7337
7363
|
`\u{1F504} Debug: Executing check "${checkName}" for item ${itemIndex + 1}/${forEachItems.length}`
|
|
7338
7364
|
);
|
|
7339
7365
|
}
|
|
7366
|
+
const iterationStart = this.recordIterationStart(checkName);
|
|
7340
7367
|
const itemResult = await this.executeWithRouting(
|
|
7341
7368
|
checkName,
|
|
7342
7369
|
checkConfig,
|
|
@@ -7356,6 +7383,17 @@ ${expr}
|
|
|
7356
7383
|
parent: forEachParentName
|
|
7357
7384
|
}
|
|
7358
7385
|
);
|
|
7386
|
+
const iterationDuration = (Date.now() - iterationStart) / 1e3;
|
|
7387
|
+
this.recordIterationComplete(
|
|
7388
|
+
checkName,
|
|
7389
|
+
iterationStart,
|
|
7390
|
+
true,
|
|
7391
|
+
itemResult.issues || [],
|
|
7392
|
+
itemResult.output
|
|
7393
|
+
);
|
|
7394
|
+
logger.info(
|
|
7395
|
+
` \u2714 ${itemIndex + 1}/${forEachItems.length} (${iterationDuration.toFixed(1)}s)`
|
|
7396
|
+
);
|
|
7359
7397
|
return { index: itemIndex, itemResult };
|
|
7360
7398
|
});
|
|
7361
7399
|
const forEachConcurrency = Math.max(
|
|
@@ -7417,8 +7455,8 @@ ${expr}
|
|
|
7417
7455
|
debug
|
|
7418
7456
|
);
|
|
7419
7457
|
if (!shouldRun) {
|
|
7420
|
-
|
|
7421
|
-
logger.info(`\u23ED
|
|
7458
|
+
this.recordSkip(checkName, "if_condition", checkConfig.if);
|
|
7459
|
+
logger.info(`\u23ED Skipped (if: ${this.truncate(checkConfig.if, 40)})`);
|
|
7422
7460
|
return {
|
|
7423
7461
|
checkName,
|
|
7424
7462
|
error: null,
|
|
@@ -7442,6 +7480,13 @@ ${expr}
|
|
|
7442
7480
|
debug,
|
|
7443
7481
|
results
|
|
7444
7482
|
);
|
|
7483
|
+
this.recordIterationComplete(
|
|
7484
|
+
checkName,
|
|
7485
|
+
checkStartTime,
|
|
7486
|
+
true,
|
|
7487
|
+
finalResult.issues || [],
|
|
7488
|
+
finalResult.output
|
|
7489
|
+
);
|
|
7445
7490
|
if (checkConfig.forEach) {
|
|
7446
7491
|
try {
|
|
7447
7492
|
const finalResultWithOutput = finalResult;
|
|
@@ -7471,7 +7516,22 @@ ${expr}
|
|
|
7471
7516
|
};
|
|
7472
7517
|
const checkDuration = ((Date.now() - checkStartTime) / 1e3).toFixed(1);
|
|
7473
7518
|
const issueCount = enrichedIssues.length;
|
|
7474
|
-
|
|
7519
|
+
const checkStats = this.executionStats.get(checkName);
|
|
7520
|
+
if (checkStats && checkStats.totalRuns > 1) {
|
|
7521
|
+
if (issueCount > 0) {
|
|
7522
|
+
logger.success(
|
|
7523
|
+
`Check complete: ${checkName} (${checkDuration}s) - ${checkStats.totalRuns} runs, ${issueCount} issue${issueCount === 1 ? "" : "s"}`
|
|
7524
|
+
);
|
|
7525
|
+
} else {
|
|
7526
|
+
logger.success(
|
|
7527
|
+
`Check complete: ${checkName} (${checkDuration}s) - ${checkStats.totalRuns} runs`
|
|
7528
|
+
);
|
|
7529
|
+
}
|
|
7530
|
+
} else if (checkStats && checkStats.outputsProduced && checkStats.outputsProduced > 0) {
|
|
7531
|
+
logger.success(
|
|
7532
|
+
`Check complete: ${checkName} (${checkDuration}s) - ${checkStats.outputsProduced} items`
|
|
7533
|
+
);
|
|
7534
|
+
} else if (issueCount > 0) {
|
|
7475
7535
|
logger.success(
|
|
7476
7536
|
`Check complete: ${checkName} (${checkDuration}s) - ${issueCount} issue${issueCount === 1 ? "" : "s"} found`
|
|
7477
7537
|
);
|
|
@@ -7484,9 +7544,10 @@ ${expr}
|
|
|
7484
7544
|
result: enrichedResult
|
|
7485
7545
|
};
|
|
7486
7546
|
} catch (error) {
|
|
7487
|
-
failedChecksCount++;
|
|
7488
7547
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7489
7548
|
const checkDuration = ((Date.now() - checkStartTime) / 1e3).toFixed(1);
|
|
7549
|
+
this.recordError(checkName, error instanceof Error ? error : new Error(String(error)));
|
|
7550
|
+
this.recordIterationComplete(checkName, checkStartTime, false, [], void 0);
|
|
7490
7551
|
logger.error(`\u2716 Check failed: ${checkName} (${checkDuration}s) - ${errorMessage}`);
|
|
7491
7552
|
if (debug) {
|
|
7492
7553
|
log2(`\u{1F527} Debug: Error in check ${checkName}: ${errorMessage}`);
|
|
@@ -7594,19 +7655,13 @@ ${expr}
|
|
|
7594
7655
|
}
|
|
7595
7656
|
}
|
|
7596
7657
|
}
|
|
7597
|
-
const
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
logger.step(`Execution complete (${executionDuration}s)`);
|
|
7601
|
-
logger.info(` \u2714 Successful: ${successfulChecks}/${totalChecksCount}`);
|
|
7602
|
-
if (skippedChecksCount > 0) {
|
|
7603
|
-
logger.info(` \u23ED Skipped: ${skippedChecksCount}`);
|
|
7604
|
-
}
|
|
7605
|
-
if (failedChecksCount > 0) {
|
|
7606
|
-
logger.info(` \u2716 Failed: ${failedChecksCount}`);
|
|
7658
|
+
const executionStatistics = this.buildExecutionStatistics();
|
|
7659
|
+
if (logFn === console.log) {
|
|
7660
|
+
this.logExecutionSummary(executionStatistics);
|
|
7607
7661
|
}
|
|
7608
7662
|
if (shouldStopExecution) {
|
|
7609
|
-
logger.
|
|
7663
|
+
logger.info("");
|
|
7664
|
+
logger.warn(`\u26A0\uFE0F Execution stopped early due to fail-fast`);
|
|
7610
7665
|
}
|
|
7611
7666
|
if (debug) {
|
|
7612
7667
|
if (shouldStopExecution) {
|
|
@@ -8561,6 +8616,220 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
8561
8616
|
}
|
|
8562
8617
|
return "pr_updated";
|
|
8563
8618
|
}
|
|
8619
|
+
/**
|
|
8620
|
+
* Initialize execution statistics for a check
|
|
8621
|
+
*/
|
|
8622
|
+
initializeCheckStats(checkName) {
|
|
8623
|
+
this.executionStats.set(checkName, {
|
|
8624
|
+
checkName,
|
|
8625
|
+
totalRuns: 0,
|
|
8626
|
+
successfulRuns: 0,
|
|
8627
|
+
failedRuns: 0,
|
|
8628
|
+
skipped: false,
|
|
8629
|
+
totalDuration: 0,
|
|
8630
|
+
issuesFound: 0,
|
|
8631
|
+
issuesBySeverity: {
|
|
8632
|
+
critical: 0,
|
|
8633
|
+
error: 0,
|
|
8634
|
+
warning: 0,
|
|
8635
|
+
info: 0
|
|
8636
|
+
},
|
|
8637
|
+
perIterationDuration: []
|
|
8638
|
+
});
|
|
8639
|
+
}
|
|
8640
|
+
/**
|
|
8641
|
+
* Record the start of a check iteration
|
|
8642
|
+
* Returns the start timestamp for duration tracking
|
|
8643
|
+
*/
|
|
8644
|
+
recordIterationStart(_checkName) {
|
|
8645
|
+
return Date.now();
|
|
8646
|
+
}
|
|
8647
|
+
/**
|
|
8648
|
+
* Record completion of a check iteration
|
|
8649
|
+
*/
|
|
8650
|
+
recordIterationComplete(checkName, startTime, success, issues, output) {
|
|
8651
|
+
const stats = this.executionStats.get(checkName);
|
|
8652
|
+
if (!stats) return;
|
|
8653
|
+
const duration = Date.now() - startTime;
|
|
8654
|
+
stats.totalRuns++;
|
|
8655
|
+
if (success) {
|
|
8656
|
+
stats.successfulRuns++;
|
|
8657
|
+
} else {
|
|
8658
|
+
stats.failedRuns++;
|
|
8659
|
+
}
|
|
8660
|
+
stats.totalDuration += duration;
|
|
8661
|
+
stats.perIterationDuration.push(duration);
|
|
8662
|
+
for (const issue of issues) {
|
|
8663
|
+
stats.issuesFound++;
|
|
8664
|
+
if (issue.severity === "critical") stats.issuesBySeverity.critical++;
|
|
8665
|
+
else if (issue.severity === "error") stats.issuesBySeverity.error++;
|
|
8666
|
+
else if (issue.severity === "warning") stats.issuesBySeverity.warning++;
|
|
8667
|
+
else if (issue.severity === "info") stats.issuesBySeverity.info++;
|
|
8668
|
+
}
|
|
8669
|
+
if (output !== void 0) {
|
|
8670
|
+
stats.outputsProduced = (stats.outputsProduced || 0) + 1;
|
|
8671
|
+
}
|
|
8672
|
+
}
|
|
8673
|
+
/**
|
|
8674
|
+
* Record that a check was skipped
|
|
8675
|
+
*/
|
|
8676
|
+
recordSkip(checkName, reason, condition) {
|
|
8677
|
+
const stats = this.executionStats.get(checkName);
|
|
8678
|
+
if (!stats) return;
|
|
8679
|
+
stats.skipped = true;
|
|
8680
|
+
stats.skipReason = reason;
|
|
8681
|
+
if (condition) {
|
|
8682
|
+
stats.skipCondition = condition;
|
|
8683
|
+
}
|
|
8684
|
+
}
|
|
8685
|
+
/**
|
|
8686
|
+
* Record forEach preview items
|
|
8687
|
+
*/
|
|
8688
|
+
recordForEachPreview(checkName, items) {
|
|
8689
|
+
const stats = this.executionStats.get(checkName);
|
|
8690
|
+
if (!stats || !items.length) return;
|
|
8691
|
+
const preview = items.slice(0, 3).map((item) => {
|
|
8692
|
+
const str = typeof item === "string" ? item : JSON.stringify(item);
|
|
8693
|
+
return str.length > 50 ? str.substring(0, 47) + "..." : str;
|
|
8694
|
+
});
|
|
8695
|
+
if (items.length > 3) {
|
|
8696
|
+
preview.push(`...${items.length - 3} more`);
|
|
8697
|
+
}
|
|
8698
|
+
stats.forEachPreview = preview;
|
|
8699
|
+
}
|
|
8700
|
+
/**
|
|
8701
|
+
* Record an error for a check
|
|
8702
|
+
*/
|
|
8703
|
+
recordError(checkName, error) {
|
|
8704
|
+
const stats = this.executionStats.get(checkName);
|
|
8705
|
+
if (!stats) return;
|
|
8706
|
+
stats.errorMessage = error instanceof Error ? error.message : String(error);
|
|
8707
|
+
}
|
|
8708
|
+
/**
|
|
8709
|
+
* Build the final execution statistics object
|
|
8710
|
+
*/
|
|
8711
|
+
buildExecutionStatistics() {
|
|
8712
|
+
const checks = Array.from(this.executionStats.values());
|
|
8713
|
+
const totalExecutions = checks.reduce((sum, s) => sum + s.totalRuns, 0);
|
|
8714
|
+
const successfulExecutions = checks.reduce((sum, s) => sum + s.successfulRuns, 0);
|
|
8715
|
+
const failedExecutions = checks.reduce((sum, s) => sum + s.failedRuns, 0);
|
|
8716
|
+
const skippedChecks = checks.filter((s) => s.skipped).length;
|
|
8717
|
+
const totalDuration = checks.reduce((sum, s) => sum + s.totalDuration, 0);
|
|
8718
|
+
return {
|
|
8719
|
+
totalChecksConfigured: checks.length,
|
|
8720
|
+
totalExecutions,
|
|
8721
|
+
successfulExecutions,
|
|
8722
|
+
failedExecutions,
|
|
8723
|
+
skippedChecks,
|
|
8724
|
+
totalDuration,
|
|
8725
|
+
checks
|
|
8726
|
+
};
|
|
8727
|
+
}
|
|
8728
|
+
/**
|
|
8729
|
+
* Truncate a string to max length with ellipsis
|
|
8730
|
+
*/
|
|
8731
|
+
truncate(str, maxLen) {
|
|
8732
|
+
if (str.length <= maxLen) return str;
|
|
8733
|
+
return str.substring(0, maxLen - 3) + "...";
|
|
8734
|
+
}
|
|
8735
|
+
/**
|
|
8736
|
+
* Format the Status column for execution summary table
|
|
8737
|
+
*/
|
|
8738
|
+
formatStatusColumn(stats) {
|
|
8739
|
+
if (stats.skipped) {
|
|
8740
|
+
if (stats.skipReason === "if_condition") return "\u23ED if";
|
|
8741
|
+
if (stats.skipReason === "fail_fast") return "\u23ED ff";
|
|
8742
|
+
if (stats.skipReason === "dependency_failed") return "\u23ED dep";
|
|
8743
|
+
return "\u23ED";
|
|
8744
|
+
}
|
|
8745
|
+
if (stats.totalRuns === 0) return "-";
|
|
8746
|
+
const symbol = stats.failedRuns === 0 ? "\u2714" : stats.successfulRuns === 0 ? "\u2716" : "\u2714/\u2716";
|
|
8747
|
+
if (stats.totalRuns > 1) {
|
|
8748
|
+
if (stats.failedRuns > 0 && stats.successfulRuns > 0) {
|
|
8749
|
+
return `${symbol} ${stats.successfulRuns}/${stats.totalRuns}`;
|
|
8750
|
+
} else {
|
|
8751
|
+
return `${symbol} \xD7${stats.totalRuns}`;
|
|
8752
|
+
}
|
|
8753
|
+
}
|
|
8754
|
+
return symbol;
|
|
8755
|
+
}
|
|
8756
|
+
/**
|
|
8757
|
+
* Format the Details column for execution summary table
|
|
8758
|
+
*/
|
|
8759
|
+
formatDetailsColumn(stats) {
|
|
8760
|
+
const parts = [];
|
|
8761
|
+
if (stats.outputsProduced && stats.outputsProduced > 0) {
|
|
8762
|
+
parts.push(`\u2192${stats.outputsProduced}`);
|
|
8763
|
+
}
|
|
8764
|
+
if (stats.issuesBySeverity.critical > 0) {
|
|
8765
|
+
parts.push(`${stats.issuesBySeverity.critical}\u{1F534}`);
|
|
8766
|
+
}
|
|
8767
|
+
if (stats.issuesBySeverity.warning > 0) {
|
|
8768
|
+
parts.push(`${stats.issuesBySeverity.warning}\u26A0\uFE0F`);
|
|
8769
|
+
}
|
|
8770
|
+
if (stats.issuesBySeverity.info > 0 && stats.issuesBySeverity.critical === 0 && stats.issuesBySeverity.warning === 0) {
|
|
8771
|
+
parts.push(`${stats.issuesBySeverity.info}\u{1F4A1}`);
|
|
8772
|
+
}
|
|
8773
|
+
if (stats.errorMessage) {
|
|
8774
|
+
parts.push(this.truncate(stats.errorMessage, 20));
|
|
8775
|
+
} else if (stats.skipCondition) {
|
|
8776
|
+
parts.push(this.truncate(stats.skipCondition, 20));
|
|
8777
|
+
}
|
|
8778
|
+
return parts.join(" ");
|
|
8779
|
+
}
|
|
8780
|
+
/**
|
|
8781
|
+
* Log the execution summary table
|
|
8782
|
+
*/
|
|
8783
|
+
logExecutionSummary(stats) {
|
|
8784
|
+
const totalIssues = stats.checks.reduce((sum, s) => sum + s.issuesFound, 0);
|
|
8785
|
+
const criticalIssues = stats.checks.reduce((sum, s) => sum + s.issuesBySeverity.critical, 0);
|
|
8786
|
+
const warningIssues = stats.checks.reduce((sum, s) => sum + s.issuesBySeverity.warning, 0);
|
|
8787
|
+
const durationSec = (stats.totalDuration / 1e3).toFixed(1);
|
|
8788
|
+
const summaryTable = new (require("cli-table3"))({
|
|
8789
|
+
style: {
|
|
8790
|
+
head: [],
|
|
8791
|
+
border: []
|
|
8792
|
+
},
|
|
8793
|
+
colWidths: [41]
|
|
8794
|
+
});
|
|
8795
|
+
summaryTable.push(
|
|
8796
|
+
[`Execution Complete (${durationSec}s)`],
|
|
8797
|
+
[`Checks: ${stats.totalChecksConfigured} configured \u2192 ${stats.totalExecutions} executions`],
|
|
8798
|
+
[
|
|
8799
|
+
`Status: ${stats.successfulExecutions} \u2714 \u2502 ${stats.failedExecutions} \u2716 \u2502 ${stats.skippedChecks} \u23ED`
|
|
8800
|
+
]
|
|
8801
|
+
);
|
|
8802
|
+
if (totalIssues > 0) {
|
|
8803
|
+
let issuesLine = `Issues: ${totalIssues} total`;
|
|
8804
|
+
if (criticalIssues > 0) issuesLine += ` (${criticalIssues} \u{1F534}`;
|
|
8805
|
+
if (warningIssues > 0) issuesLine += `${criticalIssues > 0 ? " " : " ("}${warningIssues} \u26A0\uFE0F)`;
|
|
8806
|
+
else if (criticalIssues > 0) issuesLine += ")";
|
|
8807
|
+
summaryTable.push([issuesLine]);
|
|
8808
|
+
}
|
|
8809
|
+
logger.info("");
|
|
8810
|
+
logger.info(summaryTable.toString());
|
|
8811
|
+
logger.info("");
|
|
8812
|
+
logger.info("Check Details:");
|
|
8813
|
+
const detailsTable = new (require("cli-table3"))({
|
|
8814
|
+
head: ["Check", "Duration", "Status", "Details"],
|
|
8815
|
+
colWidths: [21, 10, 10, 21],
|
|
8816
|
+
style: {
|
|
8817
|
+
head: ["cyan"],
|
|
8818
|
+
border: ["grey"]
|
|
8819
|
+
}
|
|
8820
|
+
});
|
|
8821
|
+
for (const checkStats of stats.checks) {
|
|
8822
|
+
const duration = checkStats.skipped ? "-" : `${(checkStats.totalDuration / 1e3).toFixed(1)}s`;
|
|
8823
|
+
const status = this.formatStatusColumn(checkStats);
|
|
8824
|
+
const details = this.formatDetailsColumn(checkStats);
|
|
8825
|
+
detailsTable.push([checkStats.checkName, duration, status, details]);
|
|
8826
|
+
}
|
|
8827
|
+
logger.info(detailsTable.toString());
|
|
8828
|
+
logger.info("");
|
|
8829
|
+
logger.info(
|
|
8830
|
+
"Legend: \u2714=success \u2502 \u2716=failed \u2502 \u23ED=skipped \u2502 \xD7N=iterations \u2502 \u2192N=outputs \u2502 N\u{1F534}=critical \u2502 N\u26A0\uFE0F=warnings"
|
|
8831
|
+
);
|
|
8832
|
+
}
|
|
8564
8833
|
};
|
|
8565
8834
|
}
|
|
8566
8835
|
});
|