@wrongstack/plugins 0.291.2 → 0.292.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5033,8 +5033,8 @@ var plugin15 = {
5033
5033
  var file_watcher_default = plugin15;
5034
5034
 
5035
5035
  // src/format-on-save/index.ts
5036
- import { execFileSync as execFileSync2, execSync } from "node:child_process";
5037
- import { existsSync as existsSync2, statSync as statSync4 } from "node:fs";
5036
+ import { execFile as execFile3 } from "node:child_process";
5037
+ import { access, stat as stat2 } from "node:fs/promises";
5038
5038
  var API_VERSION8 = "^0.1.10";
5039
5039
  var state14 = {
5040
5040
  invocationCount: 0,
@@ -5093,29 +5093,46 @@ function evictExpired(ttlMs) {
5093
5093
  if (ts < cutoff) recentlyCovered.delete(path);
5094
5094
  }
5095
5095
  }
5096
- function formatFile(filePath, timeoutMs) {
5096
+ async function formatFile(filePath, timeoutMs) {
5097
5097
  if (!withinProject(filePath)) return null;
5098
- if (!existsSync2(filePath)) return null;
5098
+ try {
5099
+ await access(filePath);
5100
+ } catch {
5101
+ return null;
5102
+ }
5099
5103
  let bytesBefore;
5100
5104
  try {
5101
- bytesBefore = statSync4(filePath).size;
5105
+ bytesBefore = (await stat2(filePath)).size;
5102
5106
  } catch {
5103
5107
  return null;
5104
5108
  }
5105
5109
  try {
5106
- execFileSync2("npx", ["biome", "format", "--write", filePath], {
5107
- encoding: "utf-8",
5108
- timeout: timeoutMs,
5109
- cwd: process.cwd(),
5110
- stdio: ["pipe", "pipe", "pipe"]
5110
+ await new Promise((resolve28, reject) => {
5111
+ execFile3(
5112
+ "npx",
5113
+ ["biome", "format", "--write", filePath],
5114
+ {
5115
+ encoding: "utf-8",
5116
+ timeout: timeoutMs,
5117
+ cwd: process.cwd(),
5118
+ windowsHide: true,
5119
+ maxBuffer: 16 * 1024 * 1024
5120
+ },
5121
+ (err) => {
5122
+ if (err) {
5123
+ const e = err;
5124
+ if (e.killed) return reject(err);
5125
+ }
5126
+ resolve28();
5127
+ }
5128
+ );
5111
5129
  });
5112
- } catch (err) {
5113
- const e = err;
5114
- if (e.killed) return null;
5130
+ } catch {
5131
+ return null;
5115
5132
  }
5116
5133
  let bytesAfter;
5117
5134
  try {
5118
- bytesAfter = statSync4(filePath).size;
5135
+ bytesAfter = (await stat2(filePath)).size;
5119
5136
  } catch {
5120
5137
  return null;
5121
5138
  }
@@ -5123,11 +5140,22 @@ function formatFile(filePath, timeoutMs) {
5123
5140
  return { changed: true, bytesBefore, bytesAfter };
5124
5141
  }
5125
5142
  try {
5126
- execFileSync2("npx", ["biome", "format", filePath], {
5127
- encoding: "utf-8",
5128
- timeout: timeoutMs,
5129
- cwd: process.cwd(),
5130
- stdio: ["pipe", "pipe", "pipe"]
5143
+ await new Promise((resolve28, reject) => {
5144
+ execFile3(
5145
+ "npx",
5146
+ ["biome", "format", filePath],
5147
+ {
5148
+ encoding: "utf-8",
5149
+ timeout: timeoutMs,
5150
+ cwd: process.cwd(),
5151
+ windowsHide: true,
5152
+ maxBuffer: 16 * 1024 * 1024
5153
+ },
5154
+ (err) => {
5155
+ if (err) return reject(err);
5156
+ resolve28();
5157
+ }
5158
+ );
5131
5159
  });
5132
5160
  return { changed: false, bytesBefore, bytesAfter };
5133
5161
  } catch {
@@ -5168,7 +5196,7 @@ var plugin16 = {
5168
5196
  }
5169
5197
  }
5170
5198
  },
5171
- setup(api) {
5199
+ async setup(api) {
5172
5200
  clearRegistrations();
5173
5201
  state14.invocationCount = 0;
5174
5202
  state14.formattedCount = 0;
@@ -5180,11 +5208,21 @@ var plugin16 = {
5180
5208
  const cfg = readConfig12(api.config.extensions?.["format-on-save"]);
5181
5209
  let biomeAvailable = false;
5182
5210
  try {
5183
- execSync("npx biome --version", {
5184
- encoding: "utf-8",
5185
- timeout: 5e3,
5186
- cwd: process.cwd(),
5187
- stdio: ["pipe", "pipe", "pipe"]
5211
+ await new Promise((resolve28, reject) => {
5212
+ execFile3(
5213
+ "npx",
5214
+ ["biome", "--version"],
5215
+ {
5216
+ encoding: "utf-8",
5217
+ timeout: 5e3,
5218
+ cwd: process.cwd(),
5219
+ windowsHide: true
5220
+ },
5221
+ (err) => {
5222
+ if (err) reject(err);
5223
+ else resolve28();
5224
+ }
5225
+ );
5188
5226
  });
5189
5227
  biomeAvailable = true;
5190
5228
  api.log.info("format-on-save: biome detected");
@@ -5192,7 +5230,7 @@ var plugin16 = {
5192
5230
  biomeAvailable = false;
5193
5231
  api.log.warn("format-on-save: biome not found \u2014 hook will be a no-op");
5194
5232
  }
5195
- const hook = (input) => {
5233
+ const hook = async (input) => {
5196
5234
  if (!cfg.enabled || !biomeAvailable) return;
5197
5235
  if (input.toolResult?.isError) return;
5198
5236
  const toolName = input.toolName ?? "";
@@ -5214,7 +5252,7 @@ var plugin16 = {
5214
5252
  }
5215
5253
  }
5216
5254
  state14.invocationCount += 1;
5217
- const result = formatFile(filePath, cfg.timeoutMs);
5255
+ const result = await formatFile(filePath, cfg.timeoutMs);
5218
5256
  if (!result) {
5219
5257
  state14.errorCount += 1;
5220
5258
  return;
@@ -5320,56 +5358,68 @@ var plugin16 = {
5320
5358
  var format_on_save_default = plugin16;
5321
5359
 
5322
5360
  // src/git-autocommit/index.ts
5323
- import { execFileSync as execFileSync3 } from "node:child_process";
5324
- import { existsSync as existsSync3 } from "node:fs";
5361
+ import { execFile as execFile4 } from "node:child_process";
5362
+ import { existsSync as existsSync2 } from "node:fs";
5325
5363
  var API_VERSION9 = "^0.1.10";
5326
5364
  var commitCount = { value: 0 };
5327
5365
  var lastCommit = { hash: null, at: null };
5328
5366
  var llmGenerated = { value: 0 };
5329
5367
  var DEFAULT_GIT_TIMEOUT_MS = 3e4;
5330
5368
  var GIT_COMMIT_TIMEOUT_MS = 5 * 6e4;
5331
- function runGit2(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
5332
- try {
5333
- return execFileSync3("git", args, {
5334
- encoding: "utf-8",
5335
- cwd,
5336
- stdio: ["pipe", "pipe", "pipe"],
5337
- timeout: timeoutMs,
5338
- maxBuffer: 10 * 1024 * 1024,
5339
- windowsHide: true
5340
- }).trim();
5341
- } catch (err) {
5342
- const e = err;
5343
- throw new Error(`git command failed: ${e.message ?? e.stderr ?? String(err)}`);
5344
- }
5369
+ async function runGit2(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
5370
+ return await new Promise((resolvePromise, rejectPromise) => {
5371
+ execFile4(
5372
+ "git",
5373
+ args,
5374
+ {
5375
+ encoding: "utf-8",
5376
+ cwd,
5377
+ windowsHide: true,
5378
+ timeout: timeoutMs,
5379
+ maxBuffer: 10 * 1024 * 1024
5380
+ },
5381
+ (err, stdout) => {
5382
+ if (err) {
5383
+ const e = err;
5384
+ rejectPromise(
5385
+ new Error(
5386
+ `git command failed: ${e.message ?? e.stderr ?? String(err)}`
5387
+ )
5388
+ );
5389
+ return;
5390
+ }
5391
+ resolvePromise(stdout.trim());
5392
+ }
5393
+ );
5394
+ });
5345
5395
  }
5346
- function getChangedFiles(cwd) {
5347
- const output = runGit2(["status", "--porcelain"], cwd);
5396
+ async function getChangedFiles(cwd) {
5397
+ const output = await runGit2(["status", "--porcelain"], cwd);
5348
5398
  if (!output) return [];
5349
5399
  return output.split("\n").filter((l) => l.trim()).map((l) => l.slice(3).trim());
5350
5400
  }
5351
- function getStagedFiles(cwd) {
5352
- const output = runGit2(["diff", "--cached", "--name-only"], cwd);
5401
+ async function getStagedFiles(cwd) {
5402
+ const output = await runGit2(["diff", "--cached", "--name-only"], cwd);
5353
5403
  return output ? output.split("\n").filter(Boolean) : [];
5354
5404
  }
5355
- function stageFiles(files, cwd) {
5405
+ async function stageFiles(files, cwd) {
5356
5406
  if (!files || !Array.isArray(files)) return;
5357
5407
  const existing = files.filter((f) => {
5358
5408
  try {
5359
- return existsSync3(f);
5409
+ return existsSync2(f);
5360
5410
  } catch {
5361
5411
  return false;
5362
5412
  }
5363
5413
  });
5364
5414
  if (existing.length === 0) throw new Error("No files exist to stage");
5365
- runGit2(["add", ...existing], cwd);
5415
+ await runGit2(["add", ...existing], cwd);
5366
5416
  }
5367
- function commitWithMessage(message, cwd) {
5368
- return runGit2(["commit", "-m", message], cwd, GIT_COMMIT_TIMEOUT_MS);
5417
+ async function commitWithMessage(message, cwd) {
5418
+ return await runGit2(["commit", "-m", message], cwd, GIT_COMMIT_TIMEOUT_MS);
5369
5419
  }
5370
- function getWorktrees(cwd) {
5420
+ async function getWorktrees(cwd) {
5371
5421
  try {
5372
- const out = runGit2(["worktree", "list", "--porcelain"], cwd);
5422
+ const out = await runGit2(["worktree", "list", "--porcelain"], cwd);
5373
5423
  if (!out) return [];
5374
5424
  const entries = [];
5375
5425
  let current = {};
@@ -5389,28 +5439,28 @@ function getWorktrees(cwd) {
5389
5439
  return [];
5390
5440
  }
5391
5441
  }
5392
- function simultaneousEditWarning(cwd) {
5393
- const worktrees = getWorktrees(cwd);
5442
+ async function simultaneousEditWarning(cwd) {
5443
+ const worktrees = await getWorktrees(cwd);
5394
5444
  if (worktrees.length > 1) {
5395
5445
  const otherBranches = worktrees.filter((wt) => wt.branch).map((wt) => wt.branch.replace("refs/heads/", ""));
5396
5446
  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.`;
5397
5447
  }
5398
5448
  return null;
5399
5449
  }
5400
- function getStagedDiff(cwd) {
5450
+ async function getStagedDiff(cwd) {
5401
5451
  try {
5402
- const stat4 = runGit2(["diff", "--cached", "--stat"], cwd);
5403
- const diff = runGit2(["diff", "--cached"], cwd);
5452
+ const stat5 = await runGit2(["diff", "--cached", "--stat"], cwd);
5453
+ const diff = await runGit2(["diff", "--cached"], cwd);
5404
5454
  const MAX_DIFF = 2e4;
5405
5455
  const truncated = diff.length > MAX_DIFF ? diff.slice(0, MAX_DIFF) + "\n\n... (diff truncated)" : diff;
5406
- return { stat: stat4 || "(no stat)", diff: truncated || "(clean)" };
5456
+ return { stat: stat5 || "(no stat)", diff: truncated || "(clean)" };
5407
5457
  } catch {
5408
5458
  return { stat: "(unavailable)", diff: "(unavailable)" };
5409
5459
  }
5410
5460
  }
5411
- function externalChangesSinceStage(cwd) {
5461
+ async function externalChangesSinceStage(cwd) {
5412
5462
  try {
5413
- const out = runGit2(["status", "--porcelain"], cwd);
5463
+ const out = await runGit2(["status", "--porcelain"], cwd);
5414
5464
  if (!out) return null;
5415
5465
  const unstaged = out.split("\n").filter((l) => l.trim()).filter((l) => {
5416
5466
  const idx = l[0] ?? " ";
@@ -5441,14 +5491,14 @@ var VALID_TYPES = [
5441
5491
  "build",
5442
5492
  "revert"
5443
5493
  ];
5444
- async function generateCommitFromDiff(api, stat4, diff) {
5494
+ async function generateCommitFromDiff(api, stat5, diff) {
5445
5495
  if (!api.llm) return null;
5446
5496
  try {
5447
5497
  const result = await api.llm.complete(
5448
5498
  `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.
5449
5499
 
5450
5500
  Stat:
5451
- ${stat4}
5501
+ ${stat5}
5452
5502
 
5453
5503
  Diff:
5454
5504
  ${diff}`,
@@ -5585,7 +5635,7 @@ var plugin17 = {
5585
5635
  }
5586
5636
  if (files && files.length > 0) {
5587
5637
  try {
5588
- stageFiles(files);
5638
+ await stageFiles(files);
5589
5639
  } catch (err) {
5590
5640
  return {
5591
5641
  ok: false,
@@ -5595,20 +5645,20 @@ var plugin17 = {
5595
5645
  }
5596
5646
  let staged = [];
5597
5647
  try {
5598
- staged = getStagedFiles();
5648
+ staged = await getStagedFiles();
5599
5649
  } catch {
5600
5650
  staged = [];
5601
5651
  }
5602
5652
  if (staged.length === 0) {
5603
5653
  try {
5604
- const changed = getChangedFiles();
5654
+ const changed = await getChangedFiles();
5605
5655
  if (changed.length > 0) {
5606
5656
  try {
5607
- stageFiles(changed);
5657
+ await stageFiles(changed);
5608
5658
  } catch {
5609
5659
  }
5610
5660
  try {
5611
- staged = getStagedFiles();
5661
+ staged = await getStagedFiles();
5612
5662
  } catch {
5613
5663
  staged = [];
5614
5664
  }
@@ -5616,10 +5666,10 @@ var plugin17 = {
5616
5666
  } catch {
5617
5667
  }
5618
5668
  }
5619
- const { stat: stat4, diff: stagedDiff } = getStagedDiff();
5669
+ const { stat: stat5, diff: stagedDiff } = await getStagedDiff();
5620
5670
  let generatedByLlm = false;
5621
5671
  if (wantGenerate && staged.length > 0) {
5622
- const g = await generateCommitFromDiff(api, stat4, stagedDiff);
5672
+ const g = await generateCommitFromDiff(api, stat5, stagedDiff);
5623
5673
  if (g) {
5624
5674
  type = g.type;
5625
5675
  if (g.scope) scope = g.scope;
@@ -5662,8 +5712,8 @@ var plugin17 = {
5662
5712
  error: "Nothing staged. Add files with git add or provide files input."
5663
5713
  };
5664
5714
  }
5665
- const worktreeWarn = simultaneousEditWarning();
5666
- const externalChanges = externalChangesSinceStage();
5715
+ const worktreeWarn = await simultaneousEditWarning();
5716
+ const externalChanges = await externalChangesSinceStage();
5667
5717
  let externalWarning = null;
5668
5718
  if (externalChanges && externalChanges.length > 0) {
5669
5719
  const preview = externalChanges.slice(0, 10).join(", ");
@@ -5680,7 +5730,7 @@ var plugin17 = {
5680
5730
  stagedDiff: `
5681
5731
  ## Staged changes (dry run)
5682
5732
 
5683
- ${stat4}
5733
+ ${stat5}
5684
5734
 
5685
5735
  \`\`\`diff
5686
5736
  ${stagedDiff}
@@ -5688,15 +5738,15 @@ ${stagedDiff}
5688
5738
  };
5689
5739
  }
5690
5740
  let preCommitDiff = stagedDiff;
5691
- let preCommitStat = stat4;
5741
+ let preCommitStat = stat5;
5692
5742
  if (staged.length === 0) {
5693
- const fresh = getStagedDiff();
5743
+ const fresh = await getStagedDiff();
5694
5744
  preCommitDiff = fresh.diff;
5695
5745
  preCommitStat = fresh.stat;
5696
5746
  }
5697
5747
  let hash = "";
5698
5748
  try {
5699
- hash = commitWithMessage(msg);
5749
+ hash = await commitWithMessage(msg);
5700
5750
  } catch (err) {
5701
5751
  return {
5702
5752
  ok: false,
@@ -5781,7 +5831,7 @@ var git_autocommit_default = plugin17;
5781
5831
 
5782
5832
  // src/import-organizer/index.ts
5783
5833
  import { spawn } from "node:child_process";
5784
- import { existsSync as existsSync4, statSync as statSync5 } from "node:fs";
5834
+ import { existsSync as existsSync3, statSync as statSync4 } from "node:fs";
5785
5835
  import { basename as basename2, isAbsolute as isAbsolute7 } from "node:path";
5786
5836
  var ALLOWED_FIRST_TOKENS = /* @__PURE__ */ new Set([
5787
5837
  "npx",
@@ -5880,10 +5930,10 @@ function runCommand(command, args, timeoutMs, cwd) {
5880
5930
  }
5881
5931
  async function organizeImports(filePath, cfg, cwd) {
5882
5932
  if (!withinProject(filePath)) return null;
5883
- if (!existsSync4(filePath)) return null;
5933
+ if (!existsSync3(filePath)) return null;
5884
5934
  let bytesBefore;
5885
5935
  try {
5886
- bytesBefore = statSync5(filePath).size;
5936
+ bytesBefore = statSync4(filePath).size;
5887
5937
  } catch {
5888
5938
  return null;
5889
5939
  }
@@ -5904,7 +5954,7 @@ async function organizeImports(filePath, cfg, cwd) {
5904
5954
  if (result.code === 127) return null;
5905
5955
  let bytesAfter;
5906
5956
  try {
5907
- bytesAfter = statSync5(filePath).size;
5957
+ bytesAfter = statSync4(filePath).size;
5908
5958
  } catch {
5909
5959
  return null;
5910
5960
  }
@@ -6629,7 +6679,7 @@ var plugin20 = {
6629
6679
  var knowledge_graph_default = plugin20;
6630
6680
 
6631
6681
  // src/lint-gate/index.ts
6632
- import { execFile as execFile3 } from "node:child_process";
6682
+ import { execFile as execFile5 } from "node:child_process";
6633
6683
  import { readFileSync as readFileSync6 } from "node:fs";
6634
6684
  import { mkdtemp, readFile as readFile2, rm, writeFile } from "node:fs/promises";
6635
6685
  import { createRequire } from "node:module";
@@ -6699,7 +6749,7 @@ function resolveLocalLinter(name, cwd) {
6699
6749
  function runCommand2(command, args, timeoutMs, cwd, signal) {
6700
6750
  return new Promise((resolveResult) => {
6701
6751
  try {
6702
- execFile3(
6752
+ execFile5(
6703
6753
  command,
6704
6754
  args,
6705
6755
  {
@@ -7342,7 +7392,7 @@ var plugin22 = {
7342
7392
  var llm_cache_default = plugin22;
7343
7393
 
7344
7394
  // src/loop-breaker/index.ts
7345
- import { execFile as execFile4 } from "node:child_process";
7395
+ import { execFile as execFile6 } from "node:child_process";
7346
7396
  var state20 = {
7347
7397
  lastFingerprint: null,
7348
7398
  streak: 0,
@@ -7439,7 +7489,7 @@ function hashString2(value) {
7439
7489
  async function gitDiffFingerprint(cwd, signal) {
7440
7490
  try {
7441
7491
  const diff = await new Promise((resolve28, reject) => {
7442
- execFile4(
7492
+ execFile6(
7443
7493
  "git",
7444
7494
  ["diff", "--no-ext-diff", "--"],
7445
7495
  {
@@ -8007,15 +8057,134 @@ var plugin24 = {
8007
8057
  var model_router_default = plugin24;
8008
8058
 
8009
8059
  // src/notify-hub/index.ts
8060
+ import { lookup } from "node:dns/promises";
8061
+
8062
+ // src/notify-hub/webhook-channel.ts
8063
+ function freshCircuit() {
8064
+ return { open: false, consecutiveFailures: 0 };
8065
+ }
8066
+ var WebhookNotificationChannel = class {
8067
+ name = "webhook";
8068
+ type = "webhook";
8069
+ #url;
8070
+ #headers;
8071
+ #timeoutMs;
8072
+ #maxFailures;
8073
+ #circuit;
8074
+ /** Total deliveries attempted (across all resets). */
8075
+ #totalAttempted = 0;
8076
+ /** Total successful deliveries. */
8077
+ #totalDelivered = 0;
8078
+ /** Total failed deliveries. */
8079
+ #totalFailed = 0;
8080
+ /** Total suppressed by circuit breaker. */
8081
+ #totalSuppressed = 0;
8082
+ constructor(opts) {
8083
+ this.#url = opts.webhookUrl;
8084
+ this.#headers = opts.headers ?? {};
8085
+ this.#timeoutMs = opts.timeoutMs ?? 5e3;
8086
+ this.#maxFailures = opts.maxConsecutiveFailures ?? 5;
8087
+ this.#circuit = freshCircuit();
8088
+ }
8089
+ // -----------------------------------------------------------------------
8090
+ // NotificationChannel
8091
+ // -----------------------------------------------------------------------
8092
+ async deliver(msg) {
8093
+ const deliveredAt = (/* @__PURE__ */ new Date()).toISOString();
8094
+ if (this.#circuit.open && this.#maxFailures > 0) {
8095
+ this.#totalSuppressed += 1;
8096
+ return {
8097
+ ok: false,
8098
+ channel: this.name,
8099
+ error: `circuit open after ${this.#circuit.consecutiveFailures} consecutive failures`,
8100
+ deliveredAt
8101
+ };
8102
+ }
8103
+ this.#totalAttempted += 1;
8104
+ const body = JSON.stringify({
8105
+ source: "wrongstack/notify-hub",
8106
+ event: msg.source,
8107
+ ts: deliveredAt,
8108
+ title: msg.title,
8109
+ message: msg.body,
8110
+ level: msg.level,
8111
+ ...msg.metadata ?? {}
8112
+ });
8113
+ try {
8114
+ const controller = new AbortController();
8115
+ const timer = setTimeout(() => controller.abort(), this.#timeoutMs);
8116
+ try {
8117
+ const res = await fetch(this.#url, {
8118
+ method: "POST",
8119
+ headers: { "content-type": "application/json", ...this.#headers },
8120
+ body,
8121
+ signal: controller.signal
8122
+ });
8123
+ if (!res.ok) throw new Error(`webhook responded ${res.status}`);
8124
+ } finally {
8125
+ clearTimeout(timer);
8126
+ }
8127
+ this.#totalDelivered += 1;
8128
+ this.#circuit.consecutiveFailures = 0;
8129
+ this.#circuit.open = false;
8130
+ return { ok: true, channel: this.name, deliveredAt };
8131
+ } catch (err) {
8132
+ this.#totalFailed += 1;
8133
+ this.#circuit.consecutiveFailures += 1;
8134
+ if (this.#maxFailures > 0 && this.#circuit.consecutiveFailures >= this.#maxFailures) {
8135
+ this.#circuit.open = true;
8136
+ }
8137
+ return {
8138
+ ok: false,
8139
+ channel: this.name,
8140
+ error: err instanceof Error ? err.message : String(err),
8141
+ deliveredAt
8142
+ };
8143
+ }
8144
+ }
8145
+ async ping() {
8146
+ if (!this.#url) {
8147
+ return { ok: false, error: "no webhookUrl configured" };
8148
+ }
8149
+ if (this.#circuit.open) {
8150
+ return {
8151
+ ok: false,
8152
+ error: `circuit open after ${this.#circuit.consecutiveFailures} consecutive failures`
8153
+ };
8154
+ }
8155
+ return { ok: true };
8156
+ }
8157
+ // -----------------------------------------------------------------------
8158
+ // Diagnostics (not part of NotificationChannel)
8159
+ // -----------------------------------------------------------------------
8160
+ /** Reset the circuit breaker (e.g. after config update). */
8161
+ resetCircuit() {
8162
+ this.#circuit.open = false;
8163
+ this.#circuit.consecutiveFailures = 0;
8164
+ }
8165
+ /** Current circuit breaker state. */
8166
+ circuitStatus() {
8167
+ return { open: this.#circuit.open, consecutiveFailures: this.#circuit.consecutiveFailures };
8168
+ }
8169
+ /** Lifetime delivery counters (never reset). */
8170
+ counters() {
8171
+ return {
8172
+ attempted: this.#totalAttempted,
8173
+ delivered: this.#totalDelivered,
8174
+ failed: this.#totalFailed,
8175
+ suppressed: this.#totalSuppressed
8176
+ };
8177
+ }
8178
+ };
8179
+
8180
+ // src/notify-hub/index.ts
8181
+ var _pluginLog = null;
8010
8182
  var state22 = {
8011
- sent: 0,
8012
- failed: 0,
8013
- suppressed: 0,
8014
- consecutiveFailures: 0,
8015
- circuitOpen: false,
8183
+ channel: null,
8016
8184
  lastDelivery: null,
8017
8185
  stopHookUnregister: null,
8018
- eventUnsubscribers: []
8186
+ eventUnsubscribers: [],
8187
+ circuitWarned: false
8019
8188
  };
8020
8189
  var KNOWN_EVENTS = ["session.stop", "tool.error", "budget.threshold"];
8021
8190
  var DEFAULTS19 = {
@@ -8038,6 +8207,21 @@ function isBlockedHostname(hostname) {
8038
8207
  const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
8039
8208
  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);
8040
8209
  }
8210
+ async function hasPrivateResolvedIP(hostname) {
8211
+ try {
8212
+ const entries = await lookup(hostname, { all: true, verbatim: true });
8213
+ for (const { address } of entries) {
8214
+ if (isPrivateIPv4(address)) return true;
8215
+ const lower = address.toLowerCase();
8216
+ if (lower === "::1" || lower === "0:0:0:0:0:0:0:1" || lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:")) {
8217
+ return true;
8218
+ }
8219
+ }
8220
+ return false;
8221
+ } catch {
8222
+ return false;
8223
+ }
8224
+ }
8041
8225
  function normalizeWebhookUrl(raw) {
8042
8226
  if (typeof raw !== "string" || raw.trim().length === 0) return "";
8043
8227
  try {
@@ -8069,49 +8253,37 @@ function readConfig20(raw) {
8069
8253
  maxConsecutiveFailures: typeof r["maxConsecutiveFailures"] === "number" && r["maxConsecutiveFailures"] >= 1 ? r["maxConsecutiveFailures"] : DEFAULTS19.maxConsecutiveFailures
8070
8254
  };
8071
8255
  }
8072
- async function deliver(cfg, event, payload, log) {
8073
- if (!cfg.webhookUrl) return false;
8074
- if (state22.circuitOpen) {
8075
- state22.suppressed += 1;
8256
+ async function deliver(event, payload) {
8257
+ const ch = state22.channel;
8258
+ if (!ch) {
8076
8259
  return false;
8077
8260
  }
8078
- const body = JSON.stringify({
8079
- source: "wrongstack/notify-hub",
8080
- event,
8081
- ts: (/* @__PURE__ */ new Date()).toISOString(),
8082
- ...payload
8261
+ const result = await deliverViaChannel(ch, event, {
8262
+ title: typeof payload.title === "string" ? payload.title : event,
8263
+ body: typeof payload.message === "string" ? payload.message : typeof payload.error === "string" ? payload.error : JSON.stringify(payload),
8264
+ level: event === "tool.error" || event === "budget.threshold" ? "warning" : "info",
8265
+ source: event,
8266
+ metadata: payload
8083
8267
  });
8084
- try {
8085
- const controller = new AbortController();
8086
- const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
8087
- try {
8088
- const res = await fetch(cfg.webhookUrl, {
8089
- method: "POST",
8090
- headers: { "content-type": "application/json", ...cfg.headers },
8091
- body,
8092
- signal: controller.signal
8093
- });
8094
- if (!res.ok) throw new Error(`webhook responded ${res.status}`);
8095
- } finally {
8096
- clearTimeout(timer);
8097
- }
8098
- state22.sent += 1;
8099
- state22.consecutiveFailures = 0;
8268
+ return result.ok;
8269
+ }
8270
+ async function deliverViaChannel(ch, event, msg) {
8271
+ const result = await ch.deliver(msg);
8272
+ if (result.ok) {
8273
+ state22.circuitWarned = false;
8100
8274
  state22.lastDelivery = { event, ok: true, when: (/* @__PURE__ */ new Date()).toISOString() };
8101
- return true;
8102
- } catch (err) {
8103
- state22.failed += 1;
8104
- state22.consecutiveFailures += 1;
8275
+ } else {
8105
8276
  state22.lastDelivery = { event, ok: false, when: (/* @__PURE__ */ new Date()).toISOString() };
8106
- if (state22.consecutiveFailures >= cfg.maxConsecutiveFailures) {
8107
- state22.circuitOpen = true;
8108
- log.warn(
8109
- `notify-hub: ${state22.consecutiveFailures} consecutive delivery failures \u2014 circuit opened, further notifications suppressed`,
8110
- { error: err instanceof Error ? err.message : String(err) }
8277
+ const cs = ch.circuitStatus();
8278
+ if (cs.open && !state22.circuitWarned) {
8279
+ state22.circuitWarned = true;
8280
+ _pluginLog?.warn(
8281
+ `notify-hub: ${cs.consecutiveFailures} consecutive delivery failures \u2014 circuit opened, further notifications suppressed`,
8282
+ { consecutiveFailures: cs.consecutiveFailures, event }
8111
8283
  );
8112
8284
  }
8113
- return false;
8114
8285
  }
8286
+ return result;
8115
8287
  }
8116
8288
  function truncateText(s, max) {
8117
8289
  return s.length > max ? `${s.slice(0, max)}\u2026` : s;
@@ -8158,12 +8330,10 @@ var plugin25 = {
8158
8330
  }
8159
8331
  }
8160
8332
  },
8161
- setup(api) {
8162
- state22.sent = 0;
8163
- state22.failed = 0;
8164
- state22.suppressed = 0;
8165
- state22.consecutiveFailures = 0;
8166
- state22.circuitOpen = false;
8333
+ async setup(api) {
8334
+ _pluginLog = api.log;
8335
+ state22.circuitWarned = false;
8336
+ state22.channel = null;
8167
8337
  state22.lastDelivery = null;
8168
8338
  if (state22.stopHookUnregister) {
8169
8339
  try {
@@ -8180,15 +8350,35 @@ var plugin25 = {
8180
8350
  }
8181
8351
  state22.eventUnsubscribers = [];
8182
8352
  const cfg = readConfig20(api.config.extensions?.["notify-hub"]);
8183
- const active = cfg.enabled && cfg.webhookUrl.length > 0;
8353
+ let active = cfg.enabled && cfg.webhookUrl.length > 0;
8354
+ if (active) {
8355
+ try {
8356
+ const url = new URL(cfg.webhookUrl);
8357
+ const hostnameBlocked = await hasPrivateResolvedIP(url.hostname);
8358
+ if (hostnameBlocked) {
8359
+ api.log.warn(
8360
+ `notify-hub: webhook URL resolves to a private/local IP (${url.hostname}) \u2014 disabling plugin`
8361
+ );
8362
+ active = false;
8363
+ }
8364
+ } catch {
8365
+ }
8366
+ }
8367
+ if (active) {
8368
+ state22.channel = new WebhookNotificationChannel({
8369
+ webhookUrl: cfg.webhookUrl,
8370
+ headers: cfg.headers,
8371
+ timeoutMs: cfg.timeoutMs,
8372
+ maxConsecutiveFailures: cfg.maxConsecutiveFailures
8373
+ });
8374
+ api.notifier?.registerChannel(state22.channel);
8375
+ }
8184
8376
  if (active && cfg.events.includes("session.stop")) {
8185
8377
  const stopHook = (input) => {
8186
- void deliver(
8187
- cfg,
8188
- "session.stop",
8189
- { sessionId: input.sessionId ?? null, cwd: input.cwd ?? null },
8190
- api.log
8191
- );
8378
+ void deliver("session.stop", {
8379
+ sessionId: input.sessionId ?? null,
8380
+ cwd: input.cwd ?? null
8381
+ });
8192
8382
  };
8193
8383
  state22.stopHookUnregister = api.registerHook("Stop", void 0, stopHook);
8194
8384
  }
@@ -8196,26 +8386,21 @@ var plugin25 = {
8196
8386
  const off = api.onPattern("tool.*", (eventName, payload) => {
8197
8387
  if (!/error|failed/.test(eventName)) return;
8198
8388
  const p = payload;
8199
- void deliver(
8200
- cfg,
8201
- "tool.error",
8202
- {
8203
- tool: p?.tool ?? p?.name ?? "unknown",
8204
- busEvent: eventName,
8205
- error: truncateText(
8206
- p?.error instanceof Error ? p.error.message : String(p?.error ?? ""),
8207
- 500
8208
- )
8209
- },
8210
- api.log
8211
- );
8389
+ void deliver("tool.error", {
8390
+ tool: p?.tool ?? p?.name ?? "unknown",
8391
+ busEvent: eventName,
8392
+ error: truncateText(
8393
+ p?.error instanceof Error ? p.error.message : String(p?.error ?? ""),
8394
+ 500
8395
+ )
8396
+ });
8212
8397
  });
8213
8398
  state22.eventUnsubscribers.push(off);
8214
8399
  }
8215
8400
  if (active && cfg.events.includes("budget.threshold")) {
8216
8401
  const off = api.onPattern("budget.*", (eventName, payload) => {
8217
8402
  if (!eventName.includes("threshold")) return;
8218
- void deliver(cfg, "budget.threshold", { busEvent: eventName, detail: payload }, api.log);
8403
+ void deliver("budget.threshold", { busEvent: eventName, detail: payload });
8219
8404
  });
8220
8405
  state22.eventUnsubscribers.push(off);
8221
8406
  }
@@ -8240,26 +8425,23 @@ var plugin25 = {
8240
8425
  mutating: true,
8241
8426
  async execute(input) {
8242
8427
  if (!cfg.enabled) return { ok: false, error: "notify-hub is disabled" };
8243
- if (!cfg.webhookUrl) {
8428
+ const ch = state22.channel;
8429
+ if (!ch) {
8244
8430
  return {
8245
8431
  ok: false,
8246
8432
  error: 'no webhookUrl configured \u2014 set config.extensions["notify-hub"].webhookUrl to enable deliveries'
8247
8433
  };
8248
8434
  }
8249
- const delivered = await deliver(
8250
- cfg,
8251
- "manual",
8252
- {
8253
- title: truncateText(String(input.title ?? "WrongStack notification"), 200),
8254
- message: truncateText(String(input.message ?? ""), 2e3),
8255
- level: input.level === "warning" || input.level === "critical" ? input.level : "info"
8256
- },
8257
- api.log
8258
- );
8435
+ const result = await deliverViaChannel(ch, "manual", {
8436
+ title: truncateText(String(input.title ?? "WrongStack notification"), 200),
8437
+ body: truncateText(String(input.message ?? ""), 2e3),
8438
+ level: input.level === "warning" || input.level === "critical" ? input.level : "info",
8439
+ source: "manual"
8440
+ });
8259
8441
  return {
8260
- ok: delivered,
8261
- circuitOpen: state22.circuitOpen,
8262
- ...delivered ? {} : { error: "delivery failed (see notify_hub_status)" }
8442
+ ok: result.ok,
8443
+ circuitOpen: ch.circuitStatus().open,
8444
+ ...result.ok ? {} : { error: result.error ?? "delivery failed" }
8263
8445
  };
8264
8446
  }
8265
8447
  });
@@ -8271,18 +8453,26 @@ var plugin25 = {
8271
8453
  category: "Diagnostics",
8272
8454
  mutating: false,
8273
8455
  async execute() {
8456
+ const ch = state22.channel;
8457
+ const counters = ch?.counters() ?? {
8458
+ attempted: 0,
8459
+ delivered: 0,
8460
+ failed: 0,
8461
+ suppressed: 0
8462
+ };
8274
8463
  return {
8275
8464
  ok: true,
8276
8465
  enabled: cfg.enabled,
8277
8466
  webhookConfigured: cfg.webhookUrl.length > 0,
8278
8467
  events: cfg.events,
8279
8468
  timeoutMs: cfg.timeoutMs,
8280
- circuitOpen: state22.circuitOpen,
8469
+ circuitOpen: ch?.circuitStatus().open ?? false,
8281
8470
  counters: {
8282
- sent: state22.sent,
8283
- failed: state22.failed,
8284
- suppressed: state22.suppressed,
8285
- consecutiveFailures: state22.consecutiveFailures
8471
+ sent: counters.delivered,
8472
+ failed: counters.failed,
8473
+ suppressed: counters.suppressed,
8474
+ consecutiveFailures: ch?.circuitStatus().consecutiveFailures ?? 0,
8475
+ totalAttempted: counters.attempted
8286
8476
  },
8287
8477
  lastDelivery: state22.lastDelivery
8288
8478
  };
@@ -8296,6 +8486,8 @@ var plugin25 = {
8296
8486
  });
8297
8487
  },
8298
8488
  teardown(api) {
8489
+ _pluginLog = null;
8490
+ state22.circuitWarned = false;
8299
8491
  if (state22.stopHookUnregister) {
8300
8492
  try {
8301
8493
  state22.stopHookUnregister();
@@ -8310,25 +8502,22 @@ var plugin25 = {
8310
8502
  }
8311
8503
  }
8312
8504
  state22.eventUnsubscribers = [];
8313
- const final = { sent: state22.sent, failed: state22.failed, suppressed: state22.suppressed };
8314
- state22.sent = 0;
8315
- state22.failed = 0;
8316
- state22.suppressed = 0;
8317
- state22.consecutiveFailures = 0;
8318
- state22.circuitOpen = false;
8505
+ const channelCounters = state22.channel?.counters();
8506
+ state22.channel = null;
8319
8507
  state22.lastDelivery = null;
8320
- api.log.info("notify-hub: teardown complete", { final });
8508
+ api.log.info("notify-hub: teardown complete", { channelCounters });
8321
8509
  },
8322
8510
  async health() {
8511
+ const ch = state22.channel;
8512
+ const cs = ch?.circuitStatus();
8513
+ const channelCounters = ch?.counters();
8514
+ const sent = channelCounters?.delivered ?? 0;
8515
+ const failed = channelCounters?.failed ?? 0;
8516
+ const suppressed = channelCounters?.suppressed ?? 0;
8323
8517
  return {
8324
- ok: !state22.circuitOpen,
8325
- 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`,
8326
- counters: {
8327
- sent: state22.sent,
8328
- failed: state22.failed,
8329
- suppressed: state22.suppressed,
8330
- consecutiveFailures: state22.consecutiveFailures
8331
- }
8518
+ ok: !cs?.open,
8519
+ 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`,
8520
+ counters: { sent, failed, suppressed, consecutiveFailures: cs?.consecutiveFailures ?? 0 }
8332
8521
  };
8333
8522
  }
8334
8523
  };
@@ -8859,7 +9048,7 @@ function readConfig23(raw) {
8859
9048
  var plugin_stack_observer_default = PLUGIN;
8860
9049
 
8861
9050
  // src/pr-drafter/index.ts
8862
- import { execFileSync as execFileSync4 } from "node:child_process";
9051
+ import { execFileSync as execFileSync2 } from "node:child_process";
8863
9052
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
8864
9053
  import { dirname as dirname5, isAbsolute as isAbsolute10, relative as relative9, resolve as resolve9 } from "node:path";
8865
9054
  var API_VERSION13 = "^0.1.10";
@@ -8908,7 +9097,7 @@ function resolveProjectPath6(rawPath, cwd = process.cwd()) {
8908
9097
  }
8909
9098
  function gitBranch() {
8910
9099
  try {
8911
- const out = execFileSync4("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
9100
+ const out = execFileSync2("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
8912
9101
  encoding: "utf-8",
8913
9102
  cwd: process.cwd(),
8914
9103
  stdio: ["pipe", "pipe", "ignore"],
@@ -8921,7 +9110,7 @@ function gitBranch() {
8921
9110
  }
8922
9111
  function gitDiff() {
8923
9112
  try {
8924
- return execFileSync4("git", ["diff", "--stat"], {
9113
+ return execFileSync2("git", ["diff", "--stat"], {
8925
9114
  encoding: "utf-8",
8926
9115
  cwd: process.cwd(),
8927
9116
  stdio: ["pipe", "pipe", "ignore"],
@@ -9921,8 +10110,8 @@ var secret_scanner_default = plugin30;
9921
10110
  // src/semver-bump/index.ts
9922
10111
  import { expectDefined as expectDefined2 } from "@wrongstack/core";
9923
10112
  import { toErrorMessage } from "@wrongstack/core/utils";
9924
- import { execFileSync as execFileSync5 } from "node:child_process";
9925
- import { existsSync as existsSync5, readFileSync as readFileSync7, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
10113
+ import { execFileSync as execFileSync3 } from "node:child_process";
10114
+ import { existsSync as existsSync4, readFileSync as readFileSync7, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
9926
10115
  import { isAbsolute as isAbsolute11, join as join3, relative as relative10, resolve as resolve10 } from "node:path";
9927
10116
  var API_VERSION14 = "^0.1.10";
9928
10117
  function resolveProjectRoot(rawCwd, root = process.cwd()) {
@@ -9943,7 +10132,7 @@ var state29 = {
9943
10132
  };
9944
10133
  function runGit3(args, cwd) {
9945
10134
  try {
9946
- return execFileSync5("git", args, {
10135
+ return execFileSync3("git", args, {
9947
10136
  encoding: "utf-8",
9948
10137
  cwd,
9949
10138
  stdio: ["pipe", "pipe", "pipe"],
@@ -9958,7 +10147,7 @@ function runGit3(args, cwd) {
9958
10147
  }
9959
10148
  function getPackageJson(cwd) {
9960
10149
  const path = cwd ? `${cwd}/package.json` : "package.json";
9961
- if (!existsSync5(path)) return null;
10150
+ if (!existsSync4(path)) return null;
9962
10151
  try {
9963
10152
  return JSON.parse(readFileSync7(path, "utf-8"));
9964
10153
  } catch {
@@ -9968,14 +10157,14 @@ function getPackageJson(cwd) {
9968
10157
  function collectManifests(root) {
9969
10158
  const paths = [];
9970
10159
  const rootPkg = join3(root, "package.json");
9971
- if (existsSync5(rootPkg)) paths.push(rootPkg);
10160
+ if (existsSync4(rootPkg)) paths.push(rootPkg);
9972
10161
  for (const group of ["packages", "apps"]) {
9973
10162
  const groupDir = join3(root, group);
9974
- if (!existsSync5(groupDir)) continue;
10163
+ if (!existsSync4(groupDir)) continue;
9975
10164
  for (const entry of readdirSync2(groupDir, { withFileTypes: true })) {
9976
10165
  if (!entry.isDirectory()) continue;
9977
10166
  const candidate = join3(groupDir, entry.name, "package.json");
9978
- if (existsSync5(candidate)) paths.push(candidate);
10167
+ if (existsSync4(candidate)) paths.push(candidate);
9979
10168
  }
9980
10169
  }
9981
10170
  return paths;
@@ -10167,9 +10356,9 @@ var plugin31 = {
10167
10356
  const root = cwd ?? process.cwd();
10168
10357
  const bumpScript = join3(root, "scripts", "bump-version.mjs");
10169
10358
  const changed = collectManifests(root);
10170
- if (existsSync5(bumpScript)) {
10359
+ if (existsSync4(bumpScript)) {
10171
10360
  try {
10172
- execFileSync5(process.execPath, [bumpScript, "set", newVersion], {
10361
+ execFileSync3(process.execPath, [bumpScript, "set", newVersion], {
10173
10362
  cwd: root,
10174
10363
  stdio: ["pipe", "pipe", "pipe"],
10175
10364
  timeout: 3e4,
@@ -10181,7 +10370,7 @@ var plugin31 = {
10181
10370
  }
10182
10371
  for (const rel of ["package.json", "package-lock.json", "src/lib/utils.ts", "index.html"]) {
10183
10372
  const p = join3(root, "website", rel);
10184
- if (existsSync5(p)) changed.push(p);
10373
+ if (existsSync4(p)) changed.push(p);
10185
10374
  }
10186
10375
  } else {
10187
10376
  for (const manifest of changed) {
@@ -10859,8 +11048,8 @@ Tokens: ${recap.tokens.total.input} in / ${recap.tokens.total.output} out
10859
11048
  var session_recap_default = plugin32;
10860
11049
 
10861
11050
  // src/shell-check/index.ts
10862
- import { execFileSync as execFileSync6, execSync as execSync2 } from "node:child_process";
10863
- import { existsSync as existsSync6, readdirSync as readdirSync3 } from "node:fs";
11051
+ import { execFile as execFile7 } from "node:child_process";
11052
+ import { readdir } from "node:fs/promises";
10864
11053
  import { isAbsolute as isAbsolute12, join as join4, relative as relative11, resolve as resolve11 } from "node:path";
10865
11054
  var API_VERSION15 = "^0.1.10";
10866
11055
  function withinProject3(p) {
@@ -10881,15 +11070,23 @@ var state31 = {
10881
11070
  /** Most recent run summary, surfaced by health(). */
10882
11071
  lastRun: null
10883
11072
  };
10884
- function runShellCheck(files, severity, cwd) {
10885
- if (!existsSync6("shellcheck")) {
10886
- try {
10887
- execSync2("shellcheck --version", { encoding: "utf-8", stdio: "ignore", windowsHide: true });
10888
- } catch {
10889
- throw new Error(
10890
- "shellcheck is not installed. Install via: apt install shellcheck / brew install shellcheck"
11073
+ async function runShellCheck(files, severity, cwd) {
11074
+ try {
11075
+ await new Promise((resolvePromise, rejectPromise) => {
11076
+ execFile7(
11077
+ "shellcheck",
11078
+ ["--version"],
11079
+ { encoding: "utf-8", windowsHide: true },
11080
+ (err) => {
11081
+ if (err) rejectPromise(err);
11082
+ else resolvePromise();
11083
+ }
10891
11084
  );
10892
- }
11085
+ });
11086
+ } catch {
11087
+ throw new Error(
11088
+ "shellcheck is not installed. Install via: apt install shellcheck / brew install shellcheck"
11089
+ );
10893
11090
  }
10894
11091
  const levelMap = {
10895
11092
  error: "error",
@@ -10901,20 +11098,34 @@ function runShellCheck(files, severity, cwd) {
10901
11098
  const args = ["-f", "json", "-S", severityFlag, ...files];
10902
11099
  let raw;
10903
11100
  try {
10904
- raw = execFileSync6("shellcheck", args, {
10905
- encoding: "utf-8",
10906
- cwd,
10907
- stdio: ["pipe", "pipe", "pipe"],
10908
- timeout: 6e4,
10909
- windowsHide: true
11101
+ raw = await new Promise((resolvePromise, rejectPromise) => {
11102
+ execFile7(
11103
+ "shellcheck",
11104
+ args,
11105
+ {
11106
+ encoding: "utf-8",
11107
+ cwd,
11108
+ windowsHide: true,
11109
+ timeout: 6e4,
11110
+ maxBuffer: 16 * 1024 * 1024
11111
+ },
11112
+ (err, stdout, stderr) => {
11113
+ if (err) {
11114
+ const e = err;
11115
+ const diagnostic = stdout || e.stdout?.toString() || stderr || e.stderr?.toString() || "";
11116
+ if (diagnostic.trim()) {
11117
+ resolvePromise(diagnostic);
11118
+ return;
11119
+ }
11120
+ rejectPromise(err);
11121
+ return;
11122
+ }
11123
+ resolvePromise(stdout);
11124
+ }
11125
+ );
10910
11126
  });
10911
- } catch (err) {
10912
- const e = err;
10913
- if (e.stderr && !e.stderr.includes("shellcheck")) {
10914
- raw = e.stderr;
10915
- } else {
10916
- return [];
10917
- }
11127
+ } catch {
11128
+ return [];
10918
11129
  }
10919
11130
  if (!raw.trim()) return [];
10920
11131
  try {
@@ -10931,21 +11142,23 @@ function runShellCheck(files, severity, cwd) {
10931
11142
  return [];
10932
11143
  }
10933
11144
  }
10934
- function findShellFiles(dir, pattern) {
11145
+ async function findShellFiles(dir, pattern) {
10935
11146
  const results = [];
11147
+ let entries;
10936
11148
  try {
10937
- const entries = readdirSync3(dir, { withFileTypes: true });
10938
- for (const entry of entries) {
10939
- const full = join4(dir, entry.name);
10940
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
10941
- results.push(...findShellFiles(full, pattern));
10942
- } else if (entry.isFile() && (entry.name.endsWith(".sh") || entry.name === "Dockerfile")) {
10943
- if (!pattern || entry.name.includes(pattern)) {
10944
- results.push(full);
10945
- }
11149
+ entries = await readdir(dir, { withFileTypes: true });
11150
+ } catch {
11151
+ return results;
11152
+ }
11153
+ for (const entry of entries) {
11154
+ const full = join4(dir, entry.name);
11155
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
11156
+ results.push(...await findShellFiles(full, pattern));
11157
+ } else if (entry.isFile() && (entry.name.endsWith(".sh") || entry.name === "Dockerfile")) {
11158
+ if (!pattern || entry.name.includes(pattern)) {
11159
+ results.push(full);
10946
11160
  }
10947
11161
  }
10948
- } catch {
10949
11162
  }
10950
11163
  return results;
10951
11164
  }
@@ -11046,7 +11259,7 @@ var plugin33 = {
11046
11259
  if (files && files.length > 0) {
11047
11260
  checkFiles = files;
11048
11261
  } else {
11049
- checkFiles = findShellFiles(directory, pattern);
11262
+ checkFiles = await findShellFiles(directory, pattern);
11050
11263
  scannedDirectories = true;
11051
11264
  }
11052
11265
  if (checkFiles.length === 0) {
@@ -11067,7 +11280,7 @@ var plugin33 = {
11067
11280
  }
11068
11281
  let issues;
11069
11282
  try {
11070
- issues = runShellCheck(checkFiles, severity);
11283
+ issues = await runShellCheck(checkFiles, severity);
11071
11284
  } catch (err) {
11072
11285
  const msg = err instanceof Error ? err.message : String(err);
11073
11286
  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;