@probelabs/visor 0.1.17 → 0.1.18
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 +78 -0
- package/defaults/.visor.yaml +53 -1
- package/dist/ai-review-service.js +2 -2
- package/dist/ai-review-service.js.map +1 -1
- package/dist/check-execution-engine.d.ts +32 -1
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/check-execution-engine.js +289 -19
- package/dist/check-execution-engine.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -3
- package/dist/config.js.map +1 -1
- package/dist/event-mapper.d.ts.map +1 -1
- package/dist/event-mapper.js +1 -1
- package/dist/event-mapper.js.map +1 -1
- package/dist/failure-condition-evaluator.d.ts +8 -0
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/failure-condition-evaluator.js +25 -3
- package/dist/failure-condition-evaluator.js.map +1 -1
- package/dist/github-check-service.d.ts.map +1 -1
- package/dist/github-check-service.js +26 -39
- package/dist/github-check-service.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +331 -690
- package/dist/index.js.map +1 -1
- package/dist/licenses.txt +2300 -0
- package/dist/output/code-review/schema.json +84 -0
- package/dist/output/code-review/template.liquid +32 -0
- package/dist/output/plain/schema.json +14 -0
- package/dist/output/plain/template.liquid +1 -0
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/output-formatters.js +14 -14
- package/dist/output-formatters.js.map +1 -1
- package/dist/pr-analyzer.d.ts +2 -1
- package/dist/pr-analyzer.d.ts.map +1 -1
- package/dist/pr-analyzer.js +2 -1
- package/dist/pr-analyzer.js.map +1 -1
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/ai-check-provider.js +4 -0
- package/dist/providers/ai-check-provider.js.map +1 -1
- package/dist/providers/check-provider-registry.js +2 -2
- package/dist/providers/check-provider-registry.js.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +2 -0
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -3
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/noop-check-provider.d.ts +25 -0
- package/dist/providers/noop-check-provider.d.ts.map +1 -0
- package/dist/providers/noop-check-provider.js +55 -0
- package/dist/providers/noop-check-provider.js.map +1 -0
- package/dist/providers/tool-check-provider.d.ts +4 -1
- package/dist/providers/tool-check-provider.d.ts.map +1 -1
- package/dist/providers/tool-check-provider.js +64 -15
- package/dist/providers/tool-check-provider.js.map +1 -1
- package/dist/reviewer.d.ts +19 -37
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/reviewer.js +83 -593
- package/dist/reviewer.js.map +1 -1
- package/dist/tiktoken_bg.wasm +0 -0
- package/dist/types/config.d.ts +52 -5
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/providers/script-check-provider.d.ts +0 -23
- package/dist/providers/script-check-provider.d.ts.map +0 -1
- package/dist/providers/script-check-provider.js +0 -163
- package/dist/providers/script-check-provider.js.map +0 -1
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.CheckExecutionEngine = void 0;
|
|
4
37
|
const reviewer_1 = require("./reviewer");
|
|
@@ -226,7 +259,7 @@ class CheckExecutionEngine {
|
|
|
226
259
|
};
|
|
227
260
|
const result = await provider.execute(prInfo, providerConfig);
|
|
228
261
|
// Prefix issues with check name for consistent grouping
|
|
229
|
-
const prefixedIssues = result.issues.map(issue => ({
|
|
262
|
+
const prefixedIssues = (result.issues || []).map(issue => ({
|
|
230
263
|
...issue,
|
|
231
264
|
ruleId: `${checks[0]}/${issue.ruleId}`,
|
|
232
265
|
}));
|
|
@@ -263,7 +296,7 @@ class CheckExecutionEngine {
|
|
|
263
296
|
};
|
|
264
297
|
const result = await provider.execute(prInfo, providerConfig);
|
|
265
298
|
// Prefix issues with check name for consistent grouping
|
|
266
|
-
const prefixedIssues = result.issues.map(issue => ({
|
|
299
|
+
const prefixedIssues = (result.issues || []).map(issue => ({
|
|
267
300
|
...issue,
|
|
268
301
|
ruleId: `${checkName}/${issue.ruleId}`,
|
|
269
302
|
}));
|
|
@@ -290,6 +323,243 @@ class CheckExecutionEngine {
|
|
|
290
323
|
format: 'table',
|
|
291
324
|
});
|
|
292
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Execute review checks and return grouped results for new architecture
|
|
328
|
+
*/
|
|
329
|
+
async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast) {
|
|
330
|
+
// Determine where to send log messages based on output format
|
|
331
|
+
const logFn = outputFormat === 'json' || outputFormat === 'sarif' ? console.error : console.log;
|
|
332
|
+
logFn(`🔧 Debug: executeGroupedChecks called with checks: ${JSON.stringify(checks)}`);
|
|
333
|
+
logFn(`🔧 Debug: Config available: ${!!config}, Config has checks: ${!!config?.checks}`);
|
|
334
|
+
// Filter checks based on current event type to prevent execution of checks that shouldn't run
|
|
335
|
+
const filteredChecks = this.filterChecksByEvent(checks, config, prInfo, logFn);
|
|
336
|
+
if (filteredChecks.length !== checks.length) {
|
|
337
|
+
logFn(`🔧 Debug: Event filtering reduced checks from ${checks.length} to ${filteredChecks.length}: ${JSON.stringify(filteredChecks)}`);
|
|
338
|
+
}
|
|
339
|
+
// Use filtered checks for execution
|
|
340
|
+
checks = filteredChecks;
|
|
341
|
+
if (!config?.checks) {
|
|
342
|
+
throw new Error('Config with check definitions required for grouped execution');
|
|
343
|
+
}
|
|
344
|
+
// If we have a config with individual check definitions, use dependency-aware execution
|
|
345
|
+
const hasDependencies = checks.some(checkName => {
|
|
346
|
+
const checkConfig = config.checks[checkName];
|
|
347
|
+
return checkConfig?.depends_on && checkConfig.depends_on.length > 0;
|
|
348
|
+
});
|
|
349
|
+
if (checks.length > 1 || hasDependencies) {
|
|
350
|
+
logFn(`🔧 Debug: Using grouped dependency-aware execution for ${checks.length} checks (has dependencies: ${hasDependencies})`);
|
|
351
|
+
return await this.executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast);
|
|
352
|
+
}
|
|
353
|
+
// Single check execution
|
|
354
|
+
if (checks.length === 1) {
|
|
355
|
+
logFn(`🔧 Debug: Using grouped single check execution for: ${checks[0]}`);
|
|
356
|
+
const checkResult = await this.executeSingleGroupedCheck(prInfo, checks[0], timeout, config, logFn, debug);
|
|
357
|
+
const groupedResults = {};
|
|
358
|
+
groupedResults[checkResult.group] = [checkResult];
|
|
359
|
+
return groupedResults;
|
|
360
|
+
}
|
|
361
|
+
// No checks to execute
|
|
362
|
+
return {};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Execute single check and return grouped result
|
|
366
|
+
*/
|
|
367
|
+
async executeSingleGroupedCheck(prInfo, checkName, timeout, config, logFn, debug) {
|
|
368
|
+
if (!config?.checks?.[checkName]) {
|
|
369
|
+
throw new Error(`No configuration found for check: ${checkName}`);
|
|
370
|
+
}
|
|
371
|
+
const checkConfig = config.checks[checkName];
|
|
372
|
+
const provider = this.providerRegistry.getProviderOrThrow('ai');
|
|
373
|
+
const providerConfig = {
|
|
374
|
+
type: 'ai',
|
|
375
|
+
prompt: checkConfig.prompt,
|
|
376
|
+
focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
|
|
377
|
+
schema: checkConfig.schema,
|
|
378
|
+
group: checkConfig.group,
|
|
379
|
+
ai: {
|
|
380
|
+
timeout: timeout || 600000,
|
|
381
|
+
debug: debug,
|
|
382
|
+
...(checkConfig.ai || {}),
|
|
383
|
+
},
|
|
384
|
+
ai_provider: checkConfig.ai_provider || config.ai_provider,
|
|
385
|
+
ai_model: checkConfig.ai_model || config.ai_model,
|
|
386
|
+
};
|
|
387
|
+
const result = await provider.execute(prInfo, providerConfig);
|
|
388
|
+
// Render the check content using the appropriate template
|
|
389
|
+
const content = await this.renderCheckContent(checkName, result, checkConfig, prInfo);
|
|
390
|
+
return {
|
|
391
|
+
checkName,
|
|
392
|
+
content,
|
|
393
|
+
group: checkConfig.group || 'default',
|
|
394
|
+
debug: result.debug,
|
|
395
|
+
issues: result.issues, // Include structured issues
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Execute multiple checks with dependency awareness - return grouped results
|
|
400
|
+
*/
|
|
401
|
+
async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
|
|
402
|
+
// Use the existing dependency-aware execution logic
|
|
403
|
+
const reviewSummary = await this.executeDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast);
|
|
404
|
+
// Convert the flat ReviewSummary to grouped CheckResults
|
|
405
|
+
return await this.convertReviewSummaryToGroupedResults(reviewSummary, checks, config, prInfo);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Convert ReviewSummary to GroupedCheckResults
|
|
409
|
+
*/
|
|
410
|
+
async convertReviewSummaryToGroupedResults(reviewSummary, checks, config, prInfo) {
|
|
411
|
+
const groupedResults = {};
|
|
412
|
+
// Process each check individually
|
|
413
|
+
for (const checkName of checks) {
|
|
414
|
+
const checkConfig = config?.checks?.[checkName];
|
|
415
|
+
if (!checkConfig)
|
|
416
|
+
continue;
|
|
417
|
+
// Extract issues for this check
|
|
418
|
+
const checkIssues = (reviewSummary.issues || []).filter(issue => issue.ruleId?.startsWith(`${checkName}/`));
|
|
419
|
+
// Extract suggestions for this check
|
|
420
|
+
const checkSuggestions = (reviewSummary.suggestions || []).filter(suggestion => suggestion.startsWith(`[${checkName}]`));
|
|
421
|
+
// Create a mini ReviewSummary for this check
|
|
422
|
+
const checkSummary = {
|
|
423
|
+
issues: checkIssues,
|
|
424
|
+
suggestions: checkSuggestions,
|
|
425
|
+
debug: reviewSummary.debug,
|
|
426
|
+
};
|
|
427
|
+
// Render content for this check
|
|
428
|
+
const content = await this.renderCheckContent(checkName, checkSummary, checkConfig, prInfo);
|
|
429
|
+
const checkResult = {
|
|
430
|
+
checkName,
|
|
431
|
+
content,
|
|
432
|
+
group: checkConfig.group || 'default',
|
|
433
|
+
debug: reviewSummary.debug,
|
|
434
|
+
issues: checkIssues, // Include structured issues
|
|
435
|
+
};
|
|
436
|
+
// Add to appropriate group
|
|
437
|
+
const group = checkResult.group;
|
|
438
|
+
if (!groupedResults[group]) {
|
|
439
|
+
groupedResults[group] = [];
|
|
440
|
+
}
|
|
441
|
+
groupedResults[group].push(checkResult);
|
|
442
|
+
}
|
|
443
|
+
return groupedResults;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Validates that a file path is safe and within the project directory
|
|
447
|
+
* Prevents path traversal attacks by:
|
|
448
|
+
* - Blocking absolute paths
|
|
449
|
+
* - Blocking paths with ".." segments
|
|
450
|
+
* - Ensuring resolved path is within project directory
|
|
451
|
+
* - Blocking special characters and null bytes
|
|
452
|
+
* - Enforcing .liquid file extension
|
|
453
|
+
*/
|
|
454
|
+
async validateTemplatePath(templatePath) {
|
|
455
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
456
|
+
// Validate input
|
|
457
|
+
if (!templatePath || typeof templatePath !== 'string' || templatePath.trim() === '') {
|
|
458
|
+
throw new Error('Template path must be a non-empty string');
|
|
459
|
+
}
|
|
460
|
+
// Block null bytes and other dangerous characters
|
|
461
|
+
if (templatePath.includes('\0') || templatePath.includes('\x00')) {
|
|
462
|
+
throw new Error('Template path contains invalid characters');
|
|
463
|
+
}
|
|
464
|
+
// Enforce .liquid file extension
|
|
465
|
+
if (!templatePath.endsWith('.liquid')) {
|
|
466
|
+
throw new Error('Template file must have .liquid extension');
|
|
467
|
+
}
|
|
468
|
+
// Block absolute paths
|
|
469
|
+
if (path.isAbsolute(templatePath)) {
|
|
470
|
+
throw new Error('Template path must be relative to project directory');
|
|
471
|
+
}
|
|
472
|
+
// Block paths with ".." segments
|
|
473
|
+
if (templatePath.includes('..')) {
|
|
474
|
+
throw new Error('Template path cannot contain ".." segments');
|
|
475
|
+
}
|
|
476
|
+
// Block paths starting with ~ (home directory)
|
|
477
|
+
if (templatePath.startsWith('~')) {
|
|
478
|
+
throw new Error('Template path cannot reference home directory');
|
|
479
|
+
}
|
|
480
|
+
// Get the project root directory from git analyzer
|
|
481
|
+
const repositoryInfo = await this.gitAnalyzer.analyzeRepository();
|
|
482
|
+
const projectRoot = repositoryInfo.workingDirectory;
|
|
483
|
+
// Validate project root
|
|
484
|
+
if (!projectRoot || typeof projectRoot !== 'string') {
|
|
485
|
+
throw new Error('Unable to determine project root directory');
|
|
486
|
+
}
|
|
487
|
+
// Resolve the template path relative to project root
|
|
488
|
+
const resolvedPath = path.resolve(projectRoot, templatePath);
|
|
489
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
490
|
+
// Validate resolved paths
|
|
491
|
+
if (!resolvedPath ||
|
|
492
|
+
!resolvedProjectRoot ||
|
|
493
|
+
resolvedPath === '' ||
|
|
494
|
+
resolvedProjectRoot === '') {
|
|
495
|
+
throw new Error(`Unable to resolve template path: projectRoot="${projectRoot}", templatePath="${templatePath}", resolvedPath="${resolvedPath}", resolvedProjectRoot="${resolvedProjectRoot}"`);
|
|
496
|
+
}
|
|
497
|
+
// Ensure the resolved path is still within the project directory
|
|
498
|
+
if (!resolvedPath.startsWith(resolvedProjectRoot + path.sep) &&
|
|
499
|
+
resolvedPath !== resolvedProjectRoot) {
|
|
500
|
+
throw new Error('Template path escapes project directory');
|
|
501
|
+
}
|
|
502
|
+
return resolvedPath;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Render check content using the appropriate template
|
|
506
|
+
*/
|
|
507
|
+
async renderCheckContent(checkName, reviewSummary, checkConfig, _prInfo) {
|
|
508
|
+
// Import the liquid template system
|
|
509
|
+
const { Liquid } = await Promise.resolve().then(() => __importStar(require('liquidjs')));
|
|
510
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
511
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
512
|
+
const liquid = new Liquid({
|
|
513
|
+
trimTagLeft: false,
|
|
514
|
+
trimTagRight: false,
|
|
515
|
+
trimOutputLeft: false,
|
|
516
|
+
trimOutputRight: false,
|
|
517
|
+
greedy: false,
|
|
518
|
+
});
|
|
519
|
+
// Determine template to use
|
|
520
|
+
const schema = checkConfig.schema || 'plain';
|
|
521
|
+
let templateContent;
|
|
522
|
+
if (checkConfig.template) {
|
|
523
|
+
// Custom template
|
|
524
|
+
if (checkConfig.template.content) {
|
|
525
|
+
templateContent = checkConfig.template.content;
|
|
526
|
+
}
|
|
527
|
+
else if (checkConfig.template.file) {
|
|
528
|
+
// Validate the template file path to prevent path traversal attacks
|
|
529
|
+
const validatedPath = await this.validateTemplatePath(checkConfig.template.file);
|
|
530
|
+
templateContent = await fs.readFile(validatedPath, 'utf-8');
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
throw new Error('Custom template must specify either "file" or "content"');
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (schema === 'plain') {
|
|
537
|
+
// Plain schema - return raw content directly
|
|
538
|
+
// Strip [checkName] prefixes from suggestions before joining
|
|
539
|
+
const cleanedSuggestions = (reviewSummary.suggestions || []).map(suggestion => {
|
|
540
|
+
// Remove [checkName] prefix if present
|
|
541
|
+
return suggestion.replace(/^\[[^\]]+\]\s*/, '');
|
|
542
|
+
});
|
|
543
|
+
return (reviewSummary.issues?.[0]?.message || '') + (cleanedSuggestions.join('\n\n') || '');
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
// Use built-in schema template
|
|
547
|
+
const sanitizedSchema = schema.replace(/[^a-zA-Z0-9-]/g, '');
|
|
548
|
+
if (!sanitizedSchema) {
|
|
549
|
+
throw new Error('Invalid schema name');
|
|
550
|
+
}
|
|
551
|
+
const templatePath = path.join(__dirname, `../output/${sanitizedSchema}/template.liquid`);
|
|
552
|
+
templateContent = await fs.readFile(templatePath, 'utf-8');
|
|
553
|
+
}
|
|
554
|
+
// Prepare template data
|
|
555
|
+
const templateData = {
|
|
556
|
+
issues: reviewSummary.issues || [],
|
|
557
|
+
checkName: checkName,
|
|
558
|
+
suggestions: reviewSummary.suggestions || [],
|
|
559
|
+
};
|
|
560
|
+
const rendered = await liquid.parseAndRender(templateContent, templateData);
|
|
561
|
+
return rendered.trim();
|
|
562
|
+
}
|
|
293
563
|
/**
|
|
294
564
|
* Execute multiple checks with dependency awareness - intelligently parallel and sequential
|
|
295
565
|
*/
|
|
@@ -403,7 +673,7 @@ class CheckExecutionEngine {
|
|
|
403
673
|
branch: prInfo.head,
|
|
404
674
|
baseBranch: prInfo.base,
|
|
405
675
|
filesChanged: prInfo.files.map(f => f.filename),
|
|
406
|
-
event: '
|
|
676
|
+
event: 'issue_comment', // Command triggered from comment
|
|
407
677
|
environment: getSafeEnvironmentVariables(),
|
|
408
678
|
previousResults: results,
|
|
409
679
|
});
|
|
@@ -467,9 +737,9 @@ class CheckExecutionEngine {
|
|
|
467
737
|
providerConfig.sessionId = currentSessionId;
|
|
468
738
|
}
|
|
469
739
|
const result = await provider.execute(prInfo, providerConfig, dependencyResults, sessionInfo);
|
|
470
|
-
log(`🔧 Debug: Completed check: ${checkName}, issues found: ${result.issues.length}`);
|
|
740
|
+
log(`🔧 Debug: Completed check: ${checkName}, issues found: ${(result.issues || []).length}`);
|
|
471
741
|
// Add group, schema, template info and timestamp to issues from config
|
|
472
|
-
const enrichedIssues = result.issues.map(issue => ({
|
|
742
|
+
const enrichedIssues = (result.issues || []).map(issue => ({
|
|
473
743
|
...issue,
|
|
474
744
|
ruleId: `${checkName}/${issue.ruleId}`,
|
|
475
745
|
group: checkConfig.group,
|
|
@@ -544,7 +814,7 @@ class CheckExecutionEngine {
|
|
|
544
814
|
const result = levelResults[i];
|
|
545
815
|
if (result.status === 'fulfilled' && result.value.result && !result.value.error) {
|
|
546
816
|
// Check for issues that should trigger fail-fast
|
|
547
|
-
const hasFailuresToReport = result.value.result.issues.some(issue => issue.severity === 'error' || issue.severity === 'critical');
|
|
817
|
+
const hasFailuresToReport = (result.value.result.issues || []).some(issue => issue.severity === 'error' || issue.severity === 'critical');
|
|
548
818
|
if (hasFailuresToReport) {
|
|
549
819
|
log(`🛑 Check "${checkName}" found critical/error issues and fail-fast is enabled - stopping execution`);
|
|
550
820
|
shouldStopExecution = true;
|
|
@@ -612,7 +882,7 @@ class CheckExecutionEngine {
|
|
|
612
882
|
branch: prInfo.head,
|
|
613
883
|
baseBranch: prInfo.base,
|
|
614
884
|
filesChanged: prInfo.files.map(f => f.filename),
|
|
615
|
-
event: '
|
|
885
|
+
event: 'issue_comment', // Command triggered from comment
|
|
616
886
|
environment: getSafeEnvironmentVariables(),
|
|
617
887
|
previousResults: new Map(), // No previous results in parallel execution
|
|
618
888
|
});
|
|
@@ -642,9 +912,9 @@ class CheckExecutionEngine {
|
|
|
642
912
|
},
|
|
643
913
|
};
|
|
644
914
|
const result = await provider.execute(prInfo, providerConfig);
|
|
645
|
-
console.error(`🔧 Debug: Completed check: ${checkName}, issues found: ${result.issues.length}`);
|
|
915
|
+
console.error(`🔧 Debug: Completed check: ${checkName}, issues found: ${(result.issues || []).length}`);
|
|
646
916
|
// Add group, schema info and timestamp to issues from config
|
|
647
|
-
const enrichedIssues = result.issues.map(issue => ({
|
|
917
|
+
const enrichedIssues = (result.issues || []).map(issue => ({
|
|
648
918
|
...issue,
|
|
649
919
|
ruleId: `${checkName}/${issue.ruleId}`,
|
|
650
920
|
group: checkConfig.group,
|
|
@@ -712,7 +982,7 @@ class CheckExecutionEngine {
|
|
|
712
982
|
};
|
|
713
983
|
const result = await provider.execute(prInfo, providerConfig);
|
|
714
984
|
// Prefix issues with check name and add group/schema info and timestamp from config
|
|
715
|
-
const prefixedIssues = result.issues.map(issue => ({
|
|
985
|
+
const prefixedIssues = (result.issues || []).map(issue => ({
|
|
716
986
|
...issue,
|
|
717
987
|
ruleId: `${checkName}/${issue.ruleId}`,
|
|
718
988
|
group: checkConfig.group,
|
|
@@ -767,17 +1037,17 @@ class CheckExecutionEngine {
|
|
|
767
1037
|
continue;
|
|
768
1038
|
}
|
|
769
1039
|
// Check if this was a successful result
|
|
770
|
-
const hasErrors = result.issues.some(issue => issue.ruleId?.includes('/error') || issue.ruleId?.includes('/promise-error'));
|
|
1040
|
+
const hasErrors = (result.issues || []).some(issue => issue.ruleId?.includes('/error') || issue.ruleId?.includes('/promise-error'));
|
|
771
1041
|
if (hasErrors) {
|
|
772
1042
|
debugInfo.push(`❌ Check "${checkName}" failed with errors`);
|
|
773
1043
|
}
|
|
774
1044
|
else {
|
|
775
|
-
debugInfo.push(`✅ Check "${checkName}" completed: ${result.issues.length} issues found (level ${executionGroup.level})`);
|
|
1045
|
+
debugInfo.push(`✅ Check "${checkName}" completed: ${(result.issues || []).length} issues found (level ${executionGroup.level})`);
|
|
776
1046
|
}
|
|
777
1047
|
// Issues are already prefixed and enriched with group/schema info
|
|
778
|
-
aggregatedIssues.push(...result.issues);
|
|
1048
|
+
aggregatedIssues.push(...(result.issues || []));
|
|
779
1049
|
// Add suggestions with check name prefix
|
|
780
|
-
const prefixedSuggestions = result.suggestions.map(suggestion => `[${checkName}] ${suggestion}`);
|
|
1050
|
+
const prefixedSuggestions = (result.suggestions || []).map(suggestion => `[${checkName}] ${suggestion}`);
|
|
781
1051
|
aggregatedSuggestions.push(...prefixedSuggestions);
|
|
782
1052
|
}
|
|
783
1053
|
}
|
|
@@ -868,12 +1138,12 @@ class CheckExecutionEngine {
|
|
|
868
1138
|
}
|
|
869
1139
|
else if (checkResult.result) {
|
|
870
1140
|
successfulChecks++;
|
|
871
|
-
console.error(`🔧 Debug: Check ${checkName} succeeded with ${checkResult.result.issues.length} issues`);
|
|
872
|
-
debugInfo.push(`✅ Check "${checkName}" completed: ${checkResult.result.issues.length} issues found`);
|
|
1141
|
+
console.error(`🔧 Debug: Check ${checkName} succeeded with ${(checkResult.result.issues || []).length} issues`);
|
|
1142
|
+
debugInfo.push(`✅ Check "${checkName}" completed: ${(checkResult.result.issues || []).length} issues found`);
|
|
873
1143
|
// Issues are already prefixed and enriched with group/schema info
|
|
874
|
-
aggregatedIssues.push(...checkResult.result.issues);
|
|
1144
|
+
aggregatedIssues.push(...(checkResult.result.issues || []));
|
|
875
1145
|
// Add suggestions with check name prefix
|
|
876
|
-
const prefixedSuggestions = checkResult.result.suggestions.map(suggestion => `[${checkName}] ${suggestion}`);
|
|
1146
|
+
const prefixedSuggestions = (checkResult.result.suggestions || []).map(suggestion => `[${checkName}] ${suggestion}`);
|
|
877
1147
|
aggregatedSuggestions.push(...prefixedSuggestions);
|
|
878
1148
|
}
|
|
879
1149
|
}
|
|
@@ -1134,7 +1404,7 @@ class CheckExecutionEngine {
|
|
|
1134
1404
|
}
|
|
1135
1405
|
// If the result has a result with critical or error issues, it should fail fast
|
|
1136
1406
|
if (result?.result?.issues) {
|
|
1137
|
-
return result.result.issues.some((issue) => issue.severity === 'error' || issue.severity === 'critical');
|
|
1407
|
+
return (result.result.issues || []).some((issue) => issue.severity === 'error' || issue.severity === 'critical');
|
|
1138
1408
|
}
|
|
1139
1409
|
return false;
|
|
1140
1410
|
}
|