@probelabs/visor 0.1.93 → 0.1.94

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 (29) hide show
  1. package/dist/check-execution-engine.d.ts +5 -0
  2. package/dist/check-execution-engine.d.ts.map +1 -1
  3. package/dist/config.d.ts +3 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/index.js +176 -24
  6. package/dist/providers/command-check-provider.d.ts.map +1 -1
  7. package/dist/providers/log-check-provider.d.ts.map +1 -1
  8. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  9. package/dist/reviewer.d.ts +8 -0
  10. package/dist/reviewer.d.ts.map +1 -1
  11. package/dist/sdk/{check-execution-engine-RORGGGGP.mjs → check-execution-engine-YBRPVUWD.mjs} +2 -2
  12. package/dist/sdk/{chunk-Z47UECAT.mjs → chunk-DQRFOQAP.mjs} +136 -19
  13. package/dist/sdk/chunk-DQRFOQAP.mjs.map +1 -0
  14. package/dist/sdk/sdk.d.mts +11 -2
  15. package/dist/sdk/sdk.d.ts +11 -2
  16. package/dist/sdk/sdk.js +168 -22
  17. package/dist/sdk/sdk.js.map +1 -1
  18. package/dist/sdk/sdk.mjs +35 -6
  19. package/dist/sdk/sdk.mjs.map +1 -1
  20. package/dist/sdk.d.ts +11 -2
  21. package/dist/sdk.d.ts.map +1 -1
  22. package/package.json +1 -1
  23. package/dist/sdk/chunk-Z47UECAT.mjs.map +0 -1
  24. /package/dist/sdk/{check-execution-engine-RORGGGGP.mjs.map → check-execution-engine-YBRPVUWD.mjs.map} +0 -0
  25. /package/dist/traces/{run-2025-10-15T11-54-04-087Z.ndjson → run-2025-10-16T11-33-32-682Z.ndjson} +0 -0
  26. /package/dist/traces/{run-2025-10-15T11-54-14-046Z.ndjson → run-2025-10-16T11-33-43-618Z.ndjson} +0 -0
  27. /package/dist/traces/{run-2025-10-15T11-54-14-575Z.ndjson → run-2025-10-16T11-33-44-157Z.ndjson} +0 -0
  28. /package/dist/traces/{run-2025-10-15T11-54-15-082Z.ndjson → run-2025-10-16T11-33-44-647Z.ndjson} +0 -0
  29. /package/dist/traces/{run-2025-10-15T11-54-15-561Z.ndjson → run-2025-10-16T11-33-45-128Z.ndjson} +0 -0
@@ -659,9 +659,18 @@ interface RunOptions extends VisorOptions {
659
659
  output?: {
660
660
  format?: 'table' | 'json' | 'markdown' | 'sarif';
661
661
  };
662
+ /** Strict mode: treat config warnings (like unknown keys) as errors (default: false) */
663
+ strictValidation?: boolean;
662
664
  }
663
- /** Load a Visor config from path, or discover defaults if path is omitted. */
664
- declare function loadConfig(configPath?: string): Promise<VisorConfig>;
665
+ /**
666
+ * Load and validate a Visor config.
667
+ * @param configOrPath - Config object, file path, or omit to discover defaults
668
+ * @param options - Validation options
669
+ * @returns Validated config with defaults applied
670
+ */
671
+ declare function loadConfig(configOrPath?: string | Partial<VisorConfig>, options?: {
672
+ strict?: boolean;
673
+ }): Promise<VisorConfig>;
665
674
  /** Expand check IDs by including their dependencies (shallow->deep). */
666
675
  declare function resolveChecks(checkIds: string[], config: VisorConfig | undefined): string[];
667
676
  /**
package/dist/sdk/sdk.d.ts CHANGED
@@ -659,9 +659,18 @@ interface RunOptions extends VisorOptions {
659
659
  output?: {
660
660
  format?: 'table' | 'json' | 'markdown' | 'sarif';
661
661
  };
662
+ /** Strict mode: treat config warnings (like unknown keys) as errors (default: false) */
663
+ strictValidation?: boolean;
662
664
  }
663
- /** Load a Visor config from path, or discover defaults if path is omitted. */
664
- declare function loadConfig(configPath?: string): Promise<VisorConfig>;
665
+ /**
666
+ * Load and validate a Visor config.
667
+ * @param configOrPath - Config object, file path, or omit to discover defaults
668
+ * @param options - Validation options
669
+ * @returns Validated config with defaults applied
670
+ */
671
+ declare function loadConfig(configOrPath?: string | Partial<VisorConfig>, options?: {
672
+ strict?: boolean;
673
+ }): Promise<VisorConfig>;
665
674
  /** Expand check IDs by including their dependencies (shallow->deep). */
