@probelabs/visor 0.1.81 → 0.1.83

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.
@@ -248,6 +248,8 @@ interface CheckConfig {
248
248
  claude_code?: ClaudeCodeConfig;
249
249
  /** Environment variables for this check */
250
250
  env?: EnvConfig;
251
+ /** Timeout in seconds for command execution (default: 60) */
252
+ timeout?: number;
251
253
  /** Check IDs that this check depends on (optional) */
252
254
  depends_on?: string[];
253
255
  /** Group name for comment separation (e.g., "code-review", "pr-overview") - optional */
package/dist/sdk/sdk.d.ts CHANGED
@@ -248,6 +248,8 @@ interface CheckConfig {
248
248
  claude_code?: ClaudeCodeConfig;
249
249
  /** Environment variables for this check */
250
250
  env?: EnvConfig;
251
+ /** Timeout in seconds for command execution (default: 60) */
252
+ timeout?: number;
251
253
  /** Check IDs that this check depends on (optional) */
252
254
  depends_on?: string[];
253
255
  /** Group name for comment separation (e.g., "code-review", "pr-overview") - optional */
package/dist/sdk/sdk.js CHANGED
@@ -4316,6 +4316,16 @@ return ${returnTarget};
4316
4316
  return result;
4317
4317
  } catch (error) {
4318
4318
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
4319
+ let isTimeout = false;
4320
+ if (error && typeof error === "object") {
4321
+ const execError = error;
4322
+ if (execError.killed && execError.signal === "SIGTERM") {
4323
+ isTimeout = true;
4324
+ }
4325
+ if (execError.code === "ETIMEDOUT") {
4326
+ isTimeout = true;
4327
+ }
4328
+ }
4319
4329
  let stderrOutput = "";
4320
4330
  if (error && typeof error === "object") {
4321
4331
  const execError = error;
@@ -4323,17 +4333,32 @@ return ${returnTarget};
4323
4333
  stderrOutput = execError.stderr.trim();
4324
4334
  }
4325
4335
  }
4326
- const detailedMessage = stderrOutput ? `Command execution failed: ${errorMessage}
4336
+ let detailedMessage;
4337
+ let ruleId;
4338
+ if (isTimeout) {
4339
+ const timeoutSeconds = config.timeout || 60;
4340
+ detailedMessage = `Command execution timed out after ${timeoutSeconds} seconds`;
4341
+ if (stderrOutput) {
4342
+ detailedMessage += `
4343
+
4344
+ Stderr output:
4345
+ ${stderrOutput}`;
4346
+ }
4347
+ ruleId = "command/timeout";
4348
+ } else {
4349
+ detailedMessage = stderrOutput ? `Command execution failed: ${errorMessage}
4327
4350
 
4328
4351
  Stderr output:
4329
4352
  ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
4353
+ ruleId = "command/execution_error";
4354
+ }
4330
4355
  logger.error(`\u2717 ${detailedMessage}`);
