@wrongstack/plugins 0.291.1 → 0.292.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5036,8 +5036,8 @@ var plugin15 = {
5036
5036
  var file_watcher_default = plugin15;
5037
5037
 
5038
5038
  // src/format-on-save/index.ts
5039
- import { execFileSync as execFileSync2, execSync } from "node:child_process";
5040
- import { existsSync as existsSync2, statSync as statSync4 } from "node:fs";
5039
+ import { execFile as execFile3 } from "node:child_process";
5040
+ import { access, stat as stat2 } from "node:fs/promises";
5041
5041
  var API_VERSION8 = "^0.1.10";
5042
5042
  var state14 = {
5043
5043
  invocationCount: 0,
@@ -5096,29 +5096,46 @@ function evictExpired(ttlMs) {
5096
5096
  if (ts < cutoff) recentlyCovered.delete(path);
5097
5097
  }
5098
5098
  }
5099
- function formatFile(filePath, timeoutMs) {
5099
+ async function formatFile(filePath, timeoutMs) {
5100
5100
  if (!withinProject(filePath)) return null;
5101
- if (!existsSync2(filePath)) return null;
5101
+ try {
5102
+ await access(filePath);
5103
+ } catch {
5104
+ return null;
5105
+ }
5102
5106
  let bytesBefore;
5103
5107
  try {
5104
- bytesBefore = statSync4(filePath).size;
5108
+ bytesBefore = (await stat2(filePath)).size;
5105
5109
  } catch {
5106
5110
  return null;
5107
5111
  }
5108
5112
  try {
5109
- execFileSync2("npx", ["biome", "format", "--write", filePath], {
5110
- encoding: "utf-8",
5111
- timeout: timeoutMs,
5112
- cwd: process.cwd(),
5113
- stdio: ["pipe", "pipe", "pipe"]
5113
+ await new Promise((resolve28, reject) => {
5114
+ execFile3(
5115
+ "npx",
5116
+ ["biome", "format", "--write", filePath],
5117
+ {
5118
+ encoding: "utf-8",
5119
+ timeout: timeoutMs,
5120
+ cwd: process.cwd(),
5121
+ windowsHide: true,
5122
+ maxBuffer: 16 * 1024 * 1024
5123
+ },
5124
+ (err) => {
5125
+ if (err) {
5126
+ const e = err;
5127
+ if (e.killed) return reject(err);
5128
+ }
5129
+ resolve28();
5130
+ }
5131
+ );
5114
5132
  });
5115
- } catch (err) {
5116
- const e = err;
5117
- if (e.killed) return null;
5133
+ } catch {
5134
+ return null;
5118
5135
  }
5119
5136
  let bytesAfter;
5120
5137
  try {
5121
- bytesAfter = statSync4(filePath).size;
5138
+ bytesAfter = (await stat2(filePath)).size;
5122
5139
  } catch {
5123
5140
  return null;
5124
5141
  }
@@ -5126,11 +5143,22 @@ function formatFile(filePath, timeoutMs) {
5126
5143
  return { changed: true, bytesBefore, bytesAfter };
5127
5144
  }
5128
5145
  try {
5129
- execFileSync2("npx", ["biome", "format", filePath], {
5130
- encoding: "utf-8",
5131
- timeout: timeoutMs,
5132
- cwd: process.cwd(),
5133
- stdio: ["pipe", "pipe", "pipe"]
5146
+ await new Promise((resolve28, reject) => {
5147
+ execFile3(
5148
+ "npx",
5149
+ ["biome", "format", filePath],
5150
+ {
5151
+ encoding: "utf-8",
5152
+ timeout: timeoutMs,
5153
+ cwd: process.cwd(),
5154
+ windowsHide: true,
5155
+ maxBuffer: 16 * 1024 * 1024
5156
+ },
5157
+ (err) => {
5158
+ if (err) return reject(err);
5159
+ resolve28();
5160
+ }
5161
+ );
5134
5162
  });
5135
5163
  return { changed: false, bytesBefore, bytesAfter };
5136
5164
  } catch {
@@ -5171,7 +5199,7 @@ var plugin16 = {
5171
5199
  }
5172
5200
  }
5173
5201
  },
5174
- setup(api) {
5202
+ async setup(api) {
5175
5203
  clearRegistrations();
5176
5204
  state14.invocationCount = 0;
5177
5205
  state14.formattedCount = 0;
@@ -5183,11 +5211,21 @@ var plugin16 = {
5183
5211
  const cfg = readConfig12(api.config.extensions?.["format-on-save"]);
5184
5212
  let biomeAvailable = false;
5185
5213
  try {
5186
- execSync("npx biome --version", {
5187
- encoding: "utf-8",
5188
- timeout: 5e3,
5189
- cwd: process.cwd(),
5190
- stdio: ["pipe", "pipe", "pipe"]
5214
+ await new Promise((resolve28, reject) => {
5215
+ execFile3(
5216
+ "npx",
5217
+ ["biome", "--version"],
5218
+ {
5219
+ encoding: "utf-8",
5220
+ timeout: 5e3,
5221
+ cwd: process.cwd(),
5222
+ windowsHide: true
5223
+ },
5224
+ (err) => {
5225
+ if (err) reject(err);
5226
+ else resolve28();
5227
+ }
5228
+ );
5191
5229
  });
5192
5230
  biomeAvailable = true;
5193
5231
  api.log.info("format-on-save: biome detected");
@@ -5195,7 +5233,7 @@ var plugin16 = {
5195
5233
  biomeAvailable = false;
5196
5234
  api.log.warn("format-on-save: biome not found \u2014 hook will be a no-op");
5197
5235
  }
5198
- const hook = (input) => {
5236
+ const hook = async (input) => {
5199
5237
  if (!cfg.enabled || !biomeAvailable) return;
5200
5238
  if (input.toolResult?.isError) return;
5201
5239
  const toolName = input.toolName ?? "";
@@ -5217,7 +5255,7 @@ var plugin16 = {
5217
5255
  }
5218
5256
  }
5219
5257
  state14.invocationCount += 1;
5220
- const result = formatFile(filePath, cfg.timeoutMs);
5258
+ const result = await formatFile(filePath, cfg.timeoutMs);
5221
5259
  if (!result) {
5222
5260
  state14.errorCount += 1;
5223
5261
  return;
@@ -5323,56 +5361,68 @@ var plugin16 = {
5323
5361
  var format_on_save_default = plugin16;
5324
5362
 
5325
5363
  // src/git-autocommit/index.ts
5326
- import { execFileSync as execFileSync3 } from "node:child_process";
5327
- import { existsSync as existsSync3 } from "node:fs";
5364
+ import { execFile as execFile4 } from "node:child_process";
5365
+ import { existsSync as existsSync2 } from "node:fs";
5328
5366
  var API_VERSION9 = "^0.1.10";
5329
5367
  var commitCount = { value: 0 };
5330
5368
  var lastCommit = { hash: null, at: null };
5331
5369
  var llmGenerated = { value: 0 };
5332
5370
  var DEFAULT_GIT_TIMEOUT_MS = 3e4;
5333
5371
  var GIT_COMMIT_TIMEOUT_MS = 5 * 6e4;
5334
- function runGit2(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
5335
- try {
5336
- return execFileSync3("git", args, {
5337
- encoding: "utf-8",
5338
- cwd,
5339
- stdio: ["pipe", "pipe", "pipe"],
5340
- timeout: timeoutMs,
5341
- maxBuffer: 10 * 1024 * 1024,
5342
- windowsHide: true
5343
- }).trim();
5344
- } catch (err) {
5345
- const e = err;
5346
- throw new Error(`git command failed: ${e.message ?? e.stderr ?? String(err)}`);
5347
- }
5372
+ async function runGit2(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
5373
+ return await new Promise((resolvePromise, rejectPromise) => {
5374
+ execFile4(
5375
+ "git",
5376
+ args,
5377
+ {
5378
+ encoding: "utf-8",
5379
+ cwd,
5380
+ windowsHide: true,
5381
+ timeout: timeoutMs,
5382
+ maxBuffer: 10 * 1024 * 1024
5383
+ },
5384
+ (err, stdout) => {
5385
+ if (err) {
5386
+ const e = err;
5387
+ rejectPromise(
5388
+ new Error(
5389
+ `git command failed: ${e.message ?? e.stderr ?? String(err)}`
5390
+ )
5391
+ );
5392
+ return;
5393
+ }
5394
+ resolvePromise(stdout.trim());
5395
+ }
5396
+ );
5397
+ });
5348
5398
  }
5349
- function getChangedFiles(cwd) {
5350
- const output = runGit2(["status", "--porcelain"], cwd);
5399
+ async function getChangedFiles(cwd) {
5400
+ const output = await runGit2(["status", "--porcelain"], cwd);
5351
5401
  if (!output) return [];
5352
5402
  return output.split("\n").filter((l) => l.trim()).map((l) => l.slice(3).trim());
5353
5403
  }
5354
- function getStagedFiles(cwd) {
5355
- const output = runGit2(["diff", "--cached", "--name-only"], cwd);
5404
+ async function getStagedFiles(cwd) {
5405
+ const output = await runGit2(["diff", "--cached", "--name-only"], cwd);
5356
5406
  return output ? output.split("\n").filter(Boolean) : [];
5357
5407
  }
5358
- function stageFiles(files, cwd) {
5408
+ async function stageFiles(files, cwd) {
5359
5409
  if (!files || !Array.isArray(files)) return;
5360
5410
  const existing = files.filter((f) => {
5361
5411
  try {
5362
- return existsSync3(f);
5412
+ return existsSync2(f);
5363
5413
  } catch {
5364
5414
  return false;
5365
5415
  }
5366
5416
  });
5367
5417
  if (existing.length === 0) throw new Error("No files exist to stage");
5368
- runGit2(["add", ...existing], cwd);
5418
+ await runGit2(["add", ...existing], cwd);
5369
5419
  }
5370
- function commitWithMessage(message, cwd) {
5371
- return runGit2(["commit", "-m", message], cwd, GIT_COMMIT_TIMEOUT_MS);
5420
+ async function commitWithMessage(message, cwd) {
5421
+ return await runGit2(["commit", "-m", message], cwd, GIT_COMMIT_TIMEOUT_MS);
5372
5422
  }
5373
- function getWorktrees(cwd) {
5423
+ async function getWorktrees(cwd) {
5374
5424
  try {
5375
- const out = runGit2(["worktree", "list", "--porcelain"], cwd);
5425
+ const out = await runGit2(["worktree", "list", "--porcelain"], cwd);
5376
5426
  if (!out) return [];
5377
5427
  const entries = [];
5378
5428
  let current = {};
@@ -5392,28 +5442,28 @@ function getWorktrees(cwd) {
5392
5442
  return [];
5393
5443
  }
5394
5444
  }
5395
- function simultaneousEditWarning(cwd) {
5396
- const worktrees = getWorktrees(cwd);
5445
+ async function simultaneousEditWarning(cwd) {
5446
+ const worktrees = await getWorktrees(cwd);
5397
5447
  if (worktrees.length > 1) {
5398
5448
  const otherBranches = worktrees.filter((wt) => wt.branch).map((wt) => wt.branch.replace("refs/heads/", ""));
5399
5449
  return `\u26A0 Simultaneous edits detected: ${worktrees.length} active worktrees (${otherBranches.join(", ")}). Changes from other agents may mix into this commit. Consider using worktree isolation or verifying the diff below before committing.`;
5400
5450
  }
5401
5451
  return null;
5402
5452
  }
5403
- function getStagedDiff(cwd) {
5453
+ async function getStagedDiff(cwd) {
5404
5454
  try {
5405
- const stat4 = runGit2(["diff", "--cached", "--stat"], cwd);
5406
- const diff = runGit2(["diff", "--cached"], cwd);
5455
+ const stat5 = await runGit2(["diff", "--cached", "--stat"], cwd);
5456
+ const diff = await runGit2(["diff", "--cached"], cwd);
5407
5457
  const MAX_DIFF = 2e4;
5408
5458
  const truncated = diff.length > MAX_DIFF ? diff.slice(0, MAX_DIFF) + "\n\n... (diff truncated)" : diff;
5409
- return { stat: stat4 || "(no stat)", diff: truncated || "(clean)" };
5459
+ return { stat: stat5 || "(no stat)", diff: truncated || "(clean)" };
5410
5460
  } catch {
5411
5461
  return { stat: "(unavailable)", diff: "(unavailable)" };
5412
5462
  }
5413
5463
  }
5414
- function externalChangesSinceStage(cwd) {
5464
+ async function externalChangesSinceStage(cwd) {
5415
5465
  try {
5416
- const out = runGit2(["status", "--porcelain"], cwd);
5466
+ const out = await runGit2(["status", "--porcelain"], cwd);
5417
5467
  if (!out) return null;
5418
5468
  const unstaged = out.split("\n").filter((l) => l.trim()).filter((l) => {
5419
5469
  const idx = l[0] ?? " ";
@@ -5444,14 +5494,14 @@ var VALID_TYPES = [
5444
5494
  "build",
5445
5495
  "revert"
5446
5496
  ];
5447
- async function generateCommitFromDiff(api, stat4, diff) {
5497
+ async function generateCommitFromDiff(api, stat5, diff) {
5448
5498
  if (!api.llm) return null;
5449
5499
  try {
5450
5500
  const result = await api.llm.complete(
5451
5501
  `Write a Conventional Commits message for this staged git diff. Respond with ONLY a JSON object of the form {"type": string, "scope": string, "summary": string, "body": string}. type is one of: ${VALID_TYPES.join(", ")}. scope is a short area (empty string if unclear). summary is an imperative, lower-case, <=72-char subject with no trailing period. body is an optional short explanation (empty string if not needed). No prose outside the JSON.
5452
5502
 
5453
5503
  Stat:
5454
- ${stat4}
5504
+ ${stat5}
5455
5505
 
5456
5506
  Diff:
5457
5507
  ${diff}`,
@@ -5588,7 +5638,7 @@ var plugin17 = {
5588
5638
  }
5589
5639
  if (files && files.length > 0) {
5590
5640
  try {
5591
- stageFiles(files);
5641
+ await stageFiles(files);
5592
5642
  } catch (err) {
5593
5643
  return {
5594
5644
  ok: false,
@@ -5598,20 +5648,20 @@ var plugin17 = {
5598
5648
  }
5599
5649
  let staged = [];
5600
5650
  try {
5601
- staged = getStagedFiles();
5651
+ staged = await getStagedFiles();
5602
5652
  } catch {
5603
5653
  staged = [];
5604
5654
  }
5605
5655
  if (staged.length === 0) {
5606
5656
  try {
5607
- const changed = getChangedFiles();
5657
+ const changed = await getChangedFiles();
5608
5658
  if (changed.length > 0) {
5609
5659
  try {
5610
- stageFiles(changed);
5660
+ await stageFiles(changed);
5611
5661
  } catch {
5612
5662
  }
5613
5663
  try {
5614
- staged = getStagedFiles();
5664
+ staged = await getStagedFiles();
5615
5665
  } catch {
5616
5666
  staged = [];
5617
5667
  }
@@ -5619,10 +5669,10 @@ var plugin17 = {
5619
5669
  } catch {
5620
5670
  }
5621
5671
  }
5622
- const { stat: stat4, diff: stagedDiff } = getStagedDiff();
5672
+ const { stat: stat5, diff: stagedDiff } = await getStagedDiff();
5623
5673
  let generatedByLlm = false;
5624
5674
  if (wantGenerate && staged.length > 0) {
5625
- const g = await generateCommitFromDiff(api, stat4, stagedDiff);
5675
+ const g = await generateCommitFromDiff(api, stat5, stagedDiff);
5626
5676
  if (g) {
5627
5677
  type = g.type;
5628
5678
  if (g.scope) scope = g.scope;
@@ -5665,8 +5715,8 @@ var plugin17 = {
5665
5715
  error: "Nothing staged. Add files with git add or provide files input."
5666
5716
  };
5667
5717
  }
5668
- const worktreeWarn = simultaneousEditWarning();
5669
- const externalChanges = externalChangesSinceStage();
5718
+ const worktreeWarn = await simultaneousEditWarning();
5719
+ const externalChanges = await externalChangesSinceStage();
5670
5720
  let externalWarning = null;
5671
5721
  if (externalChanges && externalChanges.length > 0) {
5672
5722
  const preview = externalChanges.slice(0, 10).join(", ");
@@ -5683,7 +5733,7 @@ var plugin17 = {
5683
5733
  stagedDiff: `
5684
5734
  ## Staged changes (dry run)
5685
5735
 
5686
- ${stat4}
5736
+ ${stat5}
5687
5737
 
5688
5738
  \`\`\`diff
5689
5739
  ${stagedDiff}
@@ -5691,15 +5741,15 @@ ${stagedDiff}
5691
5741
  };
5692
5742
  }
5693
5743
  let preCommitDiff = stagedDiff;
5694
- let preCommitStat = stat4;
5744
+ let preCommitStat = stat5;
5695
5745
  if (staged.length === 0) {
5696
- const fresh = getStagedDiff();
5746
+ const fresh = await getStagedDiff();
5697
5747
  preCommitDiff = fresh.diff;
5698
5748
  preCommitStat = fresh.stat;
5699
5749
  }
5700
5750
  let hash = "";
5701
5751
  try {
5702
- hash = commitWithMessage(msg);
5752
+ hash = await commitWithMessage(msg);
5703
5753
  } catch (err) {
5704
5754
  return {
5705
5755
  ok: false,
@@ -5784,7 +5834,7 @@ var git_autocommit_default = plugin17;
5784
5834
 
5785
5835
  // src/import-organizer/index.ts
5786
5836
  import { spawn } from "node:child_process";
5787
- import { existsSync as existsSync4, statSync as statSync5 } from "node:fs";
5837
+ import { existsSync as existsSync3, statSync as statSync4 } from "node:fs";
5788
5838
  import { basename as basename2, isAbsolute as isAbsolute7 } from "node:path";
5789
5839
  var ALLOWED_FIRST_TOKENS = /* @__PURE__ */ new Set([
5790
5840
  "npx",
@@ -5883,10 +5933,10 @@ function runCommand(command, args, timeoutMs, cwd) {
5883
5933
  }
5884
5934
  async function organizeImports(filePath, cfg, cwd) {
5885
5935
  if (!withinProject(filePath)) return null;
5886
- if (!existsSync4(filePath)) return null;
5936
+ if (!existsSync3(filePath)) return null;
5887
5937
  let bytesBefore;
5888
5938
  try {
5889
- bytesBefore = statSync5(filePath).size;
5939
+ bytesBefore = statSync4(filePath).size;
5890
5940
  } catch {
5891
5941
  return null;
5892
5942
  }
@@ -5907,7 +5957,7 @@ async function organizeImports(filePath, cfg, cwd) {
5907
5957
  if (result.code === 127) return null;
5908
5958
  let bytesAfter;
5909
5959
  try {
5910
- bytesAfter = statSync5(filePath).size;
5960
+ bytesAfter = statSync4(filePath).size;
5911
5961
  } catch {
5912
5962
  return null;
5913
5963
  }
@@ -6632,7 +6682,7 @@ var plugin20 = {
6632
6682
  var knowledge_graph_default = plugin20;
6633
6683
 
6634
6684
  // src/lint-gate/index.ts
6635
- import { execFile as execFile3 } from "node:child_process";
6685
+ import { execFile as execFile5 } from "node:child_process";
6636
6686
  import { readFileSync as readFileSync6 } from "node:fs";
6637
6687
  import { mkdtemp, readFile as readFile2, rm, writeFile } from "node:fs/promises";
6638
6688
  import { createRequire } from "node:module";
@@ -6702,7 +6752,7 @@ function resolveLocalLinter(name, cwd) {
6702
6752
  function runCommand2(command, args, timeoutMs, cwd, signal) {
6703
6753
  return new Promise((resolveResult) => {
6704
6754
  try {
6705
- execFile3(
6755
+ execFile5(
6706
6756
  command,
6707
6757
  args,
6708
6758
  {
@@ -7345,7 +7395,7 @@ var plugin22 = {
7345
7395
  var llm_cache_default = plugin22;
7346
7396
 
7347
7397
  // src/loop-breaker/index.ts
7348
- import { execFile as execFile4 } from "node:child_process";
7398
+ import { execFile as execFile6 } from "node:child_process";
7349
7399
  var state20 = {
7350
7400
  lastFingerprint: null,
7351
7401
  streak: 0,
@@ -7442,7 +7492,7 @@ function hashString2(value) {
7442
7492
  async function gitDiffFingerprint(cwd, signal) {
7443
7493
  try {
7444
7494
  const diff = await new Promise((resolve28, reject) => {
7445
- execFile4(
7495
+ execFile6(
7446
7496
  "git",
7447
7497
  ["diff", "--no-ext-diff", "--"],
7448
7498
  {
@@ -8010,15 +8060,134 @@ var plugin24 = {
8010
8060
  var model_router_default = plugin24;
8011
8061
 
8012
8062
  // src/notify-hub/index.ts
8063
+ import { lookup } from "node:dns/promises";
8064
+
8065
+ // src/notify-hub/webhook-channel.ts
8066
+ function freshCircuit() {
8067
+ return { open: false, consecutiveFailures: 0 };
8068
+ }
8069
+ var WebhookNotificationChannel = class {
8070
+ name = "webhook";
8071
+ type = "webhook";
8072
+ #url;
8073
+ #headers;
8074
+ #timeoutMs;
8075
+ #maxFailures;
8076
+ #circuit;
8077
+ /** Total deliveries attempted (across all resets). */
8078
+ #totalAttempted = 0;
8079
+ /** Total successful deliveries. */
8080
+ #totalDelivered = 0;
8081
+ /** Total failed deliveries. */
8082
+ #totalFailed = 0;
8083
+ /** Total suppressed by circuit breaker. */
8084
+ #totalSuppressed = 0;
8085
+ constructor(opts) {
8086
+ this.#url = opts.webhookUrl;
8087
+ this.#headers = opts.headers ?? {};
8088
+ this.#timeoutMs = opts.timeoutMs ?? 5e3;
8089
+ this.#maxFailures = opts.maxConsecutiveFailures ?? 5;
8090
+ this.#circuit = freshCircuit();
8091
+ }
8092
+ // -----------------------------------------------------------------------
8093
+ // NotificationChannel
8094
+ // -----------------------------------------------------------------------
8095
+ async deliver(msg) {
8096
+ const deliveredAt = (/* @__PURE__ */ new Date()).toISOString();
8097
+ if (this.#circuit.open && this.#maxFailures > 0) {
8098
+ this.#totalSuppressed += 1;
8099
+ return {
8100
+ ok: false,
8101
+ channel: this.name,
8102
+ error: `circuit open after ${this.#circuit.consecutiveFailures} consecutive failures`,
8103
+ deliveredAt
8104
+ };
8105
+ }
8106
+ this.#totalAttempted += 1;
8107
+ const body = JSON.stringify({
8108
+ source: "wrongstack/notify-hub",
8109
+ event: msg.source,
8110
+ ts: deliveredAt,
8111
+ title: msg.title,
8112
+ message: msg.body,
8113
+ level: msg.level,
8114
+ ...msg.metadata ?? {}
8115
+ });
8116
+ try {
8117
+ const controller = new AbortController();
8118
+ const timer = setTimeout(() => controller.abort(), this.#timeoutMs);
8119
+ try {
8120
+ const res = await fetch(this.#url, {
8121
+ method: "POST",
8122
+ headers: { "content-type": "application/json", ...this.#headers },
8123
+ body,
8124
+ signal: controller.signal
8125
+ });
8126
+ if (!res.ok) throw new Error(`webhook responded ${res.status}`);
8127
+ } finally {
8128
+ clearTimeout(timer);
8129
+ }
8130
+ this.#totalDelivered += 1;
8131
+ this.#circuit.consecutiveFailures = 0;
8132
+ this.#circuit.open = false;
8133
+ return { ok: true, channel: this.name, deliveredAt };
8134
+ } catch (err) {
8135
+ this.#totalFailed += 1;
8136
+ this.#circuit.consecutiveFailures += 1;
8137
+ if (this.#maxFailures > 0 && this.#circuit.consecutiveFailures >= this.#maxFailures) {
8138
+ this.#circuit.open = true;
8139
+ }
8140
+ return {
8141
+ ok: false,
8142
+ channel: this.name,
8143
+ error: err instanceof Error ? err.message : String(err),
8144
+ deliveredAt
8145
+ };
8146
+ }
8147
+ }
8148
+ async ping() {
8149
+ if (!this.#url) {
8150
+ return { ok: false, error: "no webhookUrl configured" };
8151
+ }
8152
+ if (this.#circuit.open) {
8153
+ return {
8154
+ ok: false,
8155
+ error: `circuit open after ${this.#circuit.consecutiveFailures} consecutive failures`
8156
+ };
8157
+ }
8158
+ return { ok: true };
8159
+ }
8160
+ // -----------------------------------------------------------------------
8161
+ // Diagnostics (not part of NotificationChannel)
8162
+ // -----------------------------------------------------------------------
8163
+ /** Reset the circuit breaker (e.g. after config update). */
8164
+ resetCircuit() {
8165
+ this.#circuit.open = false;
8166
+ this.#circuit.consecutiveFailures = 0;
8167
+ }
8168
+ /** Current circuit breaker state. */
8169
+ circuitStatus() {
8170
+ return { open: this.#circuit.open, consecutiveFailures: this.#circuit.consecutiveFailures };
8171
+ }
8172
+ /** Lifetime delivery counters (never reset). */
8173
+ counters() {
8174
+ return {
8175
+ attempted: this.#totalAttempted,
8176
+ delivered: this.#totalDelivered,
8177
+ failed: this.#totalFailed,
8178
+ suppressed: this.#totalSuppressed
8179
+ };
8180
+ }
8181
+ };
8182
+
8183
+ // src/notify-hub/index.ts
8184
+ var _pluginLog = null;
8013
8185
  var state22 = {
8014
- sent: 0,
8015
- failed: 0,
8016
- suppressed: 0,
8017
- consecutiveFailures: 0,
8018
- circuitOpen: false,
8186
+ channel: null,
8019
8187
  lastDelivery: null,
8020
8188
  stopHookUnregister: null,
8021
- eventUnsubscribers: []
8189
+ eventUnsubscribers: [],
8190
+ circuitWarned: false
8022
8191
  };
8023
8192
  var KNOWN_EVENTS = ["session.stop", "tool.error", "budget.threshold"];
8024
8193
  var DEFAULTS19 = {
@@ -8041,6 +8210,21 @@ function isBlockedHostname(hostname) {
8041
8210
  const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
8042
8211
  return h === "localhost" || h.endsWith(".localhost") || h === "::1" || h === "0:0:0:0:0:0:0:1" || h.startsWith("fc") || h.startsWith("fd") || h.startsWith("fe80:") || isPrivateIPv4(h);
8043
8212
  }
8213
+ async function hasPrivateResolvedIP(hostname) {
8214
+ try {
8215
+ const entries = await lookup(hostname, { all: true, verbatim: true });
8216
+ for (const { address } of entries) {
8217
+ if (isPrivateIPv4(address)) return true;
8218
+ const lower = address.toLowerCase();
8219
+ if (lower === "::1" || lower === "0:0:0:0:0:0:0:1" || lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:")) {
8220
+ return true;
8221
+ }
8222
+ }
8223
+ return false;
8224
+ } catch {
8225
+ return false;
8226
+ }
8227
+ }
8044
8228
  function normalizeWebhookUrl(raw) {
8045
8229
  if (typeof raw !== "string" || raw.trim().length === 0) return "";
8046
8230
  try {
@@ -8072,49 +8256,37 @@ function readConfig20(raw) {
8072
8256
  maxConsecutiveFailures: typeof r["maxConsecutiveFailures"] === "number" && r["maxConsecutiveFailures"] >= 1 ? r["maxConsecutiveFailures"] : DEFAULTS19.maxConsecutiveFailures
8073
8257
  };
8074
8258
  }
8075
- async function deliver(cfg, event, payload, log) {
8076
- if (!cfg.webhookUrl) return false;
8077
- if (state22.circuitOpen) {
8078
- state22.suppressed += 1;
8259
+ async function deliver(event, payload) {
8260
+ const ch = state22.channel;
8261
+ if (!ch) {
8079
8262
  return false;
8080
8263
  }
8081
- const body = JSON.stringify({
8082
- source: "wrongstack/notify-hub",
8083
- event,
8084
- ts: (/* @__PURE__ */ new Date()).toISOString(),
8085
- ...payload
8264
+ const result = await deliverViaChannel(ch, event, {
8265
+ title: typeof payload.title === "string" ? payload.title : event,
8266
+ body: typeof payload.message === "string" ? payload.message : typeof payload.error === "string" ? payload.error : JSON.stringify(payload),
8267
+ level: event === "tool.error" || event === "budget.threshold" ? "warning" : "info",
8268
+ source: event,
8269
+ metadata: payload
8086
8270
  });
8087
- try {
8088
- const controller = new AbortController();
8089
- const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
8090
- try {
8091
- const res = await fetch(cfg.webhookUrl, {
8092
- method: "POST",
8093
- headers: { "content-type": "application/json", ...cfg.headers },
8094
- body,
8095
- signal: controller.signal
8096
- });
8097
- if (!res.ok) throw new Error(`webhook responded ${res.status}`);
8098
- } finally {
8099
- clearTimeout(timer);
8100
- }
8101
- state22.sent += 1;
8102
- state22.consecutiveFailures = 0;
8271
+ return result.ok;
8272
+ }
8273
+ async function deliverViaChannel(ch, event, msg) {
8274
+ const result = await ch.deliver(msg);
8275
+ if (result.ok) {
8276
+ state22.circuitWarned = false;
8103
8277
  state22.lastDelivery = { event, ok: true, when: (/* @__PURE__ */ new Date()).toISOString() };
8104
- return true;
8105
- } catch (err) {
8106
- state22.failed += 1;
8107
- state22.consecutiveFailures += 1;
8278
+ } else {
8108
8279
  state22.lastDelivery = { event, ok: false, when: (/* @__PURE__ */ new Date()).toISOString() };
8109
- if (state22.consecutiveFailures >= cfg.maxConsecutiveFailures) {
8110
- state22.circuitOpen = true;
8111
- log.warn(
8112
- `notify-hub: ${state22.consecutiveFailures} consecutive delivery failures \u2014 circuit opened, further notifications suppressed`,
8113
- { error: err instanceof Error ? err.message : String(err) }
8280
+ const cs = ch.circuitStatus();
8281
+ if (cs.open && !state22.circuitWarned) {
8282
+ state22.circuitWarned = true;
8283
+ _pluginLog?.warn(
8284
+ `notify-hub: ${cs.consecutiveFailures} consecutive delivery failures \u2014 circuit opened, further notifications suppressed`,
8285
+ { consecutiveFailures: cs.consecutiveFailures, event }
8114
8286
  );
8115
8287
  }
8116
- return false;
8117
8288
  }
8289
+ return result;
8118
8290
  }
8119
8291
  function truncateText(s, max) {
8120
8292
  return s.length > max ? `${s.slice(0, max)}\u2026` : s;
@@ -8161,12 +8333,10 @@ var plugin25 = {
8161
8333
  }
8162
8334
  }
8163
8335
  },
8164
- setup(api) {
8165
- state22.sent = 0;
8166
- state22.failed = 0;
8167
- state22.suppressed = 0;
8168
- state22.consecutiveFailures = 0;
8169
- state22.circuitOpen = false;
8336
+ async setup(api) {
8337
+ _pluginLog = api.log;
8338
+ state22.circuitWarned = false;
8339
+ state22.channel = null;
8170
8340
  state22.lastDelivery = null;
8171
8341
  if (state22.stopHookUnregister) {
8172
8342
  try {
@@ -8183,15 +8353,35 @@ var plugin25 = {
8183
8353
  }
8184
8354
  state22.eventUnsubscribers = [];
8185
8355
  const cfg = readConfig20(api.config.extensions?.["notify-hub"]);
8186
- const active = cfg.enabled && cfg.webhookUrl.length > 0;
8356
+ let active = cfg.enabled && cfg.webhookUrl.length > 0;
8357
+ if (active) {
8358
+ try {
8359
+ const url = new URL(cfg.webhookUrl);
8360
+ const hostnameBlocked = await hasPrivateResolvedIP(url.hostname);
8361
+ if (hostnameBlocked) {
8362
+ api.log.warn(
8363
+ `notify-hub: webhook URL resolves to a private/local IP (${url.hostname}) \u2014 disabling plugin`
8364
+ );
8365
+ active = false;
8366
+ }
8367
+ } catch {
8368
+ }
8369
+ }
8370
+ if (active) {
8371
+ state22.channel = new WebhookNotificationChannel({
8372
+ webhookUrl: cfg.webhookUrl,
8373
+ headers: cfg.headers,
8374
+ timeoutMs: cfg.timeoutMs,
8375
+ maxConsecutiveFailures: cfg.maxConsecutiveFailures
8376
+ });
8377
+ api.notifier?.registerChannel(state22.channel);
8378
+ }
8187
8379
  if (active && cfg.events.includes("session.stop")) {
8188
8380
  const stopHook = (input) => {
8189
- void deliver(
8190
- cfg,
8191
- "session.stop",
8192
- { sessionId: input.sessionId ?? null, cwd: input.cwd ?? null },
8193
- api.log
8194
- );
8381
+ void deliver("session.stop", {
8382
+ sessionId: input.sessionId ?? null,
8383
+ cwd: input.cwd ?? null
8384
+ });
8195
8385
  };
8196
8386
  state22.stopHookUnregister = api.registerHook("Stop", void 0, stopHook);
8197
8387
  }
@@ -8199,26 +8389,21 @@ var plugin25 = {
8199
8389
  const off = api.onPattern("tool.*", (eventName, payload) => {
8200
8390
  if (!/error|failed/.test(eventName)) return;
8201
8391
  const p = payload;
8202
- void deliver(
8203
- cfg,
8204
- "tool.error",
8205
- {
8206
- tool: p?.tool ?? p?.name ?? "unknown",
8207
- busEvent: eventName,
8208
- error: truncateText(
8209
- p?.error instanceof Error ? p.error.message : String(p?.error ?? ""),
8210
- 500
8211
- )
8212
- },
8213
- api.log
8214
- );
8392
+ void deliver("tool.error", {
8393
+ tool: p?.tool ?? p?.name ?? "unknown",
8394
+ busEvent: eventName,
8395
+ error: truncateText(
8396
+ p?.error instanceof Error ? p.error.message : String(p?.error ?? ""),
8397
+ 500
8398
+ )
8399
+ });
8215
8400
  });
8216
8401
  state22.eventUnsubscribers.push(off);
8217
8402
  }
8218
8403
  if (active && cfg.events.includes("budget.threshold")) {
8219
8404
  const off = api.onPattern("budget.*", (eventName, payload) => {
8220
8405
  if (!eventName.includes("threshold")) return;
8221
- void deliver(cfg, "budget.threshold", { busEvent: eventName, detail: payload }, api.log);
8406
+ void deliver("budget.threshold", { busEvent: eventName, detail: payload });
8222
8407
  });
8223
8408
  state22.eventUnsubscribers.push(off);
8224
8409
  }
@@ -8243,26 +8428,23 @@ var plugin25 = {
8243
8428
  mutating: true,
8244
8429
  async execute(input) {
8245
8430
  if (!cfg.enabled) return { ok: false, error: "notify-hub is disabled" };
8246
- if (!cfg.webhookUrl) {
8431
+ const ch = state22.channel;
8432
+ if (!ch) {
8247
8433
  return {
8248
8434
  ok: false,
8249
8435
  error: 'no webhookUrl configured \u2014 set config.extensions["notify-hub"].webhookUrl to enable deliveries'
8250
8436
  };
8251
8437
  }
8252
- const delivered = await deliver(
8253
- cfg,
8254
- "manual",
8255
- {
8256
- title: truncateText(String(input.title ?? "WrongStack notification"), 200),
8257
- message: truncateText(String(input.message ?? ""), 2e3),
8258
- level: input.level === "warning" || input.level === "critical" ? input.level : "info"
8259
- },
8260
- api.log
8261
- );
8438
+ const result = await deliverViaChannel(ch, "manual", {
8439
+ title: truncateText(String(input.title ?? "WrongStack notification"), 200),
8440
+ body: truncateText(String(input.message ?? ""), 2e3),
8441
+ level: input.level === "warning" || input.level === "critical" ? input.level : "info",
8442
+ source: "manual"
8443
+ });
8262
8444
  return {
8263
- ok: delivered,
8264
- circuitOpen: state22.circuitOpen,
8265
- ...delivered ? {} : { error: "delivery failed (see notify_hub_status)" }
8445
+ ok: result.ok,
8446
+ circuitOpen: ch.circuitStatus().open,
8447
+ ...result.ok ? {} : { error: result.error ?? "delivery failed" }
8266
8448
  };
8267
8449
  }
8268
8450
  });
@@ -8274,18 +8456,26 @@ var plugin25 = {
8274
8456
  category: "Diagnostics",
8275
8457
  mutating: false,
8276
8458
  async execute() {
8459
+ const ch = state22.channel;
8460
+ const counters = ch?.counters() ?? {
8461
+ attempted: 0,
8462
+ delivered: 0,
8463
+ failed: 0,
8464
+ suppressed: 0
8465
+ };
8277
8466
  return {
8278
8467
  ok: true,
8279
8468
  enabled: cfg.enabled,
8280
8469
  webhookConfigured: cfg.webhookUrl.length > 0,
8281
8470
  events: cfg.events,
8282
8471
  timeoutMs: cfg.timeoutMs,
8283
- circuitOpen: state22.circuitOpen,
8472
+ circuitOpen: ch?.circuitStatus().open ?? false,
8284
8473
  counters: {
8285
- sent: state22.sent,
8286
- failed: state22.failed,
8287
- suppressed: state22.suppressed,
8288
- consecutiveFailures: state22.consecutiveFailures
8474
+ sent: counters.delivered,
8475
+ failed: counters.failed,
8476
+ suppressed: counters.suppressed,
8477
+ consecutiveFailures: ch?.circuitStatus().consecutiveFailures ?? 0,
8478
+ totalAttempted: counters.attempted
8289
8479
  },
8290
8480
  lastDelivery: state22.lastDelivery
8291
8481
  };
@@ -8299,6 +8489,8 @@ var plugin25 = {
8299
8489
  });
8300
8490
  },
8301
8491
  teardown(api) {
8492
+ _pluginLog = null;
8493
+ state22.circuitWarned = false;
8302
8494
  if (state22.stopHookUnregister) {
8303
8495
  try {
8304
8496
  state22.stopHookUnregister();
@@ -8313,25 +8505,22 @@ var plugin25 = {
8313
8505
  }
8314
8506
  }
8315
8507
  state22.eventUnsubscribers = [];
8316
- const final = { sent: state22.sent, failed: state22.failed, suppressed: state22.suppressed };
8317
- state22.sent = 0;
8318
- state22.failed = 0;
8319
- state22.suppressed = 0;
8320
- state22.consecutiveFailures = 0;
8321
- state22.circuitOpen = false;
8508
+ const channelCounters = state22.channel?.counters();
8509
+ state22.channel = null;
8322
8510
  state22.lastDelivery = null;
8323
- api.log.info("notify-hub: teardown complete", { final });
8511
+ api.log.info("notify-hub: teardown complete", { channelCounters });
8324
8512
  },
8325
8513
  async health() {
8514
+ const ch = state22.channel;
8515
+ const cs = ch?.circuitStatus();
8516
+ const channelCounters = ch?.counters();
8517
+ const sent = channelCounters?.delivered ?? 0;
8518
+ const failed = channelCounters?.failed ?? 0;
8519
+ const suppressed = channelCounters?.suppressed ?? 0;
8326
8520
  return {
8327
- ok: !state22.circuitOpen,
8328
- message: state22.circuitOpen ? `notify-hub: circuit OPEN after ${state22.consecutiveFailures} consecutive failures \u2014 deliveries suppressed` : `notify-hub: ${state22.sent} sent, ${state22.failed} failed, ${state22.suppressed} suppressed`,
8329
- counters: {
8330
- sent: state22.sent,
8331
- failed: state22.failed,
8332
- suppressed: state22.suppressed,
8333
- consecutiveFailures: state22.consecutiveFailures
8334
- }
8521
+ ok: !cs?.open,
8522
+ message: cs?.open ? `notify-hub: circuit OPEN after ${cs.consecutiveFailures} consecutive failures \u2014 deliveries suppressed` : ch ? `notify-hub: ${sent} sent, ${failed} failed, ${suppressed} suppressed` : `notify-hub: idle \u2014 no webhook channel configured`,
8523
+ counters: { sent, failed, suppressed, consecutiveFailures: cs?.consecutiveFailures ?? 0 }
8335
8524
  };
8336
8525
  }
8337
8526
  };
@@ -8862,7 +9051,7 @@ function readConfig23(raw) {
8862
9051
  var plugin_stack_observer_default = PLUGIN;
8863
9052
 
8864
9053
  // src/pr-drafter/index.ts
8865
- import { execFileSync as execFileSync4 } from "node:child_process";
9054
+ import { execFileSync as execFileSync2 } from "node:child_process";
8866
9055
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
8867
9056
  import { dirname as dirname5, isAbsolute as isAbsolute10, relative as relative9, resolve as resolve9 } from "node:path";
8868
9057
  var API_VERSION13 = "^0.1.10";
@@ -8911,7 +9100,7 @@ function resolveProjectPath6(rawPath, cwd = process.cwd()) {
8911
9100
  }
8912
9101
  function gitBranch() {
8913
9102
  try {
8914
- const out = execFileSync4("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
9103
+ const out = execFileSync2("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
8915
9104
  encoding: "utf-8",
8916
9105
  cwd: process.cwd(),
8917
9106
  stdio: ["pipe", "pipe", "ignore"],
@@ -8924,7 +9113,7 @@ function gitBranch() {
8924
9113
  }
8925
9114
  function gitDiff() {
8926
9115
  try {
8927
- return execFileSync4("git", ["diff", "--stat"], {
9116
+ return execFileSync2("git", ["diff", "--stat"], {
8928
9117
  encoding: "utf-8",
8929
9118
  cwd: process.cwd(),
8930
9119
  stdio: ["pipe", "pipe", "ignore"],
@@ -9924,8 +10113,8 @@ var secret_scanner_default = plugin30;
9924
10113
  // src/semver-bump/index.ts
9925
10114
  import { expectDefined as expectDefined2 } from "@wrongstack/core";
9926
10115
  import { toErrorMessage } from "@wrongstack/core/utils";
9927
- import { execFileSync as execFileSync5 } from "node:child_process";
9928
- import { existsSync as existsSync5, readFileSync as readFileSync7, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
10116
+ import { execFileSync as execFileSync3 } from "node:child_process";
10117
+ import { existsSync as existsSync4, readFileSync as readFileSync7, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
9929
10118
  import { isAbsolute as isAbsolute11, join as join3, relative as relative10, resolve as resolve10 } from "node:path";
9930
10119
  var API_VERSION14 = "^0.1.10";
9931
10120
  function resolveProjectRoot(rawCwd, root = process.cwd()) {
@@ -9946,7 +10135,7 @@ var state29 = {
9946
10135
  };
9947
10136
  function runGit3(args, cwd) {
9948
10137
  try {
9949
- return execFileSync5("git", args, {
10138
+ return execFileSync3("git", args, {
9950
10139
  encoding: "utf-8",
9951
10140
  cwd,
9952
10141
  stdio: ["pipe", "pipe", "pipe"],
@@ -9961,7 +10150,7 @@ function runGit3(args, cwd) {
9961
10150
  }
9962
10151
  function getPackageJson(cwd) {
9963
10152
  const path = cwd ? `${cwd}/package.json` : "package.json";
9964
- if (!existsSync5(path)) return null;
10153
+ if (!existsSync4(path)) return null;
9965
10154
  try {
9966
10155
  return JSON.parse(readFileSync7(path, "utf-8"));
9967
10156
  } catch {
@@ -9971,14 +10160,14 @@ function getPackageJson(cwd) {
9971
10160
  function collectManifests(root) {
9972
10161
  const paths = [];
9973
10162
  const rootPkg = join3(root, "package.json");
9974
- if (existsSync5(rootPkg)) paths.push(rootPkg);
10163
+ if (existsSync4(rootPkg)) paths.push(rootPkg);
9975
10164
  for (const group of ["packages", "apps"]) {
9976
10165
  const groupDir = join3(root, group);
9977
- if (!existsSync5(groupDir)) continue;
10166
+ if (!existsSync4(groupDir)) continue;
9978
10167
  for (const entry of readdirSync2(groupDir, { withFileTypes: true })) {
9979
10168
  if (!entry.isDirectory()) continue;
9980
10169
  const candidate = join3(groupDir, entry.name, "package.json");
9981
- if (existsSync5(candidate)) paths.push(candidate);
10170
+ if (existsSync4(candidate)) paths.push(candidate);
9982
10171
  }
9983
10172
  }
9984
10173
  return paths;
@@ -10170,9 +10359,9 @@ var plugin31 = {
10170
10359
  const root = cwd ?? process.cwd();
10171
10360
  const bumpScript = join3(root, "scripts", "bump-version.mjs");
10172
10361
  const changed = collectManifests(root);
10173
- if (existsSync5(bumpScript)) {
10362
+ if (existsSync4(bumpScript)) {
10174
10363
  try {
10175
- execFileSync5(process.execPath, [bumpScript, "set", newVersion], {
10364
+ execFileSync3(process.execPath, [bumpScript, "set", newVersion], {
10176
10365
  cwd: root,
10177
10366
  stdio: ["pipe", "pipe", "pipe"],
10178
10367
  timeout: 3e4,
@@ -10184,7 +10373,7 @@ var plugin31 = {
10184
10373
  }
10185
10374
  for (const rel of ["package.json", "package-lock.json", "src/lib/utils.ts", "index.html"]) {
10186
10375
  const p = join3(root, "website", rel);
10187
- if (existsSync5(p)) changed.push(p);
10376
+ if (existsSync4(p)) changed.push(p);
10188
10377
  }
10189
10378
  } else {
10190
10379
  for (const manifest of changed) {
@@ -10862,8 +11051,8 @@ Tokens: ${recap.tokens.total.input} in / ${recap.tokens.total.output} out
10862
11051
  var session_recap_default = plugin32;
10863
11052
 
10864
11053
  // src/shell-check/index.ts
10865
- import { execFileSync as execFileSync6, execSync as execSync2 } from "node:child_process";
10866
- import { existsSync as existsSync6, readdirSync as readdirSync3 } from "node:fs";
11054
+ import { execFile as execFile7 } from "node:child_process";
11055
+ import { readdir } from "node:fs/promises";
10867
11056
  import { isAbsolute as isAbsolute12, join as join4, relative as relative11, resolve as resolve11 } from "node:path";
10868
11057
  var API_VERSION15 = "^0.1.10";
10869
11058
  function withinProject3(p) {
@@ -10884,15 +11073,23 @@ var state31 = {
10884
11073
  /** Most recent run summary, surfaced by health(). */
10885
11074
  lastRun: null
10886
11075
  };
10887
- function runShellCheck(files, severity, cwd) {
10888
- if (!existsSync6("shellcheck")) {
10889
- try {
10890
- execSync2("shellcheck --version", { encoding: "utf-8", stdio: "ignore", windowsHide: true });
10891
- } catch {
10892
- throw new Error(
10893
- "shellcheck is not installed. Install via: apt install shellcheck / brew install shellcheck"
11076
+ async function runShellCheck(files, severity, cwd) {
11077
+ try {
11078
+ await new Promise((resolvePromise, rejectPromise) => {
11079
+ execFile7(
11080
+ "shellcheck",
11081
+ ["--version"],
11082
+ { encoding: "utf-8", windowsHide: true },
11083
+ (err) => {
11084
+ if (err) rejectPromise(err);
11085
+ else resolvePromise();
11086
+ }
10894
11087
  );
10895
- }
11088
+ });
11089
+ } catch {
11090
+ throw new Error(
11091
+ "shellcheck is not installed. Install via: apt install shellcheck / brew install shellcheck"
11092
+ );
10896
11093
  }
10897
11094
  const levelMap = {
10898
11095
  error: "error",
@@ -10904,20 +11101,34 @@ function runShellCheck(files, severity, cwd) {
10904
11101
  const args = ["-f", "json", "-S", severityFlag, ...files];
10905
11102
  let raw;
10906
11103
  try {
10907
- raw = execFileSync6("shellcheck", args, {
10908
- encoding: "utf-8",
10909
- cwd,
10910
- stdio: ["pipe", "pipe", "pipe"],
10911
- timeout: 6e4,
10912
- windowsHide: true
11104
+ raw = await new Promise((resolvePromise, rejectPromise) => {
11105
+ execFile7(
11106
+ "shellcheck",
11107
+ args,
11108
+ {
11109
+ encoding: "utf-8",
11110
+ cwd,
11111
+ windowsHide: true,
11112
+ timeout: 6e4,
11113
+ maxBuffer: 16 * 1024 * 1024
11114
+ },
11115
+ (err, stdout, stderr) => {
11116
+ if (err) {
11117
+ const e = err;
11118
+ const diagnostic = stdout || e.stdout?.toString() || stderr || e.stderr?.toString() || "";
11119
+ if (diagnostic.trim()) {
11120
+ resolvePromise(diagnostic);
11121
+ return;
11122
+ }
11123
+ rejectPromise(err);
11124
+ return;
11125
+ }
11126
+ resolvePromise(stdout);
11127
+ }
11128
+ );
10913
11129
  });
10914
- } catch (err) {
10915
- const e = err;
10916
- if (e.stderr && !e.stderr.includes("shellcheck")) {
10917
- raw = e.stderr;
10918
- } else {
10919
- return [];
10920
- }
11130
+ } catch {
11131
+ return [];
10921
11132
  }
10922
11133
  if (!raw.trim()) return [];
10923
11134
  try {
@@ -10934,21 +11145,23 @@ function runShellCheck(files, severity, cwd) {
10934
11145
  return [];
10935
11146
  }
10936
11147
  }
10937
- function findShellFiles(dir, pattern) {
11148
+ async function findShellFiles(dir, pattern) {
10938
11149
  const results = [];
11150
+ let entries;
10939
11151
  try {
10940
- const entries = readdirSync3(dir, { withFileTypes: true });
10941
- for (const entry of entries) {
10942
- const full = join4(dir, entry.name);
10943
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
10944
- results.push(...findShellFiles(full, pattern));
10945
- } else if (entry.isFile() && (entry.name.endsWith(".sh") || entry.name === "Dockerfile")) {
10946
- if (!pattern || entry.name.includes(pattern)) {
10947
- results.push(full);
10948
- }
11152
+ entries = await readdir(dir, { withFileTypes: true });
11153
+ } catch {
11154
+ return results;
11155
+ }
11156
+ for (const entry of entries) {
11157
+ const full = join4(dir, entry.name);
11158
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
11159
+ results.push(...await findShellFiles(full, pattern));
11160
+ } else if (entry.isFile() && (entry.name.endsWith(".sh") || entry.name === "Dockerfile")) {
11161
+ if (!pattern || entry.name.includes(pattern)) {
11162
+ results.push(full);
10949
11163
  }
10950
11164
  }
10951
- } catch {
10952
11165
  }
10953
11166
  return results;
10954
11167
  }
@@ -11049,7 +11262,7 @@ var plugin33 = {
11049
11262
  if (files && files.length > 0) {
11050
11263
  checkFiles = files;
11051
11264
  } else {
11052
- checkFiles = findShellFiles(directory, pattern);
11265
+ checkFiles = await findShellFiles(directory, pattern);
11053
11266
  scannedDirectories = true;
11054
11267
  }
11055
11268
  if (checkFiles.length === 0) {
@@ -11070,7 +11283,7 @@ var plugin33 = {
11070
11283
  }
11071
11284
  let issues;
11072
11285
  try {
11073
- issues = runShellCheck(checkFiles, severity);
11286
+ issues = await runShellCheck(checkFiles, severity);
11074
11287
  } catch (err) {
11075
11288
  const msg = err instanceof Error ? err.message : String(err);
11076
11289
  return { ok: false, error: msg, issues: [], filesScanned: 0 };
@@ -11709,8 +11922,8 @@ var plugin35 = {
11709
11922
  var test_coverage_gate_default = plugin35;
11710
11923
 
11711
11924
  // src/test-runner-gate/index.ts
11712
- import { execFile as execFile5 } from "node:child_process";
11713
- import { access } from "node:fs/promises";
11925
+ import { execFile as execFile8 } from "node:child_process";
11926
+ import { access as access2 } from "node:fs/promises";
11714
11927
  import { basename as basename3, dirname as dirname6, isAbsolute as isAbsolute15, join as join5 } from "node:path";
11715
11928
  var API_VERSION18 = "^0.1.10";
11716
11929
  var state33 = {
@@ -11813,7 +12026,7 @@ async function findTestFile(sourcePath, patterns) {
11813
12026
  const candidates = resolveTestFiles(sourcePath, patterns);
11814
12027
  for (const candidate of candidates) {
11815
12028
  try {
11816
- await access(candidate);
12029
+ await access2(candidate);
11817
12030
  return candidate;
11818
12031
  } catch {
11819
12032
  }
@@ -11831,7 +12044,7 @@ async function detectRunner(requested) {
11831
12044
  if (!match) return null;
11832
12045
  try {
11833
12046
  await new Promise((resolve28, reject) => {
11834
- execFile5("npx", [`${match.name}`, "--version"], {
12047
+ execFile8("npx", [`${match.name}`, "--version"], {
11835
12048
  encoding: "utf-8",
11836
12049
  timeout: 5e3,
11837
12050
  cwd: process.cwd()
@@ -11845,7 +12058,7 @@ async function detectRunner(requested) {
11845
12058
  for (const candidate of candidates) {
11846
12059
  try {
11847
12060
  await new Promise((resolve28, reject) => {
11848
- execFile5("npx", [`${candidate.name}`, "--version"], {
12061
+ execFile8("npx", [`${candidate.name}`, "--version"], {
11849
12062
  encoding: "utf-8",
11850
12063
  timeout: 5e3,
11851
12064
  cwd: process.cwd()
@@ -11896,7 +12109,7 @@ async function runTests(testFile, runner, customCommand, timeoutMs) {
11896
12109
  try {
11897
12110
  const { stdout: out } = await new Promise(
11898
12111
  (resolve28, reject) => {
11899
- execFile5(
12112
+ execFile8(
11900
12113
  cmd,
11901
12114
  fullArgs,
11902
12115
  { encoding: "utf-8", timeout: timeoutMs, cwd: process.cwd() },
@@ -12198,8 +12411,7 @@ Fix the failing tests or revert the change if it broke something.`
12198
12411
  var test_runner_gate_default = plugin36;
12199
12412
 
12200
12413
  // src/type-gate/index.ts
12201
- import { execFileSync as execFileSync7 } from "node:child_process";
12202
- import { existsSync as existsSync7 } from "node:fs";
12414
+ import { existsSync as existsSync5 } from "node:fs";
12203
12415
  var API_VERSION19 = "^0.1.10";
12204
12416
  var state34 = {
12205
12417
  invocationCount: 0,
@@ -12253,7 +12465,7 @@ function validateTsConfigPath(p) {
12253
12465
  const sanitized = sanitizeRunnerPath(p);
12254
12466
  return sanitized;
12255
12467
  }
12256
- function runTypeCheck(cfg) {
12468
+ async function runTypeCheck(cfg) {
12257
12469
  const start = Date.now();
12258
12470
  const rawTsConfig = cfg.tsConfigPath;
12259
12471
  const validTsConfig = rawTsConfig && rawTsConfig !== "tsconfig.json" ? validateTsConfigPath(rawTsConfig) : null;
@@ -12267,32 +12479,22 @@ function runTypeCheck(cfg) {
12267
12479
  if (!resolved) return null;
12268
12480
  argv = [resolved.cmd, ...resolved.args];
12269
12481
  } else {
12270
- argv = ["npx", "tsc", "--noEmit"];
12482
+ argv = ["npx", "tsc", "--noEmit", "--incremental"];
12271
12483
  if (tsConfig && tsConfig !== "tsconfig.json") {
12272
12484
  argv = [...argv, "-p", tsConfig];
12273
12485
  }
12274
12486
  }
12275
- if (!cfg.command && tsConfig && !existsSync7(tsConfig)) {
12487
+ if (!cfg.command && tsConfig && !existsSync5(tsConfig)) {
12276
12488
  return null;
12277
12489
  }
12278
- let stdout = "";
12279
- let stderr = "";
12280
- try {
12281
- const out = execFileSync7(argv[0], argv.slice(1), {
12282
- encoding: "utf-8",
12283
- timeout: cfg.timeoutMs,
12284
- cwd: process.cwd(),
12285
- stdio: ["pipe", "pipe", "pipe"],
12286
- shell: false
12287
- });
12288
- stdout = out;
12289
- } catch (err) {
12290
- const e = err;
12291
- if (e.killed) return null;
12292
- stdout = e.stdout ?? "";
12293
- stderr = e.stderr ?? "";
12294
- }
12295
- void runRunnerCommand;
12490
+ const result = await runRunnerCommand([argv[0], ...argv.slice(1)], {
12491
+ cwd: process.cwd(),
12492
+ timeoutMs: cfg.timeoutMs
12493
+ });
12494
+ if (result.timedOut) return null;
12495
+ if (result.spawnError) return null;
12496
+ const stdout = result.stdout;
12497
+ const stderr = result.stderr;
12296
12498
  const combined = `${stdout}
12297
12499
  ${stderr}`.trim();
12298
12500
  const durationMs = Date.now() - start;
@@ -12375,7 +12577,7 @@ var plugin37 = {
12375
12577
  state34.lastResult = null;
12376
12578
  state34.hookUnregister = null;
12377
12579
  const cfg = readConfig30(api.config.extensions?.["type-gate"]);
12378
- const hook = (input) => {
12580
+ const hook = async (input) => {
12379
12581
  if (!cfg.enabled) return;
12380
12582
  if (input.toolResult?.isError) return;
12381
12583
  const inp = input.toolInput ?? {};
@@ -12388,7 +12590,7 @@ var plugin37 = {
12388
12590
  return;
12389
12591
  }
12390
12592
  state34.invocationCount += 1;
12391
- const result = runTypeCheck(cfg);
12593
+ const result = await runTypeCheck(cfg);
12392
12594
  if (!result) {
12393
12595
  state34.errorCount += 1;
12394
12596
  return;
@@ -13968,8 +14170,8 @@ var plugin42 = {
13968
14170
  var accessibility_auditor_default = plugin42;
13969
14171
 
13970
14172
  // src/api-compatibility-gate/index.ts
13971
- import { execFileSync as execFileSync8 } from "node:child_process";
13972
- import { existsSync as existsSync8, readFileSync as readFileSync10 } from "node:fs";
14173
+ import { execFileSync as execFileSync4 } from "node:child_process";
14174
+ import { existsSync as existsSync6, readFileSync as readFileSync10 } from "node:fs";
13973
14175
  import { basename as basename4, dirname as dirname7, extname as extname2, isAbsolute as isAbsolute17, relative as relative14, resolve as resolve14 } from "node:path";
13974
14176
  var API_VERSION22 = "^0.1.10";
13975
14177
  var state40 = {
@@ -14050,7 +14252,7 @@ function isPackageEntryPoint(filePath) {
14050
14252
  const resolved = resolveToProjectRoot(filePath);
14051
14253
  const dir = dirname7(resolved);
14052
14254
  const pkgPath = resolve14(dir, "package.json");
14053
- if (!existsSync8(pkgPath)) return false;
14255
+ if (!existsSync6(pkgPath)) return false;
14054
14256
  try {
14055
14257
  const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
14056
14258
  const entries = [];
@@ -14125,7 +14327,7 @@ function readGitVersion(filePath) {
14125
14327
  const resolved = resolveToProjectRoot(filePath);
14126
14328
  const relPath = normalizePath2(relative14(process.cwd(), resolved));
14127
14329
  try {
14128
- const out = execFileSync8("git", ["show", `HEAD:${relPath}`], {
14330
+ const out = execFileSync4("git", ["show", `HEAD:${relPath}`], {
14129
14331
  encoding: "utf-8",
14130
14332
  cwd: process.cwd(),
14131
14333
  stdio: ["pipe", "pipe", "pipe"]
@@ -14930,7 +15132,7 @@ var plugin45 = {
14930
15132
  var code_metrics_default = plugin45;
14931
15133
 
14932
15134
  // src/dead-code-detector/index.ts
14933
- import { readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync6 } from "node:fs";
15135
+ import { readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
14934
15136
  import { extname as extname3, join as join6, relative as relative16, resolve as resolve16 } from "node:path";
14935
15137
  var API_VERSION25 = "^0.1.10";
14936
15138
  var state43 = {
@@ -14965,7 +15167,7 @@ function gatherFiles(root, depth, cfg) {
14965
15167
  function walk(dir, remaining) {
14966
15168
  let entries;
14967
15169
  try {
14968
- entries = readdirSync4(dir, { withFileTypes: true });
15170
+ entries = readdirSync3(dir, { withFileTypes: true });
14969
15171
  } catch {
14970
15172
  return;
14971
15173
  }
@@ -15068,7 +15270,7 @@ function scan(root, depth, cfg) {
15068
15270
  function resolveScanRoot(rawPath) {
15069
15271
  const resolved = resolve16(process.cwd(), rawPath);
15070
15272
  try {
15071
- const stats = statSync6(resolved);
15273
+ const stats = statSync5(resolved);
15072
15274
  if (!stats.isDirectory()) {
15073
15275
  return resolve16(resolved, "..");
15074
15276
  }
@@ -15271,8 +15473,8 @@ Consider removing the export if it is not part of the public API.`;
15271
15473
  var dead_code_detector_default = plugin46;
15272
15474
 
15273
15475
  // src/dependency-vulnerability-gate/index.ts
15274
- import { execFileSync as execFileSync9 } from "node:child_process";
15275
- import { existsSync as existsSync9 } from "node:fs";
15476
+ import { execFileSync as execFileSync5 } from "node:child_process";
15477
+ import { existsSync as existsSync7 } from "node:fs";
15276
15478
  var API_VERSION26 = "^0.1.10";
15277
15479
  var state44 = {
15278
15480
  invocations: 0,
@@ -15385,14 +15587,14 @@ function isInstallCommand(input) {
15385
15587
  return INSTALL_RE2.test(command);
15386
15588
  }
15387
15589
  function detectManager() {
15388
- return existsSync9("pnpm-lock.yaml") ? "pnpm" : "npm";
15590
+ return existsSync7("pnpm-lock.yaml") ? "pnpm" : "npm";
15389
15591
  }
15390
15592
  function runAudit(cfg) {
15391
15593
  const manager = detectManager();
15392
15594
  const start = Date.now();
15393
15595
  let stdout = "";
15394
15596
  try {
15395
- stdout = execFileSync9(manager, ["audit", "--json"], {
15597
+ stdout = execFileSync5(manager, ["audit", "--json"], {
15396
15598
  encoding: "utf-8",
15397
15599
  timeout: cfg.timeoutMs,
15398
15600
  cwd: process.cwd(),
@@ -15788,7 +15990,7 @@ Consider mentioning these changes in the documentation.`;
15788
15990
  var doc_sync_guard_default = plugin48;
15789
15991
 
15790
15992
  // src/duplicate-code-detector/index.ts
15791
- import { readFileSync as readFileSync14 } from "node:fs";
15993
+ import { readFileSync as readFileSync14, statSync as statSync6 } from "node:fs";
15792
15994
  import { isAbsolute as isAbsolute19, relative as relative17, resolve as resolve17 } from "node:path";
15793
15995
  var API_VERSION28 = "^0.1.10";
15794
15996
  var state46 = {
@@ -15798,7 +16000,8 @@ var state46 = {
15798
16000
  warningCount: 0,
15799
16001
  errorCount: 0,
15800
16002
  hookUnregister: null,
15801
- lastHookWarning: /* @__PURE__ */ new Map()
16003
+ lastHookWarning: /* @__PURE__ */ new Map(),
16004
+ fileIndex: /* @__PURE__ */ new Map()
15802
16005
  };
15803
16006
  var DEFAULTS38 = {
15804
16007
  enabled: false,
@@ -15946,6 +16149,7 @@ var plugin49 = {
15946
16149
  state46.warningCount = 0;
15947
16150
  state46.errorCount = 0;
15948
16151
  state46.lastHookWarning.clear();
16152
+ state46.fileIndex.clear();
15949
16153
  if (state46.hookUnregister) {
15950
16154
  try {
15951
16155
  state46.hookUnregister();
@@ -15954,6 +16158,27 @@ var plugin49 = {
15954
16158
  state46.hookUnregister = null;
15955
16159
  }
15956
16160
  const cfg = readConfig41(api.config.extensions?.["duplicate-code-detector"]);
16161
+ function readCachedWindows(filePath, minLines) {
16162
+ let st;
16163
+ try {
16164
+ st = statSync6(filePath);
16165
+ } catch {
16166
+ return null;
16167
+ }
16168
+ const cached = state46.fileIndex.get(filePath);
16169
+ if (cached && cached.mtimeMs === st.mtimeMs && cached.size === st.size) {
16170
+ return cached.windows;
16171
+ }
16172
+ let content;
16173
+ try {
16174
+ content = readFileSync14(filePath, "utf-8");
16175
+ } catch {
16176
+ return null;
16177
+ }
16178
+ const windows = extractWindows(filePath, content, minLines);
16179
+ state46.fileIndex.set(filePath, { mtimeMs: st.mtimeMs, size: st.size, windows });
16180
+ return windows;
16181
+ }
15957
16182
  const hook = (input) => {
15958
16183
  if (!cfg.enabled) return;
15959
16184
  if (input.toolResult?.isError) return;
@@ -15968,33 +16193,27 @@ var plugin49 = {
15968
16193
  const lastWarning = state46.lastHookWarning.get(sourcePath);
15969
16194
  if (lastWarning !== void 0 && now - lastWarning < 6e4) return;
15970
16195
  const changedFile = resolve17(process.cwd(), sourcePath);
15971
- let content;
15972
- try {
15973
- content = readFileSync14(changedFile, "utf-8");
15974
- } catch {
16196
+ const changedWindows = readCachedWindows(changedFile, cfg.minLines);
16197
+ if (changedWindows === null) {
15975
16198
  state46.errorCount += 1;
15976
16199
  return;
15977
16200
  }
15978
- const changedWindows = extractWindows(changedFile, content, cfg.minLines);
15979
16201
  if (changedWindows.length === 0) return;
15980
16202
  const projectRoot = resolve17(process.cwd());
15981
- let otherFiles;
16203
+ let otherFilePaths;
15982
16204
  try {
15983
- const filePaths = collectSourceFiles(projectRoot, { extensions: cfg.extensions, excludeDirs: cfg.excludeDirs }).filter((p) => p !== changedFile);
15984
- otherFiles = /* @__PURE__ */ new Map();
15985
- for (const p of filePaths) {
15986
- try {
15987
- otherFiles.set(p, readFileSync14(p, "utf-8"));
15988
- } catch {
15989
- }
15990
- }
16205
+ otherFilePaths = collectSourceFiles(projectRoot, {
16206
+ extensions: cfg.extensions,
16207
+ excludeDirs: cfg.excludeDirs
16208
+ }).filter((p) => p !== changedFile);
15991
16209
  } catch {
15992
16210
  state46.errorCount += 1;
15993
16211
  return;
15994
16212
  }
15995
16213
  const existingWindows = [];
15996
- for (const [p, c] of otherFiles.entries()) {
15997
- existingWindows.push(...extractWindows(p, c, cfg.minLines));
16214
+ for (const p of otherFilePaths) {
16215
+ const w = readCachedWindows(p, cfg.minLines);
16216
+ if (w !== null) existingWindows.push(...w);
15998
16217
  }
15999
16218
  const hits = [];
16000
16219
  for (const cw of changedWindows) {
@@ -16895,7 +17114,7 @@ Allowed licenses: ${cfg.allowedLicenses.join(", ")}
16895
17114
  var license_audit_gate_default = plugin52;
16896
17115
 
16897
17116
  // src/migration-planner/index.ts
16898
- import { existsSync as existsSync10, readFileSync as readFileSync18 } from "node:fs";
17117
+ import { existsSync as existsSync8, readFileSync as readFileSync18 } from "node:fs";
16899
17118
  var API_VERSION32 = "^0.1.10";
16900
17119
  var state50 = {
16901
17120
  plansGenerated: 0,
@@ -16933,7 +17152,7 @@ function readChangelog(packageName, cfg) {
16933
17152
  candidates.push(`node_modules/${packageName}/changelog.md`);
16934
17153
  for (const candidate of candidates) {
16935
17154
  if (!withinProject(candidate)) continue;
16936
- if (existsSync10(candidate)) {
17155
+ if (existsSync8(candidate)) {
16937
17156
  try {
16938
17157
  const content = readFileSync18(candidate, "utf-8");
16939
17158
  return { source: candidate, content: content.slice(0, cfg.maxChars) };
@@ -17357,7 +17576,7 @@ var plugin53 = {
17357
17576
  var migration_planner_default = plugin53;
17358
17577
 
17359
17578
  // src/performance-regression-gate/index.ts
17360
- import { existsSync as existsSync11, readFileSync as readFileSync19 } from "node:fs";
17579
+ import { existsSync as existsSync9, readFileSync as readFileSync19 } from "node:fs";
17361
17580
  import { isAbsolute as isAbsolute22, relative as relative20, resolve as resolve21 } from "node:path";
17362
17581
  var API_VERSION33 = "^0.1.10";
17363
17582
  var state51 = {
@@ -17423,7 +17642,7 @@ function flattenResults(results) {
17423
17642
  return flat;
17424
17643
  }
17425
17644
  function loadResults(path) {
17426
- if (!path || !existsSync11(path)) return null;
17645
+ if (!path || !existsSync9(path)) return null;
17427
17646
  try {
17428
17647
  const raw = JSON.parse(readFileSync19(path, "utf-8"));
17429
17648
  return raw;
@@ -18001,7 +18220,7 @@ var plugin55 = {
18001
18220
  var refactor_suggester_default = plugin55;
18002
18221
 
18003
18222
  // src/release-notes-generator/index.ts
18004
- import { execFileSync as execFileSync10 } from "node:child_process";
18223
+ import { execFileSync as execFileSync6 } from "node:child_process";
18005
18224
  var API_VERSION35 = "^0.1.10";
18006
18225
  var state53 = {
18007
18226
  generateCount: 0,
@@ -18060,7 +18279,7 @@ function resolveFromRef(defaultFrom, inputFrom) {
18060
18279
  if (inputFrom) return inputFrom;
18061
18280
  if (defaultFrom === "latest-tag") {
18062
18281
  try {
18063
- return execFileSync10("git", ["describe", "--tags", "--abbrev=0"], GIT_OPTIONS).trim();
18282
+ return execFileSync6("git", ["describe", "--tags", "--abbrev=0"], GIT_OPTIONS).trim();
18064
18283
  } catch {
18065
18284
  return "";
18066
18285
  }
@@ -18068,7 +18287,7 @@ function resolveFromRef(defaultFrom, inputFrom) {
18068
18287
  return defaultFrom;
18069
18288
  }
18070
18289
  function resolveCommit(ref) {
18071
- const resolved = execFileSync10(
18290
+ const resolved = execFileSync6(
18072
18291
  "git",
18073
18292
  ["rev-parse", "--verify", "--end-of-options", `${ref}^{commit}`],
18074
18293
  GIT_OPTIONS
@@ -18081,7 +18300,7 @@ function resolveCommit(ref) {
18081
18300
  function getCommits(from, to) {
18082
18301
  const toCommit = resolveCommit(to);
18083
18302
  const range = from ? `${resolveCommit(from)}..${toCommit}` : toCommit;
18084
- const output = execFileSync10(
18303
+ const output = execFileSync6(
18085
18304
  "git",
18086
18305
  ["log", "--pretty=format:%H%x09%s", "--end-of-options", range],
18087
18306
  GIT_OPTIONS
@@ -18634,7 +18853,7 @@ ${body}${suffix}`;
18634
18853
  var schema_evolution_guard_default = plugin57;
18635
18854
 
18636
18855
  // src/security-hotspot-scanner/index.ts
18637
- import { readdirSync as readdirSync5, readFileSync as readFileSync22, statSync as statSync7 } from "node:fs";
18856
+ import { readdirSync as readdirSync4, readFileSync as readFileSync22, statSync as statSync7 } from "node:fs";
18638
18857
  import { isAbsolute as isAbsolute24, relative as relative22, resolve as resolve23 } from "node:path";
18639
18858
  var API_VERSION37 = "^0.1.10";
18640
18859
  var state55 = {
@@ -18750,7 +18969,7 @@ function scanPath5(inputPath, cfg) {
18750
18969
  const walk = (dir) => {
18751
18970
  let entries;
18752
18971
  try {
18753
- entries = readdirSync5(dir);
18972
+ entries = readdirSync4(dir);
18754
18973
  } catch {
18755
18974
  return;
18756
18975
  }
@@ -19699,7 +19918,7 @@ var plugin60 = {
19699
19918
  var smart_rename_default = plugin60;
19700
19919
 
19701
19920
  // src/test-flake-detector/index.ts
19702
- import { execFileSync as execFileSync11 } from "node:child_process";
19921
+ import { execFileSync as execFileSync7 } from "node:child_process";
19703
19922
  import { readFileSync as readFileSync24 } from "node:fs";
19704
19923
  import { createRequire as createRequire2 } from "node:module";
19705
19924
  import { dirname as dirname8, isAbsolute as isAbsolute27, relative as relative25, resolve as resolve26 } from "node:path";
@@ -19845,7 +20064,7 @@ function resolveTestCommand(baseCommand, testPattern) {
19845
20064
  }
19846
20065
  function runOnce(command, timeoutMs) {
19847
20066
  try {
19848
- const output = execFileSync11(command.cmd, command.args, {
20067
+ const output = execFileSync7(command.cmd, command.args, {
19849
20068
  encoding: "utf-8",
19850
20069
  timeout: timeoutMs,
19851
20070
  cwd: process.cwd(),
@@ -20679,8 +20898,8 @@ var plugin63 = {
20679
20898
  state60.postInvocations += 1;
20680
20899
  let content;
20681
20900
  try {
20682
- const stat4 = await fs3.stat(filePath);
20683
- if (!stat4.isFile()) return;
20901
+ const stat5 = await fs3.stat(filePath);
20902
+ if (!stat5.isFile()) return;
20684
20903
  content = await fs3.readFile(filePath, "utf-8");
20685
20904
  } catch {
20686
20905
  state60.readErrorCount += 1;