@probelabs/visor 0.1.37 → 0.1.39

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.
Files changed (42) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +228 -1981
  3. package/action.yml +10 -0
  4. package/defaults/.visor.yaml +1 -1
  5. package/dist/action-cli-bridge.d.ts +2 -0
  6. package/dist/action-cli-bridge.d.ts.map +1 -1
  7. package/dist/ai-review-service.d.ts +4 -0
  8. package/dist/ai-review-service.d.ts.map +1 -1
  9. package/dist/check-execution-engine.d.ts +6 -1
  10. package/dist/check-execution-engine.d.ts.map +1 -1
  11. package/dist/cli-main.d.ts.map +1 -1
  12. package/dist/cli.d.ts.map +1 -1
  13. package/dist/config.d.ts +4 -0
  14. package/dist/config.d.ts.map +1 -1
  15. package/dist/defaults/.visor.yaml +1 -1
  16. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +569 -163
  19. package/dist/output-formatters.d.ts.map +1 -1
  20. package/dist/providers/ai-check-provider.d.ts +8 -0
  21. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  22. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  23. package/dist/providers/check-provider.interface.d.ts +1 -7
  24. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  25. package/dist/providers/claude-code-check-provider.d.ts.map +1 -1
  26. package/dist/providers/http-check-provider.d.ts.map +1 -1
  27. package/dist/providers/http-client-provider.d.ts.map +1 -1
  28. package/dist/providers/http-input-provider.d.ts.map +1 -1
  29. package/dist/providers/index.d.ts +1 -0
  30. package/dist/providers/index.d.ts.map +1 -1
  31. package/dist/providers/log-check-provider.d.ts +29 -0
  32. package/dist/providers/log-check-provider.d.ts.map +1 -0
  33. package/dist/providers/noop-check-provider.d.ts.map +1 -1
  34. package/dist/providers/tool-check-provider.d.ts +0 -1
  35. package/dist/providers/tool-check-provider.d.ts.map +1 -1
  36. package/dist/reviewer.d.ts +0 -1
  37. package/dist/reviewer.d.ts.map +1 -1
  38. package/dist/types/cli.d.ts +4 -0
  39. package/dist/types/cli.d.ts.map +1 -1
  40. package/dist/types/config.d.ts +22 -1
  41. package/dist/types/config.d.ts.map +1 -1
  42. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -93923,9 +93923,6 @@ class AIReviewService {
93923
93923
  category: 'logic',
93924
93924
  },
93925
93925
  ],
93926
- suggestions: [
93927
- 'Configure API keys in your GitHub repository secrets or environment variables',
93928
- ],
93929
93926
  debug: debugInfo,
93930
93927
  };
93931
93928
  }
@@ -93962,7 +93959,6 @@ class AIReviewService {
93962
93959
  category: 'logic',
93963
93960
  },
93964
93961
  ],
93965
- suggestions: ['Check AI service configuration and API key validity'],
93966
93962
  debug: debugInfo,
93967
93963
  };
93968
93964
  }
@@ -94034,9 +94030,6 @@ class AIReviewService {
94034
94030
  category: 'logic',
94035
94031
  },
94036
94032
  ],
94037
- suggestions: [
94038
- 'Check session reuse configuration and ensure parent check completed successfully',
94039
- ],
94040
94033
  debug: debugInfo,
94041
94034
  };
94042
94035
  }
@@ -94577,23 +94570,18 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
94577
94570
  // Handle plain schema or no schema - no JSON parsing, return response as-is
94578
94571
  if (_schema === 'plain' || !_schema) {
94579
94572
  log(`📋 ${_schema === 'plain' ? 'Plain' : 'No'} schema detected - returning raw response without JSON parsing`);
94580
- // Strip common XML wrapper tags that AI might add when given XML-formatted prompts
94581
- let cleanedResponse = response.trim();
94582
- // Remove <result>...</result> tags if present
94583
- cleanedResponse = cleanedResponse
94584
- .replace(/^<result>\s*/i, '')
94585
- .replace(/\s*<\/result>$/i, '');
94586
- // Remove <response>...</response> tags if present
94587
- cleanedResponse = cleanedResponse
94588
- .replace(/^<response>\s*/i, '')
94589
- .replace(/\s*<\/response>$/i, '');
94590
- // Remove <answer>...</answer> tags if present
94591
- cleanedResponse = cleanedResponse
94592
- .replace(/^<answer>\s*/i, '')
94593
- .replace(/\s*<\/answer>$/i, '');
94573
+ // For plain schema, return the raw response as an issue
94594
94574
  return {
94595
- issues: [],
94596
- suggestions: [cleanedResponse.trim()],
94575
+ issues: [
94576
+ {
94577
+ file: 'AI_RESPONSE',
94578
+ line: 1,
94579
+ ruleId: 'ai/raw_response',
94580
+ message: response,
94581
+ severity: 'info',
94582
+ category: 'documentation',
94583
+ },
94584
+ ],
94597
94585
  debug: debugInfo,
94598
94586
  };
94599
94587
  }
@@ -94615,9 +94603,6 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
94615
94603
  console.error('đŸšĢ AI refused to analyze - returning empty result');
94616
94604
  return {
94617
94605
  issues: [],
94618
- suggestions: [
94619
- 'AI was unable to analyze this code. Please check the content or try again.',
94620
- ],
94621
94606
  };
94622
94607
  }
94623
94608
  // Try to extract JSON using improved method with proper bracket matching
@@ -94634,24 +94619,33 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
94634
94619
  // Check if response is plain text and doesn't contain structured data
94635
94620
  if (!response.includes('{') && !response.includes('}')) {
94636
94621
  log('🔧 Plain text response detected, creating structured fallback...');
94637
- const isNoChanges = response.toLowerCase().includes('no') &&
94638
- (response.toLowerCase().includes('changes') ||
94639
- response.toLowerCase().includes('code'));
94640
94622
  reviewData = {
94641
- issues: [],
94642
- suggestions: isNoChanges
94643
- ? ['No code changes detected in this analysis']
94644
- : [
94645
- `AI response: ${response.substring(0, 200)}${response.length > 200 ? '...' : ''}`,
94646
- ],
94623
+ issues: [
94624
+ {
94625
+ file: 'AI_RESPONSE',
94626
+ line: 1,
94627
+ ruleId: 'ai/raw_response',
94628
+ message: response,
94629
+ severity: 'info',
94630
+ category: 'documentation',
94631
+ },
94632
+ ],
94647
94633
  };
94648
94634
  }
94649
94635
  else {
94650
- // Fallback: treat the entire response as a suggestion
94636
+ // Fallback: treat the entire response as an issue
94651
94637
  log('🔧 Creating fallback response from non-JSON content...');
94652
94638
  reviewData = {
94653
- issues: [],
94654
- suggestions: [response.trim()],
94639
+ issues: [
94640
+ {
94641
+ file: 'AI_RESPONSE',
94642
+ line: 1,
94643
+ ruleId: 'ai/raw_response',
94644
+ message: response,
94645
+ severity: 'info',
94646
+ category: 'documentation',
94647
+ },
94648
+ ],
94655
94649
  };
94656
94650
  }
94657
94651
  }
@@ -94660,8 +94654,16 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
94660
94654
  // No JSON found at all - treat as plain text response
94661
94655
  log('🔧 No JSON found in response, treating as plain text...');
94662
94656
  reviewData = {
94663
- issues: [],
94664
- suggestions: [response.trim()],
94657
+ issues: [
94658
+ {
94659
+ file: 'AI_RESPONSE',
94660
+ line: 1,
94661
+ ruleId: 'ai/raw_response',
94662
+ message: response,
94663
+ severity: 'info',
94664
+ category: 'documentation',
94665
+ },
94666
+ ],
94665
94667
  };
94666
94668
  }
94667
94669
  }
@@ -94671,7 +94673,6 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
94671
94673
  log(`📊 Overall score: ${0}`);
94672
94674
  log(`📋 Total issues: ${reviewData.issues?.length || 0}`);
94673
94675
  log(`🚨 Critical issues: ${reviewData.issues?.filter((i) => i.severity === 'critical').length || 0}`);
