@wrongstack/plugins 0.313.1 → 0.316.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.
Files changed (48) hide show
  1. package/dist/accessibility-auditor.js +3 -2
  2. package/dist/agent-handoff.js +13 -3
  3. package/dist/auto-doc.js +5 -2
  4. package/dist/auto-i18n-extractor.js +2 -1
  5. package/dist/branch-guard.js +1 -1
  6. package/dist/changelog-writer.js +16 -4
  7. package/dist/checkpoint.js +2 -1
  8. package/dist/commit-validator.js +2 -2
  9. package/dist/config-validator.js +26 -1
  10. package/dist/cost-tracker.js +2 -1
  11. package/dist/cron.js +1 -1
  12. package/dist/dependency-vulnerability-gate.js +12 -0
  13. package/dist/diff-summary.js +1 -1
  14. package/dist/doc-sync-guard.js +8 -1
  15. package/dist/duplicate-code-detector.js +4 -3
  16. package/dist/error-lens.js +3 -3
  17. package/dist/git-autocommit.js +5 -2
  18. package/dist/gitignore-guard.js +6 -2
  19. package/dist/import-organizer.js +10 -4
  20. package/dist/index.js +304 -186
  21. package/dist/interface-contract-guard.js +12 -3
  22. package/dist/knowledge-graph.js +5 -1
  23. package/dist/llm-cache.js +3 -2
  24. package/dist/loop-breaker.js +1 -1
  25. package/dist/migration-planner.js +0 -2
  26. package/dist/model-router.js +2 -0
  27. package/dist/notify-hub.js +5 -1
  28. package/dist/path-guard.js +2 -1
  29. package/dist/performance-regression-gate.js +2 -0
  30. package/dist/plugin-stack-observer.js +1 -0
  31. package/dist/pr-drafter.js +2 -2
  32. package/dist/process-guard.js +1 -2
  33. package/dist/prompt-firewall.js +3 -2
  34. package/dist/refactor-suggester.js +11 -0
  35. package/dist/security-hotspot-scanner.js +5 -5
  36. package/dist/semver-bump.js +3 -1
  37. package/dist/smart-rename.js +2 -2
  38. package/dist/spec-linker.js +1 -1
  39. package/dist/template-engine.js +4 -4
  40. package/dist/test-coverage-gate.js +5 -4
  41. package/dist/test-flake-detector.js +22 -8
  42. package/dist/test-generator.js +2 -2
  43. package/dist/todo-listener.js +3 -2
  44. package/dist/todo-tracker.js +2 -1
  45. package/package.json +5 -5
  46. package/dist/runtime/bounded-map.d.ts +0 -3
  47. package/dist/runtime/local-bin.d.ts +0 -3
  48. package/dist/runtime/safe-json.d.ts +0 -3
