agent-gauntlet 0.15.4 → 1.0.0

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/dist/index.js CHANGED
@@ -299,7 +299,7 @@ import { Command } from "commander";
299
299
  // package.json
300
300
  var package_default = {
301
301
  name: "agent-gauntlet",
302
- version: "0.15.4",
302
+ version: "1.0.0",
303
303
  description: "A CLI tool for testing AI coding agents",
304
304
  license: "MIT",
305
305
  author: "Paul Caplan",
@@ -349,8 +349,7 @@ var package_default = {
349
349
  detect: "bun src/index.ts detect",
350
350
  list: "bun src/index.ts list",
351
351
  health: "bun src/index.ts health",
352
- validate: "bun src/index.ts validate",
353
- "wait-ci": "bun src/index.ts wait-ci"
352
+ validate: "bun src/index.ts validate"
354
353
  },
355
354
  devDependencies: {
356
355
  "@biomejs/biome": "^2.4.3",
@@ -391,23 +390,17 @@ var debugLogConfigSchema = z.object({
391
390
  var globalConfigSchema = z.object({
392
391
  stop_hook: z.object({
393
392
  enabled: z.boolean().default(false),
394
- run_interval_minutes: z.number().default(5),
395
- auto_push_pr: z.boolean().default(false),
396
- auto_fix_pr: z.boolean().default(false)
393
+ run_interval_minutes: z.number().default(5)
397
394
  }).default({
398
395
  enabled: false,
399
- run_interval_minutes: 5,
400
- auto_push_pr: false,
401
- auto_fix_pr: false
396
+ run_interval_minutes: 5
402
397
  }),
403
398
  debug_log: debugLogConfigSchema.default({ enabled: false, max_size_mb: 10 })
404
399
  });
405
400
  var DEFAULT_GLOBAL_CONFIG = {
406
401
  stop_hook: {
407
402
  enabled: false,
408
- run_interval_minutes: 5,
409
- auto_push_pr: false,
410
- auto_fix_pr: false
403
+ run_interval_minutes: 5
411
404
  },
412
405
  debug_log: {
413
406
  enabled: false,
@@ -625,9 +618,7 @@ var loggingConfigSchema = z2.object({
625
618
  });
626
619
  var stopHookConfigSchema = z2.object({
627
620
  enabled: z2.boolean().optional(),
628
- run_interval_minutes: z2.number().int().min(0).optional(),
629
- auto_push_pr: z2.boolean().optional(),
630
- auto_fix_pr: z2.boolean().optional()
621
+ run_interval_minutes: z2.number().int().min(0).optional()
631
622
  });
632
623
  var gauntletConfigSchema = z2.object({
633
624
  base_branch: z2.string().min(1).default("origin/main"),
@@ -1371,7 +1362,7 @@ import { exec as exec3 } from "node:child_process";
1371
1362
  import fs12 from "node:fs/promises";
1372
1363
  import os2 from "node:os";
1373
1364
  import path11 from "node:path";
1374
- import { promisify as promisify4 } from "node:util";
1365
+ import { promisify as promisify3 } from "node:util";
1375
1366
 
1376
1367
  // src/commands/stop-hook.ts
1377
1368
  import fsSync from "node:fs";
@@ -1469,17 +1460,13 @@ class CursorStopHookAdapter {
1469
1460
  }
1470
1461
 
1471
1462
  // src/hooks/stop-hook-handler.ts
1472
- import { execFile } from "node:child_process";
1473
1463
  import fs10 from "node:fs/promises";
1474
1464
  import path9 from "node:path";
1475
- import { promisify as promisify3 } from "node:util";
1476
1465
  import YAML3 from "yaml";
1477
1466
 
1478
1467
  // src/config/stop-hook-config.ts
1479
1468
  var GAUNTLET_STOP_HOOK_ENABLED = "GAUNTLET_STOP_HOOK_ENABLED";
1480
1469
  var GAUNTLET_STOP_HOOK_INTERVAL_MINUTES = "GAUNTLET_STOP_HOOK_INTERVAL_MINUTES";
1481
- var GAUNTLET_AUTO_PUSH_PR = "GAUNTLET_AUTO_PUSH_PR";
1482
- var GAUNTLET_AUTO_FIX_PR = "GAUNTLET_AUTO_FIX_PR";
1483
1470
  function parseBooleanEnv(envVar) {
1484
1471
  if (envVar === undefined)
1485
1472
  return;
@@ -1503,9 +1490,7 @@ function parseIntegerEnv(envVar) {
1503
1490
  function parseStopHookEnvVars() {
1504
1491
  return {
1505
1492
  enabled: parseBooleanEnv(process.env[GAUNTLET_STOP_HOOK_ENABLED]),
1506
- run_interval_minutes: parseIntegerEnv(process.env[GAUNTLET_STOP_HOOK_INTERVAL_MINUTES]),
1507
- auto_push_pr: parseBooleanEnv(process.env[GAUNTLET_AUTO_PUSH_PR]),
1508
- auto_fix_pr: parseBooleanEnv(process.env[GAUNTLET_AUTO_FIX_PR])
1493
+ run_interval_minutes: parseIntegerEnv(process.env[GAUNTLET_STOP_HOOK_INTERVAL_MINUTES])
1509
1494
  };
1510
1495
  }
1511
1496
  function resolveField(envValue, projectValue, globalValue) {
@@ -1520,13 +1505,7 @@ function resolveStopHookConfig(projectConfig, globalConfig) {
1520
1505
  const globalStop = globalConfig.stop_hook;
1521
1506
  const enabled = resolveField(envVars.enabled, projectConfig?.enabled, globalStop.enabled);
1522
1507
  const run_interval_minutes = resolveField(envVars.run_interval_minutes, projectConfig?.run_interval_minutes, globalStop.run_interval_minutes);
1523
- const auto_push_pr = resolveField(envVars.auto_push_pr, projectConfig?.auto_push_pr, globalStop.auto_push_pr);
1524
- let auto_fix_pr = resolveField(envVars.auto_fix_pr, projectConfig?.auto_fix_pr, globalStop.auto_fix_pr);
1525
- if (auto_fix_pr && !auto_push_pr) {
1526
- console.error("[gauntlet] Warning: auto_fix_pr=true requires auto_push_pr=true. Treating auto_fix_pr as false.");
1527
- auto_fix_pr = false;
1528
- }
1529
- return { enabled, run_interval_minutes, auto_push_pr, auto_fix_pr };
1508
+ return { enabled, run_interval_minutes };
1530
1509
  }
1531
1510
 
1532
1511
  // src/output/app-logger.ts
@@ -2214,12 +2193,6 @@ async function hasChangesVsBaseBranch(cwd, baseBranch) {
2214
2193
  return true;
2215
2194
  }
2216
2195
  }
2217
- async function getLastRunStatus(logDir) {
2218
- const state = await readExecutionState(logDir);
2219
- if (!state)
2220
- return null;
2221
- return null;
2222
- }
2223
2196
  async function readBlockTimestamps(logDir) {
2224
2197
  try {
2225
2198
  const filePath = path8.join(logDir, BLOCK_TIMESTAMPS_FILE);
@@ -2254,14 +2227,9 @@ async function resetBlockTimestamps(logDir) {
2254
2227
  }
2255
2228
 
2256
2229
  // src/hooks/stop-hook-handler.ts
2257
- var execFileAsync = promisify3(execFile);
2258
2230
  var DEFAULT_LOG_DIR = "gauntlet_logs";
2259
2231
  var SKILL_INSTRUCTIONS = {
2260
- validation_required: "Changes detected, you must use the `gauntlet-run` skill to validate them now.",
2261
- pr_push_required: "Gauntlet passed. You must use the `gauntlet-push-pr` skill to create or update your pull request.",
2262
- pr_push_required_with_warnings: "Gauntlet passed with warnings (some issues were skipped). You must use the `gauntlet-push-pr` skill to create or update your pull request. Include a summary of skipped issues in the PR description.",
2263
- ci_pending: "PR is up to date but CI checks are still running. You must use the `gauntlet-fix-pr` skill to poll CI status until checks complete, then fix any failures.",
2264
- ci_failed: "PR is up to date but CI checks have failed. You must use the `gauntlet-fix-pr` skill to check which CI checks failed, fix the issues, and push."
2232
+ validation_required: "Changes detected, you must use the `gauntlet-run` skill to validate them now."
2265
2233
  };
2266
2234
  async function readProjectConfig(projectCwd) {
2267
2235
  try {
@@ -2283,10 +2251,6 @@ var STATUS_MESSAGES = {
2283
2251
  retry_limit_exceeded: "⚠ Gauntlet terminated — retry limit exceeded. Run `agent-gauntlet clean` to archive and continue.",
2284
2252
  lock_conflict: "⏭ Gauntlet skipped — another gauntlet run is already in progress.",
2285
2253
  failed: "✗ Gauntlet failed — issues must be fixed before stopping.",
2286
- pr_push_required: "✓ Gauntlet passed — PR needs to be created or updated before stopping.",
2287
- ci_pending: "⏳ CI checks still running — waiting for completion.",
2288
- ci_failed: "✗ CI failed or review changes requested — fix issues and push.",
2289
- ci_passed: "✓ CI passed — all checks completed and no blocking reviews.",
2290
2254
  validation_required: "✗ Validation required — changes detected that need validation before stopping.",
2291
2255
  no_config: "○ Not a gauntlet project — no .gauntlet/config.yml found.",
2292
2256
  stop_hook_active: "↺ Stop hook cycle detected — allowing stop to prevent infinite loop.",
@@ -2323,71 +2287,6 @@ async function getResolvedStopHookConfig(projectCwd) {
2323
2287
  return null;
2324
2288
  }
2325
2289
  }
2326
- async function checkPRStatus(cwd) {
2327
- try {
2328
- try {
2329
- await execFileAsync("gh", ["--version"], { cwd });
2330
- } catch {
2331
- return {
2332
- prExists: false,
2333
- upToDate: false,
2334
- error: "gh CLI not installed"
2335
- };
2336
- }
2337
- let prInfo;
2338
- try {
2339
- const { stdout } = await execFileAsync("gh", ["pr", "view", "--json", "number,state,headRefOid"], { cwd });
2340
- prInfo = JSON.parse(stdout.trim());
2341
- } catch (e) {
2342
- const errMsg = e.message ?? "unknown";
2343
- if (errMsg.includes("no pull requests found") || errMsg.includes("Could not resolve")) {
2344
- return { prExists: false, upToDate: false };
2345
- }
2346
- return {
2347
- prExists: false,
2348
- upToDate: false,
2349
- error: `gh pr view failed: ${errMsg}`
2350
- };
2351
- }
2352
- if (prInfo.state !== "OPEN") {
2353
- return { prExists: false, upToDate: false };
2354
- }
2355
- const { stdout: localHead } = await execFileAsync("git", ["rev-parse", "HEAD"], { cwd });
2356
- const localSha = localHead.trim();
2357
- const upToDate = prInfo.headRefOid === localSha;
2358
- return {
2359
- prExists: true,
2360
- upToDate,
2361
- prNumber: prInfo.number
2362
- };
2363
- } catch (error) {
2364
- const errMsg = error.message ?? "unknown";
2365
- return {
2366
- prExists: false,
2367
- upToDate: false,
2368
- error: `PR status check failed: ${errMsg}`
2369
- };
2370
- }
2371
- }
2372
- async function checkCIStatus(cwd) {
2373
- try {
2374
- const { stdout } = await execFileAsync("gh", ["pr", "checks", "--json", "name,state"], { cwd });
2375
- const checks = JSON.parse(stdout.trim());
2376
- if (checks.length === 0) {
2377
- return { status: "passed" };
2378
- }
2379
- const hasFailed = checks.some((c) => c.state === "FAILURE" || c.state === "ERROR");
2380
- if (hasFailed)
2381
- return { status: "failed" };
2382
- const hasPending = checks.some((c) => c.state === "PENDING" || c.state === "EXPECTED");
2383
- if (hasPending)
2384
- return { status: "pending" };
2385
- return { status: "passed" };
2386
- } catch (e) {
2387
- const errMsg = e.message ?? "unknown";
2388
- return { status: "error", error: errMsg };
2389
- }
2390
- }
2391
2290
 
2392
2291
  class StopHookHandler {
2393
2292
  debugLogger;
@@ -2425,9 +2324,6 @@ class StopHookHandler {
2425
2324
  const changesResult = await this.checkForChanges(hctx);
2426
2325
  if (changesResult)
2427
2326
  return changesResult;
2428
- const prCiResult = await this.checkPRAndCI(hctx, config);
2429
- if (prCiResult)
2430
- return prCiResult;
2431
2327
  hctx.log.info("All checks passed — allowing stop");
2432
2328
  return this.allow("passed");
2433
2329
  }
@@ -2463,44 +2359,6 @@ class StopHookHandler {
2463
2359
  }
2464
2360
  return null;
2465
2361
  }
2466
- async checkPRAndCI(hctx, config) {
2467
- if (!config?.auto_push_pr)
2468
- return null;
2469
- const prStatus = await checkPRStatus(hctx.cwd);
2470
- if (prStatus.error) {
2471
- hctx.log.warn(`PR status check failed: ${prStatus.error} — allowing stop`);
2472
- return null;
2473
- }
2474
- if (!(prStatus.prExists && prStatus.upToDate)) {
2475
- hctx.log.info("PR missing or outdated — blocking with pr_push_required");
2476
- return this.blockForPR(hctx);
2477
- }
2478
- if (!config?.auto_fix_pr)
2479
- return null;
2480
- return this.checkCI(hctx);
2481
- }
2482
- async blockForPR(hctx) {
2483
- const lastStatus = await getLastRunStatus(hctx.logDir);
2484
- const instruction = lastStatus === "passed_with_warnings" ? SKILL_INSTRUCTIONS.pr_push_required_with_warnings : SKILL_INSTRUCTIONS.pr_push_required;
2485
- return this.block("pr_push_required", instruction);
2486
- }
2487
- async checkCI(hctx) {
2488
- const ciResult = await checkCIStatus(hctx.cwd);
2489
- if (ciResult.status === "error") {
2490
- hctx.log.warn(`CI status check failed: ${ciResult.error} — allowing stop`);
2491
- return null;
2492
- }
2493
- if (ciResult.status === "pending") {
2494
- hctx.log.info("CI pending — blocking");
2495
- return this.block("ci_pending", SKILL_INSTRUCTIONS.ci_pending);
2496
- }
2497
- if (ciResult.status === "failed") {
2498
- hctx.log.info("CI failed — blocking");
2499
- return this.block("ci_failed", SKILL_INSTRUCTIONS.ci_failed);
2500
- }
2501
- hctx.log.info("CI passed — allowing stop");
2502
- return this.allow("ci_passed");
2503
- }
2504
2362
  async block(status, reason) {
2505
2363
  await this.debugLogger?.logStopHook("block", status);
2506
2364
  return {
@@ -2523,7 +2381,7 @@ class StopHookHandler {
2523
2381
 
2524
2382
  // src/types/gauntlet-status.ts
2525
2383
  function isSuccessStatus(status) {
2526
- return status === "passed" || status === "passed_with_warnings" || status === "no_applicable_gates" || status === "no_changes" || status === "ci_passed";
2384
+ return status === "passed" || status === "passed_with_warnings" || status === "no_applicable_gates" || status === "no_changes";
2527
2385
  }
2528
2386
 
2529
2387
  // src/commands/stop-hook.ts
@@ -2839,7 +2697,7 @@ var GEMINI_THINKING_BUDGET = {
2839
2697
  };
2840
2698
 
2841
2699
  // src/cli-adapters/claude.ts
2842
- var execAsync3 = promisify4(exec3);
2700
+ var execAsync3 = promisify3(exec3);
2843
2701
  function countBraceChange(line) {
2844
2702
  const stripped = line.replace(/\\./g, "").replace(/"[^"]*"/g, "").replace(/'[^']*'/g, "");
2845
2703
  let depth = 0;
@@ -3154,8 +3012,8 @@ import { exec as exec4 } from "node:child_process";
3154
3012
  import fs13 from "node:fs/promises";
3155
3013
  import os3 from "node:os";
3156
3014
  import path12 from "node:path";
3157
- import { promisify as promisify5 } from "node:util";
3158
- var execAsync4 = promisify5(exec4);
3015
+ import { promisify as promisify4 } from "node:util";
3016
+ var execAsync4 = promisify4(exec4);
3159
3017
  function parseJsonlLine(line) {
3160
3018
  try {
3161
3019
  const obj = JSON.parse(line);
@@ -3356,7 +3214,7 @@ import { exec as exec5 } from "node:child_process";
3356
3214
  import fs14 from "node:fs/promises";
3357
3215
  import os4 from "node:os";
3358
3216
  import path13 from "node:path";
3359
- import { promisify as promisify6 } from "node:util";
3217
+ import { promisify as promisify5 } from "node:util";
3360
3218
 
3361
3219
  // src/cli-adapters/model-resolution.ts
3362
3220
  var TIER_SUFFIXES = ["-low", "-high", "-xhigh", "-fast"];
@@ -3395,7 +3253,7 @@ function resolveModelFromList(allModels, opts) {
3395
3253
  }
3396
3254
 
3397
3255
  // src/cli-adapters/cursor.ts
3398
- var execAsync5 = promisify6(exec5);
3256
+ var execAsync5 = promisify5(exec5);
3399
3257
  var log = getCategoryLogger("cursor");
3400
3258
  function parseModelList(output) {
3401
3259
  return output.split(`
@@ -3526,8 +3384,8 @@ import { exec as exec6 } from "node:child_process";
3526
3384
  import fs15 from "node:fs/promises";
3527
3385
  import os5 from "node:os";
3528
3386
  import path14 from "node:path";
3529
- import { promisify as promisify7 } from "node:util";
3530
- var execAsync6 = promisify7(exec6);
3387
+ import { promisify as promisify6 } from "node:util";
3388
+ var execAsync6 = promisify6(exec6);
3531
3389
  var TOKEN_TYPE_MAP = {
3532
3390
  input: "inputTokens",
3533
3391
  output: "outputTokens",
@@ -3887,8 +3745,8 @@ import { exec as exec7 } from "node:child_process";
3887
3745
  import fs16 from "node:fs/promises";
3888
3746
  import os6 from "node:os";
3889
3747
  import path15 from "node:path";
3890
- import { promisify as promisify8 } from "node:util";
3891
- var execAsync7 = promisify8(exec7);
3748
+ import { promisify as promisify7 } from "node:util";
3749
+ var execAsync7 = promisify7(exec7);
3892
3750
  var log2 = getCategoryLogger("github-copilot");
3893
3751
  function parseCopilotModels(helpOutput) {
3894
3752
  const match = helpOutput.match(/choices:\s*(.+?)\)/);
@@ -4210,9 +4068,9 @@ async function handleCriticalError(error, jobId, startTime, logPaths, mainLogger
4210
4068
 
4211
4069
  // src/gates/review-diff.ts
4212
4070
  import { exec as exec8 } from "node:child_process";
4213
- import { promisify as promisify9 } from "node:util";
4071
+ import { promisify as promisify8 } from "node:util";
4214
4072
  var log4 = getCategoryLogger("gate", "review");
4215
- var execAsync8 = promisify9(exec8);
4073
+ var execAsync8 = promisify8(exec8);
4216
4074
  function parseLines(stdout) {
4217
4075
  return stdout.split(`
4218
4076
  `).map((line) => line.trim()).filter((line) => line.length > 0);
@@ -6156,7 +6014,6 @@ ${chalk2.bold(SEPARATOR)}`);
6156
6014
  }
6157
6015
  const { overallStatus, statusColor } = computeOverallStatus(results, statusOverride);
6158
6016
  console.error(statusColor(`Status: ${overallStatus}`));
6159
- console.log(`Status: ${overallStatus}`);
6160
6017
  console.error(chalk2.bold(`${SEPARATOR}
6161
6018
  `));
6162
6019
  }
@@ -8170,20 +8027,10 @@ var SKILLS_SOURCE_DIR = (() => {
8170
8027
  throw err;
8171
8028
  }
8172
8029
  })();
8173
- var SKILL_ACTIONS = [
8174
- "run",
8175
- "check",
8176
- "push-pr",
8177
- "fix-pr",
8178
- "status",
8179
- "help",
8180
- "setup"
8181
- ];
8030
+ var SKILL_ACTIONS = ["run", "check", "status", "help", "setup"];
8182
8031
  var SKILL_DESCRIPTIONS = {
8183
8032
  run: "Run the verification suite",
8184
8033
  check: "Run checks only (no reviews)",
8185
- "push-pr": "Commit, push, and create a PR",
8186
- "fix-pr": "Fix PR review comments and CI failures",
8187
8034
  status: "Show gauntlet status",
8188
8035
  help: "Diagnose and explain gauntlet behavior",
8189
8036
  setup: "Configure checks and reviews interactively"
@@ -8366,13 +8213,10 @@ entry_points: []
8366
8213
 
8367
8214
  # Stop hook — auto-run gauntlet when the agent stops
8368
8215
  # Precedence: env vars > project config > global config (~/.config/agent-gauntlet/config.yml)
8369
- # Env overrides: GAUNTLET_STOP_HOOK_ENABLED, GAUNTLET_STOP_HOOK_INTERVAL_MINUTES,
8370
- # GAUNTLET_AUTO_PUSH_PR, GAUNTLET_AUTO_FIX_PR
8216
+ # Env overrides: GAUNTLET_STOP_HOOK_ENABLED, GAUNTLET_STOP_HOOK_INTERVAL_MINUTES
8371
8217
  # stop_hook:
8372
8218
  # enabled: false
8373
8219
  # run_interval_minutes: 5 # Minimum minutes between runs (0 = always run)
8374
- # auto_push_pr: false # Check/create PR after gates pass
8375
- # auto_fix_pr: false # Wait for CI checks after PR (requires auto_push_pr)
8376
8220
 
8377
8221
  # Debug log — persistent debug logging to .debug.log
8378
8222
  # debug_log:
@@ -8502,12 +8346,12 @@ function registerReviewCommand(program) {
8502
8346
  program.command("review").description("Run only applicable reviews for detected changes").option("-b, --base-branch <branch>", "Override base branch for change detection").option("-g, --gate <name>", "Run specific review gate only").option("-c, --commit <sha>", "Use diff for a specific commit").option("-u, --uncommitted", "Use diff for current uncommitted changes (staged and unstaged)").action((options) => executeGateCommand("review", options));
8503
8347
  }
8504
8348
  // src/core/diff-stats.ts
8505
- import { execFile as execFile2 } from "node:child_process";
8506
- import { promisify as promisify10 } from "node:util";
8507
- var execFileAsyncOriginal = promisify10(execFile2);
8508
- var execFileAsync2 = execFileAsyncOriginal;
8349
+ import { execFile } from "node:child_process";
8350
+ import { promisify as promisify9 } from "node:util";
8351
+ var execFileAsyncOriginal = promisify9(execFile);
8352
+ var execFileAsync = execFileAsyncOriginal;
8509
8353
  async function gitExec(args) {
8510
- const { stdout } = await execFileAsync2("git", args);
8354
+ const { stdout } = await execFileAsync("git", args);
8511
8355
  return stdout;
8512
8356
  }
8513
8357
  async function computeDiffStats(baseBranch, options = {}) {
@@ -8891,10 +8735,6 @@ var statusMessages = {
8891
8735
  interval_not_elapsed: "Run interval not elapsed.",
8892
8736
  invalid_input: "Invalid input.",
8893
8737
  stop_hook_disabled: "",
8894
- pr_push_required: "Gates passed -- PR needs to be created/updated.",
8895
- ci_pending: "CI checks still running.",
8896
- ci_failed: "CI checks failed or review changes requested.",
8897
- ci_passed: "CI checks passed, no blocking reviews.",
8898
8738
  validation_required: "Changes need validation or previous run has unresolved failures."
8899
8739
  };
8900
8740
  function getStatusMessage2(status) {
@@ -9238,7 +9078,39 @@ function registerRunCommand(program) {
9238
9078
  commit: options.commit,
9239
9079
  uncommitted: options.uncommitted
9240
9080
  });
9241
- process.exit(isSuccessStatus(result.status) ? 0 : 1);
9081
+ const code = isSuccessStatus(result.status) ? 0 : 1;
9082
+ process.exit(code);
9083
+ });
9084
+ }
9085
+ // src/commands/skip.ts
9086
+ import chalk13 from "chalk";
9087
+ function registerSkipCommand(program) {
9088
+ program.command("skip").description("Advance execution state baseline without running gates").action(async () => {
9089
+ let config;
9090
+ let lockAcquired = false;
9091
+ try {
9092
+ config = await loadConfig();
9093
+ const globalConfig = await loadGlobalConfig();
9094
+ const debugLogConfig = mergeDebugLogConfig(config.project.debug_log, globalConfig.debug_log);
9095
+ initDebugLogger(config.project.log_dir, debugLogConfig);
9096
+ await acquireLock(config.project.log_dir);
9097
+ lockAcquired = true;
9098
+ const debugLogger = getDebugLogger();
9099
+ await debugLogger?.logCommand("skip", []);
9100
+ await cleanLogs(config.project.log_dir, config.project.max_previous_logs);
9101
+ await writeExecutionState(config.project.log_dir);
9102
+ const commit = await getCurrentCommit();
9103
+ const shortSha = commit.slice(0, 7);
9104
+ await releaseLock(config.project.log_dir);
9105
+ console.log(chalk13.green(`Baseline advanced to ${shortSha}. Next run will diff from here.`));
9106
+ } catch (error) {
9107
+ if (config && lockAcquired) {
9108
+ await releaseLock(config.project.log_dir);
9109
+ }
9110
+ const err = error;
9111
+ console.error(chalk13.red("Error:"), err.message);
9112
+ process.exit(1);
9113
+ }
9242
9114
  });
9243
9115
  }
9244
9116
  // src/commands/start-hook.ts
@@ -9321,285 +9193,20 @@ function registerStatusCommand(program) {
9321
9193
  });
9322
9194
  }
9323
9195
  // src/commands/validate.ts
9324
- import chalk13 from "chalk";
9196
+ import chalk14 from "chalk";
9325
9197
  function registerValidateCommand(program) {
9326
9198
  program.command("validate").description("Validate .gauntlet/ config files against schemas").action(async () => {
9327
9199
  try {
9328
9200
  await loadConfig();
9329
- console.log(chalk13.green("All config files are valid."));
9201
+ console.log(chalk14.green("All config files are valid."));
9330
9202
  process.exitCode = 0;
9331
9203
  } catch (error) {
9332
9204
  const message = error instanceof Error ? error.message : String(error);
9333
- console.error(chalk13.red("Validation failed:"), message);
9205
+ console.error(chalk14.red("Validation failed:"), message);
9334
9206
  process.exitCode = 1;
9335
9207
  }
9336
9208
  });
9337
9209
  }
9338
- // src/commands/wait-ci.ts
9339
- import { spawn as spawn3 } from "node:child_process";
9340
- async function isGhAvailable() {
9341
- return new Promise((resolve) => {
9342
- const proc = spawn3("gh", ["--version"], { stdio: "pipe" });
9343
- proc.on("close", (code) => resolve(code === 0));
9344
- proc.on("error", () => resolve(false));
9345
- });
9346
- }
9347
- async function runGh(args, cwd) {
9348
- return new Promise((resolve) => {
9349
- const proc = spawn3("gh", args, { stdio: "pipe", cwd });
9350
- let stdout = "";
9351
- let stderr = "";
9352
- proc.stdout.on("data", (data) => {
9353
- stdout += data.toString();
9354
- });
9355
- proc.stderr.on("data", (data) => {
9356
- stderr += data.toString();
9357
- });
9358
- proc.on("close", (code) => {
9359
- resolve({ code: code ?? 1, stdout, stderr });
9360
- });
9361
- proc.on("error", (err) => {
9362
- resolve({ code: 1, stdout: "", stderr: err.message });
9363
- });
9364
- });
9365
- }
9366
- async function getPRInfo(cwd) {
9367
- const result = await runGh(["pr", "view", "--json", "number,url,headRefName"], cwd);
9368
- if (result.code !== 0) {
9369
- return null;
9370
- }
9371
- try {
9372
- return JSON.parse(result.stdout.trim());
9373
- } catch {
9374
- return null;
9375
- }
9376
- }
9377
- async function getChecks(cwd) {
9378
- const result = await runGh(["pr", "checks", "--json", "name,state,link"], cwd);
9379
- const output = result.stdout.trim();
9380
- if (!output) {
9381
- return result.code === 0 ? [] : null;
9382
- }
9383
- try {
9384
- return JSON.parse(output) || [];
9385
- } catch {
9386
- return result.code === 0 ? [] : null;
9387
- }
9388
- }
9389
- async function getReviews(prNumber, cwd) {
9390
- const repoResult = await runGh(["repo", "view", "--json", "owner,name"], cwd);
9391
- if (repoResult.code !== 0) {
9392
- return null;
9393
- }
9394
- let owner;
9395
- let repo;
9396
- try {
9397
- const repoInfo = JSON.parse(repoResult.stdout.trim());
9398
- owner = repoInfo.owner.login;
9399
- repo = repoInfo.name;
9400
- } catch {
9401
- return null;
9402
- }
9403
- const result = await runGh([
9404
- "api",
9405
- "--paginate",
9406
- `repos/${owner}/${repo}/pulls/${prNumber}/reviews?per_page=100`
9407
- ], cwd);
9408
- if (result.code !== 0) {
9409
- return null;
9410
- }
9411
- try {
9412
- const rawReviews = JSON.parse(result.stdout.trim()) || [];
9413
- return rawReviews.filter((r) => r.user?.login).map((r) => ({
9414
- author: { login: r.user.login },
9415
- state: r.state,
9416
- body: r.body || ""
9417
- }));
9418
- } catch {
9419
- return null;
9420
- }
9421
- }
9422
- function getLatestReviewsByAuthor(reviews) {
9423
- const latestByAuthor = new Map;
9424
- for (const review of reviews) {
9425
- latestByAuthor.set(review.author.login, review);
9426
- }
9427
- return Array.from(latestByAuthor.values());
9428
- }
9429
- function sleep(ms) {
9430
- return new Promise((resolve) => setTimeout(resolve, ms));
9431
- }
9432
- function extractRunId(link) {
9433
- const match = link.match(/\/actions\/runs\/(\d+)/);
9434
- return match?.[1] ?? null;
9435
- }
9436
- async function getFailedRunLogs(runId, cwd) {
9437
- const result = await runGh(["run", "view", runId, "--log-failed"], cwd);
9438
- if (result.code !== 0 || !result.stdout.trim()) {
9439
- return null;
9440
- }
9441
- const lines = result.stdout.trim().split(`
9442
- `);
9443
- const maxLines = 100;
9444
- if (lines.length > maxLines) {
9445
- return `... (${lines.length - maxLines} lines truncated)
9446
- ${lines.slice(-maxLines).join(`
9447
- `)}`;
9448
- }
9449
- return result.stdout.trim();
9450
- }
9451
- function groupChecksByRunId(failedChecks) {
9452
- const runIdToChecks = new Map;
9453
- for (const check of failedChecks) {
9454
- const runId = extractRunId(check.link);
9455
- const key = runId ?? "";
9456
- const existing = runIdToChecks.get(key) ?? [];
9457
- existing.push(check);
9458
- runIdToChecks.set(key, existing);
9459
- }
9460
- return runIdToChecks;
9461
- }
9462
- async function enrichFailedChecksWithLogs(failedChecks, cwd) {
9463
- const runIdToChecks = groupChecksByRunId(failedChecks);
9464
- const entries = Array.from(runIdToChecks.entries());
9465
- const logResults = await Promise.all(entries.map(([runId]) => runId ? getFailedRunLogs(runId, cwd) : Promise.resolve(null)));
9466
- const results = [];
9467
- for (let i = 0;i < entries.length; i++) {
9468
- const [, checks] = entries[i];
9469
- const logs = logResults[i];
9470
- for (const check of checks) {
9471
- results.push({ ...check, log_output: logs ?? undefined });
9472
- }
9473
- }
9474
- return results;
9475
- }
9476
- function createResult(opts) {
9477
- const elapsed = Math.round((Date.now() - opts.startTime) / 1000);
9478
- return {
9479
- ci_status: opts.status,
9480
- pr_number: opts.prInfo?.number,
9481
- pr_url: opts.prInfo?.url,
9482
- failed_checks: opts.failedChecks?.map((c) => ({
9483
- name: c.name,
9484
- conclusion: c.state.toLowerCase(),
9485
- details_url: c.link,
9486
- log_output: c.log_output
9487
- })) || [],
9488
- review_comments: opts.reviewComments || [],
9489
- elapsed_seconds: elapsed,
9490
- error_message: opts.errorMessage
9491
- };
9492
- }
9493
- async function pollCIStatus(cwd, prNumber, isFirstPoll) {
9494
- const checks = await getChecks(cwd);
9495
- const reviews = await getReviews(prNumber, cwd);
9496
- if (checks === null || reviews === null) {
9497
- return { error: "Failed to fetch CI status or reviews from GitHub" };
9498
- }
9499
- if (checks.length === 0) {
9500
- return isFirstPoll ? { noChecksYet: true } : { noChecksConfigured: true };
9501
- }
9502
- const latestReviews = getLatestReviewsByAuthor(reviews);
9503
- const failedChecks = checks.filter((c) => c.state === "FAILURE");
9504
- const blockingReviews = latestReviews.filter((r) => r.state === "CHANGES_REQUESTED");
9505
- const reviewComments = blockingReviews.map((r) => ({
9506
- author: r.author.login,
9507
- body: r.body || ""
9508
- }));
9509
- const pendingChecks = checks.filter((c) => c.state === "PENDING" || c.state === "QUEUED" || c.state === "IN_PROGRESS");
9510
- const shouldFail = failedChecks.length > 0 || blockingReviews.length > 0;
9511
- const shouldPass = pendingChecks.length === 0;
9512
- return { shouldFail, shouldPass, failedChecks, reviewComments };
9513
- }
9514
- async function waitForCI(timeoutSeconds, pollIntervalSeconds, cwd) {
9515
- const startTime = Date.now();
9516
- let isFirstPoll = true;
9517
- if (!await isGhAvailable()) {
9518
- return createResult({
9519
- status: "error",
9520
- startTime,
9521
- errorMessage: "gh CLI is not installed or not authenticated"
9522
- });
9523
- }
9524
- const prInfo = await getPRInfo(cwd);
9525
- if (!prInfo) {
9526
- return createResult({
9527
- status: "error",
9528
- startTime,
9529
- errorMessage: "No PR found for current branch"
9530
- });
9531
- }
9532
- const timeoutMs = timeoutSeconds * 1000;
9533
- while (Date.now() - startTime < timeoutMs) {
9534
- const pollOutcome = await pollCIStatus(cwd, prInfo.number, isFirstPoll);
9535
- isFirstPoll = false;
9536
- if (pollOutcome.error) {
9537
- return createResult({
9538
- status: "error",
9539
- startTime,
9540
- prInfo,
9541
- errorMessage: pollOutcome.error
9542
- });
9543
- }
9544
- if (pollOutcome.noChecksYet) {
9545
- await sleep(pollIntervalSeconds * 1000);
9546
- continue;
9547
- }
9548
- if (pollOutcome.noChecksConfigured) {
9549
- return createResult({ status: "passed", startTime, prInfo });
9550
- }
9551
- if (pollOutcome.shouldFail && pollOutcome.failedChecks) {
9552
- const enrichedChecks = await enrichFailedChecksWithLogs(pollOutcome.failedChecks, cwd);
9553
- return createResult({
9554
- status: "failed",
9555
- startTime,
9556
- prInfo,
9557
- failedChecks: enrichedChecks,
9558
- reviewComments: pollOutcome.reviewComments
9559
- });
9560
- }
9561
- if (pollOutcome.shouldPass) {
9562
- return createResult({ status: "passed", startTime, prInfo });
9563
- }
9564
- await sleep(pollIntervalSeconds * 1000);
9565
- }
9566
- return createResult({ status: "pending", startTime, prInfo });
9567
- }
9568
- function registerWaitCICommand(program) {
9569
- program.command("wait-ci").description("Wait for CI checks to complete and check for blocking reviews").option("--timeout <seconds>", "Maximum time to wait for CI (default: 270)", "270").option("--poll-interval <seconds>", "Time between CI status checks (default: 15)", "15").action(async (options) => {
9570
- const timeout = Number.parseInt(options.timeout, 10);
9571
- const pollInterval = Number.parseInt(options.pollInterval, 10);
9572
- if (Number.isNaN(timeout) || timeout <= 0) {
9573
- console.log(JSON.stringify({
9574
- ci_status: "error",
9575
- failed_checks: [],
9576
- review_comments: [],
9577
- elapsed_seconds: 0,
9578
- error_message: "Invalid timeout value"
9579
- }));
9580
- process.exit(1);
9581
- }
9582
- if (Number.isNaN(pollInterval) || pollInterval <= 0) {
9583
- console.log(JSON.stringify({
9584
- ci_status: "error",
9585
- failed_checks: [],
9586
- review_comments: [],
9587
- elapsed_seconds: 0,
9588
- error_message: "Invalid poll-interval value"
9589
- }));
9590
- process.exit(1);
9591
- }
9592
- const result = await waitForCI(timeout, pollInterval);
9593
- console.log(JSON.stringify(result));
9594
- if (result.ci_status === "passed") {
9595
- process.exit(0);
9596
- } else if (result.ci_status === "pending") {
9597
- process.exit(2);
9598
- } else {
9599
- process.exit(1);
9600
- }
9601
- });
9602
- }
9603
9210
  // src/index.ts
9604
9211
  var program = new Command;
9605
9212
  program.name("agent-gauntlet").description("AI-assisted quality gates").version(package_default.version);
@@ -9613,14 +9220,14 @@ registerListCommand(program);
9613
9220
  registerHealthCommand(program);
9614
9221
  registerInitCommand(program);
9615
9222
  registerValidateCommand(program);
9223
+ registerSkipCommand(program);
9616
9224
  registerStartHookCommand(program);
9617
9225
  registerStatusCommand(program);
9618
9226
  registerStopHookCommand(program);
9619
- registerWaitCICommand(program);
9620
9227
  registerHelpCommand(program);
9621
9228
  if (process.argv.length < 3) {
9622
9229
  process.argv.push("help");
9623
9230
  }
9624
9231
  program.parse(process.argv);
9625
9232
 
9626
- //# debugId=5FCEF2943643F3B864756E2164756E21
9233
+ //# debugId=E93D541AB16DB2CC64756E2164756E21