@probelabs/visor 0.1.94 → 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.
Files changed (41) hide show
  1. package/README.md +4 -4
  2. package/defaults/.visor.yaml +86 -6
  3. package/dist/ai-review-service.d.ts +1 -1
  4. package/dist/ai-review-service.d.ts.map +1 -1
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/commands.d.ts.map +1 -1
  8. package/dist/config.d.ts +6 -1
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/defaults/.visor.yaml +86 -6
  11. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  12. package/dist/footer.d.ts +25 -0
  13. package/dist/footer.d.ts.map +1 -0
  14. package/dist/github-check-service.d.ts.map +1 -1
  15. package/dist/github-comments.d.ts.map +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +926 -476
  18. package/dist/output/code-review/schema.json +0 -23
  19. package/dist/reviewer.d.ts +5 -2
  20. package/dist/reviewer.d.ts.map +1 -1
  21. package/dist/sdk/{check-execution-engine-YBRPVUWD.mjs → check-execution-engine-NMPXJ7FQ.mjs} +2 -2
  22. package/dist/sdk/{chunk-DQRFOQAP.mjs → chunk-Q4S5A5TO.mjs} +199 -113
  23. package/dist/sdk/chunk-Q4S5A5TO.mjs.map +1 -0
  24. package/dist/sdk/sdk.js +249 -149
  25. package/dist/sdk/sdk.js.map +1 -1
  26. package/dist/sdk/sdk.mjs +23 -19
  27. package/dist/sdk/sdk.mjs.map +1 -1
  28. package/dist/traces/{run-2025-10-16T11-33-32-682Z.ndjson → run-2025-10-18T18-27-25-085Z.ndjson} +8 -1
  29. package/dist/traces/{run-2025-10-16T11-33-43-618Z.ndjson → run-2025-10-18T18-27-35-400Z.ndjson} +8 -1
  30. package/dist/traces/{run-2025-10-16T11-33-44-157Z.ndjson → run-2025-10-18T18-27-35-937Z.ndjson} +8 -1
  31. package/dist/traces/{run-2025-10-16T11-33-44-647Z.ndjson → run-2025-10-18T18-27-36-428Z.ndjson} +8 -1
  32. package/dist/types/cli.d.ts +3 -2
  33. package/dist/types/cli.d.ts.map +1 -1
  34. package/dist/types/config.d.ts +0 -2
  35. package/dist/types/config.d.ts.map +1 -1
  36. package/dist/utils/diff-processor.d.ts +6 -0
  37. package/dist/utils/diff-processor.d.ts.map +1 -0
  38. package/package.json +2 -2
  39. package/dist/sdk/chunk-DQRFOQAP.mjs.map +0 -1
  40. /package/dist/sdk/{check-execution-engine-YBRPVUWD.mjs.map → check-execution-engine-NMPXJ7FQ.mjs.map} +0 -0
  41. /package/dist/traces/{run-2025-10-16T11-33-45-128Z.ndjson → run-2025-10-18T18-27-36-917Z.ndjson} +0 -0