package/dist/index.js CHANGED
@@ -300,8 +300,9 @@ var plugin = {
300
300
  if (!cfg.enabled || !cfg.onWriteEdit) return;
301
301
  if (input.toolResult?.isError) return;
302
302
  const inp = input.toolInput ?? {};
303
- const sourcePath = inp["path"];
304
- if (!sourcePath || typeof sourcePath !== "string") return;
303
+ const rawPath = inp["path"] ?? inp["filePath"] ?? inp["file_path"];
304
+ const sourcePath = typeof rawPath === "string" ? rawPath : void 0;
305
+ if (!sourcePath) return;
305
306
  if (!(0, runtime_exports.withinProject)(sourcePath)) return;
306
307
  const exts = normalizeExtensions(cfg.includeExtensions);
307
308
  if (!(0, runtime_exports.matchesExtension)(sourcePath, exts)) return;
@@ -520,12 +521,22 @@ function buildBody(payload, cfg) {
520
521
  return truncate(lines.join("\n"), cfg.maxBodyChars);
521
522
  }
522
523
  async function sendHandoff(cfg, mailbox, payload) {
523
- const body = buildBody(payload, cfg);
524
- const hash = hashString(body);
524
+ const hash = hashString(
525
+ (0, runtime_exports.safeJsonStringify)({
526
+ agentId: payload.agentId,
527
+ agentName: payload.agentName,
528
+ task: payload.task,
529
+ status: payload.status,
530
+ summary: payload.summary,
531
+ result: payload.result,
532
+ todos: payload.todos
533
+ })
534
+ );
525
535
  if (hash === state2.lastPayloadHash) {
526
536
  state2.skippedCount += 1;
527
537
  return;
528
538
  }
539
+ const body = buildBody(payload, cfg);
529
540
  const subject = `${cfg.subjectPrefix}${payload.agentName ?? payload.agentId ?? "subagent"} \u2014 ${payload.status ?? "done"}`.slice(
530
541
  0,
531
542
  200
@@ -540,7 +551,7 @@ async function sendHandoff(cfg, mailbox, payload) {
540
551
  };
541
552
  const result = await mailbox.send(sendInput);
542
553
  state2.sentCount += 1;
543
- state2.lastMessageId = result.id ?? null;
554
+ state2.lastMessageId = result?.id ?? null;
544
555
  state2.lastPayloadHash = hash;
545
556
  }
546
557
  var plugin2 = {
@@ -1247,8 +1258,11 @@ function needsDocComment(content, entity) {
1247
1258
  const lines = content.split("\n");
1248
1259
  const lineIdx = entity.startLine - 1;
1249
1260
  if (lineIdx < 1) return true;
1250
- const prevLine = lines[lineIdx - 1] ?? "";
1251
- return !/^\s*\/\*\*\s*$/.test(prevLine.trim());
1261
+ const prevLine = lines[lineIdx - 1]?.trim() ?? "";
1262
+ if (/^\s*\*\/\s*$/.test(prevLine) || /^\s*\/\*\*.*?\*\/\s*$/.test(prevLine) || /^\s*\/\*\*/.test(prevLine)) {
1263
+ return false;
1264
+ }
1265
+ return true;
1252
1266
  }
1253
1267
  function injectDocComment(content, entity, doc) {
1254
1268
  const lines = content.split("\n");
@@ -1288,10 +1302,10 @@ async function runAutoDoc(input, api) {
1288
1302
  continue;
1289
1303
  }
1290
1304
  try {
1291
- const { readFileSync: readFileSync16, writeFileSync: writeFileSync3 } = await import("node:fs");
1305
+ const { readFileSync: readFileSync17, writeFileSync: writeFileSync3 } = await import("node:fs");
1292
1306
  let content;
1293
1307
  try {
1294
- content = readFileSync16(safeFile, "utf-8");
1308
+ content = readFileSync17(safeFile, "utf-8");
1295
1309
  } catch {
1296
1310
  api.log.warn(`auto-doc: could not read file ${safeFile}`);
1297
1311
  continue;
@@ -1702,7 +1716,7 @@ function looksLikeUserText(value, minLength) {
1702
1716
  function isExcludedContext(line, quoteIndex, excludeAttributes) {
1703
1717
  const before = line.slice(0, quoteIndex);
1704
1718
  for (const attr of excludeAttributes) {
1705
- const re = new RegExp(`\\b${attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=\\s*["'{]?$`);
1719
+ const re = new RegExp(`\\b${attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=\\s*(?:\\{\\s*)?["']?$`);
1706
1720
  if (re.test(before)) return true;
1707
1721
  }
1708
1722
  return false;
@@ -1713,6 +1727,7 @@ function extractStrings(content, cfg) {
1713
1727
  const stringRegex = /(["'`])((?:\\.|(?!\1)[^\\])*?)\1/g;
1714
1728
  for (let i = 0; i < lines.length; i += 1) {
1715
1729
  const line = lines[i];
1730
+ if (/^\s*(?:import\b|export\b.*?from\b|const\s+.*=\s*require\()/.test(line)) continue;
1716
1731
  stringRegex.lastIndex = 0;
1717
1732
  for (const match of line.matchAll(stringRegex)) {
1718
1733
  const value = match[2] ?? "";
@@ -2014,16 +2029,16 @@ function hasDisabledPluginEntry(raw) {
2014
2029
  return name === "branch-guard" || name === "@wrongstack/plugins/branch-guard";
2015
2030
  });
2016
2031
  }
2017
- var branchCache = new runtime_exports.BoundedMap({ max: 64, ttlMs: 3e4 });
2032
+ var branchCache = new runtime_exports.BoundedMap({ max: 64, ttlMs: 2e3 });
2018
2033
  function runGit(args, cwd, signal) {
2019
- return new Promise((resolve27, reject) => {
2034
+ return new Promise((resolve28, reject) => {
2020
2035
  execFile2(
2021
2036
  "git",
2022
2037
  args,
2023
2038
  { encoding: "utf-8", timeout: 3e3, cwd, windowsHide: true, signal },
2024
2039
  (error, stdout) => {
2025
2040
  if (error) reject(error);
2026
- else resolve27(stdout);
2041
+ else resolve28(stdout);
2027
2042
  }
2028
2043
  );
2029
2044
  });
@@ -2344,10 +2359,10 @@ function commitToEntry(subject) {
2344
2359
  }
2345
2360
  function commitSubjectFromCommand(command) {
2346
2361
  if (!/\bgit\s+commit\b/.test(command)) return null;
2347
- const m = /-m\s+(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(command);
2348
- const subject = m?.[1] ?? m?.[2] ?? m?.[3] ?? null;
2349
- if (!subject) return null;
2350
- return subject.split("\n")[0]?.trim() || null;
2362
+ const m = /(?:-m|--message)\s+(?:"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'|(\S+))/.exec(command);
2363
+ const rawSubject = m?.[1] ? m[1].replace(/\\"/g, '"') : m?.[2] ?? m?.[3] ?? null;
2364
+ if (!rawSubject) return null;
2365
+ return rawSubject.split("\n")[0]?.trim() || null;
2351
2366
  }
2352
2367
  var CHANGELOG_HEADER = `# Changelog
2353
2368
 
@@ -2486,6 +2501,18 @@ var plugin8 = {
2486
2501
  if (typeof raw === "string" && raw) state8.filesTouched.add(raw);
2487
2502
  return;
2488
2503
  }
2504
+ if (toolName === "git_autocommit") {
2505
+ const type = typeof input["type"] === "string" ? input["type"] : "";
2506
+ const msg = typeof input["message"] === "string" ? input["message"] : "";
2507
+ const subject = msg ? type && !msg.startsWith(type) ? `${type}: ${msg}` : msg : null;
2508
+ if (subject) {
2509
+ state8.commitsSeen += 1;
2510
+ api.metrics.counter("commits_seen");
2511
+ const { section, text } = commitToEntry(subject);
2512
+ addEntry({ section, text, origin: "commit", when: (/* @__PURE__ */ new Date()).toISOString() });
2513
+ }
2514
+ return;
2515
+ }
2489
2516
  if (toolName === "bash" || toolName === "exec") {
2490
2517
  const command = typeof input["command"] === "string" ? input["command"] : "";
2491
2518
  const subject = command ? commitSubjectFromCommand(command) : null;
@@ -2956,7 +2983,8 @@ var plugin9 = {
2956
2983
  error: input.id ? `no snapshot with id "${input.id}"` : "no snapshots captured yet"
2957
2984
  };
2958
2985
  }
2959
- const targets = input.path ? snapshot.files.filter((f) => f.path === input.path) : snapshot.files;
2986
+ const targetPath = input.path ? resolveProjectPath3(input.path) ?? input.path : null;
2987
+ const targets = targetPath ? snapshot.files.filter((f) => f.path === targetPath || f.path === input.path) : snapshot.files;
2960
2988
  if (targets.length === 0) {
2961
2989
  return { ok: false, error: `snapshot ${snapshot.id} has no entry for "${input.path}"` };
2962
2990
  }
@@ -3490,7 +3518,7 @@ function extractMessageFromBash(command) {
3490
3518
  if (value !== void 0) parts.push(value);
3491
3519
  }
3492
3520
  if (parts.length === 0) return null;
3493
- return parts.join("\n");
3521
+ return parts.join("\n\n");
3494
3522
  }
3495
3523
  var plugin11 = {
3496
3524
  name: "commit-validator",
@@ -3628,7 +3656,7 @@ Examples:
3628
3656
  };
3629
3657
  }
3630
3658
  let baseContext = `
3631
- ?? commit-validator: commit message has ${parsed.errors.length} issue(s):
3659
+ \u26A0\uFE0F commit-validator: commit message has ${parsed.errors.length} issue(s):
3632
3660
  ${errorList}
3633
3661
  Expected: <type>[(scope)][!]: <description>`;
3634
3662
  if (cfg.suggestFix && api.llm) {
@@ -3812,9 +3840,34 @@ function stripJsonc(text) {
3812
3840
  i += 1;
3813
3841
  continue;
3814
3842
  }
3843
+ if (ch === ",") {
3844
+ let j = i + 1;
3845
+ let isTrailing = false;
3846
+ while (j < text.length) {
3847
+ const c = text[j];
3848
+ if (c === " " || c === " " || c === "\r" || c === "\n") {
3849
+ j++;
3850
+ } else if (c === "/" && text[j + 1] === "/") {
3851
+ j += 2;
3852
+ while (j < text.length && text[j] !== "\n") j++;
3853
+ } else if (c === "/" && text[j + 1] === "*") {
3854
+ j += 2;
3855
+ while (j < text.length && !(text[j] === "*" && text[j + 1] === "/")) j++;
3856
+ j += 2;
3857
+ } else if (c === "}" || c === "]") {
3858
+ isTrailing = true;
3859
+ break;
3860
+ } else {
3861
+ break;
3862
+ }
3863
+ }
3864
+ if (isTrailing) {
3865
+ continue;
3866
+ }
3867
+ }
3815
3868
  out += ch;
3816
3869
  }
3817
- return out.replace(/,\s*([}\]])/g, "$1");
3870
+ return out;
3818
3871
  }
3819
3872
  function positionToLineCol(text, pos) {
3820
3873
  const upTo = text.slice(0, pos);
@@ -4376,7 +4429,8 @@ function estimateCost(model, freshTokens, completionTokens, cachedTokens = 0) {
4376
4429
  key = model.toLowerCase();
4377
4430
  modelKeyCache.set(model, key);
4378
4431
  }
4379
- const pricing = pricingOverrides[key] ?? bundledFromRegistry[key] ?? PRICING[key] ?? DEFAULT_PRICING;
4432
+ const unnamespaced = key.includes("/") ? key.split("/").pop() : key;
4433
+ const pricing = pricingOverrides[key] ?? pricingOverrides[unnamespaced] ?? bundledFromRegistry[key] ?? bundledFromRegistry[unnamespaced] ?? PRICING[key] ?? PRICING[unnamespaced] ?? DEFAULT_PRICING;
4380
4434
  const inputCost = freshTokens / 1e6 * pricing.input + cachedTokens / 1e6 * (pricing.cacheRead ?? pricing.input);
4381
4435
  const outputCost = completionTokens / 1e6 * pricing.output;
4382
4436
  return inputCost + outputCost;
@@ -4732,7 +4786,7 @@ var state14 = {
4732
4786
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
4733
4787
  };
4734
4788
  function formatNextRun(intervalMs) {
4735
- const ms = Number.isNaN(intervalMs) || intervalMs <= 0 ? 6e4 : intervalMs;
4789
+ const ms = Number.isNaN(intervalMs) || !Number.isFinite(intervalMs) || intervalMs <= 0 ? 6e4 : intervalMs;
4736
4790
  return new Date(Date.now() + ms).toISOString();
4737
4791
  }
4738
4792
  function buildSnapshot(s, maxConcurrent) {
@@ -5788,6 +5842,18 @@ function parseAuditJson(jsonString) {
5788
5842
  }
5789
5843
  }
5790
5844
  }
5845
+ const advisories = parsed["advisories"];
5846
+ if (advisories && typeof advisories === "object" && !Array.isArray(advisories)) {
5847
+ recognizedShape = true;
5848
+ for (const entry of Object.values(advisories)) {
5849
+ if (entry && typeof entry === "object") {
5850
+ const severity = entry["severity"];
5851
+ if (typeof severity === "string") {
5852
+ collectSeverities(out, severity);
5853
+ }
5854
+ }
5855
+ }
5856
+ }
5791
5857
  if (out.total === 0) {
5792
5858
  const metadata = parsed["metadata"];
5793
5859
  const metaVulns = metadata && typeof metadata === "object" ? metadata["vulnerabilities"] : void 0;
@@ -5861,7 +5927,7 @@ async function runAudit(cfg) {
5861
5927
  } catch {
5862
5928
  return null;
5863
5929
  }
5864
- const stdout = await new Promise((resolve27) => {
5930
+ const stdout = await new Promise((resolve28) => {
5865
5931
  execFile3(
5866
5932
  invocation.cmd,
5867
5933
  invocation.args,
@@ -5876,10 +5942,10 @@ async function runAudit(cfg) {
5876
5942
  },
5877
5943
  (error, output) => {
5878
5944
  if (error && error.killed) {
5879
- resolve27(null);
5945
+ resolve28(null);
5880
5946
  return;
5881
5947
  }
5882
- resolve27(output);
5948
+ resolve28(output);
5883
5949
  }
5884
5950
  );
5885
5951
  });
@@ -6256,7 +6322,7 @@ var plugin19 = {
6256
6322
  state18.fallbackCount += 1;
6257
6323
  return;
6258
6324
  }
6259
- const toolInputForHash = toolName === "edit" ? inp["new_string"] ?? "" : toolName === "write" ? inp["content"] ?? "" : "";
6325
+ const toolInputForHash = toolName === "edit" ? `${String(inp["old_string"] ?? "")}:::${String(inp["new_string"] ?? "")}` : toolName === "write" ? inp["content"] ?? "" : "";
6260
6326
  const now = Date.now();
6261
6327
  const memo = pathMemo.get(filePath);
6262
6328
  if (memo) {
@@ -6386,6 +6452,7 @@ var plugin19 = {
6386
6452
  var diff_summary_default = plugin19;
6387
6453
 
6388
6454
  // src/doc-sync-guard/index.ts
6455
+ import { existsSync as existsSync2, readFileSync as readFileSync6 } from "node:fs";
6389
6456
  import { basename as basename2, extname as extname3 } from "node:path";
6390
6457
  var API_VERSION13 = "^0.1.10";
6391
6458
  var state19 = {
@@ -6519,7 +6586,13 @@ var plugin20 = {
6519
6586
  }
6520
6587
  if (isDocFile(path, cfg.docNames)) {
6521
6588
  state19.docWrites += 1;
6522
- const content = extractDocContent(input.toolInput);
6589
+ let content = extractDocContent(input.toolInput);
6590
+ if (!content) {
6591
+ try {
6592
+ if (existsSync2(path)) content = readFileSync6(path, "utf-8");
6593
+ } catch {
6594
+ }
6595
+ }
6523
6596
  if (!content || state19.changedFiles.length === 0) return;
6524
6597
  const missing = state19.changedFiles.filter((changedPath) => !isReferenced(changedPath, content));
6525
6598
  if (missing.length === 0) return;
@@ -6598,7 +6671,7 @@ var doc_sync_guard_default = plugin20;
6598
6671
 
6599
6672
  // src/duplicate-code-detector/index.ts
6600
6673
  import { readFile as readFile6, realpath, stat as stat3 } from "node:fs/promises";
6601
- import { isAbsolute as isAbsolute8, relative as relative9, resolve as resolve9, sep } from "node:path";
6674
+ import { extname as extname4, isAbsolute as isAbsolute8, relative as relative9, resolve as resolve9, sep } from "node:path";
6602
6675
  var API_VERSION14 = "^0.1.10";
6603
6676
  var HOOK_WARNING_COOLDOWN_MS = 6e4;
6604
6677
  var state20 = {
@@ -6893,7 +6966,7 @@ var plugin21 = {
6893
6966
  return;
6894
6967
  }
6895
6968
  if (!isWithinRoot(projectRoot, changedFile)) return;
6896
- const ext = sourcePath.includes(".") ? sourcePath.slice(sourcePath.lastIndexOf(".")).toLowerCase() : "";
6969
+ const ext = extname4(sourcePath).toLowerCase();
6897
6970
  if (!extensionsSet.has(ext)) return;
6898
6971
  state20.hookInvocationCount += 1;
6899
6972
  const now = Date.now();
@@ -6916,8 +6989,9 @@ var plugin21 = {
6916
6989
  return;
6917
6990
  }
6918
6991
  const matched = /* @__PURE__ */ new Set();
6992
+ const resolvedChanged = resolve9(changedFile).toLowerCase();
6919
6993
  for (const p of otherFilePaths) {
6920
- if (resolve9(p) === resolve9(changedFile)) continue;
6994
+ if (resolve9(p).toLowerCase() === resolvedChanged) continue;
6921
6995
  const otherFps = await readCachedFingerprints(p, cfg.minLines);
6922
6996
  if (otherFps === null || otherFps.size === 0) continue;
6923
6997
  for (const fp of changedFps) {
@@ -7101,13 +7175,13 @@ var ERROR_LINE_PATTERNS = [
7101
7175
  ];
7102
7176
  var FRAME_PATTERNS = [
7103
7177
  // Node/V8: "at fn (path/file.ts:12:5)" or "at path/file.ts:12:5"
7104
- /\bat\s+(?:[^(\n]+\()?([^()\n:]+:\d+)(?::\d+)?\)?/g,
7178
+ /\bat\s+(?:[^(\n]+\()?((?:[A-Za-z]:[\\/])?[^()\n:]+:\d+)(?::\d+)?\)?/g,
7105
7179
  // Python: File "path/file.py", line 12
7106
7180
  /File "([^"]+)", line (\d+)/g,
7107
7181
  // tsc/vitest/eslint bare: "src/foo.ts:12:5" (require a path-ish prefix)
7108
- /(?:^|[ \t(])([A-Za-z0-9_./\\-]+\.[a-z]{1,4}:\d+)(?::\d+)?/gm,
7182
+ /(?:^|[ \t(])((?:[A-Za-z]:[\\/])?[A-Za-z0-9_./\\-]+\.[a-z]{1,4}:\d+)(?::\d+)?/gm,
7109
7183
  // Rust: "--> src/main.rs:4:5"
7110
- /-->\s+([^\s:]+:\d+)/g
7184
+ /-->\s+((?:[A-Za-z]:[\\/])?[^\s:]+:\d+)/g
7111
7185
  ];
7112
7186
  function extractErrorLine(output) {
7113
7187
  for (const re of ERROR_LINE_PATTERNS) {
@@ -8115,7 +8189,7 @@ async function formatFile(filePath, timeoutMs) {
8115
8189
  return null;
8116
8190
  }
8117
8191
  try {
8118
- await new Promise((resolve27, reject) => {
8192
+ await new Promise((resolve28, reject) => {
8119
8193
  execFile5(
8120
8194
  invocation.cmd,
8121
8195
  invocation.args,
@@ -8132,7 +8206,7 @@ async function formatFile(filePath, timeoutMs) {
8132
8206
  const e = err;
8133
8207
  if (e.killed) return reject(err);
8134
8208
  }
8135
- resolve27();
8209
+ resolve28();
8136
8210
  }
8137
8211
  );
8138
8212
  });
@@ -8351,7 +8425,8 @@ var format_on_save_default = plugin25;
8351
8425
 
8352
8426
  // src/git-autocommit/index.ts
8353
8427
  import { execFile as execFile6 } from "node:child_process";
8354
- import { existsSync as existsSync2 } from "node:fs";
8428
+ import { existsSync as existsSync3 } from "node:fs";
8429
+ import { resolve as resolve11 } from "node:path";
8355
8430
  var API_VERSION18 = "^0.1.10";
8356
8431
  var commitCount = { value: 0 };
8357
8432
  var lastCommit = { hash: null, at: null };
@@ -8459,12 +8534,14 @@ async function stageFiles(files, cwd) {
8459
8534
  if (!hasPattern) {
8460
8535
  const existing = files.filter((f) => {
8461
8536
  try {
8462
- return existsSync2(f);
8537
+ return existsSync3(cwd ? resolve11(cwd, f) : f);
8463
8538
  } catch {
8464
8539
  return false;
8465
8540
  }
8466
8541
  });
8467
- if (existing.length === 0) throw new Error("No files exist to stage");
8542
+ if (existing.length === 0) {
8543
+ throw new Error("Failed to stage files: none of the specified files exist on disk");
8544
+ }
8468
8545
  await runGit3(["add", "--", ...existing], cwd);
8469
8546
  return;
8470
8547
  }
@@ -8982,7 +9059,7 @@ var git_autocommit_default = plugin26;
8982
9059
 
8983
9060
  // src/gitignore-guard/index.ts
8984
9061
  import { access as access3, readFile as readFile9, writeFile as writeFile2 } from "node:fs/promises";
8985
- import { basename as basename3, isAbsolute as isAbsolute10, join as join3, relative as relative11, resolve as resolve11, sep as sep2 } from "node:path";
9062
+ import { basename as basename3, isAbsolute as isAbsolute10, join as join3, relative as relative11, resolve as resolve12, sep as sep2 } from "node:path";
8986
9063
  var API_VERSION19 = "^0.1.10";
8987
9064
  var DEFAULT_ARTIFACT_PATTERNS = Object.freeze([
8988
9065
  "dist/",
@@ -9018,9 +9095,13 @@ function globToRegExp(glob) {
9018
9095
  return new RegExp(`^${globToSource(glob)}$`);
9019
9096
  }
9020
9097
  function matchGitignorePattern(relPath, pattern) {
9021
- const rel = toForwardSlashes(relPath);
9022
- const pat = pattern.trim();
9098
+ const rel = toForwardSlashes(relPath).replace(/^\//, "");
9099
+ let pat = pattern.trim();
9023
9100
  if (pat.length === 0 || pat.startsWith("#")) return false;
9101
+ if (pat.startsWith("/")) {
9102
+ pat = pat.slice(1);
9103
+ if (pat.length === 0) return false;
9104
+ }
9024
9105
  const segments = rel.split("/");
9025
9106
  if (pat.endsWith("/")) {
9026
9107
  const dir = pat.slice(0, -1);
@@ -9104,7 +9185,7 @@ async function appendPatterns(target, patterns, limit) {
9104
9185
  }
9105
9186
  function projectRelativePath(rawPath, cwd) {
9106
9187
  const root = cwd ?? process.cwd();
9107
- const abs = isAbsolute10(rawPath) ? rawPath : resolve11(root, rawPath);
9188
+ const abs = isAbsolute10(rawPath) ? rawPath : resolve12(root, rawPath);
9108
9189
  const rel = toForwardSlashes(relative11(root, abs));
9109
9190
  if (rel.startsWith("..") || isAbsolute10(rel) || rel === "") return null;
9110
9191
  return { abs, rel, root };
@@ -9417,7 +9498,7 @@ var gitignore_guard_default = plugin27;
9417
9498
 
9418
9499
  // src/import-organizer/index.ts
9419
9500
  import { spawn } from "node:child_process";
9420
- import { existsSync as existsSync3, statSync as statSync2 } from "node:fs";
9501
+ import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
9421
9502
  import { basename as basename4, isAbsolute as isAbsolute11 } from "node:path";
9422
9503
  var ALLOWED_FIRST_TOKENS = /* @__PURE__ */ new Set([
9423
9504
  "npx",
@@ -9522,13 +9603,13 @@ function readConfig23(raw) {
9522
9603
  }
9523
9604
  var MAX_CAPTURE_BYTES = 4 * 1024 * 1024;
9524
9605
  function runCommand(command, args, timeoutMs, cwd) {
9525
- return new Promise((resolve27) => {
9606
+ return new Promise((resolve28) => {
9526
9607
  let timedOut = false;
9527
9608
  let settled = false;
9528
9609
  const settle = (r) => {
9529
9610
  if (settled) return;
9530
9611
  settled = true;
9531
- resolve27(r);
9612
+ resolve28(r);
9532
9613
  };
9533
9614
  const stdoutChunks = [];
9534
9615
  const stderrChunks = [];
@@ -9588,10 +9669,13 @@ function runCommand(command, args, timeoutMs, cwd) {
9588
9669
  }
9589
9670
  async function organizeImports(filePath, cfg, cwd) {
9590
9671
  if (!(0, runtime_exports.withinProject)(filePath)) return null;
9591
- if (!existsSync3(filePath)) return null;
9672
+ if (!existsSync4(filePath)) return null;
9592
9673
  let bytesBefore;
9674
+ let mtimeBefore;
9593
9675
  try {
9594
- bytesBefore = statSync2(filePath).size;
9676
+ const st = statSync2(filePath);
9677
+ bytesBefore = st.size;
9678
+ mtimeBefore = st.mtimeMs;
9595
9679
  } catch {
9596
9680
  return null;
9597
9681
  }
@@ -9611,13 +9695,16 @@ async function organizeImports(filePath, cfg, cwd) {
9611
9695
  if (result.timedOut || result.code === null) return null;
9612
9696
  if (result.code === 127) return null;
9613
9697
  let bytesAfter;
9698
+ let mtimeAfter;
9614
9699
  try {
9615
- bytesAfter = statSync2(filePath).size;
9700
+ const st = statSync2(filePath);
9701
+ bytesAfter = st.size;
9702
+ mtimeAfter = st.mtimeMs;
9616
9703
  } catch {
9617
9704
  return null;
9618
9705
  }
9619
9706
  return {
9620
- changed: bytesAfter !== bytesBefore,
9707
+ changed: bytesAfter !== bytesBefore || mtimeAfter > mtimeBefore,
9621
9708
  bytesBefore,
9622
9709
  bytesAfter,
9623
9710
  command: usedCommand,
@@ -9680,7 +9767,7 @@ var plugin28 = {
9680
9767
  const filePath = inp["path"];
9681
9768
  if (!filePath || typeof filePath !== "string") return;
9682
9769
  const ext = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")) : "";
9683
- if (![".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts"].includes(ext)) return;
9770
+ if (![".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts", ".cjs", ".cts"].includes(ext)) return;
9684
9771
  state25.invocationCount += 1;
9685
9772
  const result = await organizeImports(filePath, cfg, process.cwd());
9686
9773
  if (!result) {
@@ -10029,7 +10116,7 @@ var injection_shield_default = plugin29;
10029
10116
 
10030
10117
  // src/interface-contract-guard/index.ts
10031
10118
  import { readFile as readFile10 } from "node:fs/promises";
10032
- import { isAbsolute as isAbsolute12, relative as relative12, resolve as resolve12 } from "node:path";
10119
+ import { isAbsolute as isAbsolute12, relative as relative12, resolve as resolve13 } from "node:path";
10033
10120
  var API_VERSION21 = "^0.1.10";
10034
10121
  var state27 = {
10035
10122
  scanCount: 0,
@@ -10080,17 +10167,26 @@ function extractInterfaceNames(content) {
10080
10167
  }
10081
10168
  return names;
10082
10169
  }
10083
- var IMPLEMENTER_RE = /(?:implements|satisfies|\bas)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
10170
+ var IMPLEMENTS_EXTENDS_RE = /(?:\bimplements\b|\bextends\b)\s+([^{;=]+)/g;
10171
+ var SATISFIES_AS_RE = /(?:\bsatisfies\b|\bas\b)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
10172
+ var IDENTIFIER_RE = /[A-Za-z_$][A-Za-z0-9_$]*/g;
10084
10173
  function collectImplementedNames(content, into) {
10085
- IMPLEMENTER_RE.lastIndex = 0;
10086
- for (const m of content.matchAll(IMPLEMENTER_RE)) {
10174
+ for (const m of content.matchAll(IMPLEMENTS_EXTENDS_RE)) {
10175
+ const clause = m[1];
10176
+ if (clause) {
10177
+ for (const id of clause.matchAll(IDENTIFIER_RE)) {
10178
+ if (id[0]) into.add(id[0]);
10179
+ }
10180
+ }
10181
+ }
10182
+ for (const m of content.matchAll(SATISFIES_AS_RE)) {
10087
10183
  const name = m[1];
10088
10184
  if (name) into.add(name);
10089
10185
  }
10090
10186
  }
10091
10187
  async function scanPath3(rawPath, cfg) {
10092
10188
  const root = process.cwd();
10093
- const resolved = isAbsolute12(rawPath) ? resolve12(rawPath) : resolve12(root, rawPath);
10189
+ const resolved = isAbsolute12(rawPath) ? resolve13(rawPath) : resolve13(root, rawPath);
10094
10190
  const exts = normalizeExtensions4(cfg.extensions);
10095
10191
  const allFiles = await (0, runtime_exports.collectSourceFilesAsync)(resolved, { extensions: exts });
10096
10192
  const files = allFiles.slice(0, cfg.maxFiles);
@@ -10180,7 +10276,7 @@ var plugin30 = {
10180
10276
  const exts = normalizeExtensions4(cfg.extensions);
10181
10277
  if (!(0, runtime_exports.matchesExtension)(sourcePath, exts)) return;
10182
10278
  state27.hookInvocationCount += 1;
10183
- const resolved = resolve12(process.cwd(), sourcePath);
10279
+ const resolved = resolve13(process.cwd(), sourcePath);
10184
10280
  let content;
10185
10281
  try {
10186
10282
  content = await readFile10(resolved, "utf-8");
@@ -10228,7 +10324,7 @@ If you changed member shapes, search the project for implementers/\`satisfies\`/
10228
10324
  state27.findingCount += result.findings.length;
10229
10325
  return {
10230
10326
  ok: true,
10231
- path: relativePath5(resolve12(process.cwd(), rawPath)),
10327
+ path: relativePath5(resolve13(process.cwd(), rawPath)),
10232
10328
  scannedFiles: result.scannedFiles,
10233
10329
  findings: result.findings,
10234
10330
  // Say so when the corpus was cut short. A partial scan that
@@ -10308,8 +10404,8 @@ If you changed member shapes, search the project for implementers/\`satisfies\`/
10308
10404
  var interface_contract_guard_default = plugin30;
10309
10405
 
10310
10406
  // src/knowledge-graph/index.ts
10311
- import { readFileSync as readFileSync6 } from "node:fs";
10312
- import { dirname as dirname4, isAbsolute as isAbsolute13, relative as relative13, resolve as resolve13 } from "node:path";
10407
+ import { readFileSync as readFileSync7 } from "node:fs";
10408
+ import { dirname as dirname4, isAbsolute as isAbsolute13, relative as relative13, resolve as resolve14 } from "node:path";
10313
10409
  import { atomicWrite as atomicWrite2, ensureDir as ensureDir2 } from "@wrongstack/core/utils";
10314
10410
  var API_VERSION22 = "^0.1.10";
10315
10411
  var state28 = {
@@ -10331,8 +10427,8 @@ var DEFAULTS25 = {
10331
10427
  };
10332
10428
  function resolveProjectPath5(rawPath, cwd = process.cwd()) {
10333
10429
  if (typeof rawPath !== "string" || rawPath.length === 0) return null;
10334
- const root = resolve13(cwd);
10335
- const resolved = isAbsolute13(rawPath) ? resolve13(rawPath) : resolve13(root, rawPath);
10430
+ const root = resolve14(cwd);
10431
+ const resolved = isAbsolute13(rawPath) ? resolve14(rawPath) : resolve14(root, rawPath);
10336
10432
  const rel = relative13(root, resolved);
10337
10433
  if (rel === "" || !rel.startsWith("..") && !isAbsolute13(rel)) return resolved;
10338
10434
  return null;
@@ -10352,11 +10448,15 @@ function readConfig26(raw) {
10352
10448
  function loadFacts(filePath) {
10353
10449
  if (!filePath) return { facts: [], nextId: 1 };
10354
10450
  try {
10355
- const raw = JSON.parse(readFileSync6(filePath, "utf-8"));
10451
+ const raw = JSON.parse(readFileSync7(filePath, "utf-8"));
10356
10452
  const facts = Array.isArray(raw.facts) ? raw.facts.filter(
10357
10453
  (f) => !!f && typeof f === "object" && typeof f.id === "string" && typeof f.subject === "string" && typeof f.relation === "string" && typeof f.object === "string"
10358
10454
  ) : [];
10359
- const nextId2 = typeof raw.nextId === "number" && raw.nextId >= 1 ? raw.nextId : facts.length + 1;
10455
+ const maxExistingId = facts.reduce((max, f) => {
10456
+ const n = parseInt(f.id.replace(/^\D+/, ""), 10);
10457
+ return Number.isFinite(n) && n > max ? n : max;
10458
+ }, 0);
10459
+ const nextId2 = typeof raw.nextId === "number" && raw.nextId >= 1 ? raw.nextId : maxExistingId + 1;
10360
10460
  return { facts, nextId: nextId2 };
10361
10461
  } catch {
10362
10462
  return { facts: [], nextId: 1 };
@@ -10632,8 +10732,8 @@ var plugin31 = {
10632
10732
  var knowledge_graph_default = plugin31;
10633
10733
 
10634
10734
  // src/license-audit-gate/index.ts
10635
- import { readFileSync as readFileSync7 } from "node:fs";
10636
- import { resolve as resolve14 } from "node:path";
10735
+ import { readFileSync as readFileSync8 } from "node:fs";
10736
+ import { resolve as resolve15 } from "node:path";
10637
10737
  var API_VERSION23 = "^0.1.10";
10638
10738
  var state29 = {
10639
10739
  invocations: 0,
@@ -10695,8 +10795,8 @@ function auditPackages(names, allowedLicenses) {
10695
10795
  for (const name of names) {
10696
10796
  let licenses = [];
10697
10797
  try {
10698
- const pkgPath = resolve14("node_modules", name, "package.json");
10699
- const raw = JSON.parse(readFileSync7(pkgPath, "utf-8"));
10798
+ const pkgPath = resolve15("node_modules", name, "package.json");
10799
+ const raw = JSON.parse(readFileSync8(pkgPath, "utf-8"));
10700
10800
  licenses = extractLicenseStrings(raw);
10701
10801
  } catch {
10702
10802
  errors.push(name);
@@ -10876,11 +10976,11 @@ var license_audit_gate_default = plugin32;
10876
10976
 
10877
10977
  // src/lint-gate/index.ts
10878
10978
  import { execFile as execFile7 } from "node:child_process";
10879
- import { readFileSync as readFileSync8 } from "node:fs";
10979
+ import { readFileSync as readFileSync9 } from "node:fs";
10880
10980
  import { mkdtemp, readFile as readFile11, rm, writeFile as writeFile3 } from "node:fs/promises";
10881
10981
  import { createRequire } from "node:module";
10882
10982
  import { tmpdir } from "node:os";
10883
- import { dirname as dirname5, isAbsolute as isAbsolute14, join as join4, relative as relative14, resolve as resolve15, sep as sep3 } from "node:path";
10983
+ import { dirname as dirname5, isAbsolute as isAbsolute14, join as join4, relative as relative14, resolve as resolve16, sep as sep3 } from "node:path";
10884
10984
  var API_VERSION24 = "^0.1.10";
10885
10985
  var state30 = {
10886
10986
  /** Total PreToolUse invocations. */
@@ -10926,13 +11026,13 @@ function isInside(parent, candidate) {
10926
11026
  function resolveLocalLinter(name, cwd) {
10927
11027
  try {
10928
11028
  const packageName = LINTER_PACKAGES[name];
10929
- const requireFromProject = createRequire(resolve15(cwd, "package.json"));
11029
+ const requireFromProject = createRequire(resolve16(cwd, "package.json"));
10930
11030
  const packagePath = requireFromProject.resolve(`${packageName}/package.json`);
10931
- const packageJson = JSON.parse(readFileSync8(packagePath, "utf-8"));
11031
+ const packageJson = JSON.parse(readFileSync9(packagePath, "utf-8"));
10932
11032
  const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.[name] ?? Object.values(packageJson.bin ?? {})[0];
10933
11033
  if (!relativeBin || isAbsolute14(relativeBin)) return null;
10934
11034
  const packageDir = dirname5(packagePath);
10935
- const entry = resolve15(packageDir, relativeBin);
11035
+ const entry = resolve16(packageDir, relativeBin);
10936
11036
  if (!isInside(packageDir, entry)) return null;
10937
11037
  return {
10938
11038
  cmd: process.execPath,
@@ -11111,7 +11211,7 @@ var plugin33 = {
11111
11211
  state30.lastResult = null;
11112
11212
  linterCache.clear();
11113
11213
  const cfg = readConfig28(api.config.extensions?.["lint-gate"]);
11114
- const cwd = resolve15(api.config.cwd ?? process.cwd());
11214
+ const cwd = resolve16(api.config.cwd ?? process.cwd());
11115
11215
  const linterReady = detectLinter(cfg.linter, cwd).then((linter) => {
11116
11216
  if (!linter) {
11117
11217
  api.log.warn("lint-gate: no linter found (biome or eslint) \u2014 hook will be a no-op", {
@@ -11376,7 +11476,7 @@ function readConfig29(raw) {
11376
11476
  }
11377
11477
  function isDeterministic(request) {
11378
11478
  const t = request["temperature"];
11379
- return t === void 0 || t === 0;
11479
+ return t === void 0 || t === null || t === 0;
11380
11480
  }
11381
11481
  var fingerprintCache = /* @__PURE__ */ new WeakMap();
11382
11482
  function fingerprintRequest(request) {
@@ -11518,7 +11618,8 @@ var plugin34 = {
11518
11618
  state31.misses += 1;
11519
11619
  api.metrics.counter("misses");
11520
11620
  const response = await inner(_ctx, request);
11521
- if (response && typeof response === "object" && response.stopReason === "end_turn") {
11621
+ const sr = response && typeof response === "object" ? response.stopReason : void 0;
11622
+ if (response && typeof response === "object" && (sr === "end_turn" || sr === "stop" || sr === "tool_use")) {
11522
11623
  lruSet(key, response, cfg.maxEntries);
11523
11624
  }
11524
11625
  return response;
@@ -11726,11 +11827,11 @@ function hashString2(value) {
11726
11827
  }
11727
11828
  async function gitDiffFingerprint(cwd, targetPath, signal) {
11728
11829
  const pathspec = isAbsolute15(targetPath) ? relative15(cwd, targetPath) : targetPath;
11729
- if (!pathspec || pathspec === ".." || pathspec.startsWith("../") || pathspec.startsWith("..\\")) {
11830
+ if (!pathspec || isAbsolute15(pathspec) || pathspec === ".." || pathspec.startsWith("../") || pathspec.startsWith("..\\")) {
11730
11831
  return null;
11731
11832
  }
11732
11833
  try {
11733
- const diff = await new Promise((resolve27, reject) => {
11834
+ const diff = await new Promise((resolve28, reject) => {
11734
11835
  execFile8(
11735
11836
  "git",
11736
11837
  ["diff", "--no-ext-diff", "--", pathspec],
@@ -11747,7 +11848,7 @@ async function gitDiffFingerprint(cwd, targetPath, signal) {
11747
11848
  },
11748
11849
  (error, stdout) => {
11749
11850
  if (error) reject(error);
11750
- else resolve27(stdout);
11851
+ else resolve28(stdout);
11751
11852
  }
11752
11853
  );
11753
11854
  });
@@ -12104,7 +12205,7 @@ var plugin35 = {
12104
12205
  var loop_breaker_default = plugin35;
12105
12206
 
12106
12207
  // src/migration-planner/index.ts
12107
- import { existsSync as existsSync4, readFileSync as readFileSync9 } from "node:fs";
12208
+ import { existsSync as existsSync5, readFileSync as readFileSync10 } from "node:fs";
12108
12209
 
12109
12210
  // src/runtime/llm.ts
12110
12211
  import {
@@ -12152,9 +12253,9 @@ function readChangelog(packageName, cfg) {
12152
12253
  candidates.push(`node_modules/${packageName}/changelog.md`);
12153
12254
  for (const candidate of candidates) {
12154
12255
  if (!(0, runtime_exports.withinProject)(candidate)) continue;
12155
- if (existsSync4(candidate)) {
12256
+ if (existsSync5(candidate)) {
12156
12257
  try {
12157
- const content = readFileSync9(candidate, "utf-8");
12258
+ const content = readFileSync10(candidate, "utf-8");
12158
12259
  return { source: candidate, content: content.slice(0, cfg.maxChars) };
12159
12260
  } catch {
12160
12261
  }
@@ -12199,7 +12300,6 @@ function extractBreakingChanges(sectionText) {
12199
12300
  for (const rawLine of sectionText.split(/\r?\n/)) {
12200
12301
  const line = rawLine.trim();
12201
12302
  if (!line) {
12202
- inBreakingSection = false;
12203
12303
  continue;
12204
12304
  }
12205
12305
  if (/^#{3,4}\s+(?:BREAKING\s+CHANGES?|Breaking\s+Changes?|Breaking)/i.test(line)) {
@@ -12225,7 +12325,6 @@ function extractRecommendedSteps(sectionText) {
12225
12325
  for (const rawLine of sectionText.split(/\r?\n/)) {
12226
12326
  const line = rawLine.trim();
12227
12327
  if (!line) {
12228
- inMigrationSection = false;
12229
12328
  continue;
12230
12329
  }
12231
12330
  if (/^#{3,4}\s+(?:Migration|Upgrade|How to|Steps|Recommended)/i.test(line)) {
@@ -12620,6 +12719,8 @@ function requestCharSize(request) {
12620
12719
  const o = v;
12621
12720
  if (typeof o["text"] === "string") size += o["text"].length;
12622
12721
  if (o["content"] !== void 0) walk(o["content"]);
12722
+ if (o["input"] !== void 0) walk(o["input"]);
12723
+ if (o["arguments"] !== void 0) walk(o["arguments"]);
12623
12724
  }
12624
12725
  };
12625
12726
  walk(request["system"]);
@@ -12820,7 +12921,7 @@ var WebhookNotificationChannel = class {
12820
12921
  // -----------------------------------------------------------------------
12821
12922
  async deliver(msg) {
12822
12923
  const deliveredAt = (/* @__PURE__ */ new Date()).toISOString();
12823
- const inCooldown = this.#resetMs > 0 && Date.now() - this.#openedAt < this.#resetMs;
12924
+ const inCooldown = this.#resetMs === 0 || Date.now() - this.#openedAt < this.#resetMs;
12824
12925
  if (this.#circuit.open && this.#maxFailures > 0 && inCooldown) {
12825
12926
  this.#totalSuppressed += 1;
12826
12927
  return {
@@ -12850,6 +12951,10 @@ var WebhookNotificationChannel = class {
12850
12951
  body,
12851
12952
  signal: controller.signal
12852
12953
  });
12954
+ if (typeof res.arrayBuffer === "function") {
12955
+ await res.arrayBuffer().catch(() => {
12956
+ });
12957
+ }
12853
12958
  if (!res.ok) throw new Error(`webhook responded ${res.status}`);
12854
12959
  } finally {
12855
12960
  clearTimeout(timer);
@@ -13352,6 +13457,7 @@ function normalizePath2(p) {
13352
13457
  }
13353
13458
  const joined = segments.join("/");
13354
13459
  if (drive) return `${drive}/${joined}`.replace(/\/$/, "");
13460
+ if (slashNormalized.startsWith("//")) return `//${joined}`.replace(/\/$/, "") || "//";
13355
13461
  if (slashNormalized.startsWith("/")) return `/${joined}`.replace(/\/$/, "") || "/";
13356
13462
  return joined;
13357
13463
  }
@@ -14270,7 +14376,7 @@ function destructiveTargetsAtDepth(command, depth) {
14270
14376
  targets.push(...roots.length > 0 ? roots : ["."]);
14271
14377
  f = findDelete.exec(normalizedCommand);
14272
14378
  }
14273
- const shellWrapper = /\b(?:ba|z|k)?sh\s+-c\s+(['"])(.*?)\1/gi;
14379
+ const shellWrapper = /\b(?:(?:ba|z|k)?sh|pwsh|powershell)\s+(?:-c|-Command)\s+(['"])(.*?)\1/gi;
14274
14380
  let w = shellWrapper.exec(normalizedCommand);
14275
14381
  while (w !== null) {
14276
14382
  if (w[2] && !tokenIsQuoted(w, w[0].split(/\s/)[0] ?? "")) {
@@ -14565,11 +14671,11 @@ function operationLabel(toolName) {
14565
14671
 
14566
14672
  // src/path-guard/index.ts
14567
14673
  import { realpathSync } from "node:fs";
14568
- import { resolve as resolve16 } from "node:path";
14674
+ import { resolve as resolve17 } from "node:path";
14569
14675
  function isSymlinkEscape(path, cwd) {
14570
14676
  if (!(0, runtime_exports.withinProject)(path)) return false;
14571
14677
  try {
14572
- const abs = resolve16(cwd ?? process.cwd(), path);
14678
+ const abs = resolve17(cwd ?? process.cwd(), path);
14573
14679
  const real = realpathSync(abs);
14574
14680
  return !(0, runtime_exports.withinProject)(real);
14575
14681
  } catch {
@@ -14840,8 +14946,8 @@ var plugin39 = {
14840
14946
  var path_guard_default = plugin39;
14841
14947
 
14842
14948
  // src/performance-regression-gate/index.ts
14843
- import { existsSync as existsSync5, readFileSync as readFileSync10 } from "node:fs";
14844
- import { isAbsolute as isAbsolute16, relative as relative16, resolve as resolve17 } from "node:path";
14949
+ import { existsSync as existsSync6, readFileSync as readFileSync11 } from "node:fs";
14950
+ import { isAbsolute as isAbsolute16, relative as relative16, resolve as resolve18 } from "node:path";
14845
14951
  var API_VERSION26 = "^0.1.10";
14846
14952
  var state36 = {
14847
14953
  invocationCount: 0,
@@ -14866,8 +14972,8 @@ function readConfig35(raw) {
14866
14972
  }
14867
14973
  function withinProject17(p, cwd = process.cwd()) {
14868
14974
  if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
14869
- const root = resolve17(cwd);
14870
- const resolved = isAbsolute16(p) ? resolve17(p) : resolve17(root, p);
14975
+ const root = resolve18(cwd);
14976
+ const resolved = isAbsolute16(p) ? resolve18(p) : resolve18(root, p);
14871
14977
  const rel = relative16(root, resolved);
14872
14978
  if (rel === "" || rel === ".") return true;
14873
14979
  if (rel.startsWith("..")) return false;
@@ -14876,8 +14982,8 @@ function withinProject17(p, cwd = process.cwd()) {
14876
14982
  }
14877
14983
  function resolveProjectPath6(rawPath, cwd = process.cwd()) {
14878
14984
  if (typeof rawPath !== "string" || rawPath.length === 0) return null;
14879
- const root = resolve17(cwd);
14880
- const resolved = isAbsolute16(rawPath) ? resolve17(rawPath) : resolve17(root, rawPath);
14985
+ const root = resolve18(cwd);
14986
+ const resolved = isAbsolute16(rawPath) ? resolve18(rawPath) : resolve18(root, rawPath);
14881
14987
  if (!withinProject17(resolved, cwd)) return null;
14882
14988
  return resolved;
14883
14989
  }
@@ -14906,9 +15012,9 @@ function flattenResults(results) {
14906
15012
  return flat;
14907
15013
  }
14908
15014
  function loadResults(path) {
14909
- if (!path || !existsSync5(path)) return null;
15015
+ if (!path || !existsSync6(path)) return null;
14910
15016
  try {
14911
- const raw = JSON.parse(readFileSync10(path, "utf-8"));
15017
+ const raw = JSON.parse(readFileSync11(path, "utf-8"));
14912
15018
  return raw;
14913
15019
  } catch {
14914
15020
  return null;
@@ -14949,6 +15055,8 @@ function pairCrossFile(baseline, current) {
14949
15055
  const byKey = /* @__PURE__ */ new Map();
14950
15056
  for (const bench of baseline) {
14951
15057
  byKey.set(bench.key, bench);
15058
+ const base = stripVariantSuffix(bench.name).base;
15059
+ byKey.set(`${bench.group} > ${base}`, bench);
14952
15060
  }
14953
15061
  const pairs = [];
14954
15062
  for (const bench of current) {
@@ -15209,6 +15317,7 @@ var PLUGIN = {
15209
15317
  if (typeof p.plugin !== "string" || p.plugin.length === 0) return;
15210
15318
  const wraps = Array.isArray(p.wraps) ? p.wraps.filter((w) => typeof w === "string") : [];
15211
15319
  const kind = typeof p.kind === "string" ? p.kind : "unknown";
15320
+ state37.wraps = state37.wraps.filter((w) => w.plugin !== p.plugin);
15212
15321
  state37.wraps.push({
15213
15322
  plugin: p.plugin,
15214
15323
  kind,
@@ -15292,7 +15401,7 @@ var plugin_stack_observer_default = PLUGIN;
15292
15401
  // src/pr-drafter/index.ts
15293
15402
  import { execFile as execFile9 } from "node:child_process";
15294
15403
  import { mkdir as mkdir2, writeFile as writeFile4 } from "node:fs/promises";
15295
- import { dirname as dirname6, isAbsolute as isAbsolute17, relative as relative17, resolve as resolve18 } from "node:path";
15404
+ import { dirname as dirname6, isAbsolute as isAbsolute17, relative as relative17, resolve as resolve19 } from "node:path";
15296
15405
  var API_VERSION27 = "^0.1.10";
15297
15406
  var state38 = {
15298
15407
  commits: [],
@@ -15331,8 +15440,8 @@ function readConfig37(raw) {
15331
15440
  }
15332
15441
  function resolveProjectPath7(rawPath, cwd = process.cwd()) {
15333
15442
  if (typeof rawPath !== "string" || rawPath.length === 0) return null;
15334
- const root = resolve18(cwd);
15335
- const resolved = isAbsolute17(rawPath) ? resolve18(rawPath) : resolve18(root, rawPath);
15443
+ const root = resolve19(cwd);
15444
+ const resolved = isAbsolute17(rawPath) ? resolve19(rawPath) : resolve19(root, rawPath);
15336
15445
  const rel = relative17(root, resolved);
15337
15446
  if (rel === "" || !rel.startsWith("..") && !isAbsolute17(rel)) return resolved;
15338
15447
  return null;
@@ -15548,10 +15657,10 @@ var plugin41 = {
15548
15657
  category: "Workflow",
15549
15658
  mutating: true,
15550
15659
  capabilities: ["fs.write"],
15551
- async execute(input) {
15660
+ async execute(input = {}) {
15552
15661
  if (!cfg.enabled) return { ok: false, error: "pr-drafter is disabled" };
15553
15662
  const draft = await buildDraft(cfg, api.llm);
15554
- if (input.preview) {
15663
+ if (input?.preview) {
15555
15664
  return { ok: true, preview: true, title: draft.title, body: draft.body };
15556
15665
  }
15557
15666
  const resolved = resolveProjectPath7(cfg.outputPath);
@@ -15695,8 +15804,7 @@ var plugin42 = {
15695
15804
  const ti = input.toolInput ?? {};
15696
15805
  const command = typeof ti["command"] === "string" ? ti["command"] : "";
15697
15806
  if (!command) return;
15698
- const cmdLower = command.toLowerCase();
15699
- const isKillRelated = cmdLower.includes("kill") || cmdLower.includes("taskkill") || cmdLower.includes("stop-process") || cmdLower.includes("tskill") || cmdLower.includes("pkill") || cmdLower.includes("killall") || cmdLower.includes("wmic");
15807
+ const isKillRelated = /\b(?:kill|taskkill|stop-process|tskill|pkill|killall|wmic)\b/i.test(command);
15700
15808
  if (!isKillRelated) return;
15701
15809
  state39.detections += 1;
15702
15810
  state39.lastDetection = {
@@ -15848,7 +15956,7 @@ function growMatch(re, p, text, absStart, deadline) {
15848
15956
  const ext = text.slice(absStart, extEnd);
15849
15957
  re.lastIndex = 0;
15850
15958
  const em = re.exec(ext);
15851
- if (!em || em.index !== 0 || em[0].length === 0) return null;
15959
+ if (em?.index !== 0 || em[0].length === 0) return null;
15852
15960
  const end = absStart + em[0].length;
15853
15961
  if (end < extEnd || extEnd === text.length) {
15854
15962
  return { start: absStart, end, matched: em[0] };
@@ -15858,7 +15966,8 @@ function growMatch(re, p, text, absStart, deadline) {
15858
15966
  return null;
15859
15967
  }
15860
15968
  function* execWindowed(p, text, deadline) {
15861
- const re = new RegExp(p.re.source, p.re.flags);
15969
+ const flags = p.re.flags.includes("g") ? p.re.flags : `${p.re.flags}g`;
15970
+ const re = new RegExp(p.re.source, flags);
15862
15971
  let acceptLo = 0;
15863
15972
  let highWater = 0;
15864
15973
  for (let window = 0; acceptLo < text.length; window++) {
@@ -16286,7 +16395,7 @@ var prompt_firewall_default = plugin43;
16286
16395
 
16287
16396
  // src/refactor-suggester/index.ts
16288
16397
  import { readFile as readFile12 } from "node:fs/promises";
16289
- import { isAbsolute as isAbsolute18, relative as relative18, resolve as resolve19 } from "node:path";
16398
+ import { isAbsolute as isAbsolute18, relative as relative18, resolve as resolve20 } from "node:path";
16290
16399
  var API_VERSION28 = "^0.1.10";
16291
16400
  var HOOK_WARNING_COOLDOWN_MS2 = 6e4;
16292
16401
  var state41 = {
@@ -16342,10 +16451,21 @@ function detectSmells(filePath, content, rules) {
16342
16451
  const suggestions = [];
16343
16452
  const lines = content.split(/\r?\n/);
16344
16453
  const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, " ");
16454
+ const CONTROL_KEYWORDS = /* @__PURE__ */ new Set([
16455
+ "if",
16456
+ "for",
16457
+ "while",
16458
+ "switch",
16459
+ "catch",
16460
+ "with",
16461
+ "typeof",
16462
+ "instanceof"
16463
+ ]);
16345
16464
  const functionLikeRe = /(?:export\s+)?(?:async\s+)?(?:function\s+)?([A-Za-z_$][A-Za-z0-9_$]*)\s*\(([^)]*)\)\s*\{/g;
16346
16465
  functionLikeRe.lastIndex = 0;
16347
16466
  for (const match of stripped.matchAll(functionLikeRe)) {
16348
16467
  const name = match[1];
16468
+ if (CONTROL_KEYWORDS.has(name)) continue;
16349
16469
  const paramsRaw = match[2];
16350
16470
  const params = paramsRaw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
16351
16471
  if (params.length > rules.maxParams) {
@@ -16417,7 +16537,7 @@ function detectSmells(filePath, content, rules) {
16417
16537
  }
16418
16538
  async function scanPath4(rawPath, cfg) {
16419
16539
  const root = process.cwd();
16420
- const resolved = isAbsolute18(rawPath) ? resolve19(rawPath) : resolve19(root, rawPath);
16540
+ const resolved = isAbsolute18(rawPath) ? resolve20(rawPath) : resolve20(root, rawPath);
16421
16541
  const exts = normalizeExtensions5(cfg.extensions);
16422
16542
  const files = await (0, runtime_exports.collectSourceFilesAsync)(resolved, { extensions: exts });
16423
16543
  const suggestions = [];
@@ -16504,7 +16624,7 @@ var plugin44 = {
16504
16624
  const now = Date.now();
16505
16625
  const lastWarning = state41.lastHookWarning.get(sourcePath);
16506
16626
  if (lastWarning !== void 0 && now - lastWarning < HOOK_WARNING_COOLDOWN_MS2) return;
16507
- const resolved = resolve19(process.cwd(), sourcePath);
16627
+ const resolved = resolve20(process.cwd(), sourcePath);
16508
16628
  let content;
16509
16629
  try {
16510
16630
  content = await readFile12(resolved, "utf-8");
@@ -16551,7 +16671,7 @@ var plugin44 = {
16551
16671
  state41.suggestionCount += result.suggestions.length;
16552
16672
  return {
16553
16673
  ok: true,
16554
- path: relativePath6(resolve19(process.cwd(), rawPath)),
16674
+ path: relativePath6(resolve20(process.cwd(), rawPath)),
16555
16675
  scannedFiles: result.scannedFiles,
16556
16676
  discoveredFiles: result.discoveredFiles,
16557
16677
  // Say so when the cap stopped the walk early: a partial scan
@@ -16966,7 +17086,7 @@ var plugin45 = {
16966
17086
  var release_notes_generator_default = plugin45;
16967
17087
 
16968
17088
  // src/schema-evolution-guard/index.ts
16969
- import { readFileSync as readFileSync11 } from "node:fs";
17089
+ import { readFileSync as readFileSync12 } from "node:fs";
16970
17090
  import { basename as basename5 } from "node:path";
16971
17091
  var API_VERSION30 = "^0.1.10";
16972
17092
  var state43 = {
@@ -17169,7 +17289,7 @@ var plugin46 = {
17169
17289
  content = toolInput["content"];
17170
17290
  } else if ((0, runtime_exports.withinProject)(filePath)) {
17171
17291
  try {
17172
- content = readFileSync11(filePath, "utf-8");
17292
+ content = readFileSync12(filePath, "utf-8");
17173
17293
  } catch {
17174
17294
  content = void 0;
17175
17295
  }
@@ -17763,7 +17883,7 @@ var secret_scanner_default = plugin47;
17763
17883
 
17764
17884
  // src/security-hotspot-scanner/index.ts
17765
17885
  import { readdir as readdir2, readFile as readFile13, stat as stat5 } from "node:fs/promises";
17766
- import { isAbsolute as isAbsolute19, relative as relative19, resolve as resolve20 } from "node:path";
17886
+ import { extname as extname5, isAbsolute as isAbsolute19, relative as relative19, resolve as resolve21 } from "node:path";
17767
17887
  var API_VERSION31 = "^0.1.10";
17768
17888
  var state44 = {
17769
17889
  scanCount: 0,
@@ -17854,7 +17974,7 @@ function isSourceFile2(filePath, extensions) {
17854
17974
  async function scanPath5(inputPath, cfg) {
17855
17975
  const start = Date.now();
17856
17976
  const root = process.cwd();
17857
- const resolved = isAbsolute19(inputPath) ? resolve20(inputPath) : resolve20(root, inputPath);
17977
+ const resolved = isAbsolute19(inputPath) ? resolve21(inputPath) : resolve21(root, inputPath);
17858
17978
  if (!(0, runtime_exports.withinProject)(inputPath)) {
17859
17979
  return {
17860
17980
  path: inputPath,
@@ -17890,7 +18010,7 @@ async function scanPath5(inputPath, cfg) {
17890
18010
  }
17891
18011
  for (const entry of entries) {
17892
18012
  if (allFindings.length >= cfg.maxFindings) return;
17893
- const full = resolve20(dir, entry);
18013
+ const full = resolve21(dir, entry);
17894
18014
  try {
17895
18015
  const st = await stat5(full);
17896
18016
  if (st.isDirectory()) {
@@ -17992,10 +18112,10 @@ var plugin48 = {
17992
18112
  if (!cfg.enabled) return;
17993
18113
  if (input.toolResult?.isError) return;
17994
18114
  const inp = input.toolInput ?? {};
17995
- const sourcePath = inp["path"];
17996
- if (!sourcePath || typeof sourcePath !== "string") return;
17997
- if (!(0, runtime_exports.withinProject)(sourcePath)) return;
17998
- const ext = sourcePath.includes(".") ? sourcePath.slice(sourcePath.lastIndexOf(".")).toLowerCase() : "";
18115
+ const rawPath = inp["path"] ?? inp["filePath"] ?? inp["file_path"];
18116
+ const sourcePath = typeof rawPath === "string" ? rawPath : void 0;
18117
+ if (!sourcePath || !(0, runtime_exports.withinProject)(sourcePath)) return;
18118
+ const ext = extname5(sourcePath).toLowerCase();
17999
18119
  if (!scanOnChangeSet.has(ext)) {
18000
18120
  state44.skippedCount += 1;
18001
18121
  return;
@@ -18159,7 +18279,7 @@ var security_hotspot_scanner_default = plugin48;
18159
18279
 
18160
18280
  // src/semantic-search-indexer/index.ts
18161
18281
  import * as fs2 from "node:fs/promises";
18162
- import { isAbsolute as isAbsolute20, relative as relative20, resolve as resolve21 } from "node:path";
18282
+ import { isAbsolute as isAbsolute20, relative as relative20, resolve as resolve22 } from "node:path";
18163
18283
  import { DEFAULT_WALK_IGNORE_DIRS } from "@wrongstack/core/utils";
18164
18284
  var API_VERSION32 = "^0.1.10";
18165
18285
  var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -18240,7 +18360,7 @@ function normalizeSlashes2(p) {
18240
18360
  function withinProject21(p) {
18241
18361
  if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
18242
18362
  const root = normalizeSlashes2(process.cwd());
18243
- const resolved = normalizeSlashes2(isAbsolute20(p) ? resolve21(p) : resolve21(root, p));
18363
+ const resolved = normalizeSlashes2(isAbsolute20(p) ? resolve22(p) : resolve22(root, p));
18244
18364
  const rel = normalizeSlashes2(relative20(root, resolved));
18245
18365
  if (rel === "" || rel === ".") return true;
18246
18366
  if (rel.startsWith("..")) return false;
@@ -18251,7 +18371,7 @@ function resolveProjectPath8(p) {
18251
18371
  const raw = typeof p === "string" && p.length > 0 ? p : ".";
18252
18372
  if (!withinProject21(raw)) return null;
18253
18373
  const root = normalizeSlashes2(process.cwd());
18254
- return normalizeSlashes2(isAbsolute20(raw) ? resolve21(raw) : resolve21(root, raw));
18374
+ return normalizeSlashes2(isAbsolute20(raw) ? resolve22(raw) : resolve22(root, raw));
18255
18375
  }
18256
18376
  function tokenize(text, minLength) {
18257
18377
  const tokens = [];
@@ -18281,7 +18401,7 @@ function shouldIndexFile(filePath, cfg) {
18281
18401
  var INDEX_BATCH_SIZE = 32;
18282
18402
  var YIELD_EVERY_FILES = 64;
18283
18403
  function yieldEventLoop() {
18284
- return new Promise((resolve27) => setImmediate(resolve27));
18404
+ return new Promise((resolve28) => setImmediate(resolve28));
18285
18405
  }
18286
18406
  function addFileToIndex(relPath, content, size, cfg) {
18287
18407
  if (!state45.index || content.includes("\0")) return;
@@ -18335,7 +18455,7 @@ async function walkDirectory(absPath, cfg, excludes, fileBatch) {
18335
18455
  state45.truncated = true;
18336
18456
  return;
18337
18457
  }
18338
- const absChild = normalizeSlashes2(resolve21(absPath, ent.name));
18458
+ const absChild = normalizeSlashes2(resolve22(absPath, ent.name));
18339
18459
  const relChild = normalizeSlashes2(relative20(root, absChild));
18340
18460
  if (relChild === "" || relChild === ".") continue;
18341
18461
  if (excludes.some((re) => re.test(relChild))) continue;
@@ -18675,12 +18795,12 @@ import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
18675
18795
  import { toErrorMessage } from "@wrongstack/core/utils";
18676
18796
  import { execFile as execFile11 } from "node:child_process";
18677
18797
  import { access as access4, readFile as readFile15, readdir as readdir4, writeFile as writeFile5 } from "node:fs/promises";
18678
- import { isAbsolute as isAbsolute21, join as join5, relative as relative21, resolve as resolve22 } from "node:path";
18798
+ import { isAbsolute as isAbsolute21, join as join5, relative as relative21, resolve as resolve23 } from "node:path";
18679
18799
  var API_VERSION33 = "^0.1.10";
18680
18800
  function resolveProjectRoot(rawCwd, root = process.cwd()) {
18681
18801
  if (typeof rawCwd !== "string" || rawCwd.length === 0) return root;
18682
- const base = resolve22(root);
18683
- const resolved = isAbsolute21(rawCwd) ? resolve22(rawCwd) : resolve22(base, rawCwd);
18802
+ const base = resolve23(root);
18803
+ const resolved = isAbsolute21(rawCwd) ? resolve23(rawCwd) : resolve23(base, rawCwd);
18684
18804
  const rel = relative21(base, resolved);
18685
18805
  if (rel === "" || !rel.startsWith("..") && !isAbsolute21(rel)) return resolved;
18686
18806
  return null;
@@ -18694,7 +18814,7 @@ var state46 = {
18694
18814
  lastBump: null
18695
18815
  };
18696
18816
  function runCommand3(command, args, cwd) {
18697
- return new Promise((resolve27, reject) => {
18817
+ return new Promise((resolve28, reject) => {
18698
18818
  execFile11(command, args, {
18699
18819
  encoding: "utf-8",
18700
18820
  cwd,
@@ -18702,7 +18822,7 @@ function runCommand3(command, args, cwd) {
18702
18822
  windowsHide: true
18703
18823
  }, (err, stdout, stderr) => {
18704
18824
  if (!err) {
18705
- resolve27(stdout.trim());
18825
+ resolve28(stdout.trim());
18706
18826
  return;
18707
18827
  }
18708
18828
  const failure = err;
@@ -18889,6 +19009,7 @@ var plugin50 = {
18889
19009
  state46.lastBump = null;
18890
19010
  const tagPrefix = api.config.extensions?.["semver-bump"]?.["tagPrefix"] ?? "v";
18891
19011
  const autoTag = api.config.extensions?.["semver-bump"]?.["autoTag"] ?? true;
19012
+ const tagMessage = api.config.extensions?.["semver-bump"]?.["tagMessage"] ?? "Release {{version}}";
18892
19013
  const VALID_PARTS = ["major", "minor", "patch", "auto"];
18893
19014
  function readDefaultPart(cfg) {
18894
19015
  const raw = cfg.extensions?.["semver-bump"]?.["defaultPart"];
@@ -18997,7 +19118,8 @@ var plugin50 = {
18997
19118
  }
18998
19119
  if (autoTag) {
18999
19120
  try {
19000
- await runGit6(["tag", "-a", `${tagPrefix}${newVersion}`, "-m", `Release ${newVersion}`], cwd);
19121
+ const msg = tagMessage.replace("{{version}}", newVersion);
19122
+ await runGit6(["tag", "-a", `${tagPrefix}${newVersion}`, "-m", msg], cwd);
19001
19123
  } catch {
19002
19124
  }
19003
19125
  }
@@ -19705,12 +19827,12 @@ var session_recap_default = plugin51;
19705
19827
  // src/shell-check/index.ts
19706
19828
  import { execFile as execFile12 } from "node:child_process";
19707
19829
  import { readdir as readdir5 } from "node:fs/promises";
19708
- import { isAbsolute as isAbsolute22, join as join6, relative as relative22, resolve as resolve23 } from "node:path";
19830
+ import { isAbsolute as isAbsolute22, join as join6, relative as relative22, resolve as resolve24 } from "node:path";
19709
19831
  var API_VERSION34 = "^0.1.10";
19710
19832
  function withinProject22(p) {
19711
19833
  if (p.startsWith("-")) return false;
19712
19834
  const root = process.cwd();
19713
- const resolved = isAbsolute22(p) ? resolve23(p) : resolve23(root, p);
19835
+ const resolved = isAbsolute22(p) ? resolve24(p) : resolve24(root, p);
19714
19836
  const rel = relative22(root, resolved);
19715
19837
  if (rel === "" || rel === ".") return true;
19716
19838
  if (rel.startsWith("..")) return false;
@@ -20006,8 +20128,8 @@ var plugin52 = {
20006
20128
  var shell_check_default = plugin52;
20007
20129
 
20008
20130
  // src/smart-rename/index.ts
20009
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "node:fs";
20010
- import { extname as extname4, isAbsolute as isAbsolute23, relative as relative23, resolve as resolve24 } from "node:path";
20131
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "node:fs";
20132
+ import { extname as extname6, isAbsolute as isAbsolute23, relative as relative23, resolve as resolve25 } from "node:path";
20011
20133
  var API_VERSION35 = "^0.1.10";
20012
20134
  var state49 = {
20013
20135
  renameCount: 0,
@@ -20029,7 +20151,7 @@ function readConfig47(raw) {
20029
20151
  function withinProject23(p) {
20030
20152
  if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
20031
20153
  const root = process.cwd();
20032
- const resolved = isAbsolute23(p) ? resolve24(p) : resolve24(root, p);
20154
+ const resolved = isAbsolute23(p) ? resolve25(p) : resolve25(root, p);
20033
20155
  const rel = relative23(root, resolved);
20034
20156
  if (rel === "" || rel === ".") return true;
20035
20157
  if (rel.startsWith("..")) return false;
@@ -20045,9 +20167,9 @@ function relativePath7(p) {
20045
20167
  function escapeRegex2(s) {
20046
20168
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20047
20169
  }
20048
- var IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
20170
+ var IDENTIFIER_RE2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
20049
20171
  function isIdentifier(name) {
20050
- return IDENTIFIER_RE.test(name);
20172
+ return IDENTIFIER_RE2.test(name);
20051
20173
  }
20052
20174
  function renameInContent(content, oldName, newName) {
20053
20175
  const re = new RegExp(
@@ -20128,14 +20250,14 @@ var plugin53 = {
20128
20250
  if (!withinProject23(rawPath)) {
20129
20251
  return { ok: false, error: "path is outside the project root" };
20130
20252
  }
20131
- const ext = extname4(rawPath).toLowerCase();
20253
+ const ext = extname6(rawPath).toLowerCase();
20132
20254
  if (!cfg.extensions.includes(ext)) {
20133
20255
  return { ok: false, error: `extension ${ext} is not allowed for rename` };
20134
20256
  }
20135
- const resolved = resolve24(process.cwd(), rawPath);
20257
+ const resolved = resolve25(process.cwd(), rawPath);
20136
20258
  let content;
20137
20259
  try {
20138
- content = readFileSync12(resolved, "utf-8");
20260
+ content = readFileSync13(resolved, "utf-8");
20139
20261
  } catch (err) {
20140
20262
  state49.errorCount += 1;
20141
20263
  return { ok: false, error: String(err) };
@@ -20178,8 +20300,8 @@ var plugin53 = {
20178
20300
  },
20179
20301
  async health() {
20180
20302
  return {
20181
- ok: state49.errorCount === 0,
20182
- message: state49.errorCount ? `smart-rename: ${state49.errorCount} error(s)` : `smart-rename: ${state49.renameCount} rename(s), ${state49.replacementCount} replacement(s)`,
20303
+ ok: true,
20304
+ message: `smart-rename: ${state49.renameCount} rename(s), ${state49.replacementCount} replacement(s)${state49.errorCount ? ` (${state49.errorCount} error(s))` : ""}`,
20183
20305
  counters: {
20184
20306
  renames: state49.renameCount,
20185
20307
  replacements: state49.replacementCount,
@@ -20400,7 +20522,7 @@ function isWrappedAsLinkOrCode(line, name) {
20400
20522
  const target = name.toLowerCase();
20401
20523
  if (lower.includes(`[${target}](`)) return true;
20402
20524
  if (lower.includes(`\`${target}\``)) return true;
20403
- if (lower.includes(`[\``) && lower.includes(`\`](`)) return true;
20525
+ if (new RegExp(`\\[[^\\]]*\`${escapeRegExp2(target)}\`[^\\]]*\\]\\(`, "i").test(line)) return true;
20404
20526
  return false;
20405
20527
  }
20406
20528
  function escapeRegExp2(s) {
@@ -20692,7 +20814,7 @@ function templateChars(template) {
20692
20814
  var contributorUnregister = null;
20693
20815
  function expandTemplate(template, variables) {
20694
20816
  let result = template;
20695
- result = result.replace(/\{\{(\w+)\}\}/g, (match, key) => {
20817
+ result = result.replace(/\{\{([\w.-]+)\}\}/g, (match, key) => {
20696
20818
  const value = variables[key];
20697
20819
  if (value !== void 0) return value;
20698
20820
  return match;
@@ -20700,18 +20822,18 @@ function expandTemplate(template, variables) {
20700
20822
  return result;
20701
20823
  }
20702
20824
  function expandConditionals(template, variables) {
20703
- return template.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, key, content) => {
20825
+ return template.replace(/\{\{#if\s+([\w.-]+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, key, content) => {
20704
20826
  const val = variables[key];
20705
20827
  return val !== void 0 && val !== "" && val !== "false" && val !== "0" ? content : "";
20706
20828
  });
20707
20829
  }
20708
20830
  function expandLoops(template, variables) {
20709
- return template.replace(/\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g, (_, key, content) => {
20831
+ return template.replace(/\{\{#each\s+([\w.-]+)\}\}([\s\S]*?)\{\{\/each\}\}/g, (_, key, content) => {
20710
20832
  const val = variables[key];
20711
20833
  if (!val) return "";
20712
20834
  if (typeof val === "string" && val.includes(",")) {
20713
20835
  const items = val.split(",").map((s) => s.trim());
20714
- return items.map((item) => expandTemplate(content, { ...variables, [key]: item })).join("\n");
20836
+ return items.map((item) => expandTemplate(content, { ...variables, [key]: item, item })).join("\n");
20715
20837
  }
20716
20838
  return expandTemplate(content, variables);
20717
20839
  });
@@ -21075,7 +21197,8 @@ var plugin55 = {
21075
21197
  var template_engine_default = plugin55;
21076
21198
 
21077
21199
  // src/test-coverage-gate/index.ts
21078
- import { readFileSync as readFileSync13 } from "node:fs";
21200
+ import { readFileSync as readFileSync14 } from "node:fs";
21201
+ import { extname as extname7 } from "node:path";
21079
21202
  var API_VERSION37 = "^0.1.10";
21080
21203
  var state51 = {
21081
21204
  invocationCount: 0,
@@ -21116,7 +21239,7 @@ function readConfig49(raw) {
21116
21239
  }
21117
21240
  function readCoverageSummary(coveragePath) {
21118
21241
  try {
21119
- const raw = readFileSync13(coveragePath, "utf-8");
21242
+ const raw = readFileSync14(coveragePath, "utf-8");
21120
21243
  return JSON.parse(raw);
21121
21244
  } catch {
21122
21245
  return null;
@@ -21192,10 +21315,10 @@ var plugin56 = {
21192
21315
  if (!cfg.enabled) return;
21193
21316
  if (input.toolResult?.isError) return;
21194
21317
  const inp = input.toolInput ?? {};
21195
- const sourcePath = inp["path"];
21196
- if (!sourcePath || typeof sourcePath !== "string") return;
21197
- if (!(0, runtime_exports.withinProject)(sourcePath)) return;
21198
- const ext = sourcePath.includes(".") ? sourcePath.slice(sourcePath.lastIndexOf(".")).toLowerCase() : "";
21318
+ const rawPath = inp["path"] ?? inp["filePath"] ?? inp["file_path"];
21319
+ const sourcePath = typeof rawPath === "string" ? rawPath : void 0;
21320
+ if (!sourcePath || !(0, runtime_exports.withinProject)(sourcePath)) return;
21321
+ const ext = extname7(sourcePath).toLowerCase();
21199
21322
  if (!runOnChangeSet.has(ext)) {
21200
21323
  state51.skippedCount += 1;
21201
21324
  return;
@@ -21332,9 +21455,9 @@ var test_coverage_gate_default = plugin56;
21332
21455
 
21333
21456
  // src/test-flake-detector/index.ts
21334
21457
  import { execFile as execFile13 } from "node:child_process";
21335
- import { readFileSync as readFileSync14 } from "node:fs";
21458
+ import { readFileSync as readFileSync15 } from "node:fs";
21336
21459
  import { createRequire as createRequire2 } from "node:module";
21337
- import { dirname as dirname7, isAbsolute as isAbsolute25, relative as relative24, resolve as resolve25 } from "node:path";
21460
+ import { dirname as dirname7, isAbsolute as isAbsolute25, relative as relative24, resolve as resolve26 } from "node:path";
21338
21461
  var API_VERSION38 = "^0.1.10";
21339
21462
  var state52 = {
21340
21463
  invocationCount: 0,
@@ -21403,13 +21526,6 @@ var ALLOWED_RUNNER_FLAGS = /* @__PURE__ */ new Set([
21403
21526
  "--reporter=verbose",
21404
21527
  "--reporter=default"
21405
21528
  ]);
21406
- function withinProject26(p) {
21407
- if (p.length === 0 || p.length > 4096 || p.startsWith("-")) return false;
21408
- const root = resolve25(process.cwd());
21409
- const resolved = isAbsolute25(p) ? resolve25(p) : resolve25(root, p);
21410
- const rel = relative24(root, resolved);
21411
- return rel === "" || !rel.startsWith("..") && !isAbsolute25(rel);
21412
- }
21413
21529
  function isInside2(parent, child) {
21414
21530
  if (parent === child) return true;
21415
21531
  const rel = relative24(parent, child);
@@ -21450,13 +21566,13 @@ function resolveTestCommand(baseCommand, testPattern) {
21450
21566
  if (runnerArgs.some((arg) => !ALLOWED_RUNNER_FLAGS.has(arg))) return null;
21451
21567
  let resolvedEntry;
21452
21568
  try {
21453
- const requireFromProject = createRequire2(resolve25(process.cwd(), "package.json"));
21569
+ const requireFromProject = createRequire2(resolve26(process.cwd(), "package.json"));
21454
21570
  const packagePath = requireFromProject.resolve(`${runner}/package.json`);
21455
- const packageJson = JSON.parse(readFileSync14(packagePath, "utf8"));
21571
+ const packageJson = JSON.parse(readFileSync15(packagePath, "utf8"));
21456
21572
  const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.[runner] ?? Object.values(packageJson.bin ?? {})[0];
21457
21573
  if (!relativeBin) return null;
21458
21574
  const packageDir = dirname7(packagePath);
21459
- const candidate = resolve25(packageDir, relativeBin);
21575
+ const candidate = resolve26(packageDir, relativeBin);
21460
21576
  if (isAbsolute25(relativeBin) || !isInside2(packageDir, candidate)) {
21461
21577
  return null;
21462
21578
  }
@@ -21466,7 +21582,7 @@ function resolveTestCommand(baseCommand, testPattern) {
21466
21582
  }
21467
21583
  const args = [resolvedEntry, ...runnerArgs];
21468
21584
  if (testPattern) {
21469
- if (!withinProject26(testPattern)) return null;
21585
+ if (!(0, runtime_exports.withinProject)(testPattern)) return null;
21470
21586
  args.push(testPattern);
21471
21587
  }
21472
21588
  return {
@@ -21699,8 +21815,8 @@ var plugin57 = {
21699
21815
  var test_flake_detector_default = plugin57;
21700
21816
 
21701
21817
  // src/test-generator/index.ts
21702
- import { readFileSync as readFileSync15 } from "node:fs";
21703
- import { isAbsolute as isAbsolute26, relative as relative25, resolve as resolve26 } from "node:path";
21818
+ import { readFileSync as readFileSync16 } from "node:fs";
21819
+ import { isAbsolute as isAbsolute26, relative as relative25, resolve as resolve27 } from "node:path";
21704
21820
  var API_VERSION39 = "^0.1.10";
21705
21821
  var state53 = {
21706
21822
  generateCount: 0,
@@ -21759,7 +21875,7 @@ var SOURCE_EXTENSIONS = [
21759
21875
  function withinProject27(p) {
21760
21876
  if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
21761
21877
  const root = process.cwd();
21762
- const resolved = isAbsolute26(p) ? resolve26(p) : resolve26(root, p);
21878
+ const resolved = isAbsolute26(p) ? resolve27(p) : resolve27(root, p);
21763
21879
  const rel = relative25(root, resolved);
21764
21880
  if (rel === "" || rel === ".") return true;
21765
21881
  if (rel.startsWith("..")) return false;
@@ -21864,7 +21980,7 @@ function generateTestContent(sourcePath, sourceModule, detected, cfg) {
21864
21980
  return lines.join("\n");
21865
21981
  }
21866
21982
  function generateForFile(filePath, cfg) {
21867
- const sourceContent = readFileSync15(filePath, "utf-8");
21983
+ const sourceContent = readFileSync16(filePath, "utf-8");
21868
21984
  const detected = detectExports(sourceContent);
21869
21985
  const sourceFile = relativePath8(filePath);
21870
21986
  const sourceParts = sourceFile.split("/");
@@ -21989,7 +22105,7 @@ var plugin58 = {
21989
22105
  error: `test generation only reads source files (${SOURCE_EXTENSIONS.join(", ")}); refusing "${rawPath}"`
21990
22106
  };
21991
22107
  }
21992
- const resolved = resolve26(process.cwd(), rawPath);
22108
+ const resolved = resolve27(process.cwd(), rawPath);
21993
22109
  state53.generateCount += 1;
21994
22110
  let result;
21995
22111
  try {
@@ -22059,8 +22175,8 @@ var plugin58 = {
22059
22175
  },
22060
22176
  async health() {
22061
22177
  return {
22062
- ok: state53.errorCount === 0,
22063
- message: state53.errorCount ? `test-generator: ${state53.errorCount} error(s)` : `test-generator: ${state53.generateCount} generation(s), ${state53.exportCount} export(s)`,
22178
+ ok: true,
22179
+ message: `test-generator: ${state53.generateCount} generation(s), ${state53.exportCount} export(s)${state53.errorCount ? ` (${state53.errorCount} error(s))` : ""}`,
22064
22180
  counters: {
22065
22181
  generated: state53.generateCount,
22066
22182
  exports: state53.exportCount,
@@ -22205,7 +22321,7 @@ async function detectRunner(requested) {
22205
22321
  const match = candidates.find((c) => c.name === requested);
22206
22322
  if (!match) return null;
22207
22323
  try {
22208
- await new Promise((resolve27, reject) => {
22324
+ await new Promise((resolve28, reject) => {
22209
22325
  const ex = resolveExec("npx", [`${match.name}`, "--version"]);
22210
22326
  execFile14(ex.cmd, ex.args, {
22211
22327
  encoding: "utf-8",
@@ -22213,7 +22329,7 @@ async function detectRunner(requested) {
22213
22329
  cwd: process.cwd(),
22214
22330
  windowsHide: true,
22215
22331
  ...ex.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
22216
- }, (err) => err ? reject(err) : resolve27());
22332
+ }, (err) => err ? reject(err) : resolve28());
22217
22333
  });
22218
22334
  return match;
22219
22335
  } catch {
@@ -22222,7 +22338,7 @@ async function detectRunner(requested) {
22222
22338
  }
22223
22339
  for (const candidate of candidates) {
22224
22340
  try {
22225
- await new Promise((resolve27, reject) => {
22341
+ await new Promise((resolve28, reject) => {
22226
22342
  const ex = resolveExec("npx", [`${candidate.name}`, "--version"]);
22227
22343
  execFile14(ex.cmd, ex.args, {
22228
22344
  encoding: "utf-8",
@@ -22230,7 +22346,7 @@ async function detectRunner(requested) {
22230
22346
  cwd: process.cwd(),
22231
22347
  windowsHide: true,
22232
22348
  ...ex.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
22233
- }, (err) => err ? reject(err) : resolve27());
22349
+ }, (err) => err ? reject(err) : resolve28());
22234
22350
  });
22235
22351
  return candidate;
22236
22352
  } catch {
@@ -22276,7 +22392,7 @@ async function runTests(testFile, runner, customCommand, timeoutMs) {
22276
22392
  let stdout = "";
22277
22393
  try {
22278
22394
  const { stdout: out } = await new Promise(
22279
- (resolve27, reject) => {
22395
+ (resolve28, reject) => {
22280
22396
  const ex = resolveExec(cmd, fullArgs);
22281
22397
  execFile14(
22282
22398
  ex.cmd,
@@ -22290,7 +22406,7 @@ async function runTests(testFile, runner, customCommand, timeoutMs) {
22290
22406
  },
22291
22407
  (err, out2, stderr) => {
22292
22408
  if (err) reject(Object.assign(err, { stdout: out2, stderr }));
22293
- else resolve27({ stdout: out2, stderr });
22409
+ else resolve28({ stdout: out2, stderr });
22294
22410
  }
22295
22411
  );
22296
22412
  }
@@ -22615,8 +22731,9 @@ function readConfig53(raw) {
22615
22731
  function hashTodos(todos) {
22616
22732
  const sorted = todos.map((t) => `${t.id}|${t.status}|${t.content ?? ""}`).sort();
22617
22733
  let h = 2166136261;
22618
- for (let i = 0; i < sorted.join("\n").length; i++) {
22619
- h ^= sorted.join("\n").charCodeAt(i);
22734
+ const joined = sorted.join("\n");
22735
+ for (let i = 0; i < joined.length; i++) {
22736
+ h ^= joined.charCodeAt(i);
22620
22737
  h = h * 16777619 >>> 0;
22621
22738
  }
22622
22739
  return h.toString(16);
@@ -22803,6 +22920,7 @@ var todo_listener_default = plugin60;
22803
22920
  // src/todo-tracker/index.ts
22804
22921
  import { randomUUID } from "node:crypto";
22805
22922
  import * as fsp from "node:fs/promises";
22923
+ import { dirname as dirname9 } from "node:path";
22806
22924
  import { atomicWrite as atomicWrite3, ensureDir as ensureDir3 } from "@wrongstack/core/utils";
22807
22925
  import { nowIso } from "@wrongstack/primitives";
22808
22926
  function deriveFilePath(api) {
@@ -22834,7 +22952,7 @@ async function loadFile(filePath) {
22834
22952
  }
22835
22953
  }
22836
22954
  async function saveFile(filePath, file) {
22837
- await ensureDir3(filePath.replace(/[/\\][^/\\]+$/, ""));
22955
+ await ensureDir3(dirname9(filePath));
22838
22956
  await atomicWrite3(filePath, JSON.stringify(file, null, 2), { mode: 384 });
22839
22957
  }
22840
22958
  var state56 = {
@@ -23565,15 +23683,15 @@ function estimateRequestTokens(request, charsPerToken) {
23565
23683
  }
23566
23684
  function sleep(ms, signal) {
23567
23685
  if (signal?.aborted) return Promise.resolve();
23568
- return new Promise((resolve27) => {
23686
+ return new Promise((resolve28) => {
23569
23687
  const timer = setTimeout(() => {
23570
23688
  signal?.removeEventListener("abort", onAbort);
23571
- resolve27();
23689
+ resolve28();
23572
23690
  }, ms);
23573
23691
  timer.unref?.();
23574
23692
  function onAbort() {
23575
23693
  clearTimeout(timer);
23576
- resolve27();
23694
+ resolve28();
23577
23695
  }
23578
23696
  signal?.addEventListener("abort", onAbort, { once: true });
23579
23697
  });
@@ -23724,7 +23842,7 @@ var plugin63 = {
23724
23842
  var token_throttle_default = plugin63;
23725
23843
 
23726
23844
  // src/type-gate/index.ts
23727
- import { existsSync as existsSync6 } from "node:fs";
23845
+ import { existsSync as existsSync7 } from "node:fs";
23728
23846
  var API_VERSION42 = "^0.1.10";
23729
23847
  var state59 = {
23730
23848
  invocationCount: 0,
@@ -23805,7 +23923,7 @@ async function runTypeCheck(cfg) {
23805
23923
  argv = [...argv, "-p", tsConfig];
23806
23924
  }
23807
23925
  }
23808
- if (!cfg.command && tsConfig && !existsSync6(tsConfig)) {
23926
+ if (!cfg.command && tsConfig && !existsSync7(tsConfig)) {
23809
23927
  return null;
23810
23928
  }
23811
23929
  const result = await (0, runtime_exports.runRunnerCommand)([argv[0], ...argv.slice(1)], {