4331
4356
  return {
4332
4357
  issues: [
4333
4358
  {
4334
4359
  file: "command",
4335
4360
  line: 0,
4336
- ruleId: "command/execution_error",
4361
+ ruleId,
4337
4362
  message: detailedMessage,
4338
4363
  severity: "error",
4339
4364
  category: "logic"
@@ -7324,172 +7349,187 @@ ${expr}
7324
7349
  let finalResult;
7325
7350
  if (isForEachDependent && forEachParentName) {
7326
7351
  this.recordForEachPreview(checkName, forEachItems);
7327
- if (debug) {
7328
- log2(
7329
- `\u{1F504} Debug: Check "${checkName}" depends on forEach check "${forEachParentName}", executing ${forEachItems.length} times`
7352
+ if (forEachItems.length === 0) {
7353
+ if (debug) {
7354
+ log2(
7355
+ `\u{1F504} Debug: Skipping check "${checkName}" - forEach check "${forEachParentName}" returned 0 items`
7356
+ );
7357
+ }
7358
+ logger.info(` forEach: no items from "${forEachParentName}", skipping check...`);
7359
+ finalResult = {
7360
+ issues: [],
7361
+ output: []
7362
+ };
7363
+ finalResult.isForEach = true;
7364
+ finalResult.forEachItems = [];
7365
+ } else {
7366
+ if (debug) {
7367
+ log2(
7368
+ `\u{1F504} Debug: Check "${checkName}" depends on forEach check "${forEachParentName}", executing ${forEachItems.length} times`
7369
+ );
7370
+ }
7371
+ logger.info(
7372
+ ` forEach: processing ${forEachItems.length} items from "${forEachParentName}"...`
7330
7373
  );
7331
- }
7332
- logger.info(
7333
- ` forEach: processing ${forEachItems.length} items from "${forEachParentName}"...`
7334
- );
7335
- const allIssues = [];
7336
- const allOutputs = [];
7337
- const aggregatedContents = [];
7338
- const itemTasks = forEachItems.map((item, itemIndex) => async () => {
7339
- const forEachDependencyResults = /* @__PURE__ */ new Map();
7340
- for (const [depName, depResult] of dependencyResults) {
7341
- if (forEachParents.includes(depName)) {
7342
- const depForEachResult = depResult;
7343
- if (Array.isArray(depForEachResult.output) && depForEachResult.output[itemIndex] !== void 0) {
7344
- const modifiedResult = {
7345
- issues: [],
7346
- output: depForEachResult.output[itemIndex]
7347
- };
7348
- forEachDependencyResults.set(depName, modifiedResult);
7349
- const rawResult = {
7350
- issues: [],
7351
- output: depForEachResult.output
7352
- };
7353
- forEachDependencyResults.set(`${depName}-raw`, rawResult);
7374
+ const allIssues = [];
7375
+ const allOutputs = [];
7376
+ const aggregatedContents = [];
7377
+ const itemTasks = forEachItems.map((item, itemIndex) => async () => {
7378
+ const forEachDependencyResults = /* @__PURE__ */ new Map();
7379
+ for (const [depName, depResult] of dependencyResults) {
7380
+ if (forEachParents.includes(depName)) {
7381
+ const depForEachResult = depResult;
7382
+ if (Array.isArray(depForEachResult.output) && depForEachResult.output[itemIndex] !== void 0) {
7383
+ const modifiedResult = {
7384
+ issues: [],
7385
+ output: depForEachResult.output[itemIndex]
7386
+ };
7387
+ forEachDependencyResults.set(depName, modifiedResult);
7388
+ const rawResult = {
7389
+ issues: [],
7390
+ output: depForEachResult.output
7391
+ };
7392
+ forEachDependencyResults.set(`${depName}-raw`, rawResult);
7393
+ } else {
7394
+ forEachDependencyResults.set(depName, depResult);
7395
+ }
7354
7396
  } else {
7355
7397
  forEachDependencyResults.set(depName, depResult);
7356
7398
  }
7357
- } else {
7358
- forEachDependencyResults.set(depName, depResult);
7359
7399
  }
7360
- }
7361
- if (checkConfig.if) {
7362
- const conditionResults = new Map(results);
7363
- for (const [depName, depResult] of forEachDependencyResults) {
7364
- conditionResults.set(depName, depResult);
7400
+ if (checkConfig.if) {
7401
+ const conditionResults = new Map(results);
7402
+ for (const [depName, depResult] of forEachDependencyResults) {
7403
+ conditionResults.set(depName, depResult);
7404
+ }
7405
+ const shouldRun = await this.evaluateCheckCondition(
7406
+ checkName,
7407
+ checkConfig.if,
7408
+ prInfo,
7409
+ conditionResults,
7410
+ debug
7411
+ );
7412
+ if (!shouldRun) {
7413
+ if (debug) {
7414
+ log2(
7415
+ `\u{1F504} Debug: Skipping forEach item ${itemIndex + 1} for check "${checkName}" (if condition evaluated to false)`
7416
+ );
7417
+ }
7418
+ return {
7419
+ index: itemIndex,
7420
+ itemResult: { issues: [] },
7421
+ skipped: true
7422
+ };
7423
+ }
7424
+ }
7425
+ if (debug) {
7426
+ log2(
7427
+ `\u{1F504} Debug: Executing check "${checkName}" for item ${itemIndex + 1}/${forEachItems.length}`
7428
+ );
7365
7429
  }
7366
- const shouldRun = await this.evaluateCheckCondition(
7430
+ const iterationStart = this.recordIterationStart(checkName);
7431
+ const itemResult = await this.executeWithRouting(
7367
7432
  checkName,
7368
- checkConfig.if,
7433
+ checkConfig,
7434
+ provider,
7435
+ providerConfig,
7369
7436
  prInfo,
7370
- conditionResults,
7371
- debug
7437
+ forEachDependencyResults,
7438
+ sessionInfo,
7439
+ config,
7440
+ dependencyGraph,
7441
+ debug,
7442
+ results,
7443
+ /*foreachContext*/
7444
+ {
7445
+ index: itemIndex,
7446
+ total: forEachItems.length,
7447
+ parent: forEachParentName
7448
+ }
7449
+ );
7450
+ const hadFatalError = (itemResult.issues || []).some((issue) => {
7451
+ const id = issue.ruleId || "";
7452
+ return id === "command/execution_error" || id.endsWith("/command/execution_error") || id === "command/transform_js_error" || id.endsWith("/command/transform_js_error") || id === "command/transform_error" || id.endsWith("/command/transform_error");
7453
+ });
7454
+ const iterationDuration = (Date.now() - iterationStart) / 1e3;
7455
+ this.recordIterationComplete(
7456
+ checkName,
7457
+ iterationStart,
7458
+ !hadFatalError,
7459
+ // Success if no fatal errors
7460
+ itemResult.issues || [],
7461
+ itemResult.output
7462
+ );
7463
+ logger.info(
7464
+ ` \u2714 ${itemIndex + 1}/${forEachItems.length} (${iterationDuration.toFixed(1)}s)`
7465
+ );
7466
+ return { index: itemIndex, itemResult };
7467
+ });
7468
+ const forEachConcurrency = Math.max(
7469
+ 1,
7470
+ Math.min(forEachItems.length, effectiveMaxParallelism)
7471
+ );
7472
+ if (debug && forEachConcurrency > 1) {
7473
+ log2(
7474
+ `\u{1F504} Debug: Limiting forEach concurrency for check "${checkName}" to ${forEachConcurrency}`
7372
7475
  );
7373
- if (!shouldRun) {
7476
+ }
7477
+ const forEachResults = await this.executeWithLimitedParallelism(
7478
+ itemTasks,
7479
+ forEachConcurrency,
7480
+ false
7481
+ );
7482
+ for (const result of forEachResults) {
7483
+ if (result.status === "rejected") {
7484
+ const error = result.reason;
7485
+ const errorMessage = error instanceof Error ? error.message : String(error);
7486
+ allIssues.push({
7487
+ ruleId: `${checkName}/forEach/iteration_error`,
7488
+ severity: "error",
7489
+ category: "logic",
7490
+ message: `forEach iteration failed: ${errorMessage}`,
7491
+ file: "",
7492
+ line: 0
7493
+ });
7374
7494
  if (debug) {
7375
7495
  log2(
7376
- `\u{1F504} Debug: Skipping forEach item ${itemIndex + 1} for check "${checkName}" (if condition evaluated to false)`
7496
+ `\u{1F504} Debug: forEach iteration for check "${checkName}" failed: ${errorMessage}`
7377
7497
  );
7378
7498
  }
7379
- return {
7380
- index: itemIndex,
7381
- itemResult: { issues: [] },
7382
- skipped: true
7383
- };
7499
+ continue;
7384
7500
  }
7385
- }
7386
- if (debug) {
7387
- log2(
7388
- `\u{1F504} Debug: Executing check "${checkName}" for item ${itemIndex + 1}/${forEachItems.length}`
7389
- );
7390
- }
7391
- const iterationStart = this.recordIterationStart(checkName);
7392
- const itemResult = await this.executeWithRouting(
7393
- checkName,
7394
- checkConfig,
7395
- provider,
7396
- providerConfig,
7397
- prInfo,
7398
- forEachDependencyResults,
7399
- sessionInfo,
7400
- config,
7401
- dependencyGraph,
7402
- debug,
7403
- results,
7404
- /*foreachContext*/
7405
- {
7406
- index: itemIndex,
7407
- total: forEachItems.length,
7408
- parent: forEachParentName
7501
+ if (result.value.skipped) {
7502
+ continue;
7409
7503
  }
7410
- );
7411
- const hadFatalError = (itemResult.issues || []).some((issue) => {
7412
- const id = issue.ruleId || "";
7413
- return id === "command/execution_error" || id.endsWith("/command/execution_error") || id === "command/transform_js_error" || id.endsWith("/command/transform_js_error") || id === "command/transform_error" || id.endsWith("/command/transform_error");
7414
- });
7415
- const iterationDuration = (Date.now() - iterationStart) / 1e3;
7416
- this.recordIterationComplete(
7417
- checkName,
7418
- iterationStart,
7419
- !hadFatalError,
7420
- // Success if no fatal errors
7421
- itemResult.issues || [],
7422
- itemResult.output
7423
- );
7424
- logger.info(
7425
- ` \u2714 ${itemIndex + 1}/${forEachItems.length} (${iterationDuration.toFixed(1)}s)`
7426
- );
7427
- return { index: itemIndex, itemResult };
7428
- });
7429
- const forEachConcurrency = Math.max(
7430
- 1,
7431
- Math.min(forEachItems.length, effectiveMaxParallelism)
7432
- );
7433
- if (debug && forEachConcurrency > 1) {
7434
- log2(
7435
- `\u{1F504} Debug: Limiting forEach concurrency for check "${checkName}" to ${forEachConcurrency}`
7436
- );
7437
- }
7438
- const forEachResults = await this.executeWithLimitedParallelism(
7439
- itemTasks,
7440
- forEachConcurrency,
7441
- false
7442
- );
7443
- for (const result of forEachResults) {
7444
- if (result.status === "rejected") {
7445
- const error = result.reason;
7446
- const errorMessage = error instanceof Error ? error.message : String(error);
7447
- allIssues.push({
7448
- ruleId: `${checkName}/forEach/iteration_error`,
7449
- severity: "error",
7450
- category: "logic",
7451
- message: `forEach iteration failed: ${errorMessage}`,
7452
- file: "",
7453
- line: 0
7454
- });
7455
- if (debug) {
7456
- log2(
7457
- `\u{1F504} Debug: forEach iteration for check "${checkName}" failed: ${errorMessage}`
7458
- );
7504
+ const { itemResult } = result.value;
7505
+ if (itemResult.issues) {
7506
+ allIssues.push(...itemResult.issues);
7507
+ }
7508
+ const resultWithOutput = itemResult;
7509
+ if (resultWithOutput.output !== void 0) {
7510
+ allOutputs.push(resultWithOutput.output);
7511
+ }
7512
+ const itemContent = resultWithOutput.content;
7513
+ if (typeof itemContent === "string" && itemContent.trim()) {
7514
+ aggregatedContents.push(itemContent.trim());
7459
7515
  }
7460
- continue;
7461
- }
7462
- if (result.value.skipped) {
7463
- continue;
7464
- }
7465
- const { itemResult } = result.value;
7466
- if (itemResult.issues) {
7467
- allIssues.push(...itemResult.issues);
7468
7516
  }
7469
- const resultWithOutput = itemResult;
7470
- if (resultWithOutput.output !== void 0) {
7471
- allOutputs.push(resultWithOutput.output);
7517
+ const finalOutput = allOutputs.length > 0 ? allOutputs : void 0;
7518
+ finalResult = {
7519
+ issues: allIssues,
7520
+ ...finalOutput !== void 0 ? { output: finalOutput } : {}
7521
+ };
7522
+ if (allOutputs.length > 0) {
7523
+ finalResult.isForEach = true;
7524
+ finalResult.forEachItems = allOutputs;
7472
7525
  }
7473
- const itemContent = resultWithOutput.content;
7474
- if (typeof itemContent === "string" && itemContent.trim()) {
7475
- aggregatedContents.push(itemContent.trim());
7526
+ if (aggregatedContents.length > 0) {
7527
+ finalResult.content = aggregatedContents.join("\n");
7476
7528
  }
7529
+ log2(
7530
+ `\u{1F504} Debug: Completed forEach execution for check "${checkName}", total issues: ${allIssues.length}`
7531
+ );
7477
7532
  }
7478
- const finalOutput = allOutputs.length > 0 ? allOutputs : void 0;
7479
- finalResult = {
7480
- issues: allIssues,
7481
- ...finalOutput !== void 0 ? { output: finalOutput } : {}
7482
- };
7483
- if (allOutputs.length > 0) {
7484
- finalResult.isForEach = true;
7485
- finalResult.forEachItems = allOutputs;
7486
- }
7487
- if (aggregatedContents.length > 0) {
7488
- finalResult.content = aggregatedContents.join("\n");
7489
- }
7490
- log2(
7491
- `\u{1F504} Debug: Completed forEach execution for check "${checkName}", total issues: ${allIssues.length}`
7492
- );
7493
7533
  } else {
7494
7534
  if (checkConfig.if) {
7495
7535
  const shouldRun = await this.evaluateCheckCondition(
@@ -9353,6 +9393,10 @@ var init_config_schema = __esm({
9353
9393
  $ref: "#/definitions/EnvConfig",
9354
9394
  description: "Environment variables for this check"
9355
9395
  },
9396
+ timeout: {
9397
+ type: "number",
9398
+ description: "Timeout in seconds for command execution (default: 60)"
9399
+ },
9356
9400
  depends_on: {
9357
9401
  type: "array",
9358
9402
  items: {