@@ -217,6 +217,30 @@ var init_session_registry = __esm({
217
217
  // src/github-comments.ts
218
218
  init_logger();
219
219
  import { v4 as uuidv4 } from "uuid";
220
+
221
+ // src/footer.ts
222
+ function generateFooter(options = {}) {
223
+ const { includeMetadata, includeSeparator = true } = options;
224
+ const parts = [];
225
+ if (includeSeparator) {
226
+ parts.push("---");
227
+ parts.push("");
228
+ }
229
+ parts.push(
230
+ "*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*"
231
+ );
232
+ if (includeMetadata) {
233
+ const { lastUpdated, triggeredBy, commitSha } = includeMetadata;
234
+ const commitInfo = commitSha ? ` | Commit: ${commitSha.substring(0, 7)}` : "";
235
+ parts.push("");
236
+ parts.push(`*Last updated: ${lastUpdated} | Triggered by: ${triggeredBy}${commitInfo}*`);
237
+ }
238
+ parts.push("");
239
+ parts.push("\u{1F4A1} **TIP:** You can chat with Visor using `/visor ask <your question>`");
240
+ return parts.join("\n");
241
+ }
242
+
243
+ // src/github-comments.ts
220
244
  var CommentManager = class {
221
245
  octokit;
222
246
  retryConfig;
@@ -318,15 +342,17 @@ var CommentManager = class {
318
342
  */
319
343
  formatCommentWithMetadata(content, metadata) {
320
344
  const { commentId, lastUpdated, triggeredBy, commitSha } = metadata;
321
- const commitInfo = commitSha ? ` | Commit: ${commitSha.substring(0, 7)}` : "";
345
+ const footer = generateFooter({
346
+ includeMetadata: {
347
+ lastUpdated,
348
+ triggeredBy,
349
+ commitSha
350
+ }
351
+ });
322
352
  return `<!-- visor-comment-id:${commentId} -->
323
353
  ${content}
324
354
 
325
- ---
326
-
327
- *Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*
328
-
329
- *Last updated: ${lastUpdated} | Triggered by: ${triggeredBy}${commitInfo}*
355
+ ${footer}
330
356
  <!-- /visor-comment-id:${commentId} -->`;
331
357
  }
332
358
  /**
@@ -508,6 +534,64 @@ init_session_registry();
508
534
  init_logger();
509
535
  init_tracer_init();
510
536
  import { ProbeAgent } from "@probelabs/probe";
537
+
538
+ // src/utils/diff-processor.ts
539
+ import { extract } from "@probelabs/probe";
540
+ import * as path from "path";
541
+ async function processDiffWithOutline(diffContent) {
542
+ if (!diffContent || diffContent.trim().length === 0) {
543
+ return diffContent;
544
+ }
545
+ try {
546
+ const originalProbePath = process.env.PROBE_PATH;
547
+ const fs5 = __require("fs");
548
+ const possiblePaths = [
549
+ // Relative to current working directory (most common in production)
550
+ path.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
551
+ // Relative to __dirname (for unbundled development)
552
+ path.join(__dirname, "../..", "node_modules/@probelabs/probe/bin/probe-binary"),
553
+ // Relative to dist directory (for bundled CLI)
554
+ path.join(__dirname, "node_modules/@probelabs/probe/bin/probe-binary")
555
+ ];
556
+ let probeBinaryPath;
557
+ for (const candidatePath of possiblePaths) {
558
+ if (fs5.existsSync(candidatePath)) {
559
+ probeBinaryPath = candidatePath;
560
+ break;
561
+ }
562
+ }
563
+ if (!probeBinaryPath) {
564
+ if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
565
+ console.error("Probe binary not found. Tried:", possiblePaths);
566
+ }
567
+ return diffContent;
568
+ }
569
+ process.env.PROBE_PATH = probeBinaryPath;
570
+ const extractPromise = extract({
571
+ content: diffContent,
572
+ format: "outline-diff",
573
+ allowTests: true
574
+ // Allow test files and test code blocks in extraction results
575
+ });
576
+ const timeoutPromise = new Promise((_, reject) => {
577
+ setTimeout(() => reject(new Error("Extract timeout after 30s")), 3e4);
578
+ });
579
+ const result = await Promise.race([extractPromise, timeoutPromise]);
580
+ if (originalProbePath !== void 0) {
581
+ process.env.PROBE_PATH = originalProbePath;
582
+ } else {
583
+ delete process.env.PROBE_PATH;
584
+ }
585
+ return typeof result === "string" ? result : JSON.stringify(result);
586
+ } catch (error) {
587
+ if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
588
+ console.error("Failed to process diff with outline-diff format:", error);
589
+ }
590
+ return diffContent;
591
+ }
592
+ }
593
+
594
+ // src/ai-review-service.ts
511
595
  function log(...args) {
512
596
  logger.debug(args.join(" "));
513
597
  }
@@ -549,7 +633,7 @@ var AIReviewService = class {
549
633
  /**
550
634
  * Execute AI review using probe agent
551
635
  */
552
- async executeReview(prInfo, customPrompt, schema, _checkName, sessionId) {
636
+ async executeReview(prInfo, customPrompt, schema, checkName, sessionId) {
553
637
  const startTime = Date.now();
554
638
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
555
639
  const prompt = await this.buildCustomPrompt(prInfo, customPrompt, schema);
@@ -606,7 +690,7 @@ var AIReviewService = class {
606
690
  prompt,
607
691
  schema,
608
692
  debugInfo,
609
- _checkName,
693
+ checkName,
610
694
  sessionId
611
695
  );
612
696
  const processingTime = Date.now() - startTime;
@@ -766,9 +850,9 @@ var AIReviewService = class {
766
850
  */
767
851
  async buildCustomPrompt(prInfo, customInstructions, schema, options) {
768
852
  const skipPRContext = options?.skipPRContext === true;
769
- const prContext = skipPRContext ? "" : this.formatPRContext(prInfo);
770
- const isIssue = prInfo.isIssue === true;
771
853
  const isCodeReviewSchema = schema === "code-review";
854
+ const prContext = skipPRContext ? "" : await this.formatPRContext(prInfo, isCodeReviewSchema);
855
+ const isIssue = prInfo.isIssue === true;
772
856
  if (isIssue) {
773
857
  if (skipPRContext) {
774
858
  return `<instructions>
@@ -857,7 +941,7 @@ ${prContext}
857
941
  /**
858
942
  * Format PR or Issue context for the AI using XML structure
859
943
  */
860
- formatPRContext(prInfo) {
944
+ async formatPRContext(prInfo, isCodeReviewSchema) {
861
945
  const prContextInfo = prInfo;
862
946
  const isIssue = prContextInfo.isIssue === true;
863
947
  const isPRContext = prContextInfo.isPRContext === true;
@@ -939,7 +1023,12 @@ ${this.escapeXml(prInfo.body)}
939
1023
  }
940
1024
  const issueComments = prInfo.comments;
941
1025
  if (issueComments && issueComments.length > 0) {
942
- const historicalComments = triggeringComment2 ? issueComments.filter((c) => c.id !== triggeringComment2.id) : issueComments;
1026
+ let historicalComments = triggeringComment2 ? issueComments.filter((c) => c.id !== triggeringComment2.id) : issueComments;
1027
+ if (isCodeReviewSchema) {
1028
+ historicalComments = historicalComments.filter(
1029
+ (c) => !c.body || !c.body.includes("visor-comment-id:pr-review-")
1030
+ );
1031
+ }
943
1032
  if (historicalComments.length > 0) {
944
1033
  context2 += `
945
1034
  <!-- Previous comments in chronological order (excluding triggering comment) -->
@@ -981,24 +1070,27 @@ ${this.escapeXml(prInfo.body)}
981
1070
  }
982
1071
  if (includeCodeContext) {
983
1072
  if (prInfo.fullDiff) {
1073
+ const processedFullDiff = await processDiffWithOutline(prInfo.fullDiff);
984
1074
  context += `
985
- <!-- Complete unified diff showing all changes in the pull request -->
1075
+ <!-- Complete unified diff showing all changes in the pull request (processed with outline-diff) -->
986
1076
  <full_diff>
987
- ${this.escapeXml(prInfo.fullDiff)}
1077
+ ${this.escapeXml(processedFullDiff)}
988
1078
  </full_diff>`;
989
1079
  }
990
1080
  if (prInfo.isIncremental) {
991
1081
  if (prInfo.commitDiff && prInfo.commitDiff.length > 0) {
1082
+ const processedCommitDiff = await processDiffWithOutline(prInfo.commitDiff);
992
1083
  context += `
993
- <!-- Diff of only the latest commit for incremental analysis -->
1084
+ <!-- Diff of only the latest commit for incremental analysis (processed with outline-diff) -->
994
1085
  <commit_diff>
995
- ${this.escapeXml(prInfo.commitDiff)}
1086
+ ${this.escapeXml(processedCommitDiff)}
996
1087
  </commit_diff>`;
997
1088
  } else {
1089
+ const processedFallbackDiff = prInfo.fullDiff ? await processDiffWithOutline(prInfo.fullDiff) : "";
998
1090
  context += `
999
- <!-- Commit diff could not be retrieved - falling back to full diff analysis -->
1091
+ <!-- Commit diff could not be retrieved - falling back to full diff analysis (processed with outline-diff) -->
1000
1092
  <commit_diff>
1001
- ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ""}
1093
+ ${this.escapeXml(processedFallbackDiff)}
1002
1094
  </commit_diff>`;
1003
1095
  }
1004
1096
  }
@@ -1034,7 +1126,12 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ""}
1034
1126
  }
1035
1127
  const prComments = prInfo.comments;
1036
1128
  if (prComments && prComments.length > 0) {
1037
- const historicalComments = triggeringComment ? prComments.filter((c) => c.id !== triggeringComment.id) : prComments;
1129
+ let historicalComments = triggeringComment ? prComments.filter((c) => c.id !== triggeringComment.id) : prComments;
1130
+ if (isCodeReviewSchema) {
1131
+ historicalComments = historicalComments.filter(
1132
+ (c) => !c.body || !c.body.includes("visor-comment-id:pr-review-")
1133
+ );
1134
+ }
1038
1135
  if (historicalComments.length > 0) {
1039
1136
  context += `
1040
1137
  <!-- Previous PR comments in chronological order (excluding triggering comment) -->
@@ -1105,7 +1202,7 @@ ${schemaString}`);
1105
1202
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1106
1203
  try {
1107
1204
  const fs5 = __require("fs");
1108
- const path5 = __require("path");
1205
+ const path6 = __require("path");
1109
1206
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1110
1207
  const provider = this.config.provider || "auto";
1111
1208
  const model = this.config.model || "default";
@@ -1219,16 +1316,16 @@ ${"=".repeat(60)}
1219
1316
  `;
1220
1317
  readableVersion += `${"=".repeat(60)}
1221
1318
  `;
1222
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
1319
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1223
1320
  if (!fs5.existsSync(debugArtifactsDir)) {
1224
1321
  fs5.mkdirSync(debugArtifactsDir, { recursive: true });
1225
1322
  }
1226
- const debugFile = path5.join(
1323
+ const debugFile = path6.join(
1227
1324
  debugArtifactsDir,
1228
1325
  `prompt-${_checkName || "unknown"}-${timestamp}.json`
1229
1326
  );
1230
1327
  fs5.writeFileSync(debugFile, debugJson, "utf-8");
1231
- const readableFile = path5.join(
1328
+ const readableFile = path6.join(
1232
1329
  debugArtifactsDir,
1233
1330
  `prompt-${_checkName || "unknown"}-${timestamp}.txt`
1234
1331
  );
@@ -1265,7 +1362,7 @@ ${"=".repeat(60)}
1265
1362
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1266
1363
  try {
1267
1364
  const fs5 = __require("fs");
1268
- const path5 = __require("path");
1365
+ const path6 = __require("path");
1269
1366
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1270
1367
  const agentAny2 = agent;
1271
1368
  let fullHistory = [];
@@ -1276,8 +1373,8 @@ ${"=".repeat(60)}
1276
1373
  } else if (agentAny2._messages) {
1277
1374
  fullHistory = agentAny2._messages;
1278
1375
  }
1279
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
1280
- const sessionFile = path5.join(
1376
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1377
+ const sessionFile = path6.join(
1281
1378
  debugArtifactsDir,
1282
1379
  `session-${_checkName || "unknown"}-${timestamp}.json`
1283
1380
  );
@@ -1292,7 +1389,7 @@ ${"=".repeat(60)}
1292
1389
  latestResponse: response
1293
1390
  };
1294
1391
  fs5.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1295
- const sessionTxtFile = path5.join(
1392
+ const sessionTxtFile = path6.join(
1296
1393
  debugArtifactsDir,
1297
1394
  `session-${_checkName || "unknown"}-${timestamp}.txt`
1298
1395
  );
@@ -1336,10 +1433,10 @@ ${"=".repeat(60)}
1336
1433
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1337
1434
  try {
1338
1435
  const fs5 = __require("fs");
1339
- const path5 = __require("path");
1436
+ const path6 = __require("path");
1340
1437
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1341
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
1342
- const responseFile = path5.join(
1438
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1439
+ const responseFile = path6.join(
1343
1440
  debugArtifactsDir,
1344
1441
  `response-${_checkName || "unknown"}-${timestamp}.txt`
1345
1442
  );
@@ -1511,7 +1608,7 @@ ${schemaString}`);
1511
1608
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1512
1609
  try {
1513
1610
  const fs5 = __require("fs");
1514
- const path5 = __require("path");
1611
+ const path6 = __require("path");
1515
1612
  const os = __require("os");
1516
1613
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1517
1614
  const debugData = {
@@ -1585,21 +1682,21 @@ ${"=".repeat(60)}
1585
1682
  readableVersion += `${"=".repeat(60)}
1586
1683
  `;
1587
1684
  const tempDir = os.tmpdir();
1588
- const promptFile = path5.join(tempDir, `visor-prompt-${timestamp}.txt`);
1685
+ const promptFile = path6.join(tempDir, `visor-prompt-${timestamp}.txt`);
1589
1686
  fs5.writeFileSync(promptFile, prompt, "utf-8");
1590
1687
  log(`
1591
1688
  \u{1F4BE} Prompt saved to: ${promptFile}`);
1592
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
1689
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1593
1690
  try {
1594
1691
  if (!fs5.existsSync(debugArtifactsDir)) {
1595
1692
  fs5.mkdirSync(debugArtifactsDir, { recursive: true });
1596
1693
  }
1597
- const debugFile = path5.join(
1694
+ const debugFile = path6.join(
1598
1695
  debugArtifactsDir,
1599
1696
  `prompt-${_checkName || "unknown"}-${timestamp}.json`
1600
1697
  );
1601
1698
  fs5.writeFileSync(debugFile, debugJson, "utf-8");
1602
- const readableFile = path5.join(
1699
+ const readableFile = path6.join(
1603
1700
  debugArtifactsDir,
1604
1701
  `prompt-${_checkName || "unknown"}-${timestamp}.txt`
1605
1702
  );
@@ -1652,7 +1749,7 @@ $ ${cliCommand}
1652
1749
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1653
1750
  try {
1654
1751
  const fs5 = __require("fs");
1655
- const path5 = __require("path");
1752
+ const path6 = __require("path");
1656
1753
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1657
1754
  const agentAny = agent;
1658
1755
  let fullHistory = [];
@@ -1663,8 +1760,8 @@ $ ${cliCommand}
1663
1760
  } else if (agentAny._messages) {
1664
1761
  fullHistory = agentAny._messages;
1665
1762
  }
1666
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
1667
- const sessionFile = path5.join(
1763
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1764
+ const sessionFile = path6.join(
1668
1765
  debugArtifactsDir,
1669
1766
  `session-${_checkName || "unknown"}-${timestamp}.json`
1670
1767
  );
@@ -1679,7 +1776,7 @@ $ ${cliCommand}
1679
1776
  latestResponse: response
1680
1777
  };
1681
1778
  fs5.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1682
- const sessionTxtFile = path5.join(
1779
+ const sessionTxtFile = path6.join(
1683
1780
  debugArtifactsDir,
1684
1781
  `session-${_checkName || "unknown"}-${timestamp}.txt`
1685
1782
  );
@@ -1723,10 +1820,10 @@ ${"=".repeat(60)}
1723
1820
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
1724
1821
  try {
1725
1822
  const fs5 = __require("fs");
1726
- const path5 = __require("path");
1823
+ const path6 = __require("path");
1727
1824
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1728
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
1729
- const responseFile = path5.join(
1825
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path6.join(process.cwd(), "debug-artifacts");
1826
+ const responseFile = path6.join(
1730
1827
  debugArtifactsDir,
1731
1828
  `response-${_checkName || "unknown"}-${timestamp}.txt`
1732
1829
  );
@@ -1816,7 +1913,7 @@ ${"=".repeat(60)}
1816
1913
  */
1817
1914
  async loadSchemaContent(schema) {
1818
1915
  const fs5 = __require("fs").promises;
1819
- const path5 = __require("path");
1916
+ const path6 = __require("path");
1820
1917
  if (typeof schema === "object" && schema !== null) {
1821
1918
  log("\u{1F4CB} Using inline schema object from configuration");
1822
1919
  return JSON.stringify(schema);
@@ -1829,12 +1926,12 @@ ${"=".repeat(60)}
1829
1926
  }
1830
1927
  } catch {
1831
1928
  }
1832
- if ((schema.startsWith("./") || schema.includes(".json")) && !path5.isAbsolute(schema)) {
1929
+ if ((schema.startsWith("./") || schema.includes(".json")) && !path6.isAbsolute(schema)) {
1833
1930
  if (schema.includes("..") || schema.includes("\0")) {
1834
1931
  throw new Error("Invalid schema path: path traversal not allowed");
1835
1932
  }
1836
1933
  try {
1837
- const schemaPath2 = path5.resolve(process.cwd(), schema);
1934
+ const schemaPath2 = path6.resolve(process.cwd(), schema);
1838
1935
  log(`\u{1F4CB} Loading custom schema from file: ${schemaPath2}`);
1839
1936
  const schemaContent = await fs5.readFile(schemaPath2, "utf-8");
1840
1937
  return schemaContent.trim();
@@ -1848,7 +1945,7 @@ ${"=".repeat(60)}
1848
1945
  if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
1849
1946
  throw new Error("Invalid schema name");
1850
1947
  }
1851
- const schemaPath = path5.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
1948
+ const schemaPath = path6.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
1852
1949
  try {
1853
1950
  const schemaContent = await fs5.readFile(schemaPath, "utf-8");
1854
1951
  return schemaContent.trim();
@@ -2178,7 +2275,7 @@ var PRReviewer = class {
2178
2275
  async reviewPR(owner, repo, prNumber, prInfo, options = {}) {
2179
2276
  const { debug = false, config, checks } = options;
2180
2277
  if (config && checks && checks.length > 0) {
2181
- const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-YBRPVUWD.mjs");
2278
+ const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-NMPXJ7FQ.mjs");
2182
2279
  const engine = new CheckExecutionEngine2();
2183
2280
  const { results } = await engine.executeGroupedChecks(
2184
2281
  prInfo,
@@ -2198,30 +2295,36 @@ var PRReviewer = class {
2198
2295
  );
2199
2296
  }
2200
2297
  /**
2201
- * Helper to check if a schema definition has a "text" field in its properties
2298
+ * Helper to check if a schema is comment-generating
2299
+ * Comment-generating schemas include:
2300
+ * - Built-in schemas: code-review, overview, plain, text
2301
+ * - Custom schemas with a "text" field in properties
2202
2302
  */
2203
- async schemaHasTextField(schema) {
2303
+ async isCommentGeneratingSchema(schema) {
2204
2304
  try {
2205
- let schemaObj;
2206
- if (typeof schema === "object") {
2207
- schemaObj = schema;
2208
- } else {
2305
+ if (typeof schema === "string") {
2306
+ if (["code-review", "overview", "plain", "text"].includes(schema)) {
2307
+ return true;
2308
+ }
2209
2309
  const fs5 = __require("fs").promises;
2210
- const path5 = __require("path");
2310
+ const path6 = __require("path");
2211
2311
  const sanitizedSchemaName = schema.replace(/[^a-zA-Z0-9-]/g, "");
2212
2312
  if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
2213
2313
  return false;
2214
2314
  }
2215
- const schemaPath = path5.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2315
+ const schemaPath = path6.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2216
2316
  try {
2217
2317
  const schemaContent = await fs5.readFile(schemaPath, "utf-8");
2218
- schemaObj = JSON.parse(schemaContent);
2318
+ const schemaObj = JSON.parse(schemaContent);
2319
+ const properties = schemaObj.properties;
2320
+ return !!(properties && "text" in properties);
2219
2321
  } catch {
2220
2322
  return false;
2221
2323
  }
2324
+ } else {
2325
+ const properties = schema.properties;
2326
+ return !!(properties && "text" in properties);
2222
2327
  }
2223
- const properties = schemaObj.properties;
2224
- return !!(properties && "text" in properties);
2225
2328
  } catch {
2226
2329
  return false;
2227
2330
  }
@@ -2239,14 +2342,8 @@ var PRReviewer = class {
2239
2342
  const isAICheck = type === "ai" || type === "claude-code";
2240
2343
  if (!schema || schema === "") {
2241
2344
  shouldPostComment = isAICheck;
2242
- } else if (typeof schema === "string") {
2243
- if (schema === "text" || schema === "plain") {
2244
- shouldPostComment = true;
2245
- } else {
2246
- shouldPostComment = await this.schemaHasTextField(schema);
2247
- }
2248
- } else if (typeof schema === "object") {
2249
- shouldPostComment = await this.schemaHasTextField(schema);
2345
+ } else {
2346
+ shouldPostComment = await this.isCommentGeneratingSchema(schema);
2250
2347
  }
2251
2348
  if (shouldPostComment) {
2252
2349
  filtered.push(r);
@@ -2389,14 +2486,14 @@ var PRReviewer = class {
2389
2486
  saveDebugArtifact(debug) {
2390
2487
  try {
2391
2488
  const fs5 = __require("fs");
2392
- const path5 = __require("path");
2393
- const debugDir = path5.join(process.cwd(), "debug-artifacts");
2489
+ const path6 = __require("path");
2490
+ const debugDir = path6.join(process.cwd(), "debug-artifacts");
2394
2491
  if (!fs5.existsSync(debugDir)) {
2395
2492
  fs5.mkdirSync(debugDir, { recursive: true });
2396
2493
  }
2397
2494
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2398
2495
  const filename = `visor-debug-${timestamp}.md`;
2399
- const filepath = path5.join(debugDir, filename);
2496
+ const filepath = path6.join(debugDir, filename);
2400
2497
  const content = [
2401
2498
  `# Visor Debug Information`,
2402
2499
  ``,
@@ -2428,7 +2525,7 @@ var PRReviewer = class {
2428
2525
 
2429
2526
  // src/git-repository-analyzer.ts
2430
2527
  import { simpleGit } from "simple-git";
2431
- import * as path from "path";
2528
+ import * as path2 from "path";
2432
2529
  import * as fs from "fs";
2433
2530
  var MAX_PATCH_SIZE = 50 * 1024;
2434
2531
  var GitRepositoryAnalyzer = class {
@@ -2643,7 +2740,7 @@ ${file.patch}`).join("\n\n");
2643
2740
  console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
2644
2741
  continue;
2645
2742
  }
2646
- const filePath = path.join(this.cwd, file);
2743
+ const filePath = path2.join(this.cwd, file);
2647
2744
  const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
2648
2745
  changes.push(fileChange);
2649
2746
  }
@@ -3104,7 +3201,7 @@ var EnvironmentResolver = class {
3104
3201
 
3105
3202
  // src/issue-filter.ts
3106
3203
  import * as fs2 from "fs";
3107
- import * as path2 from "path";
3204
+ import * as path3 from "path";
3108
3205
  var IssueFilter = class {
3109
3206
  fileCache = /* @__PURE__ */ new Map();
3110
3207
  suppressionEnabled;
@@ -3172,7 +3269,7 @@ var IssueFilter = class {
3172
3269
  return this.fileCache.get(filePath);
3173
3270
  }
3174
3271
  try {
3175
- const resolvedPath = path2.isAbsolute(filePath) ? filePath : path2.join(workingDir, filePath);
3272
+ const resolvedPath = path3.isAbsolute(filePath) ? filePath : path3.join(workingDir, filePath);
3176
3273
  if (!fs2.existsSync(resolvedPath)) {
3177
3274
  if (fs2.existsSync(filePath)) {
3178
3275
  const content2 = fs2.readFileSync(filePath, "utf8");
@@ -3200,7 +3297,7 @@ var IssueFilter = class {
3200
3297
 
3201
3298
  // src/providers/ai-check-provider.ts
3202
3299
  import fs3 from "fs/promises";
3203
- import path3 from "path";
3300
+ import path4 from "path";
3204
3301
  var AICheckProvider = class extends CheckProvider {
3205
3302
  aiReviewService;
3206
3303
  liquidEngine;
@@ -3317,7 +3414,7 @@ var AICheckProvider = class extends CheckProvider {
3317
3414
  const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
3318
3415
  const hasPathSeparators = /[\/\\]/.test(str);
3319
3416
  const isRelativePath = /^\.{1,2}\//.test(str);
3320
- const isAbsolutePath = path3.isAbsolute(str);
3417
+ const isAbsolutePath = path4.isAbsolute(str);
3321
3418
  const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
3322
3419
  if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
3323
3420
  return false;
@@ -3327,10 +3424,10 @@ var AICheckProvider = class extends CheckProvider {
3327
3424
  }
3328
3425
  try {
3329
3426
  let resolvedPath;
3330
- if (path3.isAbsolute(str)) {
3331
- resolvedPath = path3.normalize(str);
3427
+ if (path4.isAbsolute(str)) {
3428
+ resolvedPath = path4.normalize(str);
3332
3429
  } else {
3333
- resolvedPath = path3.resolve(process.cwd(), str);
3430
+ resolvedPath = path4.resolve(process.cwd(), str);
3334
3431
  }
3335
3432
  const fs5 = __require("fs").promises;
3336
3433
  try {
@@ -3351,14 +3448,14 @@ var AICheckProvider = class extends CheckProvider {
3351
3448
  throw new Error("Prompt file must have .liquid extension");
3352
3449
  }
3353
3450
  let resolvedPath;
3354
- if (path3.isAbsolute(promptPath)) {
3451
+ if (path4.isAbsolute(promptPath)) {
3355
3452
  resolvedPath = promptPath;
3356
3453
  } else {
3357
- resolvedPath = path3.resolve(process.cwd(), promptPath);
3454
+ resolvedPath = path4.resolve(process.cwd(), promptPath);
3358
3455
  }
3359
- if (!path3.isAbsolute(promptPath)) {
3360
- const normalizedPath = path3.normalize(resolvedPath);
3361
- const currentDir = path3.resolve(process.cwd());
3456
+ if (!path4.isAbsolute(promptPath)) {
3457
+ const normalizedPath = path4.normalize(resolvedPath);
3458
+ const currentDir = path4.resolve(process.cwd());
3362
3459
  if (!normalizedPath.startsWith(currentDir)) {
3363
3460
  throw new Error("Invalid prompt file path: path traversal detected");
3364
3461
  }
@@ -4641,7 +4738,7 @@ ${cfg.value_js}
4641
4738
 
4642
4739
  // src/providers/claude-code-check-provider.ts
4643
4740
  import fs4 from "fs/promises";
4644
- import path4 from "path";
4741
+ import path5 from "path";
4645
4742
 
4646
4743
  // src/providers/claude-code-types.ts
4647
4744
  async function safeImport(moduleName) {
@@ -4802,7 +4899,7 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
4802
4899
  const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
4803
4900
  const hasPathSeparators = /[\/\\]/.test(str);
4804
4901
  const isRelativePath = /^\.{1,2}\//.test(str);
4805
- const isAbsolutePath = path4.isAbsolute(str);
4902
+ const isAbsolutePath = path5.isAbsolute(str);
4806
4903
  const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
4807
4904
  if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
4808
4905
  return false;
@@ -4812,10 +4909,10 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
4812
4909
  }
4813
4910
  try {
4814
4911
  let resolvedPath;
4815
- if (path4.isAbsolute(str)) {
4816
- resolvedPath = path4.normalize(str);
4912
+ if (path5.isAbsolute(str)) {
4913
+ resolvedPath = path5.normalize(str);
4817
4914
  } else {
4818
- resolvedPath = path4.resolve(process.cwd(), str);
4915
+ resolvedPath = path5.resolve(process.cwd(), str);
4819
4916
  }
4820
4917
  try {
4821
4918
  const stat = await fs4.stat(resolvedPath);
@@ -4835,14 +4932,14 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
4835
4932
  throw new Error("Prompt file must have .liquid extension");
4836
4933
  }
4837
4934
  let resolvedPath;
4838
- if (path4.isAbsolute(promptPath)) {
4935
+ if (path5.isAbsolute(promptPath)) {
4839
4936
  resolvedPath = promptPath;
4840
4937
  } else {
4841
- resolvedPath = path4.resolve(process.cwd(), promptPath);
4938
+ resolvedPath = path5.resolve(process.cwd(), promptPath);
4842
4939
  }
4843
- if (!path4.isAbsolute(promptPath)) {
4844
- const normalizedPath = path4.normalize(resolvedPath);
4845
- const currentDir = path4.resolve(process.cwd());
4940
+ if (!path5.isAbsolute(promptPath)) {
4941
+ const normalizedPath = path5.normalize(resolvedPath);
4942
+ const currentDir = path5.resolve(process.cwd());
4846
4943
  if (!normalizedPath.startsWith(currentDir)) {
4847
4944
  throw new Error("Invalid prompt file path: path traversal detected");
4848
4945
  }
@@ -7506,10 +7603,6 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
7506
7603
  if (!Array.isArray(issues2)) return false;
7507
7604
  return issues2.some((issue) => issue.file?.includes(pattern));
7508
7605
  };
7509
- const hasSuggestion = (suggestions2, text) => {
7510
- if (!Array.isArray(suggestions2)) return false;
7511
- return suggestions2.some((s) => s.toLowerCase().includes(text.toLowerCase()));
7512
- };
7513
7606
  const hasIssueWith = hasIssue;
7514
7607
  const hasFileWith = hasFileMatching;
7515
7608
  const permissionHelpers = createPermissionHelpers(
@@ -7524,7 +7617,6 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
7524
7617
  const isFirstTimer = permissionHelpers.isFirstTimer;
7525
7618
  const output = context.output || {};
7526
7619
  const issues = output.issues || [];
7527
- const suggestions = [];
7528
7620
  const metadata = context.metadata || {
7529
7621
  checkName: context.checkName || "",
7530
7622
  schema: context.schema || "",
@@ -7568,7 +7660,6 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
7568
7660
  memory: memoryAccessor,
7569
7661
  // Legacy compatibility variables
7570
7662
  issues,
7571
- suggestions,
7572
7663
  metadata,
7573
7664
  criticalIssues,
7574
7665
  errorIssues,
@@ -7597,7 +7688,6 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
7597
7688
  hasIssue,
7598
7689
  countIssues,
7599
7690
  hasFileMatching,
7600
- hasSuggestion,
7601
7691
  hasIssueWith,
7602
7692
  hasFileWith,
7603
7693
  // Permission helpers
@@ -8101,11 +8191,7 @@ Please check your configuration and try again.`
8101
8191
  });
8102
8192
  }
8103
8193
  sections.push("");
8104
- sections.push("---");
8105
- sections.push("");
8106
- sections.push(
8107
- "*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*"
8108
- );
8194
+ sections.push(generateFooter());
8109
8195
  return sections.join("\n");
8110
8196
  }
8111
8197
  /**
@@ -9645,7 +9731,7 @@ ${expr}
9645
9731
  * - Enforcing .liquid file extension
9646
9732
  */
9647
9733
  async validateTemplatePath(templatePath) {
9648
- const path5 = await import("path");
9734
+ const path6 = await import("path");
9649
9735
  if (!templatePath || typeof templatePath !== "string" || templatePath.trim() === "") {
9650
9736
  throw new Error("Template path must be a non-empty string");
9651
9737
  }
@@ -9655,7 +9741,7 @@ ${expr}
9655
9741
  if (!templatePath.endsWith(".liquid")) {
9656
9742
  throw new Error("Template file must have .liquid extension");
9657
9743
  }
9658
- if (path5.isAbsolute(templatePath)) {
9744
+ if (path6.isAbsolute(templatePath)) {
9659
9745
  throw new Error("Template path must be relative to project directory");
9660
9746
  }
9661
9747
  if (templatePath.includes("..")) {
@@ -9669,14 +9755,14 @@ ${expr}
9669
9755
  if (!projectRoot || typeof projectRoot !== "string") {
9670
9756
  throw new Error("Unable to determine project root directory");
9671
9757
  }
9672
- const resolvedPath = path5.resolve(projectRoot, templatePath);
9673
- const resolvedProjectRoot = path5.resolve(projectRoot);
9758
+ const resolvedPath = path6.resolve(projectRoot, templatePath);
9759
+ const resolvedProjectRoot = path6.resolve(projectRoot);
9674
9760
  if (!resolvedPath || !resolvedProjectRoot || resolvedPath === "" || resolvedProjectRoot === "") {
9675
9761
  throw new Error(
9676
9762
  `Unable to resolve template path: projectRoot="${projectRoot}", templatePath="${templatePath}", resolvedPath="${resolvedPath}", resolvedProjectRoot="${resolvedProjectRoot}"`
9677
9763
  );
9678
9764
  }
9679
- if (!resolvedPath.startsWith(resolvedProjectRoot + path5.sep) && resolvedPath !== resolvedProjectRoot) {
9765
+ if (!resolvedPath.startsWith(resolvedProjectRoot + path6.sep) && resolvedPath !== resolvedProjectRoot) {
9680
9766
  throw new Error("Template path escapes project directory");
9681
9767
  }
9682
9768
  return resolvedPath;
@@ -9721,7 +9807,7 @@ ${expr}
9721
9807
  }
9722
9808
  const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-GMEGEGC3.mjs");
9723
9809
  const fs5 = await import("fs/promises");
9724
- const path5 = await import("path");
9810
+ const path6 = await import("path");
9725
9811
  const liquid = createExtendedLiquid2({
9726
9812
  trimTagLeft: false,
9727
9813
  trimTagRight: false,
@@ -9755,7 +9841,7 @@ ${expr}
9755
9841
  if (!sanitizedSchema) {
9756
9842
  throw new Error("Invalid schema name");
9757
9843
  }
9758
- const templatePath = path5.join(__dirname, `../output/${sanitizedSchema}/template.liquid`);
9844
+ const templatePath = path6.join(__dirname, `../output/${sanitizedSchema}/template.liquid`);
9759
9845
  templateContent = await fs5.readFile(templatePath, "utf-8");
9760
9846
  if (sanitizedSchema === "issue-assistant") {
9761
9847
  enrichAssistantContext = true;
@@ -12611,4 +12697,4 @@ ${result.value.result.debug.rawResponse}`;
12611
12697
  export {
12612
12698
  CheckExecutionEngine
12613
12699
  };
12614
- //# sourceMappingURL=chunk-DQRFOQAP.mjs.map
12700
+ //# sourceMappingURL=chunk-Q4S5A5TO.mjs.map