@probelabs/visor 0.1.37 → 0.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +504 -58
- package/action.yml +10 -0
- package/dist/action-cli-bridge.d.ts +2 -0
- package/dist/action-cli-bridge.d.ts.map +1 -1
- package/dist/ai-review-service.d.ts +4 -0
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts +6 -1
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +510 -10
- package/dist/providers/ai-check-provider.d.ts +8 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +1 -7
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts +29 -0
- package/dist/providers/log-check-provider.d.ts.map +1 -0
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +22 -1
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -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: ${
|
|
95014
|
-
const reviewSummary = await this.executeReviewChecks(prInfo,
|
|
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:
|
|
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 =
|
|
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
|
}
|
|
@@ -96799,17 +96853,35 @@ async function main() {
|
|
|
96799
96853
|
logFn(`🤖 Executing checks: ${checksToRun.join(', ')}`);
|
|
96800
96854
|
// Create CheckExecutionEngine for running checks
|
|
96801
96855
|
const engine = new check_execution_engine_1.CheckExecutionEngine();
|
|
96856
|
+
// Build tag filter from CLI options
|
|
96857
|
+
const tagFilter = options.tags || options.excludeTags
|
|
96858
|
+
? {
|
|
96859
|
+
include: options.tags,
|
|
96860
|
+
exclude: options.excludeTags,
|
|
96861
|
+
}
|
|
96862
|
+
: undefined;
|
|
96802
96863
|
// Execute checks with proper parameters (cast to PRInfo)
|
|
96803
|
-
const groupedResults = await engine.executeGroupedChecks(repositoryInfo, checksToRun, options.timeout, config, options.output, options.debug || false);
|
|
96864
|
+
const groupedResults = await engine.executeGroupedChecks(repositoryInfo, checksToRun, options.timeout, config, options.output, options.debug || false, options.maxParallelism, options.failFast, tagFilter);
|
|
96804
96865
|
// Format output based on format type
|
|
96805
96866
|
let output;
|
|
96806
96867
|
if (options.output === 'json') {
|
|
96807
96868
|
output = JSON.stringify(groupedResults, null, 2);
|
|
96808
96869
|
}
|
|
96809
96870
|
else if (options.output === 'sarif') {
|
|
96810
|
-
//
|
|
96811
|
-
|
|
96812
|
-
|
|
96871
|
+
// Build analysis result and format as SARIF
|
|
96872
|
+
const analysisResult = {
|
|
96873
|
+
repositoryInfo,
|
|
96874
|
+
reviewSummary: {
|
|
96875
|
+
issues: Object.values(groupedResults)
|
|
96876
|
+
.flatMap((r) => r.map((check) => check.issues || []).flat())
|
|
96877
|
+
.flat(),
|
|
96878
|
+
suggestions: [],
|
|
96879
|
+
},
|
|
96880
|
+
executionTime: 0,
|
|
96881
|
+
timestamp: new Date().toISOString(),
|
|
96882
|
+
checksExecuted: checksToRun,
|
|
96883
|
+
};
|
|
96884
|
+
output = output_formatters_1.OutputFormatters.formatAsSarif(analysisResult);
|
|
96813
96885
|
}
|
|
96814
96886
|
else if (options.output === 'markdown') {
|
|
96815
96887
|
// Create analysis result for markdown formatting
|
|
@@ -96958,6 +97030,8 @@ class CLI {
|
|
|
96958
97030
|
.option('--max-parallelism <count>', 'Maximum number of checks to run in parallel (default: 3)', value => parseInt(value, 10))
|
|
96959
97031
|
.option('--debug', 'Enable debug mode for detailed output')
|
|
96960
97032
|
.option('--fail-fast', 'Stop execution on first failure condition')
|
|
97033
|
+
.option('--tags <tags>', 'Include checks with these tags (comma-separated)')
|
|
97034
|
+
.option('--exclude-tags <tags>', 'Exclude checks with these tags (comma-separated)')
|
|
96961
97035
|
.option('--no-remote-extends', 'Disable loading configurations from remote URLs')
|
|
96962
97036
|
.addHelpText('after', this.getExamplesText())
|
|
96963
97037
|
.exitOverride(); // Prevent automatic process.exit for better error handling
|
|
@@ -96993,6 +97067,8 @@ class CLI {
|
|
|
96993
97067
|
.option('--max-parallelism <count>', 'Maximum number of checks to run in parallel (default: 3)', value => parseInt(value, 10))
|
|
96994
97068
|
.option('--debug', 'Enable debug mode for detailed output')
|
|
96995
97069
|
.option('--fail-fast', 'Stop execution on first failure condition')
|
|
97070
|
+
.option('--tags <tags>', 'Include checks with these tags (comma-separated)')
|
|
97071
|
+
.option('--exclude-tags <tags>', 'Exclude checks with these tags (comma-separated)')
|
|
96996
97072
|
.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
97073
|
.allowUnknownOption(false)
|
|
96998
97074
|
.allowExcessArguments(false) // Don't allow positional arguments
|
|
@@ -97015,6 +97091,15 @@ class CLI {
|
|
|
97015
97091
|
.split(',')
|
|
97016
97092
|
.map((p) => p.trim());
|
|
97017
97093
|
}
|
|
97094
|
+
// Parse tag filters
|
|
97095
|
+
let tags;
|
|
97096
|
+
if (options.tags) {
|
|
97097
|
+
tags = options.tags.split(',').map((t) => t.trim());
|
|
97098
|
+
}
|
|
97099
|
+
let excludeTags;
|
|
97100
|
+
if (options.excludeTags) {
|
|
97101
|
+
excludeTags = options.excludeTags.split(',').map((t) => t.trim());
|
|
97102
|
+
}
|
|
97018
97103
|
return {
|
|
97019
97104
|
checks: uniqueChecks,
|
|
97020
97105
|
output: options.output,
|
|
@@ -97023,6 +97108,8 @@ class CLI {
|
|
|
97023
97108
|
maxParallelism: options.maxParallelism,
|
|
97024
97109
|
debug: options.debug,
|
|
97025
97110
|
failFast: options.failFast,
|
|
97111
|
+
tags,
|
|
97112
|
+
excludeTags,
|
|
97026
97113
|
allowedRemotePatterns,
|
|
97027
97114
|
help: options.help,
|
|
97028
97115
|
version: options.version,
|
|
@@ -97129,7 +97216,10 @@ Examples:
|
|
|
97129
97216
|
visor --check all --timeout 300000 --output json # 5 minute timeout
|
|
97130
97217
|
visor --check all --max-parallelism 5 --output json # Run up to 5 checks in parallel
|
|
97131
97218
|
visor --check all --debug --output markdown # Enable debug mode
|
|
97132
|
-
visor --check all --fail-fast --output json # Stop on first failure
|
|
97219
|
+
visor --check all --fail-fast --output json # Stop on first failure
|
|
97220
|
+
visor --tags local,fast --output table # Run checks tagged as 'local' or 'fast'
|
|
97221
|
+
visor --exclude-tags slow,experimental --output json # Skip checks tagged as 'slow' or 'experimental'
|
|
97222
|
+
visor --tags security --exclude-tags slow # Run security checks but skip slow ones`;
|
|
97133
97223
|
}
|
|
97134
97224
|
/**
|
|
97135
97225
|
* Display help
|
|
@@ -97270,6 +97360,7 @@ class ConfigManager {
|
|
|
97270
97360
|
'http_input',
|
|
97271
97361
|
'http_client',
|
|
97272
97362
|
'noop',
|
|
97363
|
+
'log',
|
|
97273
97364
|
];
|
|
97274
97365
|
validEventTriggers = [
|
|
97275
97366
|
'pr_opened',
|
|
@@ -97580,6 +97671,10 @@ class ConfigManager {
|
|
|
97580
97671
|
});
|
|
97581
97672
|
}
|
|
97582
97673
|
}
|
|
97674
|
+
// Validate tag_filter if present
|
|
97675
|
+
if (config.tag_filter) {
|
|
97676
|
+
this.validateTagFilter(config.tag_filter, errors);
|
|
97677
|
+
}
|
|
97583
97678
|
if (errors.length > 0) {
|
|
97584
97679
|
throw new Error(errors[0].message);
|
|
97585
97680
|
}
|
|
@@ -97694,6 +97789,98 @@ class ConfigManager {
|
|
|
97694
97789
|
}
|
|
97695
97790
|
}
|
|
97696
97791
|
}
|
|
97792
|
+
// Validate tags configuration
|
|
97793
|
+
if (checkConfig.tags !== undefined) {
|
|
97794
|
+
if (!Array.isArray(checkConfig.tags)) {
|
|
97795
|
+
errors.push({
|
|
97796
|
+
field: `checks.${checkName}.tags`,
|
|
97797
|
+
message: `Invalid tags value for "${checkName}": must be an array of strings`,
|
|
97798
|
+
value: checkConfig.tags,
|
|
97799
|
+
});
|
|
97800
|
+
}
|
|
97801
|
+
else {
|
|
97802
|
+
// Validate each tag
|
|
97803
|
+
const validTagPattern = /^[a-zA-Z0-9][a-zA-Z0-9-_]*$/;
|
|
97804
|
+
checkConfig.tags.forEach((tag, index) => {
|
|
97805
|
+
if (typeof tag !== 'string') {
|
|
97806
|
+
errors.push({
|
|
97807
|
+
field: `checks.${checkName}.tags[${index}]`,
|
|
97808
|
+
message: `Invalid tag at index ${index} for "${checkName}": must be a string`,
|
|
97809
|
+
value: tag,
|
|
97810
|
+
});
|
|
97811
|
+
}
|
|
97812
|
+
else if (!validTagPattern.test(tag)) {
|
|
97813
|
+
errors.push({
|
|
97814
|
+
field: `checks.${checkName}.tags[${index}]`,
|
|
97815
|
+
message: `Invalid tag "${tag}" for "${checkName}": tags must be alphanumeric with hyphens or underscores (start with alphanumeric)`,
|
|
97816
|
+
value: tag,
|
|
97817
|
+
});
|
|
97818
|
+
}
|
|
97819
|
+
});
|
|
97820
|
+
}
|
|
97821
|
+
}
|
|
97822
|
+
}
|
|
97823
|
+
/**
|
|
97824
|
+
* Validate tag filter configuration
|
|
97825
|
+
*/
|
|
97826
|
+
validateTagFilter(tagFilter, errors) {
|
|
97827
|
+
const validTagPattern = /^[a-zA-Z0-9][a-zA-Z0-9-_]*$/;
|
|
97828
|
+
// Validate include tags
|
|
97829
|
+
if (tagFilter.include !== undefined) {
|
|
97830
|
+
if (!Array.isArray(tagFilter.include)) {
|
|
97831
|
+
errors.push({
|
|
97832
|
+
field: 'tag_filter.include',
|
|
97833
|
+
message: 'tag_filter.include must be an array of strings',
|
|
97834
|
+
value: tagFilter.include,
|
|
97835
|
+
});
|
|
97836
|
+
}
|
|
97837
|
+
else {
|
|
97838
|
+
tagFilter.include.forEach((tag, index) => {
|
|
97839
|
+
if (typeof tag !== 'string') {
|
|
97840
|
+
errors.push({
|
|
97841
|
+
field: `tag_filter.include[${index}]`,
|
|
97842
|
+
message: `Invalid tag at index ${index}: must be a string`,
|
|
97843
|
+
value: tag,
|
|
97844
|
+
});
|
|
97845
|
+
}
|
|
97846
|
+
else if (!validTagPattern.test(tag)) {
|
|
97847
|
+
errors.push({
|
|
97848
|
+
field: `tag_filter.include[${index}]`,
|
|
97849
|
+
message: `Invalid tag "${tag}": tags must be alphanumeric with hyphens or underscores`,
|
|
97850
|
+
value: tag,
|
|
97851
|
+
});
|
|
97852
|
+
}
|
|
97853
|
+
});
|
|
97854
|
+
}
|
|
97855
|
+
}
|
|
97856
|
+
// Validate exclude tags
|
|
97857
|
+
if (tagFilter.exclude !== undefined) {
|
|
97858
|
+
if (!Array.isArray(tagFilter.exclude)) {
|
|
97859
|
+
errors.push({
|
|
97860
|
+
field: 'tag_filter.exclude',
|
|
97861
|
+
message: 'tag_filter.exclude must be an array of strings',
|
|
97862
|
+
value: tagFilter.exclude,
|
|
97863
|
+
});
|
|
97864
|
+
}
|
|
97865
|
+
else {
|
|
97866
|
+
tagFilter.exclude.forEach((tag, index) => {
|
|
97867
|
+
if (typeof tag !== 'string') {
|
|
97868
|
+
errors.push({
|
|
97869
|
+
field: `tag_filter.exclude[${index}]`,
|
|
97870
|
+
message: `Invalid tag at index ${index}: must be a string`,
|
|
97871
|
+
value: tag,
|
|
97872
|
+
});
|
|
97873
|
+
}
|
|
97874
|
+
else if (!validTagPattern.test(tag)) {
|
|
97875
|
+
errors.push({
|
|
97876
|
+
field: `tag_filter.exclude[${index}]`,
|
|
97877
|
+
message: `Invalid tag "${tag}": tags must be alphanumeric with hyphens or underscores`,
|
|
97878
|
+
value: tag,
|
|
97879
|
+
});
|
|
97880
|
+
}
|
|
97881
|
+
});
|
|
97882
|
+
}
|
|
97883
|
+
}
|
|
97697
97884
|
}
|
|
97698
97885
|
/**
|
|
97699
97886
|
* Validate HTTP server configuration
|
|
@@ -99612,6 +99799,9 @@ async function run() {
|
|
|
99612
99799
|
'max-parallelism': (0, core_1.getInput)('max-parallelism') || undefined,
|
|
99613
99800
|
'ai-provider': (0, core_1.getInput)('ai-provider') || undefined,
|
|
99614
99801
|
'ai-model': (0, core_1.getInput)('ai-model') || undefined,
|
|
99802
|
+
// Tag filtering inputs
|
|
99803
|
+
tags: (0, core_1.getInput)('tags') || undefined,
|
|
99804
|
+
'exclude-tags': (0, core_1.getInput)('exclude-tags') || undefined,
|
|
99615
99805
|
// Legacy inputs for backward compatibility
|
|
99616
99806
|
'visor-config-path': (0, core_1.getInput)('visor-config-path') || undefined,
|
|
99617
99807
|
'visor-checks': (0, core_1.getInput)('visor-checks') || undefined,
|
|
@@ -101683,6 +101873,7 @@ const issue_filter_1 = __nccwpck_require__(36879);
|
|
|
101683
101873
|
const liquidjs_1 = __nccwpck_require__(48694);
|
|
101684
101874
|
const promises_1 = __importDefault(__nccwpck_require__(91943));
|
|
101685
101875
|
const path_1 = __importDefault(__nccwpck_require__(16928));
|
|
101876
|
+
const claude_code_types_1 = __nccwpck_require__(21710);
|
|
101686
101877
|
/**
|
|
101687
101878
|
* AI-powered check provider using probe agent
|
|
101688
101879
|
*/
|
|
@@ -101724,6 +101915,39 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
|
|
|
101724
101915
|
!['google', 'anthropic', 'openai', 'mock'].includes(cfg.ai.provider)) {
|
|
101725
101916
|
return false;
|
|
101726
101917
|
}
|
|
101918
|
+
// Validate mcpServers if present
|
|
101919
|
+
if (cfg.ai.mcpServers) {
|
|
101920
|
+
if (!this.validateMcpServers(cfg.ai.mcpServers)) {
|
|
101921
|
+
return false;
|
|
101922
|
+
}
|
|
101923
|
+
}
|
|
101924
|
+
}
|
|
101925
|
+
// Validate check-level MCP servers if present
|
|
101926
|
+
if (cfg.ai_mcp_servers) {
|
|
101927
|
+
if (!this.validateMcpServers(cfg.ai_mcp_servers)) {
|
|
101928
|
+
return false;
|
|
101929
|
+
}
|
|
101930
|
+
}
|
|
101931
|
+
return true;
|
|
101932
|
+
}
|
|
101933
|
+
/**
|
|
101934
|
+
* Validate MCP servers configuration
|
|
101935
|
+
*/
|
|
101936
|
+
validateMcpServers(mcpServers) {
|
|
101937
|
+
if (typeof mcpServers !== 'object' || mcpServers === null) {
|
|
101938
|
+
return false;
|
|
101939
|
+
}
|
|
101940
|
+
for (const serverConfig of Object.values(mcpServers)) {
|
|
101941
|
+
if (!serverConfig || typeof serverConfig !== 'object') {
|
|
101942
|
+
return false;
|
|
101943
|
+
}
|
|
101944
|
+
const config = serverConfig;
|
|
101945
|
+
if (!config.command || typeof config.command !== 'string') {
|
|
101946
|
+
return false;
|
|
101947
|
+
}
|
|
101948
|
+
if (config.args && !Array.isArray(config.args)) {
|
|
101949
|
+
return false;
|
|
101950
|
+
}
|
|
101727
101951
|
}
|
|
101728
101952
|
return true;
|
|
101729
101953
|
}
|
|
@@ -101990,6 +102214,53 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
|
|
|
101990
102214
|
throw new Error(`Failed to render prompt template: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
101991
102215
|
}
|
|
101992
102216
|
}
|
|
102217
|
+
/**
|
|
102218
|
+
* Setup MCP tools based on AI configuration
|
|
102219
|
+
*/
|
|
102220
|
+
async setupMcpTools(aiConfig) {
|
|
102221
|
+
const tools = [];
|
|
102222
|
+
// Setup custom MCP servers if configured
|
|
102223
|
+
if (aiConfig.mcpServers) {
|
|
102224
|
+
try {
|
|
102225
|
+
// Import MCP SDK for custom server creation using safe import
|
|
102226
|
+
const mcpModule = await (0, claude_code_types_1.safeImport)('@modelcontextprotocol/sdk');
|
|
102227
|
+
if (!mcpModule) {
|
|
102228
|
+
console.warn('@modelcontextprotocol/sdk package not found. MCP servers disabled.');
|
|
102229
|
+
return tools;
|
|
102230
|
+
}
|
|
102231
|
+
const createSdkMcpServer = mcpModule.createSdkMcpServer || mcpModule.default?.createSdkMcpServer;
|
|
102232
|
+
if (createSdkMcpServer) {
|
|
102233
|
+
for (const [serverName, serverConfig] of Object.entries(aiConfig.mcpServers)) {
|
|
102234
|
+
try {
|
|
102235
|
+
// Create MCP server instance
|
|
102236
|
+
const server = await createSdkMcpServer({
|
|
102237
|
+
name: serverName,
|
|
102238
|
+
command: serverConfig.command,
|
|
102239
|
+
args: serverConfig.args || [],
|
|
102240
|
+
env: { ...process.env, ...serverConfig.env },
|
|
102241
|
+
});
|
|
102242
|
+
// Add server tools to available tools
|
|
102243
|
+
const serverTools = await server.listTools();
|
|
102244
|
+
tools.push(...serverTools.map((tool) => ({
|
|
102245
|
+
name: tool.name,
|
|
102246
|
+
server: serverName,
|
|
102247
|
+
})));
|
|
102248
|
+
}
|
|
102249
|
+
catch (serverError) {
|
|
102250
|
+
console.warn(`Failed to setup MCP server ${serverName}: ${serverError instanceof Error ? serverError.message : 'Unknown error'}`);
|
|
102251
|
+
}
|
|
102252
|
+
}
|
|
102253
|
+
}
|
|
102254
|
+
else {
|
|
102255
|
+
console.warn('createSdkMcpServer function not found in @modelcontextprotocol/sdk. MCP servers disabled.');
|
|
102256
|
+
}
|
|
102257
|
+
}
|
|
102258
|
+
catch (error) {
|
|
102259
|
+
console.warn(`Failed to import MCP SDK: ${error instanceof Error ? error.message : 'Unknown error'}. MCP servers disabled.`);
|
|
102260
|
+
}
|
|
102261
|
+
}
|
|
102262
|
+
return tools;
|
|
102263
|
+
}
|
|
101993
102264
|
async execute(prInfo, config, _dependencyResults, sessionInfo) {
|
|
101994
102265
|
// Apply environment configuration if present
|
|
101995
102266
|
if (config.env) {
|
|
@@ -102038,6 +102309,32 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
|
|
|
102038
102309
|
if (!customPrompt) {
|
|
102039
102310
|
throw new Error(`No prompt defined for check. All checks must have prompts defined in .visor.yaml configuration.`);
|
|
102040
102311
|
}
|
|
102312
|
+
// Setup MCP tools from multiple configuration levels
|
|
102313
|
+
const mcpServers = {};
|
|
102314
|
+
// 1. Start with global MCP servers (from visor config root)
|
|
102315
|
+
const globalConfig = config; // Cast to access potential global config
|
|
102316
|
+
if (globalConfig.ai_mcp_servers) {
|
|
102317
|
+
Object.assign(mcpServers, globalConfig.ai_mcp_servers);
|
|
102318
|
+
}
|
|
102319
|
+
// 2. Add check-level MCP servers (overrides global)
|
|
102320
|
+
if (config.ai_mcp_servers) {
|
|
102321
|
+
Object.assign(mcpServers, config.ai_mcp_servers);
|
|
102322
|
+
}
|
|
102323
|
+
// 3. Add ai.mcpServers (overrides everything)
|
|
102324
|
+
if (config.ai?.mcpServers) {
|
|
102325
|
+
Object.assign(mcpServers, config.ai.mcpServers);
|
|
102326
|
+
}
|
|
102327
|
+
// Setup MCP tools if any servers are configured
|
|
102328
|
+
if (Object.keys(mcpServers).length > 0) {
|
|
102329
|
+
const mcpConfig = { mcpServers };
|
|
102330
|
+
const mcpTools = await this.setupMcpTools(mcpConfig);
|
|
102331
|
+
if (mcpTools.length > 0) {
|
|
102332
|
+
aiConfig.tools = mcpTools;
|
|
102333
|
+
if (aiConfig.debug) {
|
|
102334
|
+
console.error(`🔧 Debug: AI check configured with ${mcpTools.length} MCP tools from ${Object.keys(mcpServers).length} servers`);
|
|
102335
|
+
}
|
|
102336
|
+
}
|
|
102337
|
+
}
|
|
102041
102338
|
// Process prompt with Liquid templates and file loading
|
|
102042
102339
|
const processedPrompt = await this.processPrompt(customPrompt, prInfo, config.eventContext, _dependencyResults);
|
|
102043
102340
|
// Create AI service with config - environment variables will be used if aiConfig is empty
|
|
@@ -102106,8 +102403,10 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
|
|
|
102106
102403
|
'ai.model',
|
|
102107
102404
|
'ai.apiKey',
|
|
102108
102405
|
'ai.timeout',
|
|
102406
|
+
'ai.mcpServers',
|
|
102109
102407
|
'ai_model',
|
|
102110
102408
|
'ai_provider',
|
|
102409
|
+
'ai_mcp_servers',
|
|
102111
102410
|
'env',
|
|
102112
102411
|
];
|
|
102113
102412
|
}
|
|
@@ -102143,6 +102442,7 @@ const http_check_provider_1 = __nccwpck_require__(31115);
|
|
|
102143
102442
|
const http_input_provider_1 = __nccwpck_require__(74423);
|
|
102144
102443
|
const http_client_provider_1 = __nccwpck_require__(36270);
|
|
102145
102444
|
const noop_check_provider_1 = __nccwpck_require__(53003);
|
|
102445
|
+
const log_check_provider_1 = __nccwpck_require__(24903);
|
|
102146
102446
|
const claude_code_check_provider_1 = __nccwpck_require__(17985);
|
|
102147
102447
|
/**
|
|
102148
102448
|
* Registry for managing check providers
|
|
@@ -102174,6 +102474,7 @@ class CheckProviderRegistry {
|
|
|
102174
102474
|
this.register(new http_input_provider_1.HttpInputProvider());
|
|
102175
102475
|
this.register(new http_client_provider_1.HttpClientProvider());
|
|
102176
102476
|
this.register(new noop_check_provider_1.NoopCheckProvider());
|
|
102477
|
+
this.register(new log_check_provider_1.LogCheckProvider());
|
|
102177
102478
|
// Try to register ClaudeCodeCheckProvider - it may fail if dependencies are missing
|
|
102178
102479
|
try {
|
|
102179
102480
|
this.register(new claude_code_check_provider_1.ClaudeCodeCheckProvider());
|
|
@@ -103555,6 +103856,205 @@ class HttpInputProvider extends check_provider_interface_1.CheckProvider {
|
|
|
103555
103856
|
exports.HttpInputProvider = HttpInputProvider;
|
|
103556
103857
|
|
|
103557
103858
|
|
|
103859
|
+
/***/ }),
|
|
103860
|
+
|
|
103861
|
+
/***/ 24903:
|
|
103862
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
103863
|
+
|
|
103864
|
+
"use strict";
|
|
103865
|
+
|
|
103866
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
103867
|
+
exports.LogCheckProvider = void 0;
|
|
103868
|
+
const check_provider_interface_1 = __nccwpck_require__(14131);
|
|
103869
|
+
const liquidjs_1 = __nccwpck_require__(48694);
|
|
103870
|
+
/**
|
|
103871
|
+
* Check provider that outputs debugging and logging information.
|
|
103872
|
+
* Useful for troubleshooting check workflows and understanding execution flow.
|
|
103873
|
+
*/
|
|
103874
|
+
class LogCheckProvider extends check_provider_interface_1.CheckProvider {
|
|
103875
|
+
liquid;
|
|
103876
|
+
constructor() {
|
|
103877
|
+
super();
|
|
103878
|
+
this.liquid = new liquidjs_1.Liquid({
|
|
103879
|
+
strictVariables: false,
|
|
103880
|
+
strictFilters: false,
|
|
103881
|
+
});
|
|
103882
|
+
}
|
|
103883
|
+
getName() {
|
|
103884
|
+
return 'log';
|
|
103885
|
+
}
|
|
103886
|
+
getDescription() {
|
|
103887
|
+
return 'Output debugging and logging information for troubleshooting check workflows';
|
|
103888
|
+
}
|
|
103889
|
+
async validateConfig(config) {
|
|
103890
|
+
if (!config || typeof config !== 'object') {
|
|
103891
|
+
return false;
|
|
103892
|
+
}
|
|
103893
|
+
const cfg = config;
|
|
103894
|
+
// Type must be 'log'
|
|
103895
|
+
if (cfg.type !== 'log') {
|
|
103896
|
+
return false;
|
|
103897
|
+
}
|
|
103898
|
+
// Message is required
|
|
103899
|
+
if (!cfg.message || typeof cfg.message !== 'string') {
|
|
103900
|
+
return false;
|
|
103901
|
+
}
|
|
103902
|
+
// Validate log level if provided
|
|
103903
|
+
if (cfg.level && !['debug', 'info', 'warn', 'error'].includes(cfg.level)) {
|
|
103904
|
+
return false;
|
|
103905
|
+
}
|
|
103906
|
+
return true;
|
|
103907
|
+
}
|
|
103908
|
+
async execute(prInfo, config, dependencyResults, _sessionInfo) {
|
|
103909
|
+
const message = config.message;
|
|
103910
|
+
const level = config.level || 'info';
|
|
103911
|
+
const includePrContext = config.include_pr_context !== false;
|
|
103912
|
+
const includeDependencies = config.include_dependencies !== false;
|
|
103913
|
+
const includeMetadata = config.include_metadata !== false;
|
|
103914
|
+
// Prepare template context
|
|
103915
|
+
const templateContext = this.buildTemplateContext(prInfo, dependencyResults, includePrContext, includeDependencies, includeMetadata);
|
|
103916
|
+
// Render the log message template
|
|
103917
|
+
const renderedMessage = await this.liquid.parseAndRender(message, templateContext);
|
|
103918
|
+
// Build the log output
|
|
103919
|
+
const logOutput = this.formatLogOutput(level, renderedMessage, templateContext, includePrContext, includeDependencies, includeMetadata);
|
|
103920
|
+
return {
|
|
103921
|
+
issues: [], // Log provider doesn't generate issues
|
|
103922
|
+
suggestions: [logOutput], // Put formatted log output as the primary suggestion
|
|
103923
|
+
};
|
|
103924
|
+
}
|
|
103925
|
+
buildTemplateContext(prInfo, dependencyResults, includePrContext = true, includeDependencies = true, includeMetadata = true) {
|
|
103926
|
+
const context = {};
|
|
103927
|
+
if (includePrContext) {
|
|
103928
|
+
context.pr = {
|
|
103929
|
+
number: prInfo.number,
|
|
103930
|
+
title: prInfo.title,
|
|
103931
|
+
body: prInfo.body,
|
|
103932
|
+
author: prInfo.author,
|
|
103933
|
+
base: prInfo.base,
|
|
103934
|
+
head: prInfo.head,
|
|
103935
|
+
totalAdditions: prInfo.totalAdditions,
|
|
103936
|
+
totalDeletions: prInfo.totalDeletions,
|
|
103937
|
+
files: prInfo.files.map(f => ({
|
|
103938
|
+
filename: f.filename,
|
|
103939
|
+
status: f.status,
|
|
103940
|
+
additions: f.additions,
|
|
103941
|
+
deletions: f.deletions,
|
|
103942
|
+
changes: f.changes,
|
|
103943
|
+
})),
|
|
103944
|
+
};
|
|
103945
|
+
// Add convenience data
|
|
103946
|
+
context.filenames = prInfo.files.map(f => f.filename);
|
|
103947
|
+
context.fileCount = prInfo.files.length;
|
|
103948
|
+
}
|
|
103949
|
+
if (includeDependencies && dependencyResults) {
|
|
103950
|
+
const dependencies = {};
|
|
103951
|
+
context.dependencyCount = dependencyResults.size;
|
|
103952
|
+
for (const [checkName, result] of dependencyResults.entries()) {
|
|
103953
|
+
dependencies[checkName] = {
|
|
103954
|
+
issueCount: result.issues?.length || 0,
|
|
103955
|
+
suggestionCount: result.suggestions?.length || 0,
|
|
103956
|
+
issues: result.issues || [],
|
|
103957
|
+
suggestions: result.suggestions || [],
|
|
103958
|
+
};
|
|
103959
|
+
}
|
|
103960
|
+
context.dependencies = dependencies;
|
|
103961
|
+
}
|
|
103962
|
+
if (includeMetadata) {
|
|
103963
|
+
context.metadata = {
|
|
103964
|
+
timestamp: new Date().toISOString(),
|
|
103965
|
+
executionTime: Date.now(),
|
|
103966
|
+
nodeVersion: process.version,
|
|
103967
|
+
platform: process.platform,
|
|
103968
|
+
workingDirectory: process.cwd(),
|
|
103969
|
+
};
|
|
103970
|
+
}
|
|
103971
|
+
return context;
|
|
103972
|
+
}
|
|
103973
|
+
formatLogOutput(level, message, templateContext, includePrContext, includeDependencies, includeMetadata) {
|
|
103974
|
+
const sections = [];
|
|
103975
|
+
// Log level and message
|
|
103976
|
+
const levelEmoji = this.getLevelEmoji(level);
|
|
103977
|
+
sections.push(`${levelEmoji} **${level.toUpperCase()}**: ${message}`);
|
|
103978
|
+
// PR context section
|
|
103979
|
+
if (includePrContext && templateContext.pr) {
|
|
103980
|
+
const pr = templateContext.pr;
|
|
103981
|
+
sections.push('');
|
|
103982
|
+
sections.push('### PR Context');
|
|
103983
|
+
sections.push(`- **PR #${pr.number}**: ${pr.title}`);
|
|
103984
|
+
sections.push(`- **Author**: ${pr.author}`);
|
|
103985
|
+
sections.push(`- **Base**: ${pr.base} → **Head**: ${pr.head}`);
|
|
103986
|
+
sections.push(`- **Changes**: +${pr.totalAdditions} -${pr.totalDeletions}`);
|
|
103987
|
+
sections.push(`- **Files Modified**: ${templateContext.fileCount}`);
|
|
103988
|
+
}
|
|
103989
|
+
// Dependencies section
|
|
103990
|
+
if (includeDependencies && templateContext.dependencies) {
|
|
103991
|
+
const deps = templateContext.dependencies;
|
|
103992
|
+
sections.push('');
|
|
103993
|
+
sections.push('### Dependency Results');
|
|
103994
|
+
if (Object.keys(deps).length === 0) {
|
|
103995
|
+
sections.push('- No dependency results available');
|
|
103996
|
+
}
|
|
103997
|
+
else {
|
|
103998
|
+
for (const [checkName, result] of Object.entries(deps)) {
|
|
103999
|
+
sections.push(`- **${checkName}**: ${result.issueCount} issues, ${result.suggestionCount} suggestions`);
|
|
104000
|
+
}
|
|
104001
|
+
}
|
|
104002
|
+
}
|
|
104003
|
+
// Metadata section
|
|
104004
|
+
if (includeMetadata && templateContext.metadata) {
|
|
104005
|
+
const meta = templateContext.metadata;
|
|
104006
|
+
sections.push('');
|
|
104007
|
+
sections.push('### Execution Metadata');
|
|
104008
|
+
sections.push(`- **Timestamp**: ${meta.timestamp}`);
|
|
104009
|
+
sections.push(`- **Node Version**: ${meta.nodeVersion}`);
|
|
104010
|
+
sections.push(`- **Platform**: ${meta.platform}`);
|
|
104011
|
+
sections.push(`- **Working Directory**: ${meta.workingDirectory}`);
|
|
104012
|
+
}
|
|
104013
|
+
return sections.join('\n');
|
|
104014
|
+
}
|
|
104015
|
+
getLevelEmoji(level) {
|
|
104016
|
+
switch (level) {
|
|
104017
|
+
case 'debug':
|
|
104018
|
+
return '🐛';
|
|
104019
|
+
case 'info':
|
|
104020
|
+
return 'ℹ️';
|
|
104021
|
+
case 'warn':
|
|
104022
|
+
return '⚠️';
|
|
104023
|
+
case 'error':
|
|
104024
|
+
return '❌';
|
|
104025
|
+
default:
|
|
104026
|
+
return 'ℹ️';
|
|
104027
|
+
}
|
|
104028
|
+
}
|
|
104029
|
+
getSupportedConfigKeys() {
|
|
104030
|
+
return [
|
|
104031
|
+
'type',
|
|
104032
|
+
'message',
|
|
104033
|
+
'level',
|
|
104034
|
+
'include_pr_context',
|
|
104035
|
+
'include_dependencies',
|
|
104036
|
+
'include_metadata',
|
|
104037
|
+
'group',
|
|
104038
|
+
'command',
|
|
104039
|
+
'depends_on',
|
|
104040
|
+
'on',
|
|
104041
|
+
'if',
|
|
104042
|
+
];
|
|
104043
|
+
}
|
|
104044
|
+
async isAvailable() {
|
|
104045
|
+
// Log provider is always available
|
|
104046
|
+
return true;
|
|
104047
|
+
}
|
|
104048
|
+
getRequirements() {
|
|
104049
|
+
return [
|
|
104050
|
+
'No external dependencies required',
|
|
104051
|
+
'Used for debugging and logging check execution flow',
|
|
104052
|
+
];
|
|
104053
|
+
}
|
|
104054
|
+
}
|
|
104055
|
+
exports.LogCheckProvider = LogCheckProvider;
|
|
104056
|
+
|
|
104057
|
+
|
|
103558
104058
|
/***/ }),
|
|
103559
104059
|
|
|
103560
104060
|
/***/ 53003:
|