@probelabs/visor 0.1.83 → 0.1.85

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.
@@ -260,8 +260,8 @@ interface CheckConfig {
260
260
  template?: CustomTemplateConfig;
261
261
  /** Condition to determine if check should run - runs if expression evaluates to true */
262
262
  if?: string;
263
- /** Whether to reuse AI session from dependency checks (only works with depends_on) */
264
- reuse_ai_session?: boolean;
263
+ /** Check name to reuse AI session from, or true to use first dependency (only works with depends_on) */
264
+ reuse_ai_session?: string | boolean;
265
265
  /** Simple fail condition - fails check if expression evaluates to true */
266
266
  fail_if?: string;
267
267
  /** Check-specific failure conditions - optional (deprecated, use fail_if) */
package/dist/sdk/sdk.d.ts CHANGED
@@ -260,8 +260,8 @@ interface CheckConfig {
260
260
  template?: CustomTemplateConfig;
261
261
  /** Condition to determine if check should run - runs if expression evaluates to true */
262
262
  if?: string;
263
- /** Whether to reuse AI session from dependency checks (only works with depends_on) */
264
- reuse_ai_session?: boolean;
263
+ /** Check name to reuse AI session from, or true to use first dependency (only works with depends_on) */
264
+ reuse_ai_session?: string | boolean;
265
265
  /** Simple fail condition - fails check if expression evaluates to true */
266
266
  fail_if?: string;
267
267
  /** Check-specific failure conditions - optional (deprecated, use fail_if) */
package/dist/sdk/sdk.js CHANGED
@@ -4127,6 +4127,7 @@ var init_command_check_provider = __esm({
4127
4127
  try {
4128
4128
  const parsed = JSON.parse(rawOutput);
4129
4129
  output = parsed;
4130
+ logger.debug(`\u{1F527} Debug: Parsed entire output as JSON successfully`);
4130
4131
  } catch {
4131
4132
  const extracted2 = this.extractJsonFromEnd(rawOutput);
4132
4133
  if (extracted2) {
@@ -4135,13 +4136,28 @@ var init_command_check_provider = __esm({
4135
4136
  logger.debug(
4136
4137
  `\u{1F527} Debug: Extracted and parsed JSON from end of output (${extracted2.length} chars from ${rawOutput.length} total)`
4137
4138
  );
4138
- } catch {
4139
+ logger.debug(`\u{1F527} Debug: Extracted JSON content: ${extracted2.slice(0, 200)}`);
4140
+ } catch (parseError) {
4141
+ logger.debug(
4142
+ `\u{1F527} Debug: Extracted text is not valid JSON: ${parseError instanceof Error ? parseError.message : "Unknown error"}`
4143
+ );
4139
4144
  output = rawOutput;
4140
4145
  }
4141
4146
  } else {
4147
+ logger.debug(`\u{1F527} Debug: No JSON found in output, keeping as string`);
4142
4148
  output = rawOutput;
4143
4149
  }
4144
4150
  }
4151
+ if (output !== rawOutput) {
4152
+ try {
4153
+ const outputType = Array.isArray(output) ? `array[${output.length}]` : typeof output;
4154
+ logger.debug(`\u{1F527} Debug: Parsed output type: ${outputType}`);
4155
+ if (typeof output === "object" && output !== null) {
4156
+ logger.debug(`\u{1F527} Debug: Parsed output keys: ${Object.keys(output).join(", ")}`);
4157
+ }
4158
+ } catch {
4159
+ }
4160
+ }
4145
4161
  let finalOutput = output;
4146
4162
  if (transform) {
4147
4163
  try {
@@ -5436,6 +5452,14 @@ var init_failure_condition_evaluator = __esm({
5436
5452
  */
5437
5453
  buildEvaluationContext(checkName, checkSchema, checkGroup, reviewSummary, previousOutputs) {
5438
5454
  const { issues, debug } = reviewSummary;
5455
+ const reviewSummaryWithOutput = reviewSummary;
5456
+ const {
5457
+ output: extractedOutput,
5458
+ // Exclude issues from otherFields since we handle it separately
5459
+ issues: _issues,
5460
+ // eslint-disable-line @typescript-eslint/no-unused-vars
5461
+ ...otherFields
5462
+ } = reviewSummaryWithOutput;
5439
5463
  const context = {
5440
5464
  output: {
5441
5465
  issues: (issues || []).map((issue) => ({
@@ -5452,9 +5476,9 @@ var init_failure_condition_evaluator = __esm({
5452
5476
  replacement: issue.replacement
5453
5477
  })),
5454
5478
  // Include additional schema-specific data from reviewSummary
5455
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5456
- ...reviewSummary
5457
- // Pass through any additional fields
5479
+ ...otherFields,
5480
+ // Spread the extracted output directly (avoid output.output nesting)
5481
+ ...extractedOutput && typeof extractedOutput === "object" ? extractedOutput : {}
5458
5482
  },
5459
5483
  outputs: (() => {
5460
5484
  if (!previousOutputs) return {};
@@ -6894,6 +6918,31 @@ ${expr}
6894
6918
  };
6895
6919
  providerConfig.forEach = checkConfig.forEach;
6896
6920
  const result = await provider.execute(prInfo, providerConfig);
6921
+ if (checkConfig.forEach && (!result.issues || result.issues.length === 0)) {
6922
+ const reviewSummaryWithOutput = result;
6923
+ const validation = this.validateAndNormalizeForEachOutput(
6924
+ checkName,
6925
+ reviewSummaryWithOutput.output,
6926
+ checkConfig.group
6927
+ );
6928
+ if (!validation.isValid) {
6929
+ return validation.error;
6930
+ }
6931
+ }
6932
+ if (config && (config.fail_if || checkConfig.fail_if)) {
6933
+ const failureResults = await this.evaluateFailureConditions(checkName, result, config);
6934
+ if (failureResults.length > 0) {
6935
+ const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
6936
+ file: "system",
6937
+ line: 0,
6938
+ ruleId: f.conditionName,
6939
+ message: f.message || `Failure condition met: ${f.expression}`,
6940
+ severity: f.severity || "error",
6941
+ category: "logic"
6942
+ }));
6943
+ result.issues = [...result.issues || [], ...failureIssues];
6944
+ }
6945
+ }
6897
6946
  const content = await this.renderCheckContent(checkName, result, checkConfig, prInfo);
6898
6947
  return {
6899
6948
  checkName,
@@ -6904,6 +6953,53 @@ ${expr}
6904
6953
  // Include structured issues
6905
6954
  };
6906
6955
  }
6956
+ /**
6957
+ * Validate and normalize forEach output
6958
+ * Returns normalized array or throws validation error result
6959
+ */
6960
+ validateAndNormalizeForEachOutput(checkName, output, checkGroup) {
6961
+ if (output === void 0) {
6962
+ logger.error(`\u2717 forEach check "${checkName}" produced undefined output`);
6963
+ return {
6964
+ isValid: false,
6965
+ error: {
6966
+ checkName,
6967
+ content: "",
6968
+ group: checkGroup || "default",
6969
+ issues: [
6970
+ {
6971
+ file: "system",
6972
+ line: 0,
6973
+ ruleId: "forEach/undefined_output",
6974
+ message: `forEach check "${checkName}" produced undefined output. Verify your command outputs valid data and your transform_js returns a value.`,
6975
+ severity: "error",
6976
+ category: "logic"
6977
+ }
6978
+ ]
6979
+ }
6980
+ };
6981
+ }
6982
+ let normalizedOutput;
6983
+ if (Array.isArray(output)) {
6984
+ normalizedOutput = output;
6985
+ } else if (typeof output === "string") {
6986
+ try {
6987
+ const parsed = JSON.parse(output);
6988
+ normalizedOutput = Array.isArray(parsed) ? parsed : [parsed];
6989
+ } catch {
6990
+ normalizedOutput = [output];
6991
+ }
6992
+ } else if (output === null) {
6993
+ normalizedOutput = [];
6994
+ } else {
6995
+ normalizedOutput = [output];
6996
+ }
6997
+ logger.info(` Found ${normalizedOutput.length} items for forEach iteration`);
6998
+ return {
6999
+ isValid: true,
7000
+ normalizedOutput
7001
+ };
7002
+ }
6907
7003
  /**
6908
7004
  * Execute multiple checks with dependency awareness - return grouped results with statistics
6909
7005
  */
@@ -7113,10 +7209,14 @@ ${expr}
7113
7209
  const checkConfig = config.checks[checkName];
7114
7210
  if (checkConfig) {
7115
7211
  dependencies[checkName] = checkConfig.depends_on || [];
7116
- if (checkConfig.reuse_ai_session === true) {
7212
+ if (checkConfig.reuse_ai_session) {
7117
7213
  sessionReuseChecks.add(checkName);
7118
- if (checkConfig.depends_on && checkConfig.depends_on.length > 0) {
7119
- sessionProviders.set(checkName, checkConfig.depends_on[0]);
7214
+ if (typeof checkConfig.reuse_ai_session === "string") {
7215
+ sessionProviders.set(checkName, checkConfig.reuse_ai_session);
7216
+ } else if (checkConfig.reuse_ai_session === true) {
7217
+ if (checkConfig.depends_on && checkConfig.depends_on.length > 0) {
7218
+ sessionProviders.set(checkName, checkConfig.depends_on[0]);
7219
+ }
7120
7220
  }
7121
7221
  }
7122
7222
  } else {
@@ -7679,27 +7779,23 @@ ${expr}
7679
7779
  }
7680
7780
  const reviewResult = result.value.result;
7681
7781
  const reviewSummaryWithOutput = reviewResult;
7682
- if (checkConfig?.forEach && reviewSummaryWithOutput.output !== void 0) {
7782
+ if (checkConfig?.forEach && (!reviewResult.issues || reviewResult.issues.length === 0)) {
7783
+ const validation2 = this.validateAndNormalizeForEachOutput(
7784
+ checkName,
7785
+ reviewSummaryWithOutput.output,
7786
+ checkConfig.group
7787
+ );
7788
+ if (!validation2.isValid) {
7789
+ results.set(
7790
+ checkName,
7791
+ validation2.error.issues ? { issues: validation2.error.issues } : {}
7792
+ );
7793
+ continue;
7794
+ }
7795
+ const normalizedOutput = validation2.normalizedOutput;
7683
7796
  logger.debug(
7684
7797
  `\u{1F527} Debug: Raw output for forEach check ${checkName}: ${Array.isArray(reviewSummaryWithOutput.output) ? `array(${reviewSummaryWithOutput.output.length})` : typeof reviewSummaryWithOutput.output}`
7685
7798
  );
7686
- const rawOutput = reviewSummaryWithOutput.output;
7687
- let normalizedOutput;
7688
- if (Array.isArray(rawOutput)) {
7689
- normalizedOutput = rawOutput;
7690
- } else if (typeof rawOutput === "string") {
7691
- try {
7692
- const parsed = JSON.parse(rawOutput);
7693
- normalizedOutput = Array.isArray(parsed) ? parsed : [parsed];
7694
- } catch {
7695
- normalizedOutput = [rawOutput];
7696
- }
7697
- } else if (rawOutput === void 0 || rawOutput === null) {
7698
- normalizedOutput = [];
7699
- } else {
7700
- normalizedOutput = [rawOutput];
7701
- }
7702
- logger.info(` Found ${normalizedOutput.length} items for forEach iteration`);
7703
7799
  try {
7704
7800
  const preview = JSON.stringify(normalizedOutput);
7705
7801
  logger.debug(
@@ -9428,8 +9524,8 @@ var init_config_schema = __esm({
9428
9524
  description: "Condition to determine if check should run - runs if expression evaluates to true"
9429
9525
  },
9430
9526
  reuse_ai_session: {
9431
- type: "boolean",
9432
- description: "Whether to reuse AI session from dependency checks (only works with depends_on)"
9527
+ type: ["string", "boolean"],
9528
+ description: "Check name to reuse AI session from, or true to use first dependency (only works with depends_on)"
9433
9529
  },
9434
9530
  fail_if: {
9435
9531
  type: "string",
@@ -10766,7 +10862,7 @@ var ConfigManager = class {
10766
10862
  if (!checkConfig.type) {
10767
10863
  checkConfig.type = "ai";
10768
10864
  }
10769
- this.validateCheckConfig(checkName, checkConfig, errors);
10865
+ this.validateCheckConfig(checkName, checkConfig, errors, config);
10770
10866
  if (checkConfig.ai_mcp_servers) {
10771
10867
  this.validateMcpServersObject(
10772
10868
  checkConfig.ai_mcp_servers,
@@ -10866,7 +10962,7 @@ var ConfigManager = class {
10866
10962
  /**
10867
10963
  * Validate individual check configuration
10868
10964
  */
10869
- validateCheckConfig(checkName, checkConfig, errors) {
10965
+ validateCheckConfig(checkName, checkConfig, errors, config) {
10870
10966
  if (!checkConfig.type) {
10871
10967
  checkConfig.type = "ai";
10872
10968
  }
@@ -10947,12 +11043,23 @@ var ConfigManager = class {
10947
11043
  }
10948
11044
  }
10949
11045
  if (checkConfig.reuse_ai_session !== void 0) {
10950
- if (typeof checkConfig.reuse_ai_session !== "boolean") {
11046
+ const isString = typeof checkConfig.reuse_ai_session === "string";
11047
+ const isBoolean = typeof checkConfig.reuse_ai_session === "boolean";
11048
+ if (!isString && !isBoolean) {
10951
11049
  errors.push({
10952
11050
  field: `checks.${checkName}.reuse_ai_session`,
10953
- message: `Invalid reuse_ai_session value for "${checkName}": must be boolean`,
11051
+ message: `Invalid reuse_ai_session value for "${checkName}": must be string (check name) or boolean`,
10954
11052
  value: checkConfig.reuse_ai_session
10955
11053
  });
11054
+ } else if (isString) {
11055
+ const targetCheckName = checkConfig.reuse_ai_session;
11056
+ if (!config?.checks || !config.checks[targetCheckName]) {
11057
+ errors.push({
11058
+ field: `checks.${checkName}.reuse_ai_session`,
11059
+ message: `Check "${checkName}" references non-existent check "${targetCheckName}" for session reuse`,
11060
+ value: checkConfig.reuse_ai_session
11061
+ });
11062
+ }
10956
11063
  } else if (checkConfig.reuse_ai_session === true) {
10957
11064
  if (!checkConfig.depends_on || !Array.isArray(checkConfig.depends_on) || checkConfig.depends_on.length === 0) {
10958
11065
  errors.push({