pullfrog 0.0.200 → 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,
@@ -97692,14 +97692,14 @@ var require_turndown_cjs = __commonJS({
97692
97692
  } else if (node2.nodeType === 1) {
97693
97693
  replacement = replacementForNode.call(self2, node2);
97694
97694
  }
97695
- return join14(output, replacement);
97695
+ return join15(output, replacement);
97696
97696
  }, "");
97697
97697
  }
97698
97698
  function postProcess(output) {
97699
97699
  var self2 = this;
97700
97700
  this.rules.forEach(function(rule) {
97701
97701
  if (typeof rule.append === "function") {
97702
- output = join14(output, rule.append(self2.options));
97702
+ output = join15(output, rule.append(self2.options));
97703
97703
  }
97704
97704
  });
97705
97705
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -97711,7 +97711,7 @@ var require_turndown_cjs = __commonJS({
97711
97711
  if (whitespace.leading || whitespace.trailing) content = content.trim();
97712
97712
  return whitespace.leading + rule.replacement(content, node2, this.options) + whitespace.trailing;
97713
97713
  }
97714
- function join14(output, replacement) {
97714
+ function join15(output, replacement) {
97715
97715
  var s1 = trimTrailingNewlines(output);
97716
97716
  var s2 = trimLeadingNewlines(replacement);
97717
97717
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -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
  };
@@ -99208,6 +99208,8 @@ import { dirname as dirname3 } from "node:path";
99208
99208
 
99209
99209
  // main.ts
99210
99210
  var core6 = __toESM(require_core(), 1);
99211
+ import { existsSync as existsSync6, readdirSync } from "node:fs";
99212
+ import { join as join14 } from "node:path";
99211
99213
 
99212
99214
  // node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
99213
99215
  var liftArray = (data) => Array.isArray(data) ? data : [data];
@@ -105526,10 +105528,10 @@ var BaseScope = class {
105526
105528
  });
105527
105529
  };
105528
105530
  lazyResolutions = [];
105529
- lazilyResolve(resolve2, syntheticAlias) {
105531
+ lazilyResolve(resolve3, syntheticAlias) {
105530
105532
  const node2 = this.node("alias", {
105531
105533
  reference: syntheticAlias ?? "synthetic",
105532
- resolve: resolve2
105534
+ resolve: resolve3
105533
105535
  }, { prereduced: true });
105534
105536
  if (!this.resolved)
105535
105537
  this.lazyResolutions.push(node2);
@@ -107679,6 +107681,81 @@ var core = __toESM(require_core(), 1);
107679
107681
  var import_table = __toESM(require_src(), 1);
107680
107682
  import { AsyncLocalStorage } from "node:async_hooks";
107681
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
+
107682
107759
  // utils/globals.ts
107683
107760
  import { existsSync } from "node:fs";
107684
107761
  var isCloudflareSandbox = !!process.env.CLOUDFLARE_APPLICATION_ID && !!process.env.SANDBOX_VERSION;
@@ -107875,20 +107952,26 @@ function formatJsonValue(value2) {
107875
107952
  }
107876
107953
  function formatUsageSummary(entries) {
107877
107954
  if (entries.length === 0) return "";
107878
- const header = "| Agent | Input | Output | Cache Read | Cache Write |";
107879
- const separatorRow = "| --- | ---: | ---: | ---: | ---: |";
107955
+ const header = "| Agent | Input | Cache Read | Cache Write | Output | Total | Cost ($) |";
107956
+ const separatorRow = "| --- | ---: | ---: | ---: | ---: | ---: | ---: |";
107880
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";
107881
107961
  const rows = entries.map(
107882
- (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)} |`
107883
107963
  );
107884
107964
  const totalsRows = [];
107885
107965
  if (entries.length > 1) {
107886
- const totalInput = entries.reduce((sum, e) => sum + e.inputTokens, 0);
107966
+ const totalInput = entries.reduce((sum, e) => sum + nonCachedInput(e), 0);
107887
107967
  const totalOutput = entries.reduce((sum, e) => sum + e.outputTokens, 0);
107888
107968
  const totalCacheRead = entries.reduce((sum, e) => sum + (e.cacheReadTokens ?? 0), 0);
107889
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";
107890
107973
  totalsRows.push(
107891
- `| **Total** | **${fmt(totalInput)}** | **${fmt(totalOutput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** |`
107974
+ `| **Total** | **${fmt(totalInput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** | **${fmt(totalOutput)}** | **${fmt(grandTotal)}** | ${totalCostCell} |`
107892
107975
  );
107893
107976
  }
107894
107977
  return [
@@ -107931,7 +108014,7 @@ var providers = {
107931
108014
  models: {
107932
108015
  "claude-opus": {
107933
108016
  displayName: "Claude Opus",
107934
- resolve: "anthropic/claude-opus-4-6",
108017
+ resolve: "anthropic/claude-opus-4-7",
107935
108018
  openRouterResolve: "openrouter/anthropic/claude-opus-4.6",
107936
108019
  preferred: true
107937
108020
  },
@@ -107959,7 +108042,7 @@ var providers = {
107959
108042
  },
107960
108043
  "gpt-codex-mini": {
107961
108044
  displayName: "GPT Codex Mini",
107962
- resolve: "openai/codex-mini-latest",
108045
+ resolve: "openai/gpt-5.1-codex-mini",
107963
108046
  openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
107964
108047
  },
107965
108048
  o3: {
@@ -108049,7 +108132,7 @@ var providers = {
108049
108132
  },
108050
108133
  "claude-opus": {
108051
108134
  displayName: "Claude Opus",
108052
- resolve: "opencode/claude-opus-4-6",
108135
+ resolve: "opencode/claude-opus-4-7",
108053
108136
  openRouterResolve: "openrouter/anthropic/claude-opus-4.6"
108054
108137
  },
108055
108138
  "claude-sonnet": {
@@ -108327,22 +108410,35 @@ async function retry(fn2, options = {}) {
108327
108410
  }
108328
108411
 
108329
108412
  // utils/patchWorkflowRunFields.ts
108330
- var ARTIFACT_PATCH_KEYS = [
108413
+ var STRING_KEYS = [
108331
108414
  "prNodeId",
108332
108415
  "issueNodeId",
108333
108416
  "reviewNodeId",
108334
108417
  "planCommentNodeId",
108335
108418
  "summaryCommentNodeId"
108336
108419
  ];
108420
+ var NUMBER_KEYS = [
108421
+ "inputTokens",
108422
+ "outputTokens",
108423
+ "cacheReadTokens",
108424
+ "cacheWriteTokens",
108425
+ "costUsd"
108426
+ ];
108337
108427
  async function patchWorkflowRunFields(ctx, fields) {
108338
108428
  if (ctx.runId === void 0 || !ctx.apiToken) return;
108339
108429
  const body = {};
108340
- for (const key of ARTIFACT_PATCH_KEYS) {
108430
+ for (const key of STRING_KEYS) {
108341
108431
  const value2 = fields[key];
108342
108432
  if (typeof value2 === "string" && value2.length > 0) {
108343
108433
  body[key] = value2;
108344
108434
  }
108345
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
+ }
108346
108442
  if (Object.keys(body).length === 0) return;
108347
108443
  try {
108348
108444
  await retry(
@@ -108369,6 +108465,38 @@ async function patchWorkflowRunFields(ctx, fields) {
108369
108465
  log.warning(`patchWorkflowRunFields exhausted retries: ${error49}`);
108370
108466
  }
108371
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
+ }
108372
108500
 
108373
108501
  // node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
108374
108502
  var LIST_ITEM_MARKER = "-";
@@ -108725,6 +108853,126 @@ function resolveOptions(options) {
108725
108853
  };
108726
108854
  }
108727
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
+
108728
108976
  // mcp/shared.ts
108729
108977
  var tool = (toolDef) => toolDef;
108730
108978
  var handleToolSuccess = (data) => {
@@ -108760,15 +109008,21 @@ var execute = (fn2, toolName) => {
108760
109008
  };
108761
109009
  return _fn;
108762
109010
  };
108763
- var addTools = (_ctx, server, tools) => {
109011
+ var addTools = (ctx, server, tools) => {
109012
+ const shouldSanitize = isGeminiRouted(ctx);
108764
109013
  for (const tool2 of tools) {
108765
- server.addTool(tool2);
109014
+ server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
108766
109015
  }
108767
109016
  return server;
108768
109017
  };
108769
109018
 
108770
109019
  // mcp/comment.ts
108771
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
+ }
108772
109026
  function buildCommentFooter(ctx, customParts) {
108773
109027
  const runId = ctx.runId;
108774
109028
  return buildPullfrogFooter({
@@ -109012,8 +109266,9 @@ function ReportProgressTool(ctx) {
109012
109266
  if (!params.target_plan_comment && ctx.toolState.todoTracker) {
109013
109267
  ctx.toolState.todoTracker.cancel();
109014
109268
  await ctx.toolState.todoTracker.settled();
109015
- ctx.toolState.todoTracker.completeInProgress();
109016
- const collapsible = ctx.toolState.todoTracker.renderCollapsible();
109269
+ const collapsible = ctx.toolState.todoTracker.renderCollapsible({
109270
+ completeInProgress: true
109271
+ });
109017
109272
  if (collapsible) {
109018
109273
  body = `${body}
109019
109274
 
@@ -109400,8 +109655,27 @@ import { performance as performance3 } from "node:perf_hooks";
109400
109655
 
109401
109656
  // utils/activity.ts
109402
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
+ }
109403
109661
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
109404
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
+ }
109405
109679
  var _lastActivity = performance2.now();
109406
109680
  function markActivity() {
109407
109681
  _lastActivity = performance2.now();
@@ -109411,7 +109685,9 @@ function getIdleMs() {
109411
109685
  }
109412
109686
  function wrapWrite(original, onActivity) {
109413
109687
  const wrapped = (chunk, encodingOrCb, cb) => {
109414
- onActivity();
109688
+ if (!isActivityNoise(chunk)) {
109689
+ onActivity();
109690
+ }
109415
109691
  if (typeof encodingOrCb === "function") {
109416
109692
  return original(chunk, encodingOrCb);
109417
109693
  }
@@ -109425,10 +109701,15 @@ function startProcessOutputMonitor(ctx) {
109425
109701
  const originalStderrWrite = process.stderr.write.bind(process.stderr);
109426
109702
  process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
109427
109703
  process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
109428
- 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`);
109429
109710
  const intervalId = setInterval(() => {
109430
109711
  const idleMs = getIdleMs();
109431
- log.debug(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
109712
+ debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
109432
109713
  if (timedOut || idleMs <= ctx.timeoutMs) return;
109433
109714
  timedOut = true;
109434
109715
  ctx.onTimeout(idleMs);
@@ -109456,12 +109737,26 @@ function createProcessOutputActivityTimeout(ctx) {
109456
109737
  if (monitor) {
109457
109738
  monitor.stop();
109458
109739
  }
109459
- 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`));
109460
109743
  }
109461
109744
  });
109462
109745
  return {
109463
109746
  promise: promise2,
109464
- 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
+ }
109465
109760
  };
109466
109761
  }
109467
109762
 
@@ -109491,6 +109786,16 @@ function exitWithSignal(signal) {
109491
109786
  }
109492
109787
 
109493
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
+ };
109494
109799
  var activeChildren = /* @__PURE__ */ new Map();
109495
109800
  var externalSignalHandler = null;
109496
109801
  function trackChild(options) {
@@ -109536,7 +109841,7 @@ async function spawn(options) {
109536
109841
  const startTime = performance3.now();
109537
109842
  let stdoutBuffer = "";
109538
109843
  let stderrBuffer = "";
109539
- return new Promise((resolve2, reject) => {
109844
+ return new Promise((resolve3, reject) => {
109540
109845
  const child = nodeSpawn(options.cmd, options.args, {
109541
109846
  env: options.env || {
109542
109847
  PATH: process.env.PATH || "",
@@ -109547,15 +109852,17 @@ async function spawn(options) {
109547
109852
  });
109548
109853
  trackChild({ child });
109549
109854
  let timeoutId;
109855
+ let sigkillEscalatorId;
109550
109856
  let activityCheckIntervalId;
109551
109857
  let isTimedOut = false;
109552
109858
  let isActivityTimedOut = false;
109553
109859
  let lastActivityTime = performance3.now();
109860
+ let killedAtIdleMs;
109554
109861
  if (options.timeout) {
109555
109862
  timeoutId = setTimeout(() => {
109556
109863
  isTimedOut = true;
109557
109864
  child.kill("SIGTERM");
109558
- setTimeout(() => {
109865
+ sigkillEscalatorId = setTimeout(() => {
109559
109866
  if (!child.killed) {
109560
109867
  child.kill("SIGKILL");
109561
109868
  }
@@ -109573,12 +109880,20 @@ async function spawn(options) {
109573
109880
  );
109574
109881
  if (idleMs > activityTimeoutMs) {
109575
109882
  isActivityTimedOut = true;
109883
+ killedAtIdleMs = idleMs;
109576
109884
  const idleSec = Math.round(idleMs / 1e3);
109577
109885
  log.info(
109578
109886
  `no output for ${idleSec}s from pid=${child.pid} (${options.cmd}), killing process`
109579
109887
  );
109580
109888
  child.kill("SIGKILL");
109581
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
+ }
109582
109897
  }
109583
109898
  }, DEFAULT_ACTIVITY_CHECK_INTERVAL_MS);
109584
109899
  }
@@ -109600,24 +109915,41 @@ async function spawn(options) {
109600
109915
  options.onStderr?.(chunk);
109601
109916
  });
109602
109917
  }
109603
- child.on("close", (exitCode) => {
109918
+ child.on("close", (exitCode, signal) => {
109604
109919
  const durationMs = performance3.now() - startTime;
109605
109920
  untrackChild(child);
109606
109921
  if (timeoutId) clearTimeout(timeoutId);
109922
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
109607
109923
  if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
109608
109924
  if (isTimedOut) {
109609
- 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
+ );
109610
109928
  return;
109611
109929
  }
109612
109930
  if (isActivityTimedOut) {
109613
- const idleSec = Math.round((performance3.now() - lastActivityTime) / 1e3);
109614
- 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
+ );
109615
109939
  return;
109616
109940
  }
109617
- 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({
109618
109950
  stdout: stdoutBuffer,
109619
- stderr: stderrBuffer,
109620
- exitCode: exitCode || 0,
109951
+ stderr: resolvedStderr,
109952
+ exitCode: resolvedExitCode,
109621
109953
  durationMs
109622
109954
  });
109623
109955
  });
@@ -109625,9 +109957,13 @@ async function spawn(options) {
109625
109957
  const durationMs = performance3.now() - startTime;
109626
109958
  untrackChild(child);
109627
109959
  if (timeoutId) clearTimeout(timeoutId);
109960
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
109628
109961
  if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
109629
- console.error(`[spawn] process spawn error: ${error49.message}`);
109630
- resolve2({
109962
+ const errMsg = `[spawn] ${options.cmd}: ${error49.message}`;
109963
+ console.error(errMsg);
109964
+ stderrBuffer = stderrBuffer ? `${stderrBuffer}
109965
+ ${errMsg}` : errMsg;
109966
+ resolve3({
109631
109967
  stdout: stdoutBuffer,
109632
109968
  stderr: stderrBuffer,
109633
109969
  exitCode: 1,
@@ -114394,7 +114730,7 @@ var Protocol = class {
114394
114730
  return;
114395
114731
  }
114396
114732
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
114397
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
114733
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
114398
114734
  options?.signal?.throwIfAborted();
114399
114735
  }
114400
114736
  } catch (error49) {
@@ -114411,7 +114747,7 @@ var Protocol = class {
114411
114747
  */
114412
114748
  request(request2, resultSchema, options) {
114413
114749
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
114414
- return new Promise((resolve2, reject) => {
114750
+ return new Promise((resolve3, reject) => {
114415
114751
  const earlyReject = (error49) => {
114416
114752
  reject(error49);
114417
114753
  };
@@ -114489,7 +114825,7 @@ var Protocol = class {
114489
114825
  if (!parseResult.success) {
114490
114826
  reject(parseResult.error);
114491
114827
  } else {
114492
- resolve2(parseResult.data);
114828
+ resolve3(parseResult.data);
114493
114829
  }
114494
114830
  } catch (error49) {
114495
114831
  reject(error49);
@@ -114750,12 +115086,12 @@ var Protocol = class {
114750
115086
  }
114751
115087
  } catch {
114752
115088
  }
114753
- return new Promise((resolve2, reject) => {
115089
+ return new Promise((resolve3, reject) => {
114754
115090
  if (signal.aborted) {
114755
115091
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
114756
115092
  return;
114757
115093
  }
114758
- const timeoutId = setTimeout(resolve2, interval);
115094
+ const timeoutId = setTimeout(resolve3, interval);
114759
115095
  signal.addEventListener("abort", () => {
114760
115096
  clearTimeout(timeoutId);
114761
115097
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -115625,12 +115961,12 @@ var StdioServerTransport = class {
115625
115961
  this.onclose?.();
115626
115962
  }
115627
115963
  send(message) {
115628
- return new Promise((resolve2) => {
115964
+ return new Promise((resolve3) => {
115629
115965
  const json4 = serializeMessage(message);
115630
115966
  if (this._stdout.write(json4)) {
115631
- resolve2();
115967
+ resolve3();
115632
115968
  } else {
115633
- this._stdout.once("drain", resolve2);
115969
+ this._stdout.once("drain", resolve3);
115634
115970
  }
115635
115971
  });
115636
115972
  }
@@ -136658,12 +136994,12 @@ var require_schemes2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136658
136994
  var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136659
136995
  const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils5();
136660
136996
  const { SCHEMES, getSchemeHandler } = require_schemes2();
136661
- function normalize2(uri$2, options) {
136997
+ function normalize3(uri$2, options) {
136662
136998
  if (typeof uri$2 === "string") uri$2 = serialize(parse5(uri$2, options), options);
136663
136999
  else if (typeof uri$2 === "object") uri$2 = parse5(serialize(uri$2, options), options);
136664
137000
  return uri$2;
136665
137001
  }
136666
- function resolve2(baseURI, relativeURI, options) {
137002
+ function resolve3(baseURI, relativeURI, options) {
136667
137003
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
136668
137004
  const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
136669
137005
  schemelessOptions.skipEscape = true;
@@ -136836,8 +137172,8 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
136836
137172
  }
136837
137173
  const fastUri = {
136838
137174
  SCHEMES,
136839
- normalize: normalize2,
136840
- resolve: resolve2,
137175
+ normalize: normalize3,
137176
+ resolve: resolve3,
136841
137177
  resolveComponent,
136842
137178
  equal: equal$1,
136843
137179
  serialize,
@@ -140425,7 +140761,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140425
140761
  new Error(`Connection is in ${this.#connectionState} state`)
140426
140762
  );
140427
140763
  }
140428
- return new Promise((resolve2, reject) => {
140764
+ return new Promise((resolve3, reject) => {
140429
140765
  const timeout = setTimeout(() => {
140430
140766
  reject(
140431
140767
  new Error(
@@ -140435,7 +140771,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140435
140771
  }, 5e3);
140436
140772
  this.once("ready", () => {
140437
140773
  clearTimeout(timeout);
140438
- resolve2();
140774
+ resolve3();
140439
140775
  });
140440
140776
  this.once("error", (event) => {
140441
140777
  clearTimeout(timeout);
@@ -140882,7 +141218,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140882
141218
  }
140883
141219
  });
140884
141220
  if (this.#needsEventLoopFlush) {
140885
- await new Promise((resolve2) => setImmediate(resolve2));
141221
+ await new Promise((resolve3) => setImmediate(resolve3));
140886
141222
  }
140887
141223
  } catch (progressError) {
140888
141224
  this.#logger.warn(
@@ -140940,7 +141276,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
140940
141276
  }
140941
141277
  });
140942
141278
  if (this.#needsEventLoopFlush) {
140943
- await new Promise((resolve2) => setImmediate(resolve2));
141279
+ await new Promise((resolve3) => setImmediate(resolve3));
140944
141280
  }
140945
141281
  } catch (streamError) {
140946
141282
  this.#logger.warn(
@@ -141945,7 +142281,7 @@ function formatMcpToolRef(agentId, toolName) {
141945
142281
  switch (agentId) {
141946
142282
  case "claude":
141947
142283
  return `mcp__${pullfrogMcpName}__${toolName}`;
141948
- case "opentoad":
142284
+ case "opencode":
141949
142285
  return `${pullfrogMcpName}_${toolName}`;
141950
142286
  default:
141951
142287
  return agentId;
@@ -141953,7 +142289,7 @@ function formatMcpToolRef(agentId, toolName) {
141953
142289
  }
141954
142290
 
141955
142291
  // utils/browser.ts
141956
- import { execFileSync, spawnSync } from "node:child_process";
142292
+ import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
141957
142293
  import { existsSync as existsSync4 } from "node:fs";
141958
142294
  import { dirname } from "node:path";
141959
142295
 
@@ -141968,12 +142304,77 @@ var SENSITIVE_PATTERNS = [
141968
142304
  function isSensitiveEnvName(key) {
141969
142305
  return SENSITIVE_PATTERNS.some((p2) => p2.test(key));
141970
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
+ }
141971
142369
  function filterEnv() {
141972
142370
  const filtered = {};
141973
142371
  for (const [key, value2] of Object.entries(process.env)) {
141974
142372
  if (value2 === void 0) continue;
141975
- if (isSensitiveEnvName(key)) continue;
141976
- 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
+ }
141977
142378
  }
141978
142379
  return filtered;
141979
142380
  }
@@ -141993,7 +142394,7 @@ var import_semver = __toESM(require_semver2(), 1);
141993
142394
  // package.json
141994
142395
  var package_default = {
141995
142396
  name: "pullfrog",
141996
- version: "0.0.200",
142397
+ version: "0.0.202",
141997
142398
  type: "module",
141998
142399
  bin: {
141999
142400
  pullfrog: "dist/cli.mjs",
@@ -142005,6 +142406,7 @@ var package_default = {
142005
142406
  ],
142006
142407
  scripts: {
142007
142408
  test: "vitest",
142409
+ "test:catalog": "vitest run --config vitest.main.config.ts",
142008
142410
  typecheck: "tsc --noEmit",
142009
142411
  build: "node esbuild.config.js && tsc -p tsconfig.exports.json",
142010
142412
  "check:entrypoints": "node scripts/check-entrypoint-imports.ts",
@@ -142017,7 +142419,7 @@ var package_default = {
142017
142419
  },
142018
142420
  devDependencies: {
142019
142421
  "@actions/core": "^1.11.1",
142020
- "@anthropic-ai/claude-code": "2.1.85",
142422
+ "@anthropic-ai/claude-code": "2.1.112",
142021
142423
  "@ark/fs": "0.56.0",
142022
142424
  "@ark/util": "0.56.0",
142023
142425
  "@clack/prompts": "^1.2.0",
@@ -142141,7 +142543,7 @@ function ensureBrowserDaemon(toolState) {
142141
142543
  log.info("agent-browser installed");
142142
142544
  let binDir;
142143
142545
  try {
142144
- const binPath = execFileSync("which", ["agent-browser"], { encoding: "utf-8" }).trim();
142546
+ const binPath = execFileSync2("which", ["agent-browser"], { encoding: "utf-8" }).trim();
142145
142547
  binDir = dirname(binPath);
142146
142548
  log.info(`agent-browser binary: ${binPath}`);
142147
142549
  } catch {
@@ -142188,9 +142590,271 @@ function closeBrowserDaemon(toolState) {
142188
142590
  }
142189
142591
 
142190
142592
  // mcp/checkout.ts
142593
+ import { createHash as createHash2 } from "node:crypto";
142191
142594
  import { writeFileSync } from "node:fs";
142192
142595
  import { join as join3 } from "node:path";
142193
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
+
142194
142858
  // utils/gitAuth.ts
142195
142859
  import { execSync } from "node:child_process";
142196
142860
  import { createHash } from "node:crypto";
@@ -142204,7 +142868,7 @@ function resolveGit() {
142204
142868
  const resolvedPath = realpathSync(whichPath);
142205
142869
  const sha256 = hashFile(resolvedPath);
142206
142870
  gitBinary = { path: resolvedPath, sha256 };
142207
- log.info(`git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
142871
+ log.debug(`\xBB git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
142208
142872
  }
142209
142873
  function verifyGitBinary() {
142210
142874
  if (!gitBinary) {
@@ -142283,29 +142947,43 @@ async function $git(subcommand, args2, options) {
142283
142947
  }
142284
142948
 
142285
142949
  // lifecycle.ts
142286
- var LIFECYCLE_HOOK_TIMEOUT_MS = 12e4;
142950
+ var LIFECYCLE_HOOK_TIMEOUT_MS = 6e5;
142287
142951
 
142288
142952
  // utils/lifecycle.ts
142289
142953
  async function executeLifecycleHook(params) {
142290
- if (!params.script) return;
142954
+ if (!params.script) return {};
142291
142955
  log.info(`\xBB executing ${params.event} lifecycle hook...`);
142292
- const result = await spawn({
142293
- cmd: "bash",
142294
- args: ["-c", params.script],
142295
- env: process.env,
142296
- timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
142297
- activityTimeout: 0,
142298
- onStdout: (chunk) => process.stdout.write(chunk),
142299
- onStderr: (chunk) => process.stderr.write(chunk)
142300
- });
142301
- if (result.exitCode !== 0) {
142302
- const output = result.stderr || result.stdout;
142303
- throw new Error(
142304
- `lifecycle hook '${params.event}' failed with exit code ${result.exitCode}:
142305
- ${output}`
142306
- );
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
+ };
142307
142986
  }
142308
- log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
142309
142987
  }
142310
142988
 
142311
142989
  // utils/shell.ts
@@ -142455,6 +143133,787 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
142455
143133
  return hasChanges ? out : null;
142456
143134
  }
142457
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
+
142458
143917
  // mcp/checkout.ts
142459
143918
  function formatFilesWithLineNumbers(files) {
142460
143919
  const output = [];
@@ -142521,7 +143980,10 @@ function formatFilesWithLineNumbers(files) {
142521
143980
  }
142522
143981
  const tocLines = [`## Files (${files.length})`];
142523
143982
  for (const entry of tocEntries) {
142524
- 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
+ );
142525
143987
  }
142526
143988
  tocLines.push("");
142527
143989
  tocLines.push("---");
@@ -142543,7 +144005,7 @@ async function fetchAndFormatPrDiff(ctx, pullNumber) {
142543
144005
  pull_number: pullNumber,
142544
144006
  per_page: 100
142545
144007
  });
142546
- return formatFilesWithLineNumbers(files);
144008
+ return { ...formatFilesWithLineNumbers(files), files };
142547
144009
  }
142548
144010
  async function createTempBranch(params) {
142549
144011
  const response = await params.octokit.rest.git.createRef({
@@ -142610,6 +144072,8 @@ async function ensureBeforeShaReachable(params) {
142610
144072
  async function checkoutPrBranch(pr, params) {
142611
144073
  const { octokit, owner, name, gitToken, toolState, beforeSha } = params;
142612
144074
  log.info(`\xBB checking out PR #${pr.number}...`);
144075
+ rejectIfLeadingDash(pr.baseRef, "PR base ref");
144076
+ rejectIfLeadingDash(pr.headRef, "PR head ref");
142613
144077
  const isFork = pr.headRepoFullName !== pr.baseRepoFullName;
142614
144078
  const localBranch = `pr-${pr.number}`;
142615
144079
  const isShallow = $("git", ["rev-parse", "--is-shallow-repository"], { log: false }).trim() === "true";
@@ -142620,7 +144084,7 @@ async function checkoutPrBranch(pr, params) {
142620
144084
  if (!alreadyOnBranch) {
142621
144085
  $("git", ["checkout", "-B", pr.baseRef, `origin/${pr.baseRef}`], { log: false });
142622
144086
  log.debug(`\xBB fetching PR #${pr.number} (${localBranch})...`);
142623
- await $git("fetch", ["--no-tags", "origin", `pull/${pr.number}/head:${localBranch}`], {
144087
+ await $git("fetch", ["--no-tags", "origin", `+pull/${pr.number}/head:${localBranch}`], {
142624
144088
  token: gitToken
142625
144089
  });
142626
144090
  $("git", ["checkout", localBranch], { log: false });
@@ -142703,10 +144167,11 @@ async function checkoutPrBranch(pr, params) {
142703
144167
  remoteBranch: pr.headRef,
142704
144168
  localBranch
142705
144169
  };
142706
- await executeLifecycleHook({
144170
+ const postCheckoutHook = await executeLifecycleHook({
142707
144171
  event: "post-checkout",
142708
144172
  script: params.postCheckoutScript
142709
144173
  });
144174
+ return { hookWarning: postCheckoutHook.warning };
142710
144175
  }
142711
144176
  function CheckoutPrTool(ctx) {
142712
144177
  return tool({
@@ -142732,7 +144197,7 @@ function CheckoutPrTool(ctx) {
142732
144197
  baseRepoFullName: prResponse.data.base.repo.full_name,
142733
144198
  maintainerCanModify: prResponse.data.maintainer_can_modify
142734
144199
  };
142735
- await checkoutPrBranch(pr, {
144200
+ const checkoutResult = await checkoutPrBranch(pr, {
142736
144201
  octokit: ctx.octokit,
142737
144202
  owner: ctx.repo.owner,
142738
144203
  name: ctx.repo.name,
@@ -142775,11 +144240,49 @@ ${diffPreview}`);
142775
144240
  const diffPath = join3(tempDir, `pr-${pull_number}-${headShort}.diff`);
142776
144241
  writeFileSync(diffPath, formatResult.content);
142777
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;
142778
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.` : "";
142779
144281
  return {
142780
144282
  success: true,
142781
144283
  number: prResponse.data.number,
142782
144284
  title: prResponse.data.title,
144285
+ body: prResponse.data.body,
142783
144286
  base: pr.baseRef,
142784
144287
  localBranch: `pr-${pull_number}`,
142785
144288
  remoteBranch: `refs/heads/${pr.headRef}`,
@@ -142790,7 +144293,12 @@ ${diffPreview}`);
142790
144293
  diffPath,
142791
144294
  incrementalDiffPath,
142792
144295
  toc: formatResult.toc,
142793
- 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
142794
144302
  };
142795
144303
  })
142796
144304
  });
@@ -143018,244 +144526,6 @@ function CommitInfoTool(ctx) {
143018
144526
  });
143019
144527
  }
143020
144528
 
143021
- // mcp/git.ts
143022
- function getPushDestination(branch, storedDest) {
143023
- if (storedDest && storedDest.localBranch === branch) {
143024
- log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
143025
- const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
143026
- log: false
143027
- }).trim();
143028
- return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
143029
- }
143030
- try {
143031
- const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
143032
- const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
143033
- const remoteBranch = merge4.replace(/^refs\/heads\//, "");
143034
- const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
143035
- return { remoteName: pushRemote, remoteBranch, url: url4 };
143036
- } catch {
143037
- log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
143038
- const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
143039
- return { remoteName: "origin", remoteBranch: branch, url: url4 };
143040
- }
143041
- }
143042
- function normalizeUrl(url4) {
143043
- return url4.replace(/\.git$/, "").toLowerCase();
143044
- }
143045
- function validatePushDestination(ctx, branch) {
143046
- const pushUrl = ctx.toolState.pushUrl;
143047
- if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
143048
- const dest = getPushDestination(branch, ctx.toolState.pushDest);
143049
- if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
143050
- throw new Error(
143051
- `Push blocked: destination does not match expected repository.
143052
- Expected: ${pushUrl}
143053
- Actual: ${dest.url}
143054
- Git configuration may have been tampered with.`
143055
- );
143056
- }
143057
- return dest;
143058
- }
143059
- var PushBranch = type({
143060
- branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
143061
- force: type.boolean.describe("Force push (use with caution)").default(false)
143062
- });
143063
- function PushBranchTool(ctx) {
143064
- const defaultBranch = ctx.repo.data.default_branch || "main";
143065
- const pushPermission = ctx.payload.push;
143066
- return tool({
143067
- name: "push_branch",
143068
- 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.",
143069
- parameters: PushBranch,
143070
- execute: execute(async ({ branchName, force }) => {
143071
- if (pushPermission === "disabled") {
143072
- throw new Error("Push is disabled. This repository is configured for read-only access.");
143073
- }
143074
- const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
143075
- const status = $("git", ["status", "--porcelain"], { log: false });
143076
- if (status) {
143077
- throw new Error(
143078
- `push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
143079
-
143080
- git status:
143081
- ${status}`
143082
- );
143083
- }
143084
- const pushDest = validatePushDestination(ctx, branch);
143085
- if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
143086
- throw new Error(
143087
- `Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
143088
- );
143089
- }
143090
- const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
143091
- const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
143092
- await executeLifecycleHook({ event: "prepush", script: ctx.prepushScript });
143093
- log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
143094
- if (force) {
143095
- log.warning(`force pushing - this will overwrite remote history`);
143096
- }
143097
- try {
143098
- await $git("push", pushArgs, {
143099
- token: ctx.gitToken
143100
- });
143101
- } catch (err) {
143102
- const msg = err instanceof Error ? err.message : String(err);
143103
- if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
143104
- throw new Error(
143105
- `push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
143106
-
143107
- to resolve this:
143108
- 1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
143109
- 2. use the git tool to rebase your changes: git({ subcommand: "rebase", args: ["origin/${pushDest.remoteBranch}"] })
143110
- 3. resolve any merge conflicts if needed
143111
- 4. retry push_branch`
143112
- );
143113
- }
143114
- throw err;
143115
- }
143116
- return {
143117
- success: true,
143118
- branch,
143119
- remoteBranch: pushDest.remoteBranch,
143120
- remote: pushDest.remoteName,
143121
- force,
143122
- message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
143123
- };
143124
- })
143125
- });
143126
- }
143127
- var AUTH_REQUIRED_REDIRECT = {
143128
- push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
143129
- fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
143130
- pull: "use git_fetch to fetch the remote ref, then use this git tool with subcommand 'merge' or 'rebase' locally.",
143131
- clone: "the repository is already cloned. use checkout_pr for PR branches."
143132
- };
143133
- var NOSHELL_BLOCKED_SUBCOMMANDS = {
143134
- config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
143135
- submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
143136
- "update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
143137
- "filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
143138
- replace: "Blocked: git replace can redirect object lookups.",
143139
- // subcommands that accept --exec or similar flags for arbitrary code execution
143140
- rebase: "Blocked: git rebase --exec can execute arbitrary shell commands.",
143141
- bisect: "Blocked: git bisect run can execute arbitrary shell commands."
143142
- };
143143
- var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
143144
- var COLLAPSE_THRESHOLD = 200;
143145
- var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
143146
- var Git = type({
143147
- subcommand: type(subcommandPattern).describe("Git subcommand (e.g., 'status', 'log', 'diff')"),
143148
- args: type.string.array().describe("Additional arguments for the git command").optional()
143149
- });
143150
- function GitTool(ctx) {
143151
- return tool({
143152
- name: "git",
143153
- description: "Run git commands. For push/fetch/pull, use the dedicated MCP tools instead (push_branch, git_fetch).",
143154
- parameters: Git,
143155
- execute: execute(async (params) => {
143156
- const subcommand = params.subcommand;
143157
- const args2 = params.args ?? [];
143158
- const redirect = AUTH_REQUIRED_REDIRECT[subcommand];
143159
- if (redirect) {
143160
- throw new Error(`git ${subcommand} is not available through this tool \u2014 ${redirect}`);
143161
- }
143162
- if (ctx.payload.shell === "disabled") {
143163
- const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[subcommand];
143164
- if (blocked) {
143165
- throw new Error(blocked);
143166
- }
143167
- for (const arg4 of args2) {
143168
- const isBlocked = NOSHELL_BLOCKED_ARGS.some(
143169
- (flag) => arg4 === flag || arg4.startsWith(flag + "=")
143170
- );
143171
- if (isBlocked) {
143172
- throw new Error(
143173
- `Blocked: '${arg4}' flag can execute arbitrary code and is not allowed.`
143174
- );
143175
- }
143176
- }
143177
- }
143178
- const output = $("git", [subcommand, ...args2], { log: false });
143179
- const lineCount = output.split("\n").length;
143180
- if (lineCount > COLLAPSE_THRESHOLD) {
143181
- log.group(`git ${subcommand} output (${lineCount} lines)`, () => {
143182
- log.info(output);
143183
- });
143184
- } else if (output) {
143185
- log.info(output);
143186
- }
143187
- return { success: true, output };
143188
- })
143189
- });
143190
- }
143191
- var GitFetch = type({
143192
- ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
143193
- depth: type.number.describe("Fetch depth (for shallow clones)").optional()
143194
- });
143195
- function GitFetchTool(ctx) {
143196
- return tool({
143197
- name: "git_fetch",
143198
- description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
143199
- parameters: GitFetch,
143200
- execute: execute(async (params) => {
143201
- const fetchArgs = ["--no-tags", "origin", params.ref];
143202
- if (params.depth !== void 0) {
143203
- fetchArgs.push(`--depth=${params.depth}`);
143204
- }
143205
- await $git("fetch", fetchArgs, {
143206
- token: ctx.gitToken
143207
- });
143208
- return { success: true, ref: params.ref };
143209
- })
143210
- });
143211
- }
143212
- var DeleteBranch = type({
143213
- branchName: type.string.describe("Remote branch to delete")
143214
- });
143215
- function DeleteBranchTool(ctx) {
143216
- const pushPermission = ctx.payload.push;
143217
- return tool({
143218
- name: "delete_branch",
143219
- description: "Delete a remote branch. Requires push: enabled permission.",
143220
- parameters: DeleteBranch,
143221
- execute: execute(async (params) => {
143222
- if (pushPermission !== "enabled") {
143223
- throw new Error(
143224
- "Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
143225
- );
143226
- }
143227
- await $git("push", ["origin", "--delete", params.branchName], {
143228
- token: ctx.gitToken
143229
- });
143230
- return { success: true, deleted: params.branchName };
143231
- })
143232
- });
143233
- }
143234
- var PushTags = type({
143235
- tag: type.string.describe("Tag name to push"),
143236
- force: type.boolean.describe("Force push the tag").default(false)
143237
- });
143238
- function PushTagsTool(ctx) {
143239
- const pushPermission = ctx.payload.push;
143240
- return tool({
143241
- name: "push_tags",
143242
- description: "Push a tag to remote. Requires push: enabled permission.",
143243
- parameters: PushTags,
143244
- execute: execute(async (params) => {
143245
- if (pushPermission !== "enabled") {
143246
- throw new Error(
143247
- "Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
143248
- );
143249
- }
143250
- const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
143251
- await $git("push", pushArgs, {
143252
- token: ctx.gitToken
143253
- });
143254
- return { success: true, tag: params.tag };
143255
- })
143256
- });
143257
- }
143258
-
143259
144529
  // mcp/issue.ts
143260
144530
  var Issue = type({
143261
144531
  title: type.string.describe("the title of the issue"),
@@ -143718,221 +144988,6 @@ function PullRequestInfoTool(ctx) {
143718
144988
  });
143719
144989
  }
143720
144990
 
143721
- // mcp/review.ts
143722
- function getHttpStatus(err) {
143723
- if (typeof err !== "object" || err === null) return void 0;
143724
- const status = err.status;
143725
- return typeof status === "number" ? status : void 0;
143726
- }
143727
- var CreatePullRequestReview = type({
143728
- pull_number: type.number.describe("The pull request number to review"),
143729
- body: type.string.describe(
143730
- "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."
143731
- ).optional(),
143732
- approved: type.boolean.describe(
143733
- "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."
143734
- ).optional(),
143735
- commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
143736
- comments: type({
143737
- path: type.string.describe(
143738
- "The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
143739
- ),
143740
- line: type.number.describe(
143741
- "Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
143742
- ),
143743
- side: type.enumerated("LEFT", "RIGHT").describe(
143744
- "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
143745
- ).optional(),
143746
- body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
143747
- suggestion: type.string.describe(
143748
- "Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
143749
- ).optional(),
143750
- start_line: type.number.describe(
143751
- "Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
143752
- ).optional()
143753
- }).array().describe(
143754
- "Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
143755
- ).optional()
143756
- });
143757
- function CreatePullRequestReviewTool(ctx) {
143758
- return tool({
143759
- name: "create_pull_request_review",
143760
- 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.`,
143761
- parameters: CreatePullRequestReview,
143762
- execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
143763
- if (body) body = fixDoubleEscapedString(body);
143764
- if (body && ctx.toolState.selectedMode === "Review" && ctx.toolState.todoTracker) {
143765
- ctx.toolState.todoTracker.cancel();
143766
- await ctx.toolState.todoTracker.settled();
143767
- ctx.toolState.todoTracker.completeInProgress();
143768
- const collapsible = ctx.toolState.todoTracker.renderCollapsible();
143769
- if (collapsible) {
143770
- body = `${body}
143771
-
143772
- ${collapsible}`;
143773
- }
143774
- }
143775
- ctx.toolState.issueNumber = pull_number;
143776
- if (!approved && !body && comments.length === 0) {
143777
- log.info(
143778
- "review has no body and no inline comments \u2014 skipping submission (no issues found)"
143779
- );
143780
- return {
143781
- success: true,
143782
- skipped: true,
143783
- reason: "no issues found \u2014 nothing to post"
143784
- };
143785
- }
143786
- let event = approved ? "APPROVE" : "COMMENT";
143787
- if (event === "APPROVE" && !ctx.prApproveEnabled) {
143788
- log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
143789
- event = "COMMENT";
143790
- }
143791
- const params = {
143792
- owner: ctx.repo.owner,
143793
- repo: ctx.repo.name,
143794
- pull_number,
143795
- event
143796
- };
143797
- let latestHeadSha;
143798
- if (commit_id) {
143799
- params.commit_id = commit_id;
143800
- } else {
143801
- const pr = await ctx.octokit.rest.pulls.get({
143802
- owner: ctx.repo.owner,
143803
- repo: ctx.repo.name,
143804
- pull_number
143805
- });
143806
- latestHeadSha = pr.data.head.sha;
143807
- params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
143808
- if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143809
- log.info(
143810
- `anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
143811
- );
143812
- }
143813
- }
143814
- const reviewComments = comments.map((comment) => {
143815
- let commentBody = fixDoubleEscapedString(comment.body || "");
143816
- if (comment.suggestion !== void 0) {
143817
- const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
143818
- commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
143819
- }
143820
- const side = comment.side || "RIGHT";
143821
- const reviewComment = {
143822
- path: comment.path,
143823
- line: comment.line,
143824
- body: commentBody,
143825
- side
143826
- };
143827
- if (comment.start_line != null && comment.start_line !== comment.line) {
143828
- reviewComment.start_line = comment.start_line;
143829
- reviewComment.start_side = side;
143830
- }
143831
- return reviewComment;
143832
- });
143833
- if (reviewComments.length > 0) {
143834
- params.comments = reviewComments;
143835
- }
143836
- let result;
143837
- try {
143838
- result = body ? await createAndSubmitWithFooter(ctx, params, {
143839
- body,
143840
- approved: approved ?? false,
143841
- hasComments: reviewComments.length > 0
143842
- }) : await ctx.octokit.rest.pulls.createReview(params);
143843
- } catch (err) {
143844
- if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
143845
- const details = params.comments.map((c2) => {
143846
- const line = c2.line ?? 0;
143847
- const startLine = c2.start_line ?? line;
143848
- const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
143849
- return `${c2.path}:${range2} (${c2.side ?? "RIGHT"})`;
143850
- });
143851
- throw new Error(
143852
- `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(", ")}`
143853
- );
143854
- }
143855
- log.debug(`createReview response: ${JSON.stringify(result.data)}`);
143856
- if (!result.data.id) {
143857
- throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
143858
- }
143859
- const reviewId = result.data.id;
143860
- const reviewNodeId = result.data.node_id;
143861
- const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
143862
- ctx.toolState.review = {
143863
- id: reviewId,
143864
- nodeId: reviewNodeId,
143865
- reviewedSha: actuallyReviewedSha
143866
- };
143867
- if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
143868
- const fromSha = ctx.toolState.checkoutSha;
143869
- const toSha = latestHeadSha;
143870
- ctx.toolState.beforeSha = fromSha;
143871
- ctx.toolState.checkoutSha = toSha;
143872
- log.info(
143873
- `new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
143874
- );
143875
- return {
143876
- success: true,
143877
- reviewId,
143878
- html_url: result.data.html_url,
143879
- state: result.data.state,
143880
- user: result.data.user?.login,
143881
- submitted_at: result.data.submitted_at,
143882
- newCommits: {
143883
- from: fromSha,
143884
- to: toSha,
143885
- 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.`
143886
- }
143887
- };
143888
- }
143889
- return {
143890
- success: true,
143891
- reviewId,
143892
- html_url: result.data.html_url,
143893
- state: result.data.state,
143894
- user: result.data.user?.login,
143895
- submitted_at: result.data.submitted_at
143896
- };
143897
- })
143898
- });
143899
- }
143900
- async function createAndSubmitWithFooter(ctx, params, opts) {
143901
- const { event: _2, ...pendingParams } = params;
143902
- const pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
143903
- if (!pending.data.id) {
143904
- throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
143905
- }
143906
- const customParts = [];
143907
- if (!opts.approved) {
143908
- const apiUrl = getApiUrl();
143909
- if (opts.hasComments) {
143910
- const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143911
- const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
143912
- customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
143913
- } else {
143914
- const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
143915
- customParts.push(`[Fix it \u2794](${fixUrl})`);
143916
- }
143917
- }
143918
- const footer = buildPullfrogFooter({
143919
- workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
143920
- customParts,
143921
- model: ctx.toolState.model
143922
- });
143923
- return ctx.octokit.rest.pulls.submitReview({
143924
- owner: params.owner,
143925
- repo: params.repo,
143926
- pull_number: params.pull_number,
143927
- review_id: pending.data.id,
143928
- event: params.event,
143929
- body: opts.body + footer
143930
- });
143931
- }
143932
- async function reportReviewNodeId(ctx, params) {
143933
- await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
143934
- }
143935
-
143936
144991
  // mcp/reviewComments.ts
143937
144992
  import { writeFileSync as writeFileSync4 } from "node:fs";
143938
144993
  import { join as join6 } from "node:path";
@@ -143980,7 +145035,7 @@ query ($owner: String!, $name: String!, $prNumber: Int!) {
143980
145035
  }
143981
145036
  }
143982
145037
  `;
143983
- function countLines(str) {
145038
+ function countLines2(str) {
143984
145039
  let count = 1;
143985
145040
  let index = -1;
143986
145041
  while ((index = str.indexOf("\n", index + 1)) !== -1) {
@@ -144122,13 +145177,13 @@ function formatReviewThreads(threadBlocks, header) {
144122
145177
  const reviewBodyLines = [];
144123
145178
  if (header.reviewBody) {
144124
145179
  reviewBodyLines.push("## Review Body", "", header.reviewBody, "");
144125
- currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines(line), 0);
145180
+ currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines2(line), 0);
144126
145181
  }
144127
145182
  const tocEntries = [];
144128
145183
  const threadLines = [];
144129
145184
  for (const block of threadBlocks) {
144130
145185
  const startLine = currentLine;
144131
- const actualLineCount = block.content.reduce((sum, line) => sum + countLines(line), 0);
145186
+ const actualLineCount = block.content.reduce((sum, line) => sum + countLines2(line), 0);
144132
145187
  const endLine = currentLine + actualLineCount - 1;
144133
145188
  tocEntries.push(`- ${block.path}:${block.lineRange} \u2192 lines ${startLine}-${endLine}`);
144134
145189
  threadLines.push(...block.content);
@@ -144451,7 +145506,7 @@ GitHub's markdown parser requires a blank line between ALL block-level elements.
144451
145506
  Rules:
144452
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
144453
145508
  - ALL variable names, identifiers, and file names in body text must be in backticks
144454
- - 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.
144455
145510
  - Add <br/> before each ## heading for visual spacing. Do NOT use horizontal rules (---)
144456
145511
  - Do NOT include raw diff stats like '+123 / -45' or line counts
144457
145512
  - Do NOT include code blocks or repeat diff contents
@@ -144526,7 +145581,7 @@ ${learningsStep(t2, 6)}`
144526
145581
  description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
144527
145582
  prompt: `### Checklist
144528
145583
 
144529
- 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.
144530
145585
 
144531
145586
  2. For each area of change:
144532
145587
  - read the diff and trace data flow, check boundaries, and verify assumptions
@@ -144543,6 +145598,7 @@ ${learningsStep(t2, 6)}`
144543
145598
  4. Submit \u2014 ALWAYS submit exactly one review via \`${t2("create_pull_request_review")}\`.
144544
145599
  Do NOT call \`report_progress\` \u2014 the review is the final record and the progress
144545
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.
144546
145602
 
144547
145603
  - **critical issues** (blocks merge \u2014 bugs, security, data loss):
144548
145604
  \`approved: false\`. Body begins with a GitHub alert blockquote, e.g.:
@@ -144560,7 +145616,7 @@ ${learningsStep(t2, 6)}`
144560
145616
  description: "Re-review a PR after new commits are pushed; focus on new changes since the last review",
144561
145617
  prompt: `### Checklist
144562
145618
 
144563
- 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.
144564
145620
 
144565
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.
144566
145622
 
@@ -144584,6 +145640,7 @@ ${learningsStep(t2, 6)}`
144584
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.
144585
145641
 
144586
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.
144587
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.
144588
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).
144589
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).
@@ -144688,7 +145745,7 @@ ${PR_SUMMARY_FORMAT}`
144688
145745
  }
144689
145746
  ];
144690
145747
  }
144691
- var modes = computeModes("opentoad");
145748
+ var modes = computeModes("opencode");
144692
145749
 
144693
145750
  // mcp/selectMode.ts
144694
145751
  var SelectModeParams = type({
@@ -144891,13 +145948,19 @@ function detectSandboxMethod() {
144891
145948
  } catch {
144892
145949
  }
144893
145950
  detectedSandboxMethod = "none";
144894
- log.info("PID namespace isolation not available - falling back to env filtering only");
145951
+ log.info("PID namespace isolation not available");
144895
145952
  return "none";
144896
145953
  }
144897
145954
  var PROC_CLEANUP = "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
144898
145955
  function spawnShell(params) {
144899
145956
  const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
144900
145957
  const sandboxMethod = detectSandboxMethod();
145958
+ const ci = process.env.CI === "true";
145959
+ if (ci && sandboxMethod === "none") {
145960
+ throw new Error(
145961
+ "pid namespace isolation is required in CI but unavailable (both unshare and sudo unshare failed)"
145962
+ );
145963
+ }
144901
145964
  if (sandboxMethod === "unshare") {
144902
145965
  return spawn2(
144903
145966
  "unshare",
@@ -145045,11 +146108,11 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
145045
146108
  await killProcessGroup(proc);
145046
146109
  }
145047
146110
  }, timeout);
145048
- const exitCode = await new Promise((resolve2) => {
146111
+ const exitCode = await new Promise((resolve3) => {
145049
146112
  const done = (code) => {
145050
146113
  exited = true;
145051
146114
  clearTimeout(timeoutId);
145052
- resolve2(code);
146115
+ resolve3(code);
145053
146116
  };
145054
146117
  proc.on("exit", done);
145055
146118
  proc.on("error", () => done(null));
@@ -145188,12 +146251,12 @@ function readEnvPort() {
145188
146251
  return parsed2;
145189
146252
  }
145190
146253
  function isPortAvailable(port) {
145191
- return new Promise((resolve2) => {
146254
+ return new Promise((resolve3) => {
145192
146255
  const server = createServer();
145193
146256
  server.unref();
145194
- server.once("error", () => resolve2(false));
146257
+ server.once("error", () => resolve3(false));
145195
146258
  server.once("listening", () => {
145196
- server.close(() => resolve2(true));
146259
+ server.close(() => resolve3(true));
145197
146260
  });
145198
146261
  server.listen(port, mcpHost);
145199
146262
  });
@@ -145333,9 +146396,12 @@ async function killBackgroundProcesses(toolState) {
145333
146396
  async function startMcpHttpServer(ctx, options) {
145334
146397
  const tools = buildOrchestratorTools(ctx, options?.outputSchema);
145335
146398
  const startResult = await selectMcpPort(ctx, tools);
146399
+ let disposed = false;
145336
146400
  return {
145337
146401
  url: startResult.url,
145338
146402
  [Symbol.asyncDispose]: async () => {
146403
+ if (disposed) return;
146404
+ disposed = true;
145339
146405
  closeBrowserDaemon(ctx.toolState);
145340
146406
  await killBackgroundProcesses(ctx.toolState);
145341
146407
  await startResult.server.stop();
@@ -145530,39 +146596,6 @@ var ThinkingTimer = class {
145530
146596
  }
145531
146597
  };
145532
146598
 
145533
- // agents/shared.ts
145534
- import { execFileSync as execFileSync2 } from "node:child_process";
145535
- var MAX_STDERR_LINES = 20;
145536
- var MAX_COMMIT_RETRIES = 3;
145537
- function getGitStatus() {
145538
- try {
145539
- return execFileSync2("git", ["status", "--porcelain"], {
145540
- encoding: "utf-8",
145541
- timeout: 1e4
145542
- }).trim();
145543
- } catch {
145544
- return "";
145545
- }
145546
- }
145547
- function buildCommitPrompt(_agentId, status) {
145548
- return [
145549
- `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.`,
145550
- "",
145551
- "```",
145552
- status,
145553
- "```"
145554
- ].join("\n");
145555
- }
145556
- var agent = (input) => {
145557
- return {
145558
- ...input,
145559
- run: async (ctx) => {
145560
- log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
145561
- return input.run(ctx);
145562
- }
145563
- };
145564
- };
145565
-
145566
146599
  // agents/claude.ts
145567
146600
  async function installClaudeCli() {
145568
146601
  return await installFromNpmTarball({
@@ -145601,6 +146634,7 @@ async function runClaude(params) {
145601
146634
  let finalOutput = "";
145602
146635
  let sessionId;
145603
146636
  let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
146637
+ let accumulatedCostUsd = 0;
145604
146638
  let tokensLogged = false;
145605
146639
  function buildUsage() {
145606
146640
  const totalInput = accumulatedTokens.input + accumulatedTokens.cacheRead + accumulatedTokens.cacheWrite;
@@ -145609,7 +146643,8 @@ async function runClaude(params) {
145609
146643
  inputTokens: totalInput,
145610
146644
  outputTokens: accumulatedTokens.output,
145611
146645
  cacheReadTokens: accumulatedTokens.cacheRead || void 0,
145612
- cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
146646
+ cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
146647
+ costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
145613
146648
  } : void 0;
145614
146649
  }
145615
146650
  const handlers2 = {
@@ -145626,6 +146661,12 @@ async function runClaude(params) {
145626
146661
  finalOutput = message;
145627
146662
  } else if (block.type === "tool_use") {
145628
146663
  const toolName = block.name || "unknown";
146664
+ if (params.onToolUse) {
146665
+ params.onToolUse({
146666
+ toolName,
146667
+ input: block.input
146668
+ });
146669
+ }
145629
146670
  thinkingTimer.markToolCall();
145630
146671
  log.toolCall({ toolName, input: block.input || {} });
145631
146672
  if (toolName.includes("report_progress") && params.todoTracker) {
@@ -145641,6 +146682,8 @@ async function runClaude(params) {
145641
146682
  if (msgUsage) {
145642
146683
  accumulatedTokens.input += msgUsage.input_tokens || 0;
145643
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;
145644
146687
  }
145645
146688
  },
145646
146689
  user: (event) => {
@@ -145671,19 +146714,18 @@ async function runClaude(params) {
145671
146714
  const cacheRead = usage?.cache_read_input_tokens || 0;
145672
146715
  const cacheWrite = usage?.cache_creation_input_tokens || 0;
145673
146716
  const outputTokens = usage?.output_tokens || 0;
145674
- 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;
145675
146718
  accumulatedTokens = { input: inputTokens, output: outputTokens, cacheRead, cacheWrite };
146719
+ accumulatedCostUsd = costUsd;
145676
146720
  log.info(`\xBB ${params.label} result: subtype=${subtype}, turns=${numTurns}`);
145677
146721
  if (!tokensLogged) {
145678
- log.table([
145679
- [
145680
- { data: "Input", header: true },
145681
- { data: "Cache Read", header: true },
145682
- { data: "Cache Write", header: true },
145683
- { data: "Output", header: true }
145684
- ],
145685
- [String(totalInput), String(cacheRead), String(cacheWrite), String(outputTokens)]
145686
- ]);
146722
+ logTokenTable({
146723
+ input: inputTokens,
146724
+ cacheRead,
146725
+ cacheWrite,
146726
+ output: outputTokens,
146727
+ costUsd
146728
+ });
145687
146729
  tokensLogged = true;
145688
146730
  }
145689
146731
  } else if (subtype === "error_max_turns") {
@@ -145718,6 +146760,7 @@ async function runClaude(params) {
145718
146760
  cwd: params.cwd,
145719
146761
  env: params.env,
145720
146762
  activityTimeout: 3e5,
146763
+ onActivityTimeout: params.onActivityTimeout,
145721
146764
  stdio: ["ignore", "pipe", "pipe"],
145722
146765
  onStdout: async (chunk) => {
145723
146766
  const text = chunk.toString();
@@ -145729,25 +146772,33 @@ async function runClaude(params) {
145729
146772
  for (const line of lines) {
145730
146773
  const trimmed = line.trim();
145731
146774
  if (!trimmed) continue;
146775
+ let event;
145732
146776
  try {
145733
- const event = JSON.parse(trimmed);
145734
- eventCount++;
145735
- log.debug(JSON.stringify(event, null, 2));
145736
- const timeSinceLastActivity = getIdleMs();
145737
- if (timeSinceLastActivity > 1e4) {
145738
- log.info(
145739
- `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
145740
- );
145741
- }
145742
- markActivity();
145743
- const handler2 = handlers2[event.type];
145744
- if (handler2) {
145745
- handler2(event);
145746
- } else {
145747
- log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
145748
- }
146777
+ event = JSON.parse(trimmed);
145749
146778
  } catch {
145750
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
+ );
145751
146802
  }
145752
146803
  }
145753
146804
  },
@@ -145781,16 +146832,9 @@ async function runClaude(params) {
145781
146832
  if (stderrContext) log.info(`\xBB last stderr output:
145782
146833
  ${stderrContext}`);
145783
146834
  }
145784
- if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
145785
- const totalTokens = accumulatedTokens.input + accumulatedTokens.output;
145786
- log.table([
145787
- [
145788
- { data: "Input Tokens", header: true },
145789
- { data: "Output Tokens", header: true },
145790
- { data: "Total Tokens", header: true }
145791
- ],
145792
- [String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
145793
- ]);
146835
+ if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
146836
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
146837
+ tokensLogged = true;
145794
146838
  }
145795
146839
  const usage = buildUsage();
145796
146840
  if (result.exitCode !== 0) {
@@ -145823,7 +146867,7 @@ ${stderrContext}`);
145823
146867
  params.todoTracker?.cancel();
145824
146868
  const duration4 = performance6.now() - startTime;
145825
146869
  const errorMessage = error49 instanceof Error ? error49.message : String(error49);
145826
- const isActivityTimeout = errorMessage.includes("activity timeout");
146870
+ const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
145827
146871
  const stderrContext = recentStderr.slice(-10).join("\n");
145828
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`;
145829
146873
  log.info(
@@ -145914,8 +146958,7 @@ var claude = agent({
145914
146958
  "--effort",
145915
146959
  effort,
145916
146960
  "--disallowedTools",
145917
- "Bash",
145918
- "Agent(Bash)"
146961
+ "Bash,Agent(Bash)"
145919
146962
  ];
145920
146963
  if (model) {
145921
146964
  baseArgs.push("--model", model);
@@ -145928,11 +146971,19 @@ var claude = agent({
145928
146971
  log.info(`\xBB effort: ${effort}`);
145929
146972
  log.debug(`\xBB starting Pullfrog (Claude Code): node ${baseArgs.join(" ")}`);
145930
146973
  log.debug(`\xBB working directory: ${repoDir}`);
145931
- 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
+ };
145932
146982
  let result = await runClaude({
145933
146983
  ...runParams,
145934
146984
  args: [...baseArgs, "-p", ctx.instructions.full]
145935
146985
  });
146986
+ let aggregatedUsage = result.usage;
145936
146987
  for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
145937
146988
  if (!result.success || !result.sessionId) break;
145938
146989
  const status = getGitStatus();
@@ -145949,12 +147000,13 @@ ${status}`);
145949
147000
  result.sessionId
145950
147001
  ]
145951
147002
  });
147003
+ aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
145952
147004
  }
145953
- return result;
147005
+ return { ...result, usage: aggregatedUsage };
145954
147006
  }
145955
147007
  });
145956
147008
 
145957
- // agents/opentoad.ts
147009
+ // agents/opencode.ts
145958
147010
  import { execFileSync as execFileSync4 } from "node:child_process";
145959
147011
  import { mkdirSync as mkdirSync4 } from "node:fs";
145960
147012
  import { join as join10 } from "node:path";
@@ -146032,6 +147084,7 @@ async function runOpenCode(params) {
146032
147084
  const thinkingTimer = new ThinkingTimer();
146033
147085
  let finalOutput = "";
146034
147086
  let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
147087
+ let accumulatedCostUsd = 0;
146035
147088
  let tokensLogged = false;
146036
147089
  const toolCallTimings = /* @__PURE__ */ new Map();
146037
147090
  let currentStepId = null;
@@ -146044,7 +147097,8 @@ async function runOpenCode(params) {
146044
147097
  inputTokens: totalInput,
146045
147098
  outputTokens: accumulatedTokens.output,
146046
147099
  cacheReadTokens: accumulatedTokens.cacheRead || void 0,
146047
- cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
147100
+ cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
147101
+ costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
146048
147102
  } : void 0;
146049
147103
  }
146050
147104
  const handlers2 = {
@@ -146055,6 +147109,7 @@ async function runOpenCode(params) {
146055
147109
  log.debug(`\xBB ${params.label} init event (full): ${JSON.stringify(event)}`);
146056
147110
  finalOutput = "";
146057
147111
  accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
147112
+ accumulatedCostUsd = 0;
146058
147113
  tokensLogged = false;
146059
147114
  },
146060
147115
  message: (event) => {
@@ -146099,6 +147154,9 @@ async function runOpenCode(params) {
146099
147154
  accumulatedTokens.cacheRead += eventTokens.cache?.read || 0;
146100
147155
  accumulatedTokens.cacheWrite += eventTokens.cache?.write || 0;
146101
147156
  }
147157
+ if (typeof event.part?.cost === "number" && Number.isFinite(event.part.cost)) {
147158
+ accumulatedCostUsd += event.part.cost;
147159
+ }
146102
147160
  if (currentStepId === stepId) {
146103
147161
  currentStepId = null;
146104
147162
  currentStepType = null;
@@ -146116,6 +147174,12 @@ async function runOpenCode(params) {
146116
147174
  if (stepHistory.length > 0) {
146117
147175
  stepHistory[stepHistory.length - 1].toolCalls.push(toolName);
146118
147176
  }
147177
+ if (params.onToolUse) {
147178
+ params.onToolUse({
147179
+ toolName,
147180
+ input: event.part?.state?.input
147181
+ });
147182
+ }
146119
147183
  thinkingTimer.markToolCall();
146120
147184
  log.toolCall({ toolName, input: event.part?.state?.input || {} });
146121
147185
  if (event.part?.state?.status === "completed" && event.part.state.output) {
@@ -146171,19 +147235,9 @@ async function runOpenCode(params) {
146171
147235
  if (event.status === "error") {
146172
147236
  log.info(`\xBB ${params.label} failed: ${JSON.stringify(event)}`);
146173
147237
  } else {
146174
- const inputTokens = event.stats?.input_tokens || accumulatedTokens.input || 0;
146175
- const outputTokens = event.stats?.output_tokens || accumulatedTokens.output || 0;
146176
- const totalTokens = event.stats?.total_tokens || inputTokens + outputTokens;
146177
147238
  log.info(`\xBB run complete: tool_calls=${toolCalls}, duration=${duration4}ms`);
146178
- if ((inputTokens > 0 || outputTokens > 0) && !tokensLogged) {
146179
- log.table([
146180
- [
146181
- { data: "Input Tokens", header: true },
146182
- { data: "Output Tokens", header: true },
146183
- { data: "Total Tokens", header: true }
146184
- ],
146185
- [String(inputTokens), String(outputTokens), String(totalTokens)]
146186
- ]);
147239
+ if ((accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0) && !tokensLogged) {
147240
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
146187
147241
  tokensLogged = true;
146188
147242
  }
146189
147243
  }
@@ -146200,6 +147254,7 @@ async function runOpenCode(params) {
146200
147254
  cwd: params.cwd,
146201
147255
  env: params.env,
146202
147256
  activityTimeout: 3e5,
147257
+ onActivityTimeout: params.onActivityTimeout,
146203
147258
  stdio: ["ignore", "pipe", "pipe"],
146204
147259
  onStdout: async (chunk) => {
146205
147260
  const text = chunk.toString();
@@ -146211,29 +147266,37 @@ async function runOpenCode(params) {
146211
147266
  for (const line of lines) {
146212
147267
  const trimmed = line.trim();
146213
147268
  if (!trimmed) continue;
147269
+ let event;
146214
147270
  try {
146215
- const event = JSON.parse(trimmed);
146216
- eventCount++;
146217
- log.debug(JSON.stringify(event, null, 2));
146218
- const timeSinceLastActivity = getIdleMs();
146219
- if (timeSinceLastActivity > 1e4) {
146220
- const activeToolCalls = toolCallTimings.size;
146221
- const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
146222
- log.info(
146223
- `\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
146224
- );
146225
- }
146226
- markActivity();
146227
- const handler2 = handlers2[event.type];
146228
- if (handler2) {
146229
- await handler2(event);
146230
- } else {
146231
- log.info(
146232
- `\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
146233
- );
146234
- }
147271
+ event = JSON.parse(trimmed);
146235
147272
  } catch {
146236
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
+ );
146237
147300
  }
146238
147301
  }
146239
147302
  },
@@ -146267,16 +147330,9 @@ async function runOpenCode(params) {
146267
147330
  if (stderrContext) log.info(`\xBB last stderr output:
146268
147331
  ${stderrContext}`);
146269
147332
  }
146270
- if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
146271
- const totalTokens = accumulatedTokens.input + accumulatedTokens.output;
146272
- log.table([
146273
- [
146274
- { data: "Input Tokens", header: true },
146275
- { data: "Output Tokens", header: true },
146276
- { data: "Total Tokens", header: true }
146277
- ],
146278
- [String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
146279
- ]);
147333
+ if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
147334
+ logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
147335
+ tokensLogged = true;
146280
147336
  }
146281
147337
  const usage = buildUsage();
146282
147338
  if (result.exitCode !== 0) {
@@ -146302,7 +147358,7 @@ ${stderrContext}`);
146302
147358
  params.todoTracker?.cancel();
146303
147359
  const duration4 = performance7.now() - startTime;
146304
147360
  const errorMessage = error49 instanceof Error ? error49.message : String(error49);
146305
- const isActivityTimeout = errorMessage.includes("activity timeout");
147361
+ const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
146306
147362
  const stderrContext = recentStderr.slice(-10).join("\n");
146307
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`;
146308
147364
  log.info(
@@ -146322,8 +147378,8 @@ ${stderrContext}`
146322
147378
  };
146323
147379
  }
146324
147380
  }
146325
- var opentoad = agent({
146326
- name: "opentoad",
147381
+ var opencode = agent({
147382
+ name: "opencode",
146327
147383
  install: installOpencodeCli,
146328
147384
  run: async (ctx) => {
146329
147385
  const cliPath = await installOpencodeCli();
@@ -146359,12 +147415,15 @@ var opentoad = agent({
146359
147415
  cliPath,
146360
147416
  cwd: repoDir,
146361
147417
  env: env2,
146362
- todoTracker: ctx.todoTracker
147418
+ todoTracker: ctx.todoTracker,
147419
+ onActivityTimeout: ctx.onActivityTimeout,
147420
+ onToolUse: ctx.onToolUse
146363
147421
  };
146364
147422
  let result = await runOpenCode({
146365
147423
  ...runParams,
146366
147424
  args: [...baseArgs, ctx.instructions.full]
146367
147425
  });
147426
+ let aggregatedUsage = result.usage;
146368
147427
  for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
146369
147428
  if (!result.success) break;
146370
147429
  const status = getGitStatus();
@@ -146373,15 +147432,16 @@ var opentoad = agent({
146373
147432
  ${status}`);
146374
147433
  result = await runOpenCode({
146375
147434
  ...runParams,
146376
- args: [...baseArgs, "--continue", buildCommitPrompt("opentoad", status)]
147435
+ args: [...baseArgs, "--continue", buildCommitPrompt("opencode", status)]
146377
147436
  });
147437
+ aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
146378
147438
  }
146379
- return result;
147439
+ return { ...result, usage: aggregatedUsage };
146380
147440
  }
146381
147441
  });
146382
147442
 
146383
147443
  // agents/index.ts
146384
- var agents = { claude, opentoad };
147444
+ var agents = { claude, opencode };
146385
147445
 
146386
147446
  // utils/agent.ts
146387
147447
  function hasEnvVar(name) {
@@ -146394,13 +147454,11 @@ function hasClaudeCodeAuth() {
146394
147454
  function resolveModel(ctx) {
146395
147455
  const envModel = process.env.PULLFROG_MODEL?.trim();
146396
147456
  if (envModel) {
146397
- log.info(`\xBB model: ${envModel} (override via PULLFROG_MODEL)`);
146398
- return envModel;
147457
+ return resolveCliModel(envModel) ?? envModel;
146399
147458
  }
146400
147459
  if (ctx.slug) {
146401
147460
  const resolved = resolveCliModel(ctx.slug);
146402
147461
  if (resolved) {
146403
- log.info(`\xBB model: ${resolved} (resolved from ${ctx.slug})`);
146404
147462
  return resolved;
146405
147463
  }
146406
147464
  log.warning(`\xBB unknown model slug "${ctx.slug}" \u2014 agent will auto-select`);
@@ -146411,7 +147469,6 @@ function resolveAgent(ctx) {
146411
147469
  const envAgent = process.env.PULLFROG_AGENT?.trim();
146412
147470
  if (envAgent) {
146413
147471
  if (envAgent in agents) {
146414
- log.info(`\xBB agent: ${envAgent} (override via PULLFROG_AGENT)`);
146415
147472
  return agents[envAgent];
146416
147473
  }
146417
147474
  log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
@@ -146420,13 +147477,12 @@ function resolveAgent(ctx) {
146420
147477
  try {
146421
147478
  const provider2 = getModelProvider(ctx.model);
146422
147479
  if (provider2 === "anthropic" && hasClaudeCodeAuth()) {
146423
- log.info(`\xBB agent: claude (auto-selected for ${ctx.model})`);
146424
147480
  return agents.claude;
146425
147481
  }
146426
147482
  } catch {
146427
147483
  }
146428
147484
  }
146429
- return agents.opentoad;
147485
+ return agents.opencode;
146430
147486
  }
146431
147487
 
146432
147488
  // utils/apiKeys.ts
@@ -150640,9 +151696,9 @@ async function startGitAuthServer(tmpdir3) {
150640
151696
  res.writeHead(409, { "Content-Type": "text/plain" });
150641
151697
  res.end("compromised");
150642
151698
  });
150643
- await new Promise((resolve2, reject) => {
151699
+ await new Promise((resolve3, reject) => {
150644
151700
  server.on("error", reject);
150645
- server.listen(0, "127.0.0.1", () => resolve2());
151701
+ server.listen(0, "127.0.0.1", () => resolve3());
150646
151702
  });
150647
151703
  const rawAddr = server.address();
150648
151704
  if (!rawAddr || typeof rawAddr === "string") {
@@ -150686,7 +151742,7 @@ async function startGitAuthServer(tmpdir3) {
150686
151742
  clearTimeout(entry.timeout);
150687
151743
  }
150688
151744
  codes.clear();
150689
- await new Promise((resolve2) => server.close(() => resolve2()));
151745
+ await new Promise((resolve3) => server.close(() => resolve3()));
150690
151746
  log.debug("git auth server closed");
150691
151747
  }
150692
151748
  return {
@@ -150873,7 +151929,7 @@ MCP servers provide tools you can call. Inspect your available MCP servers at st
150873
151929
 
150874
151930
  ### Git
150875
151931
 
150876
- 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:
150877
151933
  - \`${t2("push_branch")}\` - push current or specified branch
150878
151934
  - \`${t2("git_fetch")}\` - fetch refs from remote
150879
151935
  - \`${t2("checkout_pr")}\` - checkout a PR branch (fetches and configures push for forks)
@@ -150914,6 +151970,8 @@ Never use \`sleep\` to wait for commands to complete. Commands run synchronously
150914
151970
 
150915
151971
  When posting comments via ${pullfrogMcpName}, write as a professional team member would. Your final comments should be polished and actionable \u2014 do not include intermediate reasoning like "I'll now look at the code" or "Let me respond to the question."
150916
151972
 
151973
+ When embedding images (e.g. uploaded screenshots) in comments or PR bodies, always use markdown image syntax: \`![description](url)\`. Never paste a naked URL \u2014 it will not render as an image.
151974
+
150917
151975
  ### Progress reporting
150918
151976
 
150919
151977
  **Task list**: at the start of every run, create an internal task list based on the steps in your current mode. Update it as you complete each step. The system automatically renders this list to the progress comment \u2014 you do not need to call \`report_progress\` for this.
@@ -151049,7 +152107,7 @@ function normalizeEnv() {
151049
152107
 
151050
152108
  // utils/payload.ts
151051
152109
  var core4 = __toESM(require_core(), 1);
151052
- import { isAbsolute, resolve } from "node:path";
152110
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
151053
152111
 
151054
152112
  // utils/versioning.ts
151055
152113
  var import_semver2 = __toESM(require_semver2(), 1);
@@ -151103,8 +152161,8 @@ function isPayloadEvent(value2) {
151103
152161
  function resolveCwd(cwd) {
151104
152162
  const workspace = process.env.GITHUB_WORKSPACE;
151105
152163
  if (!cwd) return workspace;
151106
- if (isAbsolute(cwd)) return cwd;
151107
- return workspace ? resolve(workspace, cwd) : cwd;
152164
+ if (isAbsolute2(cwd)) return cwd;
152165
+ return workspace ? resolve2(workspace, cwd) : cwd;
151108
152166
  }
151109
152167
  function resolvePromptInput() {
151110
152168
  const prompt = core4.getInput("prompt", { required: true });
@@ -151283,7 +152341,8 @@ var defaultSettings = {
151283
152341
  shell: "restricted",
151284
152342
  prApproveEnabled: false,
151285
152343
  modeInstructions: {},
151286
- learnings: null
152344
+ learnings: null,
152345
+ envAllowlist: null
151287
152346
  };
151288
152347
  var defaultRunContext = {
151289
152348
  settings: defaultSettings,
@@ -151363,7 +152422,7 @@ async function resolveRunContextData(params) {
151363
152422
  }
151364
152423
 
151365
152424
  // utils/setup.ts
151366
- import { execSync as execSync3 } from "node:child_process";
152425
+ import { execFileSync as execFileSync5, execSync as execSync3 } from "node:child_process";
151367
152426
  import { mkdtempSync } from "node:fs";
151368
152427
  import { tmpdir as tmpdir2 } from "node:os";
151369
152428
  import { join as join13 } from "node:path";
@@ -151373,6 +152432,51 @@ function createTempDirectory() {
151373
152432
  log.info(`\xBB created temp dir at ${sharedTempDir}`);
151374
152433
  return sharedTempDir;
151375
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
+ }
151376
152480
  async function setupGit(params) {
151377
152481
  const repoDir = process.cwd();
151378
152482
  log.info("\xBB setting up git configuration...");
@@ -151419,24 +152523,7 @@ async function setupGit(params) {
151419
152523
  } catch {
151420
152524
  log.debug("\xBB no existing authentication headers to remove");
151421
152525
  }
151422
- try {
151423
- const configOutput = execSync3("git config --local --get-regexp ^includeif\\.", {
151424
- cwd: repoDir,
151425
- encoding: "utf-8",
151426
- stdio: "pipe"
151427
- });
151428
- for (const line of configOutput.trim().split("\n")) {
151429
- const key = line.split(" ")[0];
151430
- if (!key) continue;
151431
- execSync3(`git config --local --unset "${key}"`, {
151432
- cwd: repoDir,
151433
- stdio: "pipe"
151434
- });
151435
- }
151436
- log.info("\xBB removed includeIf credential entries");
151437
- } catch {
151438
- log.debug("\xBB no includeIf credential entries to remove");
151439
- }
152526
+ removeIncludeIfEntries(repoDir);
151440
152527
  const originUrl = `https://github.com/${params.owner}/${params.name}.git`;
151441
152528
  $("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
151442
152529
  params.toolState.pushUrl = originUrl;
@@ -151455,6 +152542,13 @@ function parseTimeString(input) {
151455
152542
  const seconds = parseInt(match3[3] || "0", 10);
151456
152543
  return (hours * 3600 + minutes * 60 + seconds) * 1e3;
151457
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
+ }
151458
152552
 
151459
152553
  // utils/todoTracking.ts
151460
152554
  function isValidTodoStatus(value2) {
@@ -151557,9 +152651,12 @@ function createTodoTracker(onUpdate) {
151557
152651
  if (item.status === "in_progress") item.status = "completed";
151558
152652
  }
151559
152653
  },
151560
- renderCollapsible() {
152654
+ renderCollapsible(options) {
151561
152655
  if (state.size === 0) return "";
151562
- 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
+ );
151563
152660
  const completed = todos.filter((t2) => t2.status === "completed").length;
151564
152661
  const markdown = renderTodoMarkdown(todos);
151565
152662
  return `<details>
@@ -151619,6 +152716,32 @@ function resolveOutputSchema() {
151619
152716
  log.info("\xBB structured output schema provided \u2014 output will be required");
151620
152717
  return parsed2;
151621
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
+ }
151622
152745
  async function mintProxyKey(ctx) {
151623
152746
  try {
151624
152747
  process.env.ACTIONS_ID_TOKEN_REQUEST_URL = ctx.oidcCredentials.requestUrl;
@@ -151678,6 +152801,7 @@ async function main() {
151678
152801
  }
151679
152802
  const timer = new Timer();
151680
152803
  let activityTimeout = null;
152804
+ let safetyNetTimer;
151681
152805
  const resolvedPromptInput = resolvePromptInput();
151682
152806
  const toolState = initToolState({
151683
152807
  progressCommentId: typeof resolvedPromptInput !== "string" ? resolvedPromptInput.progressCommentId : void 0
@@ -151697,6 +152821,9 @@ async function main() {
151697
152821
  const count = Object.keys(runContext.dbSecrets).length;
151698
152822
  if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
151699
152823
  }
152824
+ if (runContext.repoSettings.envAllowlist) {
152825
+ setEnvAllowlist(runContext.repoSettings.envAllowlist);
152826
+ }
151700
152827
  const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
151701
152828
  toolState.model = payload.model;
151702
152829
  if (payload.event.trigger === "pull_request_synchronize") {
@@ -151761,10 +152888,13 @@ async function main() {
151761
152888
  postCheckoutScript: runContext.repoSettings.postCheckoutScript
151762
152889
  });
151763
152890
  timer.checkpoint("git");
151764
- await executeLifecycleHook({
152891
+ const setupHook = await executeLifecycleHook({
151765
152892
  event: "setup",
151766
152893
  script: runContext.repoSettings.setupScript
151767
152894
  });
152895
+ if (setupHook.warning) {
152896
+ throw new Error(setupHook.warning);
152897
+ }
151768
152898
  timer.checkpoint("lifecycleHooks::setup");
151769
152899
  const agentId = agent2.name;
151770
152900
  const modes2 = [...computeModes(agentId), ...runContext.repoSettings.modes];
@@ -151786,17 +152916,22 @@ async function main() {
151786
152916
  runId: runInfo.runId,
151787
152917
  jobId: runInfo.jobId,
151788
152918
  mcpServerUrl: "",
151789
- tmpdir: tmpdir3
152919
+ tmpdir: tmpdir3,
152920
+ resolvedModel
151790
152921
  };
151791
152922
  const mcpHttpServer = __using(_stack, await startMcpHttpServer(toolContext, { outputSchema }), true);
151792
152923
  toolContext.mcpServerUrl = mcpHttpServer.url;
151793
152924
  log.info(`\xBB MCP server started at ${mcpHttpServer.url}`);
151794
152925
  timer.checkpoint("mcpServer");
151795
152926
  startInstallation(toolContext);
151796
- if (payload.model) log.info(`\xBB model: ${payload.model}`);
151797
- 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}`);
151798
152932
  log.info(`\xBB push: ${payload.push}`);
151799
152933
  log.info(`\xBB shell: ${payload.shell}`);
152934
+ log.info(`\xBB timeout: ${timeoutForLog}`);
151800
152935
  const instructions = resolveInstructions({
151801
152936
  payload,
151802
152937
  repo: runContext.repo,
@@ -151818,6 +152953,18 @@ ${instructions.user}` : null,
151818
152953
  log.group("View full prompt", () => {
151819
152954
  log.info(instructions.full);
151820
152955
  });
152956
+ if (agentId === "opencode") {
152957
+ const pluginDir = join14(process.cwd(), ".opencode", "plugin");
152958
+ const hasPlugins = existsSync6(pluginDir) && readdirSync(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
152959
+ if (hasPlugins && toolState.dependencyInstallation?.promise) {
152960
+ log.info(
152961
+ "\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
152962
+ );
152963
+ await toolState.dependencyInstallation.promise.catch(() => {
152964
+ });
152965
+ timer.checkpoint("awaitDepsForPlugins");
152966
+ }
152967
+ }
151821
152968
  activityTimeout = createProcessOutputActivityTimeout({
151822
152969
  timeoutMs: DEFAULT_ACTIVITY_TIMEOUT_MS,
151823
152970
  checkIntervalMs: DEFAULT_ACTIVITY_CHECK_INTERVAL_MS
@@ -151833,24 +152980,62 @@ ${instructions.user}` : null,
151833
152980
  }
151834
152981
  });
151835
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
+ };
151836
153005
  const agentPromise = agent2.run({
151837
153006
  payload,
151838
153007
  resolvedModel,
151839
153008
  mcpServerUrl: mcpHttpServer.url,
151840
153009
  tmpdir: tmpdir3,
151841
153010
  instructions,
151842
- 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(() => {
151843
153028
  });
151844
153029
  let result;
151845
153030
  if (payload.timeout === TIMEOUT_DISABLED) {
151846
153031
  result = await Promise.race([agentPromise, activityTimeout.promise]);
151847
153032
  } else {
151848
- const parsed2 = payload.timeout ? parseTimeString(payload.timeout) : null;
151849
- if (payload.timeout && parsed2 === null) {
151850
- 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`);
151851
153036
  }
151852
- const timeoutMs = parsed2 ?? 36e5;
151853
- const actualTimeout = parsed2 !== null ? payload.timeout : "1h";
153037
+ const timeoutMs = usable ?? 36e5;
153038
+ const actualTimeout = usable !== null ? payload.timeout : "1h";
151854
153039
  let timeoutId;
151855
153040
  const timeoutPromise = new Promise((_4, reject) => {
151856
153041
  timeoutId = setTimeout(() => {
@@ -151912,7 +153097,14 @@ ${instructions.user}` : null,
151912
153097
  killTrackedChildren();
151913
153098
  log.error(errorMessage);
151914
153099
  try {
151915
- await writeJobSummary(toolState);
153100
+ const errorSummary = `### \u274C Pullfrog failed
153101
+
153102
+ \`\`\`
153103
+ ${errorMessage}
153104
+ \`\`\``;
153105
+ const usageSummary = formatUsageSummary(toolState.usageEntries);
153106
+ const parts = [errorSummary, toolState.lastProgressBody, usageSummary].filter(Boolean);
153107
+ await writeSummary(parts.join("\n\n"));
151916
153108
  } catch {
151917
153109
  }
151918
153110
  try {
@@ -151930,8 +153122,21 @@ ${instructions.user}` : null,
151930
153122
  };
151931
153123
  } finally {
151932
153124
  activityTimeout?.stop();
153125
+ if (safetyNetTimer) clearTimeout(safetyNetTimer);
151933
153126
  if (usageSummaryPath) {
151934
- 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
+ }
151935
153140
  }
151936
153141
  }
151937
153142
  } catch (_3) {
@@ -151985,7 +153190,7 @@ async function validateStuckProgressComment(ctx) {
151985
153190
  comment_id: commentId
151986
153191
  });
151987
153192
  const body = commentResult.data.body ?? "";
151988
- if (body.startsWith(LEAPING_INTO_ACTION_PREFIX)) {
153193
+ if (isLeapingIntoActionCommentBody(body)) {
151989
153194
  log.info(`[post] comment ${commentId} is stuck on "Leaping into action"`);
151990
153195
  return commentId;
151991
153196
  }
@@ -152195,7 +153400,7 @@ async function run(args2) {
152195
153400
  }
152196
153401
 
152197
153402
  // commands/init.ts
152198
- import { execFileSync as execFileSync5 } from "node:child_process";
153403
+ import { execFileSync as execFileSync6 } from "node:child_process";
152199
153404
 
152200
153405
  // node_modules/.pnpm/@clack+core@1.2.0/node_modules/@clack/core/dist/index.mjs
152201
153406
  import { styleText as y } from "node:util";
@@ -153187,7 +154392,7 @@ function handleCancel(value2) {
153187
154392
  function getGhToken() {
153188
154393
  let token;
153189
154394
  try {
153190
- token = execFileSync5("gh", ["auth", "token"], { encoding: "utf-8" }).trim();
154395
+ token = execFileSync6("gh", ["auth", "token"], { encoding: "utf-8" }).trim();
153191
154396
  } catch {
153192
154397
  bail(
153193
154398
  `gh cli not found or not authenticated.
@@ -153230,7 +154435,7 @@ async function ghApi(path3, token) {
153230
154435
  function parseGitRemote() {
153231
154436
  let url4;
153232
154437
  try {
153233
- url4 = execFileSync5("git", ["remote", "get-url", "origin"], { encoding: "utf-8" }).trim();
154438
+ url4 = execFileSync6("git", ["remote", "get-url", "origin"], { encoding: "utf-8" }).trim();
153234
154439
  } catch {
153235
154440
  bail("not a git repository or no 'origin' remote found.");
153236
154441
  }
@@ -153241,10 +154446,10 @@ function parseGitRemote() {
153241
154446
  function openBrowser(url4) {
153242
154447
  try {
153243
154448
  const platform = process.platform;
153244
- if (platform === "darwin") execFileSync5("open", [url4], { stdio: "ignore" });
154449
+ if (platform === "darwin") execFileSync6("open", [url4], { stdio: "ignore" });
153245
154450
  else if (platform === "win32")
153246
- execFileSync5("cmd", ["/c", "start", "", url4], { stdio: "ignore" });
153247
- else execFileSync5("xdg-open", [url4], { stdio: "ignore" });
154451
+ execFileSync6("cmd", ["/c", "start", "", url4], { stdio: "ignore" });
154452
+ else execFileSync6("xdg-open", [url4], { stdio: "ignore" });
153248
154453
  } catch {
153249
154454
  }
153250
154455
  }
@@ -153471,7 +154676,7 @@ function setGhSecret(ctx) {
153471
154676
  let orgFailed = false;
153472
154677
  if (ctx.org) {
153473
154678
  try {
153474
- execFileSync5("gh", ["secret", "set", ctx.name, "--org", ctx.org, "--visibility", "all"], {
154679
+ execFileSync6("gh", ["secret", "set", ctx.name, "--org", ctx.org, "--visibility", "all"], {
153475
154680
  input: ctx.value,
153476
154681
  stdio: ["pipe", "ignore", "pipe"],
153477
154682
  encoding: "utf-8"
@@ -153482,7 +154687,7 @@ function setGhSecret(ctx) {
153482
154687
  }
153483
154688
  }
153484
154689
  try {
153485
- execFileSync5("gh", ["secret", "set", ctx.name, "--repo", ctx.repoSlug], {
154690
+ execFileSync6("gh", ["secret", "set", ctx.name, "--repo", ctx.repoSlug], {
153486
154691
  input: ctx.value,
153487
154692
  stdio: ["pipe", "ignore", "pipe"],
153488
154693
  encoding: "utf-8"
@@ -153852,7 +155057,7 @@ async function run2() {
153852
155057
  }
153853
155058
 
153854
155059
  // cli.ts
153855
- var VERSION10 = "0.0.200";
155060
+ var VERSION10 = "0.0.202";
153856
155061
  var bin = basename2(process.argv[1] || "");
153857
155062
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
153858
155063
  var rawArgs = process.argv.slice(2);