pullfrog 0.0.201 → 0.0.202

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
@@ -4076,8 +4076,8 @@ var require_util2 = __commonJS({
4076
4076
  function createDeferredPromise() {
4077
4077
  let res;
4078
4078
  let rej;
4079
- const promise2 = new Promise((resolve2, reject) => {
4080
- res = resolve2;
4079
+ const promise2 = new Promise((resolve3, reject) => {
4080
+ res = resolve3;
4081
4081
  rej = reject;
4082
4082
  });
4083
4083
  return { promise: promise2, resolve: res, reject: rej };
@@ -5581,8 +5581,8 @@ Content-Type: ${value2.type || "application/octet-stream"}\r
5581
5581
  });
5582
5582
  }
5583
5583
  });
5584
- const busboyResolve = new Promise((resolve2, reject) => {
5585
- busboy.on("finish", resolve2);
5584
+ const busboyResolve = new Promise((resolve3, reject) => {
5585
+ busboy.on("finish", resolve3);
5586
5586
  busboy.on("error", (err) => reject(new TypeError(err)));
5587
5587
  });
5588
5588
  if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
@@ -6116,9 +6116,9 @@ var require_dispatcher_base = __commonJS({
6116
6116
  }
6117
6117
  close(callback) {
6118
6118
  if (callback === void 0) {
6119
- return new Promise((resolve2, reject) => {
6119
+ return new Promise((resolve3, reject) => {
6120
6120
  this.close((err, data) => {
6121
- return err ? reject(err) : resolve2(data);
6121
+ return err ? reject(err) : resolve3(data);
6122
6122
  });
6123
6123
  });
6124
6124
  }
@@ -6156,12 +6156,12 @@ var require_dispatcher_base = __commonJS({
6156
6156
  err = null;
6157
6157
  }
6158
6158
  if (callback === void 0) {
6159
- return new Promise((resolve2, reject) => {
6159
+ return new Promise((resolve3, reject) => {
6160
6160
  this.destroy(err, (err2, data) => {
6161
6161
  return err2 ? (
6162
6162
  /* istanbul ignore next: should never error */
6163
6163
  reject(err2)
6164
- ) : resolve2(data);
6164
+ ) : resolve3(data);
6165
6165
  });
6166
6166
  });
6167
6167
  }
@@ -7221,16 +7221,16 @@ var require_client = __commonJS({
7221
7221
  return this[kNeedDrain] < 2;
7222
7222
  }
7223
7223
  async [kClose]() {
7224
- return new Promise((resolve2) => {
7224
+ return new Promise((resolve3) => {
7225
7225
  if (!this[kSize]) {
7226
- resolve2(null);
7226
+ resolve3(null);
7227
7227
  } else {
7228
- this[kClosedResolve] = resolve2;
7228
+ this[kClosedResolve] = resolve3;
7229
7229
  }
7230
7230
  });
7231
7231
  }
7232
7232
  async [kDestroy](err) {
7233
- return new Promise((resolve2) => {
7233
+ return new Promise((resolve3) => {
7234
7234
  const requests = this[kQueue].splice(this[kPendingIdx]);
7235
7235
  for (let i = 0; i < requests.length; i++) {
7236
7236
  const request2 = requests[i];
@@ -7241,7 +7241,7 @@ var require_client = __commonJS({
7241
7241
  this[kClosedResolve]();
7242
7242
  this[kClosedResolve] = null;
7243
7243
  }
7244
- resolve2();
7244
+ resolve3();
7245
7245
  };
7246
7246
  if (this[kHTTP2Session] != null) {
7247
7247
  util2.destroy(this[kHTTP2Session], err);
@@ -7821,7 +7821,7 @@ var require_client = __commonJS({
7821
7821
  });
7822
7822
  }
7823
7823
  try {
7824
- const socket = await new Promise((resolve2, reject) => {
7824
+ const socket = await new Promise((resolve3, reject) => {
7825
7825
  client[kConnector]({
7826
7826
  host,
7827
7827
  hostname: hostname4,
@@ -7833,7 +7833,7 @@ var require_client = __commonJS({
7833
7833
  if (err) {
7834
7834
  reject(err);
7835
7835
  } else {
7836
- resolve2(socket2);
7836
+ resolve3(socket2);
7837
7837
  }
7838
7838
  });
7839
7839
  });
@@ -8457,12 +8457,12 @@ upgrade: ${upgrade}\r
8457
8457
  cb();
8458
8458
  }
8459
8459
  }
8460
- const waitForDrain = () => new Promise((resolve2, reject) => {
8460
+ const waitForDrain = () => new Promise((resolve3, reject) => {
8461
8461
  assert3(callback === null);
8462
8462
  if (socket[kError]) {
8463
8463
  reject(socket[kError]);
8464
8464
  } else {
8465
- callback = resolve2;
8465
+ callback = resolve3;
8466
8466
  }
8467
8467
  });
8468
8468
  if (client[kHTTPConnVersion] === "h2") {
@@ -8807,8 +8807,8 @@ var require_pool_base = __commonJS({
8807
8807
  if (this[kQueue].isEmpty()) {
8808
8808
  return Promise.all(this[kClients].map((c) => c.close()));
8809
8809
  } else {
8810
- return new Promise((resolve2) => {
8811
- this[kClosedResolve] = resolve2;
8810
+ return new Promise((resolve3) => {
8811
+ this[kClosedResolve] = resolve3;
8812
8812
  });
8813
8813
  }
8814
8814
  }
@@ -9386,7 +9386,7 @@ var require_readable = __commonJS({
9386
9386
  if (this.closed) {
9387
9387
  return Promise.resolve(null);
9388
9388
  }
9389
- return new Promise((resolve2, reject) => {
9389
+ return new Promise((resolve3, reject) => {
9390
9390
  const signalListenerCleanup = signal ? util2.addAbortListener(signal, () => {
9391
9391
  this.destroy();
9392
9392
  }) : noop4;
@@ -9395,7 +9395,7 @@ var require_readable = __commonJS({
9395
9395
  if (signal && signal.aborted) {
9396
9396
  reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
9397
9397
  } else {
9398
- resolve2(null);
9398
+ resolve3(null);
9399
9399
  }
9400
9400
  }).on("error", noop4).on("data", function(chunk) {
9401
9401
  limit -= chunk.length;
@@ -9417,11 +9417,11 @@ var require_readable = __commonJS({
9417
9417
  throw new TypeError("unusable");
9418
9418
  }
9419
9419
  assert3(!stream[kConsume]);
9420
- return new Promise((resolve2, reject) => {
9420
+ return new Promise((resolve3, reject) => {
9421
9421
  stream[kConsume] = {
9422
9422
  type: type2,
9423
9423
  stream,
9424
- resolve: resolve2,
9424
+ resolve: resolve3,
9425
9425
  reject,
9426
9426
  length: 0,
9427
9427
  body: []
@@ -9456,12 +9456,12 @@ var require_readable = __commonJS({
9456
9456
  }
9457
9457
  }
9458
9458
  function consumeEnd(consume2) {
9459
- const { type: type2, body, resolve: resolve2, stream, length } = consume2;
9459
+ const { type: type2, body, resolve: resolve3, stream, length } = consume2;
9460
9460
  try {
9461
9461
  if (type2 === "text") {
9462
- resolve2(toUSVString(Buffer.concat(body)));
9462
+ resolve3(toUSVString(Buffer.concat(body)));
9463
9463
  } else if (type2 === "json") {
9464
- resolve2(JSON.parse(Buffer.concat(body)));
9464
+ resolve3(JSON.parse(Buffer.concat(body)));
9465
9465
  } else if (type2 === "arrayBuffer") {
9466
9466
  const dst = new Uint8Array(length);
9467
9467
  let pos = 0;
@@ -9469,12 +9469,12 @@ var require_readable = __commonJS({
9469
9469
  dst.set(buf, pos);
9470
9470
  pos += buf.byteLength;
9471
9471
  }
9472
- resolve2(dst.buffer);
9472
+ resolve3(dst.buffer);
9473
9473
  } else if (type2 === "blob") {
9474
9474
  if (!Blob2) {
9475
9475
  Blob2 = __require("buffer").Blob;
9476
9476
  }
9477
- resolve2(new Blob2(body, { type: stream[kContentType] }));
9477
+ resolve3(new Blob2(body, { type: stream[kContentType] }));
9478
9478
  }
9479
9479
  consumeFinish(consume2);
9480
9480
  } catch (err) {
@@ -9729,9 +9729,9 @@ var require_api_request = __commonJS({
9729
9729
  };
9730
9730
  function request2(opts, callback) {
9731
9731
  if (callback === void 0) {
9732
- return new Promise((resolve2, reject) => {
9732
+ return new Promise((resolve3, reject) => {
9733
9733
  request2.call(this, opts, (err, data) => {
9734
- return err ? reject(err) : resolve2(data);
9734
+ return err ? reject(err) : resolve3(data);
9735
9735
  });
9736
9736
  });
9737
9737
  }
@@ -9904,9 +9904,9 @@ var require_api_stream = __commonJS({
9904
9904
  };
9905
9905
  function stream(opts, factory, callback) {
9906
9906
  if (callback === void 0) {
9907
- return new Promise((resolve2, reject) => {
9907
+ return new Promise((resolve3, reject) => {
9908
9908
  stream.call(this, opts, factory, (err, data) => {
9909
- return err ? reject(err) : resolve2(data);
9909
+ return err ? reject(err) : resolve3(data);
9910
9910
  });
9911
9911
  });
9912
9912
  }
@@ -10187,9 +10187,9 @@ var require_api_upgrade = __commonJS({
10187
10187
  };
10188
10188
  function upgrade(opts, callback) {
10189
10189
  if (callback === void 0) {
10190
- return new Promise((resolve2, reject) => {
10190
+ return new Promise((resolve3, reject) => {
10191
10191
  upgrade.call(this, opts, (err, data) => {
10192
- return err ? reject(err) : resolve2(data);
10192
+ return err ? reject(err) : resolve3(data);
10193
10193
  });
10194
10194
  });
10195
10195
  }
@@ -10278,9 +10278,9 @@ var require_api_connect = __commonJS({
10278
10278
  };
10279
10279
  function connect(opts, callback) {
10280
10280
  if (callback === void 0) {
10281
- return new Promise((resolve2, reject) => {
10281
+ return new Promise((resolve3, reject) => {
10282
10282
  connect.call(this, opts, (err, data) => {
10283
- return err ? reject(err) : resolve2(data);
10283
+ return err ? reject(err) : resolve3(data);
10284
10284
  });
10285
10285
  });
10286
10286
  }
@@ -13902,7 +13902,7 @@ var require_fetch = __commonJS({
13902
13902
  async function dispatch({ body }) {
13903
13903
  const url4 = requestCurrentURL(request2);
13904
13904
  const agent2 = fetchParams.controller.dispatcher;
13905
- return new Promise((resolve2, reject) => agent2.dispatch(
13905
+ return new Promise((resolve3, reject) => agent2.dispatch(
13906
13906
  {
13907
13907
  path: url4.pathname + url4.search,
13908
13908
  origin: url4.origin,
@@ -13978,7 +13978,7 @@ var require_fetch = __commonJS({
13978
13978
  }
13979
13979
  }
13980
13980
  }
13981
- resolve2({
13981
+ resolve3({
13982
13982
  status,
13983
13983
  statusText,
13984
13984
  headersList: headers[kHeadersList],
@@ -14021,7 +14021,7 @@ var require_fetch = __commonJS({
14021
14021
  const val = headersList[n + 1].toString("latin1");
14022
14022
  headers[kHeadersList].append(key, val);
14023
14023
  }
14024
- resolve2({
14024
+ resolve3({
14025
14025
  status,
14026
14026
  statusText: STATUS_CODES[status],
14027
14027
  headersList: headers[kHeadersList],
@@ -17375,11 +17375,11 @@ var require_lib = __commonJS({
17375
17375
  };
17376
17376
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
17377
17377
  function adopt(value2) {
17378
- return value2 instanceof P ? value2 : new P(function(resolve2) {
17379
- resolve2(value2);
17378
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
17379
+ resolve3(value2);
17380
17380
  });
17381
17381
  }
17382
- return new (P || (P = Promise))(function(resolve2, reject) {
17382
+ return new (P || (P = Promise))(function(resolve3, reject) {
17383
17383
  function fulfilled(value2) {
17384
17384
  try {
17385
17385
  step(generator.next(value2));
@@ -17395,7 +17395,7 @@ var require_lib = __commonJS({
17395
17395
  }
17396
17396
  }
17397
17397
  function step(result) {
17398
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
17398
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
17399
17399
  }
17400
17400
  step((generator = generator.apply(thisArg, _arguments || [])).next());
17401
17401
  });
@@ -17481,26 +17481,26 @@ var require_lib = __commonJS({
17481
17481
  }
17482
17482
  readBody() {
17483
17483
  return __awaiter(this, void 0, void 0, function* () {
17484
- return new Promise((resolve2) => __awaiter(this, void 0, void 0, function* () {
17484
+ return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
17485
17485
  let output = Buffer.alloc(0);
17486
17486
  this.message.on("data", (chunk) => {
17487
17487
  output = Buffer.concat([output, chunk]);
17488
17488
  });
17489
17489
  this.message.on("end", () => {
17490
- resolve2(output.toString());
17490
+ resolve3(output.toString());
17491
17491
  });
17492
17492
  }));
17493
17493
  });
17494
17494
  }
17495
17495
  readBodyBuffer() {
17496
17496
  return __awaiter(this, void 0, void 0, function* () {
17497
- return new Promise((resolve2) => __awaiter(this, void 0, void 0, function* () {
17497
+ return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
17498
17498
  const chunks = [];
17499
17499
  this.message.on("data", (chunk) => {
17500
17500
  chunks.push(chunk);
17501
17501
  });
17502
17502
  this.message.on("end", () => {
17503
- resolve2(Buffer.concat(chunks));
17503
+ resolve3(Buffer.concat(chunks));
17504
17504
  });
17505
17505
  }));
17506
17506
  });
@@ -17709,14 +17709,14 @@ var require_lib = __commonJS({
17709
17709
  */
17710
17710
  requestRaw(info2, data) {
17711
17711
  return __awaiter(this, void 0, void 0, function* () {
17712
- return new Promise((resolve2, reject) => {
17712
+ return new Promise((resolve3, reject) => {
17713
17713
  function callbackForResult(err, res) {
17714
17714
  if (err) {
17715
17715
  reject(err);
17716
17716
  } else if (!res) {
17717
17717
  reject(new Error("Unknown error"));
17718
17718
  } else {
17719
- resolve2(res);
17719
+ resolve3(res);
17720
17720
  }
17721
17721
  }
17722
17722
  this.requestRawWithCallback(info2, data, callbackForResult);
@@ -17898,12 +17898,12 @@ var require_lib = __commonJS({
17898
17898
  return __awaiter(this, void 0, void 0, function* () {
17899
17899
  retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
17900
17900
  const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
17901
- return new Promise((resolve2) => setTimeout(() => resolve2(), ms));
17901
+ return new Promise((resolve3) => setTimeout(() => resolve3(), ms));
17902
17902
  });
17903
17903
  }
17904
17904
  _processResponse(res, options) {
17905
17905
  return __awaiter(this, void 0, void 0, function* () {
17906
- return new Promise((resolve2, reject) => __awaiter(this, void 0, void 0, function* () {
17906
+ return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
17907
17907
  const statusCode = res.message.statusCode || 0;
17908
17908
  const response = {
17909
17909
  statusCode,
@@ -17911,7 +17911,7 @@ var require_lib = __commonJS({
17911
17911
  headers: {}
17912
17912
  };
17913
17913
  if (statusCode === HttpCodes.NotFound) {
17914
- resolve2(response);
17914
+ resolve3(response);
17915
17915
  }
17916
17916
  function dateTimeDeserializer(key, value2) {
17917
17917
  if (typeof value2 === "string") {
@@ -17950,7 +17950,7 @@ var require_lib = __commonJS({
17950
17950
  err.result = response.result;
17951
17951
  reject(err);
17952
17952
  } else {
17953
- resolve2(response);
17953
+ resolve3(response);
17954
17954
  }
17955
17955
  }));
17956
17956
  });
@@ -17967,11 +17967,11 @@ var require_auth = __commonJS({
17967
17967
  "use strict";
17968
17968
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
17969
17969
  function adopt(value2) {
17970
- return value2 instanceof P ? value2 : new P(function(resolve2) {
17971
- resolve2(value2);
17970
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
17971
+ resolve3(value2);
17972
17972
  });
17973
17973
  }
17974
- return new (P || (P = Promise))(function(resolve2, reject) {
17974
+ return new (P || (P = Promise))(function(resolve3, reject) {
17975
17975
  function fulfilled(value2) {
17976
17976
  try {
17977
17977
  step(generator.next(value2));
@@ -17987,7 +17987,7 @@ var require_auth = __commonJS({
17987
17987
  }
17988
17988
  }
17989
17989
  function step(result) {
17990
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
17990
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
17991
17991
  }
17992
17992
  step((generator = generator.apply(thisArg, _arguments || [])).next());
17993
17993
  });
@@ -18071,11 +18071,11 @@ var require_oidc_utils = __commonJS({
18071
18071
  "use strict";
18072
18072
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
18073
18073
  function adopt(value2) {
18074
- return value2 instanceof P ? value2 : new P(function(resolve2) {
18075
- resolve2(value2);
18074
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
18075
+ resolve3(value2);
18076
18076
  });
18077
18077
  }
18078
- return new (P || (P = Promise))(function(resolve2, reject) {
18078
+ return new (P || (P = Promise))(function(resolve3, reject) {
18079
18079
  function fulfilled(value2) {
18080
18080
  try {
18081
18081
  step(generator.next(value2));
@@ -18091,7 +18091,7 @@ var require_oidc_utils = __commonJS({
18091
18091
  }
18092
18092
  }
18093
18093
  function step(result) {
18094
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18094
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18095
18095
  }
18096
18096
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18097
18097
  });
@@ -18169,11 +18169,11 @@ var require_summary = __commonJS({
18169
18169
  "use strict";
18170
18170
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
18171
18171
  function adopt(value2) {
18172
- return value2 instanceof P ? value2 : new P(function(resolve2) {
18173
- resolve2(value2);
18172
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
18173
+ resolve3(value2);
18174
18174
  });
18175
18175
  }
18176
- return new (P || (P = Promise))(function(resolve2, reject) {
18176
+ return new (P || (P = Promise))(function(resolve3, reject) {
18177
18177
  function fulfilled(value2) {
18178
18178
  try {
18179
18179
  step(generator.next(value2));
@@ -18189,7 +18189,7 @@ var require_summary = __commonJS({
18189
18189
  }
18190
18190
  }
18191
18191
  function step(result) {
18192
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18192
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18193
18193
  }
18194
18194
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18195
18195
  });
@@ -18535,11 +18535,11 @@ var require_io_util = __commonJS({
18535
18535
  };
18536
18536
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
18537
18537
  function adopt(value2) {
18538
- return value2 instanceof P ? value2 : new P(function(resolve2) {
18539
- resolve2(value2);
18538
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
18539
+ resolve3(value2);
18540
18540
  });
18541
18541
  }
18542
- return new (P || (P = Promise))(function(resolve2, reject) {
18542
+ return new (P || (P = Promise))(function(resolve3, reject) {
18543
18543
  function fulfilled(value2) {
18544
18544
  try {
18545
18545
  step(generator.next(value2));
@@ -18555,7 +18555,7 @@ var require_io_util = __commonJS({
18555
18555
  }
18556
18556
  }
18557
18557
  function step(result) {
18558
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18558
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18559
18559
  }
18560
18560
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18561
18561
  });
@@ -18708,11 +18708,11 @@ var require_io = __commonJS({
18708
18708
  };
18709
18709
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
18710
18710
  function adopt(value2) {
18711
- return value2 instanceof P ? value2 : new P(function(resolve2) {
18712
- resolve2(value2);
18711
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
18712
+ resolve3(value2);
18713
18713
  });
18714
18714
  }
18715
- return new (P || (P = Promise))(function(resolve2, reject) {
18715
+ return new (P || (P = Promise))(function(resolve3, reject) {
18716
18716
  function fulfilled(value2) {
18717
18717
  try {
18718
18718
  step(generator.next(value2));
@@ -18728,7 +18728,7 @@ var require_io = __commonJS({
18728
18728
  }
18729
18729
  }
18730
18730
  function step(result) {
18731
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18731
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18732
18732
  }
18733
18733
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18734
18734
  });
@@ -18956,11 +18956,11 @@ var require_toolrunner = __commonJS({
18956
18956
  };
18957
18957
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
18958
18958
  function adopt(value2) {
18959
- return value2 instanceof P ? value2 : new P(function(resolve2) {
18960
- resolve2(value2);
18959
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
18960
+ resolve3(value2);
18961
18961
  });
18962
18962
  }
18963
- return new (P || (P = Promise))(function(resolve2, reject) {
18963
+ return new (P || (P = Promise))(function(resolve3, reject) {
18964
18964
  function fulfilled(value2) {
18965
18965
  try {
18966
18966
  step(generator.next(value2));
@@ -18976,7 +18976,7 @@ var require_toolrunner = __commonJS({
18976
18976
  }
18977
18977
  }
18978
18978
  function step(result) {
18979
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18979
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18980
18980
  }
18981
18981
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18982
18982
  });
@@ -19204,7 +19204,7 @@ var require_toolrunner = __commonJS({
19204
19204
  this.toolPath = path3.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
19205
19205
  }
19206
19206
  this.toolPath = yield io.which(this.toolPath, true);
19207
- return new Promise((resolve2, reject) => __awaiter(this, void 0, void 0, function* () {
19207
+ return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
19208
19208
  this._debug(`exec tool: ${this.toolPath}`);
19209
19209
  this._debug("arguments:");
19210
19210
  for (const arg of this.args) {
@@ -19287,7 +19287,7 @@ var require_toolrunner = __commonJS({
19287
19287
  if (error49) {
19288
19288
  reject(error49);
19289
19289
  } else {
19290
- resolve2(exitCode);
19290
+ resolve3(exitCode);
19291
19291
  }
19292
19292
  });
19293
19293
  if (this.options.input) {
@@ -19440,11 +19440,11 @@ var require_exec = __commonJS({
19440
19440
  };
19441
19441
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
19442
19442
  function adopt(value2) {
19443
- return value2 instanceof P ? value2 : new P(function(resolve2) {
19444
- resolve2(value2);
19443
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
19444
+ resolve3(value2);
19445
19445
  });
19446
19446
  }
19447
- return new (P || (P = Promise))(function(resolve2, reject) {
19447
+ return new (P || (P = Promise))(function(resolve3, reject) {
19448
19448
  function fulfilled(value2) {
19449
19449
  try {
19450
19450
  step(generator.next(value2));
@@ -19460,7 +19460,7 @@ var require_exec = __commonJS({
19460
19460
  }
19461
19461
  }
19462
19462
  function step(result) {
19463
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19463
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19464
19464
  }
19465
19465
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19466
19466
  });
@@ -19551,11 +19551,11 @@ var require_platform = __commonJS({
19551
19551
  };
19552
19552
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
19553
19553
  function adopt(value2) {
19554
- return value2 instanceof P ? value2 : new P(function(resolve2) {
19555
- resolve2(value2);
19554
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
19555
+ resolve3(value2);
19556
19556
  });
19557
19557
  }
19558
- return new (P || (P = Promise))(function(resolve2, reject) {
19558
+ return new (P || (P = Promise))(function(resolve3, reject) {
19559
19559
  function fulfilled(value2) {
19560
19560
  try {
19561
19561
  step(generator.next(value2));
@@ -19571,7 +19571,7 @@ var require_platform = __commonJS({
19571
19571
  }
19572
19572
  }
19573
19573
  function step(result) {
19574
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19574
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19575
19575
  }
19576
19576
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19577
19577
  });
@@ -19670,11 +19670,11 @@ var require_core = __commonJS({
19670
19670
  };
19671
19671
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
19672
19672
  function adopt(value2) {
19673
- return value2 instanceof P ? value2 : new P(function(resolve2) {
19674
- resolve2(value2);
19673
+ return value2 instanceof P ? value2 : new P(function(resolve3) {
19674
+ resolve3(value2);
19675
19675
  });
19676
19676
  }
19677
- return new (P || (P = Promise))(function(resolve2, reject) {
19677
+ return new (P || (P = Promise))(function(resolve3, reject) {
19678
19678
  function fulfilled(value2) {
19679
19679
  try {
19680
19680
  step(generator.next(value2));
@@ -19690,7 +19690,7 @@ var require_core = __commonJS({
19690
19690
  }
19691
19691
  }
19692
19692
  function step(result) {
19693
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19693
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19694
19694
  }
19695
19695
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19696
19696
  });
@@ -45381,8 +45381,8 @@ var require_resolve = __commonJS({
45381
45381
  }
45382
45382
  return count;
45383
45383
  }
45384
- function getFullPath(resolver, id = "", normalize2) {
45385
- if (normalize2 !== false)
45384
+ function getFullPath(resolver, id = "", normalize3) {
45385
+ if (normalize3 !== false)
45386
45386
  id = normalizeId(id);
45387
45387
  const p = resolver.parse(id);
45388
45388
  return _getFullPath(resolver, p);
@@ -46130,7 +46130,7 @@ var require_compile = __commonJS({
46130
46130
  const schOrFunc = root.refs[ref];
46131
46131
  if (schOrFunc)
46132
46132
  return schOrFunc;
46133
- let _sch = resolve2.call(this, root, ref);
46133
+ let _sch = resolve3.call(this, root, ref);
46134
46134
  if (_sch === void 0) {
46135
46135
  const schema2 = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
46136
46136
  const { schemaId } = this.opts;
@@ -46157,7 +46157,7 @@ var require_compile = __commonJS({
46157
46157
  function sameSchemaEnv(s1, s2) {
46158
46158
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
46159
46159
  }
46160
- function resolve2(root, ref) {
46160
+ function resolve3(root, ref) {
46161
46161
  let sch;
46162
46162
  while (typeof (sch = this.refs[ref]) == "string")
46163
46163
  ref = sch;
@@ -46722,7 +46722,7 @@ var require_fast_uri = __commonJS({
46722
46722
  "use strict";
46723
46723
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils4();
46724
46724
  var { SCHEMES, getSchemeHandler } = require_schemes();
46725
- function normalize2(uri, options) {
46725
+ function normalize3(uri, options) {
46726
46726
  if (typeof uri === "string") {
46727
46727
  uri = /** @type {T} */
46728
46728
  serialize(parse5(uri, options), options);
@@ -46732,7 +46732,7 @@ var require_fast_uri = __commonJS({
46732
46732
  }
46733
46733
  return uri;
46734
46734
  }
46735
- function resolve2(baseURI, relativeURI, options) {
46735
+ function resolve3(baseURI, relativeURI, options) {
46736
46736
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
46737
46737
  const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
46738
46738
  schemelessOptions.skipEscape = true;
@@ -46958,8 +46958,8 @@ var require_fast_uri = __commonJS({
46958
46958
  }
46959
46959
  var fastUri = {
46960
46960
  SCHEMES,
46961
- normalize: normalize2,
46962
- resolve: resolve2,
46961
+ normalize: normalize3,
46962
+ resolve: resolve3,
46963
46963
  resolveComponent,
46964
46964
  equal,
46965
46965
  serialize,
@@ -52535,9 +52535,9 @@ var require_dispatcher_base2 = __commonJS({
52535
52535
  }
52536
52536
  close(callback) {
52537
52537
  if (callback === void 0) {
52538
- return new Promise((resolve2, reject) => {
52538
+ return new Promise((resolve3, reject) => {
52539
52539
  this.close((err, data) => {
52540
- return err ? reject(err) : resolve2(data);
52540
+ return err ? reject(err) : resolve3(data);
52541
52541
  });
52542
52542
  });
52543
52543
  }
@@ -52575,9 +52575,9 @@ var require_dispatcher_base2 = __commonJS({
52575
52575
  err = null;
52576
52576
  }
52577
52577
  if (callback === void 0) {
52578
- return new Promise((resolve2, reject) => {
52578
+ return new Promise((resolve3, reject) => {
52579
52579
  this.destroy(err, (err2, data) => {
52580
- return err2 ? reject(err2) : resolve2(data);
52580
+ return err2 ? reject(err2) : resolve3(data);
52581
52581
  });
52582
52582
  });
52583
52583
  }
@@ -56053,8 +56053,8 @@ var require_promise = __commonJS({
56053
56053
  function createDeferredPromise() {
56054
56054
  let res;
56055
56055
  let rej;
56056
- const promise2 = new Promise((resolve2, reject) => {
56057
- res = resolve2;
56056
+ const promise2 = new Promise((resolve3, reject) => {
56057
+ res = resolve3;
56058
56058
  rej = reject;
56059
56059
  });
56060
56060
  return { promise: promise2, resolve: res, reject: rej };
@@ -57352,12 +57352,12 @@ upgrade: ${upgrade}\r
57352
57352
  cb();
57353
57353
  }
57354
57354
  }
57355
- const waitForDrain = () => new Promise((resolve2, reject) => {
57355
+ const waitForDrain = () => new Promise((resolve3, reject) => {
57356
57356
  assert3(callback === null);
57357
57357
  if (socket[kError]) {
57358
57358
  reject(socket[kError]);
57359
57359
  } else {
57360
- callback = resolve2;
57360
+ callback = resolve3;
57361
57361
  }
57362
57362
  });
57363
57363
  socket.on("close", onDrain).on("drain", onDrain);
@@ -58198,12 +58198,12 @@ var require_client_h2 = __commonJS({
58198
58198
  cb();
58199
58199
  }
58200
58200
  }
58201
- const waitForDrain = () => new Promise((resolve2, reject) => {
58201
+ const waitForDrain = () => new Promise((resolve3, reject) => {
58202
58202
  assert3(callback === null);
58203
58203
  if (socket[kError]) {
58204
58204
  reject(socket[kError]);
58205
58205
  } else {
58206
- callback = resolve2;
58206
+ callback = resolve3;
58207
58207
  }
58208
58208
  });
58209
58209
  h2stream.on("close", onDrain).on("drain", onDrain);
@@ -58511,16 +58511,16 @@ var require_client2 = __commonJS({
58511
58511
  return this[kNeedDrain] < 2;
58512
58512
  }
58513
58513
  [kClose]() {
58514
- return new Promise((resolve2) => {
58514
+ return new Promise((resolve3) => {
58515
58515
  if (this[kSize]) {
58516
- this[kClosedResolve] = resolve2;
58516
+ this[kClosedResolve] = resolve3;
58517
58517
  } else {
58518
- resolve2(null);
58518
+ resolve3(null);
58519
58519
  }
58520
58520
  });
58521
58521
  }
58522
58522
  [kDestroy](err) {
58523
- return new Promise((resolve2) => {
58523
+ return new Promise((resolve3) => {
58524
58524
  const requests = this[kQueue].splice(this[kPendingIdx]);
58525
58525
  for (let i = 0; i < requests.length; i++) {
58526
58526
  const request2 = requests[i];
@@ -58531,7 +58531,7 @@ var require_client2 = __commonJS({
58531
58531
  this[kClosedResolve]();
58532
58532
  this[kClosedResolve] = null;
58533
58533
  }
58534
- resolve2(null);
58534
+ resolve3(null);
58535
58535
  };
58536
58536
  if (this[kHTTPContext]) {
58537
58537
  this[kHTTPContext].destroy(err, callback);
@@ -58928,8 +58928,8 @@ var require_pool_base2 = __commonJS({
58928
58928
  }
58929
58929
  return Promise.all(closeAll);
58930
58930
  } else {
58931
- return new Promise((resolve2) => {
58932
- this[kClosedResolve] = resolve2;
58931
+ return new Promise((resolve3) => {
58932
+ this[kClosedResolve] = resolve3;
58933
58933
  });
58934
58934
  }
58935
58935
  }
@@ -60458,7 +60458,7 @@ var require_readable2 = __commonJS({
60458
60458
  if (this._readableState.closeEmitted) {
60459
60459
  return Promise.resolve(null);
60460
60460
  }
60461
- return new Promise((resolve2, reject) => {
60461
+ return new Promise((resolve3, reject) => {
60462
60462
  if (this[kContentLength] && this[kContentLength] > limit || this[kBytesRead] > limit) {
60463
60463
  this.destroy(new AbortError2());
60464
60464
  }
@@ -60472,11 +60472,11 @@ var require_readable2 = __commonJS({
60472
60472
  if (signal.aborted) {
60473
60473
  reject(signal.reason ?? new AbortError2());
60474
60474
  } else {
60475
- resolve2(null);
60475
+ resolve3(null);
60476
60476
  }
60477
60477
  });
60478
60478
  } else {
60479
- this.on("close", resolve2);
60479
+ this.on("close", resolve3);
60480
60480
  }
60481
60481
  this.on("error", noop4).on("data", () => {
60482
60482
  if (this[kBytesRead] > limit) {
@@ -60504,7 +60504,7 @@ var require_readable2 = __commonJS({
60504
60504
  }
60505
60505
  function consume(stream, type2) {
60506
60506
  assert3(!stream[kConsume]);
60507
- return new Promise((resolve2, reject) => {
60507
+ return new Promise((resolve3, reject) => {
60508
60508
  if (isUnusable(stream)) {
60509
60509
  const rState = stream._readableState;
60510
60510
  if (rState.destroyed && rState.closeEmitted === false) {
@@ -60519,7 +60519,7 @@ var require_readable2 = __commonJS({
60519
60519
  stream[kConsume] = {
60520
60520
  type: type2,
60521
60521
  stream,
60522
- resolve: resolve2,
60522
+ resolve: resolve3,
60523
60523
  reject,
60524
60524
  length: 0,
60525
60525
  body: []
@@ -60593,18 +60593,18 @@ var require_readable2 = __commonJS({
60593
60593
  return buffer;
60594
60594
  }
60595
60595
  function consumeEnd(consume2, encoding) {
60596
- const { type: type2, body, resolve: resolve2, stream, length } = consume2;
60596
+ const { type: type2, body, resolve: resolve3, stream, length } = consume2;
60597
60597
  try {
60598
60598
  if (type2 === "text") {
60599
- resolve2(chunksDecode(body, length, encoding));
60599
+ resolve3(chunksDecode(body, length, encoding));
60600
60600
  } else if (type2 === "json") {
60601
- resolve2(JSON.parse(chunksDecode(body, length, encoding)));
60601
+ resolve3(JSON.parse(chunksDecode(body, length, encoding)));
60602
60602
  } else if (type2 === "arrayBuffer") {
60603
- resolve2(chunksConcat(body, length).buffer);
60603
+ resolve3(chunksConcat(body, length).buffer);
60604
60604
  } else if (type2 === "blob") {
60605
- resolve2(new Blob(body, { type: stream[kContentType] }));
60605
+ resolve3(new Blob(body, { type: stream[kContentType] }));
60606
60606
  } else if (type2 === "bytes") {
60607
- resolve2(chunksConcat(body, length));
60607
+ resolve3(chunksConcat(body, length));
60608
60608
  }
60609
60609
  consumeFinish(consume2);
60610
60610
  } catch (err) {
@@ -60794,9 +60794,9 @@ var require_api_request2 = __commonJS({
60794
60794
  };
60795
60795
  function request2(opts, callback) {
60796
60796
  if (callback === void 0) {
60797
- return new Promise((resolve2, reject) => {
60797
+ return new Promise((resolve3, reject) => {
60798
60798
  request2.call(this, opts, (err, data) => {
60799
- return err ? reject(err) : resolve2(data);
60799
+ return err ? reject(err) : resolve3(data);
60800
60800
  });
60801
60801
  });
60802
60802
  }
@@ -61008,9 +61008,9 @@ var require_api_stream2 = __commonJS({
61008
61008
  };
61009
61009
  function stream(opts, factory, callback) {
61010
61010
  if (callback === void 0) {
61011
- return new Promise((resolve2, reject) => {
61011
+ return new Promise((resolve3, reject) => {
61012
61012
  stream.call(this, opts, factory, (err, data) => {
61013
- return err ? reject(err) : resolve2(data);
61013
+ return err ? reject(err) : resolve3(data);
61014
61014
  });
61015
61015
  });
61016
61016
  }
@@ -61298,9 +61298,9 @@ var require_api_upgrade2 = __commonJS({
61298
61298
  };
61299
61299
  function upgrade(opts, callback) {
61300
61300
  if (callback === void 0) {
61301
- return new Promise((resolve2, reject) => {
61301
+ return new Promise((resolve3, reject) => {
61302
61302
  upgrade.call(this, opts, (err, data) => {
61303
- return err ? reject(err) : resolve2(data);
61303
+ return err ? reject(err) : resolve3(data);
61304
61304
  });
61305
61305
  });
61306
61306
  }
@@ -61393,9 +61393,9 @@ var require_api_connect2 = __commonJS({
61393
61393
  };
61394
61394
  function connect(opts, callback) {
61395
61395
  if (callback === void 0) {
61396
- return new Promise((resolve2, reject) => {
61396
+ return new Promise((resolve3, reject) => {
61397
61397
  connect.call(this, opts, (err, data) => {
61398
- return err ? reject(err) : resolve2(data);
61398
+ return err ? reject(err) : resolve3(data);
61399
61399
  });
61400
61400
  });
61401
61401
  }
@@ -62663,7 +62663,7 @@ var require_snapshot_recorder = __commonJS({
62663
62663
  "node_modules/.pnpm/undici@7.22.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
62664
62664
  "use strict";
62665
62665
  var { writeFile: writeFile2, readFile, mkdir } = __require("node:fs/promises");
62666
- var { dirname: dirname3, resolve: resolve2 } = __require("node:path");
62666
+ var { dirname: dirname3, resolve: resolve3 } = __require("node:path");
62667
62667
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
62668
62668
  var { InvalidArgumentError, UndiciError } = require_errors4();
62669
62669
  var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -62864,7 +62864,7 @@ var require_snapshot_recorder = __commonJS({
62864
62864
  throw new InvalidArgumentError("Snapshot path is required");
62865
62865
  }
62866
62866
  try {
62867
- const data = await readFile(resolve2(path3), "utf8");
62867
+ const data = await readFile(resolve3(path3), "utf8");
62868
62868
  const parsed2 = JSON.parse(data);
62869
62869
  if (Array.isArray(parsed2)) {
62870
62870
  this.#snapshots.clear();
@@ -62893,7 +62893,7 @@ var require_snapshot_recorder = __commonJS({
62893
62893
  if (!path3) {
62894
62894
  throw new InvalidArgumentError("Snapshot path is required");
62895
62895
  }
62896
- const resolvedPath = resolve2(path3);
62896
+ const resolvedPath = resolve3(path3);
62897
62897
  await mkdir(dirname3(resolvedPath), { recursive: true });
62898
62898
  const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot2]) => ({
62899
62899
  hash: hash2,
@@ -69470,7 +69470,7 @@ var require_fetch2 = __commonJS({
69470
69470
  function dispatch({ body }) {
69471
69471
  const url4 = requestCurrentURL(request2);
69472
69472
  const agent2 = fetchParams.controller.dispatcher;
69473
- return new Promise((resolve2, reject) => agent2.dispatch(
69473
+ return new Promise((resolve3, reject) => agent2.dispatch(
69474
69474
  {
69475
69475
  path: url4.pathname + url4.search,
69476
69476
  origin: url4.origin,
@@ -69550,7 +69550,7 @@ var require_fetch2 = __commonJS({
69550
69550
  }
69551
69551
  }
69552
69552
  const onError = this.onError.bind(this);
69553
- resolve2({
69553
+ resolve3({
69554
69554
  status,
69555
69555
  statusText,
69556
69556
  headersList,
@@ -69603,7 +69603,7 @@ var require_fetch2 = __commonJS({
69603
69603
  headersList.append(headerName, String(value2), true);
69604
69604
  }
69605
69605
  }
69606
- resolve2({
69606
+ resolve3({
69607
69607
  status,
69608
69608
  statusText: STATUS_CODES[status],
69609
69609
  headersList,
@@ -69619,7 +69619,7 @@ var require_fetch2 = __commonJS({
69619
69619
  for (let i = 0; i < rawHeaders.length; i += 2) {
69620
69620
  headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
69621
69621
  }
69622
- resolve2({
69622
+ resolve3({
69623
69623
  status,
69624
69624
  statusText: STATUS_CODES[status],
69625
69625
  headersList,
@@ -97961,8 +97961,8 @@ var require_light = __commonJS({
97961
97961
  return this.Promise.resolve();
97962
97962
  }
97963
97963
  yieldLoop(t = 0) {
97964
- return new this.Promise(function(resolve2, reject) {
97965
- return setTimeout(resolve2, t);
97964
+ return new this.Promise(function(resolve3, reject) {
97965
+ return setTimeout(resolve3, t);
97966
97966
  });
97967
97967
  }
97968
97968
  computePenalty() {
@@ -98173,15 +98173,15 @@ var require_light = __commonJS({
98173
98173
  return this._queue.length === 0;
98174
98174
  }
98175
98175
  async _tryToRun() {
98176
- var args2, cb, error49, reject, resolve2, returned, task;
98176
+ var args2, cb, error49, reject, resolve3, returned, task;
98177
98177
  if (this._running < 1 && this._queue.length > 0) {
98178
98178
  this._running++;
98179
- ({ task, args: args2, resolve: resolve2, reject } = this._queue.shift());
98179
+ ({ task, args: args2, resolve: resolve3, reject } = this._queue.shift());
98180
98180
  cb = await (async function() {
98181
98181
  try {
98182
98182
  returned = await task(...args2);
98183
98183
  return function() {
98184
- return resolve2(returned);
98184
+ return resolve3(returned);
98185
98185
  };
98186
98186
  } catch (error1) {
98187
98187
  error49 = error1;
@@ -98196,13 +98196,13 @@ var require_light = __commonJS({
98196
98196
  }
98197
98197
  }
98198
98198
  schedule(task, ...args2) {
98199
- var promise2, reject, resolve2;
98200
- resolve2 = reject = null;
98199
+ var promise2, reject, resolve3;
98200
+ resolve3 = reject = null;
98201
98201
  promise2 = new this.Promise(function(_resolve, _reject) {
98202
- resolve2 = _resolve;
98202
+ resolve3 = _resolve;
98203
98203
  return reject = _reject;
98204
98204
  });
98205
- this._queue.push({ task, args: args2, resolve: resolve2, reject });
98205
+ this._queue.push({ task, args: args2, resolve: resolve3, reject });
98206
98206
  this._tryToRun();
98207
98207
  return promise2;
98208
98208
  }
@@ -98603,14 +98603,14 @@ var require_light = __commonJS({
98603
98603
  counts = this._states.counts;
98604
98604
  return counts[0] + counts[1] + counts[2] + counts[3] === at;
98605
98605
  };
98606
- return new this.Promise((resolve2, reject) => {
98606
+ return new this.Promise((resolve3, reject) => {
98607
98607
  if (finished()) {
98608
- return resolve2();
98608
+ return resolve3();
98609
98609
  } else {
98610
98610
  return this.on("done", () => {
98611
98611
  if (finished()) {
98612
98612
  this.removeAllListeners("done");
98613
- return resolve2();
98613
+ return resolve3();
98614
98614
  }
98615
98615
  });
98616
98616
  }
@@ -98703,9 +98703,9 @@ var require_light = __commonJS({
98703
98703
  options = parser$5.load(options, this.jobDefaults);
98704
98704
  }
98705
98705
  task = (...args3) => {
98706
- return new this.Promise(function(resolve2, reject) {
98706
+ return new this.Promise(function(resolve3, reject) {
98707
98707
  return fn2(...args3, function(...args4) {
98708
- return (args4[0] != null ? reject : resolve2)(args4);
98708
+ return (args4[0] != null ? reject : resolve3)(args4);
98709
98709
  });
98710
98710
  });
98711
98711
  };
@@ -105245,10 +105245,10 @@ var BaseScope = class {
105245
105245
  });
105246
105246
  };
105247
105247
  lazyResolutions = [];
105248
- lazilyResolve(resolve2, syntheticAlias) {
105248
+ lazilyResolve(resolve3, syntheticAlias) {
105249
105249
  const node2 = this.node("alias", {
105250
105250
  reference: syntheticAlias ?? "synthetic",
105251
- resolve: resolve2
105251
+ resolve: resolve3
105252
105252
  }, { prereduced: true });
105253
105253
  if (!this.resolved)
105254
105254
  this.lazyResolutions.push(node2);
@@ -107398,6 +107398,81 @@ var core = __toESM(require_core(), 1);
107398
107398
  var import_table = __toESM(require_src(), 1);
107399
107399
  import { AsyncLocalStorage } from "node:async_hooks";
107400
107400
 
107401
+ // agents/shared.ts
107402
+ import { execFileSync } from "node:child_process";
107403
+ var MAX_STDERR_LINES = 20;
107404
+ var MAX_COMMIT_RETRIES = 3;
107405
+ function getGitStatus() {
107406
+ try {
107407
+ return execFileSync("git", ["status", "--porcelain"], {
107408
+ encoding: "utf-8",
107409
+ timeout: 1e4
107410
+ }).trim();
107411
+ } catch {
107412
+ return "";
107413
+ }
107414
+ }
107415
+ function buildCommitPrompt(_agentId, status) {
107416
+ return [
107417
+ `UNCOMMITTED CHANGES \u2014 the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`,
107418
+ "",
107419
+ "```",
107420
+ status,
107421
+ "```"
107422
+ ].join("\n");
107423
+ }
107424
+ var agent = (input) => {
107425
+ return {
107426
+ ...input,
107427
+ run: async (ctx) => {
107428
+ log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
107429
+ return input.run(ctx);
107430
+ }
107431
+ };
107432
+ };
107433
+ function formatCostUsd(costUsd) {
107434
+ return costUsd.toFixed(4);
107435
+ }
107436
+ function mergeAgentUsage(a, b) {
107437
+ if (!a && !b) return void 0;
107438
+ if (!a) return { ...b };
107439
+ if (!b) return { ...a };
107440
+ const cacheRead = (a.cacheReadTokens ?? 0) + (b.cacheReadTokens ?? 0);
107441
+ const cacheWrite = (a.cacheWriteTokens ?? 0) + (b.cacheWriteTokens ?? 0);
107442
+ const cost = (a.costUsd ?? 0) + (b.costUsd ?? 0);
107443
+ return {
107444
+ agent: a.agent,
107445
+ inputTokens: a.inputTokens + b.inputTokens,
107446
+ outputTokens: a.outputTokens + b.outputTokens,
107447
+ cacheReadTokens: cacheRead > 0 ? cacheRead : void 0,
107448
+ cacheWriteTokens: cacheWrite > 0 ? cacheWrite : void 0,
107449
+ costUsd: cost > 0 ? cost : void 0
107450
+ };
107451
+ }
107452
+ function logTokenTable(t) {
107453
+ const total = t.input + t.cacheRead + t.cacheWrite + t.output;
107454
+ const costUsd = typeof t.costUsd === "number" && t.costUsd > 0 ? t.costUsd : void 0;
107455
+ const headerRow = [
107456
+ { data: "Input", header: true },
107457
+ { data: "Cache Read", header: true },
107458
+ { data: "Cache Write", header: true },
107459
+ { data: "Output", header: true },
107460
+ { data: "Total", header: true }
107461
+ ];
107462
+ const dataRow = [
107463
+ String(t.input),
107464
+ String(t.cacheRead),
107465
+ String(t.cacheWrite),
107466
+ String(t.output),
107467
+ String(total)
107468
+ ];
107469
+ if (costUsd !== void 0) {
107470
+ headerRow.push({ data: "Cost ($)", header: true });
107471
+ dataRow.push(formatCostUsd(costUsd));
107472
+ }
107473
+ log.table([headerRow, dataRow]);
107474
+ }
107475
+
107401
107476
  // utils/globals.ts
107402
107477
  import { existsSync } from "node:fs";
107403
107478
  var isCloudflareSandbox = !!process.env.CLOUDFLARE_APPLICATION_ID && !!process.env.SANDBOX_VERSION;
@@ -107594,20 +107669,26 @@ function formatJsonValue(value2) {
107594
107669
  }
107595
107670
  function formatUsageSummary(entries) {
107596
107671
  if (entries.length === 0) return "";
107597
- const header = "| Agent | Input | Output | Cache Read | Cache Write |";
107598
- const separatorRow = "| --- | ---: | ---: | ---: | ---: |";
107672
+ const header = "| Agent | Input | Cache Read | Cache Write | Output | Total | Cost ($) |";
107673
+ const separatorRow = "| --- | ---: | ---: | ---: | ---: | ---: | ---: |";
107599
107674
  const fmt = (n) => n.toLocaleString("en-US");
107675
+ const nonCachedInput = (e) => Math.max(0, e.inputTokens - (e.cacheReadTokens ?? 0) - (e.cacheWriteTokens ?? 0));
107676
+ const totalFor = (e) => nonCachedInput(e) + (e.cacheReadTokens ?? 0) + (e.cacheWriteTokens ?? 0) + e.outputTokens;
107677
+ const costCell = (e) => typeof e.costUsd === "number" && e.costUsd > 0 ? formatCostUsd(e.costUsd) : "\u2014";
107600
107678
  const rows = entries.map(
107601
- (e) => `| ${e.agent} | ${fmt(e.inputTokens)} | ${fmt(e.outputTokens)} | ${fmt(e.cacheReadTokens ?? 0)} | ${fmt(e.cacheWriteTokens ?? 0)} |`
107679
+ (e) => `| ${e.agent} | ${fmt(nonCachedInput(e))} | ${fmt(e.cacheReadTokens ?? 0)} | ${fmt(e.cacheWriteTokens ?? 0)} | ${fmt(e.outputTokens)} | ${fmt(totalFor(e))} | ${costCell(e)} |`
107602
107680
  );
107603
107681
  const totalsRows = [];
107604
107682
  if (entries.length > 1) {
107605
- const totalInput = entries.reduce((sum, e) => sum + e.inputTokens, 0);
107683
+ const totalInput = entries.reduce((sum, e) => sum + nonCachedInput(e), 0);
107606
107684
  const totalOutput = entries.reduce((sum, e) => sum + e.outputTokens, 0);
107607
107685
  const totalCacheRead = entries.reduce((sum, e) => sum + (e.cacheReadTokens ?? 0), 0);
107608
107686
  const totalCacheWrite = entries.reduce((sum, e) => sum + (e.cacheWriteTokens ?? 0), 0);
107687
+ const grandTotal = totalInput + totalCacheRead + totalCacheWrite + totalOutput;
107688
+ const totalCostUsd = entries.reduce((sum, e) => sum + (e.costUsd ?? 0), 0);
107689
+ const totalCostCell = totalCostUsd > 0 ? `**${formatCostUsd(totalCostUsd)}**` : "\u2014";
107609
107690
  totalsRows.push(
107610
- `| **Total** | **${fmt(totalInput)}** | **${fmt(totalOutput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** |`
107691
+ `| **Total** | **${fmt(totalInput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** | **${fmt(totalOutput)}** | **${fmt(grandTotal)}** | ${totalCostCell} |`
107611
107692
  );
107612
107693
  }
107613
107694
  return [
@@ -107650,7 +107731,7 @@ var providers = {
107650
107731
  models: {
107651
107732
  "claude-opus": {
107652
107733
  displayName: "Claude Opus",
107653
- resolve: "anthropic/claude-opus-4-6",
107734
+ resolve: "anthropic/claude-opus-4-7",
107654
107735
  openRouterResolve: "openrouter/anthropic/claude-opus-4.6",
107655
107736
  preferred: true
107656
107737
  },
@@ -107678,7 +107759,7 @@ var providers = {
107678
107759
  },
107679
107760
  "gpt-codex-mini": {
107680
107761
  displayName: "GPT Codex Mini",
107681
- resolve: "openai/codex-mini-latest",
107762
+ resolve: "openai/gpt-5.1-codex-mini",
107682
107763
  openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
107683
107764
  },
107684
107765
  o3: {
@@ -107768,7 +107849,7 @@ var providers = {
107768
107849
  },
107769
107850
  "claude-opus": {
107770
107851
  displayName: "Claude Opus",
107771
- resolve: "opencode/claude-opus-4-6",
107852
+ resolve: "opencode/claude-opus-4-7",
107772
107853
  openRouterResolve: "openrouter/anthropic/claude-opus-4.6"
107773
107854
  },
107774
107855
  "claude-sonnet": {
@@ -108046,22 +108127,35 @@ async function retry(fn2, options = {}) {
108046
108127
  }
108047
108128
 
108048
108129
  // utils/patchWorkflowRunFields.ts
108049
- var ARTIFACT_PATCH_KEYS = [
108130
+ var STRING_KEYS = [
108050
108131
  "prNodeId",
108051
108132
  "issueNodeId",
108052
108133
  "reviewNodeId",
108053
108134
  "planCommentNodeId",
108054
108135
  "summaryCommentNodeId"
108055
108136
  ];
108137
+ var NUMBER_KEYS = [
108138
+ "inputTokens",
108139
+ "outputTokens",
108140
+ "cacheReadTokens",
108141
+ "cacheWriteTokens",
108142
+ "costUsd"
108143
+ ];
108056
108144
  async function patchWorkflowRunFields(ctx, fields) {
108057
108145
  if (ctx.runId === void 0 || !ctx.apiToken) return;
108058
108146
  const body = {};
108059
- for (const key of ARTIFACT_PATCH_KEYS) {
108147
+ for (const key of STRING_KEYS) {
108060
108148
  const value2 = fields[key];
108061
108149
  if (typeof value2 === "string" && value2.length > 0) {
108062
108150
  body[key] = value2;
108063
108151
  }
108064
108152
  }
108153
+ for (const key of NUMBER_KEYS) {
108154
+ const value2 = fields[key];
108155
+ if (typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0) {
108156
+ body[key] = value2;
108157
+ }
108158
+ }
108065
108159
  if (Object.keys(body).length === 0) return;
108066
108160
  try {
108067
108161
  await retry(
@@ -108088,6 +108182,38 @@ async function patchWorkflowRunFields(ctx, fields) {
108088
108182
  log.warning(`patchWorkflowRunFields exhausted retries: ${error49}`);
108089
108183
  }
108090
108184
  }
108185
+ var INT4_MAX = 2147483647;
108186
+ function clampInt(value2, field) {
108187
+ if (value2 > INT4_MAX) {
108188
+ log.warning(
108189
+ `aggregateUsage: ${field}=${value2} exceeds INT4_MAX (${INT4_MAX}) \u2014 clamping so the rest of the usage row still persists.`
108190
+ );
108191
+ return INT4_MAX;
108192
+ }
108193
+ return value2;
108194
+ }
108195
+ function aggregateUsage(entries) {
108196
+ if (entries.length === 0) return {};
108197
+ const sum = entries.reduce(
108198
+ (acc, e) => ({
108199
+ inputTokens: acc.inputTokens + e.inputTokens,
108200
+ outputTokens: acc.outputTokens + e.outputTokens,
108201
+ cacheReadTokens: acc.cacheReadTokens + (e.cacheReadTokens ?? 0),
108202
+ cacheWriteTokens: acc.cacheWriteTokens + (e.cacheWriteTokens ?? 0),
108203
+ costUsd: acc.costUsd + (e.costUsd ?? 0)
108204
+ }),
108205
+ { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: 0 }
108206
+ );
108207
+ const out = {};
108208
+ if (sum.inputTokens > 0) out.inputTokens = clampInt(sum.inputTokens, "inputTokens");
108209
+ if (sum.outputTokens > 0) out.outputTokens = clampInt(sum.outputTokens, "outputTokens");
108210
+ if (sum.cacheReadTokens > 0)
108211
+ out.cacheReadTokens = clampInt(sum.cacheReadTokens, "cacheReadTokens");
108212
+ if (sum.cacheWriteTokens > 0)
108213
+ out.cacheWriteTokens = clampInt(sum.cacheWriteTokens, "cacheWriteTokens");
108214
+ if (sum.costUsd > 0) out.costUsd = sum.costUsd;
108215
+ return out;
108216
+ }
108091
108217
 
108092
108218
  // node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
108093
108219
  var LIST_ITEM_MARKER = "-";
@@ -108444,6 +108570,126 @@ function resolveOptions(options) {
108444
108570
  };
108445
108571
  }
108446
108572
 
108573
+ // mcp/geminiSanitizer.ts
108574
+ function parseStringEnumBranch(item) {
108575
+ if (!item || typeof item !== "object") return null;
108576
+ const record3 = item;
108577
+ if (Array.isArray(record3.enum)) {
108578
+ const strings = record3.enum.filter((v) => typeof v === "string");
108579
+ return strings.length === record3.enum.length && strings.length > 0 ? { values: strings } : null;
108580
+ }
108581
+ if (typeof record3.const === "string") {
108582
+ return { values: [record3.const] };
108583
+ }
108584
+ return null;
108585
+ }
108586
+ function collapseStringUnion(branches) {
108587
+ const values = [];
108588
+ for (const item of branches) {
108589
+ const parsed2 = parseStringEnumBranch(item);
108590
+ if (!parsed2) return null;
108591
+ values.push(...parsed2.values);
108592
+ }
108593
+ if (values.length === 0) return null;
108594
+ return { type: "string", enum: [...new Set(values)] };
108595
+ }
108596
+ function sanitizeForGemini(schema2) {
108597
+ if (!schema2 || typeof schema2 !== "object") return schema2;
108598
+ if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
108599
+ const source = schema2;
108600
+ if (Array.isArray(source.enum) && typeof source.type !== "string") {
108601
+ const allStrings = source.enum.every((v) => typeof v === "string");
108602
+ if (allStrings) {
108603
+ const result = { type: "string", enum: source.enum };
108604
+ if (typeof source.description === "string") result.description = source.description;
108605
+ return result;
108606
+ }
108607
+ }
108608
+ for (const unionKey of ["anyOf", "oneOf"]) {
108609
+ const branches = source[unionKey];
108610
+ if (Array.isArray(branches) && branches.length > 0) {
108611
+ const collapsed = collapseStringUnion(branches);
108612
+ if (collapsed) {
108613
+ const result = { ...collapsed };
108614
+ if (typeof source.description === "string") result.description = source.description;
108615
+ return result;
108616
+ }
108617
+ }
108618
+ }
108619
+ if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
108620
+ const result = {};
108621
+ if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
108622
+ if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
108623
+ return result;
108624
+ }
108625
+ const sanitized = {};
108626
+ for (const [key, value2] of Object.entries(source)) {
108627
+ if (key === "$schema") continue;
108628
+ if (key === "$defs") {
108629
+ sanitized.definitions = sanitizeForGemini(value2);
108630
+ continue;
108631
+ }
108632
+ sanitized[key] = sanitizeForGemini(value2);
108633
+ }
108634
+ return sanitized;
108635
+ }
108636
+ function wrapJsonSchemaProducer(producer) {
108637
+ return new Proxy(producer, {
108638
+ get(target, prop, receiver) {
108639
+ const value2 = Reflect.get(target, prop, receiver);
108640
+ if ((prop === "input" || prop === "output") && typeof value2 === "function") {
108641
+ const fn2 = value2;
108642
+ return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
108643
+ }
108644
+ return value2;
108645
+ }
108646
+ });
108647
+ }
108648
+ function wrapStandard(standard) {
108649
+ return new Proxy(standard, {
108650
+ get(target, prop, receiver) {
108651
+ if (prop === "jsonSchema") {
108652
+ const value2 = Reflect.get(target, prop, receiver);
108653
+ if (value2 && typeof value2 === "object") {
108654
+ return wrapJsonSchemaProducer(value2);
108655
+ }
108656
+ return value2;
108657
+ }
108658
+ return Reflect.get(target, prop, receiver);
108659
+ }
108660
+ });
108661
+ }
108662
+ function wrapSchemaForGemini(schema2) {
108663
+ return new Proxy(schema2, {
108664
+ get(target, prop, receiver) {
108665
+ if (prop === "~standard") {
108666
+ const value2 = Reflect.get(target, prop, receiver);
108667
+ if (value2 && typeof value2 === "object") {
108668
+ return wrapStandard(value2);
108669
+ }
108670
+ return value2;
108671
+ }
108672
+ if (prop === "toJsonSchema") {
108673
+ const method = Reflect.get(target, prop, receiver);
108674
+ if (typeof method === "function") {
108675
+ return () => sanitizeForGemini(method.call(target));
108676
+ }
108677
+ return method;
108678
+ }
108679
+ return Reflect.get(target, prop, receiver);
108680
+ }
108681
+ });
108682
+ }
108683
+ function sanitizeToolForGemini(tool2) {
108684
+ if (!tool2.parameters) return tool2;
108685
+ return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
108686
+ }
108687
+ function isGeminiRouted(ctx) {
108688
+ const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
108689
+ if (!effective) return false;
108690
+ return effective.toLowerCase().includes("gemini");
108691
+ }
108692
+
108447
108693
  // mcp/shared.ts
108448
108694
  var tool = (toolDef) => toolDef;
108449
108695
  var handleToolSuccess = (data) => {
@@ -108479,9 +108725,10 @@ var execute = (fn2, toolName) => {
108479
108725
  };
108480
108726
  return _fn;
108481
108727
  };
108482
- var addTools = (_ctx, server, tools) => {
108728
+ var addTools = (ctx, server, tools) => {
108729
+ const shouldSanitize = isGeminiRouted(ctx);
108483
108730
  for (const tool2 of tools) {
108484
- server.addTool(tool2);
108731
+ server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
108485
108732
  }
108486
108733
  return server;
108487
108734
  };
@@ -108730,8 +108977,9 @@ function ReportProgressTool(ctx) {
108730
108977
  if (!params.target_plan_comment && ctx.toolState.todoTracker) {
108731
108978
  ctx.toolState.todoTracker.cancel();
108732
108979
  await ctx.toolState.todoTracker.settled();
108733
- ctx.toolState.todoTracker.completeInProgress();
108734
- const collapsible = ctx.toolState.todoTracker.renderCollapsible();
108980
+ const collapsible = ctx.toolState.todoTracker.renderCollapsible({
108981
+ completeInProgress: true
108982
+ });
108735
108983
  if (collapsible) {
108736
108984
  body = `${body}
108737
108985
 
@@ -109118,8 +109366,27 @@ import { performance as performance3 } from "node:perf_hooks";
109118
109366
 
109119
109367
  // utils/activity.ts
109120
109368
  import { performance as performance2 } from "node:perf_hooks";
109369
+ function isMonitorDebugEnabled() {
109370
+ return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
109371
+ }
109121
109372
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
109122
109373
  var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
109374
+ var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
109375
+ var ACTIVITY_NOISE_PATTERNS = [
109376
+ new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
109377
+ new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
109378
+ new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
109379
+ /^::debug::(?:spawn|process) activity /
109380
+ ];
109381
+ function isActivityNoise(chunk) {
109382
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
109383
+ if (!text.trim()) return true;
109384
+ return text.split("\n").every((line) => {
109385
+ const trimmed = line.trim();
109386
+ if (!trimmed) return true;
109387
+ return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
109388
+ });
109389
+ }
109123
109390
  var _lastActivity = performance2.now();
109124
109391
  function markActivity() {
109125
109392
  _lastActivity = performance2.now();
@@ -109129,7 +109396,9 @@ function getIdleMs() {
109129
109396
  }
109130
109397
  function wrapWrite(original, onActivity) {
109131
109398
  const wrapped = (chunk, encodingOrCb, cb) => {
109132
- onActivity();
109399
+ if (!isActivityNoise(chunk)) {
109400
+ onActivity();
109401
+ }
109133
109402
  if (typeof encodingOrCb === "function") {
109134
109403
  return original(chunk, encodingOrCb);
109135
109404
  }
@@ -109143,10 +109412,15 @@ function startProcessOutputMonitor(ctx) {
109143
109412
  const originalStderrWrite = process.stderr.write.bind(process.stderr);
109144
109413
  process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
109145
109414
  process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
109146
- log.debug(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
109415
+ const debugBypass = (msg) => {
109416
+ if (!isMonitorDebugEnabled()) return;
109417
+ originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
109418
+ `);
109419
+ };
109420
+ debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
109147
109421
  const intervalId = setInterval(() => {
109148
109422
  const idleMs = getIdleMs();
109149
- log.debug(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
109423
+ debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
109150
109424
  if (timedOut || idleMs <= ctx.timeoutMs) return;
109151
109425
  timedOut = true;
109152
109426
  ctx.onTimeout(idleMs);
@@ -109174,12 +109448,26 @@ function createProcessOutputActivityTimeout(ctx) {
109174
109448
  if (monitor) {
109175
109449
  monitor.stop();
109176
109450
  }
109177
- rejectFn(new Error(`activity timeout: no output for ${idleSec}s`));
109451
+ const reject = rejectFn;
109452
+ rejectFn = null;
109453
+ reject(new Error(`activity timeout: no output for ${idleSec}s`));
109178
109454
  }
109179
109455
  });
109180
109456
  return {
109181
109457
  promise: promise2,
109182
- stop: monitor.stop
109458
+ // stop() also disarms forceReject so a late safety-net fire can't reject
109459
+ // the promise after the run has already succeeded.
109460
+ stop: () => {
109461
+ monitor?.stop();
109462
+ rejectFn = null;
109463
+ },
109464
+ forceReject: (reason) => {
109465
+ if (!rejectFn) return;
109466
+ monitor?.stop();
109467
+ const reject = rejectFn;
109468
+ rejectFn = null;
109469
+ reject(new Error(reason));
109470
+ }
109183
109471
  };
109184
109472
  }
109185
109473
 
@@ -109209,6 +109497,16 @@ function exitWithSignal(signal) {
109209
109497
  }
109210
109498
 
109211
109499
  // utils/subprocess.ts
109500
+ var SPAWN_TIMEOUT_CODE = "E_SPAWN_TIMEOUT";
109501
+ var SPAWN_ACTIVITY_TIMEOUT_CODE = "E_SPAWN_ACTIVITY_TIMEOUT";
109502
+ var SpawnTimeoutError = class extends Error {
109503
+ code;
109504
+ constructor(message, code) {
109505
+ super(message);
109506
+ this.name = "SpawnTimeoutError";
109507
+ this.code = code;
109508
+ }
109509
+ };
109212
109510
  var activeChildren = /* @__PURE__ */ new Map();
109213
109511
  var externalSignalHandler = null;
109214
109512
  function trackChild(options) {
@@ -109254,7 +109552,7 @@ async function spawn(options) {
109254
109552
  const startTime = performance3.now();
109255
109553
  let stdoutBuffer = "";
109256
109554
  let stderrBuffer = "";
109257
- return new Promise((resolve2, reject) => {
109555
+ return new Promise((resolve3, reject) => {
109258
109556
  const child = nodeSpawn(options.cmd, options.args, {
109259
109557
  env: options.env || {
109260
109558
  PATH: process.env.PATH || "",
@@ -109265,15 +109563,17 @@ async function spawn(options) {
109265
109563
  });
109266
109564
  trackChild({ child });
109267
109565
  let timeoutId;
109566
+ let sigkillEscalatorId;
109268
109567
  let activityCheckIntervalId;
109269
109568
  let isTimedOut = false;
109270
109569
  let isActivityTimedOut = false;
109271
109570
  let lastActivityTime = performance3.now();
109571
+ let killedAtIdleMs;
109272
109572
  if (options.timeout) {
109273
109573
  timeoutId = setTimeout(() => {
109274
109574
  isTimedOut = true;
109275
109575
  child.kill("SIGTERM");
109276
- setTimeout(() => {
109576
+ sigkillEscalatorId = setTimeout(() => {
109277
109577
  if (!child.killed) {
109278
109578
  child.kill("SIGKILL");
109279
109579
  }
@@ -109291,12 +109591,20 @@ async function spawn(options) {
109291
109591
  );
109292
109592
  if (idleMs > activityTimeoutMs) {
109293
109593
  isActivityTimedOut = true;
109594
+ killedAtIdleMs = idleMs;
109294
109595
  const idleSec = Math.round(idleMs / 1e3);
109295
109596
  log.info(
109296
109597
  `no output for ${idleSec}s from pid=${child.pid} (${options.cmd}), killing process`
109297
109598
  );
109298
109599
  child.kill("SIGKILL");
109299
109600
  clearInterval(activityCheckIntervalId);
109601
+ try {
109602
+ options.onActivityTimeout?.();
109603
+ } catch (err) {
109604
+ log.debug(
109605
+ `spawn onActivityTimeout handler threw: ${err instanceof Error ? err.message : String(err)}`
109606
+ );
109607
+ }
109300
109608
  }
109301
109609
  }, DEFAULT_ACTIVITY_CHECK_INTERVAL_MS);
109302
109610
  }
@@ -109318,24 +109626,41 @@ async function spawn(options) {
109318
109626
  options.onStderr?.(chunk);
109319
109627
  });
109320
109628
  }
109321
- child.on("close", (exitCode) => {
109629
+ child.on("close", (exitCode, signal) => {
109322
109630
  const durationMs = performance3.now() - startTime;
109323
109631
  untrackChild(child);
109324
109632
  if (timeoutId) clearTimeout(timeoutId);
109633
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
109325
109634
  if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
109326
109635
  if (isTimedOut) {
109327
- reject(new Error(`process timed out after ${options.timeout}ms`));
109636
+ reject(
109637
+ new SpawnTimeoutError(`process timed out after ${options.timeout}ms`, SPAWN_TIMEOUT_CODE)
109638
+ );
109328
109639
  return;
109329
109640
  }
109330
109641
  if (isActivityTimedOut) {
109331
- const idleSec = Math.round((performance3.now() - lastActivityTime) / 1e3);
109332
- reject(new Error(`activity timeout: no output for ${idleSec}s`));
109642
+ const idleMs = killedAtIdleMs ?? performance3.now() - lastActivityTime;
109643
+ const idleSec = Math.round(idleMs / 1e3);
109644
+ reject(
109645
+ new SpawnTimeoutError(
109646
+ `activity timeout: no output for ${idleSec}s`,
109647
+ SPAWN_ACTIVITY_TIMEOUT_CODE
109648
+ )
109649
+ );
109333
109650
  return;
109334
109651
  }
109335
- resolve2({
109652
+ let resolvedExitCode = exitCode ?? 0;
109653
+ let resolvedStderr = stderrBuffer;
109654
+ if (exitCode === null && signal) {
109655
+ const killMsg = `[spawn] ${options.cmd}: killed by signal ${signal}`;
109656
+ resolvedStderr = resolvedStderr ? `${resolvedStderr}
109657
+ ${killMsg}` : killMsg;
109658
+ resolvedExitCode = 1;
109659
+ }
109660
+ resolve3({
109336
109661
  stdout: stdoutBuffer,
109337
- stderr: stderrBuffer,
109338
- exitCode: exitCode || 0,
109662
+ stderr: resolvedStderr,
109663
+ exitCode: resolvedExitCode,
109339
109664
  durationMs
109340
109665
  });
109341
109666
  });
@@ -109343,9 +109668,13 @@ async function spawn(options) {
109343
109668
  const durationMs = performance3.now() - startTime;
109344
109669
  untrackChild(child);
109345
109670
  if (timeoutId) clearTimeout(timeoutId);
109671
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
109346
109672
  if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
109347
- console.error(`[spawn] process spawn error: ${error49.message}`);
109348
- resolve2({
109673
+ const errMsg = `[spawn] ${options.cmd}: ${error49.message}`;
109674
+ console.error(errMsg);
109675
+ stderrBuffer = stderrBuffer ? `${stderrBuffer}
109676
+ ${errMsg}` : errMsg;
109677
+ resolve3({
109349
109678
  stdout: stdoutBuffer,
109350
109679
  stderr: stderrBuffer,
109351
109680
  exitCode: 1,
@@ -114112,7 +114441,7 @@ var Protocol = class {
114112
114441
  return;
114113
114442
  }
114114
114443
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
114115
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
114444
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
114116
114445
  options?.signal?.throwIfAborted();
114117
114446
  }
114118
114447
  } catch (error49) {
@@ -114129,7 +114458,7 @@ var Protocol = class {
114129
114458
  */
114130
114459
  request(request2, resultSchema, options) {
114131
114460
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
114132
- return new Promise((resolve2, reject) => {
114461
+ return new Promise((resolve3, reject) => {
114133
114462
  const earlyReject = (error49) => {
114134
114463
  reject(error49);
114135
114464
  };
@@ -114207,7 +114536,7 @@ var Protocol = class {
114207
114536
  if (!parseResult.success) {
114208
114537
  reject(parseResult.error);
114209
114538
  } else {
114210
- resolve2(parseResult.data);
114539
+ resolve3(parseResult.data);
114211
114540
  }
114212
114541
  } catch (error49) {
114213
114542
  reject(error49);
@@ -114468,12 +114797,12 @@ var Protocol = class {
114468
114797
  }
114469
114798
  } catch {
114470
114799
  }
114471
- return new Promise((resolve2, reject) => {
114800
+ return new Promise((resolve3, reject) => {
114472
114801
  if (signal.aborted) {
114473
114802
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
114474
114803
  return;
114475
114804
  }
114476
- const timeoutId = setTimeout(resolve2, interval);
114805
+ const timeoutId = setTimeout(resolve3, interval);
114477
114806
  signal.addEventListener("abort", () => {
114478
114807
  clearTimeout(timeoutId);
114479
114808
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -115343,12 +115672,12 @@ var StdioServerTransport = class {
115343
115672
  this.onclose?.();
115344
115673
  }
115345
115674
  send(message) {
115346
- return new Promise((resolve2) => {
115675
+ return new Promise((resolve3) => {
115347
115676
  const json4 = serializeMessage(message);
115348
115677
  if (this._stdout.write(json4)) {
115349
- resolve2();
115678
+ resolve3();
115350
115679
  } else {
115351
- this._stdout.once("drain", resolve2);
115680
+ this._stdout.once("drain", resolve3);
115352
115681
  }
115353
115682
  });
115354
115683
  }
@@ -136376,12 +136705,12 @@ var require_schemes2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136376
136705
  var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136377
136706
  const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils5();
136378
136707
  const { SCHEMES, getSchemeHandler } = require_schemes2();
136379
- function normalize2(uri$2, options) {
136708
+ function normalize3(uri$2, options) {
136380
136709
  if (typeof uri$2 === "string") uri$2 = serialize(parse5(uri$2, options), options);
136381
136710
  else if (typeof uri$2 === "object") uri$2 = parse5(serialize(uri$2, options), options);
136382
136711
  return uri$2;
136383
136712
  }
136384
- function resolve2(baseURI, relativeURI, options) {
136713
+ function resolve3(baseURI, relativeURI, options) {
136385
136714
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
136386
136715
  const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
136387
136716
  schemelessOptions.skipEscape = true;
@@ -136554,8 +136883,8 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136554
136883
  }
136555
136884
  const fastUri = {
136556
136885
  SCHEMES,
136557
- normalize: normalize2,
136558
- resolve: resolve2,
136886
+ normalize: normalize3,
136887
+ resolve: resolve3,
136559
136888
  resolveComponent,
136560
136889
  equal: equal$1,
136561
136890
  serialize,
@@ -140143,7 +140472,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140143
140472
  new Error(`Connection is in ${this.#connectionState} state`)
140144
140473
  );
140145
140474
  }
140146
- return new Promise((resolve2, reject) => {
140475
+ return new Promise((resolve3, reject) => {
140147
140476
  const timeout = setTimeout(() => {
140148
140477
  reject(
140149
140478
  new Error(
@@ -140153,7 +140482,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140153
140482
  }, 5e3);
140154
140483
  this.once("ready", () => {
140155
140484
  clearTimeout(timeout);
140156
- resolve2();
140485
+ resolve3();
140157
140486
  });
140158
140487
  this.once("error", (event) => {
140159
140488
  clearTimeout(timeout);
@@ -140600,7 +140929,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140600
140929
  }
140601
140930
  });
140602
140931
  if (this.#needsEventLoopFlush) {
140603
- await new Promise((resolve2) => setImmediate(resolve2));
140932
+ await new Promise((resolve3) => setImmediate(resolve3));
140604
140933
  }
140605
140934
  } catch (progressError) {
140606
140935
  this.#logger.warn(
@@ -140658,7 +140987,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140658
140987
  }
140659
140988
  });
140660
140989
  if (this.#needsEventLoopFlush) {
140661
- await new Promise((resolve2) => setImmediate(resolve2));
140990
+ await new Promise((resolve3) => setImmediate(resolve3));
140662
140991
  }
140663
140992
  } catch (streamError) {
140664
140993
  this.#logger.warn(
@@ -141671,7 +142000,7 @@ function formatMcpToolRef(agentId, toolName) {
141671
142000
  }
141672
142001
 
141673
142002
  // utils/browser.ts
141674
- import { execFileSync, spawnSync } from "node:child_process";
142003
+ import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
141675
142004
  import { existsSync as existsSync4 } from "node:fs";
141676
142005
  import { dirname } from "node:path";
141677
142006
 
@@ -141686,12 +142015,77 @@ var SENSITIVE_PATTERNS = [
141686
142015
  function isSensitiveEnvName(key) {
141687
142016
  return SENSITIVE_PATTERNS.some((p) => p.test(key));
141688
142017
  }
142018
+ var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
142019
+ var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
142020
+ // system
142021
+ "CI",
142022
+ "HOME",
142023
+ "LANG",
142024
+ "LOGNAME",
142025
+ "PATH",
142026
+ "SHELL",
142027
+ "SHLVL",
142028
+ "TERM",
142029
+ "TMPDIR",
142030
+ "TZ",
142031
+ "USER",
142032
+ "XDG_CONFIG_HOME",
142033
+ "XDG_RUNTIME_DIR",
142034
+ "DEBIAN_FRONTEND",
142035
+ // runner image toolchain
142036
+ "ACCEPT_EULA",
142037
+ "AGENT_TOOLSDIRECTORY",
142038
+ "ANDROID_HOME",
142039
+ "ANDROID_NDK",
142040
+ "ANDROID_NDK_HOME",
142041
+ "ANDROID_NDK_LATEST_HOME",
142042
+ "ANDROID_NDK_ROOT",
142043
+ "ANDROID_SDK_ROOT",
142044
+ "ANT_HOME",
142045
+ "AZURE_EXTENSION_DIR",
142046
+ "BOOTSTRAP_HASKELL_NONINTERACTIVE",
142047
+ "CHROME_BIN",
142048
+ "CHROMEWEBDRIVER",
142049
+ "CONDA",
142050
+ "DOTNET_MULTILEVEL_LOOKUP",
142051
+ "DOTNET_NOLOGO",
142052
+ "DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
142053
+ "EDGEWEBDRIVER",
142054
+ "GECKOWEBDRIVER",
142055
+ "GHCUP_INSTALL_BASE_PREFIX",
142056
+ "GRADLE_HOME",
142057
+ "JAVA_HOME",
142058
+ "HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
142059
+ "HOMEBREW_NO_AUTO_UPDATE",
142060
+ "ImageOS",
142061
+ "ImageVersion",
142062
+ "NVM_DIR",
142063
+ "PIPX_BIN_DIR",
142064
+ "PIPX_HOME",
142065
+ "PSModulePath",
142066
+ "SELENIUM_JAR_PATH",
142067
+ "SGX_AESM_ADDR",
142068
+ "SWIFT_PATH",
142069
+ "VCPKG_INSTALLATION_ROOT"
142070
+ ]);
142071
+ var _userAllowlist = null;
142072
+ function setEnvAllowlist(raw2) {
142073
+ const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
142074
+ _userAllowlist = new Set(names);
142075
+ }
142076
+ function isSafeEnvVar(key) {
142077
+ if (SAFE_ENV_NAMES.has(key)) return true;
142078
+ return SAFE_ENV_PREFIXES.some((p) => key.startsWith(p));
142079
+ }
141689
142080
  function filterEnv() {
141690
142081
  const filtered = {};
141691
142082
  for (const [key, value2] of Object.entries(process.env)) {
141692
142083
  if (value2 === void 0) continue;
141693
- if (isSensitiveEnvName(key)) continue;
141694
- filtered[key] = value2;
142084
+ const userAllowed = _userAllowlist?.has(key) ?? false;
142085
+ if (isSensitiveEnvName(key) && !userAllowed) continue;
142086
+ if (isSafeEnvVar(key) || userAllowed) {
142087
+ filtered[key] = value2;
142088
+ }
141695
142089
  }
141696
142090
  return filtered;
141697
142091
  }
@@ -141711,7 +142105,7 @@ var import_semver = __toESM(require_semver2(), 1);
141711
142105
  // package.json
141712
142106
  var package_default = {
141713
142107
  name: "pullfrog",
141714
- version: "0.0.201",
142108
+ version: "0.0.202",
141715
142109
  type: "module",
141716
142110
  bin: {
141717
142111
  pullfrog: "dist/cli.mjs",
@@ -141723,6 +142117,7 @@ var package_default = {
141723
142117
  ],
141724
142118
  scripts: {
141725
142119
  test: "vitest",
142120
+ "test:catalog": "vitest run --config vitest.main.config.ts",
141726
142121
  typecheck: "tsc --noEmit",
141727
142122
  build: "node esbuild.config.js && tsc -p tsconfig.exports.json",
141728
142123
  "check:entrypoints": "node scripts/check-entrypoint-imports.ts",
@@ -141735,7 +142130,7 @@ var package_default = {
141735
142130
  },
141736
142131
  devDependencies: {
141737
142132
  "@actions/core": "^1.11.1",
141738
- "@anthropic-ai/claude-code": "2.1.85",
142133
+ "@anthropic-ai/claude-code": "2.1.112",
141739
142134
  "@ark/fs": "0.56.0",
141740
142135
  "@ark/util": "0.56.0",
141741
142136
  "@clack/prompts": "^1.2.0",
@@ -141859,7 +142254,7 @@ function ensureBrowserDaemon(toolState) {
141859
142254
  log.info("agent-browser installed");
141860
142255
  let binDir;
141861
142256
  try {
141862
- const binPath = execFileSync("which", ["agent-browser"], { encoding: "utf-8" }).trim();
142257
+ const binPath = execFileSync2("which", ["agent-browser"], { encoding: "utf-8" }).trim();
141863
142258
  binDir = dirname(binPath);
141864
142259
  log.info(`agent-browser binary: ${binPath}`);
141865
142260
  } catch {
@@ -141906,9 +142301,271 @@ function closeBrowserDaemon(toolState) {
141906
142301
  }
141907
142302
 
141908
142303
  // mcp/checkout.ts
142304
+ import { createHash as createHash2 } from "node:crypto";
141909
142305
  import { writeFileSync } from "node:fs";
141910
142306
  import { join as join3 } from "node:path";
141911
142307
 
142308
+ // utils/diffCoverage.ts
142309
+ import { isAbsolute, normalize as normalize2, resolve } from "node:path";
142310
+ function countLines(params) {
142311
+ const content = params.content;
142312
+ if (content.length === 0) return 0;
142313
+ return content.split("\n").length;
142314
+ }
142315
+ function parseDiffTocEntries(params) {
142316
+ const lines = params.toc.split("\n");
142317
+ const entries = [];
142318
+ for (const line of lines) {
142319
+ const match3 = line.match(/^- (.+) (?:→|->) lines (\d+)-(\d+)(?: · diff-[0-9a-f]+)?$/);
142320
+ if (!match3) continue;
142321
+ const startLine = Number.parseInt(match3[2], 10);
142322
+ const endLine = Number.parseInt(match3[3], 10);
142323
+ if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) continue;
142324
+ entries.push({ filename: match3[1], startLine, endLine });
142325
+ }
142326
+ return entries;
142327
+ }
142328
+ function createDiffCoverageState(params) {
142329
+ return {
142330
+ diffPath: params.diffPath,
142331
+ totalLines: params.totalLines,
142332
+ tocEntries: parseDiffTocEntries({ toc: params.toc }),
142333
+ coveredRanges: [],
142334
+ coveragePreflightRan: false
142335
+ };
142336
+ }
142337
+ function recordDiffReadFromToolUse(params) {
142338
+ const state = params.state;
142339
+ if (!state) return false;
142340
+ if (!isReadTool(params.toolName)) return false;
142341
+ const readTarget = extractReadTarget({ input: params.input });
142342
+ if (!readTarget) return false;
142343
+ const normalizedReadPath = normalizePath({ path: readTarget.path, cwd: params.cwd });
142344
+ const normalizedDiffPath = normalize2(state.diffPath);
142345
+ if (normalizedReadPath !== normalizedDiffPath) return false;
142346
+ const range2 = resolveReadRange({
142347
+ totalLines: state.totalLines,
142348
+ offset: readTarget.offset,
142349
+ limit: readTarget.limit,
142350
+ startLine: readTarget.startLine,
142351
+ endLine: readTarget.endLine,
142352
+ offsetBase: resolveOffsetBase({ toolName: params.toolName })
142353
+ });
142354
+ if (!range2) return false;
142355
+ state.coveredRanges = mergeRanges({ ranges: state.coveredRanges, nextRange: range2 });
142356
+ return true;
142357
+ }
142358
+ function getDiffCoverageBreakdown(params) {
142359
+ const state = params.state;
142360
+ const coveredRanges = mergeRangesList({ ranges: state.coveredRanges });
142361
+ const unreadRanges = invertRanges({ totalLines: state.totalLines, coveredRanges });
142362
+ const coveredLines = countLinesInRanges({ ranges: coveredRanges });
142363
+ const unreadLines = Math.max(0, state.totalLines - coveredLines);
142364
+ const coveragePercent = state.totalLines ? Number((coveredLines / state.totalLines * 100).toFixed(2)) : 100;
142365
+ const files = [];
142366
+ for (const entry of state.tocEntries) {
142367
+ const fileRange = { startLine: entry.startLine, endLine: entry.endLine };
142368
+ const coveredInFile = intersectRangesWithRange({ ranges: coveredRanges, target: fileRange });
142369
+ const unreadInFile = intersectRangesWithRange({ ranges: unreadRanges, target: fileRange });
142370
+ const totalFileLines = Math.max(0, entry.endLine - entry.startLine + 1);
142371
+ const fileCoveredLines = countLinesInRanges({ ranges: coveredInFile });
142372
+ files.push({
142373
+ filename: entry.filename,
142374
+ startLine: entry.startLine,
142375
+ endLine: entry.endLine,
142376
+ totalLines: totalFileLines,
142377
+ coveredLines: fileCoveredLines,
142378
+ coveredRanges: coveredInFile,
142379
+ unreadRanges: unreadInFile
142380
+ });
142381
+ }
142382
+ return {
142383
+ totalLines: state.totalLines,
142384
+ coveredLines,
142385
+ unreadLines,
142386
+ coveragePercent,
142387
+ coveredRanges,
142388
+ unreadRanges,
142389
+ files
142390
+ };
142391
+ }
142392
+ function renderDiffCoverageBreakdown(params) {
142393
+ const breakdown = params.breakdown;
142394
+ const lines = [];
142395
+ lines.push(`diff coverage report for \`${params.diffPath}\``);
142396
+ lines.push(
142397
+ `overall: ${breakdown.coveredLines}/${breakdown.totalLines} lines read (${breakdown.coveragePercent}%), unread: ${breakdown.unreadLines}`
142398
+ );
142399
+ lines.push(`covered ranges: ${formatRanges({ ranges: breakdown.coveredRanges })}`);
142400
+ lines.push(`unread ranges: ${formatRanges({ ranges: breakdown.unreadRanges })}`);
142401
+ lines.push("");
142402
+ lines.push("per-file TOC coverage:");
142403
+ for (const file2 of breakdown.files) {
142404
+ const filePercent = file2.totalLines ? Number((file2.coveredLines / file2.totalLines * 100).toFixed(2)) : 100;
142405
+ lines.push(
142406
+ `- ${file2.filename} (toc lines ${file2.startLine}-${file2.endLine}): ${file2.coveredLines}/${file2.totalLines} lines read (${filePercent}%)`
142407
+ );
142408
+ lines.push(` read: ${formatRanges({ ranges: file2.coveredRanges })}`);
142409
+ lines.push(` unread: ${formatRanges({ ranges: file2.unreadRanges })}`);
142410
+ }
142411
+ return lines.join("\n");
142412
+ }
142413
+ function resolveOffsetBase(params) {
142414
+ const lower2 = params.toolName.toLowerCase();
142415
+ if (lower2 === "readfile" || lower2.endsWith(".readfile")) {
142416
+ return "one";
142417
+ }
142418
+ return "zero";
142419
+ }
142420
+ function isReadTool(toolName) {
142421
+ const lower2 = toolName.toLowerCase();
142422
+ if (lower2 === "read" || lower2 === "readfile") return true;
142423
+ if (lower2.endsWith(".read") || lower2.endsWith(".readfile")) return true;
142424
+ return false;
142425
+ }
142426
+ function extractReadTarget(params) {
142427
+ const inputRecord = asRecord(params.input);
142428
+ if (!inputRecord) return null;
142429
+ const direct = extractReadTargetFromRecord({ record: inputRecord });
142430
+ if (direct) return direct;
142431
+ const nestedCandidates = [inputRecord.args, inputRecord.params, inputRecord.input];
142432
+ for (const candidate of nestedCandidates) {
142433
+ const nestedRecord = asRecord(candidate);
142434
+ if (!nestedRecord) continue;
142435
+ const nested = extractReadTargetFromRecord({ record: nestedRecord });
142436
+ if (nested) return nested;
142437
+ }
142438
+ return null;
142439
+ }
142440
+ function extractReadTargetFromRecord(params) {
142441
+ const record3 = params.record;
142442
+ const pathValue = readString({ value: record3.path }) ?? readString({ value: record3.file_path }) ?? readString({ value: record3.filePath }) ?? readString({ value: record3.filepath }) ?? readString({ value: record3.file }) ?? readString({ value: record3.target_file });
142443
+ if (!pathValue) return null;
142444
+ const offset = readNumber({ value: record3.offset });
142445
+ const limit = readNumber({ value: record3.limit });
142446
+ const startLine = readNumber({ value: record3.start_line }) ?? readNumber({ value: record3.startLine }) ?? readNumber({ value: record3.line_start });
142447
+ const endLine = readNumber({ value: record3.end_line }) ?? readNumber({ value: record3.endLine }) ?? readNumber({ value: record3.line_end });
142448
+ return { path: pathValue, offset, limit, startLine, endLine };
142449
+ }
142450
+ function resolveReadRange(params) {
142451
+ const totalLines = params.totalLines;
142452
+ if (totalLines <= 0) return null;
142453
+ if (params.startLine !== void 0 || params.endLine !== void 0) {
142454
+ const rawStart = params.startLine ?? 1;
142455
+ const rawEnd = params.endLine ?? totalLines;
142456
+ const startLine2 = clampLine({ value: rawStart, totalLines });
142457
+ const endLine2 = clampLine({ value: rawEnd, totalLines });
142458
+ if (endLine2 < startLine2) return null;
142459
+ return { startLine: startLine2, endLine: endLine2 };
142460
+ }
142461
+ let startLine = 1;
142462
+ if (params.offset !== void 0) {
142463
+ if (params.offset >= 0) {
142464
+ const normalizedOffset = params.offsetBase === "zero" ? params.offset + 1 : params.offset === 0 ? 1 : params.offset;
142465
+ startLine = clampLine({ value: normalizedOffset, totalLines });
142466
+ } else {
142467
+ startLine = clampLine({ value: totalLines + params.offset + 1, totalLines });
142468
+ }
142469
+ }
142470
+ let endLine = totalLines;
142471
+ if (params.limit !== void 0) {
142472
+ if (params.limit <= 0) return null;
142473
+ endLine = clampLine({ value: startLine + params.limit - 1, totalLines });
142474
+ }
142475
+ if (endLine < startLine) return null;
142476
+ return { startLine, endLine };
142477
+ }
142478
+ function normalizePath(params) {
142479
+ if (isAbsolute(params.path)) return normalize2(params.path);
142480
+ return normalize2(resolve(params.cwd, params.path));
142481
+ }
142482
+ function mergeRanges(params) {
142483
+ return mergeRangesList({ ranges: [...params.ranges, params.nextRange] });
142484
+ }
142485
+ function mergeRangesList(params) {
142486
+ if (params.ranges.length === 0) return [];
142487
+ const sorted = [...params.ranges].sort((a, b) => a.startLine - b.startLine);
142488
+ const merged = [];
142489
+ for (const range2 of sorted) {
142490
+ const last = merged[merged.length - 1];
142491
+ if (!last) {
142492
+ merged.push({ startLine: range2.startLine, endLine: range2.endLine });
142493
+ continue;
142494
+ }
142495
+ if (range2.startLine <= last.endLine + 1) {
142496
+ if (range2.endLine > last.endLine) {
142497
+ last.endLine = range2.endLine;
142498
+ }
142499
+ continue;
142500
+ }
142501
+ merged.push({ startLine: range2.startLine, endLine: range2.endLine });
142502
+ }
142503
+ return merged;
142504
+ }
142505
+ function invertRanges(params) {
142506
+ if (params.totalLines <= 0) return [];
142507
+ if (params.coveredRanges.length === 0) {
142508
+ return [{ startLine: 1, endLine: params.totalLines }];
142509
+ }
142510
+ const unread = [];
142511
+ let cursor = 1;
142512
+ for (const range2 of params.coveredRanges) {
142513
+ if (cursor < range2.startLine) {
142514
+ unread.push({ startLine: cursor, endLine: range2.startLine - 1 });
142515
+ }
142516
+ cursor = Math.max(cursor, range2.endLine + 1);
142517
+ }
142518
+ if (cursor <= params.totalLines) {
142519
+ unread.push({ startLine: cursor, endLine: params.totalLines });
142520
+ }
142521
+ return unread;
142522
+ }
142523
+ function intersectRangesWithRange(params) {
142524
+ const intersections = [];
142525
+ for (const range2 of params.ranges) {
142526
+ if (range2.endLine < params.target.startLine) continue;
142527
+ if (range2.startLine > params.target.endLine) continue;
142528
+ const startLine = Math.max(range2.startLine, params.target.startLine);
142529
+ const endLine = Math.min(range2.endLine, params.target.endLine);
142530
+ if (endLine >= startLine) {
142531
+ intersections.push({ startLine, endLine });
142532
+ }
142533
+ }
142534
+ return intersections;
142535
+ }
142536
+ function countLinesInRanges(params) {
142537
+ let total = 0;
142538
+ for (const range2 of params.ranges) {
142539
+ total += range2.endLine - range2.startLine + 1;
142540
+ }
142541
+ return total;
142542
+ }
142543
+ function formatRanges(params) {
142544
+ if (params.ranges.length === 0) return "none";
142545
+ return params.ranges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
142546
+ }
142547
+ function clampLine(params) {
142548
+ if (params.value < 1) return 1;
142549
+ if (params.value > params.totalLines) return params.totalLines;
142550
+ return params.value;
142551
+ }
142552
+ function asRecord(value2) {
142553
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
142554
+ return Object.fromEntries(Object.entries(value2));
142555
+ }
142556
+ function readString(params) {
142557
+ if (typeof params.value === "string") return params.value;
142558
+ return void 0;
142559
+ }
142560
+ function readNumber(params) {
142561
+ if (typeof params.value === "number" && Number.isFinite(params.value)) return params.value;
142562
+ if (typeof params.value === "string") {
142563
+ const parsed2 = Number.parseInt(params.value, 10);
142564
+ if (Number.isFinite(parsed2)) return parsed2;
142565
+ }
142566
+ return void 0;
142567
+ }
142568
+
141912
142569
  // utils/gitAuth.ts
141913
142570
  import { execSync } from "node:child_process";
141914
142571
  import { createHash } from "node:crypto";
@@ -141922,7 +142579,7 @@ function resolveGit() {
141922
142579
  const resolvedPath = realpathSync(whichPath);
141923
142580
  const sha256 = hashFile(resolvedPath);
141924
142581
  gitBinary = { path: resolvedPath, sha256 };
141925
- log.info(`git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
142582
+ log.debug(`\xBB git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
141926
142583
  }
141927
142584
  function verifyGitBinary() {
141928
142585
  if (!gitBinary) {
@@ -142001,29 +142658,43 @@ async function $git(subcommand, args2, options) {
142001
142658
  }
142002
142659
 
142003
142660
  // lifecycle.ts
142004
- var LIFECYCLE_HOOK_TIMEOUT_MS = 12e4;
142661
+ var LIFECYCLE_HOOK_TIMEOUT_MS = 6e5;
142005
142662
 
142006
142663
  // utils/lifecycle.ts
142007
142664
  async function executeLifecycleHook(params) {
142008
- if (!params.script) return;
142665
+ if (!params.script) return {};
142009
142666
  log.info(`\xBB executing ${params.event} lifecycle hook...`);
142010
- const result = await spawn({
142011
- cmd: "bash",
142012
- args: ["-c", params.script],
142013
- env: process.env,
142014
- timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
142015
- activityTimeout: 0,
142016
- onStdout: (chunk) => process.stdout.write(chunk),
142017
- onStderr: (chunk) => process.stderr.write(chunk)
142018
- });
142019
- if (result.exitCode !== 0) {
142020
- const output = result.stderr || result.stdout;
142021
- throw new Error(
142022
- `lifecycle hook '${params.event}' failed with exit code ${result.exitCode}:
142023
- ${output}`
142024
- );
142667
+ try {
142668
+ const result = await spawn({
142669
+ cmd: "bash",
142670
+ args: ["-c", params.script],
142671
+ env: process.env,
142672
+ timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
142673
+ activityTimeout: 0,
142674
+ onStdout: (chunk) => process.stdout.write(chunk),
142675
+ onStderr: (chunk) => process.stderr.write(chunk)
142676
+ });
142677
+ if (result.exitCode !== 0) {
142678
+ const output = (result.stderr || result.stdout).trim();
142679
+ return {
142680
+ warning: `lifecycle hook '${params.event}' failed with exit code ${result.exitCode}. output: ${output || "(empty)"}. retry the operation if the failure looks flaky (network blips, transient rate limits). do NOT retry if the script is broken (missing commands, syntax errors) or the error is persistent.`
142681
+ };
142682
+ }
142683
+ log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
142684
+ return {};
142685
+ } catch (err) {
142686
+ const isTimeout = err instanceof SpawnTimeoutError && (err.code === SPAWN_TIMEOUT_CODE || err.code === SPAWN_ACTIVITY_TIMEOUT_CODE);
142687
+ if (isTimeout) {
142688
+ const minutes = Math.round(LIFECYCLE_HOOK_TIMEOUT_MS / 6e4);
142689
+ return {
142690
+ warning: `lifecycle hook '${params.event}' timed out after ${minutes}min. do NOT retry \u2014 the script is likely hung or doing too much work. ask the repo owner to simplify the hook (e.g. move long-running work out of the hook, add caching, or split it).`
142691
+ };
142692
+ }
142693
+ const msg = err instanceof Error ? err.message : String(err);
142694
+ return {
142695
+ warning: `lifecycle hook '${params.event}' failed to spawn: ${msg}. this is likely a transient failure \u2014 retry the operation.`
142696
+ };
142025
142697
  }
142026
- log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
142027
142698
  }
142028
142699
 
142029
142700
  // utils/shell.ts
@@ -142173,6 +142844,787 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
142173
142844
  return hasChanges ? out : null;
142174
142845
  }
142175
142846
 
142847
+ // mcp/git.ts
142848
+ function getPushDestination(branch, storedDest) {
142849
+ if (storedDest && storedDest.localBranch === branch) {
142850
+ log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
142851
+ const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
142852
+ log: false
142853
+ }).trim();
142854
+ return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
142855
+ }
142856
+ try {
142857
+ const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
142858
+ const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
142859
+ const remoteBranch = merge4.replace(/^refs\/heads\//, "");
142860
+ const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
142861
+ return { remoteName: pushRemote, remoteBranch, url: url4 };
142862
+ } catch {
142863
+ log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
142864
+ const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
142865
+ return { remoteName: "origin", remoteBranch: branch, url: url4 };
142866
+ }
142867
+ }
142868
+ function normalizeUrl(url4) {
142869
+ return url4.replace(/\.git$/, "").toLowerCase();
142870
+ }
142871
+ function rejectIfLeadingDash(value2, kind) {
142872
+ if (value2.startsWith("-")) {
142873
+ throw new Error(`Blocked: ${kind} '${value2}' starts with '-' \u2014 git could parse it as a flag.`);
142874
+ }
142875
+ }
142876
+ var SYMBOLIC_REFS = /* @__PURE__ */ new Set(["HEAD", "FETCH_HEAD", "ORIG_HEAD", "MERGE_HEAD"]);
142877
+ function rejectSpecialRef(value2, kind) {
142878
+ rejectIfLeadingDash(value2, kind);
142879
+ if (value2.startsWith("refs/")) {
142880
+ throw new Error(
142881
+ `Blocked: ${kind} '${value2}' is a fully-qualified ref path. Use a bare branch name (e.g. 'feature/foo' or 'main'), not a 'refs/heads/...' form.`
142882
+ );
142883
+ }
142884
+ if (SYMBOLIC_REFS.has(value2)) {
142885
+ throw new Error(
142886
+ `Blocked: ${kind} '${value2}' is a git symbolic ref, not a branch name. Pass the resolved branch name (e.g. 'main'), or omit branchName to push the current branch.`
142887
+ );
142888
+ }
142889
+ const BAD = /[:+^~?*[\\\s]/;
142890
+ const badMatch = value2.match(BAD);
142891
+ if (badMatch) {
142892
+ throw new Error(
142893
+ `Blocked: ${kind} '${value2}' contains '${badMatch[0]}', which git interprets as refspec/revision syntax, not as part of a branch name.`
142894
+ );
142895
+ }
142896
+ }
142897
+ function validateTagName(tag) {
142898
+ rejectIfLeadingDash(tag, "tag");
142899
+ if (!/^[A-Za-z0-9._/-]+$/.test(tag)) {
142900
+ throw new Error(
142901
+ `Blocked: tag '${tag}' contains characters that could be parsed as a refspec or flag. Tags must match [A-Za-z0-9._/-]+.`
142902
+ );
142903
+ }
142904
+ }
142905
+ function validatePushDestination(ctx, branch) {
142906
+ const pushUrl = ctx.toolState.pushUrl;
142907
+ if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
142908
+ const dest = getPushDestination(branch, ctx.toolState.pushDest);
142909
+ if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
142910
+ throw new Error(
142911
+ `Push blocked: destination does not match expected repository.
142912
+ Expected: ${pushUrl}
142913
+ Actual: ${dest.url}
142914
+ Git configuration may have been tampered with.`
142915
+ );
142916
+ }
142917
+ return dest;
142918
+ }
142919
+ var PushBranch = type({
142920
+ branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
142921
+ force: type.boolean.describe("Force push (use with caution)").default(false)
142922
+ });
142923
+ function PushBranchTool(ctx) {
142924
+ const defaultBranch = ctx.repo.data.default_branch || "main";
142925
+ const pushPermission = ctx.payload.push;
142926
+ return tool({
142927
+ name: "push_branch",
142928
+ description: "Push the current branch to the remote repository. Omit branchName to push the current branch (recommended). If specifying branchName, use the LOCAL branch name (e.g., 'pr-1'), not the remote branch name. The correct remote and remote branch are determined automatically from branch config set by checkout_pr. Requires a clean working tree. Runs the repository prepush hook (if configured) before the network push \u2014 hook failure means tests/lint or similar in that script failed, not necessarily a Pullfrog timeout. Never force push unless explicitly requested. Pushes to the default branch are blocked in restricted mode.",
142929
+ parameters: PushBranch,
142930
+ execute: execute(async ({ branchName, force }) => {
142931
+ if (pushPermission === "disabled") {
142932
+ throw new Error("Push is disabled. This repository is configured for read-only access.");
142933
+ }
142934
+ const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
142935
+ rejectSpecialRef(branch, "branch");
142936
+ const status = $("git", ["status", "--porcelain"], { log: false });
142937
+ if (status) {
142938
+ throw new Error(
142939
+ `push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
142940
+
142941
+ git status:
142942
+ ${status}`
142943
+ );
142944
+ }
142945
+ const pushDest = validatePushDestination(ctx, branch);
142946
+ if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
142947
+ throw new Error(
142948
+ `Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
142949
+ );
142950
+ }
142951
+ const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
142952
+ const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
142953
+ const prepushHook = await executeLifecycleHook({
142954
+ event: "prepush",
142955
+ script: ctx.prepushScript
142956
+ });
142957
+ if (prepushHook.warning) {
142958
+ throw new Error(prepushHook.warning);
142959
+ }
142960
+ const postHookStatus = $("git", ["status", "--porcelain"], { log: false });
142961
+ if (postHookStatus) {
142962
+ throw new Error(
142963
+ `push blocked: the prepush hook modified the working tree. those changes are not included in the push. commit or discard them (or change the hook to not mutate tracked files) before retrying.
142964
+
142965
+ git status:
142966
+ ${postHookStatus}`
142967
+ );
142968
+ }
142969
+ log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
142970
+ if (force) {
142971
+ log.warning(`force pushing - this will overwrite remote history`);
142972
+ }
142973
+ try {
142974
+ await $git("push", pushArgs, {
142975
+ token: ctx.gitToken
142976
+ });
142977
+ } catch (err) {
142978
+ const msg = err instanceof Error ? err.message : String(err);
142979
+ if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
142980
+ const integrateStep = ctx.payload.shell === "disabled" ? `2. use the git tool to merge the remote branch into yours: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] })` : `2. use the git tool to rebase or merge your changes on top: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] }) (or 'rebase')`;
142981
+ throw new Error(
142982
+ `push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
142983
+
142984
+ to resolve this:
142985
+ 1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
142986
+ ${integrateStep}
142987
+ 3. resolve any merge conflicts if needed
142988
+ 4. retry push_branch`
142989
+ );
142990
+ }
142991
+ throw err;
142992
+ }
142993
+ return {
142994
+ success: true,
142995
+ branch,
142996
+ remoteBranch: pushDest.remoteBranch,
142997
+ remote: pushDest.remoteName,
142998
+ force,
142999
+ message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
143000
+ };
143001
+ })
143002
+ });
143003
+ }
143004
+ var AUTH_REQUIRED_REDIRECT = {
143005
+ push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
143006
+ fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
143007
+ pull: "use git_fetch to fetch the remote ref, then call this git tool with command 'merge' locally.",
143008
+ clone: "the repository is already cloned. use checkout_pr for PR branches."
143009
+ };
143010
+ var NOSHELL_BLOCKED_SUBCOMMANDS = {
143011
+ config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
143012
+ submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
143013
+ "update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
143014
+ "filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
143015
+ replace: "Blocked: git replace can redirect object lookups.",
143016
+ // subcommands that accept --exec or similar flags for arbitrary code execution
143017
+ rebase: "Blocked: git rebase --exec can execute arbitrary shell commands. Use 'merge' instead to integrate remote changes.",
143018
+ bisect: "Blocked: git bisect run can execute arbitrary shell commands. Bisect by hand (bisect start/good/bad/reset) is not available through this tool either \u2014 ask the user to run the bisect if needed.",
143019
+ // difftool/mergetool exist to shell out to external diff/merge programs.
143020
+ // both accept `--extcmd` / `-x` (difftool) or configured tool commands
143021
+ // (mergetool) that run arbitrary code. NOSHELL_BLOCKED_ARGS catches the
143022
+ // long `--extcmd` form, but not the `-x` short form — and globally blocking
143023
+ // `-x` would false-positive on `git cherry-pick -x`. block the subcommands
143024
+ // wholesale instead; neither has a meaningful use in an automated agent
143025
+ // workflow (agents use `git diff` / `git show` for diffs and resolve
143026
+ // conflicts via file edits, not a TUI merge tool).
143027
+ difftool: "Blocked: git difftool runs an external diff program via --extcmd/-x or configured tool and can execute arbitrary shell commands. Use 'diff' (or 'show' for single commits) to inspect changes \u2014 those output directly and don't invoke an external tool.",
143028
+ mergetool: "Blocked: git mergetool runs an external merge program configured via mergetool.<name>.cmd and can execute arbitrary shell commands. Resolve conflicts by editing the files directly (conflict markers are written into the working tree) and then commit."
143029
+ };
143030
+ var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
143031
+ var COLLAPSE_THRESHOLD = 200;
143032
+ var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
143033
+ var Git = type({
143034
+ command: type(subcommandPattern).describe("Git command (e.g., 'status', 'log', 'diff')"),
143035
+ args: type.string.array().describe("Additional arguments for the git command").optional()
143036
+ });
143037
+ function GitTool(ctx) {
143038
+ return tool({
143039
+ name: "git",
143040
+ description: "Run git commands. For push/fetch, use the dedicated MCP tools (push_branch, git_fetch). git pull is not available \u2014 use git_fetch then this tool with command 'merge'.",
143041
+ parameters: Git,
143042
+ execute: execute(async (params) => {
143043
+ const command = params.command;
143044
+ const args2 = params.args ?? [];
143045
+ const redirect = AUTH_REQUIRED_REDIRECT[command];
143046
+ if (redirect) {
143047
+ throw new Error(`git ${command} is not available through this tool \u2014 ${redirect}`);
143048
+ }
143049
+ if (ctx.payload.shell === "disabled") {
143050
+ const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[command];
143051
+ if (blocked) {
143052
+ throw new Error(blocked);
143053
+ }
143054
+ for (const arg of args2) {
143055
+ const isBlocked = NOSHELL_BLOCKED_ARGS.some(
143056
+ (flag) => arg === flag || arg.startsWith(flag + "=")
143057
+ );
143058
+ if (isBlocked) {
143059
+ throw new Error(
143060
+ `Blocked: '${arg}' flag can execute arbitrary code and is not allowed.`
143061
+ );
143062
+ }
143063
+ }
143064
+ }
143065
+ const output = $("git", [command, ...args2], { log: false });
143066
+ const lineCount = output.split("\n").length;
143067
+ if (lineCount > COLLAPSE_THRESHOLD) {
143068
+ log.group(`git ${command} output (${lineCount} lines)`, () => {
143069
+ log.info(output);
143070
+ });
143071
+ } else if (output) {
143072
+ log.info(output);
143073
+ }
143074
+ return { success: true, output };
143075
+ })
143076
+ });
143077
+ }
143078
+ var GitFetch = type({
143079
+ ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
143080
+ depth: type.number.describe("Fetch depth (for shallow clones)").optional()
143081
+ });
143082
+ function GitFetchTool(ctx) {
143083
+ return tool({
143084
+ name: "git_fetch",
143085
+ description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
143086
+ parameters: GitFetch,
143087
+ execute: execute(async (params) => {
143088
+ rejectIfLeadingDash(params.ref, "ref");
143089
+ const fetchArgs = ["--no-tags", "origin", params.ref];
143090
+ if (params.depth !== void 0) {
143091
+ fetchArgs.push(`--depth=${params.depth}`);
143092
+ }
143093
+ await $git("fetch", fetchArgs, {
143094
+ token: ctx.gitToken
143095
+ });
143096
+ return { success: true, ref: params.ref };
143097
+ })
143098
+ });
143099
+ }
143100
+ var DeleteBranch = type({
143101
+ branchName: type.string.describe("Remote branch to delete")
143102
+ });
143103
+ function DeleteBranchTool(ctx) {
143104
+ const pushPermission = ctx.payload.push;
143105
+ const defaultBranch = ctx.repo.data.default_branch || "main";
143106
+ return tool({
143107
+ name: "delete_branch",
143108
+ description: "Delete a remote branch. Requires push: enabled permission. Deletion of the repository's default branch is always blocked regardless of permission mode.",
143109
+ parameters: DeleteBranch,
143110
+ execute: execute(async (params) => {
143111
+ if (pushPermission !== "enabled") {
143112
+ throw new Error(
143113
+ "Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
143114
+ );
143115
+ }
143116
+ rejectSpecialRef(params.branchName, "branchName");
143117
+ if (params.branchName === defaultBranch) {
143118
+ throw new Error(
143119
+ `Blocked: cannot delete the default branch '${defaultBranch}'. If you really need to delete or rename it, do it manually via the repository settings.`
143120
+ );
143121
+ }
143122
+ await $git("push", ["origin", "--delete", `refs/heads/${params.branchName}`], {
143123
+ token: ctx.gitToken
143124
+ });
143125
+ return { success: true, deleted: params.branchName };
143126
+ })
143127
+ });
143128
+ }
143129
+ var PushTags = type({
143130
+ tag: type.string.describe("Tag name to push"),
143131
+ force: type.boolean.describe("Force push the tag").default(false)
143132
+ });
143133
+ function PushTagsTool(ctx) {
143134
+ const pushPermission = ctx.payload.push;
143135
+ return tool({
143136
+ name: "push_tags",
143137
+ description: "Push a tag to remote. Requires push: enabled permission.",
143138
+ parameters: PushTags,
143139
+ execute: execute(async (params) => {
143140
+ if (pushPermission !== "enabled") {
143141
+ throw new Error(
143142
+ "Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
143143
+ );
143144
+ }
143145
+ validateTagName(params.tag);
143146
+ const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
143147
+ await $git("push", pushArgs, {
143148
+ token: ctx.gitToken
143149
+ });
143150
+ return { success: true, tag: params.tag };
143151
+ })
143152
+ });
143153
+ }
143154
+
143155
+ // mcp/review.ts
143156
+ function getHttpStatus(err) {
143157
+ if (typeof err !== "object" || err === null) return void 0;
143158
+ const status = err.status;
143159
+ return typeof status === "number" ? status : void 0;
143160
+ }
143161
+ function commentableLinesForFile(patch) {
143162
+ const right = /* @__PURE__ */ new Set();
143163
+ const left = /* @__PURE__ */ new Set();
143164
+ if (!patch) return { RIGHT: right, LEFT: left };
143165
+ let oldLine = 0;
143166
+ let newLine = 0;
143167
+ for (const line of patch.split("\n")) {
143168
+ const hunk = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
143169
+ if (hunk) {
143170
+ oldLine = parseInt(hunk[1], 10);
143171
+ newLine = parseInt(hunk[2], 10);
143172
+ continue;
143173
+ }
143174
+ const changeType = line[0];
143175
+ if (changeType === "+") {
143176
+ right.add(newLine);
143177
+ newLine++;
143178
+ } else if (changeType === "-") {
143179
+ left.add(oldLine);
143180
+ oldLine++;
143181
+ } else if (changeType === " ") {
143182
+ right.add(newLine);
143183
+ left.add(oldLine);
143184
+ newLine++;
143185
+ oldLine++;
143186
+ }
143187
+ }
143188
+ return { RIGHT: right, LEFT: left };
143189
+ }
143190
+ async function buildCommentableMap(ctx, pullNumber) {
143191
+ const cached4 = ctx.toolState.commentableLinesByFile;
143192
+ const cachedFor = ctx.toolState.commentableLinesPullNumber;
143193
+ const cachedSha = ctx.toolState.commentableLinesCheckoutSha;
143194
+ const currentSha = ctx.toolState.checkoutSha;
143195
+ if (cached4 && cachedFor === pullNumber && cachedSha && cachedSha === currentSha) return cached4;
143196
+ const files = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listFiles, {
143197
+ owner: ctx.repo.owner,
143198
+ repo: ctx.repo.name,
143199
+ pull_number: pullNumber,
143200
+ per_page: 100
143201
+ });
143202
+ const map2 = /* @__PURE__ */ new Map();
143203
+ for (const file2 of files) {
143204
+ map2.set(file2.filename, commentableLinesForFile(file2.patch));
143205
+ }
143206
+ return map2;
143207
+ }
143208
+ function validateInlineComments(comments, map2) {
143209
+ const valid = [];
143210
+ const dropped = [];
143211
+ for (const c of comments) {
143212
+ const side = c.side === "LEFT" ? "LEFT" : "RIGHT";
143213
+ const line = c.line ?? 0;
143214
+ const startLine = c.start_line ?? line;
143215
+ const lines = map2.get(c.path);
143216
+ const record3 = (reason) => {
143217
+ const entry = { path: c.path, line, side, reason };
143218
+ if (c.start_line != null) entry.startLine = c.start_line;
143219
+ dropped.push(entry);
143220
+ };
143221
+ if (!lines) {
143222
+ record3(`file not in PR diff`);
143223
+ continue;
143224
+ }
143225
+ if (lines.LEFT.size === 0 && lines.RIGHT.size === 0) {
143226
+ record3(`file has no textual diff (binary, pure rename, or mode change)`);
143227
+ continue;
143228
+ }
143229
+ const anchors = lines[side];
143230
+ if (!anchors.has(line)) {
143231
+ record3(`line ${line} (${side}) is not inside a diff hunk`);
143232
+ continue;
143233
+ }
143234
+ if (c.start_line != null && c.start_line > line) {
143235
+ record3(
143236
+ `start_line ${c.start_line} is after line ${line} \u2014 ranges must satisfy start_line <= line`
143237
+ );
143238
+ continue;
143239
+ }
143240
+ if (startLine !== line && !anchors.has(startLine)) {
143241
+ record3(`start_line ${startLine} (${side}) is not inside a diff hunk`);
143242
+ continue;
143243
+ }
143244
+ valid.push(c);
143245
+ }
143246
+ return { valid, dropped };
143247
+ }
143248
+ var MAX_DROPPED_COMMENT_LINES = 50;
143249
+ function reviewSkipDecision(params) {
143250
+ if (params.body || params.hasComments) return null;
143251
+ if (!params.approved) {
143252
+ return {
143253
+ kind: "no-issues",
143254
+ reason: "no issues found \u2014 nothing to post"
143255
+ };
143256
+ }
143257
+ if (!params.prApproveEnabled) {
143258
+ return {
143259
+ kind: "empty-downgraded-approve",
143260
+ reason: "approve requested but prApproveEnabled is disabled; no feedback body or comments to post as a COMMENT review instead"
143261
+ };
143262
+ }
143263
+ return null;
143264
+ }
143265
+ function formatDroppedCommentsNote(dropped) {
143266
+ const renderEntry = (d) => {
143267
+ const range2 = d.startLine != null && d.startLine !== d.line ? `${d.startLine}-${d.line}` : `${d.line}`;
143268
+ return `- \`${d.path}:${range2}\` (${d.side}) \u2014 ${d.reason}`;
143269
+ };
143270
+ const shown = dropped.slice(0, MAX_DROPPED_COMMENT_LINES).map(renderEntry);
143271
+ const remainder = dropped.length - shown.length;
143272
+ if (remainder > 0) shown.push(`- \u2026and ${remainder} more dropped comment(s) not shown`);
143273
+ return `
143274
+
143275
+ ---
143276
+
143277
+ **Note:** ${dropped.length} inline comment(s) dropped because they did not anchor to lines inside the PR diff:
143278
+ ` + shown.join("\n");
143279
+ }
143280
+ var CreatePullRequestReview = type({
143281
+ pull_number: type.number.describe("The pull request number to review"),
143282
+ body: type.string.describe(
143283
+ "1-2 sentence high-level summary with urgency level, critical callouts, and feedback about code outside the diff. Specific feedback on diff lines goes in 'comments' array."
143284
+ ).optional(),
143285
+ approved: type.boolean.describe(
143286
+ "Set to true to submit as an approval. ONLY when the review contains no actionable feedback \u2014 neither inline comments nor actionable content in the body. Defaults to false (comment-only review). Rejections are not supported."
143287
+ ).optional(),
143288
+ commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
143289
+ comments: type({
143290
+ path: type.string.describe(
143291
+ "The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
143292
+ ),
143293
+ line: type.number.describe(
143294
+ "Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
143295
+ ),
143296
+ side: type.enumerated("LEFT", "RIGHT").describe(
143297
+ "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
143298
+ ).optional(),
143299
+ body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
143300
+ suggestion: type.string.describe(
143301
+ "Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
143302
+ ).optional(),
143303
+ start_line: type.number.describe(
143304
+ "Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
143305
+ ).optional()
143306
+ }).array().describe(
143307
+ "Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
143308
+ ).optional()
143309
+ });
143310
+ function CreatePullRequestReviewTool(ctx) {
143311
+ return tool({
143312
+ name: "create_pull_request_review",
143313
+ description: `Submit a review for an existing pull request. Each call creates a permanent, visible review on the PR \u2014 NEVER submit test or diagnostic reviews. Reviews with no body AND no comments are silently skipped (nothing to post). IMPORTANT: 95%+ of feedback should be in 'comments' array with file paths and line numbers. Only use 'body' for a 1-2 sentence summary with urgency and critical callouts. Use 'suggestion' to propose replacement code - MUST preserve exact indentation of original code. The first submission may error once with a one-time diff-coverage nudge listing unread TOC regions \u2014 retry with the same arguments and the pre-flight will not block again. Example replacing lines 42-44 (3 lines) with 5 lines: { path: 'src/api.ts', start_line: 42, line: 44, suggestion: ' const result = await fetch(url);\\n if (!result.ok) {\\n log.error(result.status);\\n throw new Error("request failed");\\n }' } CONSTRAINT: Inline comments can ONLY target files and lines that appear in the PR diff. Comments anchored outside a diff hunk are dropped automatically (with a note appended to the review body) \u2014 the rest of the review still posts.`,
143314
+ parameters: CreatePullRequestReview,
143315
+ execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
143316
+ if (body) body = fixDoubleEscapedString(body);
143317
+ ctx.toolState.issueNumber = pull_number;
143318
+ const skip = reviewSkipDecision({
143319
+ approved: approved ?? false,
143320
+ body,
143321
+ hasComments: comments.length > 0,
143322
+ prApproveEnabled: ctx.prApproveEnabled
143323
+ });
143324
+ if (skip) {
143325
+ log.info(`skipping review submission: ${skip.reason}`);
143326
+ return { success: true, skipped: true, reason: skip.reason };
143327
+ }
143328
+ let event = approved ? "APPROVE" : "COMMENT";
143329
+ if (event === "APPROVE" && !ctx.prApproveEnabled) {
143330
+ log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
143331
+ event = "COMMENT";
143332
+ }
143333
+ const params = {
143334
+ owner: ctx.repo.owner,
143335
+ repo: ctx.repo.name,
143336
+ pull_number,
143337
+ event
143338
+ };
143339
+ let latestHeadSha;
143340
+ if (commit_id) {
143341
+ params.commit_id = commit_id;
143342
+ } else {
143343
+ const pr = await ctx.octokit.rest.pulls.get({
143344
+ owner: ctx.repo.owner,
143345
+ repo: ctx.repo.name,
143346
+ pull_number
143347
+ });
143348
+ latestHeadSha = pr.data.head.sha;
143349
+ params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
143350
+ if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143351
+ log.info(
143352
+ `anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
143353
+ );
143354
+ }
143355
+ }
143356
+ runDiffCoveragePreflight({ ctx });
143357
+ const reviewComments = comments.map((comment) => {
143358
+ let commentBody = fixDoubleEscapedString(comment.body || "");
143359
+ if (comment.suggestion !== void 0) {
143360
+ const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
143361
+ commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
143362
+ }
143363
+ const side = comment.side || "RIGHT";
143364
+ const reviewComment = {
143365
+ path: comment.path,
143366
+ line: comment.line,
143367
+ body: commentBody,
143368
+ side
143369
+ };
143370
+ if (comment.start_line != null && comment.start_line !== comment.line) {
143371
+ reviewComment.start_line = comment.start_line;
143372
+ reviewComment.start_side = side;
143373
+ }
143374
+ return reviewComment;
143375
+ });
143376
+ let droppedComments = [];
143377
+ if (reviewComments.length > 0) {
143378
+ const commentableMap = await buildCommentableMap(ctx, pull_number);
143379
+ const validation = validateInlineComments(reviewComments, commentableMap);
143380
+ droppedComments = validation.dropped;
143381
+ if (droppedComments.length > 0) {
143382
+ log.info(
143383
+ `dropping ${droppedComments.length}/${reviewComments.length} inline comment(s) that do not anchor to PR diff lines`
143384
+ );
143385
+ }
143386
+ params.comments = validation.valid;
143387
+ }
143388
+ if (droppedComments.length > 0) {
143389
+ const note = formatDroppedCommentsNote(droppedComments);
143390
+ body = body ? body + note : note.replace(/^\n\n/, "");
143391
+ }
143392
+ if (!approved && !body && !params.comments?.length) {
143393
+ log.info("review has no body and all inline comments were dropped \u2014 skipping submission");
143394
+ return {
143395
+ success: true,
143396
+ skipped: true,
143397
+ reason: "all inline comments were invalid \u2014 nothing to post",
143398
+ droppedComments
143399
+ };
143400
+ }
143401
+ let result;
143402
+ try {
143403
+ result = body ? await createAndSubmitWithFooter(ctx, params, {
143404
+ body,
143405
+ approved: approved ?? false,
143406
+ hasComments: (params.comments?.length ?? 0) > 0
143407
+ }) : await createReviewWithStrandedRecovery(ctx, params);
143408
+ } catch (err) {
143409
+ if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
143410
+ const details = params.comments.map((c) => {
143411
+ const line = c.line ?? 0;
143412
+ const startLine = c.start_line ?? line;
143413
+ const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
143414
+ return `${c.path}:${range2} (${c.side ?? "RIGHT"})`;
143415
+ });
143416
+ const rawMsg = err instanceof Error ? err.message : String(err);
143417
+ const checkoutRef = formatMcpToolRef(ctx.agentId, "checkout_pr");
143418
+ throw new Error(
143419
+ `GitHub rejected the review with 422 even after pre-validation. Likely causes (check "GitHub said" below to narrow down): (1) new commits pushed after pre-validation \u2014 call \`${checkoutRef}\` again to refresh the diff snapshot, then resubmit; (2) the review body exceeded GitHub's ~65KB limit \u2014 shorten it and retry; (3) a \`suggestion\` block is malformed (missing backticks, extra backticks, or wrong indentation) \u2014 inspect the affected comments below. If none apply, move the failing comments into the review body as text so the rest still posts. Affected comments: ${details.join(", ")}. GitHub said: ${rawMsg}`,
143420
+ { cause: err }
143421
+ );
143422
+ }
143423
+ log.debug(`createReview response: ${JSON.stringify(result.data)}`);
143424
+ if (!result.data.id) {
143425
+ throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
143426
+ }
143427
+ const reviewId = result.data.id;
143428
+ const reviewNodeId = result.data.node_id;
143429
+ const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
143430
+ ctx.toolState.review = {
143431
+ id: reviewId,
143432
+ nodeId: reviewNodeId,
143433
+ reviewedSha: actuallyReviewedSha
143434
+ };
143435
+ if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143436
+ const fromSha = ctx.toolState.checkoutSha;
143437
+ const toSha = latestHeadSha;
143438
+ ctx.toolState.beforeSha = fromSha;
143439
+ ctx.toolState.checkoutSha = toSha;
143440
+ log.info(
143441
+ `new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
143442
+ );
143443
+ return {
143444
+ success: true,
143445
+ reviewId,
143446
+ html_url: result.data.html_url,
143447
+ state: result.data.state,
143448
+ user: result.data.user?.login,
143449
+ submitted_at: result.data.submitted_at,
143450
+ droppedComments: droppedComments.length > 0 ? droppedComments : void 0,
143451
+ newCommits: {
143452
+ from: fromSha,
143453
+ to: toSha,
143454
+ instructions: `new commits were pushed while you were reviewing. call \`${formatMcpToolRef(ctx.agentId, "checkout_pr")}\` again to fetch the latest version \u2014 it will compute the incremental diff automatically. submit another review covering only the new changes. do not repeat feedback from your previous review.`
143455
+ }
143456
+ };
143457
+ }
143458
+ return {
143459
+ success: true,
143460
+ reviewId,
143461
+ html_url: result.data.html_url,
143462
+ state: result.data.state,
143463
+ user: result.data.user?.login,
143464
+ submitted_at: result.data.submitted_at,
143465
+ droppedComments: droppedComments.length > 0 ? droppedComments : void 0
143466
+ };
143467
+ })
143468
+ });
143469
+ }
143470
+ function runDiffCoveragePreflight(params) {
143471
+ const coverageState = params.ctx.toolState.diffCoverage;
143472
+ if (!coverageState) {
143473
+ log.debug("diff coverage pre-flight skipped: no diffCoverage state present in toolState");
143474
+ return;
143475
+ }
143476
+ if (coverageState.coveragePreflightRan) {
143477
+ log.debug("diff coverage pre-flight skipped: already ran in this session");
143478
+ return;
143479
+ }
143480
+ coverageState.coveragePreflightRan = true;
143481
+ log.debug(
143482
+ `diff coverage pre-flight start: diffPath=${coverageState.diffPath}, totalLines=${coverageState.totalLines}, tocEntries=${coverageState.tocEntries.length}, coveredRanges=${coverageState.coveredRanges.length}`
143483
+ );
143484
+ const breakdown = getDiffCoverageBreakdown({ state: coverageState });
143485
+ const unread = [];
143486
+ let unreadLines = 0;
143487
+ for (const file2 of breakdown.files) {
143488
+ if (file2.unreadRanges.length === 0) continue;
143489
+ const rangesText = file2.unreadRanges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
143490
+ const fileUnreadLines = countLinesInRanges({ ranges: file2.unreadRanges });
143491
+ unread.push({ path: file2.filename, ranges: rangesText, unreadLines: fileUnreadLines });
143492
+ unreadLines += fileUnreadLines;
143493
+ }
143494
+ coverageState.lastBreakdown = renderDiffCoverageBreakdown({
143495
+ diffPath: coverageState.diffPath,
143496
+ breakdown
143497
+ });
143498
+ log.debug(
143499
+ `diff coverage pre-flight breakdown: coveredLines=${breakdown.coveredLines}, unreadLines=${unreadLines}`
143500
+ );
143501
+ if (unreadLines === 0) {
143502
+ log.debug("diff coverage pre-flight passed: no unread regions");
143503
+ return;
143504
+ }
143505
+ log.info(
143506
+ `diff coverage pre-flight nudge: unread lines=${unreadLines}, unread files=${unread.length}`
143507
+ );
143508
+ const unreadText = unread.map((entry) => `- ${entry.path} (${entry.unreadLines} lines, ${entry.ranges})`).join("\n");
143509
+ throw new Error(
143510
+ `diff coverage pre-flight: some TOC regions were not read before review submission. this is a one-time nudge \u2014 optionally read the ranges below from ${coverageState.diffPath}, then call create_pull_request_review again with the same arguments. this pre-flight will not block again in this review session.
143511
+
143512
+ unread TOC regions:
143513
+ ${unreadText}
143514
+
143515
+ ${coverageState.lastBreakdown}`
143516
+ );
143517
+ }
143518
+ async function clearStrandedPendingReview(ctx, params) {
143519
+ const originalErr = params.originalErr;
143520
+ const msg = originalErr instanceof Error ? originalErr.message.toLowerCase() : "";
143521
+ if (getHttpStatus(originalErr) !== 422 || !msg.includes("pending review")) throw originalErr;
143522
+ const reviews = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listReviews, {
143523
+ owner: params.owner,
143524
+ repo: params.repo,
143525
+ pull_number: params.pull_number,
143526
+ per_page: 100
143527
+ }).catch((listErr) => {
143528
+ log.info(
143529
+ `\xBB listReviews failed during pending-review cleanup, surfacing original 422: ${listErr instanceof Error ? listErr.message : String(listErr)}`
143530
+ );
143531
+ throw originalErr;
143532
+ });
143533
+ const leftover = reviews.find((r) => r.state === "PENDING");
143534
+ if (!leftover?.id) throw originalErr;
143535
+ log.info(
143536
+ `\xBB clearing leftover pending review ${leftover.id} (likely stranded by a killed prior run)`
143537
+ );
143538
+ try {
143539
+ await ctx.octokit.rest.pulls.deletePendingReview({
143540
+ owner: params.owner,
143541
+ repo: params.repo,
143542
+ pull_number: params.pull_number,
143543
+ review_id: leftover.id
143544
+ });
143545
+ } catch (cleanupErr) {
143546
+ const cleanupStatus = getHttpStatus(cleanupErr);
143547
+ if (cleanupStatus !== 404 && cleanupStatus !== 422) throw cleanupErr;
143548
+ log.debug(`\xBB delete of leftover pending ${leftover.id} no-op (status ${cleanupStatus})`);
143549
+ }
143550
+ }
143551
+ async function createReviewWithStrandedRecovery(ctx, params) {
143552
+ try {
143553
+ return await ctx.octokit.rest.pulls.createReview(params);
143554
+ } catch (err) {
143555
+ await clearStrandedPendingReview(ctx, {
143556
+ owner: params.owner,
143557
+ repo: params.repo,
143558
+ pull_number: params.pull_number,
143559
+ originalErr: err
143560
+ });
143561
+ return await ctx.octokit.rest.pulls.createReview(params);
143562
+ }
143563
+ }
143564
+ async function createAndSubmitWithFooter(ctx, params, opts) {
143565
+ const { event: _, ...pendingParams } = params;
143566
+ let pending;
143567
+ try {
143568
+ pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
143569
+ } catch (err) {
143570
+ await clearStrandedPendingReview(ctx, {
143571
+ owner: params.owner,
143572
+ repo: params.repo,
143573
+ pull_number: params.pull_number,
143574
+ originalErr: err
143575
+ });
143576
+ pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
143577
+ }
143578
+ if (!pending.data.id) {
143579
+ throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
143580
+ }
143581
+ try {
143582
+ const customParts = [];
143583
+ if (!opts.approved) {
143584
+ const apiUrl = getApiUrl();
143585
+ if (opts.hasComments) {
143586
+ const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143587
+ const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
143588
+ customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
143589
+ } else {
143590
+ const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143591
+ customParts.push(`[Fix it \u2794](${fixUrl})`);
143592
+ }
143593
+ }
143594
+ const footer = buildPullfrogFooter({
143595
+ workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
143596
+ customParts,
143597
+ model: ctx.toolState.model
143598
+ });
143599
+ return await ctx.octokit.rest.pulls.submitReview({
143600
+ owner: params.owner,
143601
+ repo: params.repo,
143602
+ pull_number: params.pull_number,
143603
+ review_id: pending.data.id,
143604
+ event: params.event,
143605
+ body: opts.body + footer
143606
+ });
143607
+ } catch (err) {
143608
+ try {
143609
+ await ctx.octokit.rest.pulls.deletePendingReview({
143610
+ owner: params.owner,
143611
+ repo: params.repo,
143612
+ pull_number: params.pull_number,
143613
+ review_id: pending.data.id
143614
+ });
143615
+ log.debug(`\xBB deleted leftover pending review ${pending.data.id} after failure`);
143616
+ } catch (cleanupErr) {
143617
+ log.debug(
143618
+ `\xBB failed to delete pending review ${pending.data.id}: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
143619
+ );
143620
+ }
143621
+ throw err;
143622
+ }
143623
+ }
143624
+ async function reportReviewNodeId(ctx, params) {
143625
+ await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
143626
+ }
143627
+
142176
143628
  // mcp/checkout.ts
142177
143629
  function formatFilesWithLineNumbers(files) {
142178
143630
  const output = [];
@@ -142239,7 +143691,10 @@ function formatFilesWithLineNumbers(files) {
142239
143691
  }
142240
143692
  const tocLines = [`## Files (${files.length})`];
142241
143693
  for (const entry of tocEntries) {
142242
- tocLines.push(`- ${entry.filename} \u2192 lines ${entry.startLine}-${entry.endLine}`);
143694
+ const anchor = createHash2("sha256").update(entry.filename).digest("hex");
143695
+ tocLines.push(
143696
+ `- ${entry.filename} \u2192 lines ${entry.startLine}-${entry.endLine} \xB7 diff-${anchor}`
143697
+ );
142243
143698
  }
142244
143699
  tocLines.push("");
142245
143700
  tocLines.push("---");
@@ -142261,7 +143716,7 @@ async function fetchAndFormatPrDiff(ctx, pullNumber) {
142261
143716
  pull_number: pullNumber,
142262
143717
  per_page: 100
142263
143718
  });
142264
- return formatFilesWithLineNumbers(files);
143719
+ return { ...formatFilesWithLineNumbers(files), files };
142265
143720
  }
142266
143721
  async function createTempBranch(params) {
142267
143722
  const response = await params.octokit.rest.git.createRef({
@@ -142328,6 +143783,8 @@ async function ensureBeforeShaReachable(params) {
142328
143783
  async function checkoutPrBranch(pr, params) {
142329
143784
  const { octokit, owner, name, gitToken, toolState, beforeSha } = params;
142330
143785
  log.info(`\xBB checking out PR #${pr.number}...`);
143786
+ rejectIfLeadingDash(pr.baseRef, "PR base ref");
143787
+ rejectIfLeadingDash(pr.headRef, "PR head ref");
142331
143788
  const isFork = pr.headRepoFullName !== pr.baseRepoFullName;
142332
143789
  const localBranch = `pr-${pr.number}`;
142333
143790
  const isShallow = $("git", ["rev-parse", "--is-shallow-repository"], { log: false }).trim() === "true";
@@ -142338,7 +143795,7 @@ async function checkoutPrBranch(pr, params) {
142338
143795
  if (!alreadyOnBranch) {
142339
143796
  $("git", ["checkout", "-B", pr.baseRef, `origin/${pr.baseRef}`], { log: false });
142340
143797
  log.debug(`\xBB fetching PR #${pr.number} (${localBranch})...`);
142341
- await $git("fetch", ["--no-tags", "origin", `pull/${pr.number}/head:${localBranch}`], {
143798
+ await $git("fetch", ["--no-tags", "origin", `+pull/${pr.number}/head:${localBranch}`], {
142342
143799
  token: gitToken
142343
143800
  });
142344
143801
  $("git", ["checkout", localBranch], { log: false });
@@ -142421,10 +143878,11 @@ async function checkoutPrBranch(pr, params) {
142421
143878
  remoteBranch: pr.headRef,
142422
143879
  localBranch
142423
143880
  };
142424
- await executeLifecycleHook({
143881
+ const postCheckoutHook = await executeLifecycleHook({
142425
143882
  event: "post-checkout",
142426
143883
  script: params.postCheckoutScript
142427
143884
  });
143885
+ return { hookWarning: postCheckoutHook.warning };
142428
143886
  }
142429
143887
  function CheckoutPrTool(ctx) {
142430
143888
  return tool({
@@ -142450,7 +143908,7 @@ function CheckoutPrTool(ctx) {
142450
143908
  baseRepoFullName: prResponse.data.base.repo.full_name,
142451
143909
  maintainerCanModify: prResponse.data.maintainer_can_modify
142452
143910
  };
142453
- await checkoutPrBranch(pr, {
143911
+ const checkoutResult = await checkoutPrBranch(pr, {
142454
143912
  octokit: ctx.octokit,
142455
143913
  owner: ctx.repo.owner,
142456
143914
  name: ctx.repo.name,
@@ -142493,11 +143951,49 @@ ${diffPreview}`);
142493
143951
  const diffPath = join3(tempDir, `pr-${pull_number}-${headShort}.diff`);
142494
143952
  writeFileSync(diffPath, formatResult.content);
142495
143953
  log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
143954
+ ctx.toolState.diffCoverage = createDiffCoverageState({
143955
+ diffPath,
143956
+ totalLines: countLines({ content: formatResult.content }),
143957
+ toc: formatResult.toc
143958
+ });
143959
+ log.debug(
143960
+ `\xBB diff coverage initialized: diffPath=${diffPath}, totalLines=${ctx.toolState.diffCoverage.totalLines}, tocEntries=${ctx.toolState.diffCoverage.tocEntries.length}`
143961
+ );
143962
+ const cached4 = /* @__PURE__ */ new Map();
143963
+ for (const file2 of formatResult.files) {
143964
+ cached4.set(file2.filename, commentableLinesForFile(file2.patch));
143965
+ }
143966
+ ctx.toolState.commentableLinesByFile = cached4;
143967
+ ctx.toolState.commentableLinesPullNumber = pull_number;
143968
+ ctx.toolState.commentableLinesCheckoutSha = ctx.toolState.checkoutSha;
142496
143969
  const incrementalInstructions = incrementalDiffPath ? ` IMPORTANT: incrementalDiffPath contains ONLY the changes since the last reviewed version (computed via range-diff). you MUST read incrementalDiffPath FIRST to understand what changed, then use diffPath for full PR context. do NOT skip the incremental diff.` : "";
143970
+ const COMMIT_LOG_MAX = 200;
143971
+ const baseRange = `origin/${pr.baseRef}..HEAD`;
143972
+ let commitCount = 0;
143973
+ let commitLog = "";
143974
+ let commitLogUnavailable = false;
143975
+ try {
143976
+ commitCount = parseInt(
143977
+ $("git", ["rev-list", "--count", baseRange], { log: false }).trim() || "0",
143978
+ 10
143979
+ );
143980
+ commitLog = $("git", ["log", "--oneline", `--max-count=${COMMIT_LOG_MAX}`, baseRange], {
143981
+ log: false
143982
+ });
143983
+ } catch (err) {
143984
+ commitLogUnavailable = true;
143985
+ log.debug(
143986
+ `\xBB unable to compute commit metadata for ${baseRange}: ${err instanceof Error ? err.message : String(err)}`
143987
+ );
143988
+ }
143989
+ const commitLogTruncated = commitCount > COMMIT_LOG_MAX;
143990
+ const hookWarningInstructions = checkoutResult.hookWarning ? ` HOOK WARNING: the post-checkout lifecycle hook reported a non-fatal failure (see hookWarning). decide whether to retry based on the guidance in that field before proceeding.` : "";
143991
+ const commitLogInstructions = commitLogUnavailable ? ` NOTE: commit metadata is partial (base ref unreachable, likely a shallow fetch). commitCount/commitLog may be 0/empty or incomplete; treat them as "unknown" rather than "no commits", and use \`git log\` directly if you need the full history.` : commitLogTruncated ? ` NOTE: commitLog was capped at ${COMMIT_LOG_MAX} entries out of ${commitCount} commits; use \`git log\` directly if you need the full history.` : "";
142497
143992
  return {
142498
143993
  success: true,
142499
143994
  number: prResponse.data.number,
142500
143995
  title: prResponse.data.title,
143996
+ body: prResponse.data.body,
142501
143997
  base: pr.baseRef,
142502
143998
  localBranch: `pr-${pull_number}`,
142503
143999
  remoteBranch: `refs/heads/${pr.headRef}`,
@@ -142508,7 +144004,12 @@ ${diffPreview}`);
142508
144004
  diffPath,
142509
144005
  incrementalDiffPath,
142510
144006
  toc: formatResult.toc,
142511
- instructions: `the diff file at diffPath contains a table of contents (TOC) at the top listing every changed file with its line range. use the line ranges to read specific files from the diff instead of reading the entire file. for example, if the TOC says "src/foo.ts \u2192 lines 5-42", read lines 5-42 from diffPath to see that file's changes. review files selectively based on relevance rather than reading everything sequentially. the local branch is 'localBranch' (pr-{number}), not the remote branch name. when pushing, omit branchName to use the current branch. do not use remoteBranch as a local branch name.` + incrementalInstructions
144007
+ commitCount,
144008
+ commitLog,
144009
+ commitLogTruncated,
144010
+ commitLogUnavailable,
144011
+ hookWarning: checkoutResult.hookWarning,
144012
+ instructions: `the diff file at diffPath contains a table of contents (TOC) at the top listing every changed file with its line range. use the TOC line ranges as your checklist and read specific files from the diff instead of reading the entire file. for example, if the TOC says "src/foo.ts \u2192 lines 5-42", read lines 5-42 from diffPath to see that file's changes. review files selectively based on relevance rather than reading everything sequentially. to inspect the PR's changed files, use diffPath \u2014 do NOT run \`git diff <base>..<head>\` to re-derive what's already in diffPath. the formatted diff with line numbers is authoritative. \`git log\` and \`git diff --stat\` are fine for commit-range overview, and \`git diff\` / \`git diff --cached\` are fine for inspecting *your own* uncommitted changes \u2014 but PR review content MUST come from diffPath. before your review is submitted, a one-time coverage pre-flight may error listing unread TOC regions. retry the same create_pull_request_review call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session. the local branch is 'localBranch' (pr-{number}), not the remote branch name. when pushing, omit branchName to use the current branch. do not use remoteBranch as a local branch name.` + incrementalInstructions + hookWarningInstructions + commitLogInstructions
142512
144013
  };
142513
144014
  })
142514
144015
  });
@@ -142736,244 +144237,6 @@ function CommitInfoTool(ctx) {
142736
144237
  });
142737
144238
  }
142738
144239
 
142739
- // mcp/git.ts
142740
- function getPushDestination(branch, storedDest) {
142741
- if (storedDest && storedDest.localBranch === branch) {
142742
- log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
142743
- const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
142744
- log: false
142745
- }).trim();
142746
- return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
142747
- }
142748
- try {
142749
- const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
142750
- const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
142751
- const remoteBranch = merge4.replace(/^refs\/heads\//, "");
142752
- const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
142753
- return { remoteName: pushRemote, remoteBranch, url: url4 };
142754
- } catch {
142755
- log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
142756
- const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
142757
- return { remoteName: "origin", remoteBranch: branch, url: url4 };
142758
- }
142759
- }
142760
- function normalizeUrl(url4) {
142761
- return url4.replace(/\.git$/, "").toLowerCase();
142762
- }
142763
- function validatePushDestination(ctx, branch) {
142764
- const pushUrl = ctx.toolState.pushUrl;
142765
- if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
142766
- const dest = getPushDestination(branch, ctx.toolState.pushDest);
142767
- if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
142768
- throw new Error(
142769
- `Push blocked: destination does not match expected repository.
142770
- Expected: ${pushUrl}
142771
- Actual: ${dest.url}
142772
- Git configuration may have been tampered with.`
142773
- );
142774
- }
142775
- return dest;
142776
- }
142777
- var PushBranch = type({
142778
- branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
142779
- force: type.boolean.describe("Force push (use with caution)").default(false)
142780
- });
142781
- function PushBranchTool(ctx) {
142782
- const defaultBranch = ctx.repo.data.default_branch || "main";
142783
- const pushPermission = ctx.payload.push;
142784
- return tool({
142785
- name: "push_branch",
142786
- description: "Push the current branch to the remote repository. Omit branchName to push the current branch (recommended). If specifying branchName, use the LOCAL branch name (e.g., 'pr-1'), not the remote branch name. The correct remote and remote branch are determined automatically from branch config set by checkout_pr. Requires a clean working tree. Runs the repository prepush hook (if configured) before the network push \u2014 hook failure means tests/lint or similar in that script failed, not necessarily a Pullfrog timeout. Never force push unless explicitly requested. Pushes to the default branch are blocked in restricted mode.",
142787
- parameters: PushBranch,
142788
- execute: execute(async ({ branchName, force }) => {
142789
- if (pushPermission === "disabled") {
142790
- throw new Error("Push is disabled. This repository is configured for read-only access.");
142791
- }
142792
- const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
142793
- const status = $("git", ["status", "--porcelain"], { log: false });
142794
- if (status) {
142795
- throw new Error(
142796
- `push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
142797
-
142798
- git status:
142799
- ${status}`
142800
- );
142801
- }
142802
- const pushDest = validatePushDestination(ctx, branch);
142803
- if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
142804
- throw new Error(
142805
- `Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
142806
- );
142807
- }
142808
- const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
142809
- const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
142810
- await executeLifecycleHook({ event: "prepush", script: ctx.prepushScript });
142811
- log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
142812
- if (force) {
142813
- log.warning(`force pushing - this will overwrite remote history`);
142814
- }
142815
- try {
142816
- await $git("push", pushArgs, {
142817
- token: ctx.gitToken
142818
- });
142819
- } catch (err) {
142820
- const msg = err instanceof Error ? err.message : String(err);
142821
- if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
142822
- throw new Error(
142823
- `push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
142824
-
142825
- to resolve this:
142826
- 1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
142827
- 2. use the git tool to rebase your changes: git({ subcommand: "rebase", args: ["origin/${pushDest.remoteBranch}"] })
142828
- 3. resolve any merge conflicts if needed
142829
- 4. retry push_branch`
142830
- );
142831
- }
142832
- throw err;
142833
- }
142834
- return {
142835
- success: true,
142836
- branch,
142837
- remoteBranch: pushDest.remoteBranch,
142838
- remote: pushDest.remoteName,
142839
- force,
142840
- message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
142841
- };
142842
- })
142843
- });
142844
- }
142845
- var AUTH_REQUIRED_REDIRECT = {
142846
- push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
142847
- fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
142848
- pull: "use git_fetch to fetch the remote ref, then use this git tool with subcommand 'merge' or 'rebase' locally.",
142849
- clone: "the repository is already cloned. use checkout_pr for PR branches."
142850
- };
142851
- var NOSHELL_BLOCKED_SUBCOMMANDS = {
142852
- config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
142853
- submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
142854
- "update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
142855
- "filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
142856
- replace: "Blocked: git replace can redirect object lookups.",
142857
- // subcommands that accept --exec or similar flags for arbitrary code execution
142858
- rebase: "Blocked: git rebase --exec can execute arbitrary shell commands.",
142859
- bisect: "Blocked: git bisect run can execute arbitrary shell commands."
142860
- };
142861
- var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
142862
- var COLLAPSE_THRESHOLD = 200;
142863
- var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
142864
- var Git = type({
142865
- subcommand: type(subcommandPattern).describe("Git subcommand (e.g., 'status', 'log', 'diff')"),
142866
- args: type.string.array().describe("Additional arguments for the git command").optional()
142867
- });
142868
- function GitTool(ctx) {
142869
- return tool({
142870
- name: "git",
142871
- description: "Run git commands. For push/fetch/pull, use the dedicated MCP tools instead (push_branch, git_fetch).",
142872
- parameters: Git,
142873
- execute: execute(async (params) => {
142874
- const subcommand = params.subcommand;
142875
- const args2 = params.args ?? [];
142876
- const redirect = AUTH_REQUIRED_REDIRECT[subcommand];
142877
- if (redirect) {
142878
- throw new Error(`git ${subcommand} is not available through this tool \u2014 ${redirect}`);
142879
- }
142880
- if (ctx.payload.shell === "disabled") {
142881
- const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[subcommand];
142882
- if (blocked) {
142883
- throw new Error(blocked);
142884
- }
142885
- for (const arg of args2) {
142886
- const isBlocked = NOSHELL_BLOCKED_ARGS.some(
142887
- (flag) => arg === flag || arg.startsWith(flag + "=")
142888
- );
142889
- if (isBlocked) {
142890
- throw new Error(
142891
- `Blocked: '${arg}' flag can execute arbitrary code and is not allowed.`
142892
- );
142893
- }
142894
- }
142895
- }
142896
- const output = $("git", [subcommand, ...args2], { log: false });
142897
- const lineCount = output.split("\n").length;
142898
- if (lineCount > COLLAPSE_THRESHOLD) {
142899
- log.group(`git ${subcommand} output (${lineCount} lines)`, () => {
142900
- log.info(output);
142901
- });
142902
- } else if (output) {
142903
- log.info(output);
142904
- }
142905
- return { success: true, output };
142906
- })
142907
- });
142908
- }
142909
- var GitFetch = type({
142910
- ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
142911
- depth: type.number.describe("Fetch depth (for shallow clones)").optional()
142912
- });
142913
- function GitFetchTool(ctx) {
142914
- return tool({
142915
- name: "git_fetch",
142916
- description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
142917
- parameters: GitFetch,
142918
- execute: execute(async (params) => {
142919
- const fetchArgs = ["--no-tags", "origin", params.ref];
142920
- if (params.depth !== void 0) {
142921
- fetchArgs.push(`--depth=${params.depth}`);
142922
- }
142923
- await $git("fetch", fetchArgs, {
142924
- token: ctx.gitToken
142925
- });
142926
- return { success: true, ref: params.ref };
142927
- })
142928
- });
142929
- }
142930
- var DeleteBranch = type({
142931
- branchName: type.string.describe("Remote branch to delete")
142932
- });
142933
- function DeleteBranchTool(ctx) {
142934
- const pushPermission = ctx.payload.push;
142935
- return tool({
142936
- name: "delete_branch",
142937
- description: "Delete a remote branch. Requires push: enabled permission.",
142938
- parameters: DeleteBranch,
142939
- execute: execute(async (params) => {
142940
- if (pushPermission !== "enabled") {
142941
- throw new Error(
142942
- "Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
142943
- );
142944
- }
142945
- await $git("push", ["origin", "--delete", params.branchName], {
142946
- token: ctx.gitToken
142947
- });
142948
- return { success: true, deleted: params.branchName };
142949
- })
142950
- });
142951
- }
142952
- var PushTags = type({
142953
- tag: type.string.describe("Tag name to push"),
142954
- force: type.boolean.describe("Force push the tag").default(false)
142955
- });
142956
- function PushTagsTool(ctx) {
142957
- const pushPermission = ctx.payload.push;
142958
- return tool({
142959
- name: "push_tags",
142960
- description: "Push a tag to remote. Requires push: enabled permission.",
142961
- parameters: PushTags,
142962
- execute: execute(async (params) => {
142963
- if (pushPermission !== "enabled") {
142964
- throw new Error(
142965
- "Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
142966
- );
142967
- }
142968
- const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
142969
- await $git("push", pushArgs, {
142970
- token: ctx.gitToken
142971
- });
142972
- return { success: true, tag: params.tag };
142973
- })
142974
- });
142975
- }
142976
-
142977
144240
  // mcp/issue.ts
142978
144241
  var Issue = type({
142979
144242
  title: type.string.describe("the title of the issue"),
@@ -143436,221 +144699,6 @@ function PullRequestInfoTool(ctx) {
143436
144699
  });
143437
144700
  }
143438
144701
 
143439
- // mcp/review.ts
143440
- function getHttpStatus(err) {
143441
- if (typeof err !== "object" || err === null) return void 0;
143442
- const status = err.status;
143443
- return typeof status === "number" ? status : void 0;
143444
- }
143445
- var CreatePullRequestReview = type({
143446
- pull_number: type.number.describe("The pull request number to review"),
143447
- body: type.string.describe(
143448
- "1-2 sentence high-level summary with urgency level, critical callouts, and feedback about code outside the diff. Specific feedback on diff lines goes in 'comments' array."
143449
- ).optional(),
143450
- approved: type.boolean.describe(
143451
- "Set to true to submit as an approval. ONLY when the review contains no actionable feedback \u2014 neither inline comments nor actionable content in the body. Defaults to false (comment-only review). Rejections are not supported."
143452
- ).optional(),
143453
- commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
143454
- comments: type({
143455
- path: type.string.describe(
143456
- "The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
143457
- ),
143458
- line: type.number.describe(
143459
- "Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
143460
- ),
143461
- side: type.enumerated("LEFT", "RIGHT").describe(
143462
- "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
143463
- ).optional(),
143464
- body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
143465
- suggestion: type.string.describe(
143466
- "Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
143467
- ).optional(),
143468
- start_line: type.number.describe(
143469
- "Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
143470
- ).optional()
143471
- }).array().describe(
143472
- "Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
143473
- ).optional()
143474
- });
143475
- function CreatePullRequestReviewTool(ctx) {
143476
- return tool({
143477
- name: "create_pull_request_review",
143478
- description: `Submit a review for an existing pull request. Each call creates a permanent, visible review on the PR \u2014 NEVER submit test or diagnostic reviews. Reviews with no body AND no comments are silently skipped (nothing to post). IMPORTANT: 95%+ of feedback should be in 'comments' array with file paths and line numbers. Only use 'body' for a 1-2 sentence summary with urgency and critical callouts. Use 'suggestion' to propose replacement code - MUST preserve exact indentation of original code. Example replacing lines 42-44 (3 lines) with 5 lines: { path: 'src/api.ts', start_line: 42, line: 44, suggestion: ' const result = await fetch(url);\\n if (!result.ok) {\\n log.error(result.status);\\n throw new Error("request failed");\\n }' } CONSTRAINT: Inline comments can ONLY target files and lines that appear in the PR diff. If GitHub rejects comments due to incorrect line numbers, re-read the diff and retry.`,
143479
- parameters: CreatePullRequestReview,
143480
- execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
143481
- if (body) body = fixDoubleEscapedString(body);
143482
- if (body && ctx.toolState.selectedMode === "Review" && ctx.toolState.todoTracker) {
143483
- ctx.toolState.todoTracker.cancel();
143484
- await ctx.toolState.todoTracker.settled();
143485
- ctx.toolState.todoTracker.completeInProgress();
143486
- const collapsible = ctx.toolState.todoTracker.renderCollapsible();
143487
- if (collapsible) {
143488
- body = `${body}
143489
-
143490
- ${collapsible}`;
143491
- }
143492
- }
143493
- ctx.toolState.issueNumber = pull_number;
143494
- if (!approved && !body && comments.length === 0) {
143495
- log.info(
143496
- "review has no body and no inline comments \u2014 skipping submission (no issues found)"
143497
- );
143498
- return {
143499
- success: true,
143500
- skipped: true,
143501
- reason: "no issues found \u2014 nothing to post"
143502
- };
143503
- }
143504
- let event = approved ? "APPROVE" : "COMMENT";
143505
- if (event === "APPROVE" && !ctx.prApproveEnabled) {
143506
- log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
143507
- event = "COMMENT";
143508
- }
143509
- const params = {
143510
- owner: ctx.repo.owner,
143511
- repo: ctx.repo.name,
143512
- pull_number,
143513
- event
143514
- };
143515
- let latestHeadSha;
143516
- if (commit_id) {
143517
- params.commit_id = commit_id;
143518
- } else {
143519
- const pr = await ctx.octokit.rest.pulls.get({
143520
- owner: ctx.repo.owner,
143521
- repo: ctx.repo.name,
143522
- pull_number
143523
- });
143524
- latestHeadSha = pr.data.head.sha;
143525
- params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
143526
- if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143527
- log.info(
143528
- `anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
143529
- );
143530
- }
143531
- }
143532
- const reviewComments = comments.map((comment) => {
143533
- let commentBody = fixDoubleEscapedString(comment.body || "");
143534
- if (comment.suggestion !== void 0) {
143535
- const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
143536
- commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
143537
- }
143538
- const side = comment.side || "RIGHT";
143539
- const reviewComment = {
143540
- path: comment.path,
143541
- line: comment.line,
143542
- body: commentBody,
143543
- side
143544
- };
143545
- if (comment.start_line != null && comment.start_line !== comment.line) {
143546
- reviewComment.start_line = comment.start_line;
143547
- reviewComment.start_side = side;
143548
- }
143549
- return reviewComment;
143550
- });
143551
- if (reviewComments.length > 0) {
143552
- params.comments = reviewComments;
143553
- }
143554
- let result;
143555
- try {
143556
- result = body ? await createAndSubmitWithFooter(ctx, params, {
143557
- body,
143558
- approved: approved ?? false,
143559
- hasComments: reviewComments.length > 0
143560
- }) : await ctx.octokit.rest.pulls.createReview(params);
143561
- } catch (err) {
143562
- if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
143563
- const details = params.comments.map((c) => {
143564
- const line = c.line ?? 0;
143565
- const startLine = c.start_line ?? line;
143566
- const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
143567
- return `${c.path}:${range2} (${c.side ?? "RIGHT"})`;
143568
- });
143569
- throw new Error(
143570
- `GitHub rejected inline comment(s) with "Line could not be resolved". This usually means the diff changed since you last read it (new commits pushed). Re-read the diff to get current line numbers, or move failing comments to the review body. Affected: ${details.join(", ")}`
143571
- );
143572
- }
143573
- log.debug(`createReview response: ${JSON.stringify(result.data)}`);
143574
- if (!result.data.id) {
143575
- throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
143576
- }
143577
- const reviewId = result.data.id;
143578
- const reviewNodeId = result.data.node_id;
143579
- const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
143580
- ctx.toolState.review = {
143581
- id: reviewId,
143582
- nodeId: reviewNodeId,
143583
- reviewedSha: actuallyReviewedSha
143584
- };
143585
- if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143586
- const fromSha = ctx.toolState.checkoutSha;
143587
- const toSha = latestHeadSha;
143588
- ctx.toolState.beforeSha = fromSha;
143589
- ctx.toolState.checkoutSha = toSha;
143590
- log.info(
143591
- `new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
143592
- );
143593
- return {
143594
- success: true,
143595
- reviewId,
143596
- html_url: result.data.html_url,
143597
- state: result.data.state,
143598
- user: result.data.user?.login,
143599
- submitted_at: result.data.submitted_at,
143600
- newCommits: {
143601
- from: fromSha,
143602
- to: toSha,
143603
- instructions: `new commits were pushed while you were reviewing. call \`${formatMcpToolRef(ctx.agentId, "checkout_pr")}\` again to fetch the latest version \u2014 it will compute the incremental diff automatically. submit another review covering only the new changes. do not repeat feedback from your previous review.`
143604
- }
143605
- };
143606
- }
143607
- return {
143608
- success: true,
143609
- reviewId,
143610
- html_url: result.data.html_url,
143611
- state: result.data.state,
143612
- user: result.data.user?.login,
143613
- submitted_at: result.data.submitted_at
143614
- };
143615
- })
143616
- });
143617
- }
143618
- async function createAndSubmitWithFooter(ctx, params, opts) {
143619
- const { event: _, ...pendingParams } = params;
143620
- const pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
143621
- if (!pending.data.id) {
143622
- throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
143623
- }
143624
- const customParts = [];
143625
- if (!opts.approved) {
143626
- const apiUrl = getApiUrl();
143627
- if (opts.hasComments) {
143628
- const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143629
- const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
143630
- customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
143631
- } else {
143632
- const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143633
- customParts.push(`[Fix it \u2794](${fixUrl})`);
143634
- }
143635
- }
143636
- const footer = buildPullfrogFooter({
143637
- workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
143638
- customParts,
143639
- model: ctx.toolState.model
143640
- });
143641
- return ctx.octokit.rest.pulls.submitReview({
143642
- owner: params.owner,
143643
- repo: params.repo,
143644
- pull_number: params.pull_number,
143645
- review_id: pending.data.id,
143646
- event: params.event,
143647
- body: opts.body + footer
143648
- });
143649
- }
143650
- async function reportReviewNodeId(ctx, params) {
143651
- await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
143652
- }
143653
-
143654
144702
  // mcp/reviewComments.ts
143655
144703
  import { writeFileSync as writeFileSync4 } from "node:fs";
143656
144704
  import { join as join6 } from "node:path";
@@ -143698,7 +144746,7 @@ query ($owner: String!, $name: String!, $prNumber: Int!) {
143698
144746
  }
143699
144747
  }
143700
144748
  `;
143701
- function countLines(str) {
144749
+ function countLines2(str) {
143702
144750
  let count = 1;
143703
144751
  let index = -1;
143704
144752
  while ((index = str.indexOf("\n", index + 1)) !== -1) {
@@ -143840,13 +144888,13 @@ function formatReviewThreads(threadBlocks, header) {
143840
144888
  const reviewBodyLines = [];
143841
144889
  if (header.reviewBody) {
143842
144890
  reviewBodyLines.push("## Review Body", "", header.reviewBody, "");
143843
- currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines(line), 0);
144891
+ currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines2(line), 0);
143844
144892
  }
143845
144893
  const tocEntries = [];
143846
144894
  const threadLines = [];
143847
144895
  for (const block of threadBlocks) {
143848
144896
  const startLine = currentLine;
143849
- const actualLineCount = block.content.reduce((sum, line) => sum + countLines(line), 0);
144897
+ const actualLineCount = block.content.reduce((sum, line) => sum + countLines2(line), 0);
143850
144898
  const endLine = currentLine + actualLineCount - 1;
143851
144899
  tocEntries.push(`- ${block.path}:${block.lineRange} \u2192 lines ${startLine}-${endLine}`);
143852
144900
  threadLines.push(...block.content);
@@ -144169,7 +145217,7 @@ GitHub's markdown parser requires a blank line between ALL block-level elements.
144169
145217
  Rules:
144170
145218
  - \`##\` titles and key-change bullet lead-ins are plain-language summaries; backtick only actual code tokens (files, types, functions) where they appear in the title
144171
145219
  - ALL variable names, identifiers, and file names in body text must be in backticks
144172
- - ALL file references MUST link to the PR Files Changed view. Compute anchors by running \`echo -n 'path/to/file.ts' | sha256sum\` via shell for each file. NEVER fabricate hex strings \u2014 run the actual command. If shell is unavailable, omit the #diff- anchor rather than guessing.
145220
+ - ALL file references MUST link to the PR Files Changed view. Use the \`diff-<hex>\` anchor precomputed next to each filename in the \`checkout_pr\` TOC \u2014 do NOT run \`sha256sum\` or any other shell command to compute anchors. NEVER fabricate hex strings. If a file is not in the TOC, omit the \`#diff-\` anchor rather than guessing.
144173
145221
  - Add <br/> before each ## heading for visual spacing. Do NOT use horizontal rules (---)
144174
145222
  - Do NOT include raw diff stats like '+123 / -45' or line counts
144175
145223
  - Do NOT include code blocks or repeat diff contents
@@ -144244,7 +145292,7 @@ ${learningsStep(t, 6)}`
144244
145292
  description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
144245
145293
  prompt: `### Checklist
144246
145294
 
144247
- 1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. Read the diff to identify the major areas of change.
145295
+ 1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. read the diff TOC first and treat its file line ranges as your coverage checklist.
144248
145296
 
144249
145297
  2. For each area of change:
144250
145298
  - read the diff and trace data flow, check boundaries, and verify assumptions
@@ -144261,6 +145309,7 @@ ${learningsStep(t, 6)}`
144261
145309
  4. Submit \u2014 ALWAYS submit exactly one review via \`${t("create_pull_request_review")}\`.
144262
145310
  Do NOT call \`report_progress\` \u2014 the review is the final record and the progress
144263
145311
  comment will be cleaned up automatically.
145312
+ note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
144264
145313
 
144265
145314
  - **critical issues** (blocks merge \u2014 bugs, security, data loss):
144266
145315
  \`approved: false\`. Body begins with a GitHub alert blockquote, e.g.:
@@ -144278,7 +145327,7 @@ ${learningsStep(t, 6)}`
144278
145327
  description: "Re-review a PR after new commits are pushed; focus on new changes since the last review",
144279
145328
  prompt: `### Checklist
144280
145329
 
144281
- 1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available).
145330
+ 1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available). read the diff TOC first and use its line ranges as your coverage checklist.
144282
145331
 
144283
145332
  2. If \`incrementalDiffPath\` is present, read it to see what changed since the last review. This is a range-diff that isolates the net changes, filtering out base branch noise. If not present, fall back to reviewing the full PR diff.
144284
145333
 
@@ -144302,6 +145351,7 @@ ${learningsStep(t, 6)}`
144302
145351
  - in some cases you may receive a complete diff for the whole pull request instead of an incremental one. when this happens, you will need to determine what changes have happened since Pullfrog's most recent review.
144303
145352
 
144304
145353
  7. Submit \u2014 Do NOT call \`report_progress\` or \`create_issue_comment\` \u2014 the review is the final record and the progress comment will be cleaned up automatically. the review body always includes the reviewed changes from step 6a. append \`Prior review feedback:\\n\` with the checklist from step 6b only if any prior comments were addressed. Follow these rules:
145354
+ - note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
144305
145355
  - IF NO NEW ISSUES, NON-SUBSTANTIVE CHANGES ONLY (trivial formatting, import reordering, comment tweaks): do NOT submit a review. Do NOT call \`report_progress\`. Exit \u2014 the progress comment will be cleaned up automatically.
144306
145356
  - ELSE IF NEW CRITICAL ISSUES (blocks merge): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with a GitHub alert blockquote (e.g. \`> [!CAUTION]\\n> This PR introduces ...\`), then the reviewed changes summary and prior feedback (if any).
144307
145357
  - ELSE IF NEW RECOMMENDED CHANGES (non-critical): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!IMPORTANT]\\n> ...\` alert, then the reviewed changes summary and prior feedback (if any).
@@ -144769,11 +145819,11 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
144769
145819
  await killProcessGroup(proc);
144770
145820
  }
144771
145821
  }, timeout);
144772
- const exitCode = await new Promise((resolve2) => {
145822
+ const exitCode = await new Promise((resolve3) => {
144773
145823
  const done = (code) => {
144774
145824
  exited = true;
144775
145825
  clearTimeout(timeoutId);
144776
- resolve2(code);
145826
+ resolve3(code);
144777
145827
  };
144778
145828
  proc.on("exit", done);
144779
145829
  proc.on("error", () => done(null));
@@ -144912,12 +145962,12 @@ function readEnvPort() {
144912
145962
  return parsed2;
144913
145963
  }
144914
145964
  function isPortAvailable(port) {
144915
- return new Promise((resolve2) => {
145965
+ return new Promise((resolve3) => {
144916
145966
  const server = createServer();
144917
145967
  server.unref();
144918
- server.once("error", () => resolve2(false));
145968
+ server.once("error", () => resolve3(false));
144919
145969
  server.once("listening", () => {
144920
- server.close(() => resolve2(true));
145970
+ server.close(() => resolve3(true));
144921
145971
  });
144922
145972
  server.listen(port, mcpHost);
144923
145973
  });
@@ -145057,9 +146107,12 @@ async function killBackgroundProcesses(toolState) {
145057
146107
  async function startMcpHttpServer(ctx, options) {
145058
146108
  const tools = buildOrchestratorTools(ctx, options?.outputSchema);
145059
146109
  const startResult = await selectMcpPort(ctx, tools);
146110
+ let disposed = false;
145060
146111
  return {
145061
146112
  url: startResult.url,
145062
146113
  [Symbol.asyncDispose]: async () => {
146114
+ if (disposed) return;
146115
+ disposed = true;
145063
146116
  closeBrowserDaemon(ctx.toolState);
145064
146117
  await killBackgroundProcesses(ctx.toolState);
145065
146118
  await startResult.server.stop();
@@ -145254,39 +146307,6 @@ var ThinkingTimer = class {
145254
146307
  }
145255
146308
  };
145256
146309
 
145257
- // agents/shared.ts
145258
- import { execFileSync as execFileSync2 } from "node:child_process";
145259
- var MAX_STDERR_LINES = 20;
145260
- var MAX_COMMIT_RETRIES = 3;
145261
- function getGitStatus() {
145262
- try {
145263
- return execFileSync2("git", ["status", "--porcelain"], {
145264
- encoding: "utf-8",
145265
- timeout: 1e4
145266
- }).trim();
145267
- } catch {
145268
- return "";
145269
- }
145270
- }
145271
- function buildCommitPrompt(_agentId, status) {
145272
- return [
145273
- `UNCOMMITTED CHANGES \u2014 the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`,
145274
- "",
145275
- "```",
145276
- status,
145277
- "```"
145278
- ].join("\n");
145279
- }
145280
- var agent = (input) => {
145281
- return {
145282
- ...input,
145283
- run: async (ctx) => {
145284
- log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
145285
- return input.run(ctx);
145286
- }
145287
- };
145288
- };
145289
-
145290
146310
  // agents/claude.ts
145291
146311
  async function installClaudeCli() {
145292
146312
  return await installFromNpmTarball({
@@ -145325,6 +146345,7 @@ async function runClaude(params) {
145325
146345
  let finalOutput = "";
145326
146346
  let sessionId;
145327
146347
  let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
146348
+ let accumulatedCostUsd = 0;
145328
146349
  let tokensLogged = false;
145329
146350
  function buildUsage() {
145330
146351
  const totalInput = accumulatedTokens.input + accumulatedTokens.cacheRead + accumulatedTokens.cacheWrite;
@@ -145333,7 +146354,8 @@ async function runClaude(params) {
145333
146354
  inputTokens: totalInput,
145334
146355
  outputTokens: accumulatedTokens.output,
145335
146356
  cacheReadTokens: accumulatedTokens.cacheRead || void 0,
145336
- cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
146357
+ cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
146358
+ costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
145337
146359
  } : void 0;
145338
146360
  }
145339
146361
  const handlers2 = {
@@ -145350,6 +146372,12 @@ async function runClaude(params) {
145350
146372
  finalOutput = message;
145351
146373
  } else if (block.type === "tool_use") {
145352
146374
  const toolName = block.name || "unknown";
146375
+ if (params.onToolUse) {
146376
+ params.onToolUse({
146377
+ toolName,
146378
+ input: block.input
146379
+ });
146380
+ }
145353
146381
  thinkingTimer.markToolCall();
145354
146382
  log.toolCall({ toolName, input: block.input || {} });
145355
146383
  if (toolName.includes("report_progress") && params.todoTracker) {
@@ -145365,6 +146393,8 @@ async function runClaude(params) {
145365
146393
  if (msgUsage) {
145366
146394
  accumulatedTokens.input += msgUsage.input_tokens || 0;
145367
146395
  accumulatedTokens.output += msgUsage.output_tokens || 0;
146396
+ accumulatedTokens.cacheRead += msgUsage.cache_read_input_tokens || 0;
146397
+ accumulatedTokens.cacheWrite += msgUsage.cache_creation_input_tokens || 0;
145368
146398
  }
145369
146399
  },
145370
146400
  user: (event) => {
@@ -145395,19 +146425,18 @@ async function runClaude(params) {
145395
146425
  const cacheRead = usage?.cache_read_input_tokens || 0;
145396
146426
  const cacheWrite = usage?.cache_creation_input_tokens || 0;
145397
146427
  const outputTokens = usage?.output_tokens || 0;
145398
- const totalInput = inputTokens + cacheRead + cacheWrite;
146428
+ const costUsd = typeof event.total_cost_usd === "number" && Number.isFinite(event.total_cost_usd) ? event.total_cost_usd : 0;
145399
146429
  accumulatedTokens = { input: inputTokens, output: outputTokens, cacheRead, cacheWrite };
146430
+ accumulatedCostUsd = costUsd;
145400
146431
  log.info(`\xBB ${params.label} result: subtype=${subtype}, turns=${numTurns}`);
145401
146432
  if (!tokensLogged) {
145402
- log.table([
145403
- [
145404
- { data: "Input", header: true },
145405
- { data: "Cache Read", header: true },
145406
- { data: "Cache Write", header: true },
145407
- { data: "Output", header: true }
145408
- ],
145409
- [String(totalInput), String(cacheRead), String(cacheWrite), String(outputTokens)]
145410
- ]);
146433
+ logTokenTable({
146434
+ input: inputTokens,
146435
+ cacheRead,
146436
+ cacheWrite,
146437
+ output: outputTokens,
146438
+ costUsd
146439
+ });
145411
146440
  tokensLogged = true;
145412
146441
  }
145413
146442
  } else if (subtype === "error_max_turns") {
@@ -145442,6 +146471,7 @@ async function runClaude(params) {
145442
146471
  cwd: params.cwd,
145443
146472
  env: params.env,
145444
146473
  activityTimeout: 3e5,
146474
+ onActivityTimeout: params.onActivityTimeout,
145445
146475
  stdio: ["ignore", "pipe", "pipe"],
145446
146476
  onStdout: async (chunk) => {
145447
146477
  const text = chunk.toString();
@@ -145453,25 +146483,33 @@ async function runClaude(params) {
145453
146483
  for (const line of lines) {
145454
146484
  const trimmed = line.trim();
145455
146485
  if (!trimmed) continue;
146486
+ let event;
145456
146487
  try {
145457
- const event = JSON.parse(trimmed);
145458
- eventCount++;
145459
- log.debug(JSON.stringify(event, null, 2));
145460
- const timeSinceLastActivity = getIdleMs();
145461
- if (timeSinceLastActivity > 1e4) {
145462
- log.info(
145463
- `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
145464
- );
145465
- }
145466
- markActivity();
145467
- const handler2 = handlers2[event.type];
145468
- if (handler2) {
145469
- handler2(event);
145470
- } else {
145471
- log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
145472
- }
146488
+ event = JSON.parse(trimmed);
145473
146489
  } catch {
145474
146490
  log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
146491
+ continue;
146492
+ }
146493
+ eventCount++;
146494
+ log.debug(JSON.stringify(event, null, 2));
146495
+ const timeSinceLastActivity = getIdleMs();
146496
+ if (timeSinceLastActivity > 1e4) {
146497
+ log.info(
146498
+ `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
146499
+ );
146500
+ }
146501
+ markActivity();
146502
+ const handler2 = handlers2[event.type];
146503
+ if (!handler2) {
146504
+ log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
146505
+ continue;
146506
+ }
146507
+ try {
146508
+ handler2(event);
146509
+ } catch (err) {
146510
+ log.info(
146511
+ `\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
146512
+ );
145475
146513
  }
145476
146514
  }
145477
146515
  },
@@ -145505,16 +146543,9 @@ async function runClaude(params) {
145505
146543
  if (stderrContext) log.info(`\xBB last stderr output:
145506
146544
  ${stderrContext}`);
145507
146545
  }
145508
- if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
145509
- const totalTokens = accumulatedTokens.input + accumulatedTokens.output;
145510
- log.table([
145511
- [
145512
- { data: "Input Tokens", header: true },
145513
- { data: "Output Tokens", header: true },
145514
- { data: "Total Tokens", header: true }
145515
- ],
145516
- [String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
145517
- ]);
146546
+ if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
146547
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
146548
+ tokensLogged = true;
145518
146549
  }
145519
146550
  const usage = buildUsage();
145520
146551
  if (result.exitCode !== 0) {
@@ -145547,7 +146578,7 @@ ${stderrContext}`);
145547
146578
  params.todoTracker?.cancel();
145548
146579
  const duration4 = performance6.now() - startTime;
145549
146580
  const errorMessage = error49 instanceof Error ? error49.message : String(error49);
145550
- const isActivityTimeout = errorMessage.includes("activity timeout");
146581
+ const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
145551
146582
  const stderrContext = recentStderr.slice(-10).join("\n");
145552
146583
  const diagnosis = lastProviderError ? `likely cause: ${lastProviderError}` : eventCount === 0 ? "Claude produced 0 stdout events - check if the API is reachable" : `${eventCount} events were processed before the hang`;
145553
146584
  log.info(
@@ -145638,8 +146669,7 @@ var claude = agent({
145638
146669
  "--effort",
145639
146670
  effort,
145640
146671
  "--disallowedTools",
145641
- "Bash",
145642
- "Agent(Bash)"
146672
+ "Bash,Agent(Bash)"
145643
146673
  ];
145644
146674
  if (model) {
145645
146675
  baseArgs.push("--model", model);
@@ -145652,11 +146682,19 @@ var claude = agent({
145652
146682
  log.info(`\xBB effort: ${effort}`);
145653
146683
  log.debug(`\xBB starting Pullfrog (Claude Code): node ${baseArgs.join(" ")}`);
145654
146684
  log.debug(`\xBB working directory: ${repoDir}`);
145655
- const runParams = { label: "Pullfrog", cwd: repoDir, env: env2, todoTracker: ctx.todoTracker };
146685
+ const runParams = {
146686
+ label: "Pullfrog",
146687
+ cwd: repoDir,
146688
+ env: env2,
146689
+ todoTracker: ctx.todoTracker,
146690
+ onActivityTimeout: ctx.onActivityTimeout,
146691
+ onToolUse: ctx.onToolUse
146692
+ };
145656
146693
  let result = await runClaude({
145657
146694
  ...runParams,
145658
146695
  args: [...baseArgs, "-p", ctx.instructions.full]
145659
146696
  });
146697
+ let aggregatedUsage = result.usage;
145660
146698
  for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
145661
146699
  if (!result.success || !result.sessionId) break;
145662
146700
  const status = getGitStatus();
@@ -145673,8 +146711,9 @@ ${status}`);
145673
146711
  result.sessionId
145674
146712
  ]
145675
146713
  });
146714
+ aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
145676
146715
  }
145677
- return result;
146716
+ return { ...result, usage: aggregatedUsage };
145678
146717
  }
145679
146718
  });
145680
146719
 
@@ -145756,6 +146795,7 @@ async function runOpenCode(params) {
145756
146795
  const thinkingTimer = new ThinkingTimer();
145757
146796
  let finalOutput = "";
145758
146797
  let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
146798
+ let accumulatedCostUsd = 0;
145759
146799
  let tokensLogged = false;
145760
146800
  const toolCallTimings = /* @__PURE__ */ new Map();
145761
146801
  let currentStepId = null;
@@ -145768,7 +146808,8 @@ async function runOpenCode(params) {
145768
146808
  inputTokens: totalInput,
145769
146809
  outputTokens: accumulatedTokens.output,
145770
146810
  cacheReadTokens: accumulatedTokens.cacheRead || void 0,
145771
- cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
146811
+ cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
146812
+ costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
145772
146813
  } : void 0;
145773
146814
  }
145774
146815
  const handlers2 = {
@@ -145779,6 +146820,7 @@ async function runOpenCode(params) {
145779
146820
  log.debug(`\xBB ${params.label} init event (full): ${JSON.stringify(event)}`);
145780
146821
  finalOutput = "";
145781
146822
  accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
146823
+ accumulatedCostUsd = 0;
145782
146824
  tokensLogged = false;
145783
146825
  },
145784
146826
  message: (event) => {
@@ -145823,6 +146865,9 @@ async function runOpenCode(params) {
145823
146865
  accumulatedTokens.cacheRead += eventTokens.cache?.read || 0;
145824
146866
  accumulatedTokens.cacheWrite += eventTokens.cache?.write || 0;
145825
146867
  }
146868
+ if (typeof event.part?.cost === "number" && Number.isFinite(event.part.cost)) {
146869
+ accumulatedCostUsd += event.part.cost;
146870
+ }
145826
146871
  if (currentStepId === stepId) {
145827
146872
  currentStepId = null;
145828
146873
  currentStepType = null;
@@ -145840,6 +146885,12 @@ async function runOpenCode(params) {
145840
146885
  if (stepHistory.length > 0) {
145841
146886
  stepHistory[stepHistory.length - 1].toolCalls.push(toolName);
145842
146887
  }
146888
+ if (params.onToolUse) {
146889
+ params.onToolUse({
146890
+ toolName,
146891
+ input: event.part?.state?.input
146892
+ });
146893
+ }
145843
146894
  thinkingTimer.markToolCall();
145844
146895
  log.toolCall({ toolName, input: event.part?.state?.input || {} });
145845
146896
  if (event.part?.state?.status === "completed" && event.part.state.output) {
@@ -145895,19 +146946,9 @@ async function runOpenCode(params) {
145895
146946
  if (event.status === "error") {
145896
146947
  log.info(`\xBB ${params.label} failed: ${JSON.stringify(event)}`);
145897
146948
  } else {
145898
- const inputTokens = event.stats?.input_tokens || accumulatedTokens.input || 0;
145899
- const outputTokens = event.stats?.output_tokens || accumulatedTokens.output || 0;
145900
- const totalTokens = event.stats?.total_tokens || inputTokens + outputTokens;
145901
146949
  log.info(`\xBB run complete: tool_calls=${toolCalls}, duration=${duration4}ms`);
145902
- if ((inputTokens > 0 || outputTokens > 0) && !tokensLogged) {
145903
- log.table([
145904
- [
145905
- { data: "Input Tokens", header: true },
145906
- { data: "Output Tokens", header: true },
145907
- { data: "Total Tokens", header: true }
145908
- ],
145909
- [String(inputTokens), String(outputTokens), String(totalTokens)]
145910
- ]);
146950
+ if ((accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0) && !tokensLogged) {
146951
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
145911
146952
  tokensLogged = true;
145912
146953
  }
145913
146954
  }
@@ -145924,6 +146965,7 @@ async function runOpenCode(params) {
145924
146965
  cwd: params.cwd,
145925
146966
  env: params.env,
145926
146967
  activityTimeout: 3e5,
146968
+ onActivityTimeout: params.onActivityTimeout,
145927
146969
  stdio: ["ignore", "pipe", "pipe"],
145928
146970
  onStdout: async (chunk) => {
145929
146971
  const text = chunk.toString();
@@ -145935,29 +146977,37 @@ async function runOpenCode(params) {
145935
146977
  for (const line of lines) {
145936
146978
  const trimmed = line.trim();
145937
146979
  if (!trimmed) continue;
146980
+ let event;
145938
146981
  try {
145939
- const event = JSON.parse(trimmed);
145940
- eventCount++;
145941
- log.debug(JSON.stringify(event, null, 2));
145942
- const timeSinceLastActivity = getIdleMs();
145943
- if (timeSinceLastActivity > 1e4) {
145944
- const activeToolCalls = toolCallTimings.size;
145945
- const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
145946
- log.info(
145947
- `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
145948
- );
145949
- }
145950
- markActivity();
145951
- const handler2 = handlers2[event.type];
145952
- if (handler2) {
145953
- await handler2(event);
145954
- } else {
145955
- log.info(
145956
- `\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
145957
- );
145958
- }
146982
+ event = JSON.parse(trimmed);
145959
146983
  } catch {
145960
146984
  log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
146985
+ continue;
146986
+ }
146987
+ eventCount++;
146988
+ log.debug(JSON.stringify(event, null, 2));
146989
+ const timeSinceLastActivity = getIdleMs();
146990
+ if (timeSinceLastActivity > 1e4) {
146991
+ const activeToolCalls = toolCallTimings.size;
146992
+ const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
146993
+ log.info(
146994
+ `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
146995
+ );
146996
+ }
146997
+ markActivity();
146998
+ const handler2 = handlers2[event.type];
146999
+ if (!handler2) {
147000
+ log.info(
147001
+ `\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
147002
+ );
147003
+ continue;
147004
+ }
147005
+ try {
147006
+ await handler2(event);
147007
+ } catch (err) {
147008
+ log.info(
147009
+ `\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
147010
+ );
145961
147011
  }
145962
147012
  }
145963
147013
  },
@@ -145991,16 +147041,9 @@ async function runOpenCode(params) {
145991
147041
  if (stderrContext) log.info(`\xBB last stderr output:
145992
147042
  ${stderrContext}`);
145993
147043
  }
145994
- if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
145995
- const totalTokens = accumulatedTokens.input + accumulatedTokens.output;
145996
- log.table([
145997
- [
145998
- { data: "Input Tokens", header: true },
145999
- { data: "Output Tokens", header: true },
146000
- { data: "Total Tokens", header: true }
146001
- ],
146002
- [String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
146003
- ]);
147044
+ if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
147045
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
147046
+ tokensLogged = true;
146004
147047
  }
146005
147048
  const usage = buildUsage();
146006
147049
  if (result.exitCode !== 0) {
@@ -146026,7 +147069,7 @@ ${stderrContext}`);
146026
147069
  params.todoTracker?.cancel();
146027
147070
  const duration4 = performance7.now() - startTime;
146028
147071
  const errorMessage = error49 instanceof Error ? error49.message : String(error49);
146029
- const isActivityTimeout = errorMessage.includes("activity timeout");
147072
+ const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
146030
147073
  const stderrContext = recentStderr.slice(-10).join("\n");
146031
147074
  const diagnosis = lastProviderError ? `likely cause: ${lastProviderError}` : eventCount === 0 ? "OpenCode produced 0 stdout events - check if the model provider is reachable" : `${eventCount} events were processed before the hang`;
146032
147075
  log.info(
@@ -146083,12 +147126,15 @@ var opencode = agent({
146083
147126
  cliPath,
146084
147127
  cwd: repoDir,
146085
147128
  env: env2,
146086
- todoTracker: ctx.todoTracker
147129
+ todoTracker: ctx.todoTracker,
147130
+ onActivityTimeout: ctx.onActivityTimeout,
147131
+ onToolUse: ctx.onToolUse
146087
147132
  };
146088
147133
  let result = await runOpenCode({
146089
147134
  ...runParams,
146090
147135
  args: [...baseArgs, ctx.instructions.full]
146091
147136
  });
147137
+ let aggregatedUsage = result.usage;
146092
147138
  for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
146093
147139
  if (!result.success) break;
146094
147140
  const status = getGitStatus();
@@ -146099,8 +147145,9 @@ ${status}`);
146099
147145
  ...runParams,
146100
147146
  args: [...baseArgs, "--continue", buildCommitPrompt("opencode", status)]
146101
147147
  });
147148
+ aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
146102
147149
  }
146103
- return result;
147150
+ return { ...result, usage: aggregatedUsage };
146104
147151
  }
146105
147152
  });
146106
147153
 
@@ -146118,13 +147165,11 @@ function hasClaudeCodeAuth() {
146118
147165
  function resolveModel(ctx) {
146119
147166
  const envModel = process.env.PULLFROG_MODEL?.trim();
146120
147167
  if (envModel) {
146121
- log.info(`\xBB model: ${envModel} (override via PULLFROG_MODEL)`);
146122
- return envModel;
147168
+ return resolveCliModel(envModel) ?? envModel;
146123
147169
  }
146124
147170
  if (ctx.slug) {
146125
147171
  const resolved = resolveCliModel(ctx.slug);
146126
147172
  if (resolved) {
146127
- log.info(`\xBB model: ${resolved} (resolved from ${ctx.slug})`);
146128
147173
  return resolved;
146129
147174
  }
146130
147175
  log.warning(`\xBB unknown model slug "${ctx.slug}" \u2014 agent will auto-select`);
@@ -146135,7 +147180,6 @@ function resolveAgent(ctx) {
146135
147180
  const envAgent = process.env.PULLFROG_AGENT?.trim();
146136
147181
  if (envAgent) {
146137
147182
  if (envAgent in agents) {
146138
- log.info(`\xBB agent: ${envAgent} (override via PULLFROG_AGENT)`);
146139
147183
  return agents[envAgent];
146140
147184
  }
146141
147185
  log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
@@ -146144,7 +147188,6 @@ function resolveAgent(ctx) {
146144
147188
  try {
146145
147189
  const provider2 = getModelProvider(ctx.model);
146146
147190
  if (provider2 === "anthropic" && hasClaudeCodeAuth()) {
146147
- log.info(`\xBB agent: claude (auto-selected for ${ctx.model})`);
146148
147191
  return agents.claude;
146149
147192
  }
146150
147193
  } catch {
@@ -150364,9 +151407,9 @@ async function startGitAuthServer(tmpdir3) {
150364
151407
  res.writeHead(409, { "Content-Type": "text/plain" });
150365
151408
  res.end("compromised");
150366
151409
  });
150367
- await new Promise((resolve2, reject) => {
151410
+ await new Promise((resolve3, reject) => {
150368
151411
  server.on("error", reject);
150369
- server.listen(0, "127.0.0.1", () => resolve2());
151412
+ server.listen(0, "127.0.0.1", () => resolve3());
150370
151413
  });
150371
151414
  const rawAddr = server.address();
150372
151415
  if (!rawAddr || typeof rawAddr === "string") {
@@ -150410,7 +151453,7 @@ async function startGitAuthServer(tmpdir3) {
150410
151453
  clearTimeout(entry.timeout);
150411
151454
  }
150412
151455
  codes.clear();
150413
- await new Promise((resolve2) => server.close(() => resolve2()));
151456
+ await new Promise((resolve3) => server.close(() => resolve3()));
150414
151457
  log.debug("git auth server closed");
150415
151458
  }
150416
151459
  return {
@@ -150597,7 +151640,7 @@ MCP servers provide tools you can call. Inspect your available MCP servers at st
150597
151640
 
150598
151641
  ### Git
150599
151642
 
150600
- Use \`${t("git")}\` for local git commands (status, log, diff, add, commit, checkout, branch, merge, etc.). For operations requiring remote authentication, use the dedicated MCP tools:
151643
+ Use \`${t("git")}\` for local git commands (status, log, add, commit, checkout, branch, merge, etc.). When reviewing a PR, do NOT re-derive the PR diff via \`git diff <base>..<head>\` \u2014 the diffPath returned by \`${t("checkout_pr")}\` is authoritative. \`git log\` and \`git diff --stat\` are fine for commit-range overview; \`git diff\` / \`git diff --cached\` are fine for inspecting your *own* uncommitted changes. For operations requiring remote authentication, use the dedicated MCP tools:
150601
151644
  - \`${t("push_branch")}\` - push current or specified branch
150602
151645
  - \`${t("git_fetch")}\` - fetch refs from remote
150603
151646
  - \`${t("checkout_pr")}\` - checkout a PR branch (fetches and configures push for forks)
@@ -150775,7 +151818,7 @@ function normalizeEnv() {
150775
151818
 
150776
151819
  // utils/payload.ts
150777
151820
  var core4 = __toESM(require_core(), 1);
150778
- import { isAbsolute, resolve } from "node:path";
151821
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
150779
151822
 
150780
151823
  // utils/versioning.ts
150781
151824
  var import_semver2 = __toESM(require_semver2(), 1);
@@ -150829,8 +151872,8 @@ function isPayloadEvent(value2) {
150829
151872
  function resolveCwd(cwd) {
150830
151873
  const workspace = process.env.GITHUB_WORKSPACE;
150831
151874
  if (!cwd) return workspace;
150832
- if (isAbsolute(cwd)) return cwd;
150833
- return workspace ? resolve(workspace, cwd) : cwd;
151875
+ if (isAbsolute2(cwd)) return cwd;
151876
+ return workspace ? resolve2(workspace, cwd) : cwd;
150834
151877
  }
150835
151878
  function resolvePromptInput() {
150836
151879
  const prompt = core4.getInput("prompt", { required: true });
@@ -151009,7 +152052,8 @@ var defaultSettings = {
151009
152052
  shell: "restricted",
151010
152053
  prApproveEnabled: false,
151011
152054
  modeInstructions: {},
151012
- learnings: null
152055
+ learnings: null,
152056
+ envAllowlist: null
151013
152057
  };
151014
152058
  var defaultRunContext = {
151015
152059
  settings: defaultSettings,
@@ -151089,7 +152133,7 @@ async function resolveRunContextData(params) {
151089
152133
  }
151090
152134
 
151091
152135
  // utils/setup.ts
151092
- import { execSync as execSync3 } from "node:child_process";
152136
+ import { execFileSync as execFileSync5, execSync as execSync3 } from "node:child_process";
151093
152137
  import { mkdtempSync } from "node:fs";
151094
152138
  import { tmpdir as tmpdir2 } from "node:os";
151095
152139
  import { join as join13 } from "node:path";
@@ -151099,6 +152143,51 @@ function createTempDirectory() {
151099
152143
  log.info(`\xBB created temp dir at ${sharedTempDir}`);
151100
152144
  return sharedTempDir;
151101
152145
  }
152146
+ function envScopedToRepo() {
152147
+ const scoped = { ...process.env };
152148
+ for (const key of Object.keys(scoped)) {
152149
+ if (key.startsWith("GIT_")) delete scoped[key];
152150
+ }
152151
+ return scoped;
152152
+ }
152153
+ function removeIncludeIfEntries(repoDir) {
152154
+ const env2 = envScopedToRepo();
152155
+ let configOutput;
152156
+ try {
152157
+ configOutput = execSync3("git config --local --get-regexp -z ^includeif\\.", {
152158
+ cwd: repoDir,
152159
+ encoding: "utf-8",
152160
+ stdio: "pipe",
152161
+ env: env2
152162
+ });
152163
+ } catch {
152164
+ log.debug("\xBB no includeIf credential entries to remove");
152165
+ return;
152166
+ }
152167
+ const seen = /* @__PURE__ */ new Set();
152168
+ for (const entry of configOutput.split("\0")) {
152169
+ if (!entry) continue;
152170
+ const nl = entry.indexOf("\n");
152171
+ const key = nl === -1 ? entry : entry.slice(0, nl);
152172
+ if (!key || seen.has(key)) continue;
152173
+ seen.add(key);
152174
+ try {
152175
+ execFileSync5("git", ["config", "--local", "--unset-all", key], {
152176
+ cwd: repoDir,
152177
+ stdio: "pipe",
152178
+ env: env2
152179
+ });
152180
+ } catch (error49) {
152181
+ log.debug(
152182
+ `\xBB failed to unset ${key}: ${error49 instanceof Error ? error49.message : String(error49)}`
152183
+ );
152184
+ }
152185
+ }
152186
+ if (seen.size > 0)
152187
+ log.info(
152188
+ `\xBB removed ${seen.size} includeIf credential ${seen.size === 1 ? "entry" : "entries"}`
152189
+ );
152190
+ }
151102
152191
  async function setupGit(params) {
151103
152192
  const repoDir = process.cwd();
151104
152193
  log.info("\xBB setting up git configuration...");
@@ -151145,24 +152234,7 @@ async function setupGit(params) {
151145
152234
  } catch {
151146
152235
  log.debug("\xBB no existing authentication headers to remove");
151147
152236
  }
151148
- try {
151149
- const configOutput = execSync3("git config --local --get-regexp ^includeif\\.", {
151150
- cwd: repoDir,
151151
- encoding: "utf-8",
151152
- stdio: "pipe"
151153
- });
151154
- for (const line of configOutput.trim().split("\n")) {
151155
- const key = line.split(" ")[0];
151156
- if (!key) continue;
151157
- execSync3(`git config --local --unset "${key}"`, {
151158
- cwd: repoDir,
151159
- stdio: "pipe"
151160
- });
151161
- }
151162
- log.info("\xBB removed includeIf credential entries");
151163
- } catch {
151164
- log.debug("\xBB no includeIf credential entries to remove");
151165
- }
152237
+ removeIncludeIfEntries(repoDir);
151166
152238
  const originUrl = `https://github.com/${params.owner}/${params.name}.git`;
151167
152239
  $("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
151168
152240
  params.toolState.pushUrl = originUrl;
@@ -151181,6 +152253,13 @@ function parseTimeString(input) {
151181
152253
  const seconds = parseInt(match3[3] || "0", 10);
151182
152254
  return (hours * 3600 + minutes * 60 + seconds) * 1e3;
151183
152255
  }
152256
+ var TIMEOUT_MAX_MS = 2147483647;
152257
+ function resolveTimeoutMs(input) {
152258
+ if (!input) return null;
152259
+ const parsed2 = parseTimeString(input);
152260
+ if (parsed2 === null || parsed2 <= 0 || parsed2 > TIMEOUT_MAX_MS) return null;
152261
+ return parsed2;
152262
+ }
151184
152263
 
151185
152264
  // utils/todoTracking.ts
151186
152265
  function isValidTodoStatus(value2) {
@@ -151283,9 +152362,12 @@ function createTodoTracker(onUpdate) {
151283
152362
  if (item.status === "in_progress") item.status = "completed";
151284
152363
  }
151285
152364
  },
151286
- renderCollapsible() {
152365
+ renderCollapsible(options) {
151287
152366
  if (state.size === 0) return "";
151288
- const todos = Array.from(state.values());
152367
+ const shouldCompleteInProgress = options?.completeInProgress === true;
152368
+ const todos = Array.from(state.values()).map(
152369
+ (item) => shouldCompleteInProgress && item.status === "in_progress" ? { ...item, status: "completed" } : item
152370
+ );
151289
152371
  const completed = todos.filter((t) => t.status === "completed").length;
151290
152372
  const markdown = renderTodoMarkdown(todos);
151291
152373
  return `<details>
@@ -151345,6 +152427,32 @@ function resolveOutputSchema() {
151345
152427
  log.info("\xBB structured output schema provided \u2014 output will be required");
151346
152428
  return parsed2;
151347
152429
  }
152430
+ function resolveTimeoutForLog(timeout) {
152431
+ if (!timeout) return "1h (default)";
152432
+ if (timeout === TIMEOUT_DISABLED) return "none (disabled)";
152433
+ return timeout;
152434
+ }
152435
+ function resolveModelForLog(ctx) {
152436
+ const envModel = process.env.PULLFROG_MODEL?.trim();
152437
+ if (envModel) return `${envModel} (override via PULLFROG_MODEL)`;
152438
+ if (ctx.payload.proxyModel) return `${ctx.payload.proxyModel} (proxy)`;
152439
+ if (ctx.resolvedModel && ctx.payload.model && ctx.payload.model !== ctx.resolvedModel) {
152440
+ return `${ctx.resolvedModel} (resolved from ${ctx.payload.model})`;
152441
+ }
152442
+ if (ctx.resolvedModel) return ctx.resolvedModel;
152443
+ if (ctx.payload.model) return `${ctx.payload.model} (unresolved)`;
152444
+ return "auto";
152445
+ }
152446
+ function resolveAgentForLog(ctx) {
152447
+ const envAgent = process.env.PULLFROG_AGENT?.trim();
152448
+ if (envAgent && envAgent === ctx.agentName) {
152449
+ return `${ctx.agentName} (override via PULLFROG_AGENT)`;
152450
+ }
152451
+ if (ctx.agentName === "claude" && ctx.resolvedModel) {
152452
+ return `${ctx.agentName} (auto-selected for ${ctx.resolvedModel})`;
152453
+ }
152454
+ return ctx.agentName;
152455
+ }
151348
152456
  async function mintProxyKey(ctx) {
151349
152457
  try {
151350
152458
  process.env.ACTIONS_ID_TOKEN_REQUEST_URL = ctx.oidcCredentials.requestUrl;
@@ -151404,6 +152512,7 @@ async function main() {
151404
152512
  }
151405
152513
  const timer = new Timer();
151406
152514
  let activityTimeout = null;
152515
+ let safetyNetTimer;
151407
152516
  const resolvedPromptInput = resolvePromptInput();
151408
152517
  const toolState = initToolState({
151409
152518
  progressCommentId: typeof resolvedPromptInput !== "string" ? resolvedPromptInput.progressCommentId : void 0
@@ -151423,6 +152532,9 @@ async function main() {
151423
152532
  const count = Object.keys(runContext.dbSecrets).length;
151424
152533
  if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
151425
152534
  }
152535
+ if (runContext.repoSettings.envAllowlist) {
152536
+ setEnvAllowlist(runContext.repoSettings.envAllowlist);
152537
+ }
151426
152538
  const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
151427
152539
  toolState.model = payload.model;
151428
152540
  if (payload.event.trigger === "pull_request_synchronize") {
@@ -151487,10 +152599,13 @@ async function main() {
151487
152599
  postCheckoutScript: runContext.repoSettings.postCheckoutScript
151488
152600
  });
151489
152601
  timer.checkpoint("git");
151490
- await executeLifecycleHook({
152602
+ const setupHook = await executeLifecycleHook({
151491
152603
  event: "setup",
151492
152604
  script: runContext.repoSettings.setupScript
151493
152605
  });
152606
+ if (setupHook.warning) {
152607
+ throw new Error(setupHook.warning);
152608
+ }
151494
152609
  timer.checkpoint("lifecycleHooks::setup");
151495
152610
  const agentId = agent2.name;
151496
152611
  const modes2 = [...computeModes(agentId), ...runContext.repoSettings.modes];
@@ -151512,17 +152627,22 @@ async function main() {
151512
152627
  runId: runInfo.runId,
151513
152628
  jobId: runInfo.jobId,
151514
152629
  mcpServerUrl: "",
151515
- tmpdir: tmpdir3
152630
+ tmpdir: tmpdir3,
152631
+ resolvedModel
151516
152632
  };
151517
152633
  const mcpHttpServer = __using(_stack, await startMcpHttpServer(toolContext, { outputSchema }), true);
151518
152634
  toolContext.mcpServerUrl = mcpHttpServer.url;
151519
152635
  log.info(`\xBB MCP server started at ${mcpHttpServer.url}`);
151520
152636
  timer.checkpoint("mcpServer");
151521
152637
  startInstallation(toolContext);
151522
- if (payload.model) log.info(`\xBB model: ${payload.model}`);
151523
- if (payload.timeout) log.info(`\xBB timeout: ${payload.timeout}`);
152638
+ const modelForLog = resolveModelForLog({ payload, resolvedModel });
152639
+ const agentForLog = resolveAgentForLog({ agentName: agent2.name, resolvedModel });
152640
+ const timeoutForLog = resolveTimeoutForLog(payload.timeout);
152641
+ log.info(`\xBB model: ${modelForLog}`);
152642
+ log.info(`\xBB agent: ${agentForLog}`);
151524
152643
  log.info(`\xBB push: ${payload.push}`);
151525
152644
  log.info(`\xBB shell: ${payload.shell}`);
152645
+ log.info(`\xBB timeout: ${timeoutForLog}`);
151526
152646
  const instructions = resolveInstructions({
151527
152647
  payload,
151528
152648
  repo: runContext.repo,
@@ -151571,24 +152691,62 @@ ${instructions.user}` : null,
151571
152691
  }
151572
152692
  });
151573
152693
  toolState.todoTracker = todoTracker;
152694
+ let innerTimeoutFired = false;
152695
+ const onInnerActivityTimeout = () => {
152696
+ if (innerTimeoutFired) return;
152697
+ innerTimeoutFired = true;
152698
+ log.info(
152699
+ "\xBB inner activity timeout fired \u2014 stopping MCP server and starting 5min safety-net timer"
152700
+ );
152701
+ mcpHttpServer[Symbol.asyncDispose]().catch((err) => {
152702
+ log.debug(
152703
+ `mcp server stop after inner kill failed: ${err instanceof Error ? err.message : String(err)}`
152704
+ );
152705
+ });
152706
+ safetyNetTimer = setTimeout(
152707
+ () => {
152708
+ activityTimeout?.forceReject(
152709
+ "agent still pending 5min after inner activity kill \u2014 forcing exit"
152710
+ );
152711
+ },
152712
+ 5 * 60 * 1e3
152713
+ );
152714
+ safetyNetTimer.unref?.();
152715
+ };
151574
152716
  const agentPromise = agent2.run({
151575
152717
  payload,
151576
152718
  resolvedModel,
151577
152719
  mcpServerUrl: mcpHttpServer.url,
151578
152720
  tmpdir: tmpdir3,
151579
152721
  instructions,
151580
- todoTracker
152722
+ todoTracker,
152723
+ onActivityTimeout: onInnerActivityTimeout,
152724
+ onToolUse: (event) => {
152725
+ const wasTracked = recordDiffReadFromToolUse({
152726
+ state: toolState.diffCoverage,
152727
+ toolName: event.toolName,
152728
+ input: event.input,
152729
+ cwd: process.cwd()
152730
+ });
152731
+ if (!wasTracked) return;
152732
+ const trackedRanges = toolState.diffCoverage?.coveredRanges ?? [];
152733
+ log.debug(
152734
+ `\xBB diff coverage tracked from tool ${event.toolName} (${trackedRanges.length} merged range${trackedRanges.length === 1 ? "" : "s"})`
152735
+ );
152736
+ }
152737
+ });
152738
+ agentPromise.catch(() => {
151581
152739
  });
151582
152740
  let result;
151583
152741
  if (payload.timeout === TIMEOUT_DISABLED) {
151584
152742
  result = await Promise.race([agentPromise, activityTimeout.promise]);
151585
152743
  } else {
151586
- const parsed2 = payload.timeout ? parseTimeString(payload.timeout) : null;
151587
- if (payload.timeout && parsed2 === null) {
151588
- log.warning(`invalid timeout format "${payload.timeout}", using default 1h`);
152744
+ const usable = resolveTimeoutMs(payload.timeout);
152745
+ if (payload.timeout && usable === null) {
152746
+ log.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
151589
152747
  }
151590
- const timeoutMs = parsed2 ?? 36e5;
151591
- const actualTimeout = parsed2 !== null ? payload.timeout : "1h";
152748
+ const timeoutMs = usable ?? 36e5;
152749
+ const actualTimeout = usable !== null ? payload.timeout : "1h";
151592
152750
  let timeoutId;
151593
152751
  const timeoutPromise = new Promise((_3, reject) => {
151594
152752
  timeoutId = setTimeout(() => {
@@ -151675,8 +152833,21 @@ ${errorMessage}
151675
152833
  };
151676
152834
  } finally {
151677
152835
  activityTimeout?.stop();
152836
+ if (safetyNetTimer) clearTimeout(safetyNetTimer);
151678
152837
  if (usageSummaryPath) {
151679
- await writeGitHubUsageSummaryToFile(usageSummaryPath);
152838
+ try {
152839
+ await writeGitHubUsageSummaryToFile(usageSummaryPath);
152840
+ } catch (err) {
152841
+ log.debug(
152842
+ `failed to write usage summary to ${usageSummaryPath}: ${err instanceof Error ? err.message : String(err)}`
152843
+ );
152844
+ }
152845
+ }
152846
+ if (toolContext) {
152847
+ const patch = aggregateUsage(toolState.usageEntries);
152848
+ if (Object.keys(patch).length > 0) {
152849
+ await patchWorkflowRunFields(toolContext, patch);
152850
+ }
151680
152851
  }
151681
152852
  }
151682
152853
  } catch (_2) {