666
675
  declare function resolveChecks(checkIds: string[], config: VisorConfig | undefined): string[];
667
676
  /**
package/dist/sdk/sdk.js CHANGED
@@ -2424,14 +2424,66 @@ var init_reviewer = __esm({
2424
2424
  "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
2425
  );
2426
2426
  }
2427
+ /**
2428
+ * Helper to check if a schema definition has a "text" field in its properties
2429
+ */
2430
+ async schemaHasTextField(schema) {
2431
+ try {
2432
+ let schemaObj;
2433
+ if (typeof schema === "object") {
2434
+ schemaObj = schema;
2435
+ } else {
2436
+ const fs12 = require("fs").promises;
2437
+ const path12 = require("path");
2438
+ const sanitizedSchemaName = schema.replace(/[^a-zA-Z0-9-]/g, "");
2439
+ if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
2440
+ return false;
2441
+ }
2442
+ const schemaPath = path12.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
2443
+ try {
2444
+ const schemaContent = await fs12.readFile(schemaPath, "utf-8");
2445
+ schemaObj = JSON.parse(schemaContent);
2446
+ } catch {
2447
+ return false;
2448
+ }
2449
+ }
2450
+ const properties = schemaObj.properties;
2451
+ return !!(properties && "text" in properties);
2452
+ } catch {
2453
+ return false;
2454
+ }
2455
+ }
2456
+ /**
2457
+ * Filter check results to only include those that should post GitHub comments
2458
+ */
2459
+ async filterCommentGeneratingChecks(checkResults, config) {
2460
+ const filtered = [];
2461
+ for (const r of checkResults) {
2462
+ const cfg = config.checks?.[r.checkName];
2463
+ const type = cfg?.type || "ai";
2464
+ const schema = cfg?.schema;
2465
+ let shouldPostComment = false;
2466
+ const isAICheck = type === "ai" || type === "claude-code";
2467
+ if (!schema || schema === "") {
2468
+ shouldPostComment = isAICheck;
2469
+ } else if (typeof schema === "string") {
2470
+ if (schema === "text" || schema === "plain") {
2471
+ shouldPostComment = true;
2472
+ } else {
2473
+ shouldPostComment = await this.schemaHasTextField(schema);
2474
+ }
2475
+ } else if (typeof schema === "object") {
2476
+ shouldPostComment = await this.schemaHasTextField(schema);
2477
+ }
2478
+ if (shouldPostComment) {
2479
+ filtered.push(r);
2480
+ }
2481
+ }
2482
+ return filtered;
2483
+ }
2427
2484
  async postReviewComment(owner, repo, prNumber, groupedResults, options = {}) {
2428
2485
  for (const [groupName, checkResults] of Object.entries(groupedResults)) {
2429
- const filteredResults = options.config ? checkResults.filter((r) => {
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;
2486
+ const filteredResults = options.config ? await this.filterCommentGeneratingChecks(checkResults, options.config) : checkResults;
2435
2487
  if (!filteredResults || filteredResults.length === 0) {
2436
2488
  continue;
2437
2489
  }
@@ -5127,7 +5179,8 @@ var init_log_check_provider = __esm({
5127
5179
  dependencyResults,
5128
5180
  includePrContext,
5129
5181
  includeDependencies,
5130
- includeMetadata
5182
+ includeMetadata,
5183
+ config.__outputHistory
5131
5184
  );
5132
5185
  const renderedMessage = await this.liquid.parseAndRender(message, templateContext);
5133
5186
  const logOutput = this.formatLogOutput(
@@ -5148,7 +5201,7 @@ var init_log_check_provider = __esm({
5148
5201
  logOutput
5149
5202
  };
5150
5203
  }
5151
- buildTemplateContext(prInfo, dependencyResults, _includePrContext = true, _includeDependencies = true, includeMetadata = true) {
5204
+ buildTemplateContext(prInfo, dependencyResults, _includePrContext = true, _includeDependencies = true, includeMetadata = true, outputHistory) {
5152
5205
  const context2 = {};
5153
5206
  context2.pr = {
5154
5207
  number: prInfo.number,
@@ -5172,6 +5225,7 @@ var init_log_check_provider = __esm({
5172
5225
  if (dependencyResults) {
5173
5226
  const dependencies = {};
5174
5227
  const outputs = {};
5228
+ const history = {};
5175
5229
  context2.dependencyCount = dependencyResults.size;
5176
5230
  for (const [checkName, result] of dependencyResults.entries()) {
5177
5231
  dependencies[checkName] = {
@@ -5182,6 +5236,12 @@ var init_log_check_provider = __esm({
5182
5236
  const summary = result;
5183
5237
  outputs[checkName] = summary.output !== void 0 ? summary.output : summary;
5184
5238
  }
5239
+ if (outputHistory) {
5240
+ for (const [checkName, historyArray] of outputHistory) {
5241
+ history[checkName] = historyArray;
5242
+ }
5243
+ }
5244
+ outputs.history = history;
5185
5245
  context2.dependencies = dependencies;
5186
5246
  context2.outputs = outputs;
5187
5247
  }
@@ -6084,7 +6144,10 @@ var init_command_check_provider = __esm({
6084
6144
  },
6085
6145
  files: prInfo.files,
6086
6146
  fileCount: prInfo.files.length,
6087
- outputs: this.buildOutputContext(dependencyResults),
6147
+ outputs: this.buildOutputContext(
6148
+ dependencyResults,
6149
+ config.__outputHistory
6150
+ ),
6088
6151
  env: this.getSafeEnvironmentVariables()
6089
6152
  };
6090
6153
  logger.debug(
@@ -6810,16 +6873,23 @@ ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
6810
6873
  };
6811
6874
  }
6812
6875
  }
6813
- buildOutputContext(dependencyResults) {
6876
+ buildOutputContext(dependencyResults, outputHistory) {
6814
6877
  if (!dependencyResults) {
6815
6878
  return {};
6816
6879
  }
6817
6880
  const outputs = {};
6881
+ const history = {};
6818
6882
  for (const [checkName, result] of dependencyResults) {
6819
6883
  const summary = result;
6820
6884
  const value = summary.output !== void 0 ? summary.output : summary;
6821
6885
  outputs[checkName] = this.makeJsonSmart(value);
6822
6886
  }
6887
+ if (outputHistory) {
6888
+ for (const [checkName, historyArray] of outputHistory) {
6889
+ history[checkName] = historyArray.map((val) => this.makeJsonSmart(val));
6890
+ }
6891
+ }
6892
+ outputs.history = history;
6823
6893
  return outputs;
6824
6894
  }
6825
6895
  /**
@@ -7377,7 +7447,12 @@ var init_memory_check_provider = __esm({
7377
7447
  const key = config.key;
7378
7448
  const namespace = config.namespace;
7379
7449
  const memoryStore = MemoryStore.getInstance();
7380
- const templateContext = this.buildTemplateContext(prInfo, dependencyResults, memoryStore);
7450
+ const templateContext = this.buildTemplateContext(
7451
+ prInfo,
7452
+ dependencyResults,
7453
+ memoryStore,
7454
+ config.__outputHistory
7455
+ );
7381
7456
  let result;
7382
7457
  try {
7383
7458
  switch (operation) {
@@ -7660,7 +7735,7 @@ var init_memory_check_provider = __esm({
7660
7735
  /**
7661
7736
  * Build template context for Liquid and JS evaluation
7662
7737
  */
7663
- buildTemplateContext(prInfo, dependencyResults, memoryStore) {
7738
+ buildTemplateContext(prInfo, dependencyResults, memoryStore, outputHistory) {
7664
7739
  const context2 = {};
7665
7740
  context2.pr = {
7666
7741
  number: prInfo.number,
@@ -7679,14 +7754,21 @@ var init_memory_check_provider = __esm({
7679
7754
  changes: f.changes
7680
7755
  }))
7681
7756
  };
7757
+ const outputs = {};
7758
+ const history = {};
7682
7759
  if (dependencyResults) {
7683
- const outputs = {};
7684
7760
  for (const [checkName, result] of dependencyResults.entries()) {
7685
7761
  const summary = result;
7686
7762
  outputs[checkName] = summary.output !== void 0 ? summary.output : summary;
7687
7763
  }
7688
- context2.outputs = outputs;
7689
7764
  }
7765
+ if (outputHistory) {
7766
+ for (const [checkName, historyArray] of outputHistory) {
7767
+ history[checkName] = historyArray;
7768
+ }
7769
+ }
7770
+ outputs.history = history;
7771
+ context2.outputs = outputs;
7690
7772
  if (memoryStore) {
7691
7773
  context2.memory = {
7692
7774
  get: (key, ns) => memoryStore.get(key, ns),
@@ -10681,6 +10763,8 @@ var init_check_execution_engine = __esm({
10681
10763
  webhookContext;
10682
10764
  routingSandbox;
10683
10765
  executionStats = /* @__PURE__ */ new Map();
10766
+ // Track history of all outputs for each check (useful for loops and goto)
10767
+ outputHistory = /* @__PURE__ */ new Map();
10684
10768
  // Event override to simulate alternate event (used during routing goto)
10685
10769
  routingEventOverride;
10686
10770
  // Cached GitHub context for context elevation when running in Actions
@@ -10924,6 +11008,8 @@ ${expr}
10924
11008
  transform_js: targetCfg.transform_js,
10925
11009
  env: targetCfg.env,
10926
11010
  forEach: targetCfg.forEach,
11011
+ // Pass output history for loop/goto scenarios
11012
+ __outputHistory: this.outputHistory,
10927
11013
  // Include provider-specific keys (e.g., op/values for github)
10928
11014
  ...targetCfg,
10929
11015
  ai: {
@@ -10991,6 +11077,10 @@ ${expr}
10991
11077
  timestamp: Date.now()
10992
11078
  }));
10993
11079
  const enriched = { ...r, issues: enrichedIssues };
11080
+ const enrichedWithOutput = enriched;
11081
+ if (enrichedWithOutput.output !== void 0) {
11082
+ this.trackOutputHistory(target, enrichedWithOutput.output);
11083
+ }
10994
11084
  resultsMap?.set(target, enriched);
10995
11085
  if (debug) log2(`\u{1F527} Debug: inline executed '${target}', issues: ${enrichedIssues.length}`);
10996
11086
  return enriched;
@@ -11759,6 +11849,8 @@ ${expr}
11759
11849
  ai_model: checkConfig.ai_model || config.ai_model,
11760
11850
  // Pass claude_code config if present
11761
11851
  claude_code: checkConfig.claude_code,
11852
+ // Pass output history for loop/goto scenarios
11853
+ __outputHistory: this.outputHistory,
11762
11854
  // Pass any provider-specific config
11763
11855
  ...checkConfig
11764
11856
  };
@@ -11795,10 +11887,14 @@ ${expr}
11795
11887
  }
11796
11888
  }
11797
11889
  const content = await this.renderCheckContent(checkName, result, checkConfig, prInfo);
11890
+ let group = checkConfig.group || "default";
11891
+ if (config?.output?.pr_comment?.group_by === "check" && !checkConfig.group) {
11892
+ group = checkName;
11893
+ }
11798
11894
  return {
11799
11895
  checkName,
11800
11896
  content,
11801
- group: checkConfig.group || "default",
11897
+ group,
11802
11898
  output: result.output,
11803
11899
  debug: result.debug,
11804
11900
  issues: result.issues
@@ -11937,16 +12033,19 @@ ${expr}
11937
12033
  }
11938
12034
  ];
11939
12035
  }
12036
+ let group = checkConfig.group || "default";
12037
+ if (config?.output?.pr_comment?.group_by === "check" && !checkConfig.group) {
12038
+ group = checkName;
12039
+ }
11940
12040
  const checkResult = {
11941
12041
  checkName,
11942
12042
  content,
11943
- group: checkConfig.group || "default",
12043
+ group,
11944
12044
  output: checkSummary.output,
11945
12045
  debug: reviewSummary.debug,
11946
12046
  issues: issuesForCheck
11947
12047
  // Include structured issues + rendering error if any
11948
12048
  };
11949
- const group = checkResult.group;
11950
12049
  if (!groupedResults[group]) {
11951
12050
  groupedResults[group] = [];
11952
12051
  }
@@ -12847,6 +12946,10 @@ ${expr}
12847
12946
  itemResult.issues || [],
12848
12947
  itemResult.output
12849
12948
  );
12949
+ const itemOutput = itemResult.output;
12950
+ if (itemOutput !== void 0) {
12951
+ this.trackOutputHistory(checkName, itemOutput);
12952
+ }
12850
12953
  const descendantSet = (() => {
12851
12954
  const visited = /* @__PURE__ */ new Set();
12852
12955
  const stack = [checkName];
@@ -13477,6 +13580,10 @@ ${error.stack || ""}` : String(error);
13477
13580
  ]);
13478
13581
  } catch {
13479
13582
  }
13583
+ const reviewResultWithOutput = reviewResult;
13584
+ if (reviewResultWithOutput.output !== void 0) {
13585
+ this.trackOutputHistory(checkName, reviewResultWithOutput.output);
13586
+ }
13480
13587
  results.set(checkName, reviewResult);
13481
13588
  } else {
13482
13589
  const errorSummary = {
@@ -14720,6 +14827,16 @@ ${result.value.result.debug.rawResponse}`;
14720
14827
  stats.outputsProduced = (stats.outputsProduced || 0) + 1;
14721
14828
  }
14722
14829
  }
14830
+ /**
14831
+ * Track output in history for loop/goto scenarios
14832
+ */
14833
+ trackOutputHistory(checkName, output) {
14834
+ if (output === void 0) return;
14835
+ if (!this.outputHistory.has(checkName)) {
14836
+ this.outputHistory.set(checkName, []);
14837
+ }
14838
+ this.outputHistory.get(checkName).push(output);
14839
+ }
14723
14840
  /**
14724
14841
  * Record that a check was skipped
14725
14842
  */
@@ -16823,8 +16940,10 @@ var ConfigManager = class {
16823
16940
  }
16824
16941
  /**
16825
16942
  * Validate configuration against schema
16943
+ * @param config The config to validate
16944
+ * @param strict If true, treat warnings as errors (default: false)
16826
16945
  */
16827
- validateConfig(config) {
16946
+ validateConfig(config, strict = false) {
16828
16947
  const errors = [];
16829
16948
  const warnings = [];
16830
16949
  this.validateWithAjvSchema(config, errors, warnings);
@@ -16932,10 +17051,13 @@ var ConfigManager = class {
16932
17051
  if (config.tag_filter) {
16933
17052
  this.validateTagFilter(config.tag_filter, errors);
16934
17053
  }
17054
+ if (strict && warnings.length > 0) {
17055
+ errors.push(...warnings);
17056
+ }
16935
17057
  if (errors.length > 0) {
16936
17058
  throw new Error(errors[0].message);
16937
17059
  }
16938
- if (warnings.length > 0) {
17060
+ if (!strict && warnings.length > 0) {
16939
17061
  for (const w of warnings) {
16940
17062
  logger.warn(`\u26A0\uFE0F Config warning [${w.field}]: ${w.message}`);
16941
17063
  }
@@ -17391,9 +17513,25 @@ var __ajvValidate = null;
17391
17513
  var __ajvErrors = null;
17392
17514
 
17393
17515
  // src/sdk.ts
17394
- async function loadConfig(configPath) {
17516
+ async function loadConfig(configOrPath, options) {
17395
17517
  const cm = new ConfigManager();
17396
- if (configPath) return cm.loadConfig(configPath);
17518
+ if (typeof configOrPath === "object" && configOrPath !== null) {
17519
+ cm.validateConfig(configOrPath, options?.strict ?? false);
17520
+ const defaultConfig = {
17521
+ version: "1.0",
17522
+ checks: {},
17523
+ max_parallelism: 3,
17524
+ fail_fast: false
17525
+ };
17526
+ return {
17527
+ ...defaultConfig,
17528
+ ...configOrPath,
17529
+ checks: configOrPath.checks || {}
17530
+ };
17531
+ }
17532
+ if (typeof configOrPath === "string") {
17533
+ return cm.loadConfig(configOrPath);
17534
+ }
17397
17535
  return cm.findAndLoadConfig();
17398
17536
  }
17399
17537
  function resolveChecks(checkIds, config) {
@@ -17419,7 +17557,15 @@ function resolveChecks(checkIds, config) {
17419
17557
  }
17420
17558
  async function runChecks(opts = {}) {
17421
17559
  const cm = new ConfigManager();
17422
- const config = opts.config ? opts.config : opts.configPath ? await cm.loadConfig(opts.configPath) : await cm.findAndLoadConfig();
17560
+ let config;
17561
+ if (opts.config) {
17562
+ cm.validateConfig(opts.config, opts.strictValidation ?? false);
17563
+ config = opts.config;
17564
+ } else if (opts.configPath) {
17565
+ config = await cm.loadConfig(opts.configPath);
17566
+ } else {
17567
+ config = await cm.findAndLoadConfig();
17568
+ }
17423
17569
  const checks = opts.checks && opts.checks.length > 0 ? resolveChecks(opts.checks, config) : Object.keys(config.checks || {});
17424
17570
  const engine = new CheckExecutionEngine(opts.cwd);
17425
17571
  const result = await engine.executeChecks({