94674
- log(`💡 Suggestions count: ${Array.isArray(reviewData.suggestions) ? reviewData.suggestions.length : 0}`);
94675
94676
  log(`đŸ’Ŧ Comments count: ${Array.isArray(reviewData.issues) ? reviewData.issues.length : 0}`);
94676
94677
  // Process issues from the simplified format
94677
94678
  const processedIssues = Array.isArray(reviewData.issues)
@@ -94693,7 +94694,6 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
94693
94694
  // Validate and convert to ReviewSummary format
94694
94695
  const result = {
94695
94696
  issues: processedIssues,
94696
- suggestions: Array.isArray(reviewData.suggestions) ? reviewData.suggestions : [],
94697
94697
  };
94698
94698
  // Log issue counts
94699
94699
  const criticalCount = (result.issues || []).filter(i => i.severity === 'critical').length;
@@ -94976,6 +94976,40 @@ class CheckExecutionEngine {
94976
94976
  provider.setWebhookContext(this.webhookContext.webhookData);
94977
94977
  }
94978
94978
  }
94979
+ /**
94980
+ * Filter checks based on tag filter configuration
94981
+ */
94982
+ filterChecksByTags(checks, config, tagFilter) {
94983
+ if (!tagFilter || (!tagFilter.include && !tagFilter.exclude)) {
94984
+ return checks;
94985
+ }
94986
+ const logFn = this.config?.output?.pr_comment ? console.error : console.log;
94987
+ return checks.filter(checkName => {
94988
+ const checkConfig = config?.checks?.[checkName];
94989
+ if (!checkConfig) {
94990
+ // If no config for this check, include it by default
94991
+ return true;
94992
+ }
94993
+ const checkTags = checkConfig.tags || [];
94994
+ // Check exclude tags first (if any exclude tag matches, skip the check)
94995
+ if (tagFilter.exclude && tagFilter.exclude.length > 0) {
94996
+ const hasExcludedTag = tagFilter.exclude.some(tag => checkTags.includes(tag));
94997
+ if (hasExcludedTag) {
94998
+ logFn(`â­ī¸ Skipping check '${checkName}' - has excluded tag`);
94999
+ return false;
95000
+ }
95001
+ }
95002
+ // Check include tags (if specified, at least one must match)
95003
+ if (tagFilter.include && tagFilter.include.length > 0) {
95004
+ const hasIncludedTag = tagFilter.include.some(tag => checkTags.includes(tag));
95005
+ if (!hasIncludedTag) {
95006
+ logFn(`â­ī¸ Skipping check '${checkName}' - does not have required tags`);
95007
+ return false;
95008
+ }
95009
+ }
95010
+ return true;
95011
+ });
95012
+ }
94979
95013
  /**
94980
95014
  * Execute checks on the local repository
94981
95015
  */
@@ -95005,13 +95039,23 @@ class CheckExecutionEngine {
95005
95039
  }
95006
95040
  // Convert to PRInfo format for compatibility with existing reviewer
95007
95041
  const prInfo = this.gitAnalyzer.toPRInfo(repositoryInfo);
95042
+ // Apply tag filtering if specified
95043
+ const filteredChecks = this.filterChecksByTags(options.checks, options.config, options.tagFilter || options.config?.tag_filter);
95044
+ if (filteredChecks.length === 0) {
95045
+ logFn('âš ī¸ No checks match the tag filter criteria');
95046
+ // Complete GitHub checks with no checks message if they were initialized
95047
+ if (this.checkRunMap) {
95048
+ await this.completeGitHubChecksWithError('No checks match the tag filter criteria');
95049
+ }
95050
+ return this.createErrorResult(repositoryInfo, 'No checks match the tag filter criteria', startTime, timestamp, options.checks);
95051
+ }
95008
95052
  // Update GitHub checks to in-progress status
95009
95053
  if (this.checkRunMap) {
95010
95054
  await this.updateGitHubChecksInProgress(options);
95011
95055
  }
95012
95056
  // Execute checks using the existing PRReviewer
95013
- logFn(`🤖 Executing checks: ${options.checks.join(', ')}`);
95014
- const reviewSummary = await this.executeReviewChecks(prInfo, options.checks, options.timeout, options.config, options.outputFormat, options.debug, options.maxParallelism, options.failFast);
95057
+ logFn(`🤖 Executing checks: ${filteredChecks.join(', ')}`);
95058
+ const reviewSummary = await this.executeReviewChecks(prInfo, filteredChecks, options.timeout, options.config, options.outputFormat, options.debug, options.maxParallelism, options.failFast);
95015
95059
  // Complete GitHub checks with results
95016
95060
  if (this.checkRunMap) {
95017
95061
  await this.completeGitHubChecksWithResults(reviewSummary, options);
@@ -95035,7 +95079,7 @@ class CheckExecutionEngine {
95035
95079
  reviewSummary,
95036
95080
  executionTime,
95037
95081
  timestamp,
95038
- checksExecuted: options.checks,
95082
+ checksExecuted: filteredChecks,
95039
95083
  debug: debugInfo,
95040
95084
  };
95041
95085
  }
@@ -95237,7 +95281,7 @@ class CheckExecutionEngine {
95237
95281
  /**
95238
95282
  * Execute review checks and return grouped results for new architecture
95239
95283
  */
95240
- async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast) {
95284
+ async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter) {
95241
95285
  // Determine where to send log messages based on output format
95242
95286
  const logFn = outputFormat === 'json' || outputFormat === 'sarif' ? console.error : console.log;
95243
95287
  // Only output debug messages if debug mode is enabled
@@ -95250,8 +95294,18 @@ class CheckExecutionEngine {
95250
95294
  if (filteredChecks.length !== checks.length && debug) {
95251
95295
  logFn(`🔧 Debug: Event filtering reduced checks from ${checks.length} to ${filteredChecks.length}: ${JSON.stringify(filteredChecks)}`);
95252
95296
  }
95297
+ // Apply tag filtering if specified
95298
+ const tagFilteredChecks = this.filterChecksByTags(filteredChecks, config, tagFilter || config?.tag_filter);
95299
+ if (tagFilteredChecks.length !== filteredChecks.length && debug) {
95300
+ logFn(`🔧 Debug: Tag filtering reduced checks from ${filteredChecks.length} to ${tagFilteredChecks.length}: ${JSON.stringify(tagFilteredChecks)}`);
95301
+ }
95253
95302
  // Use filtered checks for execution
95254
- checks = filteredChecks;
95303
+ checks = tagFilteredChecks;
95304
+ // Check if we have any checks left after filtering
95305
+ if (checks.length === 0) {
95306
+ logFn('âš ī¸ No checks remain after tag filtering');
95307
+ return {};
95308
+ }
95255
95309
  if (!config?.checks) {
95256
95310
  throw new Error('Config with check definitions required for grouped execution');
95257
95311
  }
@@ -95341,12 +95395,9 @@ class CheckExecutionEngine {
95341
95395
  continue;
95342
95396
  // Extract issues for this check
95343
95397
  const checkIssues = (reviewSummary.issues || []).filter(issue => issue.ruleId?.startsWith(`${checkName}/`));
95344
- // Extract suggestions for this check
95345
- const checkSuggestions = (reviewSummary.suggestions || []).filter(suggestion => suggestion.startsWith(`[${checkName}]`));
95346
95398
  // Create a mini ReviewSummary for this check
95347
95399
  const checkSummary = {
95348
95400
  issues: checkIssues,
95349
- suggestions: checkSuggestions,
95350
95401
  debug: reviewSummary.debug,
95351
95402
  };
95352
95403
  // Render content for this check
@@ -95460,12 +95511,7 @@ class CheckExecutionEngine {
95460
95511
  }
95461
95512
  else if (schema === 'plain') {
95462
95513
  // Plain schema - return raw content directly
95463
- // Strip [checkName] prefixes from suggestions before joining
95464
- const cleanedSuggestions = (reviewSummary.suggestions || []).map(suggestion => {
95465
- // Remove [checkName] prefix if present
95466
- return suggestion.replace(/^\[[^\]]+\]\s*/, '');
95467
- });
95468
- return (reviewSummary.issues?.[0]?.message || '') + (cleanedSuggestions.join('\n\n') || '');
95514
+ return reviewSummary.issues?.[0]?.message || '';
95469
95515
  }
95470
95516
  else {
95471
95517
  // Use built-in schema template
@@ -95480,7 +95526,6 @@ class CheckExecutionEngine {
95480
95526
  const templateData = {
95481
95527
  issues: reviewSummary.issues || [],
95482
95528
  checkName: checkName,
95483
- suggestions: reviewSummary.suggestions || [],
95484
95529
  };
95485
95530
  const rendered = await liquid.parseAndRender(templateContent, templateData);
95486
95531
  return rendered.trim();
@@ -95543,7 +95588,6 @@ class CheckExecutionEngine {
95543
95588
  category: 'logic',
95544
95589
  },
95545
95590
  ],
95546
- suggestions: [],
95547
95591
  };
95548
95592
  }
95549
95593
  // Build dependency graph
@@ -95560,7 +95604,6 @@ class CheckExecutionEngine {
95560
95604
  category: 'logic',
95561
95605
  },
95562
95606
  ],
