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/cli.mjs CHANGED
@@ -4293,8 +4293,8 @@ var require_util2 = __commonJS({
4293
4293
  function createDeferredPromise() {
4294
4294
  let res;
4295
4295
  let rej;
4296
- const promise2 = new Promise((resolve2, reject) => {
4297
- res = resolve2;
4296
+ const promise2 = new Promise((resolve3, reject) => {
4297
+ res = resolve3;
4298
4298
  rej = reject;
4299
4299
  });
4300
4300
  return { promise: promise2, resolve: res, reject: rej };
@@ -5798,8 +5798,8 @@ Content-Type: ${value2.type || "application/octet-stream"}\r
5798
5798
  });
5799
5799
  }
5800
5800
  });
5801
- const busboyResolve = new Promise((resolve2, reject) => {
5802
- busboy.on("finish", resolve2);
5801
+ const busboyResolve = new Promise((resolve3, reject) => {
5802
+ busboy.on("finish", resolve3);
5803
5803
  busboy.on("error", (err) => reject(new TypeError(err)));
5804
5804
  });
5805
5805
  if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
@@ -6333,9 +6333,9 @@ var require_dispatcher_base = __commonJS({
6333
6333
  }
6334
6334
  close(callback) {
6335
6335
  if (callback === void 0) {
6336
- return new Promise((resolve2, reject) => {
6336
+ return new Promise((resolve3, reject) => {
6337
6337
  this.close((err, data) => {
6338
- return err ? reject(err) : resolve2(data);
6338
+ return err ? reject(err) : resolve3(data);
6339
6339
  });
6340
6340
  });
6341
6341
  }
@@ -6373,12 +6373,12 @@ var require_dispatcher_base = __commonJS({
6373
6373
  err = null;
6374
6374
  }
6375
6375
  if (callback === void 0) {
6376
- return new Promise((resolve2, reject) => {
6376
+ return new Promise((resolve3, reject) => {
6377
6377
  this.destroy(err, (err2, data) => {
6378
6378
  return err2 ? (
6379
6379
  /* istanbul ignore next: should never error */
6380
6380
  reject(err2)
6381
- ) : resolve2(data);
6381
+ ) : resolve3(data);
6382
6382
  });
6383
6383
  });
6384
6384
  }
@@ -7438,16 +7438,16 @@ var require_client = __commonJS({
7438
7438
  return this[kNeedDrain] < 2;
7439
7439
  }
7440
7440
  async [kClose]() {
7441
- return new Promise((resolve2) => {
7441
+ return new Promise((resolve3) => {
7442
7442
  if (!this[kSize]) {
7443
- resolve2(null);
7443
+ resolve3(null);
7444
7444
  } else {
7445
- this[kClosedResolve] = resolve2;
7445
+ this[kClosedResolve] = resolve3;
7446
7446
  }
7447
7447
  });
7448
7448
  }
7449
7449
  async [kDestroy](err) {
7450
- return new Promise((resolve2) => {
7450
+ return new Promise((resolve3) => {
7451
7451
  const requests = this[kQueue].splice(this[kPendingIdx]);
7452
7452
  for (let i = 0; i < requests.length; i++) {
7453
7453
  const request2 = requests[i];
@@ -7458,7 +7458,7 @@ var require_client = __commonJS({
7458
7458
  this[kClosedResolve]();
7459
7459
  this[kClosedResolve] = null;
7460
7460
  }
7461
- resolve2();
7461
+ resolve3();
7462
7462
  };
7463
7463
  if (this[kHTTP2Session] != null) {
7464
7464
  util2.destroy(this[kHTTP2Session], err);
@@ -8038,7 +8038,7 @@ var require_client = __commonJS({
8038
8038
  });
8039
8039
  }
8040
8040
  try {
8041
- const socket = await new Promise((resolve2, reject) => {
8041
+ const socket = await new Promise((resolve3, reject) => {
8042
8042
  client[kConnector]({
8043
8043
  host,
8044
8044
  hostname: hostname4,
@@ -8050,7 +8050,7 @@ var require_client = __commonJS({
8050
8050
  if (err) {
8051
8051
  reject(err);
8052
8052
  } else {
8053
- resolve2(socket2);
8053
+ resolve3(socket2);
8054
8054
  }
8055
8055
  });
8056
8056
  });
@@ -8674,12 +8674,12 @@ upgrade: ${upgrade}\r
8674
8674
  cb();
8675
8675
  }
8676
8676
  }
8677
- const waitForDrain = () => new Promise((resolve2, reject) => {
8677
+ const waitForDrain = () => new Promise((resolve3, reject) => {
8678
8678
  assert3(callback === null);
8679
8679
  if (socket[kError]) {
8680
8680
  reject(socket[kError]);
8681
8681
  } else {
8682
- callback = resolve2;
8682
+ callback = resolve3;
8683
8683
  }
8684
8684
  });
8685
8685
  if (client[kHTTPConnVersion] === "h2") {
@@ -9024,8 +9024,8 @@ var require_pool_base = __commonJS({
9024
9024
  if (this[kQueue].isEmpty()) {
9025
9025
  return Promise.all(this[kClients].map((c2) => c2.close()));
9026
9026
  } else {
9027
- return new Promise((resolve2) => {
9028
- this[kClosedResolve] = resolve2;
9027
+ return new Promise((resolve3) => {
9028
+ this[kClosedResolve] = resolve3;
9029
9029
  });
9030
9030
  }
9031
9031
  }
@@ -9603,7 +9603,7 @@ var require_readable = __commonJS({
9603
9603
  if (this.closed) {
9604
9604
  return Promise.resolve(null);
9605
9605
  }
9606
- return new Promise((resolve2, reject) => {
9606
+ return new Promise((resolve3, reject) => {
9607
9607
  const signalListenerCleanup = signal ? util2.addAbortListener(signal, () => {
9608
9608
  this.destroy();
9609
9609
  }) : noop4;
@@ -9612,7 +9612,7 @@ var require_readable = __commonJS({
9612
9612
  if (signal && signal.aborted) {
9613
9613
  reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
9614
9614
  } else {
9615
- resolve2(null);
9615
+ resolve3(null);
9616
9616
  }
9617
9617
  }).on("error", noop4).on("data", function(chunk) {
9618
9618
  limit -= chunk.length;
@@ -9634,11 +9634,11 @@ var require_readable = __commonJS({
9634
9634
  throw new TypeError("unusable");
9635
9635
  }
9636
9636
  assert3(!stream[kConsume]);
9637
- return new Promise((resolve2, reject) => {
9637
+ return new Promise((resolve3, reject) => {
9638
9638
  stream[kConsume] = {
9639
9639
  type: type2,
9640
9640
  stream,
9641
- resolve: resolve2,
9641
+ resolve: resolve3,
9642
9642
  reject,
9643
9643
  length: 0,
9644
9644
  body: []
@@ -9673,12 +9673,12 @@ var require_readable = __commonJS({
9673
9673
  }
9674
9674
  }
9675
9675
  function consumeEnd(consume2) {
9676
- const { type: type2, body, resolve: resolve2, stream, length } = consume2;
9676
+ const { type: type2, body, resolve: resolve3, stream, length } = consume2;
9677
9677
  try {
9678
9678
  if (type2 === "text") {
9679
- resolve2(toUSVString(Buffer.concat(body)));
9679
+ resolve3(toUSVString(Buffer.concat(body)));
9680
9680
  } else if (type2 === "json") {
9681
- resolve2(JSON.parse(Buffer.concat(body)));
9681
+ resolve3(JSON.parse(Buffer.concat(body)));
9682
9682
  } else if (type2 === "arrayBuffer") {
9683
9683
  const dst = new Uint8Array(length);
9684
9684
  let pos = 0;
@@ -9686,12 +9686,12 @@ var require_readable = __commonJS({
9686
9686
  dst.set(buf, pos);
9687
9687
  pos += buf.byteLength;
9688
9688
  }
9689
- resolve2(dst.buffer);
9689
+ resolve3(dst.buffer);
9690
9690
  } else if (type2 === "blob") {
9691
9691
  if (!Blob2) {
9692
9692
  Blob2 = __require("buffer").Blob;
9693
9693
  }
9694
- resolve2(new Blob2(body, { type: stream[kContentType] }));
9694
+ resolve3(new Blob2(body, { type: stream[kContentType] }));
9695
9695
  }
9696
9696
  consumeFinish(consume2);
9697
9697
  } catch (err) {
@@ -9946,9 +9946,9 @@ var require_api_request = __commonJS({
9946
9946
  };
9947
9947
  function request2(opts, callback) {
9948
9948
  if (callback === void 0) {
9949
- return new Promise((resolve2, reject) => {
9949
+ return new Promise((resolve3, reject) => {
9950
9950
  request2.call(this, opts, (err, data) => {
9951
- return err ? reject(err) : resolve2(data);
9951
+ return err ? reject(err) : resolve3(data);
9952
9952
  });
9953
9953
  });
9954
9954
  }
@@ -10121,9 +10121,9 @@ var require_api_stream = __commonJS({
10121
10121
  };
10122
10122
  function stream(opts, factory, callback) {
10123
10123
  if (callback === void 0) {
10124
- return new Promise((resolve2, reject) => {
10124
+ return new Promise((resolve3, reject) => {
10125
10125
  stream.call(this, opts, factory, (err, data) => {
10126
- return err ? reject(err) : resolve2(data);
10126
+ return err ? reject(err) : resolve3(data);
10127
10127
  });
10128
10128
  });
10129
10129
  }
@@ -10404,9 +10404,9 @@ var require_api_upgrade = __commonJS({
10404
10404
  };
10405
10405
  function upgrade(opts, callback) {
10406
10406
  if (callback === void 0) {
10407
- return new Promise((resolve2, reject) => {
10407
+ return new Promise((resolve3, reject) => {
10408
10408
  upgrade.call(this, opts, (err, data) => {
10409
- return err ? reject(err) : resolve2(data);
10409
+ return err ? reject(err) : resolve3(data);
10410
10410
  });
10411
10411
  });
10412
10412
  }
@@ -10495,9 +10495,9 @@ var require_api_connect = __commonJS({
10495
10495
  };
10496
10496
  function connect(opts, callback) {
10497
10497
  if (callback === void 0) {
10498
- return new Promise((resolve2, reject) => {
10498
+ return new Promise((resolve3, reject) => {
10499
10499
  connect.call(this, opts, (err, data) => {
10500
- return err ? reject(err) : resolve2(data);
10500
+ return err ? reject(err) : resolve3(data);
10501
10501
  });
10502
10502
  });
10503
10503
  }
@@ -14119,7 +14119,7 @@ var require_fetch = __commonJS({
14119
14119
  async function dispatch({ body }) {
14120
14120
  const url4 = requestCurrentURL(request2);
14121
14121
  const agent2 = fetchParams.controller.dispatcher;
14122
- return new Promise((resolve2, reject) => agent2.dispatch(
14122
+ return new Promise((resolve3, reject) => agent2.dispatch(
14123
14123
  {
14124
14124
  path: url4.pathname + url4.search,
14125
14125
  origin: url4.origin,
@@ -14195,7 +14195,7 @@ var require_fetch = __commonJS({
14195
14195
  }
14196
14196
  }
14197
14197
  }
14198
- resolve2({
14198
+ resolve3({
14199
14199
  status,
14200
14200
  statusText,
14201
14201
  headersList: headers[kHeadersList],
@@ -14238,7 +14238,7 @@ var require_fetch = __commonJS({
14238
14238
  const val = headersList[n + 1].toString("latin1");
14239
14239
  headers[kHeadersList].append(key, val);
14240
14240
  }
14241
- resolve2({
14241
+ resolve3({
14242
14242
  status,
14243
14243
  statusText: STATUS_CODES[status],
14244
14244
  headersList: headers[kHeadersList],
@@ -17592,11 +17592,11 @@ var require_lib = __commonJS({
17592
17592
  };
17593
17593
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
17594
17594
  function adopt(value2) {
17595
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
17596
- resolve2(value2);
17595
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
17596
+ resolve3(value2);
17597
17597
  });
17598
17598
  }
17599
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
17599
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
17600
17600
  function fulfilled(value2) {
17601
17601
  try {
17602
17602
  step(generator.next(value2));
@@ -17612,7 +17612,7 @@ var require_lib = __commonJS({
17612
17612
  }
17613
17613
  }
17614
17614
  function step(result) {
17615
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
17615
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
17616
17616
  }
17617
17617
  step((generator = generator.apply(thisArg, _arguments || [])).next());
17618
17618
  });
@@ -17698,26 +17698,26 @@ var require_lib = __commonJS({
17698
17698
  }
17699
17699
  readBody() {
17700
17700
  return __awaiter(this, void 0, void 0, function* () {
17701
- return new Promise((resolve2) => __awaiter(this, void 0, void 0, function* () {
17701
+ return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
17702
17702
  let output = Buffer.alloc(0);
17703
17703
  this.message.on("data", (chunk) => {
17704
17704
  output = Buffer.concat([output, chunk]);
17705
17705
  });
17706
17706
  this.message.on("end", () => {
17707
- resolve2(output.toString());
17707
+ resolve3(output.toString());
17708
17708
  });
17709
17709
  }));
17710
17710
  });
17711
17711
  }
17712
17712
  readBodyBuffer() {
17713
17713
  return __awaiter(this, void 0, void 0, function* () {
17714
- return new Promise((resolve2) => __awaiter(this, void 0, void 0, function* () {
17714
+ return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
17715
17715
  const chunks = [];
17716
17716
  this.message.on("data", (chunk) => {
17717
17717
  chunks.push(chunk);
17718
17718
  });
17719
17719
  this.message.on("end", () => {
17720
- resolve2(Buffer.concat(chunks));
17720
+ resolve3(Buffer.concat(chunks));
17721
17721
  });
17722
17722
  }));
17723
17723
  });
@@ -17926,14 +17926,14 @@ var require_lib = __commonJS({
17926
17926
  */
17927
17927
  requestRaw(info3, data) {
17928
17928
  return __awaiter(this, void 0, void 0, function* () {
17929
- return new Promise((resolve2, reject) => {
17929
+ return new Promise((resolve3, reject) => {
17930
17930
  function callbackForResult(err, res) {
17931
17931
  if (err) {
17932
17932
  reject(err);
17933
17933
  } else if (!res) {
17934
17934
  reject(new Error("Unknown error"));
17935
17935
  } else {
17936
- resolve2(res);
17936
+ resolve3(res);
17937
17937
  }
17938
17938
  }
17939
17939
  this.requestRawWithCallback(info3, data, callbackForResult);
@@ -18115,12 +18115,12 @@ var require_lib = __commonJS({
18115
18115
  return __awaiter(this, void 0, void 0, function* () {
18116
18116
  retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
18117
18117
  const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
18118
- return new Promise((resolve2) => setTimeout(() => resolve2(), ms));
18118
+ return new Promise((resolve3) => setTimeout(() => resolve3(), ms));
18119
18119
  });
18120
18120
  }
18121
18121
  _processResponse(res, options) {
18122
18122
  return __awaiter(this, void 0, void 0, function* () {
18123
- return new Promise((resolve2, reject) => __awaiter(this, void 0, void 0, function* () {
18123
+ return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
18124
18124
  const statusCode = res.message.statusCode || 0;
18125
18125
  const response = {
18126
18126
  statusCode,
@@ -18128,7 +18128,7 @@ var require_lib = __commonJS({
18128
18128
  headers: {}
18129
18129
  };
18130
18130
  if (statusCode === HttpCodes.NotFound) {
18131
- resolve2(response);
18131
+ resolve3(response);
18132
18132
  }
18133
18133
  function dateTimeDeserializer(key, value2) {
18134
18134
  if (typeof value2 === "string") {
@@ -18167,7 +18167,7 @@ var require_lib = __commonJS({
18167
18167
  err.result = response.result;
18168
18168
  reject(err);
18169
18169
  } else {
18170
- resolve2(response);
18170
+ resolve3(response);
18171
18171
  }
18172
18172
  }));
18173
18173
  });
@@ -18184,11 +18184,11 @@ var require_auth = __commonJS({
18184
18184
  "use strict";
18185
18185
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
18186
18186
  function adopt(value2) {
18187
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
18188
- resolve2(value2);
18187
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
18188
+ resolve3(value2);
18189
18189
  });
18190
18190
  }
18191
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
18191
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
18192
18192
  function fulfilled(value2) {
18193
18193
  try {
18194
18194
  step(generator.next(value2));
@@ -18204,7 +18204,7 @@ var require_auth = __commonJS({
18204
18204
  }
18205
18205
  }
18206
18206
  function step(result) {
18207
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18207
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18208
18208
  }
18209
18209
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18210
18210
  });
@@ -18288,11 +18288,11 @@ var require_oidc_utils = __commonJS({
18288
18288
  "use strict";
18289
18289
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
18290
18290
  function adopt(value2) {
18291
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
18292
- resolve2(value2);
18291
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
18292
+ resolve3(value2);
18293
18293
  });
18294
18294
  }
18295
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
18295
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
18296
18296
  function fulfilled(value2) {
18297
18297
  try {
18298
18298
  step(generator.next(value2));
@@ -18308,7 +18308,7 @@ var require_oidc_utils = __commonJS({
18308
18308
  }
18309
18309
  }
18310
18310
  function step(result) {
18311
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18311
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18312
18312
  }
18313
18313
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18314
18314
  });
@@ -18386,11 +18386,11 @@ var require_summary = __commonJS({
18386
18386
  "use strict";
18387
18387
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
18388
18388
  function adopt(value2) {
18389
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
18390
- resolve2(value2);
18389
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
18390
+ resolve3(value2);
18391
18391
  });
18392
18392
  }
18393
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
18393
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
18394
18394
  function fulfilled(value2) {
18395
18395
  try {
18396
18396
  step(generator.next(value2));
@@ -18406,7 +18406,7 @@ var require_summary = __commonJS({
18406
18406
  }
18407
18407
  }
18408
18408
  function step(result) {
18409
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18409
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18410
18410
  }
18411
18411
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18412
18412
  });
@@ -18752,11 +18752,11 @@ var require_io_util = __commonJS({
18752
18752
  };
18753
18753
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
18754
18754
  function adopt(value2) {
18755
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
18756
- resolve2(value2);
18755
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
18756
+ resolve3(value2);
18757
18757
  });
18758
18758
  }
18759
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
18759
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
18760
18760
  function fulfilled(value2) {
18761
18761
  try {
18762
18762
  step(generator.next(value2));
@@ -18772,7 +18772,7 @@ var require_io_util = __commonJS({
18772
18772
  }
18773
18773
  }
18774
18774
  function step(result) {
18775
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18775
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18776
18776
  }
18777
18777
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18778
18778
  });
@@ -18925,11 +18925,11 @@ var require_io = __commonJS({
18925
18925
  };
18926
18926
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
18927
18927
  function adopt(value2) {
18928
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
18929
- resolve2(value2);
18928
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
18929
+ resolve3(value2);
18930
18930
  });
18931
18931
  }
18932
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
18932
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
18933
18933
  function fulfilled(value2) {
18934
18934
  try {
18935
18935
  step(generator.next(value2));
@@ -18945,7 +18945,7 @@ var require_io = __commonJS({
18945
18945
  }
18946
18946
  }
18947
18947
  function step(result) {
18948
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
18948
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
18949
18949
  }
18950
18950
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18951
18951
  });
@@ -19173,11 +19173,11 @@ var require_toolrunner = __commonJS({
19173
19173
  };
19174
19174
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
19175
19175
  function adopt(value2) {
19176
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
19177
- resolve2(value2);
19176
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
19177
+ resolve3(value2);
19178
19178
  });
19179
19179
  }
19180
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
19180
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
19181
19181
  function fulfilled(value2) {
19182
19182
  try {
19183
19183
  step(generator.next(value2));
@@ -19193,7 +19193,7 @@ var require_toolrunner = __commonJS({
19193
19193
  }
19194
19194
  }
19195
19195
  function step(result) {
19196
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19196
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19197
19197
  }
19198
19198
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19199
19199
  });
@@ -19421,7 +19421,7 @@ var require_toolrunner = __commonJS({
19421
19421
  this.toolPath = path3.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
19422
19422
  }
19423
19423
  this.toolPath = yield io.which(this.toolPath, true);
19424
- return new Promise((resolve2, reject) => __awaiter(this, void 0, void 0, function* () {
19424
+ return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
19425
19425
  this._debug(`exec tool: ${this.toolPath}`);
19426
19426
  this._debug("arguments:");
19427
19427
  for (const arg4 of this.args) {
@@ -19504,7 +19504,7 @@ var require_toolrunner = __commonJS({
19504
19504
  if (error49) {
19505
19505
  reject(error49);
19506
19506
  } else {
19507
- resolve2(exitCode);
19507
+ resolve3(exitCode);
19508
19508
  }
19509
19509
  });
19510
19510
  if (this.options.input) {
@@ -19657,11 +19657,11 @@ var require_exec = __commonJS({
19657
19657
  };
19658
19658
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
19659
19659
  function adopt(value2) {
19660
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
19661
- resolve2(value2);
19660
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
19661
+ resolve3(value2);
19662
19662
  });
19663
19663
  }
19664
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
19664
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
19665
19665
  function fulfilled(value2) {
19666
19666
  try {
19667
19667
  step(generator.next(value2));
@@ -19677,7 +19677,7 @@ var require_exec = __commonJS({
19677
19677
  }
19678
19678
  }
19679
19679
  function step(result) {
19680
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19680
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19681
19681
  }
19682
19682
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19683
19683
  });
@@ -19768,11 +19768,11 @@ var require_platform = __commonJS({
19768
19768
  };
19769
19769
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
19770
19770
  function adopt(value2) {
19771
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
19772
- resolve2(value2);
19771
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
19772
+ resolve3(value2);
19773
19773
  });
19774
19774
  }
19775
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
19775
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
19776
19776
  function fulfilled(value2) {
19777
19777
  try {
19778
19778
  step(generator.next(value2));
@@ -19788,7 +19788,7 @@ var require_platform = __commonJS({
19788
19788
  }
19789
19789
  }
19790
19790
  function step(result) {
19791
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19791
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19792
19792
  }
19793
19793
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19794
19794
  });
@@ -19887,11 +19887,11 @@ var require_core = __commonJS({
19887
19887
  };
19888
19888
  var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
19889
19889
  function adopt(value2) {
19890
- return value2 instanceof P3 ? value2 : new P3(function(resolve2) {
19891
- resolve2(value2);
19890
+ return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
19891
+ resolve3(value2);
19892
19892
  });
19893
19893
  }
19894
- return new (P3 || (P3 = Promise))(function(resolve2, reject) {
19894
+ return new (P3 || (P3 = Promise))(function(resolve3, reject) {
19895
19895
  function fulfilled(value2) {
19896
19896
  try {
19897
19897
  step(generator.next(value2));
@@ -19907,7 +19907,7 @@ var require_core = __commonJS({
19907
19907
  }
19908
19908
  }
19909
19909
  function step(result) {
19910
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19910
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19911
19911
  }
19912
19912
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19913
19913
  });
@@ -45598,8 +45598,8 @@ var require_resolve = __commonJS({
45598
45598
  }
45599
45599
  return count;
45600
45600
  }
45601
- function getFullPath(resolver, id = "", normalize2) {
45602
- if (normalize2 !== false)
45601
+ function getFullPath(resolver, id = "", normalize3) {
45602
+ if (normalize3 !== false)
45603
45603
  id = normalizeId(id);
45604
45604
  const p2 = resolver.parse(id);
45605
45605
  return _getFullPath(resolver, p2);
@@ -46347,7 +46347,7 @@ var require_compile = __commonJS({
46347
46347
  const schOrFunc = root.refs[ref];
46348
46348
  if (schOrFunc)
46349
46349
  return schOrFunc;
46350
- let _sch = resolve2.call(this, root, ref);
46350
+ let _sch = resolve3.call(this, root, ref);
46351
46351
  if (_sch === void 0) {
46352
46352
  const schema2 = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
46353
46353
  const { schemaId } = this.opts;
@@ -46374,7 +46374,7 @@ var require_compile = __commonJS({
46374
46374
  function sameSchemaEnv(s1, s2) {
46375
46375
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
46376
46376
  }
46377
- function resolve2(root, ref) {
46377
+ function resolve3(root, ref) {
46378
46378
  let sch;
46379
46379
  while (typeof (sch = this.refs[ref]) == "string")
46380
46380
  ref = sch;
@@ -46939,7 +46939,7 @@ var require_fast_uri = __commonJS({
46939
46939
  "use strict";
46940
46940
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils4();
46941
46941
  var { SCHEMES, getSchemeHandler } = require_schemes();
46942
- function normalize2(uri, options) {
46942
+ function normalize3(uri, options) {
46943
46943
  if (typeof uri === "string") {
46944
46944
  uri = /** @type {T} */
46945
46945
  serialize(parse5(uri, options), options);
@@ -46949,7 +46949,7 @@ var require_fast_uri = __commonJS({
46949
46949
  }
46950
46950
  return uri;
46951
46951
  }
46952
- function resolve2(baseURI, relativeURI, options) {
46952
+ function resolve3(baseURI, relativeURI, options) {
46953
46953
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
46954
46954
  const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
46955
46955
  schemelessOptions.skipEscape = true;
@@ -47175,8 +47175,8 @@ var require_fast_uri = __commonJS({
47175
47175
  }
47176
47176
  var fastUri = {
47177
47177
  SCHEMES,
47178
- normalize: normalize2,
47179
- resolve: resolve2,
47178
+ normalize: normalize3,
47179
+ resolve: resolve3,
47180
47180
  resolveComponent,
47181
47181
  equal,
47182
47182
  serialize,
@@ -52752,9 +52752,9 @@ var require_dispatcher_base2 = __commonJS({
52752
52752
  }
52753
52753
  close(callback) {
52754
52754
  if (callback === void 0) {
52755
- return new Promise((resolve2, reject) => {
52755
+ return new Promise((resolve3, reject) => {
52756
52756
  this.close((err, data) => {
52757
- return err ? reject(err) : resolve2(data);
52757
+ return err ? reject(err) : resolve3(data);
52758
52758
  });
52759
52759
  });
52760
52760
  }
@@ -52792,9 +52792,9 @@ var require_dispatcher_base2 = __commonJS({
52792
52792
  err = null;
52793
52793
  }
52794
52794
  if (callback === void 0) {
52795
- return new Promise((resolve2, reject) => {
52795
+ return new Promise((resolve3, reject) => {
52796
52796
  this.destroy(err, (err2, data) => {
52797
- return err2 ? reject(err2) : resolve2(data);
52797
+ return err2 ? reject(err2) : resolve3(data);
52798
52798
  });
52799
52799
  });
52800
52800
  }
@@ -56270,8 +56270,8 @@ var require_promise = __commonJS({
56270
56270
  function createDeferredPromise() {
56271
56271
  let res;
56272
56272
  let rej;
56273
- const promise2 = new Promise((resolve2, reject) => {
56274
- res = resolve2;
56273
+ const promise2 = new Promise((resolve3, reject) => {
56274
+ res = resolve3;
56275
56275
  rej = reject;
56276
56276
  });
56277
56277
  return { promise: promise2, resolve: res, reject: rej };
@@ -57569,12 +57569,12 @@ upgrade: ${upgrade}\r
57569
57569
  cb();
57570
57570
  }
57571
57571
  }
57572
- const waitForDrain = () => new Promise((resolve2, reject) => {
57572
+ const waitForDrain = () => new Promise((resolve3, reject) => {
57573
57573
  assert3(callback === null);
57574
57574
  if (socket[kError]) {
57575
57575
  reject(socket[kError]);
57576
57576
  } else {
57577
- callback = resolve2;
57577
+ callback = resolve3;
57578
57578
  }
57579
57579
  });
57580
57580
  socket.on("close", onDrain).on("drain", onDrain);
@@ -58415,12 +58415,12 @@ var require_client_h2 = __commonJS({
58415
58415
  cb();
58416
58416
  }
58417
58417
  }
58418
- const waitForDrain = () => new Promise((resolve2, reject) => {
58418
+ const waitForDrain = () => new Promise((resolve3, reject) => {
58419
58419
  assert3(callback === null);
58420
58420
  if (socket[kError]) {
58421
58421
  reject(socket[kError]);
58422
58422
  } else {
58423
- callback = resolve2;
58423
+ callback = resolve3;
58424
58424
  }
58425
58425
  });
58426
58426
  h2stream.on("close", onDrain).on("drain", onDrain);
@@ -58728,16 +58728,16 @@ var require_client2 = __commonJS({
58728
58728
  return this[kNeedDrain] < 2;
58729
58729
  }
58730
58730
  [kClose]() {
58731
- return new Promise((resolve2) => {
58731
+ return new Promise((resolve3) => {
58732
58732
  if (this[kSize]) {
58733
- this[kClosedResolve] = resolve2;
58733
+ this[kClosedResolve] = resolve3;
58734
58734
  } else {
58735
- resolve2(null);
58735
+ resolve3(null);
58736
58736
  }
58737
58737
  });
58738
58738
  }
58739
58739
  [kDestroy](err) {
58740
- return new Promise((resolve2) => {
58740
+ return new Promise((resolve3) => {
58741
58741
  const requests = this[kQueue].splice(this[kPendingIdx]);
58742
58742
  for (let i = 0; i < requests.length; i++) {
58743
58743
  const request2 = requests[i];
@@ -58748,7 +58748,7 @@ var require_client2 = __commonJS({
58748
58748
  this[kClosedResolve]();
58749
58749
  this[kClosedResolve] = null;
58750
58750
  }
58751
- resolve2(null);
58751
+ resolve3(null);
58752
58752
  };
58753
58753
  if (this[kHTTPContext]) {
58754
58754
  this[kHTTPContext].destroy(err, callback);
@@ -59145,8 +59145,8 @@ var require_pool_base2 = __commonJS({
59145
59145
  }
59146
59146
  return Promise.all(closeAll);
59147
59147
  } else {
59148
- return new Promise((resolve2) => {
59149
- this[kClosedResolve] = resolve2;
59148
+ return new Promise((resolve3) => {
59149
+ this[kClosedResolve] = resolve3;
59150
59150
  });
59151
59151
  }
59152
59152
  }
@@ -60675,7 +60675,7 @@ var require_readable2 = __commonJS({
60675
60675
  if (this._readableState.closeEmitted) {
60676
60676
  return Promise.resolve(null);
60677
60677
  }
60678
- return new Promise((resolve2, reject) => {
60678
+ return new Promise((resolve3, reject) => {
60679
60679
  if (this[kContentLength] && this[kContentLength] > limit || this[kBytesRead] > limit) {
60680
60680
  this.destroy(new AbortError2());
60681
60681
  }
@@ -60689,11 +60689,11 @@ var require_readable2 = __commonJS({
60689
60689
  if (signal.aborted) {
60690
60690
  reject(signal.reason ?? new AbortError2());
60691
60691
  } else {
60692
- resolve2(null);
60692
+ resolve3(null);
60693
60693
  }
60694
60694
  });
60695
60695
  } else {
60696
- this.on("close", resolve2);
60696
+ this.on("close", resolve3);
60697
60697
  }
60698
60698
  this.on("error", noop4).on("data", () => {
60699
60699
  if (this[kBytesRead] > limit) {
@@ -60721,7 +60721,7 @@ var require_readable2 = __commonJS({
60721
60721
  }
60722
60722
  function consume(stream, type2) {
60723
60723
  assert3(!stream[kConsume]);
60724
- return new Promise((resolve2, reject) => {
60724
+ return new Promise((resolve3, reject) => {
60725
60725
  if (isUnusable(stream)) {
60726
60726
  const rState = stream._readableState;
60727
60727
  if (rState.destroyed && rState.closeEmitted === false) {
@@ -60736,7 +60736,7 @@ var require_readable2 = __commonJS({
60736
60736
  stream[kConsume] = {
60737
60737
  type: type2,
60738
60738
  stream,
60739
- resolve: resolve2,
60739
+ resolve: resolve3,
60740
60740
  reject,
60741
60741
  length: 0,
60742
60742
  body: []
@@ -60810,18 +60810,18 @@ var require_readable2 = __commonJS({
60810
60810
  return buffer;
60811
60811
  }
60812
60812
  function consumeEnd(consume2, encoding) {
60813
- const { type: type2, body, resolve: resolve2, stream, length } = consume2;
60813
+ const { type: type2, body, resolve: resolve3, stream, length } = consume2;
60814
60814
  try {
60815
60815
  if (type2 === "text") {
60816
- resolve2(chunksDecode(body, length, encoding));
60816
+ resolve3(chunksDecode(body, length, encoding));
60817
60817
  } else if (type2 === "json") {
60818
- resolve2(JSON.parse(chunksDecode(body, length, encoding)));
60818
+ resolve3(JSON.parse(chunksDecode(body, length, encoding)));
60819
60819
  } else if (type2 === "arrayBuffer") {
60820
- resolve2(chunksConcat(body, length).buffer);
60820
+ resolve3(chunksConcat(body, length).buffer);
60821
60821
  } else if (type2 === "blob") {
60822
- resolve2(new Blob(body, { type: stream[kContentType] }));
60822
+ resolve3(new Blob(body, { type: stream[kContentType] }));
60823
60823
  } else if (type2 === "bytes") {
60824
- resolve2(chunksConcat(body, length));
60824
+ resolve3(chunksConcat(body, length));
60825
60825
  }
60826
60826
  consumeFinish(consume2);
60827
60827
  } catch (err) {
@@ -61011,9 +61011,9 @@ var require_api_request2 = __commonJS({
61011
61011
  };
61012
61012
  function request2(opts, callback) {
61013
61013
  if (callback === void 0) {
61014
- return new Promise((resolve2, reject) => {
61014
+ return new Promise((resolve3, reject) => {
61015
61015
  request2.call(this, opts, (err, data) => {
61016
- return err ? reject(err) : resolve2(data);
61016
+ return err ? reject(err) : resolve3(data);
61017
61017
  });
61018
61018
  });
61019
61019
  }
@@ -61225,9 +61225,9 @@ var require_api_stream2 = __commonJS({
61225
61225
  };
61226
61226
  function stream(opts, factory, callback) {
61227
61227
  if (callback === void 0) {
61228
- return new Promise((resolve2, reject) => {
61228
+ return new Promise((resolve3, reject) => {
61229
61229
  stream.call(this, opts, factory, (err, data) => {
61230
- return err ? reject(err) : resolve2(data);
61230
+ return err ? reject(err) : resolve3(data);
61231
61231
  });
61232
61232
  });
61233
61233
  }
@@ -61515,9 +61515,9 @@ var require_api_upgrade2 = __commonJS({
61515
61515
  };
61516
61516
  function upgrade(opts, callback) {
61517
61517
  if (callback === void 0) {
61518
- return new Promise((resolve2, reject) => {
61518
+ return new Promise((resolve3, reject) => {
61519
61519
  upgrade.call(this, opts, (err, data) => {
61520
- return err ? reject(err) : resolve2(data);
61520
+ return err ? reject(err) : resolve3(data);
61521
61521
  });
61522
61522
  });
61523
61523
  }
@@ -61610,9 +61610,9 @@ var require_api_connect2 = __commonJS({
61610
61610
  };
61611
61611
  function connect(opts, callback) {
61612
61612
  if (callback === void 0) {
61613
- return new Promise((resolve2, reject) => {
61613
+ return new Promise((resolve3, reject) => {
61614
61614
  connect.call(this, opts, (err, data) => {
61615
- return err ? reject(err) : resolve2(data);
61615
+ return err ? reject(err) : resolve3(data);
61616
61616
  });
61617
61617
  });
61618
61618
  }
@@ -62880,7 +62880,7 @@ var require_snapshot_recorder = __commonJS({
62880
62880
  "node_modules/.pnpm/undici@7.22.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
62881
62881
  "use strict";
62882
62882
  var { writeFile: writeFile2, readFile, mkdir } = __require("node:fs/promises");
62883
- var { dirname: dirname4, resolve: resolve2 } = __require("node:path");
62883
+ var { dirname: dirname4, resolve: resolve3 } = __require("node:path");
62884
62884
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
62885
62885
  var { InvalidArgumentError, UndiciError } = require_errors4();
62886
62886
  var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -63081,7 +63081,7 @@ var require_snapshot_recorder = __commonJS({
63081
63081
  throw new InvalidArgumentError("Snapshot path is required");
63082
63082
  }
63083
63083
  try {
63084
- const data = await readFile(resolve2(path3), "utf8");
63084
+ const data = await readFile(resolve3(path3), "utf8");
63085
63085
  const parsed2 = JSON.parse(data);
63086
63086
  if (Array.isArray(parsed2)) {
63087
63087
  this.#snapshots.clear();
@@ -63110,7 +63110,7 @@ var require_snapshot_recorder = __commonJS({
63110
63110
  if (!path3) {
63111
63111
  throw new InvalidArgumentError("Snapshot path is required");
63112
63112
  }
63113
- const resolvedPath = resolve2(path3);
63113
+ const resolvedPath = resolve3(path3);
63114
63114
  await mkdir(dirname4(resolvedPath), { recursive: true });
63115
63115
  const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot2]) => ({
63116
63116
  hash: hash2,
@@ -69687,7 +69687,7 @@ var require_fetch2 = __commonJS({
69687
69687
  function dispatch({ body }) {
69688
69688
  const url4 = requestCurrentURL(request2);
69689
69689
  const agent2 = fetchParams.controller.dispatcher;
69690
- return new Promise((resolve2, reject) => agent2.dispatch(
69690
+ return new Promise((resolve3, reject) => agent2.dispatch(
69691
69691
  {
69692
69692
  path: url4.pathname + url4.search,
69693
69693
  origin: url4.origin,
@@ -69767,7 +69767,7 @@ var require_fetch2 = __commonJS({
69767
69767
  }
69768
69768
  }
69769
69769
  const onError = this.onError.bind(this);
69770
- resolve2({
69770
+ resolve3({
69771
69771
  status,
69772
69772
  statusText,
69773
69773
  headersList,
@@ -69820,7 +69820,7 @@ var require_fetch2 = __commonJS({
69820
69820
  headersList.append(headerName, String(value2), true);
69821
69821
  }
69822
69822
  }
69823
- resolve2({
69823
+ resolve3({
69824
69824
  status,
69825
69825
  statusText: STATUS_CODES[status],
69826
69826
  headersList,
@@ -69836,7 +69836,7 @@ var require_fetch2 = __commonJS({
69836
69836
  for (let i = 0; i < rawHeaders.length; i += 2) {
69837
69837
  headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
69838
69838
  }
69839
- resolve2({
69839
+ resolve3({
69840
69840
  status,
69841
69841
  statusText: STATUS_CODES[status],
69842
69842
  headersList,
@@ -98178,8 +98178,8 @@ var require_light = __commonJS({
98178
98178
  return this.Promise.resolve();
98179
98179
  }
98180
98180
  yieldLoop(t2 = 0) {
98181
- return new this.Promise(function(resolve2, reject) {
98182
- return setTimeout(resolve2, t2);
98181
+ return new this.Promise(function(resolve3, reject) {
98182
+ return setTimeout(resolve3, t2);
98183
98183
  });
98184
98184
  }
98185
98185
  computePenalty() {
@@ -98390,15 +98390,15 @@ var require_light = __commonJS({
98390
98390
  return this._queue.length === 0;
98391
98391
  }
98392
98392
  async _tryToRun() {
98393
- var args2, cb, error49, reject, resolve2, returned, task;
98393
+ var args2, cb, error49, reject, resolve3, returned, task;
98394
98394
  if (this._running < 1 && this._queue.length > 0) {
98395
98395
  this._running++;
98396
- ({ task, args: args2, resolve: resolve2, reject } = this._queue.shift());
98396
+ ({ task, args: args2, resolve: resolve3, reject } = this._queue.shift());
98397
98397
  cb = await (async function() {
98398
98398
  try {
98399
98399
  returned = await task(...args2);
98400
98400
  return function() {
98401
- return resolve2(returned);
98401
+ return resolve3(returned);
98402
98402
  };
98403
98403
  } catch (error1) {
98404
98404
  error49 = error1;
@@ -98413,13 +98413,13 @@ var require_light = __commonJS({
98413
98413
  }
98414
98414
  }
98415
98415
  schedule(task, ...args2) {
98416
- var promise2, reject, resolve2;
98417
- resolve2 = reject = null;
98416
+ var promise2, reject, resolve3;
98417
+ resolve3 = reject = null;
98418
98418
  promise2 = new this.Promise(function(_resolve, _reject) {
98419
- resolve2 = _resolve;
98419
+ resolve3 = _resolve;
98420
98420
  return reject = _reject;
98421
98421
  });
98422
- this._queue.push({ task, args: args2, resolve: resolve2, reject });
98422
+ this._queue.push({ task, args: args2, resolve: resolve3, reject });
98423
98423
  this._tryToRun();
98424
98424
  return promise2;
98425
98425
  }
@@ -98820,14 +98820,14 @@ var require_light = __commonJS({
98820
98820
  counts = this._states.counts;
98821
98821
  return counts[0] + counts[1] + counts[2] + counts[3] === at2;
98822
98822
  };
98823
- return new this.Promise((resolve2, reject) => {
98823
+ return new this.Promise((resolve3, reject) => {
98824
98824
  if (finished()) {
98825
- return resolve2();
98825
+ return resolve3();
98826
98826
  } else {
98827
98827
  return this.on("done", () => {
98828
98828
  if (finished()) {
98829
98829
  this.removeAllListeners("done");
98830
- return resolve2();
98830
+ return resolve3();
98831
98831
  }
98832
98832
  });
98833
98833
  }
@@ -98920,9 +98920,9 @@ var require_light = __commonJS({
98920
98920
  options = parser$5.load(options, this.jobDefaults);
98921
98921
  }
98922
98922
  task = (...args3) => {
98923
- return new this.Promise(function(resolve2, reject) {
98923
+ return new this.Promise(function(resolve3, reject) {
98924
98924
  return fn2(...args3, function(...args4) {
98925
- return (args4[0] != null ? reject : resolve2)(args4);
98925
+ return (args4[0] != null ? reject : resolve3)(args4);
98926
98926
  });
98927
98927
  });
98928
98928
  };
@@ -105528,10 +105528,10 @@ var BaseScope = class {
105528
105528
  });
105529
105529
  };
105530
105530
  lazyResolutions = [];
105531
- lazilyResolve(resolve2, syntheticAlias) {
105531
+ lazilyResolve(resolve3, syntheticAlias) {
105532
105532
  const node2 = this.node("alias", {
105533
105533
  reference: syntheticAlias ?? "synthetic",
105534
- resolve: resolve2
105534
+ resolve: resolve3
105535
105535
  }, { prereduced: true });
105536
105536
  if (!this.resolved)
105537
105537
  this.lazyResolutions.push(node2);
@@ -107681,6 +107681,81 @@ var core = __toESM(require_core(), 1);
107681
107681
  var import_table = __toESM(require_src(), 1);
107682
107682
  import { AsyncLocalStorage } from "node:async_hooks";
107683
107683
 
107684
+ // agents/shared.ts
107685
+ import { execFileSync } from "node:child_process";
107686
+ var MAX_STDERR_LINES = 20;
107687
+ var MAX_COMMIT_RETRIES = 3;
107688
+ function getGitStatus() {
107689
+ try {
107690
+ return execFileSync("git", ["status", "--porcelain"], {
107691
+ encoding: "utf-8",
107692
+ timeout: 1e4
107693
+ }).trim();
107694
+ } catch {
107695
+ return "";
107696
+ }
107697
+ }
107698
+ function buildCommitPrompt(_agentId, status) {
107699
+ return [
107700
+ `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.`,
107701
+ "",
107702
+ "```",
107703
+ status,
107704
+ "```"
107705
+ ].join("\n");
107706
+ }
107707
+ var agent = (input) => {
107708
+ return {
107709
+ ...input,
107710
+ run: async (ctx) => {
107711
+ log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
107712
+ return input.run(ctx);
107713
+ }
107714
+ };
107715
+ };
107716
+ function formatCostUsd(costUsd) {
107717
+ return costUsd.toFixed(4);
107718
+ }
107719
+ function mergeAgentUsage(a, b) {
107720
+ if (!a && !b) return void 0;
107721
+ if (!a) return { ...b };
107722
+ if (!b) return { ...a };
107723
+ const cacheRead = (a.cacheReadTokens ?? 0) + (b.cacheReadTokens ?? 0);
107724
+ const cacheWrite = (a.cacheWriteTokens ?? 0) + (b.cacheWriteTokens ?? 0);
107725
+ const cost = (a.costUsd ?? 0) + (b.costUsd ?? 0);
107726
+ return {
107727
+ agent: a.agent,
107728
+ inputTokens: a.inputTokens + b.inputTokens,
107729
+ outputTokens: a.outputTokens + b.outputTokens,
107730
+ cacheReadTokens: cacheRead > 0 ? cacheRead : void 0,
107731
+ cacheWriteTokens: cacheWrite > 0 ? cacheWrite : void 0,
107732
+ costUsd: cost > 0 ? cost : void 0
107733
+ };
107734
+ }
107735
+ function logTokenTable(t2) {
107736
+ const total = t2.input + t2.cacheRead + t2.cacheWrite + t2.output;
107737
+ const costUsd = typeof t2.costUsd === "number" && t2.costUsd > 0 ? t2.costUsd : void 0;
107738
+ const headerRow = [
107739
+ { data: "Input", header: true },
107740
+ { data: "Cache Read", header: true },
107741
+ { data: "Cache Write", header: true },
107742
+ { data: "Output", header: true },
107743
+ { data: "Total", header: true }
107744
+ ];
107745
+ const dataRow = [
107746
+ String(t2.input),
107747
+ String(t2.cacheRead),
107748
+ String(t2.cacheWrite),
107749
+ String(t2.output),
107750
+ String(total)
107751
+ ];
107752
+ if (costUsd !== void 0) {
107753
+ headerRow.push({ data: "Cost ($)", header: true });
107754
+ dataRow.push(formatCostUsd(costUsd));
107755
+ }
107756
+ log.table([headerRow, dataRow]);
107757
+ }
107758
+
107684
107759
  // utils/globals.ts
107685
107760
  import { existsSync } from "node:fs";
107686
107761
  var isCloudflareSandbox = !!process.env.CLOUDFLARE_APPLICATION_ID && !!process.env.SANDBOX_VERSION;
@@ -107877,20 +107952,26 @@ function formatJsonValue(value2) {
107877
107952
  }
107878
107953
  function formatUsageSummary(entries) {
107879
107954
  if (entries.length === 0) return "";
107880
- const header = "| Agent | Input | Output | Cache Read | Cache Write |";
107881
- const separatorRow = "| --- | ---: | ---: | ---: | ---: |";
107955
+ const header = "| Agent | Input | Cache Read | Cache Write | Output | Total | Cost ($) |";
107956
+ const separatorRow = "| --- | ---: | ---: | ---: | ---: | ---: | ---: |";
107882
107957
  const fmt = (n) => n.toLocaleString("en-US");
107958
+ const nonCachedInput = (e) => Math.max(0, e.inputTokens - (e.cacheReadTokens ?? 0) - (e.cacheWriteTokens ?? 0));
107959
+ const totalFor = (e) => nonCachedInput(e) + (e.cacheReadTokens ?? 0) + (e.cacheWriteTokens ?? 0) + e.outputTokens;
107960
+ const costCell = (e) => typeof e.costUsd === "number" && e.costUsd > 0 ? formatCostUsd(e.costUsd) : "\u2014";
107883
107961
  const rows = entries.map(
107884
- (e) => `| ${e.agent} | ${fmt(e.inputTokens)} | ${fmt(e.outputTokens)} | ${fmt(e.cacheReadTokens ?? 0)} | ${fmt(e.cacheWriteTokens ?? 0)} |`
107962
+ (e) => `| ${e.agent} | ${fmt(nonCachedInput(e))} | ${fmt(e.cacheReadTokens ?? 0)} | ${fmt(e.cacheWriteTokens ?? 0)} | ${fmt(e.outputTokens)} | ${fmt(totalFor(e))} | ${costCell(e)} |`
107885
107963
  );
107886
107964
  const totalsRows = [];
107887
107965
  if (entries.length > 1) {
107888
- const totalInput = entries.reduce((sum, e) => sum + e.inputTokens, 0);
107966
+ const totalInput = entries.reduce((sum, e) => sum + nonCachedInput(e), 0);
107889
107967
  const totalOutput = entries.reduce((sum, e) => sum + e.outputTokens, 0);
107890
107968
  const totalCacheRead = entries.reduce((sum, e) => sum + (e.cacheReadTokens ?? 0), 0);
107891
107969
  const totalCacheWrite = entries.reduce((sum, e) => sum + (e.cacheWriteTokens ?? 0), 0);
107970
+ const grandTotal = totalInput + totalCacheRead + totalCacheWrite + totalOutput;
107971
+ const totalCostUsd = entries.reduce((sum, e) => sum + (e.costUsd ?? 0), 0);
107972
+ const totalCostCell = totalCostUsd > 0 ? `**${formatCostUsd(totalCostUsd)}**` : "\u2014";
107892
107973
  totalsRows.push(
107893
- `| **Total** | **${fmt(totalInput)}** | **${fmt(totalOutput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** |`
107974
+ `| **Total** | **${fmt(totalInput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** | **${fmt(totalOutput)}** | **${fmt(grandTotal)}** | ${totalCostCell} |`
107894
107975
  );
107895
107976
  }
107896
107977
  return [
@@ -107933,7 +108014,7 @@ var providers = {
107933
108014
  models: {
107934
108015
  "claude-opus": {
107935
108016
  displayName: "Claude Opus",
107936
- resolve: "anthropic/claude-opus-4-6",
108017
+ resolve: "anthropic/claude-opus-4-7",
107937
108018
  openRouterResolve: "openrouter/anthropic/claude-opus-4.6",
107938
108019
  preferred: true
107939
108020
  },
@@ -107961,7 +108042,7 @@ var providers = {
107961
108042
  },
107962
108043
  "gpt-codex-mini": {
107963
108044
  displayName: "GPT Codex Mini",
107964
- resolve: "openai/codex-mini-latest",
108045
+ resolve: "openai/gpt-5.1-codex-mini",
107965
108046
  openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
107966
108047
  },
107967
108048
  o3: {
@@ -108051,7 +108132,7 @@ var providers = {
108051
108132
  },
108052
108133
  "claude-opus": {
108053
108134
  displayName: "Claude Opus",
108054
- resolve: "opencode/claude-opus-4-6",
108135
+ resolve: "opencode/claude-opus-4-7",
108055
108136
  openRouterResolve: "openrouter/anthropic/claude-opus-4.6"
108056
108137
  },
108057
108138
  "claude-sonnet": {
@@ -108329,22 +108410,35 @@ async function retry(fn2, options = {}) {
108329
108410
  }
108330
108411
 
108331
108412
  // utils/patchWorkflowRunFields.ts
108332
- var ARTIFACT_PATCH_KEYS = [
108413
+ var STRING_KEYS = [
108333
108414
  "prNodeId",
108334
108415
  "issueNodeId",
108335
108416
  "reviewNodeId",
108336
108417
  "planCommentNodeId",
108337
108418
  "summaryCommentNodeId"
108338
108419
  ];
108420
+ var NUMBER_KEYS = [
108421
+ "inputTokens",
108422
+ "outputTokens",
108423
+ "cacheReadTokens",
108424
+ "cacheWriteTokens",
108425
+ "costUsd"
108426
+ ];
108339
108427
  async function patchWorkflowRunFields(ctx, fields) {
108340
108428
  if (ctx.runId === void 0 || !ctx.apiToken) return;
108341
108429
  const body = {};
108342
- for (const key of ARTIFACT_PATCH_KEYS) {
108430
+ for (const key of STRING_KEYS) {
108343
108431
  const value2 = fields[key];
108344
108432
  if (typeof value2 === "string" && value2.length > 0) {
108345
108433
  body[key] = value2;
108346
108434
  }
108347
108435
  }
108436
+ for (const key of NUMBER_KEYS) {
108437
+ const value2 = fields[key];
108438
+ if (typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0) {
108439
+ body[key] = value2;
108440
+ }
108441
+ }
108348
108442
  if (Object.keys(body).length === 0) return;
108349
108443
  try {
108350
108444
  await retry(
@@ -108371,6 +108465,38 @@ async function patchWorkflowRunFields(ctx, fields) {
108371
108465
  log.warning(`patchWorkflowRunFields exhausted retries: ${error49}`);
108372
108466
  }
108373
108467
  }
108468
+ var INT4_MAX = 2147483647;
108469
+ function clampInt(value2, field) {
108470
+ if (value2 > INT4_MAX) {
108471
+ log.warning(
108472
+ `aggregateUsage: ${field}=${value2} exceeds INT4_MAX (${INT4_MAX}) \u2014 clamping so the rest of the usage row still persists.`
108473
+ );
108474
+ return INT4_MAX;
108475
+ }
108476
+ return value2;
108477
+ }
108478
+ function aggregateUsage(entries) {
108479
+ if (entries.length === 0) return {};
108480
+ const sum = entries.reduce(
108481
+ (acc, e) => ({
108482
+ inputTokens: acc.inputTokens + e.inputTokens,
108483
+ outputTokens: acc.outputTokens + e.outputTokens,
108484
+ cacheReadTokens: acc.cacheReadTokens + (e.cacheReadTokens ?? 0),
108485
+ cacheWriteTokens: acc.cacheWriteTokens + (e.cacheWriteTokens ?? 0),
108486
+ costUsd: acc.costUsd + (e.costUsd ?? 0)
108487
+ }),
108488
+ { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: 0 }
108489
+ );
108490
+ const out = {};
108491
+ if (sum.inputTokens > 0) out.inputTokens = clampInt(sum.inputTokens, "inputTokens");
108492
+ if (sum.outputTokens > 0) out.outputTokens = clampInt(sum.outputTokens, "outputTokens");
108493
+ if (sum.cacheReadTokens > 0)
108494
+ out.cacheReadTokens = clampInt(sum.cacheReadTokens, "cacheReadTokens");
108495
+ if (sum.cacheWriteTokens > 0)
108496
+ out.cacheWriteTokens = clampInt(sum.cacheWriteTokens, "cacheWriteTokens");
108497
+ if (sum.costUsd > 0) out.costUsd = sum.costUsd;
108498
+ return out;
108499
+ }
108374
108500
 
108375
108501
  // node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
108376
108502
  var LIST_ITEM_MARKER = "-";
@@ -108727,6 +108853,126 @@ function resolveOptions(options) {
108727
108853
  };
108728
108854
  }
108729
108855
 
108856
+ // mcp/geminiSanitizer.ts
108857
+ function parseStringEnumBranch(item) {
108858
+ if (!item || typeof item !== "object") return null;
108859
+ const record3 = item;
108860
+ if (Array.isArray(record3.enum)) {
108861
+ const strings = record3.enum.filter((v) => typeof v === "string");
108862
+ return strings.length === record3.enum.length && strings.length > 0 ? { values: strings } : null;
108863
+ }
108864
+ if (typeof record3.const === "string") {
108865
+ return { values: [record3.const] };
108866
+ }
108867
+ return null;
108868
+ }
108869
+ function collapseStringUnion(branches) {
108870
+ const values = [];
108871
+ for (const item of branches) {
108872
+ const parsed2 = parseStringEnumBranch(item);
108873
+ if (!parsed2) return null;
108874
+ values.push(...parsed2.values);
108875
+ }
108876
+ if (values.length === 0) return null;
108877
+ return { type: "string", enum: [...new Set(values)] };
108878
+ }
108879
+ function sanitizeForGemini(schema2) {
108880
+ if (!schema2 || typeof schema2 !== "object") return schema2;
108881
+ if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
108882
+ const source = schema2;
108883
+ if (Array.isArray(source.enum) && typeof source.type !== "string") {
108884
+ const allStrings = source.enum.every((v) => typeof v === "string");
108885
+ if (allStrings) {
108886
+ const result = { type: "string", enum: source.enum };
108887
+ if (typeof source.description === "string") result.description = source.description;
108888
+ return result;
108889
+ }
108890
+ }
108891
+ for (const unionKey of ["anyOf", "oneOf"]) {
108892
+ const branches = source[unionKey];
108893
+ if (Array.isArray(branches) && branches.length > 0) {
108894
+ const collapsed = collapseStringUnion(branches);
108895
+ if (collapsed) {
108896
+ const result = { ...collapsed };
108897
+ if (typeof source.description === "string") result.description = source.description;
108898
+ return result;
108899
+ }
108900
+ }
108901
+ }
108902
+ if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
108903
+ const result = {};
108904
+ if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
108905
+ if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
108906
+ return result;
108907
+ }
108908
+ const sanitized = {};
108909
+ for (const [key, value2] of Object.entries(source)) {
108910
+ if (key === "$schema") continue;
108911
+ if (key === "$defs") {
108912
+ sanitized.definitions = sanitizeForGemini(value2);
108913
+ continue;
108914
+ }
108915
+ sanitized[key] = sanitizeForGemini(value2);
108916
+ }
108917
+ return sanitized;
108918
+ }
108919
+ function wrapJsonSchemaProducer(producer) {
108920
+ return new Proxy(producer, {
108921
+ get(target, prop, receiver) {
108922
+ const value2 = Reflect.get(target, prop, receiver);
108923
+ if ((prop === "input" || prop === "output") && typeof value2 === "function") {
108924
+ const fn2 = value2;
108925
+ return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
108926
+ }
108927
+ return value2;
108928
+ }
108929
+ });
108930
+ }
108931
+ function wrapStandard(standard) {
108932
+ return new Proxy(standard, {
108933
+ get(target, prop, receiver) {
108934
+ if (prop === "jsonSchema") {
108935
+ const value2 = Reflect.get(target, prop, receiver);
108936
+ if (value2 && typeof value2 === "object") {
108937
+ return wrapJsonSchemaProducer(value2);
108938
+ }
108939
+ return value2;
108940
+ }
108941
+ return Reflect.get(target, prop, receiver);
108942
+ }
108943
+ });
108944
+ }
108945
+ function wrapSchemaForGemini(schema2) {
108946
+ return new Proxy(schema2, {
108947
+ get(target, prop, receiver) {
108948
+ if (prop === "~standard") {
108949
+ const value2 = Reflect.get(target, prop, receiver);
108950
+ if (value2 && typeof value2 === "object") {
108951
+ return wrapStandard(value2);
108952
+ }
108953
+ return value2;
108954
+ }
108955
+ if (prop === "toJsonSchema") {
108956
+ const method = Reflect.get(target, prop, receiver);
108957
+ if (typeof method === "function") {
108958
+ return () => sanitizeForGemini(method.call(target));
108959
+ }
108960
+ return method;
108961
+ }
108962
+ return Reflect.get(target, prop, receiver);
108963
+ }
108964
+ });
108965
+ }
108966
+ function sanitizeToolForGemini(tool2) {
108967
+ if (!tool2.parameters) return tool2;
108968
+ return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
108969
+ }
108970
+ function isGeminiRouted(ctx) {
108971
+ const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
108972
+ if (!effective) return false;
108973
+ return effective.toLowerCase().includes("gemini");
108974
+ }
108975
+
108730
108976
  // mcp/shared.ts
108731
108977
  var tool = (toolDef) => toolDef;
108732
108978
  var handleToolSuccess = (data) => {
@@ -108762,15 +109008,21 @@ var execute = (fn2, toolName) => {
108762
109008
  };
108763
109009
  return _fn;
108764
109010
  };
108765
- var addTools = (_ctx, server, tools) => {
109011
+ var addTools = (ctx, server, tools) => {
109012
+ const shouldSanitize = isGeminiRouted(ctx);
108766
109013
  for (const tool2 of tools) {
108767
- server.addTool(tool2);
109014
+ server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
108768
109015
  }
108769
109016
  return server;
108770
109017
  };
108771
109018
 
108772
109019
  // mcp/comment.ts
108773
109020
  var LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
109021
+ function isLeapingIntoActionCommentBody(body) {
109022
+ const content = stripExistingFooter(body).trimStart();
109023
+ const firstLine = content.split(/\r?\n/, 1)[0]?.trimEnd() ?? "";
109024
+ return new RegExp(`(^|\\s)${LEAPING_INTO_ACTION_PREFIX}(\\.\\.\\.)?$`).test(firstLine);
109025
+ }
108774
109026
  function buildCommentFooter(ctx, customParts) {
108775
109027
  const runId = ctx.runId;
108776
109028
  return buildPullfrogFooter({
@@ -109014,8 +109266,9 @@ function ReportProgressTool(ctx) {
109014
109266
  if (!params.target_plan_comment && ctx.toolState.todoTracker) {
109015
109267
  ctx.toolState.todoTracker.cancel();
109016
109268
  await ctx.toolState.todoTracker.settled();
109017
- ctx.toolState.todoTracker.completeInProgress();
109018
- const collapsible = ctx.toolState.todoTracker.renderCollapsible();
109269
+ const collapsible = ctx.toolState.todoTracker.renderCollapsible({
109270
+ completeInProgress: true
109271
+ });
109019
109272
  if (collapsible) {
109020
109273
  body = `${body}
109021
109274
 
@@ -109402,8 +109655,27 @@ import { performance as performance3 } from "node:perf_hooks";
109402
109655
 
109403
109656
  // utils/activity.ts
109404
109657
  import { performance as performance2 } from "node:perf_hooks";
109658
+ function isMonitorDebugEnabled() {
109659
+ return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
109660
+ }
109405
109661
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
109406
109662
  var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
109663
+ var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
109664
+ var ACTIVITY_NOISE_PATTERNS = [
109665
+ new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
109666
+ new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
109667
+ new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
109668
+ /^::debug::(?:spawn|process) activity /
109669
+ ];
109670
+ function isActivityNoise(chunk) {
109671
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
109672
+ if (!text.trim()) return true;
109673
+ return text.split("\n").every((line) => {
109674
+ const trimmed = line.trim();
109675
+ if (!trimmed) return true;
109676
+ return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
109677
+ });
109678
+ }
109407
109679
  var _lastActivity = performance2.now();
109408
109680
  function markActivity() {
109409
109681
  _lastActivity = performance2.now();
@@ -109413,7 +109685,9 @@ function getIdleMs() {
109413
109685
  }
109414
109686
  function wrapWrite(original, onActivity) {
109415
109687
  const wrapped = (chunk, encodingOrCb, cb) => {
109416
- onActivity();
109688
+ if (!isActivityNoise(chunk)) {
109689
+ onActivity();
109690
+ }
109417
109691
  if (typeof encodingOrCb === "function") {
109418
109692
  return original(chunk, encodingOrCb);
109419
109693
  }
@@ -109427,10 +109701,15 @@ function startProcessOutputMonitor(ctx) {
109427
109701
  const originalStderrWrite = process.stderr.write.bind(process.stderr);
109428
109702
  process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
109429
109703
  process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
109430
- log.debug(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
109704
+ const debugBypass = (msg) => {
109705
+ if (!isMonitorDebugEnabled()) return;
109706
+ originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
109707
+ `);
109708
+ };
109709
+ debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
109431
109710
  const intervalId = setInterval(() => {
109432
109711
  const idleMs = getIdleMs();
109433
- log.debug(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
109712
+ debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
109434
109713
  if (timedOut || idleMs <= ctx.timeoutMs) return;
109435
109714
  timedOut = true;
109436
109715
  ctx.onTimeout(idleMs);
@@ -109458,12 +109737,26 @@ function createProcessOutputActivityTimeout(ctx) {
109458
109737
  if (monitor) {
109459
109738
  monitor.stop();
109460
109739
  }
109461
- rejectFn(new Error(`activity timeout: no output for ${idleSec}s`));
109740
+ const reject = rejectFn;
109741
+ rejectFn = null;
109742
+ reject(new Error(`activity timeout: no output for ${idleSec}s`));
109462
109743
  }
109463
109744
  });
109464
109745
  return {
109465
109746
  promise: promise2,
109466
- stop: monitor.stop
109747
+ // stop() also disarms forceReject so a late safety-net fire can't reject
109748
+ // the promise after the run has already succeeded.
109749
+ stop: () => {
109750
+ monitor?.stop();
109751
+ rejectFn = null;
109752
+ },
109753
+ forceReject: (reason) => {
109754
+ if (!rejectFn) return;
109755
+ monitor?.stop();
109756
+ const reject = rejectFn;
109757
+ rejectFn = null;
109758
+ reject(new Error(reason));
109759
+ }
109467
109760
  };
109468
109761
  }
109469
109762
 
@@ -109493,6 +109786,16 @@ function exitWithSignal(signal) {
109493
109786
  }
109494
109787
 
109495
109788
  // utils/subprocess.ts
109789
+ var SPAWN_TIMEOUT_CODE = "E_SPAWN_TIMEOUT";
109790
+ var SPAWN_ACTIVITY_TIMEOUT_CODE = "E_SPAWN_ACTIVITY_TIMEOUT";
109791
+ var SpawnTimeoutError = class extends Error {
109792
+ code;
109793
+ constructor(message, code) {
109794
+ super(message);
109795
+ this.name = "SpawnTimeoutError";
109796
+ this.code = code;
109797
+ }
109798
+ };
109496
109799
  var activeChildren = /* @__PURE__ */ new Map();
109497
109800
  var externalSignalHandler = null;
109498
109801
  function trackChild(options) {
@@ -109538,7 +109841,7 @@ async function spawn(options) {
109538
109841
  const startTime = performance3.now();
109539
109842
  let stdoutBuffer = "";
109540
109843
  let stderrBuffer = "";
109541
- return new Promise((resolve2, reject) => {
109844
+ return new Promise((resolve3, reject) => {
109542
109845
  const child = nodeSpawn(options.cmd, options.args, {
109543
109846
  env: options.env || {
109544
109847
  PATH: process.env.PATH || "",
@@ -109549,15 +109852,17 @@ async function spawn(options) {
109549
109852
  });
109550
109853
  trackChild({ child });
109551
109854
  let timeoutId;
109855
+ let sigkillEscalatorId;
109552
109856
  let activityCheckIntervalId;
109553
109857
  let isTimedOut = false;
109554
109858
  let isActivityTimedOut = false;
109555
109859
  let lastActivityTime = performance3.now();
109860
+ let killedAtIdleMs;
109556
109861
  if (options.timeout) {
109557
109862
  timeoutId = setTimeout(() => {
109558
109863
  isTimedOut = true;
109559
109864
  child.kill("SIGTERM");
109560
- setTimeout(() => {
109865
+ sigkillEscalatorId = setTimeout(() => {
109561
109866
  if (!child.killed) {
109562
109867
  child.kill("SIGKILL");
109563
109868
  }
@@ -109575,12 +109880,20 @@ async function spawn(options) {
109575
109880
  );
109576
109881
  if (idleMs > activityTimeoutMs) {
109577
109882
  isActivityTimedOut = true;
109883
+ killedAtIdleMs = idleMs;
109578
109884
  const idleSec = Math.round(idleMs / 1e3);
109579
109885
  log.info(
109580
109886
  `no output for ${idleSec}s from pid=${child.pid} (${options.cmd}), killing process`
109581
109887
  );
109582
109888
  child.kill("SIGKILL");
109583
109889
  clearInterval(activityCheckIntervalId);
109890
+ try {
109891
+ options.onActivityTimeout?.();
109892
+ } catch (err) {
109893
+ log.debug(
109894
+ `spawn onActivityTimeout handler threw: ${err instanceof Error ? err.message : String(err)}`
109895
+ );
109896
+ }
109584
109897
  }
109585
109898
  }, DEFAULT_ACTIVITY_CHECK_INTERVAL_MS);
109586
109899
  }
@@ -109602,24 +109915,41 @@ async function spawn(options) {
109602
109915
  options.onStderr?.(chunk);
109603
109916
  });
109604
109917
  }
109605
- child.on("close", (exitCode) => {
109918
+ child.on("close", (exitCode, signal) => {
109606
109919
  const durationMs = performance3.now() - startTime;
109607
109920
  untrackChild(child);
109608
109921
  if (timeoutId) clearTimeout(timeoutId);
109922
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
109609
109923
  if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
109610
109924
  if (isTimedOut) {
109611
- reject(new Error(`process timed out after ${options.timeout}ms`));
109925
+ reject(
109926
+ new SpawnTimeoutError(`process timed out after ${options.timeout}ms`, SPAWN_TIMEOUT_CODE)
109927
+ );
109612
109928
  return;
109613
109929
  }
109614
109930
  if (isActivityTimedOut) {
109615
- const idleSec = Math.round((performance3.now() - lastActivityTime) / 1e3);
109616
- reject(new Error(`activity timeout: no output for ${idleSec}s`));
109931
+ const idleMs = killedAtIdleMs ?? performance3.now() - lastActivityTime;
109932
+ const idleSec = Math.round(idleMs / 1e3);
109933
+ reject(
109934
+ new SpawnTimeoutError(
109935
+ `activity timeout: no output for ${idleSec}s`,
109936
+ SPAWN_ACTIVITY_TIMEOUT_CODE
109937
+ )
109938
+ );
109617
109939
  return;
109618
109940
  }
109619
- resolve2({
109941
+ let resolvedExitCode = exitCode ?? 0;
109942
+ let resolvedStderr = stderrBuffer;
109943
+ if (exitCode === null && signal) {
109944
+ const killMsg = `[spawn] ${options.cmd}: killed by signal ${signal}`;
109945
+ resolvedStderr = resolvedStderr ? `${resolvedStderr}
109946
+ ${killMsg}` : killMsg;
109947
+ resolvedExitCode = 1;
109948
+ }
109949
+ resolve3({
109620
109950
  stdout: stdoutBuffer,
109621
- stderr: stderrBuffer,
109622
- exitCode: exitCode || 0,
109951
+ stderr: resolvedStderr,
109952
+ exitCode: resolvedExitCode,
109623
109953
  durationMs
109624
109954
  });
109625
109955
  });
@@ -109627,9 +109957,13 @@ async function spawn(options) {
109627
109957
  const durationMs = performance3.now() - startTime;
109628
109958
  untrackChild(child);
109629
109959
  if (timeoutId) clearTimeout(timeoutId);
109960
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
109630
109961
  if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
109631
- console.error(`[spawn] process spawn error: ${error49.message}`);
109632
- resolve2({
109962
+ const errMsg = `[spawn] ${options.cmd}: ${error49.message}`;
109963
+ console.error(errMsg);
109964
+ stderrBuffer = stderrBuffer ? `${stderrBuffer}
109965
+ ${errMsg}` : errMsg;
109966
+ resolve3({
109633
109967
  stdout: stdoutBuffer,
109634
109968
  stderr: stderrBuffer,
109635
109969
  exitCode: 1,
@@ -114396,7 +114730,7 @@ var Protocol = class {
114396
114730
  return;
114397
114731
  }
114398
114732
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
114399
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
114733
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
114400
114734
  options?.signal?.throwIfAborted();
114401
114735
  }
114402
114736
  } catch (error49) {
@@ -114413,7 +114747,7 @@ var Protocol = class {
114413
114747
  */
114414
114748
  request(request2, resultSchema, options) {
114415
114749
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
114416
- return new Promise((resolve2, reject) => {
114750
+ return new Promise((resolve3, reject) => {
114417
114751
  const earlyReject = (error49) => {
114418
114752
  reject(error49);
114419
114753
  };
@@ -114491,7 +114825,7 @@ var Protocol = class {
114491
114825
  if (!parseResult.success) {
114492
114826
  reject(parseResult.error);
114493
114827
  } else {
114494
- resolve2(parseResult.data);
114828
+ resolve3(parseResult.data);
114495
114829
  }
114496
114830
  } catch (error49) {
114497
114831
  reject(error49);
@@ -114752,12 +115086,12 @@ var Protocol = class {
114752
115086
  }
114753
115087
  } catch {
114754
115088
  }
114755
- return new Promise((resolve2, reject) => {
115089
+ return new Promise((resolve3, reject) => {
114756
115090
  if (signal.aborted) {
114757
115091
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
114758
115092
  return;
114759
115093
  }
114760
- const timeoutId = setTimeout(resolve2, interval);
115094
+ const timeoutId = setTimeout(resolve3, interval);
114761
115095
  signal.addEventListener("abort", () => {
114762
115096
  clearTimeout(timeoutId);
114763
115097
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -115627,12 +115961,12 @@ var StdioServerTransport = class {
115627
115961
  this.onclose?.();
115628
115962
  }
115629
115963
  send(message) {
115630
- return new Promise((resolve2) => {
115964
+ return new Promise((resolve3) => {
115631
115965
  const json4 = serializeMessage(message);
115632
115966
  if (this._stdout.write(json4)) {
115633
- resolve2();
115967
+ resolve3();
115634
115968
  } else {
115635
- this._stdout.once("drain", resolve2);
115969
+ this._stdout.once("drain", resolve3);
115636
115970
  }
115637
115971
  });
115638
115972
  }
@@ -136660,12 +136994,12 @@ var require_schemes2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136660
136994
  var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136661
136995
  const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils5();
136662
136996
  const { SCHEMES, getSchemeHandler } = require_schemes2();
136663
- function normalize2(uri$2, options) {
136997
+ function normalize3(uri$2, options) {
136664
136998
  if (typeof uri$2 === "string") uri$2 = serialize(parse5(uri$2, options), options);
136665
136999
  else if (typeof uri$2 === "object") uri$2 = parse5(serialize(uri$2, options), options);
136666
137000
  return uri$2;
136667
137001
  }
136668
- function resolve2(baseURI, relativeURI, options) {
137002
+ function resolve3(baseURI, relativeURI, options) {
136669
137003
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
136670
137004
  const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
136671
137005
  schemelessOptions.skipEscape = true;
@@ -136838,8 +137172,8 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136838
137172
  }
136839
137173
  const fastUri = {
136840
137174
  SCHEMES,
136841
- normalize: normalize2,
136842
- resolve: resolve2,
137175
+ normalize: normalize3,
137176
+ resolve: resolve3,
136843
137177
  resolveComponent,
136844
137178
  equal: equal$1,
136845
137179
  serialize,
@@ -140427,7 +140761,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140427
140761
  new Error(`Connection is in ${this.#connectionState} state`)
140428
140762
  );
140429
140763
  }
140430
- return new Promise((resolve2, reject) => {
140764
+ return new Promise((resolve3, reject) => {
140431
140765
  const timeout = setTimeout(() => {
140432
140766
  reject(
140433
140767
  new Error(
@@ -140437,7 +140771,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140437
140771
  }, 5e3);
140438
140772
  this.once("ready", () => {
140439
140773
  clearTimeout(timeout);
140440
- resolve2();
140774
+ resolve3();
140441
140775
  });
140442
140776
  this.once("error", (event) => {
140443
140777
  clearTimeout(timeout);
@@ -140884,7 +141218,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140884
141218
  }
140885
141219
  });
140886
141220
  if (this.#needsEventLoopFlush) {
140887
- await new Promise((resolve2) => setImmediate(resolve2));
141221
+ await new Promise((resolve3) => setImmediate(resolve3));
140888
141222
  }
140889
141223
  } catch (progressError) {
140890
141224
  this.#logger.warn(
@@ -140942,7 +141276,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140942
141276
  }
140943
141277
  });
140944
141278
  if (this.#needsEventLoopFlush) {
140945
- await new Promise((resolve2) => setImmediate(resolve2));
141279
+ await new Promise((resolve3) => setImmediate(resolve3));
140946
141280
  }
140947
141281
  } catch (streamError) {
140948
141282
  this.#logger.warn(
@@ -141955,7 +142289,7 @@ function formatMcpToolRef(agentId, toolName) {
141955
142289
  }
141956
142290
 
141957
142291
  // utils/browser.ts
141958
- import { execFileSync, spawnSync } from "node:child_process";
142292
+ import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
141959
142293
  import { existsSync as existsSync4 } from "node:fs";
141960
142294
  import { dirname } from "node:path";
141961
142295
 
@@ -141970,12 +142304,77 @@ var SENSITIVE_PATTERNS = [
141970
142304
  function isSensitiveEnvName(key) {
141971
142305
  return SENSITIVE_PATTERNS.some((p2) => p2.test(key));
141972
142306
  }
142307
+ var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
142308
+ var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
142309
+ // system
142310
+ "CI",
142311
+ "HOME",
142312
+ "LANG",
142313
+ "LOGNAME",
142314
+ "PATH",
142315
+ "SHELL",
142316
+ "SHLVL",
142317
+ "TERM",
142318
+ "TMPDIR",
142319
+ "TZ",
142320
+ "USER",
142321
+ "XDG_CONFIG_HOME",
142322
+ "XDG_RUNTIME_DIR",
142323
+ "DEBIAN_FRONTEND",
142324
+ // runner image toolchain
142325
+ "ACCEPT_EULA",
142326
+ "AGENT_TOOLSDIRECTORY",
142327
+ "ANDROID_HOME",
142328
+ "ANDROID_NDK",
142329
+ "ANDROID_NDK_HOME",
142330
+ "ANDROID_NDK_LATEST_HOME",
142331
+ "ANDROID_NDK_ROOT",
142332
+ "ANDROID_SDK_ROOT",
142333
+ "ANT_HOME",
142334
+ "AZURE_EXTENSION_DIR",
142335
+ "BOOTSTRAP_HASKELL_NONINTERACTIVE",
142336
+ "CHROME_BIN",
142337
+ "CHROMEWEBDRIVER",
142338
+ "CONDA",
142339
+ "DOTNET_MULTILEVEL_LOOKUP",
142340
+ "DOTNET_NOLOGO",
142341
+ "DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
142342
+ "EDGEWEBDRIVER",
142343
+ "GECKOWEBDRIVER",
142344
+ "GHCUP_INSTALL_BASE_PREFIX",
142345
+ "GRADLE_HOME",
142346
+ "JAVA_HOME",
142347
+ "HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
142348
+ "HOMEBREW_NO_AUTO_UPDATE",
142349
+ "ImageOS",
142350
+ "ImageVersion",
142351
+ "NVM_DIR",
142352
+ "PIPX_BIN_DIR",
142353
+ "PIPX_HOME",
142354
+ "PSModulePath",
142355
+ "SELENIUM_JAR_PATH",
142356
+ "SGX_AESM_ADDR",
142357
+ "SWIFT_PATH",
142358
+ "VCPKG_INSTALLATION_ROOT"
142359
+ ]);
142360
+ var _userAllowlist = null;
142361
+ function setEnvAllowlist(raw2) {
142362
+ const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
142363
+ _userAllowlist = new Set(names);
142364
+ }
142365
+ function isSafeEnvVar(key) {
142366
+ if (SAFE_ENV_NAMES.has(key)) return true;
142367
+ return SAFE_ENV_PREFIXES.some((p2) => key.startsWith(p2));
142368
+ }
141973
142369
  function filterEnv() {
141974
142370
  const filtered = {};
141975
142371
  for (const [key, value2] of Object.entries(process.env)) {
141976
142372
  if (value2 === void 0) continue;
141977
- if (isSensitiveEnvName(key)) continue;
141978
- filtered[key] = value2;
142373
+ const userAllowed = _userAllowlist?.has(key) ?? false;
142374
+ if (isSensitiveEnvName(key) && !userAllowed) continue;
142375
+ if (isSafeEnvVar(key) || userAllowed) {
142376
+ filtered[key] = value2;
142377
+ }
141979
142378
  }
141980
142379
  return filtered;
141981
142380
  }
@@ -141995,7 +142394,7 @@ var import_semver = __toESM(require_semver2(), 1);
141995
142394
  // package.json
141996
142395
  var package_default = {
141997
142396
  name: "pullfrog",
141998
- version: "0.0.201",
142397
+ version: "0.0.202",
141999
142398
  type: "module",
142000
142399
  bin: {
142001
142400
  pullfrog: "dist/cli.mjs",
@@ -142007,6 +142406,7 @@ var package_default = {
142007
142406
  ],
142008
142407
  scripts: {
142009
142408
  test: "vitest",
142409
+ "test:catalog": "vitest run --config vitest.main.config.ts",
142010
142410
  typecheck: "tsc --noEmit",
142011
142411
  build: "node esbuild.config.js && tsc -p tsconfig.exports.json",
142012
142412
  "check:entrypoints": "node scripts/check-entrypoint-imports.ts",
@@ -142019,7 +142419,7 @@ var package_default = {
142019
142419
  },
142020
142420
  devDependencies: {
142021
142421
  "@actions/core": "^1.11.1",
142022
- "@anthropic-ai/claude-code": "2.1.85",
142422
+ "@anthropic-ai/claude-code": "2.1.112",
142023
142423
  "@ark/fs": "0.56.0",
142024
142424
  "@ark/util": "0.56.0",
142025
142425
  "@clack/prompts": "^1.2.0",
@@ -142143,7 +142543,7 @@ function ensureBrowserDaemon(toolState) {
142143
142543
  log.info("agent-browser installed");
142144
142544
  let binDir;
142145
142545
  try {
142146
- const binPath = execFileSync("which", ["agent-browser"], { encoding: "utf-8" }).trim();
142546
+ const binPath = execFileSync2("which", ["agent-browser"], { encoding: "utf-8" }).trim();
142147
142547
  binDir = dirname(binPath);
142148
142548
  log.info(`agent-browser binary: ${binPath}`);
142149
142549
  } catch {
@@ -142190,9 +142590,271 @@ function closeBrowserDaemon(toolState) {
142190
142590
  }
142191
142591
 
142192
142592
  // mcp/checkout.ts
142593
+ import { createHash as createHash2 } from "node:crypto";
142193
142594
  import { writeFileSync } from "node:fs";
142194
142595
  import { join as join3 } from "node:path";
142195
142596
 
142597
+ // utils/diffCoverage.ts
142598
+ import { isAbsolute, normalize as normalize2, resolve } from "node:path";
142599
+ function countLines(params) {
142600
+ const content = params.content;
142601
+ if (content.length === 0) return 0;
142602
+ return content.split("\n").length;
142603
+ }
142604
+ function parseDiffTocEntries(params) {
142605
+ const lines = params.toc.split("\n");
142606
+ const entries = [];
142607
+ for (const line of lines) {
142608
+ const match3 = line.match(/^- (.+) (?:→|->) lines (\d+)-(\d+)(?: · diff-[0-9a-f]+)?$/);
142609
+ if (!match3) continue;
142610
+ const startLine = Number.parseInt(match3[2], 10);
142611
+ const endLine = Number.parseInt(match3[3], 10);
142612
+ if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) continue;
142613
+ entries.push({ filename: match3[1], startLine, endLine });
142614
+ }
142615
+ return entries;
142616
+ }
142617
+ function createDiffCoverageState(params) {
142618
+ return {
142619
+ diffPath: params.diffPath,
142620
+ totalLines: params.totalLines,
142621
+ tocEntries: parseDiffTocEntries({ toc: params.toc }),
142622
+ coveredRanges: [],
142623
+ coveragePreflightRan: false
142624
+ };
142625
+ }
142626
+ function recordDiffReadFromToolUse(params) {
142627
+ const state = params.state;
142628
+ if (!state) return false;
142629
+ if (!isReadTool(params.toolName)) return false;
142630
+ const readTarget = extractReadTarget({ input: params.input });
142631
+ if (!readTarget) return false;
142632
+ const normalizedReadPath = normalizePath({ path: readTarget.path, cwd: params.cwd });
142633
+ const normalizedDiffPath = normalize2(state.diffPath);
142634
+ if (normalizedReadPath !== normalizedDiffPath) return false;
142635
+ const range2 = resolveReadRange({
142636
+ totalLines: state.totalLines,
142637
+ offset: readTarget.offset,
142638
+ limit: readTarget.limit,
142639
+ startLine: readTarget.startLine,
142640
+ endLine: readTarget.endLine,
142641
+ offsetBase: resolveOffsetBase({ toolName: params.toolName })
142642
+ });
142643
+ if (!range2) return false;
142644
+ state.coveredRanges = mergeRanges({ ranges: state.coveredRanges, nextRange: range2 });
142645
+ return true;
142646
+ }
142647
+ function getDiffCoverageBreakdown(params) {
142648
+ const state = params.state;
142649
+ const coveredRanges = mergeRangesList({ ranges: state.coveredRanges });
142650
+ const unreadRanges = invertRanges({ totalLines: state.totalLines, coveredRanges });
142651
+ const coveredLines = countLinesInRanges({ ranges: coveredRanges });
142652
+ const unreadLines = Math.max(0, state.totalLines - coveredLines);
142653
+ const coveragePercent = state.totalLines ? Number((coveredLines / state.totalLines * 100).toFixed(2)) : 100;
142654
+ const files = [];
142655
+ for (const entry of state.tocEntries) {
142656
+ const fileRange = { startLine: entry.startLine, endLine: entry.endLine };
142657
+ const coveredInFile = intersectRangesWithRange({ ranges: coveredRanges, target: fileRange });
142658
+ const unreadInFile = intersectRangesWithRange({ ranges: unreadRanges, target: fileRange });
142659
+ const totalFileLines = Math.max(0, entry.endLine - entry.startLine + 1);
142660
+ const fileCoveredLines = countLinesInRanges({ ranges: coveredInFile });
142661
+ files.push({
142662
+ filename: entry.filename,
142663
+ startLine: entry.startLine,
142664
+ endLine: entry.endLine,
142665
+ totalLines: totalFileLines,
142666
+ coveredLines: fileCoveredLines,
142667
+ coveredRanges: coveredInFile,
142668
+ unreadRanges: unreadInFile
142669
+ });
142670
+ }
142671
+ return {
142672
+ totalLines: state.totalLines,
142673
+ coveredLines,
142674
+ unreadLines,
142675
+ coveragePercent,
142676
+ coveredRanges,
142677
+ unreadRanges,
142678
+ files
142679
+ };
142680
+ }
142681
+ function renderDiffCoverageBreakdown(params) {
142682
+ const breakdown = params.breakdown;
142683
+ const lines = [];
142684
+ lines.push(`diff coverage report for \`${params.diffPath}\``);
142685
+ lines.push(
142686
+ `overall: ${breakdown.coveredLines}/${breakdown.totalLines} lines read (${breakdown.coveragePercent}%), unread: ${breakdown.unreadLines}`
142687
+ );
142688
+ lines.push(`covered ranges: ${formatRanges({ ranges: breakdown.coveredRanges })}`);
142689
+ lines.push(`unread ranges: ${formatRanges({ ranges: breakdown.unreadRanges })}`);
142690
+ lines.push("");
142691
+ lines.push("per-file TOC coverage:");
142692
+ for (const file2 of breakdown.files) {
142693
+ const filePercent = file2.totalLines ? Number((file2.coveredLines / file2.totalLines * 100).toFixed(2)) : 100;
142694
+ lines.push(
142695
+ `- ${file2.filename} (toc lines ${file2.startLine}-${file2.endLine}): ${file2.coveredLines}/${file2.totalLines} lines read (${filePercent}%)`
142696
+ );
142697
+ lines.push(` read: ${formatRanges({ ranges: file2.coveredRanges })}`);
142698
+ lines.push(` unread: ${formatRanges({ ranges: file2.unreadRanges })}`);
142699
+ }
142700
+ return lines.join("\n");
142701
+ }
142702
+ function resolveOffsetBase(params) {
142703
+ const lower2 = params.toolName.toLowerCase();
142704
+ if (lower2 === "readfile" || lower2.endsWith(".readfile")) {
142705
+ return "one";
142706
+ }
142707
+ return "zero";
142708
+ }
142709
+ function isReadTool(toolName) {
142710
+ const lower2 = toolName.toLowerCase();
142711
+ if (lower2 === "read" || lower2 === "readfile") return true;
142712
+ if (lower2.endsWith(".read") || lower2.endsWith(".readfile")) return true;
142713
+ return false;
142714
+ }
142715
+ function extractReadTarget(params) {
142716
+ const inputRecord = asRecord(params.input);
142717
+ if (!inputRecord) return null;
142718
+ const direct = extractReadTargetFromRecord({ record: inputRecord });
142719
+ if (direct) return direct;
142720
+ const nestedCandidates = [inputRecord.args, inputRecord.params, inputRecord.input];
142721
+ for (const candidate of nestedCandidates) {
142722
+ const nestedRecord = asRecord(candidate);
142723
+ if (!nestedRecord) continue;
142724
+ const nested = extractReadTargetFromRecord({ record: nestedRecord });
142725
+ if (nested) return nested;
142726
+ }
142727
+ return null;
142728
+ }
142729
+ function extractReadTargetFromRecord(params) {
142730
+ const record3 = params.record;
142731
+ 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 });
142732
+ if (!pathValue) return null;
142733
+ const offset = readNumber({ value: record3.offset });
142734
+ const limit = readNumber({ value: record3.limit });
142735
+ const startLine = readNumber({ value: record3.start_line }) ?? readNumber({ value: record3.startLine }) ?? readNumber({ value: record3.line_start });
142736
+ const endLine = readNumber({ value: record3.end_line }) ?? readNumber({ value: record3.endLine }) ?? readNumber({ value: record3.line_end });
142737
+ return { path: pathValue, offset, limit, startLine, endLine };
142738
+ }
142739
+ function resolveReadRange(params) {
142740
+ const totalLines = params.totalLines;
142741
+ if (totalLines <= 0) return null;
142742
+ if (params.startLine !== void 0 || params.endLine !== void 0) {
142743
+ const rawStart = params.startLine ?? 1;
142744
+ const rawEnd = params.endLine ?? totalLines;
142745
+ const startLine2 = clampLine({ value: rawStart, totalLines });
142746
+ const endLine2 = clampLine({ value: rawEnd, totalLines });
142747
+ if (endLine2 < startLine2) return null;
142748
+ return { startLine: startLine2, endLine: endLine2 };
142749
+ }
142750
+ let startLine = 1;
142751
+ if (params.offset !== void 0) {
142752
+ if (params.offset >= 0) {
142753
+ const normalizedOffset = params.offsetBase === "zero" ? params.offset + 1 : params.offset === 0 ? 1 : params.offset;
142754
+ startLine = clampLine({ value: normalizedOffset, totalLines });
142755
+ } else {
142756
+ startLine = clampLine({ value: totalLines + params.offset + 1, totalLines });
142757
+ }
142758
+ }
142759
+ let endLine = totalLines;
142760
+ if (params.limit !== void 0) {
142761
+ if (params.limit <= 0) return null;
142762
+ endLine = clampLine({ value: startLine + params.limit - 1, totalLines });
142763
+ }
142764
+ if (endLine < startLine) return null;
142765
+ return { startLine, endLine };
142766
+ }
142767
+ function normalizePath(params) {
142768
+ if (isAbsolute(params.path)) return normalize2(params.path);
142769
+ return normalize2(resolve(params.cwd, params.path));
142770
+ }
142771
+ function mergeRanges(params) {
142772
+ return mergeRangesList({ ranges: [...params.ranges, params.nextRange] });
142773
+ }
142774
+ function mergeRangesList(params) {
142775
+ if (params.ranges.length === 0) return [];
142776
+ const sorted = [...params.ranges].sort((a, b) => a.startLine - b.startLine);
142777
+ const merged = [];
142778
+ for (const range2 of sorted) {
142779
+ const last = merged[merged.length - 1];
142780
+ if (!last) {
142781
+ merged.push({ startLine: range2.startLine, endLine: range2.endLine });
142782
+ continue;
142783
+ }
142784
+ if (range2.startLine <= last.endLine + 1) {
142785
+ if (range2.endLine > last.endLine) {
142786
+ last.endLine = range2.endLine;
142787
+ }
142788
+ continue;
142789
+ }
142790
+ merged.push({ startLine: range2.startLine, endLine: range2.endLine });
142791
+ }
142792
+ return merged;
142793
+ }
142794
+ function invertRanges(params) {
142795
+ if (params.totalLines <= 0) return [];
142796
+ if (params.coveredRanges.length === 0) {
142797
+ return [{ startLine: 1, endLine: params.totalLines }];
142798
+ }
142799
+ const unread = [];
142800
+ let cursor = 1;
142801
+ for (const range2 of params.coveredRanges) {
142802
+ if (cursor < range2.startLine) {
142803
+ unread.push({ startLine: cursor, endLine: range2.startLine - 1 });
142804
+ }
142805
+ cursor = Math.max(cursor, range2.endLine + 1);
142806
+ }
142807
+ if (cursor <= params.totalLines) {
142808
+ unread.push({ startLine: cursor, endLine: params.totalLines });
142809
+ }
142810
+ return unread;
142811
+ }
142812
+ function intersectRangesWithRange(params) {
142813
+ const intersections = [];
142814
+ for (const range2 of params.ranges) {
142815
+ if (range2.endLine < params.target.startLine) continue;
142816
+ if (range2.startLine > params.target.endLine) continue;
142817
+ const startLine = Math.max(range2.startLine, params.target.startLine);
142818
+ const endLine = Math.min(range2.endLine, params.target.endLine);
142819
+ if (endLine >= startLine) {
142820
+ intersections.push({ startLine, endLine });
142821
+ }
142822
+ }
142823
+ return intersections;
142824
+ }
142825
+ function countLinesInRanges(params) {
142826
+ let total = 0;
142827
+ for (const range2 of params.ranges) {
142828
+ total += range2.endLine - range2.startLine + 1;
142829
+ }
142830
+ return total;
142831
+ }
142832
+ function formatRanges(params) {
142833
+ if (params.ranges.length === 0) return "none";
142834
+ return params.ranges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
142835
+ }
142836
+ function clampLine(params) {
142837
+ if (params.value < 1) return 1;
142838
+ if (params.value > params.totalLines) return params.totalLines;
142839
+ return params.value;
142840
+ }
142841
+ function asRecord(value2) {
142842
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
142843
+ return Object.fromEntries(Object.entries(value2));
142844
+ }
142845
+ function readString(params) {
142846
+ if (typeof params.value === "string") return params.value;
142847
+ return void 0;
142848
+ }
142849
+ function readNumber(params) {
142850
+ if (typeof params.value === "number" && Number.isFinite(params.value)) return params.value;
142851
+ if (typeof params.value === "string") {
142852
+ const parsed2 = Number.parseInt(params.value, 10);
142853
+ if (Number.isFinite(parsed2)) return parsed2;
142854
+ }
142855
+ return void 0;
142856
+ }
142857
+
142196
142858
  // utils/gitAuth.ts
142197
142859
  import { execSync } from "node:child_process";
142198
142860
  import { createHash } from "node:crypto";
@@ -142206,7 +142868,7 @@ function resolveGit() {
142206
142868
  const resolvedPath = realpathSync(whichPath);
142207
142869
  const sha256 = hashFile(resolvedPath);
142208
142870
  gitBinary = { path: resolvedPath, sha256 };
142209
- log.info(`git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
142871
+ log.debug(`\xBB git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
142210
142872
  }
142211
142873
  function verifyGitBinary() {
142212
142874
  if (!gitBinary) {
@@ -142285,29 +142947,43 @@ async function $git(subcommand, args2, options) {
142285
142947
  }
142286
142948
 
142287
142949
  // lifecycle.ts
142288
- var LIFECYCLE_HOOK_TIMEOUT_MS = 12e4;
142950
+ var LIFECYCLE_HOOK_TIMEOUT_MS = 6e5;
142289
142951
 
142290
142952
  // utils/lifecycle.ts
142291
142953
  async function executeLifecycleHook(params) {
142292
- if (!params.script) return;
142954
+ if (!params.script) return {};
142293
142955
  log.info(`\xBB executing ${params.event} lifecycle hook...`);
142294
- const result = await spawn({
142295
- cmd: "bash",
142296
- args: ["-c", params.script],
142297
- env: process.env,
142298
- timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
142299
- activityTimeout: 0,
142300
- onStdout: (chunk) => process.stdout.write(chunk),
142301
- onStderr: (chunk) => process.stderr.write(chunk)
142302
- });
142303
- if (result.exitCode !== 0) {
142304
- const output = result.stderr || result.stdout;
142305
- throw new Error(
142306
- `lifecycle hook '${params.event}' failed with exit code ${result.exitCode}:
142307
- ${output}`
142308
- );
142956
+ try {
142957
+ const result = await spawn({
142958
+ cmd: "bash",
142959
+ args: ["-c", params.script],
142960
+ env: process.env,
142961
+ timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
142962
+ activityTimeout: 0,
142963
+ onStdout: (chunk) => process.stdout.write(chunk),
142964
+ onStderr: (chunk) => process.stderr.write(chunk)
142965
+ });
142966
+ if (result.exitCode !== 0) {
142967
+ const output = (result.stderr || result.stdout).trim();
142968
+ return {
142969
+ 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.`
142970
+ };
142971
+ }
142972
+ log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
142973
+ return {};
142974
+ } catch (err) {
142975
+ const isTimeout = err instanceof SpawnTimeoutError && (err.code === SPAWN_TIMEOUT_CODE || err.code === SPAWN_ACTIVITY_TIMEOUT_CODE);
142976
+ if (isTimeout) {
142977
+ const minutes = Math.round(LIFECYCLE_HOOK_TIMEOUT_MS / 6e4);
142978
+ return {
142979
+ 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).`
142980
+ };
142981
+ }
142982
+ const msg = err instanceof Error ? err.message : String(err);
142983
+ return {
142984
+ warning: `lifecycle hook '${params.event}' failed to spawn: ${msg}. this is likely a transient failure \u2014 retry the operation.`
142985
+ };
142309
142986
  }
142310
- log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
142311
142987
  }
142312
142988
 
142313
142989
  // utils/shell.ts
@@ -142457,6 +143133,787 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
142457
143133
  return hasChanges ? out : null;
142458
143134
  }
142459
143135
 
143136
+ // mcp/git.ts
143137
+ function getPushDestination(branch, storedDest) {
143138
+ if (storedDest && storedDest.localBranch === branch) {
143139
+ log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
143140
+ const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
143141
+ log: false
143142
+ }).trim();
143143
+ return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
143144
+ }
143145
+ try {
143146
+ const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
143147
+ const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
143148
+ const remoteBranch = merge4.replace(/^refs\/heads\//, "");
143149
+ const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
143150
+ return { remoteName: pushRemote, remoteBranch, url: url4 };
143151
+ } catch {
143152
+ log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
143153
+ const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
143154
+ return { remoteName: "origin", remoteBranch: branch, url: url4 };
143155
+ }
143156
+ }
143157
+ function normalizeUrl(url4) {
143158
+ return url4.replace(/\.git$/, "").toLowerCase();
143159
+ }
143160
+ function rejectIfLeadingDash(value2, kind) {
143161
+ if (value2.startsWith("-")) {
143162
+ throw new Error(`Blocked: ${kind} '${value2}' starts with '-' \u2014 git could parse it as a flag.`);
143163
+ }
143164
+ }
143165
+ var SYMBOLIC_REFS = /* @__PURE__ */ new Set(["HEAD", "FETCH_HEAD", "ORIG_HEAD", "MERGE_HEAD"]);
143166
+ function rejectSpecialRef(value2, kind) {
143167
+ rejectIfLeadingDash(value2, kind);
143168
+ if (value2.startsWith("refs/")) {
143169
+ throw new Error(
143170
+ `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.`
143171
+ );
143172
+ }
143173
+ if (SYMBOLIC_REFS.has(value2)) {
143174
+ throw new Error(
143175
+ `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.`
143176
+ );
143177
+ }
143178
+ const BAD = /[:+^~?*[\\\s]/;
143179
+ const badMatch = value2.match(BAD);
143180
+ if (badMatch) {
143181
+ throw new Error(
143182
+ `Blocked: ${kind} '${value2}' contains '${badMatch[0]}', which git interprets as refspec/revision syntax, not as part of a branch name.`
143183
+ );
143184
+ }
143185
+ }
143186
+ function validateTagName(tag) {
143187
+ rejectIfLeadingDash(tag, "tag");
143188
+ if (!/^[A-Za-z0-9._/-]+$/.test(tag)) {
143189
+ throw new Error(
143190
+ `Blocked: tag '${tag}' contains characters that could be parsed as a refspec or flag. Tags must match [A-Za-z0-9._/-]+.`
143191
+ );
143192
+ }
143193
+ }
143194
+ function validatePushDestination(ctx, branch) {
143195
+ const pushUrl = ctx.toolState.pushUrl;
143196
+ if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
143197
+ const dest = getPushDestination(branch, ctx.toolState.pushDest);
143198
+ if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
143199
+ throw new Error(
143200
+ `Push blocked: destination does not match expected repository.
143201
+ Expected: ${pushUrl}
143202
+ Actual: ${dest.url}
143203
+ Git configuration may have been tampered with.`
143204
+ );
143205
+ }
143206
+ return dest;
143207
+ }
143208
+ var PushBranch = type({
143209
+ branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
143210
+ force: type.boolean.describe("Force push (use with caution)").default(false)
143211
+ });
143212
+ function PushBranchTool(ctx) {
143213
+ const defaultBranch = ctx.repo.data.default_branch || "main";
143214
+ const pushPermission = ctx.payload.push;
143215
+ return tool({
143216
+ name: "push_branch",
143217
+ 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.",
143218
+ parameters: PushBranch,
143219
+ execute: execute(async ({ branchName, force }) => {
143220
+ if (pushPermission === "disabled") {
143221
+ throw new Error("Push is disabled. This repository is configured for read-only access.");
143222
+ }
143223
+ const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
143224
+ rejectSpecialRef(branch, "branch");
143225
+ const status = $("git", ["status", "--porcelain"], { log: false });
143226
+ if (status) {
143227
+ throw new Error(
143228
+ `push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
143229
+
143230
+ git status:
143231
+ ${status}`
143232
+ );
143233
+ }
143234
+ const pushDest = validatePushDestination(ctx, branch);
143235
+ if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
143236
+ throw new Error(
143237
+ `Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
143238
+ );
143239
+ }
143240
+ const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
143241
+ const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
143242
+ const prepushHook = await executeLifecycleHook({
143243
+ event: "prepush",
143244
+ script: ctx.prepushScript
143245
+ });
143246
+ if (prepushHook.warning) {
143247
+ throw new Error(prepushHook.warning);
143248
+ }
143249
+ const postHookStatus = $("git", ["status", "--porcelain"], { log: false });
143250
+ if (postHookStatus) {
143251
+ throw new Error(
143252
+ `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.
143253
+
143254
+ git status:
143255
+ ${postHookStatus}`
143256
+ );
143257
+ }
143258
+ log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
143259
+ if (force) {
143260
+ log.warning(`force pushing - this will overwrite remote history`);
143261
+ }
143262
+ try {
143263
+ await $git("push", pushArgs, {
143264
+ token: ctx.gitToken
143265
+ });
143266
+ } catch (err) {
143267
+ const msg = err instanceof Error ? err.message : String(err);
143268
+ if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
143269
+ 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')`;
143270
+ throw new Error(
143271
+ `push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
143272
+
143273
+ to resolve this:
143274
+ 1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
143275
+ ${integrateStep}
143276
+ 3. resolve any merge conflicts if needed
143277
+ 4. retry push_branch`
143278
+ );
143279
+ }
143280
+ throw err;
143281
+ }
143282
+ return {
143283
+ success: true,
143284
+ branch,
143285
+ remoteBranch: pushDest.remoteBranch,
143286
+ remote: pushDest.remoteName,
143287
+ force,
143288
+ message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
143289
+ };
143290
+ })
143291
+ });
143292
+ }
143293
+ var AUTH_REQUIRED_REDIRECT = {
143294
+ push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
143295
+ fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
143296
+ pull: "use git_fetch to fetch the remote ref, then call this git tool with command 'merge' locally.",
143297
+ clone: "the repository is already cloned. use checkout_pr for PR branches."
143298
+ };
143299
+ var NOSHELL_BLOCKED_SUBCOMMANDS = {
143300
+ config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
143301
+ submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
143302
+ "update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
143303
+ "filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
143304
+ replace: "Blocked: git replace can redirect object lookups.",
143305
+ // subcommands that accept --exec or similar flags for arbitrary code execution
143306
+ rebase: "Blocked: git rebase --exec can execute arbitrary shell commands. Use 'merge' instead to integrate remote changes.",
143307
+ 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.",
143308
+ // difftool/mergetool exist to shell out to external diff/merge programs.
143309
+ // both accept `--extcmd` / `-x` (difftool) or configured tool commands
143310
+ // (mergetool) that run arbitrary code. NOSHELL_BLOCKED_ARGS catches the
143311
+ // long `--extcmd` form, but not the `-x` short form — and globally blocking
143312
+ // `-x` would false-positive on `git cherry-pick -x`. block the subcommands
143313
+ // wholesale instead; neither has a meaningful use in an automated agent
143314
+ // workflow (agents use `git diff` / `git show` for diffs and resolve
143315
+ // conflicts via file edits, not a TUI merge tool).
143316
+ 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.",
143317
+ 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."
143318
+ };
143319
+ var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
143320
+ var COLLAPSE_THRESHOLD = 200;
143321
+ var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
143322
+ var Git = type({
143323
+ command: type(subcommandPattern).describe("Git command (e.g., 'status', 'log', 'diff')"),
143324
+ args: type.string.array().describe("Additional arguments for the git command").optional()
143325
+ });
143326
+ function GitTool(ctx) {
143327
+ return tool({
143328
+ name: "git",
143329
+ 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'.",
143330
+ parameters: Git,
143331
+ execute: execute(async (params) => {
143332
+ const command = params.command;
143333
+ const args2 = params.args ?? [];
143334
+ const redirect = AUTH_REQUIRED_REDIRECT[command];
143335
+ if (redirect) {
143336
+ throw new Error(`git ${command} is not available through this tool \u2014 ${redirect}`);
143337
+ }
143338
+ if (ctx.payload.shell === "disabled") {
143339
+ const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[command];
143340
+ if (blocked) {
143341
+ throw new Error(blocked);
143342
+ }
143343
+ for (const arg4 of args2) {
143344
+ const isBlocked = NOSHELL_BLOCKED_ARGS.some(
143345
+ (flag) => arg4 === flag || arg4.startsWith(flag + "=")
143346
+ );
143347
+ if (isBlocked) {
143348
+ throw new Error(
143349
+ `Blocked: '${arg4}' flag can execute arbitrary code and is not allowed.`
143350
+ );
143351
+ }
143352
+ }
143353
+ }
143354
+ const output = $("git", [command, ...args2], { log: false });
143355
+ const lineCount = output.split("\n").length;
143356
+ if (lineCount > COLLAPSE_THRESHOLD) {
143357
+ log.group(`git ${command} output (${lineCount} lines)`, () => {
143358
+ log.info(output);
143359
+ });
143360
+ } else if (output) {
143361
+ log.info(output);
143362
+ }
143363
+ return { success: true, output };
143364
+ })
143365
+ });
143366
+ }
143367
+ var GitFetch = type({
143368
+ ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
143369
+ depth: type.number.describe("Fetch depth (for shallow clones)").optional()
143370
+ });
143371
+ function GitFetchTool(ctx) {
143372
+ return tool({
143373
+ name: "git_fetch",
143374
+ description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
143375
+ parameters: GitFetch,
143376
+ execute: execute(async (params) => {
143377
+ rejectIfLeadingDash(params.ref, "ref");
143378
+ const fetchArgs = ["--no-tags", "origin", params.ref];
143379
+ if (params.depth !== void 0) {
143380
+ fetchArgs.push(`--depth=${params.depth}`);
143381
+ }
143382
+ await $git("fetch", fetchArgs, {
143383
+ token: ctx.gitToken
143384
+ });
143385
+ return { success: true, ref: params.ref };
143386
+ })
143387
+ });
143388
+ }
143389
+ var DeleteBranch = type({
143390
+ branchName: type.string.describe("Remote branch to delete")
143391
+ });
143392
+ function DeleteBranchTool(ctx) {
143393
+ const pushPermission = ctx.payload.push;
143394
+ const defaultBranch = ctx.repo.data.default_branch || "main";
143395
+ return tool({
143396
+ name: "delete_branch",
143397
+ description: "Delete a remote branch. Requires push: enabled permission. Deletion of the repository's default branch is always blocked regardless of permission mode.",
143398
+ parameters: DeleteBranch,
143399
+ execute: execute(async (params) => {
143400
+ if (pushPermission !== "enabled") {
143401
+ throw new Error(
143402
+ "Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
143403
+ );
143404
+ }
143405
+ rejectSpecialRef(params.branchName, "branchName");
143406
+ if (params.branchName === defaultBranch) {
143407
+ throw new Error(
143408
+ `Blocked: cannot delete the default branch '${defaultBranch}'. If you really need to delete or rename it, do it manually via the repository settings.`
143409
+ );
143410
+ }
143411
+ await $git("push", ["origin", "--delete", `refs/heads/${params.branchName}`], {
143412
+ token: ctx.gitToken
143413
+ });
143414
+ return { success: true, deleted: params.branchName };
143415
+ })
143416
+ });
143417
+ }
143418
+ var PushTags = type({
143419
+ tag: type.string.describe("Tag name to push"),
143420
+ force: type.boolean.describe("Force push the tag").default(false)
143421
+ });
143422
+ function PushTagsTool(ctx) {
143423
+ const pushPermission = ctx.payload.push;
143424
+ return tool({
143425
+ name: "push_tags",
143426
+ description: "Push a tag to remote. Requires push: enabled permission.",
143427
+ parameters: PushTags,
143428
+ execute: execute(async (params) => {
143429
+ if (pushPermission !== "enabled") {
143430
+ throw new Error(
143431
+ "Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
143432
+ );
143433
+ }
143434
+ validateTagName(params.tag);
143435
+ const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
143436
+ await $git("push", pushArgs, {
143437
+ token: ctx.gitToken
143438
+ });
143439
+ return { success: true, tag: params.tag };
143440
+ })
143441
+ });
143442
+ }
143443
+
143444
+ // mcp/review.ts
143445
+ function getHttpStatus(err) {
143446
+ if (typeof err !== "object" || err === null) return void 0;
143447
+ const status = err.status;
143448
+ return typeof status === "number" ? status : void 0;
143449
+ }
143450
+ function commentableLinesForFile(patch) {
143451
+ const right = /* @__PURE__ */ new Set();
143452
+ const left = /* @__PURE__ */ new Set();
143453
+ if (!patch) return { RIGHT: right, LEFT: left };
143454
+ let oldLine = 0;
143455
+ let newLine = 0;
143456
+ for (const line of patch.split("\n")) {
143457
+ const hunk = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
143458
+ if (hunk) {
143459
+ oldLine = parseInt(hunk[1], 10);
143460
+ newLine = parseInt(hunk[2], 10);
143461
+ continue;
143462
+ }
143463
+ const changeType = line[0];
143464
+ if (changeType === "+") {
143465
+ right.add(newLine);
143466
+ newLine++;
143467
+ } else if (changeType === "-") {
143468
+ left.add(oldLine);
143469
+ oldLine++;
143470
+ } else if (changeType === " ") {
143471
+ right.add(newLine);
143472
+ left.add(oldLine);
143473
+ newLine++;
143474
+ oldLine++;
143475
+ }
143476
+ }
143477
+ return { RIGHT: right, LEFT: left };
143478
+ }
143479
+ async function buildCommentableMap(ctx, pullNumber) {
143480
+ const cached4 = ctx.toolState.commentableLinesByFile;
143481
+ const cachedFor = ctx.toolState.commentableLinesPullNumber;
143482
+ const cachedSha = ctx.toolState.commentableLinesCheckoutSha;
143483
+ const currentSha = ctx.toolState.checkoutSha;
143484
+ if (cached4 && cachedFor === pullNumber && cachedSha && cachedSha === currentSha) return cached4;
143485
+ const files = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listFiles, {
143486
+ owner: ctx.repo.owner,
143487
+ repo: ctx.repo.name,
143488
+ pull_number: pullNumber,
143489
+ per_page: 100
143490
+ });
143491
+ const map2 = /* @__PURE__ */ new Map();
143492
+ for (const file2 of files) {
143493
+ map2.set(file2.filename, commentableLinesForFile(file2.patch));
143494
+ }
143495
+ return map2;
143496
+ }
143497
+ function validateInlineComments(comments, map2) {
143498
+ const valid = [];
143499
+ const dropped = [];
143500
+ for (const c2 of comments) {
143501
+ const side = c2.side === "LEFT" ? "LEFT" : "RIGHT";
143502
+ const line = c2.line ?? 0;
143503
+ const startLine = c2.start_line ?? line;
143504
+ const lines = map2.get(c2.path);
143505
+ const record3 = (reason) => {
143506
+ const entry = { path: c2.path, line, side, reason };
143507
+ if (c2.start_line != null) entry.startLine = c2.start_line;
143508
+ dropped.push(entry);
143509
+ };
143510
+ if (!lines) {
143511
+ record3(`file not in PR diff`);
143512
+ continue;
143513
+ }
143514
+ if (lines.LEFT.size === 0 && lines.RIGHT.size === 0) {
143515
+ record3(`file has no textual diff (binary, pure rename, or mode change)`);
143516
+ continue;
143517
+ }
143518
+ const anchors = lines[side];
143519
+ if (!anchors.has(line)) {
143520
+ record3(`line ${line} (${side}) is not inside a diff hunk`);
143521
+ continue;
143522
+ }
143523
+ if (c2.start_line != null && c2.start_line > line) {
143524
+ record3(
143525
+ `start_line ${c2.start_line} is after line ${line} \u2014 ranges must satisfy start_line <= line`
143526
+ );
143527
+ continue;
143528
+ }
143529
+ if (startLine !== line && !anchors.has(startLine)) {
143530
+ record3(`start_line ${startLine} (${side}) is not inside a diff hunk`);
143531
+ continue;
143532
+ }
143533
+ valid.push(c2);
143534
+ }
143535
+ return { valid, dropped };
143536
+ }
143537
+ var MAX_DROPPED_COMMENT_LINES = 50;
143538
+ function reviewSkipDecision(params) {
143539
+ if (params.body || params.hasComments) return null;
143540
+ if (!params.approved) {
143541
+ return {
143542
+ kind: "no-issues",
143543
+ reason: "no issues found \u2014 nothing to post"
143544
+ };
143545
+ }
143546
+ if (!params.prApproveEnabled) {
143547
+ return {
143548
+ kind: "empty-downgraded-approve",
143549
+ reason: "approve requested but prApproveEnabled is disabled; no feedback body or comments to post as a COMMENT review instead"
143550
+ };
143551
+ }
143552
+ return null;
143553
+ }
143554
+ function formatDroppedCommentsNote(dropped) {
143555
+ const renderEntry = (d3) => {
143556
+ const range2 = d3.startLine != null && d3.startLine !== d3.line ? `${d3.startLine}-${d3.line}` : `${d3.line}`;
143557
+ return `- \`${d3.path}:${range2}\` (${d3.side}) \u2014 ${d3.reason}`;
143558
+ };
143559
+ const shown = dropped.slice(0, MAX_DROPPED_COMMENT_LINES).map(renderEntry);
143560
+ const remainder = dropped.length - shown.length;
143561
+ if (remainder > 0) shown.push(`- \u2026and ${remainder} more dropped comment(s) not shown`);
143562
+ return `
143563
+
143564
+ ---
143565
+
143566
+ **Note:** ${dropped.length} inline comment(s) dropped because they did not anchor to lines inside the PR diff:
143567
+ ` + shown.join("\n");
143568
+ }
143569
+ var CreatePullRequestReview = type({
143570
+ pull_number: type.number.describe("The pull request number to review"),
143571
+ body: type.string.describe(
143572
+ "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."
143573
+ ).optional(),
143574
+ approved: type.boolean.describe(
143575
+ "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."
143576
+ ).optional(),
143577
+ commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
143578
+ comments: type({
143579
+ path: type.string.describe(
143580
+ "The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
143581
+ ),
143582
+ line: type.number.describe(
143583
+ "Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
143584
+ ),
143585
+ side: type.enumerated("LEFT", "RIGHT").describe(
143586
+ "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
143587
+ ).optional(),
143588
+ body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
143589
+ suggestion: type.string.describe(
143590
+ "Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
143591
+ ).optional(),
143592
+ start_line: type.number.describe(
143593
+ "Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
143594
+ ).optional()
143595
+ }).array().describe(
143596
+ "Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
143597
+ ).optional()
143598
+ });
143599
+ function CreatePullRequestReviewTool(ctx) {
143600
+ return tool({
143601
+ name: "create_pull_request_review",
143602
+ 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.`,
143603
+ parameters: CreatePullRequestReview,
143604
+ execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
143605
+ if (body) body = fixDoubleEscapedString(body);
143606
+ ctx.toolState.issueNumber = pull_number;
143607
+ const skip = reviewSkipDecision({
143608
+ approved: approved ?? false,
143609
+ body,
143610
+ hasComments: comments.length > 0,
143611
+ prApproveEnabled: ctx.prApproveEnabled
143612
+ });
143613
+ if (skip) {
143614
+ log.info(`skipping review submission: ${skip.reason}`);
143615
+ return { success: true, skipped: true, reason: skip.reason };
143616
+ }
143617
+ let event = approved ? "APPROVE" : "COMMENT";
143618
+ if (event === "APPROVE" && !ctx.prApproveEnabled) {
143619
+ log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
143620
+ event = "COMMENT";
143621
+ }
143622
+ const params = {
143623
+ owner: ctx.repo.owner,
143624
+ repo: ctx.repo.name,
143625
+ pull_number,
143626
+ event
143627
+ };
143628
+ let latestHeadSha;
143629
+ if (commit_id) {
143630
+ params.commit_id = commit_id;
143631
+ } else {
143632
+ const pr = await ctx.octokit.rest.pulls.get({
143633
+ owner: ctx.repo.owner,
143634
+ repo: ctx.repo.name,
143635
+ pull_number
143636
+ });
143637
+ latestHeadSha = pr.data.head.sha;
143638
+ params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
143639
+ if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143640
+ log.info(
143641
+ `anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
143642
+ );
143643
+ }
143644
+ }
143645
+ runDiffCoveragePreflight({ ctx });
143646
+ const reviewComments = comments.map((comment) => {
143647
+ let commentBody = fixDoubleEscapedString(comment.body || "");
143648
+ if (comment.suggestion !== void 0) {
143649
+ const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
143650
+ commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
143651
+ }
143652
+ const side = comment.side || "RIGHT";
143653
+ const reviewComment = {
143654
+ path: comment.path,
143655
+ line: comment.line,
143656
+ body: commentBody,
143657
+ side
143658
+ };
143659
+ if (comment.start_line != null && comment.start_line !== comment.line) {
143660
+ reviewComment.start_line = comment.start_line;
143661
+ reviewComment.start_side = side;
143662
+ }
143663
+ return reviewComment;
143664
+ });
143665
+ let droppedComments = [];
143666
+ if (reviewComments.length > 0) {
143667
+ const commentableMap = await buildCommentableMap(ctx, pull_number);
143668
+ const validation = validateInlineComments(reviewComments, commentableMap);
143669
+ droppedComments = validation.dropped;
143670
+ if (droppedComments.length > 0) {
143671
+ log.info(
143672
+ `dropping ${droppedComments.length}/${reviewComments.length} inline comment(s) that do not anchor to PR diff lines`
143673
+ );
143674
+ }
143675
+ params.comments = validation.valid;
143676
+ }
143677
+ if (droppedComments.length > 0) {
143678
+ const note = formatDroppedCommentsNote(droppedComments);
143679
+ body = body ? body + note : note.replace(/^\n\n/, "");
143680
+ }
143681
+ if (!approved && !body && !params.comments?.length) {
143682
+ log.info("review has no body and all inline comments were dropped \u2014 skipping submission");
143683
+ return {
143684
+ success: true,
143685
+ skipped: true,
143686
+ reason: "all inline comments were invalid \u2014 nothing to post",
143687
+ droppedComments
143688
+ };
143689
+ }
143690
+ let result;
143691
+ try {
143692
+ result = body ? await createAndSubmitWithFooter(ctx, params, {
143693
+ body,
143694
+ approved: approved ?? false,
143695
+ hasComments: (params.comments?.length ?? 0) > 0
143696
+ }) : await createReviewWithStrandedRecovery(ctx, params);
143697
+ } catch (err) {
143698
+ if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
143699
+ const details = params.comments.map((c2) => {
143700
+ const line = c2.line ?? 0;
143701
+ const startLine = c2.start_line ?? line;
143702
+ const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
143703
+ return `${c2.path}:${range2} (${c2.side ?? "RIGHT"})`;
143704
+ });
143705
+ const rawMsg = err instanceof Error ? err.message : String(err);
143706
+ const checkoutRef = formatMcpToolRef(ctx.agentId, "checkout_pr");
143707
+ throw new Error(
143708
+ `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}`,
143709
+ { cause: err }
143710
+ );
143711
+ }
143712
+ log.debug(`createReview response: ${JSON.stringify(result.data)}`);
143713
+ if (!result.data.id) {
143714
+ throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
143715
+ }
143716
+ const reviewId = result.data.id;
143717
+ const reviewNodeId = result.data.node_id;
143718
+ const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
143719
+ ctx.toolState.review = {
143720
+ id: reviewId,
143721
+ nodeId: reviewNodeId,
143722
+ reviewedSha: actuallyReviewedSha
143723
+ };
143724
+ if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143725
+ const fromSha = ctx.toolState.checkoutSha;
143726
+ const toSha = latestHeadSha;
143727
+ ctx.toolState.beforeSha = fromSha;
143728
+ ctx.toolState.checkoutSha = toSha;
143729
+ log.info(
143730
+ `new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
143731
+ );
143732
+ return {
143733
+ success: true,
143734
+ reviewId,
143735
+ html_url: result.data.html_url,
143736
+ state: result.data.state,
143737
+ user: result.data.user?.login,
143738
+ submitted_at: result.data.submitted_at,
143739
+ droppedComments: droppedComments.length > 0 ? droppedComments : void 0,
143740
+ newCommits: {
143741
+ from: fromSha,
143742
+ to: toSha,
143743
+ 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.`
143744
+ }
143745
+ };
143746
+ }
143747
+ return {
143748
+ success: true,
143749
+ reviewId,
143750
+ html_url: result.data.html_url,
143751
+ state: result.data.state,
143752
+ user: result.data.user?.login,
143753
+ submitted_at: result.data.submitted_at,
143754
+ droppedComments: droppedComments.length > 0 ? droppedComments : void 0
143755
+ };
143756
+ })
143757
+ });
143758
+ }
143759
+ function runDiffCoveragePreflight(params) {
143760
+ const coverageState = params.ctx.toolState.diffCoverage;
143761
+ if (!coverageState) {
143762
+ log.debug("diff coverage pre-flight skipped: no diffCoverage state present in toolState");
143763
+ return;
143764
+ }
143765
+ if (coverageState.coveragePreflightRan) {
143766
+ log.debug("diff coverage pre-flight skipped: already ran in this session");
143767
+ return;
143768
+ }
143769
+ coverageState.coveragePreflightRan = true;
143770
+ log.debug(
143771
+ `diff coverage pre-flight start: diffPath=${coverageState.diffPath}, totalLines=${coverageState.totalLines}, tocEntries=${coverageState.tocEntries.length}, coveredRanges=${coverageState.coveredRanges.length}`
143772
+ );
143773
+ const breakdown = getDiffCoverageBreakdown({ state: coverageState });
143774
+ const unread = [];
143775
+ let unreadLines = 0;
143776
+ for (const file2 of breakdown.files) {
143777
+ if (file2.unreadRanges.length === 0) continue;
143778
+ const rangesText = file2.unreadRanges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
143779
+ const fileUnreadLines = countLinesInRanges({ ranges: file2.unreadRanges });
143780
+ unread.push({ path: file2.filename, ranges: rangesText, unreadLines: fileUnreadLines });
143781
+ unreadLines += fileUnreadLines;
143782
+ }
143783
+ coverageState.lastBreakdown = renderDiffCoverageBreakdown({
143784
+ diffPath: coverageState.diffPath,
143785
+ breakdown
143786
+ });
143787
+ log.debug(
143788
+ `diff coverage pre-flight breakdown: coveredLines=${breakdown.coveredLines}, unreadLines=${unreadLines}`
143789
+ );
143790
+ if (unreadLines === 0) {
143791
+ log.debug("diff coverage pre-flight passed: no unread regions");
143792
+ return;
143793
+ }
143794
+ log.info(
143795
+ `diff coverage pre-flight nudge: unread lines=${unreadLines}, unread files=${unread.length}`
143796
+ );
143797
+ const unreadText = unread.map((entry) => `- ${entry.path} (${entry.unreadLines} lines, ${entry.ranges})`).join("\n");
143798
+ throw new Error(
143799
+ `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.
143800
+
143801
+ unread TOC regions:
143802
+ ${unreadText}
143803
+
143804
+ ${coverageState.lastBreakdown}`
143805
+ );
143806
+ }
143807
+ async function clearStrandedPendingReview(ctx, params) {
143808
+ const originalErr = params.originalErr;
143809
+ const msg = originalErr instanceof Error ? originalErr.message.toLowerCase() : "";
143810
+ if (getHttpStatus(originalErr) !== 422 || !msg.includes("pending review")) throw originalErr;
143811
+ const reviews = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listReviews, {
143812
+ owner: params.owner,
143813
+ repo: params.repo,
143814
+ pull_number: params.pull_number,
143815
+ per_page: 100
143816
+ }).catch((listErr) => {
143817
+ log.info(
143818
+ `\xBB listReviews failed during pending-review cleanup, surfacing original 422: ${listErr instanceof Error ? listErr.message : String(listErr)}`
143819
+ );
143820
+ throw originalErr;
143821
+ });
143822
+ const leftover = reviews.find((r) => r.state === "PENDING");
143823
+ if (!leftover?.id) throw originalErr;
143824
+ log.info(
143825
+ `\xBB clearing leftover pending review ${leftover.id} (likely stranded by a killed prior run)`
143826
+ );
143827
+ try {
143828
+ await ctx.octokit.rest.pulls.deletePendingReview({
143829
+ owner: params.owner,
143830
+ repo: params.repo,
143831
+ pull_number: params.pull_number,
143832
+ review_id: leftover.id
143833
+ });
143834
+ } catch (cleanupErr) {
143835
+ const cleanupStatus = getHttpStatus(cleanupErr);
143836
+ if (cleanupStatus !== 404 && cleanupStatus !== 422) throw cleanupErr;
143837
+ log.debug(`\xBB delete of leftover pending ${leftover.id} no-op (status ${cleanupStatus})`);
143838
+ }
143839
+ }
143840
+ async function createReviewWithStrandedRecovery(ctx, params) {
143841
+ try {
143842
+ return await ctx.octokit.rest.pulls.createReview(params);
143843
+ } catch (err) {
143844
+ await clearStrandedPendingReview(ctx, {
143845
+ owner: params.owner,
143846
+ repo: params.repo,
143847
+ pull_number: params.pull_number,
143848
+ originalErr: err
143849
+ });
143850
+ return await ctx.octokit.rest.pulls.createReview(params);
143851
+ }
143852
+ }
143853
+ async function createAndSubmitWithFooter(ctx, params, opts) {
143854
+ const { event: _2, ...pendingParams } = params;
143855
+ let pending;
143856
+ try {
143857
+ pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
143858
+ } catch (err) {
143859
+ await clearStrandedPendingReview(ctx, {
143860
+ owner: params.owner,
143861
+ repo: params.repo,
143862
+ pull_number: params.pull_number,
143863
+ originalErr: err
143864
+ });
143865
+ pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
143866
+ }
143867
+ if (!pending.data.id) {
143868
+ throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
143869
+ }
143870
+ try {
143871
+ const customParts = [];
143872
+ if (!opts.approved) {
143873
+ const apiUrl = getApiUrl();
143874
+ if (opts.hasComments) {
143875
+ const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143876
+ const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
143877
+ customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
143878
+ } else {
143879
+ const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143880
+ customParts.push(`[Fix it \u2794](${fixUrl})`);
143881
+ }
143882
+ }
143883
+ const footer = buildPullfrogFooter({
143884
+ workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
143885
+ customParts,
143886
+ model: ctx.toolState.model
143887
+ });
143888
+ return await ctx.octokit.rest.pulls.submitReview({
143889
+ owner: params.owner,
143890
+ repo: params.repo,
143891
+ pull_number: params.pull_number,
143892
+ review_id: pending.data.id,
143893
+ event: params.event,
143894
+ body: opts.body + footer
143895
+ });
143896
+ } catch (err) {
143897
+ try {
143898
+ await ctx.octokit.rest.pulls.deletePendingReview({
143899
+ owner: params.owner,
143900
+ repo: params.repo,
143901
+ pull_number: params.pull_number,
143902
+ review_id: pending.data.id
143903
+ });
143904
+ log.debug(`\xBB deleted leftover pending review ${pending.data.id} after failure`);
143905
+ } catch (cleanupErr) {
143906
+ log.debug(
143907
+ `\xBB failed to delete pending review ${pending.data.id}: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
143908
+ );
143909
+ }
143910
+ throw err;
143911
+ }
143912
+ }
143913
+ async function reportReviewNodeId(ctx, params) {
143914
+ await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
143915
+ }
143916
+
142460
143917
  // mcp/checkout.ts
142461
143918
  function formatFilesWithLineNumbers(files) {
142462
143919
  const output = [];
@@ -142523,7 +143980,10 @@ function formatFilesWithLineNumbers(files) {
142523
143980
  }
142524
143981
  const tocLines = [`## Files (${files.length})`];
142525
143982
  for (const entry of tocEntries) {
142526
- tocLines.push(`- ${entry.filename} \u2192 lines ${entry.startLine}-${entry.endLine}`);
143983
+ const anchor = createHash2("sha256").update(entry.filename).digest("hex");
143984
+ tocLines.push(
143985
+ `- ${entry.filename} \u2192 lines ${entry.startLine}-${entry.endLine} \xB7 diff-${anchor}`
143986
+ );
142527
143987
  }
142528
143988
  tocLines.push("");
142529
143989
  tocLines.push("---");
@@ -142545,7 +144005,7 @@ async function fetchAndFormatPrDiff(ctx, pullNumber) {
142545
144005
  pull_number: pullNumber,
142546
144006
  per_page: 100
142547
144007
  });
142548
- return formatFilesWithLineNumbers(files);
144008
+ return { ...formatFilesWithLineNumbers(files), files };
142549
144009
  }
142550
144010
  async function createTempBranch(params) {
142551
144011
  const response = await params.octokit.rest.git.createRef({
@@ -142612,6 +144072,8 @@ async function ensureBeforeShaReachable(params) {
142612
144072
  async function checkoutPrBranch(pr, params) {
142613
144073
  const { octokit, owner, name, gitToken, toolState, beforeSha } = params;
142614
144074
  log.info(`\xBB checking out PR #${pr.number}...`);
144075
+ rejectIfLeadingDash(pr.baseRef, "PR base ref");
144076
+ rejectIfLeadingDash(pr.headRef, "PR head ref");
142615
144077
  const isFork = pr.headRepoFullName !== pr.baseRepoFullName;
142616
144078
  const localBranch = `pr-${pr.number}`;
142617
144079
  const isShallow = $("git", ["rev-parse", "--is-shallow-repository"], { log: false }).trim() === "true";
@@ -142622,7 +144084,7 @@ async function checkoutPrBranch(pr, params) {
142622
144084
  if (!alreadyOnBranch) {
142623
144085
  $("git", ["checkout", "-B", pr.baseRef, `origin/${pr.baseRef}`], { log: false });
142624
144086
  log.debug(`\xBB fetching PR #${pr.number} (${localBranch})...`);
142625
- await $git("fetch", ["--no-tags", "origin", `pull/${pr.number}/head:${localBranch}`], {
144087
+ await $git("fetch", ["--no-tags", "origin", `+pull/${pr.number}/head:${localBranch}`], {
142626
144088
  token: gitToken
142627
144089
  });
142628
144090
  $("git", ["checkout", localBranch], { log: false });
@@ -142705,10 +144167,11 @@ async function checkoutPrBranch(pr, params) {
142705
144167
  remoteBranch: pr.headRef,
142706
144168
  localBranch
142707
144169
  };
142708
- await executeLifecycleHook({
144170
+ const postCheckoutHook = await executeLifecycleHook({
142709
144171
  event: "post-checkout",
142710
144172
  script: params.postCheckoutScript
142711
144173
  });
144174
+ return { hookWarning: postCheckoutHook.warning };
142712
144175
  }
142713
144176
  function CheckoutPrTool(ctx) {
142714
144177
  return tool({
@@ -142734,7 +144197,7 @@ function CheckoutPrTool(ctx) {
142734
144197
  baseRepoFullName: prResponse.data.base.repo.full_name,
142735
144198
  maintainerCanModify: prResponse.data.maintainer_can_modify
142736
144199
  };
142737
- await checkoutPrBranch(pr, {
144200
+ const checkoutResult = await checkoutPrBranch(pr, {
142738
144201
  octokit: ctx.octokit,
142739
144202
  owner: ctx.repo.owner,
142740
144203
  name: ctx.repo.name,
@@ -142777,11 +144240,49 @@ ${diffPreview}`);
142777
144240
  const diffPath = join3(tempDir, `pr-${pull_number}-${headShort}.diff`);
142778
144241
  writeFileSync(diffPath, formatResult.content);
142779
144242
  log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
144243
+ ctx.toolState.diffCoverage = createDiffCoverageState({
144244
+ diffPath,
144245
+ totalLines: countLines({ content: formatResult.content }),
144246
+ toc: formatResult.toc
144247
+ });
144248
+ log.debug(
144249
+ `\xBB diff coverage initialized: diffPath=${diffPath}, totalLines=${ctx.toolState.diffCoverage.totalLines}, tocEntries=${ctx.toolState.diffCoverage.tocEntries.length}`
144250
+ );
144251
+ const cached4 = /* @__PURE__ */ new Map();
144252
+ for (const file2 of formatResult.files) {
144253
+ cached4.set(file2.filename, commentableLinesForFile(file2.patch));
144254
+ }
144255
+ ctx.toolState.commentableLinesByFile = cached4;
144256
+ ctx.toolState.commentableLinesPullNumber = pull_number;
144257
+ ctx.toolState.commentableLinesCheckoutSha = ctx.toolState.checkoutSha;
142780
144258
  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.` : "";
144259
+ const COMMIT_LOG_MAX = 200;
144260
+ const baseRange = `origin/${pr.baseRef}..HEAD`;
144261
+ let commitCount = 0;
144262
+ let commitLog = "";
144263
+ let commitLogUnavailable = false;
144264
+ try {
144265
+ commitCount = parseInt(
144266
+ $("git", ["rev-list", "--count", baseRange], { log: false }).trim() || "0",
144267
+ 10
144268
+ );
144269
+ commitLog = $("git", ["log", "--oneline", `--max-count=${COMMIT_LOG_MAX}`, baseRange], {
144270
+ log: false
144271
+ });
144272
+ } catch (err) {
144273
+ commitLogUnavailable = true;
144274
+ log.debug(
144275
+ `\xBB unable to compute commit metadata for ${baseRange}: ${err instanceof Error ? err.message : String(err)}`
144276
+ );
144277
+ }
144278
+ const commitLogTruncated = commitCount > COMMIT_LOG_MAX;
144279
+ 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.` : "";
144280
+ 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.` : "";
142781
144281
  return {
142782
144282
  success: true,
142783
144283
  number: prResponse.data.number,
142784
144284
  title: prResponse.data.title,
144285
+ body: prResponse.data.body,
142785
144286
  base: pr.baseRef,
142786
144287
  localBranch: `pr-${pull_number}`,
142787
144288
  remoteBranch: `refs/heads/${pr.headRef}`,
@@ -142792,7 +144293,12 @@ ${diffPreview}`);
142792
144293
  diffPath,
142793
144294
  incrementalDiffPath,
142794
144295
  toc: formatResult.toc,
142795
- 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
144296
+ commitCount,
144297
+ commitLog,
144298
+ commitLogTruncated,
144299
+ commitLogUnavailable,
144300
+ hookWarning: checkoutResult.hookWarning,
144301
+ 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
142796
144302
  };
142797
144303
  })
142798
144304
  });
@@ -143020,244 +144526,6 @@ function CommitInfoTool(ctx) {
143020
144526
  });
143021
144527
  }
143022
144528
 
143023
- // mcp/git.ts
143024
- function getPushDestination(branch, storedDest) {
143025
- if (storedDest && storedDest.localBranch === branch) {
143026
- log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
143027
- const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
143028
- log: false
143029
- }).trim();
143030
- return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
143031
- }
143032
- try {
143033
- const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
143034
- const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
143035
- const remoteBranch = merge4.replace(/^refs\/heads\//, "");
143036
- const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
143037
- return { remoteName: pushRemote, remoteBranch, url: url4 };
143038
- } catch {
143039
- log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
143040
- const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
143041
- return { remoteName: "origin", remoteBranch: branch, url: url4 };
143042
- }
143043
- }
143044
- function normalizeUrl(url4) {
143045
- return url4.replace(/\.git$/, "").toLowerCase();
143046
- }
143047
- function validatePushDestination(ctx, branch) {
143048
- const pushUrl = ctx.toolState.pushUrl;
143049
- if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
143050
- const dest = getPushDestination(branch, ctx.toolState.pushDest);
143051
- if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
143052
- throw new Error(
143053
- `Push blocked: destination does not match expected repository.
143054
- Expected: ${pushUrl}
143055
- Actual: ${dest.url}
143056
- Git configuration may have been tampered with.`
143057
- );
143058
- }
143059
- return dest;
143060
- }
143061
- var PushBranch = type({
143062
- branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
143063
- force: type.boolean.describe("Force push (use with caution)").default(false)
143064
- });
143065
- function PushBranchTool(ctx) {
143066
- const defaultBranch = ctx.repo.data.default_branch || "main";
143067
- const pushPermission = ctx.payload.push;
143068
- return tool({
143069
- name: "push_branch",
143070
- 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.",
143071
- parameters: PushBranch,
143072
- execute: execute(async ({ branchName, force }) => {
143073
- if (pushPermission === "disabled") {
143074
- throw new Error("Push is disabled. This repository is configured for read-only access.");
143075
- }
143076
- const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
143077
- const status = $("git", ["status", "--porcelain"], { log: false });
143078
- if (status) {
143079
- throw new Error(
143080
- `push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
143081
-
143082
- git status:
143083
- ${status}`
143084
- );
143085
- }
143086
- const pushDest = validatePushDestination(ctx, branch);
143087
- if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
143088
- throw new Error(
143089
- `Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
143090
- );
143091
- }
143092
- const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
143093
- const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
143094
- await executeLifecycleHook({ event: "prepush", script: ctx.prepushScript });
143095
- log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
143096
- if (force) {
143097
- log.warning(`force pushing - this will overwrite remote history`);
143098
- }
143099
- try {
143100
- await $git("push", pushArgs, {
143101
- token: ctx.gitToken
143102
- });
143103
- } catch (err) {
143104
- const msg = err instanceof Error ? err.message : String(err);
143105
- if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
143106
- throw new Error(
143107
- `push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
143108
-
143109
- to resolve this:
143110
- 1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
143111
- 2. use the git tool to rebase your changes: git({ subcommand: "rebase", args: ["origin/${pushDest.remoteBranch}"] })
143112
- 3. resolve any merge conflicts if needed
143113
- 4. retry push_branch`
143114
- );
143115
- }
143116
- throw err;
143117
- }
143118
- return {
143119
- success: true,
143120
- branch,
143121
- remoteBranch: pushDest.remoteBranch,
143122
- remote: pushDest.remoteName,
143123
- force,
143124
- message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
143125
- };
143126
- })
143127
- });
143128
- }
143129
- var AUTH_REQUIRED_REDIRECT = {
143130
- push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
143131
- fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
143132
- pull: "use git_fetch to fetch the remote ref, then use this git tool with subcommand 'merge' or 'rebase' locally.",
143133
- clone: "the repository is already cloned. use checkout_pr for PR branches."
143134
- };
143135
- var NOSHELL_BLOCKED_SUBCOMMANDS = {
143136
- config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
143137
- submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
143138
- "update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
143139
- "filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
143140
- replace: "Blocked: git replace can redirect object lookups.",
143141
- // subcommands that accept --exec or similar flags for arbitrary code execution
143142
- rebase: "Blocked: git rebase --exec can execute arbitrary shell commands.",
143143
- bisect: "Blocked: git bisect run can execute arbitrary shell commands."
143144
- };
143145
- var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
143146
- var COLLAPSE_THRESHOLD = 200;
143147
- var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
143148
- var Git = type({
143149
- subcommand: type(subcommandPattern).describe("Git subcommand (e.g., 'status', 'log', 'diff')"),
143150
- args: type.string.array().describe("Additional arguments for the git command").optional()
143151
- });
143152
- function GitTool(ctx) {
143153
- return tool({
143154
- name: "git",
143155
- description: "Run git commands. For push/fetch/pull, use the dedicated MCP tools instead (push_branch, git_fetch).",
143156
- parameters: Git,
143157
- execute: execute(async (params) => {
143158
- const subcommand = params.subcommand;
143159
- const args2 = params.args ?? [];
143160
- const redirect = AUTH_REQUIRED_REDIRECT[subcommand];
143161
- if (redirect) {
143162
- throw new Error(`git ${subcommand} is not available through this tool \u2014 ${redirect}`);
143163
- }
143164
- if (ctx.payload.shell === "disabled") {
143165
- const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[subcommand];
143166
- if (blocked) {
143167
- throw new Error(blocked);
143168
- }
143169
- for (const arg4 of args2) {
143170
- const isBlocked = NOSHELL_BLOCKED_ARGS.some(
143171
- (flag) => arg4 === flag || arg4.startsWith(flag + "=")
143172
- );
143173
- if (isBlocked) {
143174
- throw new Error(
143175
- `Blocked: '${arg4}' flag can execute arbitrary code and is not allowed.`
143176
- );
143177
- }
143178
- }
143179
- }
143180
- const output = $("git", [subcommand, ...args2], { log: false });
143181
- const lineCount = output.split("\n").length;
143182
- if (lineCount > COLLAPSE_THRESHOLD) {
143183
- log.group(`git ${subcommand} output (${lineCount} lines)`, () => {
143184
- log.info(output);
143185
- });
143186
- } else if (output) {
143187
- log.info(output);
143188
- }
143189
- return { success: true, output };
143190
- })
143191
- });
143192
- }
143193
- var GitFetch = type({
143194
- ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
143195
- depth: type.number.describe("Fetch depth (for shallow clones)").optional()
143196
- });
143197
- function GitFetchTool(ctx) {
143198
- return tool({
143199
- name: "git_fetch",
143200
- description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
143201
- parameters: GitFetch,
143202
- execute: execute(async (params) => {
143203
- const fetchArgs = ["--no-tags", "origin", params.ref];
143204
- if (params.depth !== void 0) {
143205
- fetchArgs.push(`--depth=${params.depth}`);
143206
- }
143207
- await $git("fetch", fetchArgs, {
143208
- token: ctx.gitToken
143209
- });
143210
- return { success: true, ref: params.ref };
143211
- })
143212
- });
143213
- }
143214
- var DeleteBranch = type({
143215
- branchName: type.string.describe("Remote branch to delete")
143216
- });
143217
- function DeleteBranchTool(ctx) {
143218
- const pushPermission = ctx.payload.push;
143219
- return tool({
143220
- name: "delete_branch",
143221
- description: "Delete a remote branch. Requires push: enabled permission.",
143222
- parameters: DeleteBranch,
143223
- execute: execute(async (params) => {
143224
- if (pushPermission !== "enabled") {
143225
- throw new Error(
143226
- "Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
143227
- );
143228
- }
143229
- await $git("push", ["origin", "--delete", params.branchName], {
143230
- token: ctx.gitToken
143231
- });
143232
- return { success: true, deleted: params.branchName };
143233
- })
143234
- });
143235
- }
143236
- var PushTags = type({
143237
- tag: type.string.describe("Tag name to push"),
143238
- force: type.boolean.describe("Force push the tag").default(false)
143239
- });
143240
- function PushTagsTool(ctx) {
143241
- const pushPermission = ctx.payload.push;
143242
- return tool({
143243
- name: "push_tags",
143244
- description: "Push a tag to remote. Requires push: enabled permission.",
143245
- parameters: PushTags,
143246
- execute: execute(async (params) => {
143247
- if (pushPermission !== "enabled") {
143248
- throw new Error(
143249
- "Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
143250
- );
143251
- }
143252
- const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
143253
- await $git("push", pushArgs, {
143254
- token: ctx.gitToken
143255
- });
143256
- return { success: true, tag: params.tag };
143257
- })
143258
- });
143259
- }
143260
-
143261
144529
  // mcp/issue.ts
143262
144530
  var Issue = type({
143263
144531
  title: type.string.describe("the title of the issue"),
@@ -143720,221 +144988,6 @@ function PullRequestInfoTool(ctx) {
143720
144988
  });
143721
144989
  }
143722
144990
 
143723
- // mcp/review.ts
143724
- function getHttpStatus(err) {
143725
- if (typeof err !== "object" || err === null) return void 0;
143726
- const status = err.status;
143727
- return typeof status === "number" ? status : void 0;
143728
- }
143729
- var CreatePullRequestReview = type({
143730
- pull_number: type.number.describe("The pull request number to review"),
143731
- body: type.string.describe(
143732
- "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."
143733
- ).optional(),
143734
- approved: type.boolean.describe(
143735
- "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."
143736
- ).optional(),
143737
- commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
143738
- comments: type({
143739
- path: type.string.describe(
143740
- "The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
143741
- ),
143742
- line: type.number.describe(
143743
- "Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
143744
- ),
143745
- side: type.enumerated("LEFT", "RIGHT").describe(
143746
- "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
143747
- ).optional(),
143748
- body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
143749
- suggestion: type.string.describe(
143750
- "Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
143751
- ).optional(),
143752
- start_line: type.number.describe(
143753
- "Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
143754
- ).optional()
143755
- }).array().describe(
143756
- "Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
143757
- ).optional()
143758
- });
143759
- function CreatePullRequestReviewTool(ctx) {
143760
- return tool({
143761
- name: "create_pull_request_review",
143762
- 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.`,
143763
- parameters: CreatePullRequestReview,
143764
- execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
143765
- if (body) body = fixDoubleEscapedString(body);
143766
- if (body && ctx.toolState.selectedMode === "Review" && ctx.toolState.todoTracker) {
143767
- ctx.toolState.todoTracker.cancel();
143768
- await ctx.toolState.todoTracker.settled();
143769
- ctx.toolState.todoTracker.completeInProgress();
143770
- const collapsible = ctx.toolState.todoTracker.renderCollapsible();
143771
- if (collapsible) {
143772
- body = `${body}
143773
-
143774
- ${collapsible}`;
143775
- }
143776
- }
143777
- ctx.toolState.issueNumber = pull_number;
143778
- if (!approved && !body && comments.length === 0) {
143779
- log.info(
143780
- "review has no body and no inline comments \u2014 skipping submission (no issues found)"
143781
- );
143782
- return {
143783
- success: true,
143784
- skipped: true,
143785
- reason: "no issues found \u2014 nothing to post"
143786
- };
143787
- }
143788
- let event = approved ? "APPROVE" : "COMMENT";
143789
- if (event === "APPROVE" && !ctx.prApproveEnabled) {
143790
- log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
143791
- event = "COMMENT";
143792
- }
143793
- const params = {
143794
- owner: ctx.repo.owner,
143795
- repo: ctx.repo.name,
143796
- pull_number,
143797
- event
143798
- };
143799
- let latestHeadSha;
143800
- if (commit_id) {
143801
- params.commit_id = commit_id;
143802
- } else {
143803
- const pr = await ctx.octokit.rest.pulls.get({
143804
- owner: ctx.repo.owner,
143805
- repo: ctx.repo.name,
143806
- pull_number
143807
- });
143808
- latestHeadSha = pr.data.head.sha;
143809
- params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
143810
- if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143811
- log.info(
143812
- `anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
143813
- );
143814
- }
143815
- }
143816
- const reviewComments = comments.map((comment) => {
143817
- let commentBody = fixDoubleEscapedString(comment.body || "");
143818
- if (comment.suggestion !== void 0) {
143819
- const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
143820
- commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
143821
- }
143822
- const side = comment.side || "RIGHT";
143823
- const reviewComment = {
143824
- path: comment.path,
143825
- line: comment.line,
143826
- body: commentBody,
143827
- side
143828
- };
143829
- if (comment.start_line != null && comment.start_line !== comment.line) {
143830
- reviewComment.start_line = comment.start_line;
143831
- reviewComment.start_side = side;
143832
- }
143833
- return reviewComment;
143834
- });
143835
- if (reviewComments.length > 0) {
143836
- params.comments = reviewComments;
143837
- }
143838
- let result;
143839
- try {
143840
- result = body ? await createAndSubmitWithFooter(ctx, params, {
143841
- body,
143842
- approved: approved ?? false,
143843
- hasComments: reviewComments.length > 0
143844
- }) : await ctx.octokit.rest.pulls.createReview(params);
143845
- } catch (err) {
143846
- if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
143847
- const details = params.comments.map((c2) => {
143848
- const line = c2.line ?? 0;
143849
- const startLine = c2.start_line ?? line;
143850
- const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
143851
- return `${c2.path}:${range2} (${c2.side ?? "RIGHT"})`;
143852
- });
143853
- throw new Error(
143854
- `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(", ")}`
143855
- );
143856
- }
143857
- log.debug(`createReview response: ${JSON.stringify(result.data)}`);
143858
- if (!result.data.id) {
143859
- throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
143860
- }
143861
- const reviewId = result.data.id;
143862
- const reviewNodeId = result.data.node_id;
143863
- const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
143864
- ctx.toolState.review = {
143865
- id: reviewId,
143866
- nodeId: reviewNodeId,
143867
- reviewedSha: actuallyReviewedSha
143868
- };
143869
- if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143870
- const fromSha = ctx.toolState.checkoutSha;
143871
- const toSha = latestHeadSha;
143872
- ctx.toolState.beforeSha = fromSha;
143873
- ctx.toolState.checkoutSha = toSha;
143874
- log.info(
143875
- `new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
143876
- );
143877
- return {
143878
- success: true,
143879
- reviewId,
143880
- html_url: result.data.html_url,
143881
- state: result.data.state,
143882
- user: result.data.user?.login,
143883
- submitted_at: result.data.submitted_at,
143884
- newCommits: {
143885
- from: fromSha,
143886
- to: toSha,
143887
- 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.`
143888
- }
143889
- };
143890
- }
143891
- return {
143892
- success: true,
143893
- reviewId,
143894
- html_url: result.data.html_url,
143895
- state: result.data.state,
143896
- user: result.data.user?.login,
143897
- submitted_at: result.data.submitted_at
143898
- };
143899
- })
143900
- });
143901
- }
143902
- async function createAndSubmitWithFooter(ctx, params, opts) {
143903
- const { event: _2, ...pendingParams } = params;
143904
- const pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
143905
- if (!pending.data.id) {
143906
- throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
143907
- }
143908
- const customParts = [];
143909
- if (!opts.approved) {
143910
- const apiUrl = getApiUrl();
143911
- if (opts.hasComments) {
143912
- const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143913
- const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
143914
- customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
143915
- } else {
143916
- const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143917
- customParts.push(`[Fix it \u2794](${fixUrl})`);
143918
- }
143919
- }
143920
- const footer = buildPullfrogFooter({
143921
- workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
143922
- customParts,
143923
- model: ctx.toolState.model
143924
- });
143925
- return ctx.octokit.rest.pulls.submitReview({
143926
- owner: params.owner,
143927
- repo: params.repo,
143928
- pull_number: params.pull_number,
143929
- review_id: pending.data.id,
143930
- event: params.event,
143931
- body: opts.body + footer
143932
- });
143933
- }
143934
- async function reportReviewNodeId(ctx, params) {
143935
- await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
143936
- }
143937
-
143938
144991
  // mcp/reviewComments.ts
143939
144992
  import { writeFileSync as writeFileSync4 } from "node:fs";
143940
144993
  import { join as join6 } from "node:path";
@@ -143982,7 +145035,7 @@ query ($owner: String!, $name: String!, $prNumber: Int!) {
143982
145035
  }
143983
145036
  }
143984
145037
  `;
143985
- function countLines(str) {
145038
+ function countLines2(str) {
143986
145039
  let count = 1;
143987
145040
  let index = -1;
143988
145041
  while ((index = str.indexOf("\n", index + 1)) !== -1) {
@@ -144124,13 +145177,13 @@ function formatReviewThreads(threadBlocks, header) {
144124
145177
  const reviewBodyLines = [];
144125
145178
  if (header.reviewBody) {
144126
145179
  reviewBodyLines.push("## Review Body", "", header.reviewBody, "");
144127
- currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines(line), 0);
145180
+ currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines2(line), 0);
144128
145181
  }
144129
145182
  const tocEntries = [];
144130
145183
  const threadLines = [];
144131
145184
  for (const block of threadBlocks) {
144132
145185
  const startLine = currentLine;
144133
- const actualLineCount = block.content.reduce((sum, line) => sum + countLines(line), 0);
145186
+ const actualLineCount = block.content.reduce((sum, line) => sum + countLines2(line), 0);
144134
145187
  const endLine = currentLine + actualLineCount - 1;
144135
145188
  tocEntries.push(`- ${block.path}:${block.lineRange} \u2192 lines ${startLine}-${endLine}`);
144136
145189
  threadLines.push(...block.content);
@@ -144453,7 +145506,7 @@ GitHub's markdown parser requires a blank line between ALL block-level elements.
144453
145506
  Rules:
144454
145507
  - \`##\` 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
144455
145508
  - ALL variable names, identifiers, and file names in body text must be in backticks
144456
- - 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.
145509
+ - 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.
144457
145510
  - Add <br/> before each ## heading for visual spacing. Do NOT use horizontal rules (---)
144458
145511
  - Do NOT include raw diff stats like '+123 / -45' or line counts
144459
145512
  - Do NOT include code blocks or repeat diff contents
@@ -144528,7 +145581,7 @@ ${learningsStep(t2, 6)}`
144528
145581
  description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
144529
145582
  prompt: `### Checklist
144530
145583
 
144531
- 1. Checkout the PR via \`${t2("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. Read the diff to identify the major areas of change.
145584
+ 1. Checkout the PR via \`${t2("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.
144532
145585
 
144533
145586
  2. For each area of change:
144534
145587
  - read the diff and trace data flow, check boundaries, and verify assumptions
@@ -144545,6 +145598,7 @@ ${learningsStep(t2, 6)}`
144545
145598
  4. Submit \u2014 ALWAYS submit exactly one review via \`${t2("create_pull_request_review")}\`.
144546
145599
  Do NOT call \`report_progress\` \u2014 the review is the final record and the progress
144547
145600
  comment will be cleaned up automatically.
145601
+ 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.
144548
145602
 
144549
145603
  - **critical issues** (blocks merge \u2014 bugs, security, data loss):
144550
145604
  \`approved: false\`. Body begins with a GitHub alert blockquote, e.g.:
@@ -144562,7 +145616,7 @@ ${learningsStep(t2, 6)}`
144562
145616
  description: "Re-review a PR after new commits are pushed; focus on new changes since the last review",
144563
145617
  prompt: `### Checklist
144564
145618
 
144565
- 1. Checkout the PR via \`${t2("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available).
145619
+ 1. Checkout the PR via \`${t2("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.
144566
145620
 
144567
145621
  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.
144568
145622
 
@@ -144586,6 +145640,7 @@ ${learningsStep(t2, 6)}`
144586
145640
  - 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.
144587
145641
 
144588
145642
  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:
145643
+ - 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.
144589
145644
  - 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.
144590
145645
  - ELSE IF NEW CRITICAL ISSUES (blocks merge): call \`${t2("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).
144591
145646
  - ELSE IF NEW RECOMMENDED CHANGES (non-critical): call \`${t2("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).
@@ -145053,11 +146108,11 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
145053
146108
  await killProcessGroup(proc);
145054
146109
  }
145055
146110
  }, timeout);
145056
- const exitCode = await new Promise((resolve2) => {
146111
+ const exitCode = await new Promise((resolve3) => {
145057
146112
  const done = (code) => {
145058
146113
  exited = true;
145059
146114
  clearTimeout(timeoutId);
145060
- resolve2(code);
146115
+ resolve3(code);
145061
146116
  };
145062
146117
  proc.on("exit", done);
145063
146118
  proc.on("error", () => done(null));
@@ -145196,12 +146251,12 @@ function readEnvPort() {
145196
146251
  return parsed2;
145197
146252
  }
145198
146253
  function isPortAvailable(port) {
145199
- return new Promise((resolve2) => {
146254
+ return new Promise((resolve3) => {
145200
146255
  const server = createServer();
145201
146256
  server.unref();
145202
- server.once("error", () => resolve2(false));
146257
+ server.once("error", () => resolve3(false));
145203
146258
  server.once("listening", () => {
145204
- server.close(() => resolve2(true));
146259
+ server.close(() => resolve3(true));
145205
146260
  });
145206
146261
  server.listen(port, mcpHost);
145207
146262
  });
@@ -145341,9 +146396,12 @@ async function killBackgroundProcesses(toolState) {
145341
146396
  async function startMcpHttpServer(ctx, options) {
145342
146397
  const tools = buildOrchestratorTools(ctx, options?.outputSchema);
145343
146398
  const startResult = await selectMcpPort(ctx, tools);
146399
+ let disposed = false;
145344
146400
  return {
145345
146401
  url: startResult.url,
145346
146402
  [Symbol.asyncDispose]: async () => {
146403
+ if (disposed) return;
146404
+ disposed = true;
145347
146405
  closeBrowserDaemon(ctx.toolState);
145348
146406
  await killBackgroundProcesses(ctx.toolState);
145349
146407
  await startResult.server.stop();
@@ -145538,39 +146596,6 @@ var ThinkingTimer = class {
145538
146596
  }
145539
146597
  };
145540
146598
 
145541
- // agents/shared.ts
145542
- import { execFileSync as execFileSync2 } from "node:child_process";
145543
- var MAX_STDERR_LINES = 20;
145544
- var MAX_COMMIT_RETRIES = 3;
145545
- function getGitStatus() {
145546
- try {
145547
- return execFileSync2("git", ["status", "--porcelain"], {
145548
- encoding: "utf-8",
145549
- timeout: 1e4
145550
- }).trim();
145551
- } catch {
145552
- return "";
145553
- }
145554
- }
145555
- function buildCommitPrompt(_agentId, status) {
145556
- return [
145557
- `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.`,
145558
- "",
145559
- "```",
145560
- status,
145561
- "```"
145562
- ].join("\n");
145563
- }
145564
- var agent = (input) => {
145565
- return {
145566
- ...input,
145567
- run: async (ctx) => {
145568
- log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
145569
- return input.run(ctx);
145570
- }
145571
- };
145572
- };
145573
-
145574
146599
  // agents/claude.ts
145575
146600
  async function installClaudeCli() {
145576
146601
  return await installFromNpmTarball({
@@ -145609,6 +146634,7 @@ async function runClaude(params) {
145609
146634
  let finalOutput = "";
145610
146635
  let sessionId;
145611
146636
  let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
146637
+ let accumulatedCostUsd = 0;
145612
146638
  let tokensLogged = false;
145613
146639
  function buildUsage() {
145614
146640
  const totalInput = accumulatedTokens.input + accumulatedTokens.cacheRead + accumulatedTokens.cacheWrite;
@@ -145617,7 +146643,8 @@ async function runClaude(params) {
145617
146643
  inputTokens: totalInput,
145618
146644
  outputTokens: accumulatedTokens.output,
145619
146645
  cacheReadTokens: accumulatedTokens.cacheRead || void 0,
145620
- cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
146646
+ cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
146647
+ costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
145621
146648
  } : void 0;
145622
146649
  }
145623
146650
  const handlers2 = {
@@ -145634,6 +146661,12 @@ async function runClaude(params) {
145634
146661
  finalOutput = message;
145635
146662
  } else if (block.type === "tool_use") {
145636
146663
  const toolName = block.name || "unknown";
146664
+ if (params.onToolUse) {
146665
+ params.onToolUse({
146666
+ toolName,
146667
+ input: block.input
146668
+ });
146669
+ }
145637
146670
  thinkingTimer.markToolCall();
145638
146671
  log.toolCall({ toolName, input: block.input || {} });
145639
146672
  if (toolName.includes("report_progress") && params.todoTracker) {
@@ -145649,6 +146682,8 @@ async function runClaude(params) {
145649
146682
  if (msgUsage) {
145650
146683
  accumulatedTokens.input += msgUsage.input_tokens || 0;
145651
146684
  accumulatedTokens.output += msgUsage.output_tokens || 0;
146685
+ accumulatedTokens.cacheRead += msgUsage.cache_read_input_tokens || 0;
146686
+ accumulatedTokens.cacheWrite += msgUsage.cache_creation_input_tokens || 0;
145652
146687
  }
145653
146688
  },
145654
146689
  user: (event) => {
@@ -145679,19 +146714,18 @@ async function runClaude(params) {
145679
146714
  const cacheRead = usage?.cache_read_input_tokens || 0;
145680
146715
  const cacheWrite = usage?.cache_creation_input_tokens || 0;
145681
146716
  const outputTokens = usage?.output_tokens || 0;
145682
- const totalInput = inputTokens + cacheRead + cacheWrite;
146717
+ const costUsd = typeof event.total_cost_usd === "number" && Number.isFinite(event.total_cost_usd) ? event.total_cost_usd : 0;
145683
146718
  accumulatedTokens = { input: inputTokens, output: outputTokens, cacheRead, cacheWrite };
146719
+ accumulatedCostUsd = costUsd;
145684
146720
  log.info(`\xBB ${params.label} result: subtype=${subtype}, turns=${numTurns}`);
145685
146721
  if (!tokensLogged) {
145686
- log.table([
145687
- [
145688
- { data: "Input", header: true },
145689
- { data: "Cache Read", header: true },
145690
- { data: "Cache Write", header: true },
145691
- { data: "Output", header: true }
145692
- ],
145693
- [String(totalInput), String(cacheRead), String(cacheWrite), String(outputTokens)]
145694
- ]);
146722
+ logTokenTable({
146723
+ input: inputTokens,
146724
+ cacheRead,
146725
+ cacheWrite,
146726
+ output: outputTokens,
146727
+ costUsd
146728
+ });
145695
146729
  tokensLogged = true;
145696
146730
  }
145697
146731
  } else if (subtype === "error_max_turns") {
@@ -145726,6 +146760,7 @@ async function runClaude(params) {
145726
146760
  cwd: params.cwd,
145727
146761
  env: params.env,
145728
146762
  activityTimeout: 3e5,
146763
+ onActivityTimeout: params.onActivityTimeout,
145729
146764
  stdio: ["ignore", "pipe", "pipe"],
145730
146765
  onStdout: async (chunk) => {
145731
146766
  const text = chunk.toString();
@@ -145737,25 +146772,33 @@ async function runClaude(params) {
145737
146772
  for (const line of lines) {
145738
146773
  const trimmed = line.trim();
145739
146774
  if (!trimmed) continue;
146775
+ let event;
145740
146776
  try {
145741
- const event = JSON.parse(trimmed);
145742
- eventCount++;
145743
- log.debug(JSON.stringify(event, null, 2));
145744
- const timeSinceLastActivity = getIdleMs();
145745
- if (timeSinceLastActivity > 1e4) {
145746
- log.info(
145747
- `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
145748
- );
145749
- }
145750
- markActivity();
145751
- const handler2 = handlers2[event.type];
145752
- if (handler2) {
145753
- handler2(event);
145754
- } else {
145755
- log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
145756
- }
146777
+ event = JSON.parse(trimmed);
145757
146778
  } catch {
145758
146779
  log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
146780
+ continue;
146781
+ }
146782
+ eventCount++;
146783
+ log.debug(JSON.stringify(event, null, 2));
146784
+ const timeSinceLastActivity = getIdleMs();
146785
+ if (timeSinceLastActivity > 1e4) {
146786
+ log.info(
146787
+ `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
146788
+ );
146789
+ }
146790
+ markActivity();
146791
+ const handler2 = handlers2[event.type];
146792
+ if (!handler2) {
146793
+ log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
146794
+ continue;
146795
+ }
146796
+ try {
146797
+ handler2(event);
146798
+ } catch (err) {
146799
+ log.info(
146800
+ `\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
146801
+ );
145759
146802
  }
145760
146803
  }
145761
146804
  },
@@ -145789,16 +146832,9 @@ async function runClaude(params) {
145789
146832
  if (stderrContext) log.info(`\xBB last stderr output:
145790
146833
  ${stderrContext}`);
145791
146834
  }
145792
- if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
145793
- const totalTokens = accumulatedTokens.input + accumulatedTokens.output;
145794
- log.table([
145795
- [
145796
- { data: "Input Tokens", header: true },
145797
- { data: "Output Tokens", header: true },
145798
- { data: "Total Tokens", header: true }
145799
- ],
145800
- [String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
145801
- ]);
146835
+ if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
146836
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
146837
+ tokensLogged = true;
145802
146838
  }
145803
146839
  const usage = buildUsage();
145804
146840
  if (result.exitCode !== 0) {
@@ -145831,7 +146867,7 @@ ${stderrContext}`);
145831
146867
  params.todoTracker?.cancel();
145832
146868
  const duration4 = performance6.now() - startTime;
145833
146869
  const errorMessage = error49 instanceof Error ? error49.message : String(error49);
145834
- const isActivityTimeout = errorMessage.includes("activity timeout");
146870
+ const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
145835
146871
  const stderrContext = recentStderr.slice(-10).join("\n");
145836
146872
  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`;
145837
146873
  log.info(
@@ -145922,8 +146958,7 @@ var claude = agent({
145922
146958
  "--effort",
145923
146959
  effort,
145924
146960
  "--disallowedTools",
145925
- "Bash",
145926
- "Agent(Bash)"
146961
+ "Bash,Agent(Bash)"
145927
146962
  ];
145928
146963
  if (model) {
145929
146964
  baseArgs.push("--model", model);
@@ -145936,11 +146971,19 @@ var claude = agent({
145936
146971
  log.info(`\xBB effort: ${effort}`);
145937
146972
  log.debug(`\xBB starting Pullfrog (Claude Code): node ${baseArgs.join(" ")}`);
145938
146973
  log.debug(`\xBB working directory: ${repoDir}`);
145939
- const runParams = { label: "Pullfrog", cwd: repoDir, env: env2, todoTracker: ctx.todoTracker };
146974
+ const runParams = {
146975
+ label: "Pullfrog",
146976
+ cwd: repoDir,
146977
+ env: env2,
146978
+ todoTracker: ctx.todoTracker,
146979
+ onActivityTimeout: ctx.onActivityTimeout,
146980
+ onToolUse: ctx.onToolUse
146981
+ };
145940
146982
  let result = await runClaude({
145941
146983
  ...runParams,
145942
146984
  args: [...baseArgs, "-p", ctx.instructions.full]
145943
146985
  });
146986
+ let aggregatedUsage = result.usage;
145944
146987
  for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
145945
146988
  if (!result.success || !result.sessionId) break;
145946
146989
  const status = getGitStatus();
@@ -145957,8 +147000,9 @@ ${status}`);
145957
147000
  result.sessionId
145958
147001
  ]
145959
147002
  });
147003
+ aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
145960
147004
  }
145961
- return result;
147005
+ return { ...result, usage: aggregatedUsage };
145962
147006
  }
145963
147007
  });
145964
147008
 
@@ -146040,6 +147084,7 @@ async function runOpenCode(params) {
146040
147084
  const thinkingTimer = new ThinkingTimer();
146041
147085
  let finalOutput = "";
146042
147086
  let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
147087
+ let accumulatedCostUsd = 0;
146043
147088
  let tokensLogged = false;
146044
147089
  const toolCallTimings = /* @__PURE__ */ new Map();
146045
147090
  let currentStepId = null;
@@ -146052,7 +147097,8 @@ async function runOpenCode(params) {
146052
147097
  inputTokens: totalInput,
146053
147098
  outputTokens: accumulatedTokens.output,
146054
147099
  cacheReadTokens: accumulatedTokens.cacheRead || void 0,
146055
- cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
147100
+ cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
147101
+ costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
146056
147102
  } : void 0;
146057
147103
  }
146058
147104
  const handlers2 = {
@@ -146063,6 +147109,7 @@ async function runOpenCode(params) {
146063
147109
  log.debug(`\xBB ${params.label} init event (full): ${JSON.stringify(event)}`);
146064
147110
  finalOutput = "";
146065
147111
  accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
147112
+ accumulatedCostUsd = 0;
146066
147113
  tokensLogged = false;
146067
147114
  },
146068
147115
  message: (event) => {
@@ -146107,6 +147154,9 @@ async function runOpenCode(params) {
146107
147154
  accumulatedTokens.cacheRead += eventTokens.cache?.read || 0;
146108
147155
  accumulatedTokens.cacheWrite += eventTokens.cache?.write || 0;
146109
147156
  }
147157
+ if (typeof event.part?.cost === "number" && Number.isFinite(event.part.cost)) {
147158
+ accumulatedCostUsd += event.part.cost;
147159
+ }
146110
147160
  if (currentStepId === stepId) {
146111
147161
  currentStepId = null;
146112
147162
  currentStepType = null;
@@ -146124,6 +147174,12 @@ async function runOpenCode(params) {
146124
147174
  if (stepHistory.length > 0) {
146125
147175
  stepHistory[stepHistory.length - 1].toolCalls.push(toolName);
146126
147176
  }
147177
+ if (params.onToolUse) {
147178
+ params.onToolUse({
147179
+ toolName,
147180
+ input: event.part?.state?.input
147181
+ });
147182
+ }
146127
147183
  thinkingTimer.markToolCall();
146128
147184
  log.toolCall({ toolName, input: event.part?.state?.input || {} });
146129
147185
  if (event.part?.state?.status === "completed" && event.part.state.output) {
@@ -146179,19 +147235,9 @@ async function runOpenCode(params) {
146179
147235
  if (event.status === "error") {
146180
147236
  log.info(`\xBB ${params.label} failed: ${JSON.stringify(event)}`);
146181
147237
  } else {
146182
- const inputTokens = event.stats?.input_tokens || accumulatedTokens.input || 0;
146183
- const outputTokens = event.stats?.output_tokens || accumulatedTokens.output || 0;
146184
- const totalTokens = event.stats?.total_tokens || inputTokens + outputTokens;
146185
147238
  log.info(`\xBB run complete: tool_calls=${toolCalls}, duration=${duration4}ms`);
146186
- if ((inputTokens > 0 || outputTokens > 0) && !tokensLogged) {
146187
- log.table([
146188
- [
146189
- { data: "Input Tokens", header: true },
146190
- { data: "Output Tokens", header: true },
146191
- { data: "Total Tokens", header: true }
146192
- ],
146193
- [String(inputTokens), String(outputTokens), String(totalTokens)]
146194
- ]);
147239
+ if ((accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0) && !tokensLogged) {
147240
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
146195
147241
  tokensLogged = true;
146196
147242
  }
146197
147243
  }
@@ -146208,6 +147254,7 @@ async function runOpenCode(params) {
146208
147254
  cwd: params.cwd,
146209
147255
  env: params.env,
146210
147256
  activityTimeout: 3e5,
147257
+ onActivityTimeout: params.onActivityTimeout,
146211
147258
  stdio: ["ignore", "pipe", "pipe"],
146212
147259
  onStdout: async (chunk) => {
146213
147260
  const text = chunk.toString();
@@ -146219,29 +147266,37 @@ async function runOpenCode(params) {
146219
147266
  for (const line of lines) {
146220
147267
  const trimmed = line.trim();
146221
147268
  if (!trimmed) continue;
147269
+ let event;
146222
147270
  try {
146223
- const event = JSON.parse(trimmed);
146224
- eventCount++;
146225
- log.debug(JSON.stringify(event, null, 2));
146226
- const timeSinceLastActivity = getIdleMs();
146227
- if (timeSinceLastActivity > 1e4) {
146228
- const activeToolCalls = toolCallTimings.size;
146229
- const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
146230
- log.info(
146231
- `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
146232
- );
146233
- }
146234
- markActivity();
146235
- const handler2 = handlers2[event.type];
146236
- if (handler2) {
146237
- await handler2(event);
146238
- } else {
146239
- log.info(
146240
- `\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
146241
- );
146242
- }
147271
+ event = JSON.parse(trimmed);
146243
147272
  } catch {
146244
147273
  log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
147274
+ continue;
147275
+ }
147276
+ eventCount++;
147277
+ log.debug(JSON.stringify(event, null, 2));
147278
+ const timeSinceLastActivity = getIdleMs();
147279
+ if (timeSinceLastActivity > 1e4) {
147280
+ const activeToolCalls = toolCallTimings.size;
147281
+ const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
147282
+ log.info(
147283
+ `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
147284
+ );
147285
+ }
147286
+ markActivity();
147287
+ const handler2 = handlers2[event.type];
147288
+ if (!handler2) {
147289
+ log.info(
147290
+ `\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
147291
+ );
147292
+ continue;
147293
+ }
147294
+ try {
147295
+ await handler2(event);
147296
+ } catch (err) {
147297
+ log.info(
147298
+ `\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
147299
+ );
146245
147300
  }
146246
147301
  }
146247
147302
  },
@@ -146275,16 +147330,9 @@ async function runOpenCode(params) {
146275
147330
  if (stderrContext) log.info(`\xBB last stderr output:
146276
147331
  ${stderrContext}`);
146277
147332
  }
146278
- if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
146279
- const totalTokens = accumulatedTokens.input + accumulatedTokens.output;
146280
- log.table([
146281
- [
146282
- { data: "Input Tokens", header: true },
146283
- { data: "Output Tokens", header: true },
146284
- { data: "Total Tokens", header: true }
146285
- ],
146286
- [String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
146287
- ]);
147333
+ if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
147334
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
147335
+ tokensLogged = true;
146288
147336
  }
146289
147337
  const usage = buildUsage();
146290
147338
  if (result.exitCode !== 0) {
@@ -146310,7 +147358,7 @@ ${stderrContext}`);
146310
147358
  params.todoTracker?.cancel();
146311
147359
  const duration4 = performance7.now() - startTime;
146312
147360
  const errorMessage = error49 instanceof Error ? error49.message : String(error49);
146313
- const isActivityTimeout = errorMessage.includes("activity timeout");
147361
+ const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
146314
147362
  const stderrContext = recentStderr.slice(-10).join("\n");
146315
147363
  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`;
146316
147364
  log.info(
@@ -146367,12 +147415,15 @@ var opencode = agent({
146367
147415
  cliPath,
146368
147416
  cwd: repoDir,
146369
147417
  env: env2,
146370
- todoTracker: ctx.todoTracker
147418
+ todoTracker: ctx.todoTracker,
147419
+ onActivityTimeout: ctx.onActivityTimeout,
147420
+ onToolUse: ctx.onToolUse
146371
147421
  };
146372
147422
  let result = await runOpenCode({
146373
147423
  ...runParams,
146374
147424
  args: [...baseArgs, ctx.instructions.full]
146375
147425
  });
147426
+ let aggregatedUsage = result.usage;
146376
147427
  for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
146377
147428
  if (!result.success) break;
146378
147429
  const status = getGitStatus();
@@ -146383,8 +147434,9 @@ ${status}`);
146383
147434
  ...runParams,
146384
147435
  args: [...baseArgs, "--continue", buildCommitPrompt("opencode", status)]
146385
147436
  });
147437
+ aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
146386
147438
  }
146387
- return result;
147439
+ return { ...result, usage: aggregatedUsage };
146388
147440
  }
146389
147441
  });
146390
147442
 
@@ -146402,13 +147454,11 @@ function hasClaudeCodeAuth() {
146402
147454
  function resolveModel(ctx) {
146403
147455
  const envModel = process.env.PULLFROG_MODEL?.trim();
146404
147456
  if (envModel) {
146405
- log.info(`\xBB model: ${envModel} (override via PULLFROG_MODEL)`);
146406
- return envModel;
147457
+ return resolveCliModel(envModel) ?? envModel;
146407
147458
  }
146408
147459
  if (ctx.slug) {
146409
147460
  const resolved = resolveCliModel(ctx.slug);
146410
147461
  if (resolved) {
146411
- log.info(`\xBB model: ${resolved} (resolved from ${ctx.slug})`);
146412
147462
  return resolved;
146413
147463
  }
146414
147464
  log.warning(`\xBB unknown model slug "${ctx.slug}" \u2014 agent will auto-select`);
@@ -146419,7 +147469,6 @@ function resolveAgent(ctx) {
146419
147469
  const envAgent = process.env.PULLFROG_AGENT?.trim();
146420
147470
  if (envAgent) {
146421
147471
  if (envAgent in agents) {
146422
- log.info(`\xBB agent: ${envAgent} (override via PULLFROG_AGENT)`);
146423
147472
  return agents[envAgent];
146424
147473
  }
146425
147474
  log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
@@ -146428,7 +147477,6 @@ function resolveAgent(ctx) {
146428
147477
  try {
146429
147478
  const provider2 = getModelProvider(ctx.model);
146430
147479
  if (provider2 === "anthropic" && hasClaudeCodeAuth()) {
146431
- log.info(`\xBB agent: claude (auto-selected for ${ctx.model})`);
146432
147480
  return agents.claude;
146433
147481
  }
146434
147482
  } catch {
@@ -150648,9 +151696,9 @@ async function startGitAuthServer(tmpdir3) {
150648
151696
  res.writeHead(409, { "Content-Type": "text/plain" });
150649
151697
  res.end("compromised");
150650
151698
  });
150651
- await new Promise((resolve2, reject) => {
151699
+ await new Promise((resolve3, reject) => {
150652
151700
  server.on("error", reject);
150653
- server.listen(0, "127.0.0.1", () => resolve2());
151701
+ server.listen(0, "127.0.0.1", () => resolve3());
150654
151702
  });
150655
151703
  const rawAddr = server.address();
150656
151704
  if (!rawAddr || typeof rawAddr === "string") {
@@ -150694,7 +151742,7 @@ async function startGitAuthServer(tmpdir3) {
150694
151742
  clearTimeout(entry.timeout);
150695
151743
  }
150696
151744
  codes.clear();
150697
- await new Promise((resolve2) => server.close(() => resolve2()));
151745
+ await new Promise((resolve3) => server.close(() => resolve3()));
150698
151746
  log.debug("git auth server closed");
150699
151747
  }
150700
151748
  return {
@@ -150881,7 +151929,7 @@ MCP servers provide tools you can call. Inspect your available MCP servers at st
150881
151929
 
150882
151930
  ### Git
150883
151931
 
150884
- Use \`${t2("git")}\` for local git commands (status, log, diff, add, commit, checkout, branch, merge, etc.). For operations requiring remote authentication, use the dedicated MCP tools:
151932
+ Use \`${t2("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 \`${t2("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:
150885
151933
  - \`${t2("push_branch")}\` - push current or specified branch
150886
151934
  - \`${t2("git_fetch")}\` - fetch refs from remote
150887
151935
  - \`${t2("checkout_pr")}\` - checkout a PR branch (fetches and configures push for forks)
@@ -151059,7 +152107,7 @@ function normalizeEnv() {
151059
152107
 
151060
152108
  // utils/payload.ts
151061
152109
  var core4 = __toESM(require_core(), 1);
151062
- import { isAbsolute, resolve } from "node:path";
152110
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
151063
152111
 
151064
152112
  // utils/versioning.ts
151065
152113
  var import_semver2 = __toESM(require_semver2(), 1);
@@ -151113,8 +152161,8 @@ function isPayloadEvent(value2) {
151113
152161
  function resolveCwd(cwd) {
151114
152162
  const workspace = process.env.GITHUB_WORKSPACE;
151115
152163
  if (!cwd) return workspace;
151116
- if (isAbsolute(cwd)) return cwd;
151117
- return workspace ? resolve(workspace, cwd) : cwd;
152164
+ if (isAbsolute2(cwd)) return cwd;
152165
+ return workspace ? resolve2(workspace, cwd) : cwd;
151118
152166
  }
151119
152167
  function resolvePromptInput() {
151120
152168
  const prompt = core4.getInput("prompt", { required: true });
@@ -151293,7 +152341,8 @@ var defaultSettings = {
151293
152341
  shell: "restricted",
151294
152342
  prApproveEnabled: false,
151295
152343
  modeInstructions: {},
151296
- learnings: null
152344
+ learnings: null,
152345
+ envAllowlist: null
151297
152346
  };
151298
152347
  var defaultRunContext = {
151299
152348
  settings: defaultSettings,
@@ -151373,7 +152422,7 @@ async function resolveRunContextData(params) {
151373
152422
  }
151374
152423
 
151375
152424
  // utils/setup.ts
151376
- import { execSync as execSync3 } from "node:child_process";
152425
+ import { execFileSync as execFileSync5, execSync as execSync3 } from "node:child_process";
151377
152426
  import { mkdtempSync } from "node:fs";
151378
152427
  import { tmpdir as tmpdir2 } from "node:os";
151379
152428
  import { join as join13 } from "node:path";
@@ -151383,6 +152432,51 @@ function createTempDirectory() {
151383
152432
  log.info(`\xBB created temp dir at ${sharedTempDir}`);
151384
152433
  return sharedTempDir;
151385
152434
  }
152435
+ function envScopedToRepo() {
152436
+ const scoped = { ...process.env };
152437
+ for (const key of Object.keys(scoped)) {
152438
+ if (key.startsWith("GIT_")) delete scoped[key];
152439
+ }
152440
+ return scoped;
152441
+ }
152442
+ function removeIncludeIfEntries(repoDir) {
152443
+ const env2 = envScopedToRepo();
152444
+ let configOutput;
152445
+ try {
152446
+ configOutput = execSync3("git config --local --get-regexp -z ^includeif\\.", {
152447
+ cwd: repoDir,
152448
+ encoding: "utf-8",
152449
+ stdio: "pipe",
152450
+ env: env2
152451
+ });
152452
+ } catch {
152453
+ log.debug("\xBB no includeIf credential entries to remove");
152454
+ return;
152455
+ }
152456
+ const seen = /* @__PURE__ */ new Set();
152457
+ for (const entry of configOutput.split("\0")) {
152458
+ if (!entry) continue;
152459
+ const nl = entry.indexOf("\n");
152460
+ const key = nl === -1 ? entry : entry.slice(0, nl);
152461
+ if (!key || seen.has(key)) continue;
152462
+ seen.add(key);
152463
+ try {
152464
+ execFileSync5("git", ["config", "--local", "--unset-all", key], {
152465
+ cwd: repoDir,
152466
+ stdio: "pipe",
152467
+ env: env2
152468
+ });
152469
+ } catch (error49) {
152470
+ log.debug(
152471
+ `\xBB failed to unset ${key}: ${error49 instanceof Error ? error49.message : String(error49)}`
152472
+ );
152473
+ }
152474
+ }
152475
+ if (seen.size > 0)
152476
+ log.info(
152477
+ `\xBB removed ${seen.size} includeIf credential ${seen.size === 1 ? "entry" : "entries"}`
152478
+ );
152479
+ }
151386
152480
  async function setupGit(params) {
151387
152481
  const repoDir = process.cwd();
151388
152482
  log.info("\xBB setting up git configuration...");
@@ -151429,24 +152523,7 @@ async function setupGit(params) {
151429
152523
  } catch {
151430
152524
  log.debug("\xBB no existing authentication headers to remove");
151431
152525
  }
151432
- try {
151433
- const configOutput = execSync3("git config --local --get-regexp ^includeif\\.", {
151434
- cwd: repoDir,
151435
- encoding: "utf-8",
151436
- stdio: "pipe"
151437
- });
151438
- for (const line of configOutput.trim().split("\n")) {
151439
- const key = line.split(" ")[0];
151440
- if (!key) continue;
151441
- execSync3(`git config --local --unset "${key}"`, {
151442
- cwd: repoDir,
151443
- stdio: "pipe"
151444
- });
151445
- }
151446
- log.info("\xBB removed includeIf credential entries");
151447
- } catch {
151448
- log.debug("\xBB no includeIf credential entries to remove");
151449
- }
152526
+ removeIncludeIfEntries(repoDir);
151450
152527
  const originUrl = `https://github.com/${params.owner}/${params.name}.git`;
151451
152528
  $("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
151452
152529
  params.toolState.pushUrl = originUrl;
@@ -151465,6 +152542,13 @@ function parseTimeString(input) {
151465
152542
  const seconds = parseInt(match3[3] || "0", 10);
151466
152543
  return (hours * 3600 + minutes * 60 + seconds) * 1e3;
151467
152544
  }
152545
+ var TIMEOUT_MAX_MS = 2147483647;
152546
+ function resolveTimeoutMs(input) {
152547
+ if (!input) return null;
152548
+ const parsed2 = parseTimeString(input);
152549
+ if (parsed2 === null || parsed2 <= 0 || parsed2 > TIMEOUT_MAX_MS) return null;
152550
+ return parsed2;
152551
+ }
151468
152552
 
151469
152553
  // utils/todoTracking.ts
151470
152554
  function isValidTodoStatus(value2) {
@@ -151567,9 +152651,12 @@ function createTodoTracker(onUpdate) {
151567
152651
  if (item.status === "in_progress") item.status = "completed";
151568
152652
  }
151569
152653
  },
151570
- renderCollapsible() {
152654
+ renderCollapsible(options) {
151571
152655
  if (state.size === 0) return "";
151572
- const todos = Array.from(state.values());
152656
+ const shouldCompleteInProgress = options?.completeInProgress === true;
152657
+ const todos = Array.from(state.values()).map(
152658
+ (item) => shouldCompleteInProgress && item.status === "in_progress" ? { ...item, status: "completed" } : item
152659
+ );
151573
152660
  const completed = todos.filter((t2) => t2.status === "completed").length;
151574
152661
  const markdown = renderTodoMarkdown(todos);
151575
152662
  return `<details>
@@ -151629,6 +152716,32 @@ function resolveOutputSchema() {
151629
152716
  log.info("\xBB structured output schema provided \u2014 output will be required");
151630
152717
  return parsed2;
151631
152718
  }
152719
+ function resolveTimeoutForLog(timeout) {
152720
+ if (!timeout) return "1h (default)";
152721
+ if (timeout === TIMEOUT_DISABLED) return "none (disabled)";
152722
+ return timeout;
152723
+ }
152724
+ function resolveModelForLog(ctx) {
152725
+ const envModel = process.env.PULLFROG_MODEL?.trim();
152726
+ if (envModel) return `${envModel} (override via PULLFROG_MODEL)`;
152727
+ if (ctx.payload.proxyModel) return `${ctx.payload.proxyModel} (proxy)`;
152728
+ if (ctx.resolvedModel && ctx.payload.model && ctx.payload.model !== ctx.resolvedModel) {
152729
+ return `${ctx.resolvedModel} (resolved from ${ctx.payload.model})`;
152730
+ }
152731
+ if (ctx.resolvedModel) return ctx.resolvedModel;
152732
+ if (ctx.payload.model) return `${ctx.payload.model} (unresolved)`;
152733
+ return "auto";
152734
+ }
152735
+ function resolveAgentForLog(ctx) {
152736
+ const envAgent = process.env.PULLFROG_AGENT?.trim();
152737
+ if (envAgent && envAgent === ctx.agentName) {
152738
+ return `${ctx.agentName} (override via PULLFROG_AGENT)`;
152739
+ }
152740
+ if (ctx.agentName === "claude" && ctx.resolvedModel) {
152741
+ return `${ctx.agentName} (auto-selected for ${ctx.resolvedModel})`;
152742
+ }
152743
+ return ctx.agentName;
152744
+ }
151632
152745
  async function mintProxyKey(ctx) {
151633
152746
  try {
151634
152747
  process.env.ACTIONS_ID_TOKEN_REQUEST_URL = ctx.oidcCredentials.requestUrl;
@@ -151688,6 +152801,7 @@ async function main() {
151688
152801
  }
151689
152802
  const timer = new Timer();
151690
152803
  let activityTimeout = null;
152804
+ let safetyNetTimer;
151691
152805
  const resolvedPromptInput = resolvePromptInput();
151692
152806
  const toolState = initToolState({
151693
152807
  progressCommentId: typeof resolvedPromptInput !== "string" ? resolvedPromptInput.progressCommentId : void 0
@@ -151707,6 +152821,9 @@ async function main() {
151707
152821
  const count = Object.keys(runContext.dbSecrets).length;
151708
152822
  if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
151709
152823
  }
152824
+ if (runContext.repoSettings.envAllowlist) {
152825
+ setEnvAllowlist(runContext.repoSettings.envAllowlist);
152826
+ }
151710
152827
  const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
151711
152828
  toolState.model = payload.model;
151712
152829
  if (payload.event.trigger === "pull_request_synchronize") {
@@ -151771,10 +152888,13 @@ async function main() {
151771
152888
  postCheckoutScript: runContext.repoSettings.postCheckoutScript
151772
152889
  });
151773
152890
  timer.checkpoint("git");
151774
- await executeLifecycleHook({
152891
+ const setupHook = await executeLifecycleHook({
151775
152892
  event: "setup",
151776
152893
  script: runContext.repoSettings.setupScript
151777
152894
  });
152895
+ if (setupHook.warning) {
152896
+ throw new Error(setupHook.warning);
152897
+ }
151778
152898
  timer.checkpoint("lifecycleHooks::setup");
151779
152899
  const agentId = agent2.name;
151780
152900
  const modes2 = [...computeModes(agentId), ...runContext.repoSettings.modes];
@@ -151796,17 +152916,22 @@ async function main() {
151796
152916
  runId: runInfo.runId,
151797
152917
  jobId: runInfo.jobId,
151798
152918
  mcpServerUrl: "",
151799
- tmpdir: tmpdir3
152919
+ tmpdir: tmpdir3,
152920
+ resolvedModel
151800
152921
  };
151801
152922
  const mcpHttpServer = __using(_stack, await startMcpHttpServer(toolContext, { outputSchema }), true);
151802
152923
  toolContext.mcpServerUrl = mcpHttpServer.url;
151803
152924
  log.info(`\xBB MCP server started at ${mcpHttpServer.url}`);
151804
152925
  timer.checkpoint("mcpServer");
151805
152926
  startInstallation(toolContext);
151806
- if (payload.model) log.info(`\xBB model: ${payload.model}`);
151807
- if (payload.timeout) log.info(`\xBB timeout: ${payload.timeout}`);
152927
+ const modelForLog = resolveModelForLog({ payload, resolvedModel });
152928
+ const agentForLog = resolveAgentForLog({ agentName: agent2.name, resolvedModel });
152929
+ const timeoutForLog = resolveTimeoutForLog(payload.timeout);
152930
+ log.info(`\xBB model: ${modelForLog}`);
152931
+ log.info(`\xBB agent: ${agentForLog}`);
151808
152932
  log.info(`\xBB push: ${payload.push}`);
151809
152933
  log.info(`\xBB shell: ${payload.shell}`);
152934
+ log.info(`\xBB timeout: ${timeoutForLog}`);
151810
152935
  const instructions = resolveInstructions({
151811
152936
  payload,
151812
152937
  repo: runContext.repo,
@@ -151855,24 +152980,62 @@ ${instructions.user}` : null,
151855
152980
  }
151856
152981
  });
151857
152982
  toolState.todoTracker = todoTracker;
152983
+ let innerTimeoutFired = false;
152984
+ const onInnerActivityTimeout = () => {
152985
+ if (innerTimeoutFired) return;
152986
+ innerTimeoutFired = true;
152987
+ log.info(
152988
+ "\xBB inner activity timeout fired \u2014 stopping MCP server and starting 5min safety-net timer"
152989
+ );
152990
+ mcpHttpServer[Symbol.asyncDispose]().catch((err) => {
152991
+ log.debug(
152992
+ `mcp server stop after inner kill failed: ${err instanceof Error ? err.message : String(err)}`
152993
+ );
152994
+ });
152995
+ safetyNetTimer = setTimeout(
152996
+ () => {
152997
+ activityTimeout?.forceReject(
152998
+ "agent still pending 5min after inner activity kill \u2014 forcing exit"
152999
+ );
153000
+ },
153001
+ 5 * 60 * 1e3
153002
+ );
153003
+ safetyNetTimer.unref?.();
153004
+ };
151858
153005
  const agentPromise = agent2.run({
151859
153006
  payload,
151860
153007
  resolvedModel,
151861
153008
  mcpServerUrl: mcpHttpServer.url,
151862
153009
  tmpdir: tmpdir3,
151863
153010
  instructions,
151864
- todoTracker
153011
+ todoTracker,
153012
+ onActivityTimeout: onInnerActivityTimeout,
153013
+ onToolUse: (event) => {
153014
+ const wasTracked = recordDiffReadFromToolUse({
153015
+ state: toolState.diffCoverage,
153016
+ toolName: event.toolName,
153017
+ input: event.input,
153018
+ cwd: process.cwd()
153019
+ });
153020
+ if (!wasTracked) return;
153021
+ const trackedRanges = toolState.diffCoverage?.coveredRanges ?? [];
153022
+ log.debug(
153023
+ `\xBB diff coverage tracked from tool ${event.toolName} (${trackedRanges.length} merged range${trackedRanges.length === 1 ? "" : "s"})`
153024
+ );
153025
+ }
153026
+ });
153027
+ agentPromise.catch(() => {
151865
153028
  });
151866
153029
  let result;
151867
153030
  if (payload.timeout === TIMEOUT_DISABLED) {
151868
153031
  result = await Promise.race([agentPromise, activityTimeout.promise]);
151869
153032
  } else {
151870
- const parsed2 = payload.timeout ? parseTimeString(payload.timeout) : null;
151871
- if (payload.timeout && parsed2 === null) {
151872
- log.warning(`invalid timeout format "${payload.timeout}", using default 1h`);
153033
+ const usable = resolveTimeoutMs(payload.timeout);
153034
+ if (payload.timeout && usable === null) {
153035
+ log.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
151873
153036
  }
151874
- const timeoutMs = parsed2 ?? 36e5;
151875
- const actualTimeout = parsed2 !== null ? payload.timeout : "1h";
153037
+ const timeoutMs = usable ?? 36e5;
153038
+ const actualTimeout = usable !== null ? payload.timeout : "1h";
151876
153039
  let timeoutId;
151877
153040
  const timeoutPromise = new Promise((_4, reject) => {
151878
153041
  timeoutId = setTimeout(() => {
@@ -151959,8 +153122,21 @@ ${errorMessage}
151959
153122
  };
151960
153123
  } finally {
151961
153124
  activityTimeout?.stop();
153125
+ if (safetyNetTimer) clearTimeout(safetyNetTimer);
151962
153126
  if (usageSummaryPath) {
151963
- await writeGitHubUsageSummaryToFile(usageSummaryPath);
153127
+ try {
153128
+ await writeGitHubUsageSummaryToFile(usageSummaryPath);
153129
+ } catch (err) {
153130
+ log.debug(
153131
+ `failed to write usage summary to ${usageSummaryPath}: ${err instanceof Error ? err.message : String(err)}`
153132
+ );
153133
+ }
153134
+ }
153135
+ if (toolContext) {
153136
+ const patch = aggregateUsage(toolState.usageEntries);
153137
+ if (Object.keys(patch).length > 0) {
153138
+ await patchWorkflowRunFields(toolContext, patch);
153139
+ }
151964
153140
  }
151965
153141
  }
151966
153142
  } catch (_3) {
@@ -152014,7 +153190,7 @@ async function validateStuckProgressComment(ctx) {
152014
153190
  comment_id: commentId
152015
153191
  });
152016
153192
  const body = commentResult.data.body ?? "";
152017
- if (body.startsWith(LEAPING_INTO_ACTION_PREFIX)) {
153193
+ if (isLeapingIntoActionCommentBody(body)) {
152018
153194
  log.info(`[post] comment ${commentId} is stuck on "Leaping into action"`);
152019
153195
  return commentId;
152020
153196
  }
@@ -152224,7 +153400,7 @@ async function run(args2) {
152224
153400
  }
152225
153401
 
152226
153402
  // commands/init.ts
152227
- import { execFileSync as execFileSync5 } from "node:child_process";
153403
+ import { execFileSync as execFileSync6 } from "node:child_process";
152228
153404
 
152229
153405
  // node_modules/.pnpm/@clack+core@1.2.0/node_modules/@clack/core/dist/index.mjs
152230
153406
  import { styleText as y } from "node:util";
@@ -153216,7 +154392,7 @@ function handleCancel(value2) {
153216
154392
  function getGhToken() {
153217
154393
  let token;
153218
154394
  try {
153219
- token = execFileSync5("gh", ["auth", "token"], { encoding: "utf-8" }).trim();
154395
+ token = execFileSync6("gh", ["auth", "token"], { encoding: "utf-8" }).trim();
153220
154396
  } catch {
153221
154397
  bail(
153222
154398
  `gh cli not found or not authenticated.
@@ -153259,7 +154435,7 @@ async function ghApi(path3, token) {
153259
154435
  function parseGitRemote() {
153260
154436
  let url4;
153261
154437
  try {
153262
- url4 = execFileSync5("git", ["remote", "get-url", "origin"], { encoding: "utf-8" }).trim();
154438
+ url4 = execFileSync6("git", ["remote", "get-url", "origin"], { encoding: "utf-8" }).trim();
153263
154439
  } catch {
153264
154440
  bail("not a git repository or no 'origin' remote found.");
153265
154441
  }
@@ -153270,10 +154446,10 @@ function parseGitRemote() {
153270
154446
  function openBrowser(url4) {
153271
154447
  try {
153272
154448
  const platform = process.platform;
153273
- if (platform === "darwin") execFileSync5("open", [url4], { stdio: "ignore" });
154449
+ if (platform === "darwin") execFileSync6("open", [url4], { stdio: "ignore" });
153274
154450
  else if (platform === "win32")
153275
- execFileSync5("cmd", ["/c", "start", "", url4], { stdio: "ignore" });
153276
- else execFileSync5("xdg-open", [url4], { stdio: "ignore" });
154451
+ execFileSync6("cmd", ["/c", "start", "", url4], { stdio: "ignore" });
154452
+ else execFileSync6("xdg-open", [url4], { stdio: "ignore" });
153277
154453
  } catch {
153278
154454
  }
153279
154455
  }
@@ -153500,7 +154676,7 @@ function setGhSecret(ctx) {
153500
154676
  let orgFailed = false;
153501
154677
  if (ctx.org) {
153502
154678
  try {
153503
- execFileSync5("gh", ["secret", "set", ctx.name, "--org", ctx.org, "--visibility", "all"], {
154679
+ execFileSync6("gh", ["secret", "set", ctx.name, "--org", ctx.org, "--visibility", "all"], {
153504
154680
  input: ctx.value,
153505
154681
  stdio: ["pipe", "ignore", "pipe"],
153506
154682
  encoding: "utf-8"
@@ -153511,7 +154687,7 @@ function setGhSecret(ctx) {
153511
154687
  }
153512
154688
  }
153513
154689
  try {
153514
- execFileSync5("gh", ["secret", "set", ctx.name, "--repo", ctx.repoSlug], {
154690
+ execFileSync6("gh", ["secret", "set", ctx.name, "--repo", ctx.repoSlug], {
153515
154691
  input: ctx.value,
153516
154692
  stdio: ["pipe", "ignore", "pipe"],
153517
154693
  encoding: "utf-8"
@@ -153881,7 +155057,7 @@ async function run2() {
153881
155057
  }
153882
155058
 
153883
155059
  // cli.ts
153884
- var VERSION10 = "0.0.201";
155060
+ var VERSION10 = "0.0.202";
153885
155061
  var bin = basename2(process.argv[1] || "");
153886
155062
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
153887
155063
  var rawArgs = process.argv.slice(2);