@akasecurity/ai-tc-claude-code 0.9.5 → 0.9.6

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aka",
3
- "version": "0.9.5",
3
+ "version": "0.9.6",
4
4
  "description": "AI Traffic Control — inspect and govern AI prompts in Claude Code. Detection runs locally; events are recorded to a local SQLite store on your machine.",
5
5
  "homepage": "https://github.com/akasecurity/ai-tc",
6
6
  "author": { "name": "AKA Security" }
package/commands/setup.md CHANGED
@@ -892,3 +892,26 @@ declined, or a failed install you already reported — end with one warm close:
892
892
 
893
893
  Before you finish, confirm every AKA_SHOW region on the path you took was
894
894
  relayed to the user. If you summarized one instead of pasting it, paste it now.
895
+
896
+ ## Known limitations
897
+
898
+ Be honest about every one of these if the user asks why something wasn't caught
899
+ — never imply coverage this plugin does not have.
900
+
901
+ **The model-judge step in step 3 cannot run on native Windows.** The judge
902
+ spawns the `claude` CLI through a shell-free `execFileSync`, and an
903
+ npm-installed CLI is a `.cmd` shim on Windows, which Node has refused to spawn
904
+ without a shell since the CVE-2024-27980 fix. So a user who grants model-judge
905
+ consent gets a spawn failure at that step rather than a rated set of findings,
906
+ and the step exits non-zero instead of completing. Everything before it is
907
+ unaffected — the posture check, the consent-gated historical read, and the local
908
+ ruleset scan all run, and every finding is still detected and redacted locally.
909
+ What is unavailable is only the model pass that rates false positives and
910
+ severity.
911
+
912
+ Say this plainly if a Windows user asks why setup stopped there, and do not
913
+ offer to retry it: the fix is a code change, tracked separately. Two things
914
+ worth getting right if it comes up — running under WSL2 is Linux rather than
915
+ Windows and is not affected, and declining model-judge consent in step 1 is not
916
+ a workaround so much as the other supported path, since the local scan is what
917
+ produces the findings in the first place.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akasecurity/ai-tc-claude-code",
3
- "version": "0.9.5",
3
+ "version": "0.9.6",
4
4
  "description": "AI Traffic Control — inspect and govern AI prompts in Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -27,13 +27,13 @@
27
27
  "typescript": "^5.8.0",
28
28
  "vitest": "^4.1.10",
29
29
  "zod": "^4.0.0",
30
- "@akasecurity/persistence": "0.8.0",
31
30
  "@akasecurity/eslint-config": "0.8.0",
31
+ "@akasecurity/persistence": "0.8.0",
32
32
  "@akasecurity/plugin-sdk": "0.8.0",
33
33
  "@akasecurity/scanner": "0.8.0",
34
+ "@akasecurity/schema": "0.8.0",
34
35
  "@akasecurity/plugin-runtime": "0.8.0",
35
- "@akasecurity/setup-wizard": "0.8.0",
36
- "@akasecurity/schema": "0.8.0"
36
+ "@akasecurity/setup-wizard": "0.8.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup",
@@ -25890,6 +25890,40 @@ function escapeRegExp2(value) {
25890
25890
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25891
25891
  }
25892
25892
 
25893
+ // ../../packages/detections/src/regex-cache.ts
25894
+ var singles = /* @__PURE__ */ new WeakMap();
25895
+ var keywordLists = /* @__PURE__ */ new WeakMap();
25896
+ var labelLists = /* @__PURE__ */ new WeakMap();
25897
+ function listCache(kind) {
25898
+ return kind === "keyword" ? keywordLists : labelLists;
25899
+ }
25900
+ function memoizedRegExp(owner, build) {
25901
+ const cached2 = singles.get(owner);
25902
+ if (cached2 !== void 0) {
25903
+ cached2.lastIndex = 0;
25904
+ return cached2;
25905
+ }
25906
+ const compiled = build();
25907
+ singles.set(owner, compiled);
25908
+ return compiled;
25909
+ }
25910
+ function memoizedRegExpList(kind, owner, build) {
25911
+ const cache = listCache(kind);
25912
+ const cached2 = cache.get(owner);
25913
+ if (cached2 !== void 0) {
25914
+ if (cached2.stateful) {
25915
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
25916
+ }
25917
+ return cached2.entries;
25918
+ }
25919
+ const entries = build();
25920
+ cache.set(owner, {
25921
+ entries,
25922
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
25923
+ });
25924
+ return entries;
25925
+ }
25926
+
25893
25927
  // ../../packages/detections/src/matchers/limits.ts