95563
- suggestions: [],
95564
95607
  };
95565
95608
  }
95566
95609
  // Log execution plan
@@ -95614,7 +95657,6 @@ class CheckExecutionEngine {
95614
95657
  error: null,
95615
95658
  result: {
95616
95659
  issues: [],
95617
- suggestions: [`Check '${checkName}' was skipped - condition not met`],
95618
95660
  },
95619
95661
  };
95620
95662
  }
@@ -95727,7 +95769,6 @@ class CheckExecutionEngine {
95727
95769
  replacement: undefined,
95728
95770
  },
95729
95771
  ],
95730
- suggestions: [],
95731
95772
  };
95732
95773
  results.set(checkName, errorSummary);
95733
95774
  // Check if we should stop execution due to fail-fast
@@ -95825,7 +95866,6 @@ class CheckExecutionEngine {
95825
95866
  error: null,
95826
95867
  result: {
95827
95868
  issues: [],
95828
- suggestions: [`Check '${checkName}' was skipped - condition not met`],
95829
95869
  },
95830
95870
  };
95831
95871
  }
@@ -95947,7 +95987,6 @@ class CheckExecutionEngine {
95947
95987
  */
95948
95988
  aggregateDependencyAwareResults(results, dependencyGraph, debug, stoppedEarly) {
95949
95989
  const aggregatedIssues = [];
95950
- const aggregatedSuggestions = [];
95951
95990
  const debugInfo = [];
95952
95991
  // Add execution plan info
95953
95992
  const stats = dependency_resolver_1.DependencyResolver.getExecutionStats(dependencyGraph);
@@ -95981,13 +96020,8 @@ class CheckExecutionEngine {
95981
96020
  }
95982
96021
  // Issues are already prefixed and enriched with group/schema info
95983
96022
  aggregatedIssues.push(...(result.issues || []));
95984
- // Add suggestions with check name prefix
95985
- const prefixedSuggestions = (result.suggestions || []).map(suggestion => `[${checkName}] ${suggestion}`);
95986
- aggregatedSuggestions.push(...prefixedSuggestions);
95987
96023
  }
95988
96024
  }
95989
- // Add summary information
95990
- aggregatedSuggestions.unshift(...debugInfo);
95991
96025
  console.error(`🔧 Debug: Aggregated ${aggregatedIssues.length} issues from ${results.size} dependency-aware checks`);
95992
96026
  // Apply issue suppression filtering
95993
96027
  const suppressionEnabled = this.config?.output?.suppressionEnabled !== false;
@@ -96032,25 +96066,20 @@ class CheckExecutionEngine {
96032
96066
  }
96033
96067
  return {
96034
96068
  issues: filteredIssues,
96035
- suggestions: aggregatedSuggestions,
96036
96069
  debug: aggregatedDebug,
96037
96070
  };
96038
96071
  }
96039
96072
  /**
96040
96073
  * Aggregate results from parallel check execution (legacy method)
96041
96074
  */
96042
- aggregateParallelResults(results, checkNames, debug, stoppedEarly) {
96075
+ aggregateParallelResults(results, checkNames, debug, _stoppedEarly) {
96043
96076
  const aggregatedIssues = [];
96044
- const aggregatedSuggestions = [];
96045
96077
  const debugInfo = [];
96046
- let successfulChecks = 0;
96047
- let failedChecks = 0;
96048
96078
  results.forEach((result, index) => {
96049
96079
  const checkName = checkNames[index];
96050
96080
  if (result.status === 'fulfilled') {
96051
96081
  const checkResult = result.value;
96052
96082
  if (checkResult.error) {
96053
- failedChecks++;
96054
96083
  const log = console.error;
96055
96084
  log(`🔧 Debug: Check ${checkName} failed: ${checkResult.error}`);
96056
96085
  debugInfo.push(`❌ Check "${checkName}" failed: ${checkResult.error}`);
@@ -96076,18 +96105,13 @@ class CheckExecutionEngine {
96076
96105
  });
96077
96106
  }
96078
96107
  else if (checkResult.result) {
96079
- successfulChecks++;
96080
96108
  console.error(`🔧 Debug: Check ${checkName} succeeded with ${(checkResult.result.issues || []).length} issues`);
96081
96109
  debugInfo.push(`✅ Check "${checkName}" completed: ${(checkResult.result.issues || []).length} issues found`);
96082
96110
  // Issues are already prefixed and enriched with group/schema info
96083
96111
  aggregatedIssues.push(...(checkResult.result.issues || []));
96084
- // Add suggestions with check name prefix
96085
- const prefixedSuggestions = (checkResult.result.suggestions || []).map(suggestion => `[${checkName}] ${suggestion}`);
96086
- aggregatedSuggestions.push(...prefixedSuggestions);
96087
96112
  }
96088
96113
  }
96089
96114
  else {
96090
- failedChecks++;
96091
96115
  const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason);
96092
96116
  const log = console.error;
96093
96117
  log(`🔧 Debug: Check ${checkName} promise rejected: ${errorMessage}`);
@@ -96113,11 +96137,6 @@ class CheckExecutionEngine {
96113
96137
  });
96114
96138
  }
96115
96139
  });
96116
- // Add summary information
96117
- debugInfo.unshift(stoppedEarly
96118
- ? `🛑 Parallel execution stopped early (fail-fast): ${successfulChecks} successful, ${failedChecks} failed`
96119
- : `🔍 Parallel execution completed: ${successfulChecks} successful, ${failedChecks} failed`);
96120
- aggregatedSuggestions.unshift(...debugInfo);
96121
96140
  console.error(`🔧 Debug: Aggregated ${aggregatedIssues.length} issues from ${results.length} checks`);
96122
96141
  // Apply issue suppression filtering
96123
96142
  const suppressionEnabled = this.config?.output?.suppressionEnabled !== false;
@@ -96219,7 +96238,6 @@ class CheckExecutionEngine {
96219
96238
  }
96220
96239
  return {
96221
96240
  issues: filteredIssues,
96222
- suggestions: aggregatedSuggestions,
96223
96241
  debug: aggregatedDebug,
96224
96242
  };
96225
96243
  }
@@ -96330,7 +96348,6 @@ class CheckExecutionEngine {
96330
96348
  replacement: undefined,
96331
96349
  },
96332
96350
  ],
96333
- suggestions: [`Error: ${errorMessage}`],
96334
96351
  },
96335
96352
  executionTime,
96336
96353
  timestamp,
