@probelabs/visor 0.1.93 → 0.1.95
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 +4 -4
- package/defaults/.visor.yaml +86 -6
- package/dist/ai-review-service.d.ts +1 -1
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts +5 -0
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/config.d.ts +9 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/defaults/.visor.yaml +86 -6
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/footer.d.ts +25 -0
- package/dist/footer.d.ts.map +1 -0
- package/dist/github-check-service.d.ts.map +1 -1
- package/dist/github-comments.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1074 -472
- package/dist/output/code-review/schema.json +0 -23
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts.map +1 -1
- package/dist/providers/memory-check-provider.d.ts.map +1 -1
- package/dist/reviewer.d.ts +11 -0
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/sdk/{check-execution-engine-RORGGGGP.mjs → check-execution-engine-NMPXJ7FQ.mjs} +2 -2
- package/dist/sdk/{chunk-Z47UECAT.mjs → chunk-Q4S5A5TO.mjs} +314 -111
- package/dist/sdk/chunk-Q4S5A5TO.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +11 -2
- package/dist/sdk/sdk.d.ts +11 -2
- package/dist/sdk/sdk.js +398 -152
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +57 -24
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk.d.ts +11 -2
- package/dist/sdk.d.ts.map +1 -1
- package/dist/traces/{run-2025-10-15T11-54-04-087Z.ndjson → run-2025-10-18T18-27-25-085Z.ndjson} +8 -1
- package/dist/traces/{run-2025-10-15T11-54-14-046Z.ndjson → run-2025-10-18T18-27-35-400Z.ndjson} +8 -1
- package/dist/traces/{run-2025-10-15T11-54-14-575Z.ndjson → run-2025-10-18T18-27-35-937Z.ndjson} +8 -1
- package/dist/traces/{run-2025-10-15T11-54-15-082Z.ndjson → run-2025-10-18T18-27-36-428Z.ndjson} +8 -1
- package/dist/types/cli.d.ts +3 -2
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +0 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/diff-processor.d.ts +6 -0
- package/dist/utils/diff-processor.d.ts.map +1 -0
- package/package.json +2 -2
- package/dist/sdk/chunk-Z47UECAT.mjs.map +0 -1
- /package/dist/sdk/{check-execution-engine-RORGGGGP.mjs.map → check-execution-engine-NMPXJ7FQ.mjs.map} +0 -0
- /package/dist/traces/{run-2025-10-15T11-54-15-561Z.ndjson → run-2025-10-18T18-27-36-917Z.ndjson} +0 -0
package/dist/sdk/sdk.js
CHANGED
|
@@ -136,6 +136,33 @@ var init_logger = __esm({
|
|
|
136
136
|
}
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
// src/footer.ts
|
|
140
|
+
function generateFooter(options = {}) {
|
|
141
|
+
const { includeMetadata, includeSeparator = true } = options;
|
|
142
|
+
const parts = [];
|
|
143
|
+
if (includeSeparator) {
|
|
144
|
+
parts.push("---");
|
|
145
|
+
parts.push("");
|
|
146
|
+
}
|
|
147
|
+
parts.push(
|
|
148
|
+
"*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*"
|
|
149
|
+
);
|
|
150
|
+
if (includeMetadata) {
|
|
151
|
+
const { lastUpdated, triggeredBy, commitSha } = includeMetadata;
|
|
152
|
+
const commitInfo = commitSha ? ` | Commit: ${commitSha.substring(0, 7)}` : "";
|
|
153
|
+
parts.push("");
|
|
154
|
+
parts.push(`*Last updated: ${lastUpdated} | Triggered by: ${triggeredBy}${commitInfo}*`);
|
|
155
|
+
}
|
|
156
|
+
parts.push("");
|
|
157
|
+
parts.push("\u{1F4A1} **TIP:** You can chat with Visor using `/visor ask <your question>`");
|
|
158
|
+
return parts.join("\n");
|
|
159
|
+
}
|
|
160
|
+
var init_footer = __esm({
|
|
161
|
+
"src/footer.ts"() {
|
|
162
|
+
"use strict";
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
139
166
|
// src/github-comments.ts
|
|
140
167
|
var import_uuid, CommentManager;
|
|
141
168
|
var init_github_comments = __esm({
|
|
@@ -143,6 +170,7 @@ var init_github_comments = __esm({
|
|
|
143
170
|
"use strict";
|
|
144
171
|
import_uuid = require("uuid");
|
|
145
172
|
init_logger();
|
|
173
|
+
init_footer();
|
|
146
174
|
CommentManager = class {
|
|
147
175
|
octokit;
|
|
148
176
|
retryConfig;
|
|
@@ -244,15 +272,17 @@ var init_github_comments = __esm({
|
|
|
244
272
|
*/
|
|
245
273
|
formatCommentWithMetadata(content, metadata) {
|
|
246
274
|
const { commentId, lastUpdated, triggeredBy, commitSha } = metadata;
|
|
247
|
-
const
|
|
275
|
+
const footer = generateFooter({
|
|
276
|
+
includeMetadata: {
|
|
277
|
+
lastUpdated,
|
|
278
|
+
triggeredBy,
|
|
279
|
+
commitSha
|
|
280
|
+
}
|
|
281
|
+
});
|
|
248
282
|
return `<!-- visor-comment-id:${commentId} -->
|
|
249
283
|
${content}
|
|
250
284
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*
|
|
254
|
-
|
|
255
|
-
*Last updated: ${lastUpdated} | Triggered by: ${triggeredBy}${commitInfo}*
|
|
285
|
+
${footer}
|
|
256
286
|
<!-- /visor-comment-id:${commentId} -->`;
|
|
257
287
|
}
|
|
258
288
|
/**
|
|
@@ -718,18 +748,81 @@ var init_session_registry = __esm({
|
|
|
718
748
|
}
|
|
719
749
|
});
|
|
720
750
|
|
|
751
|
+
// src/utils/diff-processor.ts
|
|
752
|
+
async function processDiffWithOutline(diffContent) {
|
|
753
|
+
if (!diffContent || diffContent.trim().length === 0) {
|
|
754
|
+
return diffContent;
|
|
755
|
+
}
|
|
756
|
+
try {
|
|
757
|
+
const originalProbePath = process.env.PROBE_PATH;
|
|
758
|
+
const fs12 = require("fs");
|
|
759
|
+
const possiblePaths = [
|
|
760
|
+
// Relative to current working directory (most common in production)
|
|
761
|
+
path2.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
|
|
762
|
+
// Relative to __dirname (for unbundled development)
|
|
763
|
+
path2.join(__dirname, "../..", "node_modules/@probelabs/probe/bin/probe-binary"),
|
|
764
|
+
// Relative to dist directory (for bundled CLI)
|
|
765
|
+
path2.join(__dirname, "node_modules/@probelabs/probe/bin/probe-binary")
|
|
766
|
+
];
|
|
767
|
+
let probeBinaryPath;
|
|
768
|
+
for (const candidatePath of possiblePaths) {
|
|
769
|
+
if (fs12.existsSync(candidatePath)) {
|
|
770
|
+
probeBinaryPath = candidatePath;
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (!probeBinaryPath) {
|
|
775
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
776
|
+
console.error("Probe binary not found. Tried:", possiblePaths);
|
|
777
|
+
}
|
|
778
|
+
return diffContent;
|
|
779
|
+
}
|
|
780
|
+
process.env.PROBE_PATH = probeBinaryPath;
|
|
781
|
+
const extractPromise = (0, import_probe2.extract)({
|
|
782
|
+
content: diffContent,
|
|
783
|
+
format: "outline-diff",
|
|
784
|
+
allowTests: true
|
|
785
|
+
// Allow test files and test code blocks in extraction results
|
|
786
|
+
});
|
|
787
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
788
|
+
setTimeout(() => reject(new Error("Extract timeout after 30s")), 3e4);
|
|
789
|
+
});
|
|
790
|
+
const result = await Promise.race([extractPromise, timeoutPromise]);
|
|
791
|
+
if (originalProbePath !== void 0) {
|
|
792
|
+
process.env.PROBE_PATH = originalProbePath;
|
|
793
|
+
} else {
|
|
794
|
+
delete process.env.PROBE_PATH;
|
|
795
|
+
}
|
|
796
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
797
|
+
} catch (error) {
|
|
798
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
799
|
+
console.error("Failed to process diff with outline-diff format:", error);
|
|
800
|
+
}
|
|
801
|
+
return diffContent;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
var import_probe2, path2;
|
|
805
|
+
var init_diff_processor = __esm({
|
|
806
|
+
"src/utils/diff-processor.ts"() {
|
|
807
|
+
"use strict";
|
|
808
|
+
import_probe2 = require("@probelabs/probe");
|
|
809
|
+
path2 = __toESM(require("path"));
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
|
|
721
813
|
// src/ai-review-service.ts
|
|
722
814
|
function log(...args) {
|
|
723
815
|
logger.debug(args.join(" "));
|
|
724
816
|
}
|
|
725
|
-
var
|
|
817
|
+
var import_probe3, AIReviewService;
|
|
726
818
|
var init_ai_review_service = __esm({
|
|
727
819
|
"src/ai-review-service.ts"() {
|
|
728
820
|
"use strict";
|
|
729
|
-
|
|
821
|
+
import_probe3 = require("@probelabs/probe");
|
|
730
822
|
init_session_registry();
|
|
731
823
|
init_logger();
|
|
732
824
|
init_tracer_init();
|
|
825
|
+
init_diff_processor();
|
|
733
826
|
AIReviewService = class {
|
|
734
827
|
config;
|
|
735
828
|
sessionRegistry;
|
|
@@ -768,7 +861,7 @@ var init_ai_review_service = __esm({
|
|
|
768
861
|
/**
|
|
769
862
|
* Execute AI review using probe agent
|
|
770
863
|
*/
|
|
771
|
-
async executeReview(prInfo, customPrompt, schema,
|
|
864
|
+
async executeReview(prInfo, customPrompt, schema, checkName, sessionId) {
|
|
772
865
|
const startTime = Date.now();
|
|
773
866
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
774
867
|
const prompt = await this.buildCustomPrompt(prInfo, customPrompt, schema);
|
|
@@ -825,7 +918,7 @@ var init_ai_review_service = __esm({
|
|
|
825
918
|
prompt,
|
|
826
919
|
schema,
|
|
827
920
|
debugInfo,
|
|
828
|
-
|
|
921
|
+
checkName,
|
|
829
922
|
sessionId
|
|
830
923
|
);
|
|
831
924
|
const processingTime = Date.now() - startTime;
|
|
@@ -985,9 +1078,9 @@ var init_ai_review_service = __esm({
|
|
|
985
1078
|
*/
|
|
986
1079
|
async buildCustomPrompt(prInfo, customInstructions, schema, options) {
|
|
987
1080
|
const skipPRContext = options?.skipPRContext === true;
|
|
988
|
-
const prContext = skipPRContext ? "" : this.formatPRContext(prInfo);
|
|
989
|
-
const isIssue = prInfo.isIssue === true;
|
|
990
1081
|
const isCodeReviewSchema = schema === "code-review";
|
|
1082
|
+
const prContext = skipPRContext ? "" : await this.formatPRContext(prInfo, isCodeReviewSchema);
|
|
1083
|
+
const isIssue = prInfo.isIssue === true;
|
|
991
1084
|
if (isIssue) {
|
|
992
1085
|
if (skipPRContext) {
|
|
993
1086
|
return `<instructions>
|
|
@@ -1076,7 +1169,7 @@ ${prContext}
|
|
|
1076
1169
|
/**
|
|
1077
1170
|
* Format PR or Issue context for the AI using XML structure
|
|
1078
1171
|
*/
|
|
1079
|
-
formatPRContext(prInfo) {
|
|
1172
|
+
async formatPRContext(prInfo, isCodeReviewSchema) {
|
|
1080
1173
|
const prContextInfo = prInfo;
|
|
1081
1174
|
const isIssue = prContextInfo.isIssue === true;
|
|
1082
1175
|
const isPRContext = prContextInfo.isPRContext === true;
|
|
@@ -1158,7 +1251,12 @@ ${this.escapeXml(prInfo.body)}
|
|
|
1158
1251
|
}
|
|
1159
1252
|
const issueComments = prInfo.comments;
|
|
1160
1253
|
if (issueComments && issueComments.length > 0) {
|
|
1161
|
-
|
|
1254
|
+
let historicalComments = triggeringComment2 ? issueComments.filter((c) => c.id !== triggeringComment2.id) : issueComments;
|
|
1255
|
+
if (isCodeReviewSchema) {
|
|
1256
|
+
historicalComments = historicalComments.filter(
|
|
1257
|
+
(c) => !c.body || !c.body.includes("visor-comment-id:pr-review-")
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1162
1260
|
if (historicalComments.length > 0) {
|
|
1163
1261
|
context3 += `
|
|
1164
1262
|
<!-- Previous comments in chronological order (excluding triggering comment) -->
|
|
@@ -1200,24 +1298,27 @@ ${this.escapeXml(prInfo.body)}
|
|
|
1200
1298
|
}
|
|
1201
1299
|
if (includeCodeContext) {
|
|
1202
1300
|
if (prInfo.fullDiff) {
|
|
1301
|
+
const processedFullDiff = await processDiffWithOutline(prInfo.fullDiff);
|
|
1203
1302
|
context2 += `
|
|
1204
|
-
<!-- Complete unified diff showing all changes in the pull request -->
|
|
1303
|
+
<!-- Complete unified diff showing all changes in the pull request (processed with outline-diff) -->
|
|
1205
1304
|
<full_diff>
|
|
1206
|
-
${this.escapeXml(
|
|
1305
|
+
${this.escapeXml(processedFullDiff)}
|
|
1207
1306
|
</full_diff>`;
|
|
1208
1307
|
}
|
|
1209
1308
|
if (prInfo.isIncremental) {
|
|
1210
1309
|
if (prInfo.commitDiff && prInfo.commitDiff.length > 0) {
|
|
1310
|
+
const processedCommitDiff = await processDiffWithOutline(prInfo.commitDiff);
|
|
1211
1311
|
context2 += `
|
|
1212
|
-
<!-- Diff of only the latest commit for incremental analysis -->
|
|
1312
|
+
<!-- Diff of only the latest commit for incremental analysis (processed with outline-diff) -->
|
|
1213
1313
|
<commit_diff>
|
|
1214
|
-
${this.escapeXml(
|
|
1314
|
+
${this.escapeXml(processedCommitDiff)}
|
|
1215
1315
|
</commit_diff>`;
|
|
1216
1316
|
} else {
|
|
1317
|
+
const processedFallbackDiff = prInfo.fullDiff ? await processDiffWithOutline(prInfo.fullDiff) : "";
|
|
1217
1318
|
context2 += `
|
|
1218
|
-
<!-- Commit diff could not be retrieved - falling back to full diff analysis -->
|
|
1319
|
+
<!-- Commit diff could not be retrieved - falling back to full diff analysis (processed with outline-diff) -->
|
|
1219
1320
|
<commit_diff>
|
|
1220
|
-
${
|
|
1321
|
+
${this.escapeXml(processedFallbackDiff)}
|
|
1221
1322
|
</commit_diff>`;
|
|
1222
1323
|
}
|
|
1223
1324
|
}
|
|
@@ -1253,7 +1354,12 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ""}
|
|
|
1253
1354
|
}
|
|
1254
1355
|
const prComments = prInfo.comments;
|
|
1255
1356
|
if (prComments && prComments.length > 0) {
|
|
1256
|
-
|
|
1357
|
+
let historicalComments = triggeringComment ? prComments.filter((c) => c.id !== triggeringComment.id) : prComments;
|
|
1358
|
+
if (isCodeReviewSchema) {
|
|
1359
|
+
historicalComments = historicalComments.filter(
|
|
1360
|
+
(c) => !c.body || !c.body.includes("visor-comment-id:pr-review-")
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1257
1363
|
if (historicalComments.length > 0) {
|
|
1258
1364
|
context2 += `
|
|
1259
1365
|
<!-- Previous PR comments in chronological order (excluding triggering comment) -->
|
|
@@ -1324,7 +1430,7 @@ ${schemaString}`);
|
|
|
1324
1430
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1325
1431
|
try {
|
|
1326
1432
|
const fs12 = require("fs");
|
|
1327
|
-
const
|
|
1433
|
+
const path13 = require("path");
|
|
1328
1434
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1329
1435
|
const provider = this.config.provider || "auto";
|
|
1330
1436
|
const model = this.config.model || "default";
|
|
@@ -1438,16 +1544,16 @@ ${"=".repeat(60)}
|
|
|
1438
1544
|
`;
|
|
1439
1545
|
readableVersion += `${"=".repeat(60)}
|
|
1440
1546
|
`;
|
|
1441
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1547
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
|
|
1442
1548
|
if (!fs12.existsSync(debugArtifactsDir)) {
|
|
1443
1549
|
fs12.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
1444
1550
|
}
|
|
1445
|
-
const debugFile =
|
|
1551
|
+
const debugFile = path13.join(
|
|
1446
1552
|
debugArtifactsDir,
|
|
1447
1553
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
1448
1554
|
);
|
|
1449
1555
|
fs12.writeFileSync(debugFile, debugJson, "utf-8");
|
|
1450
|
-
const readableFile =
|
|
1556
|
+
const readableFile = path13.join(
|
|
1451
1557
|
debugArtifactsDir,
|
|
1452
1558
|
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1453
1559
|
);
|
|
@@ -1484,7 +1590,7 @@ ${"=".repeat(60)}
|
|
|
1484
1590
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1485
1591
|
try {
|
|
1486
1592
|
const fs12 = require("fs");
|
|
1487
|
-
const
|
|
1593
|
+
const path13 = require("path");
|
|
1488
1594
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1489
1595
|
const agentAny2 = agent;
|
|
1490
1596
|
let fullHistory = [];
|
|
@@ -1495,8 +1601,8 @@ ${"=".repeat(60)}
|
|
|
1495
1601
|
} else if (agentAny2._messages) {
|
|
1496
1602
|
fullHistory = agentAny2._messages;
|
|
1497
1603
|
}
|
|
1498
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1499
|
-
const sessionFile =
|
|
1604
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
|
|
1605
|
+
const sessionFile = path13.join(
|
|
1500
1606
|
debugArtifactsDir,
|
|
1501
1607
|
`session-${_checkName || "unknown"}-${timestamp}.json`
|
|
1502
1608
|
);
|
|
@@ -1511,7 +1617,7 @@ ${"=".repeat(60)}
|
|
|
1511
1617
|
latestResponse: response
|
|
1512
1618
|
};
|
|
1513
1619
|
fs12.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1514
|
-
const sessionTxtFile =
|
|
1620
|
+
const sessionTxtFile = path13.join(
|
|
1515
1621
|
debugArtifactsDir,
|
|
1516
1622
|
`session-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1517
1623
|
);
|
|
@@ -1555,10 +1661,10 @@ ${"=".repeat(60)}
|
|
|
1555
1661
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1556
1662
|
try {
|
|
1557
1663
|
const fs12 = require("fs");
|
|
1558
|
-
const
|
|
1664
|
+
const path13 = require("path");
|
|
1559
1665
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1560
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1561
|
-
const responseFile =
|
|
1666
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
|
|
1667
|
+
const responseFile = path13.join(
|
|
1562
1668
|
debugArtifactsDir,
|
|
1563
1669
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1564
1670
|
);
|
|
@@ -1696,7 +1802,7 @@ ${"=".repeat(60)}
|
|
|
1696
1802
|
if (this.config.model) {
|
|
1697
1803
|
options.model = this.config.model;
|
|
1698
1804
|
}
|
|
1699
|
-
const agent = new
|
|
1805
|
+
const agent = new import_probe3.ProbeAgent(options);
|
|
1700
1806
|
log("\u{1F680} Calling ProbeAgent...");
|
|
1701
1807
|
let schemaString = void 0;
|
|
1702
1808
|
let effectiveSchema = typeof schema === "object" ? "custom" : schema;
|
|
@@ -1730,7 +1836,7 @@ ${schemaString}`);
|
|
|
1730
1836
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1731
1837
|
try {
|
|
1732
1838
|
const fs12 = require("fs");
|
|
1733
|
-
const
|
|
1839
|
+
const path13 = require("path");
|
|
1734
1840
|
const os = require("os");
|
|
1735
1841
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1736
1842
|
const debugData = {
|
|
@@ -1804,21 +1910,21 @@ ${"=".repeat(60)}
|
|
|
1804
1910
|
readableVersion += `${"=".repeat(60)}
|
|
1805
1911
|
`;
|
|
1806
1912
|
const tempDir = os.tmpdir();
|
|
1807
|
-
const promptFile =
|
|
1913
|
+
const promptFile = path13.join(tempDir, `visor-prompt-${timestamp}.txt`);
|
|
1808
1914
|
fs12.writeFileSync(promptFile, prompt, "utf-8");
|
|
1809
1915
|
log(`
|
|
1810
1916
|
\u{1F4BE} Prompt saved to: ${promptFile}`);
|
|
1811
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1917
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
|
|
1812
1918
|
try {
|
|
1813
1919
|
if (!fs12.existsSync(debugArtifactsDir)) {
|
|
1814
1920
|
fs12.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
1815
1921
|
}
|
|
1816
|
-
const debugFile =
|
|
1922
|
+
const debugFile = path13.join(
|
|
1817
1923
|
debugArtifactsDir,
|
|
1818
1924
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
1819
1925
|
);
|
|
1820
1926
|
fs12.writeFileSync(debugFile, debugJson, "utf-8");
|
|
1821
|
-
const readableFile =
|
|
1927
|
+
const readableFile = path13.join(
|
|
1822
1928
|
debugArtifactsDir,
|
|
1823
1929
|
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1824
1930
|
);
|
|
@@ -1871,7 +1977,7 @@ $ ${cliCommand}
|
|
|
1871
1977
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1872
1978
|
try {
|
|
1873
1979
|
const fs12 = require("fs");
|
|
1874
|
-
const
|
|
1980
|
+
const path13 = require("path");
|
|
1875
1981
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1876
1982
|
const agentAny = agent;
|
|
1877
1983
|
let fullHistory = [];
|
|
@@ -1882,8 +1988,8 @@ $ ${cliCommand}
|
|
|
1882
1988
|
} else if (agentAny._messages) {
|
|
1883
1989
|
fullHistory = agentAny._messages;
|
|
1884
1990
|
}
|
|
1885
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1886
|
-
const sessionFile =
|
|
1991
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
|
|
1992
|
+
const sessionFile = path13.join(
|
|
1887
1993
|
debugArtifactsDir,
|
|
1888
1994
|
`session-${_checkName || "unknown"}-${timestamp}.json`
|
|
1889
1995
|
);
|
|
@@ -1898,7 +2004,7 @@ $ ${cliCommand}
|
|
|
1898
2004
|
latestResponse: response
|
|
1899
2005
|
};
|
|
1900
2006
|
fs12.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1901
|
-
const sessionTxtFile =
|
|
2007
|
+
const sessionTxtFile = path13.join(
|
|
1902
2008
|
debugArtifactsDir,
|
|
1903
2009
|
`session-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1904
2010
|
);
|
|
@@ -1942,10 +2048,10 @@ ${"=".repeat(60)}
|
|
|
1942
2048
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1943
2049
|
try {
|
|
1944
2050
|
const fs12 = require("fs");
|
|
1945
|
-
const
|
|
2051
|
+
const path13 = require("path");
|
|
1946
2052
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1947
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1948
|
-
const responseFile =
|
|
2053
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path13.join(process.cwd(), "debug-artifacts");
|
|
2054
|
+
const responseFile = path13.join(
|
|
1949
2055
|
debugArtifactsDir,
|
|
1950
2056
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1951
2057
|
);
|
|
@@ -2035,7 +2141,7 @@ ${"=".repeat(60)}
|
|
|
2035
2141
|
*/
|
|
2036
2142
|
async loadSchemaContent(schema) {
|
|
2037
2143
|
const fs12 = require("fs").promises;
|
|
2038
|
-
const
|
|
2144
|
+
const path13 = require("path");
|
|
2039
2145
|
if (typeof schema === "object" && schema !== null) {
|
|
2040
2146
|
log("\u{1F4CB} Using inline schema object from configuration");
|
|
2041
2147
|
return JSON.stringify(schema);
|
|
@@ -2048,12 +2154,12 @@ ${"=".repeat(60)}
|
|
|
2048
2154
|
}
|
|
2049
2155
|
} catch {
|
|
2050
2156
|
}
|
|
2051
|
-
if ((schema.startsWith("./") || schema.includes(".json")) && !
|
|
2157
|
+
if ((schema.startsWith("./") || schema.includes(".json")) && !path13.isAbsolute(schema)) {
|
|
2052
2158
|
if (schema.includes("..") || schema.includes("\0")) {
|
|
2053
2159
|
throw new Error("Invalid schema path: path traversal not allowed");
|
|
2054
2160
|
}
|
|
2055
2161
|
try {
|
|
2056
|
-
const schemaPath2 =
|
|
2162
|
+
const schemaPath2 = path13.resolve(process.cwd(), schema);
|
|
2057
2163
|
log(`\u{1F4CB} Loading custom schema from file: ${schemaPath2}`);
|
|
2058
2164
|
const schemaContent = await fs12.readFile(schemaPath2, "utf-8");
|
|
2059
2165
|
return schemaContent.trim();
|
|
@@ -2067,7 +2173,7 @@ ${"=".repeat(60)}
|
|
|
2067
2173
|
if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
|
|
2068
2174
|
throw new Error("Invalid schema name");
|
|
2069
2175
|
}
|
|
2070
|
-
const schemaPath =
|
|
2176
|
+
const schemaPath = path13.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
2071
2177
|
try {
|
|
2072
2178
|
const schemaContent = await fs12.readFile(schemaPath, "utf-8");
|
|
2073
2179
|
return schemaContent.trim();
|
|
@@ -2424,14 +2530,66 @@ var init_reviewer = __esm({
|
|
|
2424
2530
|
"No configuration provided. Please create a .visor.yaml file with check definitions. Built-in prompts have been removed - all checks must be explicitly configured."
|
|
2425
2531
|
);
|
|
2426
2532
|
}
|
|
2533
|
+
/**
|
|
2534
|
+
* Helper to check if a schema is comment-generating
|
|
2535
|
+
* Comment-generating schemas include:
|
|
2536
|
+
* - Built-in schemas: code-review, overview, plain, text
|
|
2537
|
+
* - Custom schemas with a "text" field in properties
|
|
2538
|
+
*/
|
|
2539
|
+
async isCommentGeneratingSchema(schema) {
|
|
2540
|
+
try {
|
|
2541
|
+
if (typeof schema === "string") {
|
|
2542
|
+
if (["code-review", "overview", "plain", "text"].includes(schema)) {
|
|
2543
|
+
return true;
|
|
2544
|
+
}
|
|
2545
|
+
const fs12 = require("fs").promises;
|
|
2546
|
+
const path13 = require("path");
|
|
2547
|
+
const sanitizedSchemaName = schema.replace(/[^a-zA-Z0-9-]/g, "");
|
|
2548
|
+
if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
|
|
2549
|
+
return false;
|
|
2550
|
+
}
|
|
2551
|
+
const schemaPath = path13.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
2552
|
+
try {
|
|
2553
|
+
const schemaContent = await fs12.readFile(schemaPath, "utf-8");
|
|
2554
|
+
const schemaObj = JSON.parse(schemaContent);
|
|
2555
|
+
const properties = schemaObj.properties;
|
|
2556
|
+
return !!(properties && "text" in properties);
|
|
2557
|
+
} catch {
|
|
2558
|
+
return false;
|
|
2559
|
+
}
|
|
2560
|
+
} else {
|
|
2561
|
+
const properties = schema.properties;
|
|
2562
|
+
return !!(properties && "text" in properties);
|
|
2563
|
+
}
|
|
2564
|
+
} catch {
|
|
2565
|
+
return false;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
/**
|
|
2569
|
+
* Filter check results to only include those that should post GitHub comments
|
|
2570
|
+
*/
|
|
2571
|
+
async filterCommentGeneratingChecks(checkResults, config) {
|
|
2572
|
+
const filtered = [];
|
|
2573
|
+
for (const r of checkResults) {
|
|
2574
|
+
const cfg = config.checks?.[r.checkName];
|
|
2575
|
+
const type = cfg?.type || "ai";
|
|
2576
|
+
const schema = cfg?.schema;
|
|
2577
|
+
let shouldPostComment = false;
|
|
2578
|
+
const isAICheck = type === "ai" || type === "claude-code";
|
|
2579
|
+
if (!schema || schema === "") {
|
|
2580
|
+
shouldPostComment = isAICheck;
|
|
2581
|
+
} else {
|
|
2582
|
+
shouldPostComment = await this.isCommentGeneratingSchema(schema);
|
|
2583
|
+
}
|
|
2584
|
+
if (shouldPostComment) {
|
|
2585
|
+
filtered.push(r);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
return filtered;
|
|
2589
|
+
}
|
|
2427
2590
|
async postReviewComment(owner, repo, prNumber, groupedResults, options = {}) {
|
|
2428
2591
|
for (const [groupName, checkResults] of Object.entries(groupedResults)) {
|
|
2429
|
-
const filteredResults = options.config ? checkResults.
|
|
2430
|
-
const cfg = options.config.checks?.[r.checkName];
|
|
2431
|
-
const t = cfg?.type || "";
|
|
2432
|
-
const isGitHubOps = t === "github" || r.group === "github" || t === "noop" || t === "command";
|
|
2433
|
-
return !isGitHubOps;
|
|
2434
|
-
}) : checkResults;
|
|
2592
|
+
const filteredResults = options.config ? await this.filterCommentGeneratingChecks(checkResults, options.config) : checkResults;
|
|
2435
2593
|
if (!filteredResults || filteredResults.length === 0) {
|
|
2436
2594
|
continue;
|
|
2437
2595
|
}
|
|
@@ -2564,14 +2722,14 @@ var init_reviewer = __esm({
|
|
|
2564
2722
|
saveDebugArtifact(debug) {
|
|
2565
2723
|
try {
|
|
2566
2724
|
const fs12 = require("fs");
|
|
2567
|
-
const
|
|
2568
|
-
const debugDir =
|
|
2725
|
+
const path13 = require("path");
|
|
2726
|
+
const debugDir = path13.join(process.cwd(), "debug-artifacts");
|
|
2569
2727
|
if (!fs12.existsSync(debugDir)) {
|
|
2570
2728
|
fs12.mkdirSync(debugDir, { recursive: true });
|
|
2571
2729
|
}
|
|
2572
2730
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2573
2731
|
const filename = `visor-debug-${timestamp}.md`;
|
|
2574
|
-
const filepath =
|
|
2732
|
+
const filepath = path13.join(debugDir, filename);
|
|
2575
2733
|
const content = [
|
|
2576
2734
|
`# Visor Debug Information`,
|
|
2577
2735
|
``,
|
|
@@ -2604,12 +2762,12 @@ var init_reviewer = __esm({
|
|
|
2604
2762
|
});
|
|
2605
2763
|
|
|
2606
2764
|
// src/git-repository-analyzer.ts
|
|
2607
|
-
var import_simple_git,
|
|
2765
|
+
var import_simple_git, path3, fs2, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
|
|
2608
2766
|
var init_git_repository_analyzer = __esm({
|
|
2609
2767
|
"src/git-repository-analyzer.ts"() {
|
|
2610
2768
|
"use strict";
|
|
2611
2769
|
import_simple_git = require("simple-git");
|
|
2612
|
-
|
|
2770
|
+
path3 = __toESM(require("path"));
|
|
2613
2771
|
fs2 = __toESM(require("fs"));
|
|
2614
2772
|
MAX_PATCH_SIZE = 50 * 1024;
|
|
2615
2773
|
GitRepositoryAnalyzer = class {
|
|
@@ -2824,7 +2982,7 @@ ${file.patch}`).join("\n\n");
|
|
|
2824
2982
|
console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
|
|
2825
2983
|
continue;
|
|
2826
2984
|
}
|
|
2827
|
-
const filePath =
|
|
2985
|
+
const filePath = path3.join(this.cwd, file);
|
|
2828
2986
|
const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
|
|
2829
2987
|
changes.push(fileChange);
|
|
2830
2988
|
}
|
|
@@ -3304,12 +3462,12 @@ var init_env_resolver = __esm({
|
|
|
3304
3462
|
});
|
|
3305
3463
|
|
|
3306
3464
|
// src/issue-filter.ts
|
|
3307
|
-
var fs3,
|
|
3465
|
+
var fs3, path4, IssueFilter;
|
|
3308
3466
|
var init_issue_filter = __esm({
|
|
3309
3467
|
"src/issue-filter.ts"() {
|
|
3310
3468
|
"use strict";
|
|
3311
3469
|
fs3 = __toESM(require("fs"));
|
|
3312
|
-
|
|
3470
|
+
path4 = __toESM(require("path"));
|
|
3313
3471
|
IssueFilter = class {
|
|
3314
3472
|
fileCache = /* @__PURE__ */ new Map();
|
|
3315
3473
|
suppressionEnabled;
|
|
@@ -3377,7 +3535,7 @@ var init_issue_filter = __esm({
|
|
|
3377
3535
|
return this.fileCache.get(filePath);
|
|
3378
3536
|
}
|
|
3379
3537
|
try {
|
|
3380
|
-
const resolvedPath =
|
|
3538
|
+
const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.join(workingDir, filePath);
|
|
3381
3539
|
if (!fs3.existsSync(resolvedPath)) {
|
|
3382
3540
|
if (fs3.existsSync(filePath)) {
|
|
3383
3541
|
const content2 = fs3.readFileSync(filePath, "utf8");
|
|
@@ -5127,7 +5285,8 @@ var init_log_check_provider = __esm({
|
|
|
5127
5285
|
dependencyResults,
|
|
5128
5286
|
includePrContext,
|
|
5129
5287
|
includeDependencies,
|
|
5130
|
-
includeMetadata
|
|
5288
|
+
includeMetadata,
|
|
5289
|
+
config.__outputHistory
|
|
5131
5290
|
);
|
|
5132
5291
|
const renderedMessage = await this.liquid.parseAndRender(message, templateContext);
|
|
5133
5292
|
const logOutput = this.formatLogOutput(
|
|
@@ -5148,7 +5307,7 @@ var init_log_check_provider = __esm({
|
|
|
5148
5307
|
logOutput
|
|
5149
5308
|
};
|
|
5150
5309
|
}
|
|
5151
|
-
buildTemplateContext(prInfo, dependencyResults, _includePrContext = true, _includeDependencies = true, includeMetadata = true) {
|
|
5310
|
+
buildTemplateContext(prInfo, dependencyResults, _includePrContext = true, _includeDependencies = true, includeMetadata = true, outputHistory) {
|
|
5152
5311
|
const context2 = {};
|
|
5153
5312
|
context2.pr = {
|
|
5154
5313
|
number: prInfo.number,
|
|
@@ -5172,6 +5331,7 @@ var init_log_check_provider = __esm({
|
|
|
5172
5331
|
if (dependencyResults) {
|
|
5173
5332
|
const dependencies = {};
|
|
5174
5333
|
const outputs = {};
|
|
5334
|
+
const history = {};
|
|
5175
5335
|
context2.dependencyCount = dependencyResults.size;
|
|
5176
5336
|
for (const [checkName, result] of dependencyResults.entries()) {
|
|
5177
5337
|
dependencies[checkName] = {
|
|
@@ -5182,6 +5342,12 @@ var init_log_check_provider = __esm({
|
|
|
5182
5342
|
const summary = result;
|
|
5183
5343
|
outputs[checkName] = summary.output !== void 0 ? summary.output : summary;
|
|
5184
5344
|
}
|
|
5345
|
+
if (outputHistory) {
|
|
5346
|
+
for (const [checkName, historyArray] of outputHistory) {
|
|
5347
|
+
history[checkName] = historyArray;
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
outputs.history = history;
|
|
5185
5351
|
context2.dependencies = dependencies;
|
|
5186
5352
|
context2.outputs = outputs;
|
|
5187
5353
|
}
|
|
@@ -6084,7 +6250,10 @@ var init_command_check_provider = __esm({
|
|
|
6084
6250
|
},
|
|
6085
6251
|
files: prInfo.files,
|
|
6086
6252
|
fileCount: prInfo.files.length,
|
|
6087
|
-
outputs: this.buildOutputContext(
|
|
6253
|
+
outputs: this.buildOutputContext(
|
|
6254
|
+
dependencyResults,
|
|
6255
|
+
config.__outputHistory
|
|
6256
|
+
),
|
|
6088
6257
|
env: this.getSafeEnvironmentVariables()
|
|
6089
6258
|
};
|
|
6090
6259
|
logger.debug(
|
|
@@ -6810,16 +6979,23 @@ ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
|
|
|
6810
6979
|
};
|
|
6811
6980
|
}
|
|
6812
6981
|
}
|
|
6813
|
-
buildOutputContext(dependencyResults) {
|
|
6982
|
+
buildOutputContext(dependencyResults, outputHistory) {
|
|
6814
6983
|
if (!dependencyResults) {
|
|
6815
6984
|
return {};
|
|
6816
6985
|
}
|
|
6817
6986
|
const outputs = {};
|
|
6987
|
+
const history = {};
|
|
6818
6988
|
for (const [checkName, result] of dependencyResults) {
|
|
6819
6989
|
const summary = result;
|
|
6820
6990
|
const value = summary.output !== void 0 ? summary.output : summary;
|
|
6821
6991
|
outputs[checkName] = this.makeJsonSmart(value);
|
|
6822
6992
|
}
|
|
6993
|
+
if (outputHistory) {
|
|
6994
|
+
for (const [checkName, historyArray] of outputHistory) {
|
|
6995
|
+
history[checkName] = historyArray.map((val) => this.makeJsonSmart(val));
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
outputs.history = history;
|
|
6823
6999
|
return outputs;
|
|
6824
7000
|
}
|
|
6825
7001
|
/**
|
|
@@ -7377,7 +7553,12 @@ var init_memory_check_provider = __esm({
|
|
|
7377
7553
|
const key = config.key;
|
|
7378
7554
|
const namespace = config.namespace;
|
|
7379
7555
|
const memoryStore = MemoryStore.getInstance();
|
|
7380
|
-
const templateContext = this.buildTemplateContext(
|
|
7556
|
+
const templateContext = this.buildTemplateContext(
|
|
7557
|
+
prInfo,
|
|
7558
|
+
dependencyResults,
|
|
7559
|
+
memoryStore,
|
|
7560
|
+
config.__outputHistory
|
|
7561
|
+
);
|
|
7381
7562
|
let result;
|
|
7382
7563
|
try {
|
|
7383
7564
|
switch (operation) {
|
|
@@ -7660,7 +7841,7 @@ var init_memory_check_provider = __esm({
|
|
|
7660
7841
|
/**
|
|
7661
7842
|
* Build template context for Liquid and JS evaluation
|
|
7662
7843
|
*/
|
|
7663
|
-
buildTemplateContext(prInfo, dependencyResults, memoryStore) {
|
|
7844
|
+
buildTemplateContext(prInfo, dependencyResults, memoryStore, outputHistory) {
|
|
7664
7845
|
const context2 = {};
|
|
7665
7846
|
context2.pr = {
|
|
7666
7847
|
number: prInfo.number,
|
|
@@ -7679,14 +7860,21 @@ var init_memory_check_provider = __esm({
|
|
|
7679
7860
|
changes: f.changes
|
|
7680
7861
|
}))
|
|
7681
7862
|
};
|
|
7863
|
+
const outputs = {};
|
|
7864
|
+
const history = {};
|
|
7682
7865
|
if (dependencyResults) {
|
|
7683
|
-
const outputs = {};
|
|
7684
7866
|
for (const [checkName, result] of dependencyResults.entries()) {
|
|
7685
7867
|
const summary = result;
|
|
7686
7868
|
outputs[checkName] = summary.output !== void 0 ? summary.output : summary;
|
|
7687
7869
|
}
|
|
7688
|
-
context2.outputs = outputs;
|
|
7689
7870
|
}
|
|
7871
|
+
if (outputHistory) {
|
|
7872
|
+
for (const [checkName, historyArray] of outputHistory) {
|
|
7873
|
+
history[checkName] = historyArray;
|
|
7874
|
+
}
|
|
7875
|
+
}
|
|
7876
|
+
outputs.history = history;
|
|
7877
|
+
context2.outputs = outputs;
|
|
7690
7878
|
if (memoryStore) {
|
|
7691
7879
|
context2.memory = {
|
|
7692
7880
|
get: (key, ns) => memoryStore.get(key, ns),
|
|
@@ -9224,7 +9412,7 @@ function resolveTargetPath(outDir) {
|
|
|
9224
9412
|
}
|
|
9225
9413
|
if (CURRENT_FILE) return CURRENT_FILE;
|
|
9226
9414
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9227
|
-
CURRENT_FILE =
|
|
9415
|
+
CURRENT_FILE = path9.join(outDir, `${ts}.ndjson`);
|
|
9228
9416
|
return CURRENT_FILE;
|
|
9229
9417
|
}
|
|
9230
9418
|
function isEnabled() {
|
|
@@ -9254,7 +9442,7 @@ async function flushNdjson() {
|
|
|
9254
9442
|
function emitNdjsonFallback(name, attrs) {
|
|
9255
9443
|
try {
|
|
9256
9444
|
if (!isEnabled()) return;
|
|
9257
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
9445
|
+
const outDir = process.env.VISOR_TRACE_DIR || path9.join(process.cwd(), "output", "traces");
|
|
9258
9446
|
const line = JSON.stringify({ name, attributes: attrs }) + "\n";
|
|
9259
9447
|
appendAsync(outDir, line);
|
|
9260
9448
|
} catch {
|
|
@@ -9263,18 +9451,18 @@ function emitNdjsonFallback(name, attrs) {
|
|
|
9263
9451
|
function emitNdjsonSpanWithEvents(name, attrs, events) {
|
|
9264
9452
|
try {
|
|
9265
9453
|
if (!isEnabled()) return;
|
|
9266
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
9454
|
+
const outDir = process.env.VISOR_TRACE_DIR || path9.join(process.cwd(), "output", "traces");
|
|
9267
9455
|
const line = JSON.stringify({ name, attributes: attrs, events }) + "\n";
|
|
9268
9456
|
appendAsync(outDir, line);
|
|
9269
9457
|
} catch {
|
|
9270
9458
|
}
|
|
9271
9459
|
}
|
|
9272
|
-
var fs8,
|
|
9460
|
+
var fs8, path9, CURRENT_FILE, dirReady, writeChain;
|
|
9273
9461
|
var init_fallback_ndjson = __esm({
|
|
9274
9462
|
"src/telemetry/fallback-ndjson.ts"() {
|
|
9275
9463
|
"use strict";
|
|
9276
9464
|
fs8 = __toESM(require("fs"));
|
|
9277
|
-
|
|
9465
|
+
path9 = __toESM(require("path"));
|
|
9278
9466
|
CURRENT_FILE = null;
|
|
9279
9467
|
dirReady = false;
|
|
9280
9468
|
writeChain = Promise.resolve();
|
|
@@ -9750,10 +9938,6 @@ var init_failure_condition_evaluator = __esm({
|
|
|
9750
9938
|
if (!Array.isArray(issues2)) return false;
|
|
9751
9939
|
return issues2.some((issue) => issue.file?.includes(pattern));
|
|
9752
9940
|
};
|
|
9753
|
-
const hasSuggestion = (suggestions2, text) => {
|
|
9754
|
-
if (!Array.isArray(suggestions2)) return false;
|
|
9755
|
-
return suggestions2.some((s) => s.toLowerCase().includes(text.toLowerCase()));
|
|
9756
|
-
};
|
|
9757
9941
|
const hasIssueWith = hasIssue;
|
|
9758
9942
|
const hasFileWith = hasFileMatching;
|
|
9759
9943
|
const permissionHelpers = createPermissionHelpers(
|
|
@@ -9768,7 +9952,6 @@ var init_failure_condition_evaluator = __esm({
|
|
|
9768
9952
|
const isFirstTimer2 = permissionHelpers.isFirstTimer;
|
|
9769
9953
|
const output = context2.output || {};
|
|
9770
9954
|
const issues = output.issues || [];
|
|
9771
|
-
const suggestions = [];
|
|
9772
9955
|
const metadata = context2.metadata || {
|
|
9773
9956
|
checkName: context2.checkName || "",
|
|
9774
9957
|
schema: context2.schema || "",
|
|
@@ -9812,7 +9995,6 @@ var init_failure_condition_evaluator = __esm({
|
|
|
9812
9995
|
memory: memoryAccessor,
|
|
9813
9996
|
// Legacy compatibility variables
|
|
9814
9997
|
issues,
|
|
9815
|
-
suggestions,
|
|
9816
9998
|
metadata,
|
|
9817
9999
|
criticalIssues,
|
|
9818
10000
|
errorIssues,
|
|
@@ -9841,7 +10023,6 @@ var init_failure_condition_evaluator = __esm({
|
|
|
9841
10023
|
hasIssue,
|
|
9842
10024
|
countIssues,
|
|
9843
10025
|
hasFileMatching,
|
|
9844
|
-
hasSuggestion,
|
|
9845
10026
|
hasIssueWith,
|
|
9846
10027
|
hasFileWith,
|
|
9847
10028
|
// Permission helpers
|
|
@@ -10111,6 +10292,7 @@ var GitHubCheckService;
|
|
|
10111
10292
|
var init_github_check_service = __esm({
|
|
10112
10293
|
"src/github-check-service.ts"() {
|
|
10113
10294
|
"use strict";
|
|
10295
|
+
init_footer();
|
|
10114
10296
|
GitHubCheckService = class {
|
|
10115
10297
|
octokit;
|
|
10116
10298
|
maxAnnotations = 50;
|
|
@@ -10351,11 +10533,7 @@ Please check your configuration and try again.`
|
|
|
10351
10533
|
});
|
|
10352
10534
|
}
|
|
10353
10535
|
sections.push("");
|
|
10354
|
-
sections.push(
|
|
10355
|
-
sections.push("");
|
|
10356
|
-
sections.push(
|
|
10357
|
-
"*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*"
|
|
10358
|
-
);
|
|
10536
|
+
sections.push(generateFooter());
|
|
10359
10537
|
return sections.join("\n");
|
|
10360
10538
|
}
|
|
10361
10539
|
/**
|
|
@@ -10573,12 +10751,12 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
|
|
|
10573
10751
|
addEvent("diagram.block", { check: checkName, origin, code });
|
|
10574
10752
|
addDiagramBlock(origin);
|
|
10575
10753
|
if (process.env.VISOR_TRACE_REPORT === "true") {
|
|
10576
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
10754
|
+
const outDir = process.env.VISOR_TRACE_DIR || path10.join(process.cwd(), "output", "traces");
|
|
10577
10755
|
try {
|
|
10578
10756
|
if (!fs9.existsSync(outDir)) fs9.mkdirSync(outDir, { recursive: true });
|
|
10579
10757
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
10580
|
-
const jsonPath =
|
|
10581
|
-
const htmlPath =
|
|
10758
|
+
const jsonPath = path10.join(outDir, `${ts}.trace.json`);
|
|
10759
|
+
const htmlPath = path10.join(outDir, `${ts}.report.html`);
|
|
10582
10760
|
let data = { spans: [] };
|
|
10583
10761
|
if (fs9.existsSync(jsonPath)) {
|
|
10584
10762
|
try {
|
|
@@ -10608,14 +10786,14 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
|
|
|
10608
10786
|
}
|
|
10609
10787
|
return count;
|
|
10610
10788
|
}
|
|
10611
|
-
var fs9,
|
|
10789
|
+
var fs9, path10, MERMAID_RE;
|
|
10612
10790
|
var init_mermaid_telemetry = __esm({
|
|
10613
10791
|
"src/utils/mermaid-telemetry.ts"() {
|
|
10614
10792
|
"use strict";
|
|
10615
10793
|
init_trace_helpers();
|
|
10616
10794
|
init_metrics2();
|
|
10617
10795
|
fs9 = __toESM(require("fs"));
|
|
10618
|
-
|
|
10796
|
+
path10 = __toESM(require("path"));
|
|
10619
10797
|
MERMAID_RE = /```mermaid\s*\n([\s\S]*?)\n```/gi;
|
|
10620
10798
|
}
|
|
10621
10799
|
});
|
|
@@ -10681,6 +10859,8 @@ var init_check_execution_engine = __esm({
|
|
|
10681
10859
|
webhookContext;
|
|
10682
10860
|
routingSandbox;
|
|
10683
10861
|
executionStats = /* @__PURE__ */ new Map();
|
|
10862
|
+
// Track history of all outputs for each check (useful for loops and goto)
|
|
10863
|
+
outputHistory = /* @__PURE__ */ new Map();
|
|
10684
10864
|
// Event override to simulate alternate event (used during routing goto)
|
|
10685
10865
|
routingEventOverride;
|
|
10686
10866
|
// Cached GitHub context for context elevation when running in Actions
|
|
@@ -10924,6 +11104,8 @@ ${expr}
|
|
|
10924
11104
|
transform_js: targetCfg.transform_js,
|
|
10925
11105
|
env: targetCfg.env,
|
|
10926
11106
|
forEach: targetCfg.forEach,
|
|
11107
|
+
// Pass output history for loop/goto scenarios
|
|
11108
|
+
__outputHistory: this.outputHistory,
|
|
10927
11109
|
// Include provider-specific keys (e.g., op/values for github)
|
|
10928
11110
|
...targetCfg,
|
|
10929
11111
|
ai: {
|
|
@@ -10991,6 +11173,10 @@ ${expr}
|
|
|
10991
11173
|
timestamp: Date.now()
|
|
10992
11174
|
}));
|
|
10993
11175
|
const enriched = { ...r, issues: enrichedIssues };
|
|
11176
|
+
const enrichedWithOutput = enriched;
|
|
11177
|
+
if (enrichedWithOutput.output !== void 0) {
|
|
11178
|
+
this.trackOutputHistory(target, enrichedWithOutput.output);
|
|
11179
|
+
}
|
|
10994
11180
|
resultsMap?.set(target, enriched);
|
|
10995
11181
|
if (debug) log2(`\u{1F527} Debug: inline executed '${target}', issues: ${enrichedIssues.length}`);
|
|
10996
11182
|
return enriched;
|
|
@@ -11759,6 +11945,8 @@ ${expr}
|
|
|
11759
11945
|
ai_model: checkConfig.ai_model || config.ai_model,
|
|
11760
11946
|
// Pass claude_code config if present
|
|
11761
11947
|
claude_code: checkConfig.claude_code,
|
|
11948
|
+
// Pass output history for loop/goto scenarios
|
|
11949
|
+
__outputHistory: this.outputHistory,
|
|
11762
11950
|
// Pass any provider-specific config
|
|
11763
11951
|
...checkConfig
|
|
11764
11952
|
};
|
|
@@ -11795,10 +11983,14 @@ ${expr}
|
|
|
11795
11983
|
}
|
|
11796
11984
|
}
|
|
11797
11985
|
const content = await this.renderCheckContent(checkName, result, checkConfig, prInfo);
|
|
11986
|
+
let group = checkConfig.group || "default";
|
|
11987
|
+
if (config?.output?.pr_comment?.group_by === "check" && !checkConfig.group) {
|
|
11988
|
+
group = checkName;
|
|
11989
|
+
}
|
|
11798
11990
|
return {
|
|
11799
11991
|
checkName,
|
|
11800
11992
|
content,
|
|
11801
|
-
group
|
|
11993
|
+
group,
|
|
11802
11994
|
output: result.output,
|
|
11803
11995
|
debug: result.debug,
|
|
11804
11996
|
issues: result.issues
|
|
@@ -11937,16 +12129,19 @@ ${expr}
|
|
|
11937
12129
|
}
|
|
11938
12130
|
];
|
|
11939
12131
|
}
|
|
12132
|
+
let group = checkConfig.group || "default";
|
|
12133
|
+
if (config?.output?.pr_comment?.group_by === "check" && !checkConfig.group) {
|
|
12134
|
+
group = checkName;
|
|
12135
|
+
}
|
|
11940
12136
|
const checkResult = {
|
|
11941
12137
|
checkName,
|
|
11942
12138
|
content,
|
|
11943
|
-
group
|
|
12139
|
+
group,
|
|
11944
12140
|
output: checkSummary.output,
|
|
11945
12141
|
debug: reviewSummary.debug,
|
|
11946
12142
|
issues: issuesForCheck
|
|
11947
12143
|
// Include structured issues + rendering error if any
|
|
11948
12144
|
};
|
|
11949
|
-
const group = checkResult.group;
|
|
11950
12145
|
if (!groupedResults[group]) {
|
|
11951
12146
|
groupedResults[group] = [];
|
|
11952
12147
|
}
|
|
@@ -11964,7 +12159,7 @@ ${expr}
|
|
|
11964
12159
|
* - Enforcing .liquid file extension
|
|
11965
12160
|
*/
|
|
11966
12161
|
async validateTemplatePath(templatePath) {
|
|
11967
|
-
const
|
|
12162
|
+
const path13 = await import("path");
|
|
11968
12163
|
if (!templatePath || typeof templatePath !== "string" || templatePath.trim() === "") {
|
|
11969
12164
|
throw new Error("Template path must be a non-empty string");
|
|
11970
12165
|
}
|
|
@@ -11974,7 +12169,7 @@ ${expr}
|
|
|
11974
12169
|
if (!templatePath.endsWith(".liquid")) {
|
|
11975
12170
|
throw new Error("Template file must have .liquid extension");
|
|
11976
12171
|
}
|
|
11977
|
-
if (
|
|
12172
|
+
if (path13.isAbsolute(templatePath)) {
|
|
11978
12173
|
throw new Error("Template path must be relative to project directory");
|
|
11979
12174
|
}
|
|
11980
12175
|
if (templatePath.includes("..")) {
|
|
@@ -11988,14 +12183,14 @@ ${expr}
|
|
|
11988
12183
|
if (!projectRoot || typeof projectRoot !== "string") {
|
|
11989
12184
|
throw new Error("Unable to determine project root directory");
|
|
11990
12185
|
}
|
|
11991
|
-
const resolvedPath =
|
|
11992
|
-
const resolvedProjectRoot =
|
|
12186
|
+
const resolvedPath = path13.resolve(projectRoot, templatePath);
|
|
12187
|
+
const resolvedProjectRoot = path13.resolve(projectRoot);
|
|
11993
12188
|
if (!resolvedPath || !resolvedProjectRoot || resolvedPath === "" || resolvedProjectRoot === "") {
|
|
11994
12189
|
throw new Error(
|
|
11995
12190
|
`Unable to resolve template path: projectRoot="${projectRoot}", templatePath="${templatePath}", resolvedPath="${resolvedPath}", resolvedProjectRoot="${resolvedProjectRoot}"`
|
|
11996
12191
|
);
|
|
11997
12192
|
}
|
|
11998
|
-
if (!resolvedPath.startsWith(resolvedProjectRoot +
|
|
12193
|
+
if (!resolvedPath.startsWith(resolvedProjectRoot + path13.sep) && resolvedPath !== resolvedProjectRoot) {
|
|
11999
12194
|
throw new Error("Template path escapes project directory");
|
|
12000
12195
|
}
|
|
12001
12196
|
return resolvedPath;
|
|
@@ -12040,7 +12235,7 @@ ${expr}
|
|
|
12040
12235
|
}
|
|
12041
12236
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
12042
12237
|
const fs12 = await import("fs/promises");
|
|
12043
|
-
const
|
|
12238
|
+
const path13 = await import("path");
|
|
12044
12239
|
const liquid = createExtendedLiquid2({
|
|
12045
12240
|
trimTagLeft: false,
|
|
12046
12241
|
trimTagRight: false,
|
|
@@ -12074,7 +12269,7 @@ ${expr}
|
|
|
12074
12269
|
if (!sanitizedSchema) {
|
|
12075
12270
|
throw new Error("Invalid schema name");
|
|
12076
12271
|
}
|
|
12077
|
-
const templatePath =
|
|
12272
|
+
const templatePath = path13.join(__dirname, `../output/${sanitizedSchema}/template.liquid`);
|
|
12078
12273
|
templateContent = await fs12.readFile(templatePath, "utf-8");
|
|
12079
12274
|
if (sanitizedSchema === "issue-assistant") {
|
|
12080
12275
|
enrichAssistantContext = true;
|
|
@@ -12847,6 +13042,10 @@ ${expr}
|
|
|
12847
13042
|
itemResult.issues || [],
|
|
12848
13043
|
itemResult.output
|
|
12849
13044
|
);
|
|
13045
|
+
const itemOutput = itemResult.output;
|
|
13046
|
+
if (itemOutput !== void 0) {
|
|
13047
|
+
this.trackOutputHistory(checkName, itemOutput);
|
|
13048
|
+
}
|
|
12850
13049
|
const descendantSet = (() => {
|
|
12851
13050
|
const visited = /* @__PURE__ */ new Set();
|
|
12852
13051
|
const stack = [checkName];
|
|
@@ -13477,6 +13676,10 @@ ${error.stack || ""}` : String(error);
|
|
|
13477
13676
|
]);
|
|
13478
13677
|
} catch {
|
|
13479
13678
|
}
|
|
13679
|
+
const reviewResultWithOutput = reviewResult;
|
|
13680
|
+
if (reviewResultWithOutput.output !== void 0) {
|
|
13681
|
+
this.trackOutputHistory(checkName, reviewResultWithOutput.output);
|
|
13682
|
+
}
|
|
13480
13683
|
results.set(checkName, reviewResult);
|
|
13481
13684
|
} else {
|
|
13482
13685
|
const errorSummary = {
|
|
@@ -14720,6 +14923,16 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
14720
14923
|
stats.outputsProduced = (stats.outputsProduced || 0) + 1;
|
|
14721
14924
|
}
|
|
14722
14925
|
}
|
|
14926
|
+
/**
|
|
14927
|
+
* Track output in history for loop/goto scenarios
|
|
14928
|
+
*/
|
|
14929
|
+
trackOutputHistory(checkName, output) {
|
|
14930
|
+
if (output === void 0) return;
|
|
14931
|
+
if (!this.outputHistory.has(checkName)) {
|
|
14932
|
+
this.outputHistory.set(checkName, []);
|
|
14933
|
+
}
|
|
14934
|
+
this.outputHistory.get(checkName).push(output);
|
|
14935
|
+
}
|
|
14723
14936
|
/**
|
|
14724
14937
|
* Record that a check was skipped
|
|
14725
14938
|
*/
|
|
@@ -16225,13 +16438,13 @@ init_check_execution_engine();
|
|
|
16225
16438
|
// src/config.ts
|
|
16226
16439
|
var yaml2 = __toESM(require("js-yaml"));
|
|
16227
16440
|
var fs11 = __toESM(require("fs"));
|
|
16228
|
-
var
|
|
16441
|
+
var path12 = __toESM(require("path"));
|
|
16229
16442
|
init_logger();
|
|
16230
16443
|
var import_simple_git2 = __toESM(require("simple-git"));
|
|
16231
16444
|
|
|
16232
16445
|
// src/utils/config-loader.ts
|
|
16233
16446
|
var fs10 = __toESM(require("fs"));
|
|
16234
|
-
var
|
|
16447
|
+
var path11 = __toESM(require("path"));
|
|
16235
16448
|
var yaml = __toESM(require("js-yaml"));
|
|
16236
16449
|
var ConfigLoader = class {
|
|
16237
16450
|
constructor(options = {}) {
|
|
@@ -16312,7 +16525,7 @@ var ConfigLoader = class {
|
|
|
16312
16525
|
return source.toLowerCase();
|
|
16313
16526
|
case "local" /* LOCAL */:
|
|
16314
16527
|
const basePath = this.options.baseDir || process.cwd();
|
|
16315
|
-
return
|
|
16528
|
+
return path11.resolve(basePath, source);
|
|
16316
16529
|
default:
|
|
16317
16530
|
return source;
|
|
16318
16531
|
}
|
|
@@ -16322,7 +16535,7 @@ var ConfigLoader = class {
|
|
|
16322
16535
|
*/
|
|
16323
16536
|
async fetchLocalConfig(filePath) {
|
|
16324
16537
|
const basePath = this.options.baseDir || process.cwd();
|
|
16325
|
-
const resolvedPath =
|
|
16538
|
+
const resolvedPath = path11.resolve(basePath, filePath);
|
|
16326
16539
|
this.validateLocalPath(resolvedPath);
|
|
16327
16540
|
if (!fs10.existsSync(resolvedPath)) {
|
|
16328
16541
|
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
@@ -16334,7 +16547,7 @@ var ConfigLoader = class {
|
|
|
16334
16547
|
throw new Error(`Invalid YAML in configuration file: ${resolvedPath}`);
|
|
16335
16548
|
}
|
|
16336
16549
|
const previousBaseDir = this.options.baseDir;
|
|
16337
|
-
this.options.baseDir =
|
|
16550
|
+
this.options.baseDir = path11.dirname(resolvedPath);
|
|
16338
16551
|
try {
|
|
16339
16552
|
if (config.extends) {
|
|
16340
16553
|
const processedConfig = await this.processExtends(config);
|
|
@@ -16414,14 +16627,14 @@ var ConfigLoader = class {
|
|
|
16414
16627
|
async fetchDefaultConfig() {
|
|
16415
16628
|
const possiblePaths = [
|
|
16416
16629
|
// When running as GitHub Action (bundled in dist/)
|
|
16417
|
-
|
|
16630
|
+
path11.join(__dirname, "defaults", ".visor.yaml"),
|
|
16418
16631
|
// When running from source
|
|
16419
|
-
|
|
16632
|
+
path11.join(__dirname, "..", "..", "defaults", ".visor.yaml"),
|
|
16420
16633
|
// Try via package root
|
|
16421
|
-
this.findPackageRoot() ?
|
|
16634
|
+
this.findPackageRoot() ? path11.join(this.findPackageRoot(), "defaults", ".visor.yaml") : "",
|
|
16422
16635
|
// GitHub Action environment variable
|
|
16423
|
-
process.env.GITHUB_ACTION_PATH ?
|
|
16424
|
-
process.env.GITHUB_ACTION_PATH ?
|
|
16636
|
+
process.env.GITHUB_ACTION_PATH ? path11.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml") : "",
|
|
16637
|
+
process.env.GITHUB_ACTION_PATH ? path11.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml") : ""
|
|
16425
16638
|
].filter((p) => p);
|
|
16426
16639
|
let defaultConfigPath;
|
|
16427
16640
|
for (const possiblePath of possiblePaths) {
|
|
@@ -16511,8 +16724,8 @@ var ConfigLoader = class {
|
|
|
16511
16724
|
*/
|
|
16512
16725
|
validateLocalPath(resolvedPath) {
|
|
16513
16726
|
const projectRoot = this.options.projectRoot || process.cwd();
|
|
16514
|
-
const normalizedPath =
|
|
16515
|
-
const normalizedRoot =
|
|
16727
|
+
const normalizedPath = path11.normalize(resolvedPath);
|
|
16728
|
+
const normalizedRoot = path11.normalize(projectRoot);
|
|
16516
16729
|
if (!normalizedPath.startsWith(normalizedRoot)) {
|
|
16517
16730
|
throw new Error(
|
|
16518
16731
|
`Security error: Path traversal detected. Cannot access files outside project root: ${projectRoot}`
|
|
@@ -16538,9 +16751,9 @@ var ConfigLoader = class {
|
|
|
16538
16751
|
*/
|
|
16539
16752
|
findPackageRoot() {
|
|
16540
16753
|
let currentDir = __dirname;
|
|
16541
|
-
const root =
|
|
16754
|
+
const root = path11.parse(currentDir).root;
|
|
16542
16755
|
while (currentDir !== root) {
|
|
16543
|
-
const packageJsonPath =
|
|
16756
|
+
const packageJsonPath = path11.join(currentDir, "package.json");
|
|
16544
16757
|
if (fs10.existsSync(packageJsonPath)) {
|
|
16545
16758
|
try {
|
|
16546
16759
|
const packageJson = JSON.parse(fs10.readFileSync(packageJsonPath, "utf8"));
|
|
@@ -16550,7 +16763,7 @@ var ConfigLoader = class {
|
|
|
16550
16763
|
} catch {
|
|
16551
16764
|
}
|
|
16552
16765
|
}
|
|
16553
|
-
currentDir =
|
|
16766
|
+
currentDir = path11.dirname(currentDir);
|
|
16554
16767
|
}
|
|
16555
16768
|
return null;
|
|
16556
16769
|
}
|
|
@@ -16573,6 +16786,16 @@ var ConfigLoader = class {
|
|
|
16573
16786
|
init_config_merger();
|
|
16574
16787
|
var import_ajv = __toESM(require("ajv"));
|
|
16575
16788
|
var import_ajv_formats = __toESM(require("ajv-formats"));
|
|
16789
|
+
var VALID_EVENT_TRIGGERS = [
|
|
16790
|
+
"pr_opened",
|
|
16791
|
+
"pr_updated",
|
|
16792
|
+
"pr_closed",
|
|
16793
|
+
"issue_opened",
|
|
16794
|
+
"issue_comment",
|
|
16795
|
+
"manual",
|
|
16796
|
+
"schedule",
|
|
16797
|
+
"webhook_received"
|
|
16798
|
+
];
|
|
16576
16799
|
var ConfigManager = class {
|
|
16577
16800
|
validCheckTypes = [
|
|
16578
16801
|
"ai",
|
|
@@ -16581,20 +16804,13 @@ var ConfigManager = class {
|
|
|
16581
16804
|
"http",
|
|
16582
16805
|
"http_input",
|
|
16583
16806
|
"http_client",
|
|
16807
|
+
"memory",
|
|
16584
16808
|
"noop",
|
|
16585
16809
|
"log",
|
|
16810
|
+
"memory",
|
|
16586
16811
|
"github"
|
|
16587
16812
|
];
|
|
16588
|
-
validEventTriggers = [
|
|
16589
|
-
"pr_opened",
|
|
16590
|
-
"pr_updated",
|
|
16591
|
-
"pr_closed",
|
|
16592
|
-
"issue_opened",
|
|
16593
|
-
"issue_comment",
|
|
16594
|
-
"manual",
|
|
16595
|
-
"schedule",
|
|
16596
|
-
"webhook_received"
|
|
16597
|
-
];
|
|
16813
|
+
validEventTriggers = [...VALID_EVENT_TRIGGERS];
|
|
16598
16814
|
validOutputFormats = ["table", "json", "markdown", "sarif"];
|
|
16599
16815
|
validGroupByOptions = ["check", "file", "severity", "group"];
|
|
16600
16816
|
/**
|
|
@@ -16602,24 +16818,25 @@ var ConfigManager = class {
|
|
|
16602
16818
|
*/
|
|
16603
16819
|
async loadConfig(configPath, options = {}) {
|
|
16604
16820
|
const { validate = true, mergeDefaults = true, allowedRemotePatterns } = options;
|
|
16821
|
+
const resolvedPath = path12.isAbsolute(configPath) ? configPath : path12.resolve(process.cwd(), configPath);
|
|
16605
16822
|
try {
|
|
16606
|
-
if (!fs11.existsSync(
|
|
16607
|
-
throw new Error(`Configuration file not found: ${
|
|
16823
|
+
if (!fs11.existsSync(resolvedPath)) {
|
|
16824
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
16608
16825
|
}
|
|
16609
|
-
const configContent = fs11.readFileSync(
|
|
16826
|
+
const configContent = fs11.readFileSync(resolvedPath, "utf8");
|
|
16610
16827
|
let parsedConfig;
|
|
16611
16828
|
try {
|
|
16612
16829
|
parsedConfig = yaml2.load(configContent);
|
|
16613
16830
|
} catch (yamlError) {
|
|
16614
16831
|
const errorMessage = yamlError instanceof Error ? yamlError.message : String(yamlError);
|
|
16615
|
-
throw new Error(`Invalid YAML syntax in ${
|
|
16832
|
+
throw new Error(`Invalid YAML syntax in ${resolvedPath}: ${errorMessage}`);
|
|
16616
16833
|
}
|
|
16617
16834
|
if (!parsedConfig || typeof parsedConfig !== "object") {
|
|
16618
16835
|
throw new Error("Configuration file must contain a valid YAML object");
|
|
16619
16836
|
}
|
|
16620
16837
|
if (parsedConfig.extends) {
|
|
16621
16838
|
const loaderOptions = {
|
|
16622
|
-
baseDir:
|
|
16839
|
+
baseDir: path12.dirname(resolvedPath),
|
|
16623
16840
|
allowRemote: this.isRemoteExtendsAllowed(),
|
|
16624
16841
|
maxDepth: 10,
|
|
16625
16842
|
allowedRemotePatterns
|
|
@@ -16651,12 +16868,12 @@ var ConfigManager = class {
|
|
|
16651
16868
|
throw error;
|
|
16652
16869
|
}
|
|
16653
16870
|
if (error.message.includes("ENOENT")) {
|
|
16654
|
-
throw new Error(`Configuration file not found: ${
|
|
16871
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
16655
16872
|
}
|
|
16656
16873
|
if (error.message.includes("EPERM")) {
|
|
16657
|
-
throw new Error(`Permission denied reading configuration file: ${
|
|
16874
|
+
throw new Error(`Permission denied reading configuration file: ${resolvedPath}`);
|
|
16658
16875
|
}
|
|
16659
|
-
throw new Error(`Failed to read configuration file ${
|
|
16876
|
+
throw new Error(`Failed to read configuration file ${resolvedPath}: ${error.message}`);
|
|
16660
16877
|
}
|
|
16661
16878
|
throw error;
|
|
16662
16879
|
}
|
|
@@ -16668,7 +16885,7 @@ var ConfigManager = class {
|
|
|
16668
16885
|
const gitRoot = await this.findGitRepositoryRoot();
|
|
16669
16886
|
const searchDirs = [gitRoot, process.cwd()].filter(Boolean);
|
|
16670
16887
|
for (const baseDir of searchDirs) {
|
|
16671
|
-
const possiblePaths = [
|
|
16888
|
+
const possiblePaths = [path12.join(baseDir, ".visor.yaml"), path12.join(baseDir, ".visor.yml")];
|
|
16672
16889
|
for (const configPath of possiblePaths) {
|
|
16673
16890
|
if (fs11.existsSync(configPath)) {
|
|
16674
16891
|
return this.loadConfig(configPath, options);
|
|
@@ -16722,18 +16939,18 @@ var ConfigManager = class {
|
|
|
16722
16939
|
const possiblePaths = [];
|
|
16723
16940
|
if (typeof __dirname !== "undefined") {
|
|
16724
16941
|
possiblePaths.push(
|
|
16725
|
-
|
|
16726
|
-
|
|
16942
|
+
path12.join(__dirname, "defaults", ".visor.yaml"),
|
|
16943
|
+
path12.join(__dirname, "..", "defaults", ".visor.yaml")
|
|
16727
16944
|
);
|
|
16728
16945
|
}
|
|
16729
16946
|
const pkgRoot = this.findPackageRoot();
|
|
16730
16947
|
if (pkgRoot) {
|
|
16731
|
-
possiblePaths.push(
|
|
16948
|
+
possiblePaths.push(path12.join(pkgRoot, "defaults", ".visor.yaml"));
|
|
16732
16949
|
}
|
|
16733
16950
|
if (process.env.GITHUB_ACTION_PATH) {
|
|
16734
16951
|
possiblePaths.push(
|
|
16735
|
-
|
|
16736
|
-
|
|
16952
|
+
path12.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml"),
|
|
16953
|
+
path12.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml")
|
|
16737
16954
|
);
|
|
16738
16955
|
}
|
|
16739
16956
|
let bundledConfigPath;
|
|
@@ -16766,8 +16983,8 @@ var ConfigManager = class {
|
|
|
16766
16983
|
*/
|
|
16767
16984
|
findPackageRoot() {
|
|
16768
16985
|
let currentDir = __dirname;
|
|
16769
|
-
while (currentDir !==
|
|
16770
|
-
const packageJsonPath =
|
|
16986
|
+
while (currentDir !== path12.dirname(currentDir)) {
|
|
16987
|
+
const packageJsonPath = path12.join(currentDir, "package.json");
|
|
16771
16988
|
if (fs11.existsSync(packageJsonPath)) {
|
|
16772
16989
|
try {
|
|
16773
16990
|
const packageJson = JSON.parse(fs11.readFileSync(packageJsonPath, "utf8"));
|
|
@@ -16777,7 +16994,7 @@ var ConfigManager = class {
|
|
|
16777
16994
|
} catch {
|
|
16778
16995
|
}
|
|
16779
16996
|
}
|
|
16780
|
-
currentDir =
|
|
16997
|
+
currentDir = path12.dirname(currentDir);
|
|
16781
16998
|
}
|
|
16782
16999
|
return null;
|
|
16783
17000
|
}
|
|
@@ -16823,8 +17040,10 @@ var ConfigManager = class {
|
|
|
16823
17040
|
}
|
|
16824
17041
|
/**
|
|
16825
17042
|
* Validate configuration against schema
|
|
17043
|
+
* @param config The config to validate
|
|
17044
|
+
* @param strict If true, treat warnings as errors (default: false)
|
|
16826
17045
|
*/
|
|
16827
|
-
validateConfig(config) {
|
|
17046
|
+
validateConfig(config, strict = false) {
|
|
16828
17047
|
const errors = [];
|
|
16829
17048
|
const warnings = [];
|
|
16830
17049
|
this.validateWithAjvSchema(config, errors, warnings);
|
|
@@ -16932,10 +17151,13 @@ var ConfigManager = class {
|
|
|
16932
17151
|
if (config.tag_filter) {
|
|
16933
17152
|
this.validateTagFilter(config.tag_filter, errors);
|
|
16934
17153
|
}
|
|
17154
|
+
if (strict && warnings.length > 0) {
|
|
17155
|
+
errors.push(...warnings);
|
|
17156
|
+
}
|
|
16935
17157
|
if (errors.length > 0) {
|
|
16936
17158
|
throw new Error(errors[0].message);
|
|
16937
17159
|
}
|
|
16938
|
-
if (warnings.length > 0) {
|
|
17160
|
+
if (!strict && warnings.length > 0) {
|
|
16939
17161
|
for (const w of warnings) {
|
|
16940
17162
|
logger.warn(`\u26A0\uFE0F Config warning [${w.field}]: ${w.message}`);
|
|
16941
17163
|
}
|
|
@@ -17157,7 +17379,7 @@ var ConfigManager = class {
|
|
|
17157
17379
|
try {
|
|
17158
17380
|
if (!__ajvValidate) {
|
|
17159
17381
|
try {
|
|
17160
|
-
const jsonPath =
|
|
17382
|
+
const jsonPath = path12.resolve(__dirname, "generated", "config-schema.json");
|
|
17161
17383
|
const jsonSchema = require(jsonPath);
|
|
17162
17384
|
if (jsonSchema) {
|
|
17163
17385
|
const ajv = new import_ajv.default({ allErrors: true, allowUnionTypes: true, strict: false });
|
|
@@ -17391,9 +17613,25 @@ var __ajvValidate = null;
|
|
|
17391
17613
|
var __ajvErrors = null;
|
|
17392
17614
|
|
|
17393
17615
|
// src/sdk.ts
|
|
17394
|
-
async function loadConfig(
|
|
17616
|
+
async function loadConfig(configOrPath, options) {
|
|
17395
17617
|
const cm = new ConfigManager();
|
|
17396
|
-
if (
|
|
17618
|
+
if (typeof configOrPath === "object" && configOrPath !== null) {
|
|
17619
|
+
cm.validateConfig(configOrPath, options?.strict ?? false);
|
|
17620
|
+
const defaultConfig = {
|
|
17621
|
+
version: "1.0",
|
|
17622
|
+
checks: {},
|
|
17623
|
+
max_parallelism: 3,
|
|
17624
|
+
fail_fast: false
|
|
17625
|
+
};
|
|
17626
|
+
return {
|
|
17627
|
+
...defaultConfig,
|
|
17628
|
+
...configOrPath,
|
|
17629
|
+
checks: configOrPath.checks || {}
|
|
17630
|
+
};
|
|
17631
|
+
}
|
|
17632
|
+
if (typeof configOrPath === "string") {
|
|
17633
|
+
return cm.loadConfig(configOrPath);
|
|
17634
|
+
}
|
|
17397
17635
|
return cm.findAndLoadConfig();
|
|
17398
17636
|
}
|
|
17399
17637
|
function resolveChecks(checkIds, config) {
|
|
@@ -17419,7 +17657,15 @@ function resolveChecks(checkIds, config) {
|
|
|
17419
17657
|
}
|
|
17420
17658
|
async function runChecks(opts = {}) {
|
|
17421
17659
|
const cm = new ConfigManager();
|
|
17422
|
-
|
|
17660
|
+
let config;
|
|
17661
|
+
if (opts.config) {
|
|
17662
|
+
cm.validateConfig(opts.config, opts.strictValidation ?? false);
|
|
17663
|
+
config = opts.config;
|
|
17664
|
+
} else if (opts.configPath) {
|
|
17665
|
+
config = await cm.loadConfig(opts.configPath);
|
|
17666
|
+
} else {
|
|
17667
|
+
config = await cm.findAndLoadConfig();
|
|
17668
|
+
}
|
|
17423
17669
|
const checks = opts.checks && opts.checks.length > 0 ? resolveChecks(opts.checks, config) : Object.keys(config.checks || {});
|
|
17424
17670
|
const engine = new CheckExecutionEngine(opts.cwd);
|
|
17425
17671
|
const result = await engine.executeChecks({
|