25894
25928
  var MAX_MATCHES_PER_RULE = 1e4;
25895
25929
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -25900,10 +25934,17 @@ var KeywordMatcher2 = class {
25900
25934
  if (rule.matcher.type !== "keyword") return [];
25901
25935
  const { keywords, caseSensitive } = rule.matcher;
25902
25936
  const spans = [];
25903
- for (const kw of keywords) {
25904
- if (kw.length === 0) continue;
25937
+ const compiled = memoizedRegExpList(
25938
+ "keyword",
25939
+ rule.matcher,
25940
+ () => keywords.map((kw) => {
25941
+ if (kw.length === 0) return void 0;
25942
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
25943
+ })
25944
+ );
25945
+ for (const re of compiled) {
25946
+ if (re === void 0) continue;
25905
25947
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
25906
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
25907
25948
  let m;
25908
25949
  while ((m = re.exec(text)) !== null) {
25909
25950
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -25919,7 +25960,10 @@ var RegexMatcher2 = class {
25919
25960
  match(text, rule) {
25920
25961
  if (rule.matcher.type !== "regex") return [];
25921
25962
  const { pattern, flags, captureGroup } = rule.matcher;
25922
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
25963
+ const re = memoizedRegExp(
25964
+ rule.matcher,
25965
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
25966
+ );
25923
25967
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
25924
25968
  const spans = [];
25925
25969
  let m;
@@ -26027,11 +26071,15 @@ function isCorroborated(candidate, candidates, text) {
26027
26071
  const labels = req.labels;
26028
26072
  if (labels && labels.length > 0) {
26029
26073
  const haystack = text.slice(Math.max(0, winStart), winEnd);
26030
- for (const label of labels) {
26031
- const trimmed = label.trim();
26032
- if (trimmed.length === 0) continue;
26033
- const re = new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
26034
- if (re.test(haystack)) return true;
26074
+ for (const re of memoizedRegExpList(
26075
+ "label",
26076
+ req,
26077
+ () => labels.map((label) => {
26078
+ const trimmed = label.trim();
26079
+ return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
26080
+ })
26081
+ )) {
26082
+ if (re?.test(haystack)) return true;
26035
26083
  }
26036
26084
  }
26037
26085
  return false;
@@ -28339,7 +28387,7 @@ function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
28339
28387
  // ../../packages/plugin-sdk/src/project-files.ts
28340
28388
  var import_ignore = __toESM(require_ignore(), 1);
28341
28389
  import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
28342
- import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
28390
+ import { basename as basename5, join as join12 } from "path";
28343
28391
 
28344
28392
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
28345
28393
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -29433,8 +29481,8 @@ function table(headers, rows, opts = {}) {
29433
29481
  const widths = headers.map(
29434
29482
  (h, i) => Math.max(visibleLength(h), ...rows.map((r) => visibleLength(r[i] ?? "")))
29435
29483
  );
29436
- const sep5 = " ".repeat(gap);
29437
- const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep5);
29484
+ const sep4 = " ".repeat(gap);
29485
+ const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep4);
29438
29486
  const headerLine = fmt(headers.map((h) => h.toUpperCase()));
29439
29487
  if (opts.rowSep === true) {
29440
29488
  const fullWidth = widths.reduce((n, w) => n + w, 0) + gap * Math.max(0, widths.length - 1);
@@ -29446,7 +29494,7 @@ function table(headers, rows, opts = {}) {
29446
29494
  });
29447
29495
  return [headerLine, rule, ...body].join("\n");
29448
29496
  }
29449
- const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep5);
29497
+ const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep4);
29450
29498
  return [headerLine, ruleLine, ...rows.map(fmt)].join("\n");
29451
29499
  }
29452
29500
  function fenced(body) {
@@ -26973,6 +26973,40 @@ function escapeRegExp2(value) {
26973
26973
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26974
26974
  }
26975
26975
 
26976
+ // ../../packages/detections/src/regex-cache.ts
26977
+ var singles = /* @__PURE__ */ new WeakMap();
26978
+ var keywordLists = /* @__PURE__ */ new WeakMap();
26979
+ var labelLists = /* @__PURE__ */ new WeakMap();
26980
+ function listCache(kind) {
26981
+ return kind === "keyword" ? keywordLists : labelLists;
26982
+ }
26983
+ function memoizedRegExp(owner, build) {
26984
+ const cached2 = singles.get(owner);
26985
+ if (cached2 !== void 0) {
26986
+ cached2.lastIndex = 0;
26987
+ return cached2;
26988
+ }
26989
+ const compiled = build();
26990
+ singles.set(owner, compiled);
26991
+ return compiled;
26992
+ }
26993
+ function memoizedRegExpList(kind, owner, build) {
26994
+ const cache = listCache(kind);
26995
+ const cached2 = cache.get(owner);
26996
+ if (cached2 !== void 0) {
26997
+ if (cached2.stateful) {
26998
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
26999
+ }
27000
+ return cached2.entries;
27001
+ }
27002
+ const entries = build();
27003
+ cache.set(owner, {
27004
+ entries,
27005
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
27006
+ });
27007
+ return entries;
27008
+ }
27009
+
26976
27010
  // ../../packages/detections/src/matchers/limits.ts
26977
27011
  var MAX_MATCHES_PER_RULE = 1e4;
26978
27012
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -26983,10 +27017,17 @@ var KeywordMatcher2 = class {
26983
27017
  if (rule.matcher.type !== "keyword") return [];
26984
27018
  const { keywords, caseSensitive } = rule.matcher;
26985
27019
  const spans = [];
26986
- for (const kw of keywords) {
26987
- if (kw.length === 0) continue;
27020
+ const compiled = memoizedRegExpList(
27021
+ "keyword",
27022
+ rule.matcher,
27023
+ () => keywords.map((kw) => {
27024
+ if (kw.length === 0) return void 0;
27025
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
27026
+ })
27027
+ );
27028
+ for (const re of compiled) {
27029
+ if (re === void 0) continue;
26988
27030
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
26989
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26990
27031
  let m;
26991
27032
  while ((m = re.exec(text)) !== null) {
26992
27033
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -27002,7 +27043,10 @@ var RegexMatcher2 = class {
27002
27043
  match(text, rule) {
27003
27044
  if (rule.matcher.type !== "regex") return [];
27004
27045
  const { pattern, flags, captureGroup } = rule.matcher;
27005
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
27046
+ const re = memoizedRegExp(
27047
+ rule.matcher,
27048
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
27049
+ );
27006
27050
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
27007
27051
  const spans = [];
27008
27052
  let m;
@@ -27110,11 +27154,15 @@ function isCorroborated(candidate, candidates, text) {
27110
27154
  const labels = req.labels;
27111
27155
  if (labels && labels.length > 0) {
27112
27156
  const haystack = text.slice(Math.max(0, winStart), winEnd);
27113
- for (const label of labels) {
27114
- const trimmed = label.trim();
27115
- if (trimmed.length === 0) continue;
27116
- const re = new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
27117
- if (re.test(haystack)) return true;
27157
+ for (const re of memoizedRegExpList(
27158
+ "label",
27159
+ req,
27160
+ () => labels.map((label) => {
27161
+ const trimmed = label.trim();
27162
+ return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
27163
+ })
27164
+ )) {
27165
+ if (re?.test(haystack)) return true;
27118
27166
  }
27119
27167
  }
27120
27168
  return false;
@@ -27382,13 +27430,14 @@ function probesFor(rule) {
27382
27430
  const derived = rule.matcher.type === "regex" ? derivedProbes(rule.matcher.pattern) : [];
27383
27431
  return [...derived, ...EXPONENTIAL_PROBES, ...POLYNOMIAL_PROBES];
27384
27432
  }
27385
- function worstProbeMs(rule) {
27433
+ var wallClock = () => performance.now();
27434
+ function worstProbeMs(rule, now = wallClock) {
27386
27435
  let ms = 0;
27387
27436
  let probe = "";
27388
27437
  for (const text of probesFor(rule)) {
27389
- const start = performance.now();
27438
+ const start = now();
27390
27439
  scan(text, [rule]);
27391
- const elapsed = performance.now() - start;
27440
+ const elapsed = now() - start;
27392
27441
  if (elapsed > ms) {
27393
27442
  ms = elapsed;
27394
27443
  probe = text;
@@ -30105,7 +30154,7 @@ import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
30105
30154
  // ../../packages/plugin-sdk/src/project-files.ts
30106
30155
  var import_ignore = __toESM(require_ignore(), 1);
30107
30156
  import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
30108
- import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
30157
+ import { basename as basename5, join as join12 } from "path";
30109
30158
 
30110
30159
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
30111
30160
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -31583,7 +31632,7 @@ import { readFileSync as readFileSync12, renameSync as renameSync6, rmSync as rm
31583
31632
 
31584
31633
  // src/remediation/redact.ts
31585
31634
  import { readFileSync as readFileSync11, realpathSync as realpathSync3, renameSync as renameSync5, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "fs";
31586
- import { isAbsolute as isAbsolute2, relative as relative2, resolve } from "path";
31635
+ import { isAbsolute as isAbsolute2, relative, resolve } from "path";
31587
31636
  function platformRedactionScope(home) {
31588
31637
  return { artifactRoots: [transcriptsDir(home)] };
31589
31638
  }
@@ -31597,7 +31646,7 @@ function realPathOrNull(path) {
31597
31646
  function isWithinRoot(realTarget, root) {
31598
31647
  const realRoot = realPathOrNull(root);
31599
31648
  if (realRoot === null) return false;
31600
- const rel = relative2(realRoot, realTarget);
31649
+ const rel = relative(realRoot, realTarget);
31601
31650
  return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
31602
31651
  }
31603
31652
  function resolveRedactableArtifact(filePath, scope) {
@@ -26912,6 +26912,40 @@ function escapeRegExp2(value) {
26912
26912
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26913
26913
  }
26914
26914
 
26915
+ // ../../packages/detections/src/regex-cache.ts
26916
+ var singles = /* @__PURE__ */ new WeakMap();
26917
+ var keywordLists = /* @__PURE__ */ new WeakMap();
26918
+ var labelLists = /* @__PURE__ */ new WeakMap();
26919
+ function listCache(kind) {
26920
+ return kind === "keyword" ? keywordLists : labelLists;
26921
+ }
26922
+ function memoizedRegExp(owner, build) {
26923
+ const cached2 = singles.get(owner);
26924
+ if (cached2 !== void 0) {
26925
+ cached2.lastIndex = 0;
26926
+ return cached2;
26927
+ }
26928
+ const compiled = build();
26929
+ singles.set(owner, compiled);
26930
+ return compiled;
26931
+ }
26932
+ function memoizedRegExpList(kind, owner, build) {
26933
+ const cache = listCache(kind);
26934
+ const cached2 = cache.get(owner);
26935
+ if (cached2 !== void 0) {
26936
+ if (cached2.stateful) {
26937
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
26938
+ }
26939
+ return cached2.entries;
26940
+ }
26941
+ const entries = build();
26942
+ cache.set(owner, {
26943
+ entries,
26944
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
26945
+ });
26946
+ return entries;
26947
+ }
26948
+
26915
26949
  // ../../packages/detections/src/matchers/limits.ts
26916
26950
  var MAX_MATCHES_PER_RULE = 1e4;
26917
26951
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -26922,10 +26956,17 @@ var KeywordMatcher2 = class {
26922
26956
  if (rule.matcher.type !== "keyword") return [];
26923
26957
  const { keywords, caseSensitive } = rule.matcher;
26924
26958
  const spans = [];
26925
- for (const kw of keywords) {
26926
- if (kw.length === 0) continue;
26959
+ const compiled = memoizedRegExpList(
26960
+ "keyword",
26961
+ rule.matcher,
26962
+ () => keywords.map((kw) => {
26963
+ if (kw.length === 0) return void 0;
26964
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26965
+ })
26966
+ );
26967
+ for (const re of compiled) {
26968
+ if (re === void 0) continue;
26927
26969
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
26928
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26929
26970
  let m;
26930
26971
  while ((m = re.exec(text)) !== null) {
26931
26972
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -26941,7 +26982,10 @@ var RegexMatcher2 = class {
26941
26982
  match(text, rule) {
26942
26983
  if (rule.matcher.type !== "regex") return [];
26943
26984
  const { pattern, flags, captureGroup } = rule.matcher;
26944
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
26985
+ const re = memoizedRegExp(
26986
+ rule.matcher,
26987
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
26988
+ );
26945
26989
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
26946
26990
  const spans = [];
26947
26991
  let m;
@@ -27049,11 +27093,15 @@ function isCorroborated(candidate, candidates, text) {
27049
27093
  const labels = req.labels;
27050
27094
  if (labels && labels.length > 0) {
27051
27095
  const haystack = text.slice(Math.max(0, winStart), winEnd);
27052
- for (const label of labels) {
27053
- const trimmed = label.trim();
27054
- if (trimmed.length === 0) continue;
27055
- const re = new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
27056
- if (re.test(haystack)) return true;
27096
+ for (const re of memoizedRegExpList(
27097
+ "label",
27098
+ req,
27099
+ () => labels.map((label) => {
27100
+ const trimmed = label.trim();
27101
+ return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
27102
+ })
27103
+ )) {
27104
+ if (re?.test(haystack)) return true;
27057
27105
  }
27058
27106
  }
27059
27107
  return false;
@@ -27321,13 +27369,14 @@ function probesFor(rule) {
27321
27369
  const derived = rule.matcher.type === "regex" ? derivedProbes(rule.matcher.pattern) : [];
27322
27370
  return [...derived, ...EXPONENTIAL_PROBES, ...POLYNOMIAL_PROBES];
27323
27371
  }
27324
- function worstProbeMs(rule) {
27372
+ var wallClock = () => performance.now();
27373
+ function worstProbeMs(rule, now = wallClock) {
27325
27374
  let ms = 0;
27326
27375
  let probe = "";
27327
27376
  for (const text of probesFor(rule)) {
27328
- const start = performance.now();
27377
+ const start = now();
27329
27378
  scan(text, [rule]);
27330
- const elapsed = performance.now() - start;
27379
+ const elapsed = now() - start;
27331
27380
  if (elapsed > ms) {
27332
27381
  ms = elapsed;
27333
27382
  probe = text;
@@ -29989,7 +30038,7 @@ function resolveNonGitProject(startDir, recognizeMarker) {
29989
30038
  // ../../packages/plugin-sdk/src/project-files.ts
29990
30039
  var import_ignore = __toESM(require_ignore(), 1);
29991
30040
  import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
29992
- import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
30041
+ import { basename as basename5, join as join12 } from "path";
29993
30042
 
29994
30043
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
29995
30044
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -30416,7 +30465,7 @@ function discoverGitRepos(opts) {
30416
30465
  }
30417
30466
 
30418
30467
  // ../../packages/scanner/src/render.ts
30419
- import { basename as basename6, relative as relative2 } from "path";
30468
+ import { basename as basename6, relative } from "path";
30420
30469
  var SEVERITY_ORDER3 = ["critical", "high", "medium", "low"];
30421
30470
  var SEVERITY_GLYPH = {
30422
30471
  critical: "\u2588",
@@ -30434,9 +30483,9 @@ function indent(text, spaces = 2) {
30434
30483
  }
30435
30484
  function table(headers, rows, gap = 3) {
30436
30485
  const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
30437
- const sep6 = " ".repeat(gap);
30438
- const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep6);
30439
- const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep6);
30486
+ const sep5 = " ".repeat(gap);
30487
+ const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep5);
30488
+ const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep5);
30440
30489
  return [fmt(headers.map((h) => h.toUpperCase())), ruleLine, ...rows.map(fmt)].join("\n");
30441
30490
  }
30442
30491
  function metaBlock(rows) {
@@ -30458,7 +30507,7 @@ function followUpSection(opts) {
30458
30507
  return opts.followUp !== void 0 ? ["", ` ${opts.followUp}`] : [];
30459
30508
  }
30460
30509
  function renderWorktreeSummary(summary, opts = {}) {
30461
- const rootLabel = relative2(process.cwd(), summary.rootDir) || ".";
30510
+ const rootLabel = relative(process.cwd(), summary.rootDir) || ".";
30462
30511
  const meta3 = metaBlock([
30463
30512
  ["Root", rootLabel],
30464
30513
  ["Scanned", `${String(summary.scanned)} files`],
@@ -30510,7 +30559,7 @@ function renderMultiRepoSummary(summary, opts = {}) {
30510
30559
 
30511
30560
  // ../../packages/scanner/src/scan.ts
30512
30561
  import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
30513
- import { extname as extname2, isAbsolute as isAbsolute2, relative as relative4 } from "path";
30562
+ import { extname as extname2, isAbsolute as isAbsolute2, relative as relative3 } from "path";
30514
30563
 
30515
30564
  // ../../packages/plugin-runtime/src/standalone-gateway.ts
30516
30565
  import { randomUUID as randomUUID15 } from "crypto";
@@ -30837,7 +30886,7 @@ import { statSync as statSync8 } from "fs";
30837
30886
  // ../../packages/scanner/src/walk.ts
30838
30887
  var import_ignore2 = __toESM(require_ignore(), 1);
30839
30888
  import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
30840
- import { extname, join as join15, relative as relative3, sep as sep5 } from "path";
30889
+ import { extname, join as join15, relative as relative2, sep as sep4 } from "path";
30841
30890
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
30842
30891
  ".ts",
30843
30892
  ".tsx",
@@ -30878,7 +30927,7 @@ function readIgnoreLayer(dir, filename) {
30878
30927
  function evaluate(layers, absPath, isDir) {
30879
30928
  let state = "unmatched";
30880
30929
  for (const layer of layers) {
30881
- const rel = relative3(layer.base, absPath).split(sep5).join("/") + (isDir ? "/" : "");
30930
+ const rel = relative2(layer.base, absPath).split(sep4).join("/") + (isDir ? "/" : "");
30882
30931
  const verdict = layer.matcher.test(rel);
30883
30932
  if (verdict.ignored) state = "ignored";
30884
30933
  else if (verdict.unignored) state = "unignored";
@@ -30947,7 +30996,7 @@ function* walkSourceFiles(opts = {}) {
30947
30996
  // Posix-separated like every stored relative path (and the ignore
30948
30997
  // matching inside walkTree) — native separators must not leak into the
30949
30998
  // contract.
30950
- relativePath: relative3(rootDir, file2.path).split(sep5).join("/"),
30999
+ relativePath: relative2(rootDir, file2.path).split(sep4).join("/"),
30951
31000
  mtime: mtime.toISOString(),
30952
31001
  size,
30953
31002
  gitignored: file2.gitignored
@@ -31015,7 +31064,7 @@ function resolveEgressProject(rootDir) {
31015
31064
  }
31016
31065
  }
31017
31066
  function egressKey(root, absPath) {
31018
- const rel = toPosix(relative4(root, absPath));
31067
+ const rel = toPosix(relative3(root, absPath));
31019
31068
  return rel === "" || rel.startsWith("../") ? null : rel;
31020
31069
  }
31021
31070
  function startEgress(rootDir) {
@@ -31069,7 +31118,7 @@ async function reopenRedetectedFindings(gateway, path, currentKeys, evidence, re
31069
31118
  }
31070
31119
  }
31071
31120
  function isUnderRoot(path, rootDir) {
31072
- const rel = relative4(rootDir, path);
31121
+ const rel = relative3(rootDir, path);
31073
31122
  return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
31074
31123
  }
31075
31124
  async function sweepDeletedFiles(gateway, rootDir, previous) {
@@ -25926,6 +25926,40 @@ function escapeRegExp2(value) {
25926
25926
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25927
25927
  }
25928
25928
 
25929
+ // ../../packages/detections/src/regex-cache.ts
25930
+ var singles = /* @__PURE__ */ new WeakMap();
25931
+ var keywordLists = /* @__PURE__ */ new WeakMap();
25932
+ var labelLists = /* @__PURE__ */ new WeakMap();
25933
+ function listCache(kind) {
25934
+ return kind === "keyword" ? keywordLists : labelLists;
25935
+ }
25936
+ function memoizedRegExp(owner, build) {
25937
+ const cached2 = singles.get(owner);
25938
+ if (cached2 !== void 0) {
25939
+ cached2.lastIndex = 0;
25940
+ return cached2;
25941
+ }
25942
+ const compiled = build();
25943
+ singles.set(owner, compiled);
25944
+ return compiled;
25945
+ }
25946
+ function memoizedRegExpList(kind, owner, build) {
25947
+ const cache = listCache(kind);
25948
+ const cached2 = cache.get(owner);
25949
+ if (cached2 !== void 0) {
25950
+ if (cached2.stateful) {
25951
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
25952
+ }
25953
+ return cached2.entries;
25954
+ }
25955
+ const entries = build();
25956
+ cache.set(owner, {
25957
+ entries,
25958
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
25959
+ });
25960
+ return entries;
25961
+ }
25962
+
25929
25963
  // ../../packages/detections/src/matchers/limits.ts
25930
25964
  var MAX_MATCHES_PER_RULE = 1e4;
25931
25965
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -25936,10 +25970,17 @@ var KeywordMatcher2 = class {
25936
25970
  if (rule.matcher.type !== "keyword") return [];
25937
25971
  const { keywords, caseSensitive } = rule.matcher;
25938
25972
  const spans = [];
25939
- for (const kw of keywords) {
25940
- if (kw.length === 0) continue;
25973
+ const compiled = memoizedRegExpList(
25974
+ "keyword",
25975
+ rule.matcher,
25976
+ () => keywords.map((kw) => {
25977
+ if (kw.length === 0) return void 0;
25978
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
25979
+ })
25980
+ );
25981
+ for (const re of compiled) {
25982
+ if (re === void 0) continue;
25941
25983
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
25942
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
25943
25984
  let m;
25944
25985
  while ((m = re.exec(text)) !== null) {
25945
25986
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -25955,7 +25996,10 @@ var RegexMatcher2 = class {
25955
25996
  match(text, rule) {
25956
25997
  if (rule.matcher.type !== "regex") return [];
25957
25998
  const { pattern, flags, captureGroup } = rule.matcher;
25958
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
25999
+ const re = memoizedRegExp(
26000
+ rule.matcher,
26001
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
26002
+ );
25959
26003
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
25960
26004
  const spans = [];
25961
26005
  let m;
@@ -28113,7 +28157,7 @@ import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
28113
28157
  // ../../packages/plugin-sdk/src/project-files.ts
28114
28158
  var import_ignore = __toESM(require_ignore(), 1);
28115
28159
  import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
28116
- import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
28160
+ import { basename as basename5, join as join12 } from "path";
28117
28161
 
28118
28162
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
28119
28163
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -28557,8 +28601,8 @@ function table(headers, rows, opts = {}) {
28557
28601
  const widths = headers.map(
28558
28602
  (h, i) => Math.max(visibleLength(h), ...rows.map((r) => visibleLength(r[i] ?? "")))
28559
28603
  );
28560
- const sep5 = " ".repeat(gap);
28561
- const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep5);
28604
+ const sep4 = " ".repeat(gap);
28605
+ const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep4);
28562
28606
  const headerLine = fmt(headers.map((h) => h.toUpperCase()));
28563
28607
  if (opts.rowSep === true) {
28564
28608
  const fullWidth = widths.reduce((n, w) => n + w, 0) + gap * Math.max(0, widths.length - 1);
@@ -28570,7 +28614,7 @@ function table(headers, rows, opts = {}) {
28570
28614
  });
28571
28615
  return [headerLine, rule, ...body].join("\n");
28572
28616
  }
28573
- const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep5);
28617
+ const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep4);
28574
28618
  return [headerLine, ruleLine, ...rows.map(fmt)].join("\n");
28575
28619
  }
28576
28620
  function fenced(body) {