@@ -96545,7 +96562,7 @@ class CheckExecutionEngine {
96545
96562
  try {
96546
96563
  const checkIssues = issuesByCheck.get(checkName) || [];
96547
96564
  // Evaluate failure conditions for this specific check
96548
- const failureResults = await this.evaluateFailureConditions(checkName, { issues: checkIssues, suggestions: [] }, options.config);
96565
+ const failureResults = await this.evaluateFailureConditions(checkName, { issues: checkIssues }, options.config);
96549
96566
  await this.githubCheckService.completeCheckRun(options.githubChecks.owner, options.githubChecks.repo, checkRun.id, checkName, failureResults, checkIssues);
96550
96567
  console.log(`✅ Completed ${checkName} check with ${checkIssues.length} issues`);
96551
96568
  }
@@ -96799,17 +96816,34 @@ async function main() {
96799
96816
  logFn(`🤖 Executing checks: ${checksToRun.join(', ')}`);
96800
96817
  // Create CheckExecutionEngine for running checks
96801
96818
  const engine = new check_execution_engine_1.CheckExecutionEngine();
96819
+ // Build tag filter from CLI options
96820
+ const tagFilter = options.tags || options.excludeTags
96821
+ ? {
96822
+ include: options.tags,
96823
+ exclude: options.excludeTags,
96824
+ }
96825
+ : undefined;
96802
96826
  // Execute checks with proper parameters (cast to PRInfo)
96803
- const groupedResults = await engine.executeGroupedChecks(repositoryInfo, checksToRun, options.timeout, config, options.output, options.debug || false);
96827
+ const groupedResults = await engine.executeGroupedChecks(repositoryInfo, checksToRun, options.timeout, config, options.output, options.debug || false, options.maxParallelism, options.failFast, tagFilter);
96804
96828
  // Format output based on format type
96805
96829
  let output;
96806
96830
  if (options.output === 'json') {
96807
96831
  output = JSON.stringify(groupedResults, null, 2);
96808
96832
  }
96809
96833
  else if (options.output === 'sarif') {
96810
- // For SARIF output, we need to convert to SARIF format
96811
- // For now, output as JSON until proper SARIF formatting is implemented
96812
- output = JSON.stringify(groupedResults, null, 2);
96834
+ // Build analysis result and format as SARIF
96835
+ const analysisResult = {
96836
+ repositoryInfo,
96837
+ reviewSummary: {
96838
+ issues: Object.values(groupedResults)
96839
+ .flatMap((r) => r.map((check) => check.issues || []).flat())
96840
+ .flat(),
96841
+ },
96842
+ executionTime: 0,
96843
+ timestamp: new Date().toISOString(),
96844
+ checksExecuted: checksToRun,
96845
+ };
96846
+ output = output_formatters_1.OutputFormatters.formatAsSarif(analysisResult);
96813
96847
  }
96814
96848
  else if (options.output === 'markdown') {
96815
96849
  // Create analysis result for markdown formatting
@@ -96819,7 +96853,6 @@ async function main() {
96819
96853
  issues: Object.values(groupedResults)
96820
96854
  .flatMap((r) => r.map((check) => check.issues || []).flat())
96821
96855
  .flat(),
96822
- suggestions: [], // Suggestions are now embedded in issues
96823
96856
  },
96824
96857
  executionTime: 0,
96825
96858
  timestamp: new Date().toISOString(),
@@ -96835,7 +96868,6 @@ async function main() {
96835
96868
  issues: Object.values(groupedResults)
96836
96869
  .flatMap((r) => r.map((check) => check.issues || []).flat())
96837
96870
  .flat(),
96838
- suggestions: [], // Suggestions are now embedded in issues
96839
96871
  },
96840
96872
  executionTime: 0,
96841
96873
  timestamp: new Date().toISOString(),
@@ -96958,6 +96990,8 @@ class CLI {
96958
96990
  .option('--max-parallelism <count>', 'Maximum number of checks to run in parallel (default: 3)', value => parseInt(value, 10))
96959
96991
  .option('--debug', 'Enable debug mode for detailed output')
96960
96992
  .option('--fail-fast', 'Stop execution on first failure condition')
96993
+ .option('--tags <tags>', 'Include checks with these tags (comma-separated)')
96994
+ .option('--exclude-tags <tags>', 'Exclude checks with these tags (comma-separated)')
96961
96995
  .option('--no-remote-extends', 'Disable loading configurations from remote URLs')
96962
96996
  .addHelpText('after', this.getExamplesText())
96963
96997
  .exitOverride(); // Prevent automatic process.exit for better error handling
@@ -96993,6 +97027,8 @@ class CLI {
96993
97027
  .option('--max-parallelism <count>', 'Maximum number of checks to run in parallel (default: 3)', value => parseInt(value, 10))
96994
97028
  .option('--debug', 'Enable debug mode for detailed output')
96995
97029
  .option('--fail-fast', 'Stop execution on first failure condition')
97030
+ .option('--tags <tags>', 'Include checks with these tags (comma-separated)')
97031
+ .option('--exclude-tags <tags>', 'Exclude checks with these tags (comma-separated)')
96996
97032
  .option('--allowed-remote-patterns <patterns>', 'Comma-separated list of allowed URL prefixes for remote config extends (e.g., "https://github.com/,https://raw.githubusercontent.com/")')
96997
97033
  .allowUnknownOption(false)
96998
97034
  .allowExcessArguments(false) // Don't allow positional arguments
@@ -97015,6 +97051,15 @@ class CLI {
97015
97051
  .split(',')
97016
97052
  .map((p) => p.trim());
97017
97053
  }
97054
+ // Parse tag filters
97055
+ let tags;
97056
+ if (options.tags) {
97057
+ tags = options.tags.split(',').map((t) => t.trim());
97058
+ }
97059
+ let excludeTags;
97060
+ if (options.excludeTags) {
97061
+ excludeTags = options.excludeTags.split(',').map((t) => t.trim());
97062
+ }
97018
97063
  return {
97019
97064
  checks: uniqueChecks,
97020
97065
  output: options.output,
@@ -97023,6 +97068,8 @@ class CLI {
97023
97068
  maxParallelism: options.maxParallelism,
97024
97069
  debug: options.debug,
97025
97070
  failFast: options.failFast,
97071
+ tags,
97072
+ excludeTags,
97026
97073
  allowedRemotePatterns,
97027
97074
  help: options.help,
97028
97075
  version: options.version,
@@ -97129,7 +97176,10 @@ Examples:
97129
97176
  visor --check all --timeout 300000 --output json # 5 minute timeout
97130
97177
  visor --check all --max-parallelism 5 --output json # Run up to 5 checks in parallel
97131
97178
  visor --check all --debug --output markdown # Enable debug mode
97132
- visor --check all --fail-fast --output json # Stop on first failure`;
97179
+ visor --check all --fail-fast --output json # Stop on first failure
97180
+ visor --tags local,fast --output table # Run checks tagged as 'local' or 'fast'
97181
+ visor --exclude-tags slow,experimental --output json # Skip checks tagged as 'slow' or 'experimental'
97182
+ visor --tags security --exclude-tags slow # Run security checks but skip slow ones`;
97133
97183
  }
97134
97184
  /**
97135
97185
  * Display help
@@ -97270,6 +97320,7 @@ class ConfigManager {
97270
97320
  'http_input',
97271
97321
  'http_client',
97272
97322
  'noop',
97323
+ 'log',
97273
97324
  ];
97274
97325
  validEventTriggers = [
97275
97326
  'pr_opened',
@@ -97580,6 +97631,10 @@ class ConfigManager {
97580
97631
  });
97581
97632
  }
97582
97633
  }
97634
+ // Validate tag_filter if present
97635
+ if (config.tag_filter) {
97636
+ this.validateTagFilter(config.tag_filter, errors);
97637
+ }
97583
97638
  if (errors.length > 0) {
97584
97639
  throw new Error(errors[0].message);
97585
97640
  }
@@ -97694,6 +97749,98 @@ class ConfigManager {
97694
97749
  }
97695
97750
  }
97696
97751
  }
97752
+ // Validate tags configuration
97753
+ if (checkConfig.tags !== undefined) {
97754
+ if (!Array.isArray(checkConfig.tags)) {
97755
+ errors.push({
97756
+ field: `checks.${checkName}.tags`,
97757
+ message: `Invalid tags value for "${checkName}": must be an array of strings`,
97758
+ value: checkConfig.tags,
97759
+ });
97760
+ }
97761
+ else {
97762
+ // Validate each tag
97763
+ const validTagPattern = /^[a-zA-Z0-9][a-zA-Z0-9-_]*$/;
97764
+ checkConfig.tags.forEach((tag, index) => {
97765
+ if (typeof tag !== 'string') {
97766
+ errors.push({
97767
+ field: `checks.${checkName}.tags[${index}]`,
97768
+ message: `Invalid tag at index ${index} for "${checkName}": must be a string`,
97769
+ value: tag,
97770
+ });
97771
+ }
97772
+ else if (!validTagPattern.test(tag)) {
97773
+ errors.push({
97774
+ field: `checks.${checkName}.tags[${index}]`,
97775
+ message: `Invalid tag "${tag}" for "${checkName}": tags must be alphanumeric with hyphens or underscores (start with alphanumeric)`,
97776
+ value: tag,
97777
+ });
97778
+ }
97779
+ });
97780
+ }
97781
+ }
97782
+ }
97783
+ /**
97784
+ * Validate tag filter configuration
97785
+ */
97786
+ validateTagFilter(tagFilter, errors) {
97787
+ const validTagPattern = /^[a-zA-Z0-9][a-zA-Z0-9-_]*$/;
97788
+ // Validate include tags
97789
+ if (tagFilter.include !== undefined) {
97790
+ if (!Array.isArray(tagFilter.include)) {
97791
+ errors.push({
97792
+ field: 'tag_filter.include',
97793
+ message: 'tag_filter.include must be an array of strings',
97794
+ value: tagFilter.include,
97795
+ });
97796
+ }
97797
+ else {
97798
+ tagFilter.include.forEach((tag, index) => {
97799
+ if (typeof tag !== 'string') {
97800
+ errors.push({
97801
+ field: `tag_filter.include[${index}]`,
97802
+ message: `Invalid tag at index ${index}: must be a string`,
97803
+ value: tag,
97804
+ });
97805
+ }
97806
+ else if (!validTagPattern.test(tag)) {
97807
+ errors.push({
97808
+ field: `tag_filter.include[${index}]`,
97809
+ message: `Invalid tag "${tag}": tags must be alphanumeric with hyphens or underscores`,
97810
+ value: tag,
97811
+ });
97812
+ }
97813
+ });
97814
+ }
97815
+ }
97816
+ // Validate exclude tags
97817
+ if (tagFilter.exclude !== undefined) {
97818
+ if (!Array.isArray(tagFilter.exclude)) {
97819
+ errors.push({
97820
+ field: 'tag_filter.exclude',
97821
+ message: 'tag_filter.exclude must be an array of strings',
97822
+ value: tagFilter.exclude,
97823
+ });
97824
+ }
97825
+ else {
97826
+ tagFilter.exclude.forEach((tag, index) => {
97827
+ if (typeof tag !== 'string') {
97828
+ errors.push({
97829
+ field: `tag_filter.exclude[${index}]`,
97830
+ message: `Invalid tag at index ${index}: must be a string`,
97831
+ value: tag,
97832
+ });
97833
+ }
97834
+ else if (!validTagPattern.test(tag)) {
97835
+ errors.push({
97836
+ field: `tag_filter.exclude[${index}]`,
97837
+ message: `Invalid tag "${tag}": tags must be alphanumeric with hyphens or underscores`,
97838
+ value: tag,
97839
+ });
97840
+ }
97841
+ });
97842
+ }
97843
+ }
97697
97844
  }
97698
97845
  /**
97699
97846
  * Validate HTTP server configuration
@@ -98144,7 +98291,6 @@ class FailureConditionEvaluator {
98144
98291
  // Required output property (empty for if conditions)
98145
98292
  output: {
98146
98293
  issues: [],
98147
- suggestions: [],
98148
98294
  },
98149
98295
  // Utility metadata
98150
98296
  metadata: {
@@ -98286,7 +98432,7 @@ class FailureConditionEvaluator {
98286
98432
  // Extract context variables
98287
98433
  const output = context.output || {};
98288
98434
  const issues = output.issues || [];
98289
- const suggestions = output.suggestions || [];
98435
+ const suggestions = [];
98290
98436
  // Backward compatibility: provide metadata for transition period
98291
98437
  // TODO: Remove after all configurations are updated
98292
98438
  const metadata = context.metadata || {
@@ -98397,7 +98543,7 @@ class FailureConditionEvaluator {
98397
98543
  * Build the evaluation context for expressions
98398
98544
  */
98399
98545
  buildEvaluationContext(checkName, checkSchema, checkGroup, reviewSummary, previousOutputs) {
98400
- const { issues, suggestions, debug } = reviewSummary;
98546
+ const { issues, debug } = reviewSummary;
98401
98547
  const context = {
98402
98548
  output: {
98403
98549
  issues: (issues || []).map(issue => ({
@@ -98413,7 +98559,6 @@ class FailureConditionEvaluator {
98413
98559
  suggestion: issue.suggestion,
98414
98560
  replacement: issue.replacement,
98415
98561
  })),
98416
- suggestions,
98417
98562
  // Include additional schema-specific data from reviewSummary
98418
98563
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
98419
98564
  ...reviewSummary, // Pass through any additional fields
@@ -99612,6 +99757,9 @@ async function run() {
99612
99757
  'max-parallelism': (0, core_1.getInput)('max-parallelism') || undefined,
99613
99758
  'ai-provider': (0, core_1.getInput)('ai-provider') || undefined,
99614
99759
  'ai-model': (0, core_1.getInput)('ai-model') || undefined,
99760
+ // Tag filtering inputs
99761
+ tags: (0, core_1.getInput)('tags') || undefined,
99762
+ 'exclude-tags': (0, core_1.getInput)('exclude-tags') || undefined,
99615
99763
  // Legacy inputs for backward compatibility
99616
99764
  'visor-config-path': (0, core_1.getInput)('visor-config-path') || undefined,
99617
99765
  'visor-checks': (0, core_1.getInput)('visor-checks') || undefined,
@@ -100553,7 +100701,6 @@ async function completeIndividualChecks(checkService, owner, repo, checkRunMap,
100553
100701
  // Create a ReviewSummary for this check's issues
100554
100702
  const checkReviewSummary = {
100555
100703
  issues: checkIssues,
100556
- suggestions: [],
100557
100704
  };
100558
100705
  // Determine which fail_if to use: check-specific overrides global
100559
100706
  const effectiveFailIf = checkFailIf || globalFailIf;
@@ -100601,7 +100748,6 @@ async function completeCombinedCheck(checkService, owner, repo, checkRunMap, gro
100601
100748
  // Create a combined ReviewSummary with all issues
100602
100749
  const combinedReviewSummary = {
100603
100750
  issues: allIssues,
100604
- suggestions: [],
100605
100751
  };
100606
100752
  // Evaluate global fail_if for the combined check
100607
100753
  const globalFailIf = config?.fail_if;
@@ -100947,22 +101093,6 @@ class OutputFormatters {
100947
101093
  else {
100948
101094
  output += 'No issues found!\n\n';
100949
101095
  }
100950
- // Suggestions table
100951
- if ((result.reviewSummary.suggestions || []).length > 0) {
100952
- const suggestionsTable = new cli_table3_1.default({
100953
- head: ['#', 'Suggestion'],
100954
- colWidths: [5, 70],
100955
- style: {
100956
- head: ['cyan', 'bold'],
100957
- border: ['grey'],
100958
- },
100959
- });
100960
- output += 'Suggestions\n';
100961
- (result.reviewSummary.suggestions || []).forEach((suggestion, index) => {
100962
- suggestionsTable.push([(index + 1).toString(), this.wrapText(suggestion, 65)]);
100963
- });
100964
- output += suggestionsTable.toString() + '\n\n';
100965
- }
100966
101096
  // Files table (if requested)
100967
101097
  if (options.includeFiles && result.repositoryInfo.files.length > 0) {
100968
101098
  const filesTable = new cli_table3_1.default({
@@ -101020,7 +101150,6 @@ class OutputFormatters {
101020
101150
  issues: options.groupByCategory
101021
101151
  ? this.groupCommentsByCategory((0, reviewer_1.convertIssuesToComments)(issues || []))
101022
101152
  : issues || [],
101023
- suggestions: result.reviewSummary.suggestions || [],
101024
101153
  files: options.includeFiles ? result.repositoryInfo.files : undefined,
101025
101154
  debug: result.debug, // Include debug information when available
101026
101155
  failureConditions: result.failureConditions || [], // Include failure condition results
@@ -101317,14 +101446,6 @@ class OutputFormatters {
101317
101446
  output += `## No Issues Found\n\n`;
101318
101447
  output += `Great job! No issues were detected in the analyzed code.\n\n`;
101319
101448
  }
101320
- // Suggestions
101321
- if ((result.reviewSummary.suggestions || []).length > 0) {
101322
- output += `## Recommendations\n\n`;
101323
- (result.reviewSummary.suggestions || []).forEach((suggestion, index) => {
101324
- output += `${index + 1}. ${suggestion}\n`;
101325
- });
101326
- output += '\n';
101327
- }
101328
101449
  // Files (if requested)
101329
101450
  if (options.includeFiles && result.repositoryInfo.files.length > 0) {
101330
101451
  output += `## Files Changed\n\n`;
@@ -101683,6 +101804,7 @@ const issue_filter_1 = __nccwpck_require__(36879);
101683
101804
  const liquidjs_1 = __nccwpck_require__(48694);
101684
101805
  const promises_1 = __importDefault(__nccwpck_require__(91943));
101685
101806
  const path_1 = __importDefault(__nccwpck_require__(16928));
101807
+ const claude_code_types_1 = __nccwpck_require__(21710);
101686
101808
  /**
101687
101809
  * AI-powered check provider using probe agent
101688
101810
  */
@@ -101724,6 +101846,39 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
101724
101846
  !['google', 'anthropic', 'openai', 'mock'].includes(cfg.ai.provider)) {
101725
101847
  return false;
101726
101848
  }
101849
+ // Validate mcpServers if present
101850
+ if (cfg.ai.mcpServers) {
101851
+ if (!this.validateMcpServers(cfg.ai.mcpServers)) {
101852
+ return false;
101853
+ }
101854
+ }
101855
+ }
101856
+ // Validate check-level MCP servers if present
101857
+ if (cfg.ai_mcp_servers) {
101858
+ if (!this.validateMcpServers(cfg.ai_mcp_servers)) {
101859
+ return false;
101860
+ }
101861
+ }
101862
+ return true;
101863
+ }
101864
+ /**
101865
+ * Validate MCP servers configuration
101866
+ */
101867
+ validateMcpServers(mcpServers) {
101868
+ if (typeof mcpServers !== 'object' || mcpServers === null) {
101869
+ return false;
101870
+ }
101871
+ for (const serverConfig of Object.values(mcpServers)) {
101872
+ if (!serverConfig || typeof serverConfig !== 'object') {
101873
+ return false;
101874
+ }
101875
+ const config = serverConfig;
101876
+ if (!config.command || typeof config.command !== 'string') {
101877
+ return false;
101878
+ }
101879
+ if (config.args && !Array.isArray(config.args)) {
101880
+ return false;
101881
+ }
101727
101882
  }
101728
101883
  return true;
101729
101884
  }
@@ -101972,9 +102127,8 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
101972
102127
  styleIssues: result.issues?.filter(i => i.category === 'style') || [],
101973
102128
  logicIssues: result.issues?.filter(i => i.category === 'logic') || [],
101974
102129
  documentationIssues: result.issues?.filter(i => i.category === 'documentation') || [],
101975
- // All issues and suggestions
102130
+ // All issues
101976
102131
  issues: result.issues || [],
101977
- suggestions: result.suggestions || [],
101978
102132
  // Debug information if available
101979
102133
  debug: result.debug,
101980
102134
  // Raw data for advanced use
@@ -101990,6 +102144,53 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
101990
102144
  throw new Error(`Failed to render prompt template: ${error instanceof Error ? error.message : 'Unknown error'}`);
101991
102145
  }
101992
102146
  }
102147
+ /**
102148
+ * Setup MCP tools based on AI configuration
102149
+ */
102150
+ async setupMcpTools(aiConfig) {
102151
+ const tools = [];
102152
+ // Setup custom MCP servers if configured
102153
+ if (aiConfig.mcpServers) {
102154
+ try {
102155
+ // Import MCP SDK for custom server creation using safe import
102156
+ const mcpModule = await (0, claude_code_types_1.safeImport)('@modelcontextprotocol/sdk');
102157
+ if (!mcpModule) {
102158
+ console.warn('@modelcontextprotocol/sdk package not found. MCP servers disabled.');
102159
+ return tools;
102160
+ }
102161
+ const createSdkMcpServer = mcpModule.createSdkMcpServer || mcpModule.default?.createSdkMcpServer;
102162
+ if (createSdkMcpServer) {
102163
+ for (const [serverName, serverConfig] of Object.entries(aiConfig.mcpServers)) {
102164
+ try {
102165
+ // Create MCP server instance
102166
+ const server = await createSdkMcpServer({
102167
+ name: serverName,
102168
+ command: serverConfig.command,
102169
+ args: serverConfig.args || [],
102170
+ env: { ...process.env, ...serverConfig.env },
102171
+ });
102172
+ // Add server tools to available tools
102173
+ const serverTools = await server.listTools();
102174
+ tools.push(...serverTools.map((tool) => ({
102175
+ name: tool.name,
102176
+ server: serverName,
102177
+ })));
102178
+ }
102179
+ catch (serverError) {
102180
+ console.warn(`Failed to setup MCP server ${serverName}: ${serverError instanceof Error ? serverError.message : 'Unknown error'}`);
102181
+ }
102182
+ }
102183
+ }
102184
+ else {
102185
+ console.warn('createSdkMcpServer function not found in @modelcontextprotocol/sdk. MCP servers disabled.');
102186
+ }
102187
+ }
102188
+ catch (error) {
102189
+ console.warn(`Failed to import MCP SDK: ${error instanceof Error ? error.message : 'Unknown error'}. MCP servers disabled.`);
102190
+ }
102191
+ }
102192
+ return tools;
102193
+ }
101993
102194
  async execute(prInfo, config, _dependencyResults, sessionInfo) {
101994
102195
  // Apply environment configuration if present
101995
102196
  if (config.env) {
@@ -102038,6 +102239,32 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
102038
102239
  if (!customPrompt) {
102039
102240
  throw new Error(`No prompt defined for check. All checks must have prompts defined in .visor.yaml configuration.`);
102040
102241
  }
102242
+ // Setup MCP tools from multiple configuration levels
102243
+ const mcpServers = {};
102244
+ // 1. Start with global MCP servers (from visor config root)
102245
+ const globalConfig = config; // Cast to access potential global config
102246
+ if (globalConfig.ai_mcp_servers) {
102247
+ Object.assign(mcpServers, globalConfig.ai_mcp_servers);
102248
+ }
102249
+ // 2. Add check-level MCP servers (overrides global)
102250
+ if (config.ai_mcp_servers) {
102251
+ Object.assign(mcpServers, config.ai_mcp_servers);
102252
+ }
102253
+ // 3. Add ai.mcpServers (overrides everything)
102254
+ if (config.ai?.mcpServers) {
102255
+ Object.assign(mcpServers, config.ai.mcpServers);
102256
+ }
102257
+ // Setup MCP tools if any servers are configured
102258
+ if (Object.keys(mcpServers).length > 0) {
102259
+ const mcpConfig = { mcpServers };
102260
+ const mcpTools = await this.setupMcpTools(mcpConfig);
102261
+ if (mcpTools.length > 0) {
102262
+ aiConfig.tools = mcpTools;
102263
+ if (aiConfig.debug) {
102264
+ console.error(`🔧 Debug: AI check configured with ${mcpTools.length} MCP tools from ${Object.keys(mcpServers).length} servers`);
102265
+ }
102266
+ }
102267
+ }
102041
102268
  // Process prompt with Liquid templates and file loading
102042
102269
  const processedPrompt = await this.processPrompt(customPrompt, prInfo, config.eventContext, _dependencyResults);
102043
102270
  // Create AI service with config - environment variables will be used if aiConfig is empty
@@ -102106,8 +102333,10 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
102106
102333
  'ai.model',
102107
102334
  'ai.apiKey',
102108
102335
  'ai.timeout',
102336
+ 'ai.mcpServers',
102109
102337
  'ai_model',
102110
102338
  'ai_provider',
102339
+ 'ai_mcp_servers',
102111
102340
  'env',
102112
102341
  ];
102113
102342
  }
@@ -102143,6 +102372,7 @@ const http_check_provider_1 = __nccwpck_require__(31115);
102143
102372
  const http_input_provider_1 = __nccwpck_require__(74423);
102144
102373
  const http_client_provider_1 = __nccwpck_require__(36270);
102145
102374
  const noop_check_provider_1 = __nccwpck_require__(53003);
102375
+ const log_check_provider_1 = __nccwpck_require__(24903);
102146
102376
  const claude_code_check_provider_1 = __nccwpck_require__(17985);
102147
102377
  /**
102148
102378
  * Registry for managing check providers
@@ -102174,6 +102404,7 @@ class CheckProviderRegistry {
102174
102404
  this.register(new http_input_provider_1.HttpInputProvider());
102175
102405
  this.register(new http_client_provider_1.HttpClientProvider());
102176
102406
  this.register(new noop_check_provider_1.NoopCheckProvider());
102407
+ this.register(new log_check_provider_1.LogCheckProvider());
102177
102408
  // Try to register ClaudeCodeCheckProvider - it may fail if dependencies are missing
102178
102409
  try {
102179
102410
  this.register(new claude_code_check_provider_1.ClaudeCodeCheckProvider());
@@ -102707,7 +102938,6 @@ class ClaudeCodeCheckProvider extends check_provider_interface_1.CheckProvider {
102707
102938
  documentationIssues: result.issues?.filter(i => i.category === 'documentation') || [],
102708
102939
  // All issues and suggestions
102709
102940
  issues: result.issues || [],
102710
- suggestions: result.suggestions || [],
102711
102941
  // Debug information if available
102712
102942
  debug: result.debug,
102713
102943
  // Raw data for advanced use
@@ -102733,14 +102963,12 @@ class ClaudeCodeCheckProvider extends check_provider_interface_1.CheckProvider {
102733
102963
  // Convert to ReviewSummary format
102734
102964
  return {
102735
102965
  issues: parsed.issues || [],
102736
- suggestions: parsed.suggestions || [],
102737
102966
  };
102738
102967
  }
102739
102968
  catch {
102740
102969
  // If not JSON, treat as plain text comment
102741
102970
  return {
102742
102971
  issues: [],
102743
- suggestions: [],
102744
102972
  };
102745
102973
  }
102746
102974
  }
@@ -103121,7 +103349,6 @@ class HttpCheckProvider extends check_provider_interface_1.CheckProvider {
103121
103349
  : [];
103122
103350
  return {
103123
103351
  issues,
103124
- suggestions: Array.isArray(response.suggestions) ? response.suggestions : [],
103125
103352
  };
103126
103353
  }
103127
103354
  createErrorResult(url, error) {
@@ -103140,7 +103367,6 @@ class HttpCheckProvider extends check_provider_interface_1.CheckProvider {
103140
103367
  replacement: undefined,
103141
103368
  },
103142
103369
  ],
103143
- suggestions: [`Webhook ${url} failed: ${errorMessage}`],
103144
103370
  };
103145
103371
  }
103146
103372
  validateSeverity(severity) {
@@ -103301,16 +103527,14 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
103301
103527
  category: 'logic',
103302
103528
  },
103303
103529
  ],
103304
- suggestions: [],
103305
103530
  };
103306
103531
  }
103307
103532
  }
103308
- // Return the fetched data as suggestions for dependent checks to access
103533
+ // Return the fetched data as a custom field for dependent checks to access
103309
103534
  return {
103310
103535
  issues: [],
103311
- suggestions: [
103312
- typeof processedData === 'string' ? processedData : JSON.stringify(processedData),
103313
- ],
103536
+ // Add custom data field that will be passed through to dependent checks
103537
+ data: processedData,
103314
103538
  };
103315
103539
  }
103316
103540
  catch (error) {
@@ -103325,7 +103549,6 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
103325
103549
  category: 'logic',
103326
103550
  },
103327
103551
  ],
103328
- suggestions: [],
103329
103552
  };
103330
103553
  }
103331
103554
  }
@@ -103480,7 +103703,6 @@ class HttpInputProvider extends check_provider_interface_1.CheckProvider {
103480
103703
  if (!webhookData) {
103481
103704
  return {
103482
103705
  issues: [],
103483
- suggestions: [`Waiting for webhook data on endpoint: ${endpoint}`],
103484
103706
  };
103485
103707
  }
103486
103708
  // Apply transformation if specified
@@ -103512,15 +103734,15 @@ class HttpInputProvider extends check_provider_interface_1.CheckProvider {
103512
103734
  category: 'logic',
103513
103735
  },
103514
103736
  ],
103515
- suggestions: [],
103516
103737
  };
103517
103738
  }
103518
103739
  }
103519
- // Return the processed data as suggestions for dependent checks to access
103520
- // We encode the data as JSON in a suggestion so it can be accessed via outputs
103740
+ // Return the processed data as a custom field for dependent checks to access
103741
+ // This will be available in outputs for dependent checks
103521
103742
  return {
103522
103743
  issues: [],
103523
- suggestions: [JSON.stringify(processedData)],
103744
+ // Add custom data field that will be passed through
103745
+ data: processedData,
103524
103746
  };
103525
103747
  }
103526
103748
  getWebhookData(endpoint) {
@@ -103555,6 +103777,209 @@ class HttpInputProvider extends check_provider_interface_1.CheckProvider {
103555
103777
  exports.HttpInputProvider = HttpInputProvider;
103556
103778
 
103557
103779
 
103780
+ /***/ }),
103781
+
103782
+ /***/ 24903:
103783
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
103784
+
103785
+ "use strict";
103786
+
103787
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
103788
+ exports.LogCheckProvider = void 0;
103789
+ const check_provider_interface_1 = __nccwpck_require__(14131);
103790
+ const liquidjs_1 = __nccwpck_require__(48694);
103791
+ /**
103792
+ * Check provider that outputs debugging and logging information.
103793
+ * Useful for troubleshooting check workflows and understanding execution flow.
103794
+ */
103795
+ class LogCheckProvider extends check_provider_interface_1.CheckProvider {
103796
+ liquid;
103797
+ constructor() {
103798
+ super();
103799
+ this.liquid = new liquidjs_1.Liquid({
103800
+ strictVariables: false,
103801
+ strictFilters: false,
103802
+ });
103803
+ }
103804
+ getName() {
103805
+ return 'log';
103806
+ }
103807
+ getDescription() {
103808
+ return 'Output debugging and logging information for troubleshooting check workflows';
103809
+ }
103810
+ async validateConfig(config) {
103811
+ if (!config || typeof config !== 'object') {
103812
+ return false;
103813
+ }
103814
+ const cfg = config;
103815
+ // Type must be 'log'
103816
+ if (cfg.type !== 'log') {
103817
+ return false;
103818
+ }
103819
+ // Message is required
103820
+ if (!cfg.message || typeof cfg.message !== 'string') {
103821
+ return false;
103822
+ }
103823
+ // Validate log level if provided
103824
+ if (cfg.level && !['debug', 'info', 'warn', 'error'].includes(cfg.level)) {
103825
+ return false;
103826
+ }
103827
+ return true;
103828
+ }
103829
+ async execute(prInfo, config, dependencyResults, _sessionInfo) {
103830
+ const message = config.message;
103831
+ const level = config.level || 'info';
103832
+ const includePrContext = config.include_pr_context !== false;
103833
+ const includeDependencies = config.include_dependencies !== false;
103834
+ const includeMetadata = config.include_metadata !== false;
103835
+ // Prepare template context
103836
+ const templateContext = this.buildTemplateContext(prInfo, dependencyResults, includePrContext, includeDependencies, includeMetadata);
103837
+ // Render the log message template
103838
+ const renderedMessage = await this.liquid.parseAndRender(message, templateContext);
103839
+ // Build the log output
103840
+ const logOutput = this.formatLogOutput(level, renderedMessage, templateContext, includePrContext, includeDependencies, includeMetadata);
103841
+ // Output to console based on log level
103842
+ const logFn = level === 'error' ? console.error : level === 'warn' ? console.warn : console.log;
103843
+ logFn(logOutput);
103844
+ // Return with the log content as custom data for dependent checks
103845
+ return {
103846
+ issues: [],
103847
+ // Add log output as custom field
103848
+ logOutput,
103849
+ };
103850
+ }
103851
+ buildTemplateContext(prInfo, dependencyResults, includePrContext = true, includeDependencies = true, includeMetadata = true) {
103852
+ const context = {};
103853
+ if (includePrContext) {
103854
+ context.pr = {
103855
+ number: prInfo.number,
103856
+ title: prInfo.title,
103857
+ body: prInfo.body,
103858
+ author: prInfo.author,
103859
+ base: prInfo.base,
103860
+ head: prInfo.head,
103861
+ totalAdditions: prInfo.totalAdditions,
103862
+ totalDeletions: prInfo.totalDeletions,
103863
+ files: prInfo.files.map(f => ({
103864
+ filename: f.filename,
103865
+ status: f.status,
103866
+ additions: f.additions,
103867
+ deletions: f.deletions,
103868
+ changes: f.changes,
103869
+ })),
103870
+ };
103871
+ // Add convenience data
103872
+ context.filenames = prInfo.files.map(f => f.filename);
103873
+ context.fileCount = prInfo.files.length;
103874
+ }
103875
+ if (includeDependencies && dependencyResults) {
103876
+ const dependencies = {};
103877
+ context.dependencyCount = dependencyResults.size;
103878
+ for (const [checkName, result] of dependencyResults.entries()) {
103879
+ dependencies[checkName] = {
103880
+ issueCount: result.issues?.length || 0,
103881
+ suggestionCount: 0,
103882
+ issues: result.issues || [],
103883
+ };
103884
+ }
103885
+ context.dependencies = dependencies;
103886
+ }
103887
+ if (includeMetadata) {
103888
+ context.metadata = {
103889
+ timestamp: new Date().toISOString(),
103890
+ executionTime: Date.now(),
103891
+ nodeVersion: process.version,
103892
+ platform: process.platform,
103893
+ workingDirectory: process.cwd(),
103894
+ };
103895
+ }
103896
+ return context;
103897
+ }
103898
+ formatLogOutput(level, message, templateContext, includePrContext, includeDependencies, includeMetadata) {
103899
+ const sections = [];
103900
+ // Log level and message
103901
+ const levelEmoji = this.getLevelEmoji(level);
103902
+ sections.push(`${levelEmoji} **${level.toUpperCase()}**: ${message}`);
103903
+ // PR context section
103904
+ if (includePrContext && templateContext.pr) {
103905
+ const pr = templateContext.pr;
103906
+ sections.push('');
103907
+ sections.push('### PR Context');
103908
+ sections.push(`- **PR #${pr.number}**: ${pr.title}`);
103909
+ sections.push(`- **Author**: ${pr.author}`);
103910
+ sections.push(`- **Base**: ${pr.base} → **Head**: ${pr.head}`);
103911
+ sections.push(`- **Changes**: +${pr.totalAdditions} -${pr.totalDeletions}`);
103912
+ sections.push(`- **Files Modified**: ${templateContext.fileCount}`);
103913
+ }
103914
+ // Dependencies section
103915
+ if (includeDependencies && templateContext.dependencies) {
103916
+ const deps = templateContext.dependencies;
103917
+ sections.push('');
103918
+ sections.push('### Dependency Results');
103919
+ if (Object.keys(deps).length === 0) {
103920
+ sections.push('- No dependency results available');
103921
+ }
103922
+ else {
103923
+ for (const [checkName, result] of Object.entries(deps)) {
103924
+ sections.push(`- **${checkName}**: ${result.issueCount} issues, ${result.suggestionCount} suggestions`);
103925
+ }
103926
+ }
103927
+ }
103928
+ // Metadata section
103929
+ if (includeMetadata && templateContext.metadata) {
103930
+ const meta = templateContext.metadata;
103931
+ sections.push('');
103932
+ sections.push('### Execution Metadata');
103933
+ sections.push(`- **Timestamp**: ${meta.timestamp}`);
103934
+ sections.push(`- **Node Version**: ${meta.nodeVersion}`);
103935
+ sections.push(`- **Platform**: ${meta.platform}`);
103936
+ sections.push(`- **Working Directory**: ${meta.workingDirectory}`);
103937
+ }
103938
+ return sections.join('\n');
103939
+ }
103940
+ getLevelEmoji(level) {
103941
+ switch (level) {
103942
+ case 'debug':
103943
+ return '🐛';
103944
+ case 'info':
103945
+ return 'â„šī¸';
103946
+ case 'warn':
103947
+ return 'âš ī¸';
103948
+ case 'error':
103949
+ return '❌';
103950
+ default:
103951
+ return 'â„šī¸';
103952
+ }
103953
+ }
103954
+ getSupportedConfigKeys() {
103955
+ return [
103956
+ 'type',
103957
+ 'message',
103958
+ 'level',
103959
+ 'include_pr_context',
103960
+ 'include_dependencies',
103961
+ 'include_metadata',
103962
+ 'group',
103963
+ 'command',
103964
+ 'depends_on',
103965
+ 'on',
103966
+ 'if',
103967
+ ];
103968
+ }
103969
+ async isAvailable() {
103970
+ // Log provider is always available
103971
+ return true;
103972
+ }
103973
+ getRequirements() {
103974
+ return [
103975
+ 'No external dependencies required',
103976
+ 'Used for debugging and logging check execution flow',
103977
+ ];
103978
+ }
103979
+ }
103980
+ exports.LogCheckProvider = LogCheckProvider;
103981
+
103982
+
103558
103983
  /***/ }),
103559
103984
 
103560
103985
  /***/ 53003:
@@ -103597,7 +104022,6 @@ class NoopCheckProvider extends check_provider_interface_1.CheckProvider {
103597
104022
  // It exists purely for command orchestration and dependency triggering
103598
104023
  return {
103599
104024
  issues: [],
103600
- suggestions: [],
103601
104025
  };
103602
104026
  }
103603
104027
  getSupportedConfigKeys() {
@@ -103719,7 +104143,6 @@ class ToolCheckProvider extends check_provider_interface_1.CheckProvider {
103719
104143
  const filteredIssues = issueFilter.filterIssues(issues, process.cwd());
103720
104144
  return {
103721
104145
  issues: filteredIssues,
103722
- suggestions: this.generateSuggestions(comments, renderedCommand),
103723
104146
  };
103724
104147
  }
103725
104148
  async executeCommand(command, stdin) {
@@ -103774,17 +104197,6 @@ class ToolCheckProvider extends check_provider_interface_1.CheckProvider {
103774
104197
  }
103775
104198
  return comments;
103776
104199
  }
103777
- generateSuggestions(comments, command) {
103778
- const suggestions = [];
103779
- if (comments.length > 0) {
103780
- suggestions.push(`Fix ${comments.length} issues found by ${command}`);
103781
- const errorCount = comments.filter(c => c.severity === 'error').length;
103782
- if (errorCount > 0) {
103783
- suggestions.push(`Priority: Fix ${errorCount} errors before merging`);
103784
- }
103785
- }
103786
- return suggestions;
103787
- }
103788
104200
  getSupportedConfigKeys() {
103789
104201
  return ['type', 'exec', 'command', 'stdin', 'timeout', 'workingDirectory'];
103790
104202
  }
@@ -103855,7 +104267,7 @@ const ai_review_service_1 = __nccwpck_require__(51796);
103855
104267
  // Test utility function - Convert old ReviewSummary to new GroupedCheckResults format
103856
104268
  // This is for backward compatibility with tests only
103857
104269
  function convertReviewSummaryToGroupedResults(reviewSummary, checkName = 'test-check', groupName = 'default') {
103858
- // Create a simple content string from issues and suggestions
104270
+ // Create a simple content string from issues
103859
104271
  let content = '';
103860
104272
  if (reviewSummary.issues && reviewSummary.issues.length > 0) {
103861
104273
  content += `## Issues Found (${reviewSummary.issues.length})\n\n`;
@@ -103864,12 +104276,6 @@ function convertReviewSummaryToGroupedResults(reviewSummary, checkName = 'test-c
103864
104276
  });
103865
104277
  content += '\n';
103866
104278
  }
103867
- if (reviewSummary.suggestions && reviewSummary.suggestions.length > 0) {
103868
- content += `## Suggestions\n\n`;
103869
- reviewSummary.suggestions.forEach(suggestion => {
103870
- content += `- ${suggestion}\n`;
103871
- });
103872
- }
103873
104279
  if (!content) {
103874
104280
  content = 'No issues found.';
103875
104281
  }