@probelabs/visor 0.1.72 → 0.1.74

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/sdk/sdk.js CHANGED
@@ -1540,7 +1540,7 @@ var init_reviewer = __esm({
1540
1540
  if (config && checks && checks.length > 0) {
1541
1541
  const { CheckExecutionEngine: CheckExecutionEngine2 } = await Promise.resolve().then(() => (init_check_execution_engine(), check_execution_engine_exports));
1542
1542
  const engine = new CheckExecutionEngine2();
1543
- const groupedResults = await engine.executeGroupedChecks(
1543
+ const { results } = await engine.executeGroupedChecks(
1544
1544
  prInfo,
1545
1545
  checks,
1546
1546
  void 0,
@@ -1548,7 +1548,7 @@ var init_reviewer = __esm({
1548
1548
  void 0,
1549
1549
  debug
1550
1550
  );
1551
- return groupedResults;
1551
+ return results;
1552
1552
  }
1553
1553
  throw new Error(
1554
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."
@@ -6016,6 +6016,7 @@ var init_check_execution_engine = __esm({
6016
6016
  config;
6017
6017
  webhookContext;
6018
6018
  routingSandbox;
6019
+ executionStats = /* @__PURE__ */ new Map();
6019
6020
  constructor(workingDirectory) {
6020
6021
  this.workingDirectory = workingDirectory || process.cwd();
6021
6022
  this.gitAnalyzer = new GitRepositoryAnalyzer(this.workingDirectory);
@@ -6554,12 +6555,14 @@ ${expr}
6554
6555
  apiCallDetails: reviewSummary.debug.apiCallDetails
6555
6556
  };
6556
6557
  }
6558
+ const executionStatistics = this.buildExecutionStatistics();
6557
6559
  return {
6558
6560
  repositoryInfo,
6559
6561
  reviewSummary,
6560
6562
  executionTime,
6561
6563
  timestamp,
6562
6564
  checksExecuted: filteredChecks,
6565
+ executionStatistics,
6563
6566
  debug: debugInfo
6564
6567
  };
6565
6568
  } catch (error) {
@@ -6752,7 +6755,7 @@ ${expr}
6752
6755
  });
6753
6756
  }
6754
6757
  /**
6755
- * Execute review checks and return grouped results for new architecture
6758
+ * Execute review checks and return grouped results with statistics for new architecture
6756
6759
  */
6757
6760
  async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter) {
6758
6761
  const logFn = outputFormat === "json" || outputFormat === "sarif" ? debug ? console.error : () => {
@@ -6782,7 +6785,10 @@ ${expr}
6782
6785
  checks = tagFilteredChecks;
6783
6786
  if (checks.length === 0) {
6784
6787
  logger.warn("\u26A0\uFE0F No checks remain after tag filtering");
6785
- return {};
6788
+ return {
6789
+ results: {},
6790
+ statistics: this.buildExecutionStatistics()
6791
+ };
6786
6792
  }
6787
6793
  if (!config?.checks) {
6788
6794
  throw new Error("Config with check definitions required for grouped execution");
@@ -6822,9 +6828,15 @@ ${expr}
6822
6828
  );
6823
6829
  const groupedResults = {};
6824
6830
  groupedResults[checkResult.group] = [checkResult];
6825
- return groupedResults;
6831
+ return {
6832
+ results: groupedResults,
6833
+ statistics: this.buildExecutionStatistics()
6834
+ };
6826
6835
  }
6827
- return {};
6836
+ return {
6837
+ results: {},
6838
+ statistics: this.buildExecutionStatistics()
6839
+ };
6828
6840
  }
6829
6841
  /**
6830
6842
  * Execute single check and return grouped result
@@ -6870,7 +6882,7 @@ ${expr}
6870
6882
  };
6871
6883
  }
6872
6884
  /**
6873
- * Execute multiple checks with dependency awareness - return grouped results
6885
+ * Execute multiple checks with dependency awareness - return grouped results with statistics
6874
6886
  */
6875
6887
  async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
6876
6888
  const reviewSummary = await this.executeDependencyAwareChecks(
@@ -6883,7 +6895,17 @@ ${expr}
6883
6895
  maxParallelism,
6884
6896
  failFast
6885
6897
  );
6886
- return await this.convertReviewSummaryToGroupedResults(reviewSummary, checks, config, prInfo);
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
+ };
6887
6909
  }
6888
6910
  /**
6889
6911
  * Convert ReviewSummary to GroupedCheckResults
@@ -7146,9 +7168,9 @@ ${expr}
7146
7168
  let shouldStopExecution = false;
7147
7169
  let completedChecksCount = 0;
7148
7170
  const totalChecksCount = stats.totalChecks;
7149
- let skippedChecksCount = 0;
7150
- let failedChecksCount = 0;
7151
- const executionStartTime = Date.now();
7171
+ for (const checkName of checks) {
7172
+ this.initializeCheckStats(checkName);
7173
+ }
7152
7174
  for (let levelIndex = 0; levelIndex < dependencyGraph.executionOrder.length && !shouldStopExecution; levelIndex++) {
7153
7175
  const executionGroup = dependencyGraph.executionOrder[levelIndex];
7154
7176
  const checksInLevel = executionGroup.parallel;
@@ -7278,11 +7300,13 @@ ${expr}
7278
7300
  }
7279
7301
  let finalResult;
7280
7302
  if (isForEachDependent && forEachParentName) {
7303
+ this.recordForEachPreview(checkName, forEachItems);
7281
7304
  if (debug) {
7282
7305
  log2(
7283
7306
  `\u{1F504} Debug: Check "${checkName}" depends on forEach check "${forEachParentName}", executing ${forEachItems.length} times`
7284
7307
  );
7285
7308
  }
7309
+ logger.info(` Processing ${forEachItems.length} items...`);
7286
7310
  const allIssues = [];
7287
7311
  const allOutputs = [];
7288
7312
  const aggregatedContents = [];
@@ -7339,6 +7363,7 @@ ${expr}
7339
7363
  `\u{1F504} Debug: Executing check "${checkName}" for item ${itemIndex + 1}/${forEachItems.length}`
7340
7364
  );
7341
7365
  }
7366
+ const iterationStart = this.recordIterationStart(checkName);
7342
7367
  const itemResult = await this.executeWithRouting(
7343
7368
  checkName,
7344
7369
  checkConfig,
@@ -7358,6 +7383,17 @@ ${expr}
7358
7383
  parent: forEachParentName
7359
7384
  }
7360
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
+ );
7361
7397
  return { index: itemIndex, itemResult };
7362
7398
  });
7363
7399
  const forEachConcurrency = Math.max(
@@ -7419,8 +7455,8 @@ ${expr}
7419
7455
  debug
7420
7456
  );
7421
7457
  if (!shouldRun) {
7422
- skippedChecksCount++;
7423
- logger.info(`\u23ED Skipping check: ${checkName} (if condition evaluated to false)`);
7458
+ this.recordSkip(checkName, "if_condition", checkConfig.if);
7459
+ logger.info(`\u23ED Skipped (if: ${this.truncate(checkConfig.if, 40)})`);
7424
7460
  return {
7425
7461
  checkName,
7426
7462
  error: null,
@@ -7444,6 +7480,13 @@ ${expr}
7444
7480
  debug,
7445
7481
  results
7446
7482
  );
7483
+ this.recordIterationComplete(
7484
+ checkName,
7485
+ checkStartTime,
7486
+ true,
7487
+ finalResult.issues || [],
7488
+ finalResult.output
7489
+ );
7447
7490
  if (checkConfig.forEach) {
7448
7491
  try {
7449
7492
  const finalResultWithOutput = finalResult;
@@ -7473,7 +7516,22 @@ ${expr}
7473
7516
  };
7474
7517
  const checkDuration = ((Date.now() - checkStartTime) / 1e3).toFixed(1);
7475
7518
  const issueCount = enrichedIssues.length;
7476
- if (issueCount > 0) {
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) {
7477
7535
  logger.success(
7478
7536
  `Check complete: ${checkName} (${checkDuration}s) - ${issueCount} issue${issueCount === 1 ? "" : "s"} found`
7479
7537
  );
@@ -7486,9 +7544,10 @@ ${expr}
7486
7544
  result: enrichedResult
7487
7545
  };
7488
7546
  } catch (error) {
7489
- failedChecksCount++;
7490
7547
  const errorMessage = error instanceof Error ? error.message : String(error);
7491
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);
7492
7551
  logger.error(`\u2716 Check failed: ${checkName} (${checkDuration}s) - ${errorMessage}`);
7493
7552
  if (debug) {
7494
7553
  log2(`\u{1F527} Debug: Error in check ${checkName}: ${errorMessage}`);
@@ -7596,19 +7655,13 @@ ${expr}
7596
7655
  }
7597
7656
  }
7598
7657
  }
7599
- const executionDuration = ((Date.now() - executionStartTime) / 1e3).toFixed(1);
7600
- const successfulChecks = totalChecksCount - failedChecksCount - skippedChecksCount;
7601
- logger.info("");
7602
- logger.step(`Execution complete (${executionDuration}s)`);
7603
- logger.info(` \u2714 Successful: ${successfulChecks}/${totalChecksCount}`);
7604
- if (skippedChecksCount > 0) {
7605
- logger.info(` \u23ED Skipped: ${skippedChecksCount}`);
7606
- }
7607
- if (failedChecksCount > 0) {
7608
- logger.info(` \u2716 Failed: ${failedChecksCount}`);
7658
+ const executionStatistics = this.buildExecutionStatistics();
7659
+ if (logFn === console.log) {
7660
+ this.logExecutionSummary(executionStatistics);
7609
7661
  }
7610
7662
  if (shouldStopExecution) {
7611
- logger.warn(` \u26A0\uFE0F Execution stopped early due to fail-fast`);
7663
+ logger.info("");
7664
+ logger.warn(`\u26A0\uFE0F Execution stopped early due to fail-fast`);
7612
7665
  }
7613
7666
  if (debug) {
7614
7667
  if (shouldStopExecution) {
@@ -8563,6 +8616,220 @@ ${result.value.result.debug.rawResponse}`;
8563
8616
  }
8564
8617
  return "pr_updated";
8565
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
+ }
8566
8833
  };
8567
8834
  }
8568
8835
  });