enigma-cli 1.46.4 → 1.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,6 @@
7
7
  "minimalCode"
8
8
  ],
9
9
  "updated": "2026-07-17T00:17:02+02:00",
10
- "cliVersion": "1.46.4",
10
+ "cliVersion": "1.47.0",
11
11
  "sha": "3f0dcc28341bb0407860534f7ce9314cfc91b5e673f8f3d13b89d61851ed75f6"
12
12
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "On-demand over-engineering review - diff review, whole-repo audit, and enigma: debt-marker ledger (tags delete/stdlib/native/yagni/shrink, line/dep scoring); lists cuts, applies nothing.",
6
6
  "updated": "2026-06-16T11:24:30+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "f742a2be3f328b9ea1ff9a35a449177c2cbec35ad16e46f7054b7a873a2ab017"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Backend/API architecture: controller-service-repository layering, modern TypeScript project configuration (module resolution, strict flags, @/* path alias), API and request optimization (batching, avoiding redundant calls, skipping no-op writes), server-side caching (Redis), and Zod boundary validation.",
6
6
  "updated": "2026-08-02T19:59:01+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "652637f818b4ce4f4d5d83fa68fa57fe7842e32fb179523958c5496af54bb938"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Ciphera code style conventions (formatting, naming, imports incl. namespace imports for wide module surfaces, path-alias specifiers and no file extensions, comments, code-level anti-patterns; TypeScript-first, language-agnostic).",
6
6
  "updated": "2026-08-02T19:59:01+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "3beb0383a9cb0331ef8a579308a2517d24d57a8ba33751ad17d16d0a4f66e344"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Pre-delivery self-review gate, prioritized review dimensions, and change-quality criteria.",
6
6
  "updated": "2026-08-19T12:37:59+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "6b8a924cc11b61d4b45b73384aeb095ce622229359b7cc2c6ce4bb0cf3015ed7"
9
9
  }
@@ -7,6 +7,6 @@
7
7
  "codeGraph"
8
8
  ],
9
9
  "updated": "2026-08-15T20:49:24+02:00",
10
- "cliVersion": "1.46.4",
10
+ "cliVersion": "1.47.0",
11
11
  "sha": "95b7202e5659f8d9d6f11ce29e7dc5b01575ba86c78ef948bec4753ccf430155"
12
12
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Senior database architecture policy: engine selection (PostgreSQL by default, SQLite only for local-first/embedded stores), ORM selection (Prisma in TypeScript/JavaScript), query optimization, anti-duplication/normalization, scalability, and RGPD/GDPR encryption.",
6
6
  "updated": "2026-08-04T18:33:27+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "26fddc14caf697e830448a2cc453ddc470fe83d098d50bd67b278effba63274e"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Reproduce-isolate-fix debugging methodology with root-cause discipline and regression verification.",
6
6
  "updated": "2026-08-02T04:15:47+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "968876bcac9ce6e5a20be05c89ac97191b112c9e08679a8dc506d150c375fec4"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Dependency and supply-chain security: lockfiles and reproducible installs, version pinning, vulnerability auditing, vetting/minimizing packages, vendoring, and SBOM/provenance.",
6
6
  "updated": "2026-06-01T00:45:28+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "6375d835c2aef2c9bd31ce116444dc3d796f510f9970a213aa3ac4696d7e21b9"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Transactional email: React Email templates instead of hand-written HTML tables, server-side rendering, one send module behind the provider SDK, plain-text alternatives, idempotent background sending, link safety, and deliverability (SPF/DKIM/DMARC, bounce suppression, unsubscribe).",
6
6
  "updated": "2026-07-30T19:29:19+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "c9724fdbcdbeab99573be3fd44d4cdd97c2a394d99f3c4395f118f17356b00ed"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one.",
6
6
  "updated": "2026-07-29T01:18:36+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "9e30ee7d8a1a1e8c6e7f4e043857cd01841c68a427752e45bc0cad9ec5cfa279"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Frontend architecture: reusable components, abstraction thresholds, state management, real-time form validation where an emptied or not-yet-filled field is incomplete rather than invalid (a `*` on the label and an aria-disabled submit, never \"email is not valid\" over an empty input), no-op detection (skip any operation whose result equals current state, not just form saves; dirty means different from the loaded snapshot, not touched), instant first paint (render the shell, load data async, skeletons), perceived performance (prefetch on intent, debounce/throttle, cancel stale requests, avoid waterfalls, lazy widgets), large-list rendering (infinite scroll is the preferred default, pagination the deliberate exception when the design or the user calls for it, virtualization, skeletons, progressive loading), data views that ship their own affordances by default (a log, expense, transaction or history table is not done when the rows render: fuse.js search, the filters its column kinds imply, a date range, sort, filter state in the URL, and an export of the filtered set), every reference to an entity being a way into it (a name, id, project or path in a row links to that record, reveals it in a hover card, or at minimum copies and filters by it, never inert text; machine codes get human labels and raw payloads are never dumped into a cell), optimistic UI with rollback, visual restraint (one card level, spacing before borders, one elevation scale), icon actions (repeated row/card actions are icon-only buttons with aria-label plus title, not text labels), responsive/adaptive layout (fluid units, breakpoints, no overlap/overflow, viewport meta, touch targets), form fields that declare their keyboard and casing (autocapitalize/autocomplete/inputmode per field kind, set once in the shared Input, normalized on blur), variable-length text (min-width:0 in flex/grid, wrap vs truncate, long unbroken strings, worst-case content checks), auth screens (breached-password feedback, strength meter, cookie consent before login/register), and AI chat/agent interfaces via Vercel's AI Elements registry instead of hand-rolled message threads.",
6
6
  "updated": "2026-09-07T04:01:56+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "8bfd675f3a66dbdcaa7e670bc40246adb0c470b93c1d8677338b4092dfa9ada4"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Git & contribution policy (senior engineering standards).",
6
6
  "updated": "2026-08-22T09:25:34+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "cb320a6a5fd3279604ad7920b877c1a36e48f4f39aeead1a4c4daa89901d9ce0"
9
9
  }
@@ -7,6 +7,6 @@
7
7
  "logoColorPolicy"
8
8
  ],
9
9
  "updated": "2026-07-17T00:17:02+02:00",
10
- "cliVersion": "1.46.4",
10
+ "cliVersion": "1.47.0",
11
11
  "sha": "09cdbefd98625b02a7d03685e5deed128238ff8454a83fe22279610fe3ef8ddf"
12
12
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Application and AI-agent security: secrets, authn/authz (least privilege), credential flows with breach-checked passwords (Have I Been Pwned), OWASP Top 10, transport/crypto baseline, cookies and consent, secure logging, and agent/MCP/tool-use safety.",
6
6
  "updated": "2026-09-05T02:46:46+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "d64fe3a4a54f27a71e3066875ee949500f170d04da5c548ef605268a12102afb"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Create new skills, modify and improve existing skills, and measure skill performance with evals and benchmarks.",
6
6
  "updated": "2026-08-03T00:16:54+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "a49f6166f57d734e0f13487332da37903e925f32257447856af0fbd6a5f1dc84"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Exhaustive completion discipline for long/multi-item tasks - inventory, coverage ledger, verified done.",
6
6
  "updated": "2026-08-02T04:15:47+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "4c2197954135dd3375f32d839238b82a95c0e93e976ddf5a9cbc4a0cf0146e97"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Concise, realistic technical copy - UI microcopy, descriptions, hints, empty/error states, and README/doc prose that informs without over-explaining or restating the obvious, and never uses a typographic dash.",
6
6
  "updated": "2026-08-19T12:37:59+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "a5a228a302f50d975a947b9d84676b4c2a4780f2b222f819388cc98d6a12f207"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Test strategy, coverage gates, deterministic tests, mocking discipline, regression-first bug fixing, and test-suite organization (layout by type/domain, mirrored paths, file naming, fixture/helper placement).",
6
6
  "updated": "2026-06-16T17:11:49+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "3bdf591057b760f674fb2b1425f63acb426cda2c4f042e1a74c5a5d3807df664"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Strict frontend + backend schema validation, normalization before validation (shared normalizers: trim, lowercase email, capitalize names, canonicalize links and handles), real-time error timing (an emptied or not-yet-filled field is incomplete, not invalid: a `*` and an aria-disabled submit instead of \"not valid\" over an empty input), schema consistency, and safe client-facing error handling.",
6
6
  "updated": "2026-08-11T17:10:56+02:00",
7
- "cliVersion": "1.46.4",
7
+ "cliVersion": "1.47.0",
8
8
  "sha": "24928a4c9eed28ebede9f23f746b09a9d3d8176f8fcca61a7780a853fa5bed4e"
9
9
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "enigma-darwin-arm64": "a9965c664e520e3280236ed055301152093e91e1017c03b61aa8eca776462042",
3
- "enigma-linux-arm64": "bf9533b3cc8a1850e6e5efd97923c2404deb4635e46e0ce1c4d1df44bd4a464f",
4
- "enigma-linux-x64": "ecfd3464f10cd78b071ed8f85c44ef110634d36e6d69c91fc3c71d4dbb031395",
5
- "enigma-win32-x64.exe": "30b495218a27b9f71ceb20b748a92bb665336ab5dc5f1b3a2ff63c8fc747b3b6"
2
+ "enigma-darwin-arm64": "a7fb66c4f1d421d71822eb1179118e59b35fb77062c53d4453b880b7fe78b2da",
3
+ "enigma-linux-arm64": "3b0fd51bf5f6160215d0a00f4fb9225b4b6551b5a046250ed457310fc10775c1",
4
+ "enigma-linux-x64": "b4ca6b52c7a752e45fbc3be2fe3a8cf066c5d214dd184d3e7d9a752781604ab3",
5
+ "enigma-win32-x64.exe": "c95d6ffbe7985e30f5b0ce761eb1a8e5f230a9c2f6f377324275d114bd7da041"
6
6
  }
@@ -538,7 +538,7 @@ var init_codegraph_rank = __esm({
538
538
  });
539
539
 
540
540
  // src/codegraph-extract.ts
541
- import { execFileSync } from "child_process";
541
+ import { execFileSync as execFileSync2 } from "child_process";
542
542
  import { extname, join as join4, relative as relative2, resolve as resolve2 } from "path";
543
543
  import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
544
544
  function symRules(lang) {
@@ -830,7 +830,7 @@ function extractFile(lang, content) {
830
830
  }
831
831
  function gitList(root, args) {
832
832
  try {
833
- return execFileSync("git", ["-C", root, "ls-files", ...args, "-z"], {
833
+ return execFileSync2("git", ["-C", root, "ls-files", ...args, "-z"], {
834
834
  encoding: "utf8",
835
835
  maxBuffer: 64 * 1024 * 1024,
836
836
  stdio: ["ignore", "pipe", "ignore"],
@@ -2246,6 +2246,24 @@ var init_codegraph_format = __esm({
2246
2246
 
2247
2247
  // src/codegraph-hook.ts
2248
2248
  import { homedir as homedir3 } from "os";
2249
+
2250
+ // src/governor.ts
2251
+ import { execFileSync } from "child_process";
2252
+ import { cpus, totalmem, freemem, setPriority } from "os";
2253
+ function priorityValue(priority) {
2254
+ return priority === "idle" ? 19 : 10;
2255
+ }
2256
+ function deprioritize(child) {
2257
+ if (child.pid === void 0) return false;
2258
+ try {
2259
+ setPriority(child.pid, priorityValue("below-normal"));
2260
+ return true;
2261
+ } catch {
2262
+ return false;
2263
+ }
2264
+ }
2265
+
2266
+ // src/codegraph-hook.ts
2249
2267
  import { spawn } from "child_process";
2250
2268
  import { basename as basename2, join as join7, relative as relative3, resolve as resolve4 } from "path";
2251
2269
 
@@ -2400,6 +2418,7 @@ function backgroundIndex(cg, dir) {
2400
2418
  if (cg.isManagedPath(dir) || !claimIndex(dir)) return;
2401
2419
  try {
2402
2420
  const child = spawn(process.execPath, [process.argv[1], "codegraph", "index", dir], { detached: true, stdio: "ignore", windowsHide: true });
2421
+ deprioritize(child);
2403
2422
  child.unref();
2404
2423
  } catch {
2405
2424
  }
@@ -796,6 +796,36 @@ var BUILTIN_RULES = [
796
796
  severity: "warn",
797
797
  skill: "frontend-policy"
798
798
  },
799
+ {
800
+ id: "perf-unbounded-fanout",
801
+ label: "Parallel work over a list nobody counted",
802
+ files: ["*.ts", "*.tsx", "*.mts", "*.cts", "*.js", "*.jsx", "*.mjs", "*.cjs"],
803
+ excludeFiles: [
804
+ "*.test.*",
805
+ "*.spec.*",
806
+ "**/tests/**",
807
+ "**/__tests__/**",
808
+ "**/fixtures/**",
809
+ "*.min.js",
810
+ "*.d.ts",
811
+ "**/dist/**",
812
+ "**/build/**",
813
+ "**/node_modules/**",
814
+ "**/vendor/**",
815
+ "dist/**",
816
+ "build/**",
817
+ "node_modules/**",
818
+ "vendor/**"
819
+ ],
820
+ scope: "file",
821
+ // DIFF stage: an existing fan-out has been running in production and is somebody's
822
+ // decision; the one a turn just wrote is the one nobody has counted yet.
823
+ stage: "diff",
824
+ fileCheck: "perf-unbounded-fanout",
825
+ message: "This starts one expensive operation per element of a list whose length nothing here bounds - a process, a socket, a file handle or a database round trip, times however many the glob, the query or the response happened to return. It is fine for the ten items you have in mind and takes the machine down for the fifty thousand it may actually get. Cap the concurrency (a limiter, a `concurrency` option, or process the list in chunks) so the number is a decision instead of an accident, or say why the list is small enough not to need one.",
826
+ severity: "warn",
827
+ skill: "core-engineering-policy"
828
+ },
799
829
  {
800
830
  id: "fe-textarea-size-bounds",
801
831
  label: "Textarea declares a minimum and a maximum size",
@@ -2068,6 +2098,7 @@ var FILE_CHECKS = {
2068
2098
  "proc-windows-hide": (content) => missingWindowsHide(content),
2069
2099
  "fe-server-first-mutation": (content) => serverFirstMutation(content),
2070
2100
  "fe-mutation-without-optimistic": (content) => mutationWithoutOptimisticUpdate(content),
2101
+ "perf-unbounded-fanout": (content) => unboundedFanout(content),
2071
2102
  "fe-textarea-size-bounds": (content) => textareaSizeBounds(content),
2072
2103
  "fe-view-blanked-while-loading": (content) => viewBlankedWhileLoading(content),
2073
2104
  "fe-truncated-value-unreachable": (content) => truncatedValueUnreachable(content),
@@ -2256,6 +2287,28 @@ function mutationWithoutOptimisticUpdate(content) {
2256
2287
  }
2257
2288
  return out;
2258
2289
  }
2290
+ var PROMISE_FANOUT = /\bPromise\.(all|allSettled)\s*\(/;
2291
+ var FANOUT_LITERAL = /\bPromise\.(all|allSettled)\s*\(\s*\[/;
2292
+ var FANOUT_MAPPED = /\.\s*map\s*\(/;
2293
+ var FANOUT_OVER_OBJECT = /\bObject\.(values|keys|entries)\s*\(/;
2294
+ var EXPENSIVE_PER_ITEM = /\b(?:fetch|spawn|spawnSync|exec|execSync|execFile|execFileSync|request)\s*\(|\b(?:readFile|writeFile|readFileSync|writeFileSync|copyFile|rm|mkdir)\s*\(|\.\s*(?:query|findMany|aggregate|createMany|send|upload|download)\s*\(/;
2295
+ var CONCURRENCY_BOUND = /\bp-?[Ll]imit\b|\bpMap\b|\bBottleneck\b|\bSemaphore\b|\bconcurrency\b|\bchunk(?:ed|s)?\s*\(|\bbatch(?:ed|es)?\s*\(/;
2296
+ var FANOUT_WINDOW = 6;
2297
+ function unboundedFanout(content) {
2298
+ if (CONCURRENCY_BOUND.test(content)) return [];
2299
+ const lines = content.split("\n");
2300
+ const out = [];
2301
+ for (let i = 0; i < lines.length; i++) {
2302
+ const line = lines[i];
2303
+ if (COMMENT_LINE.test(line) || !PROMISE_FANOUT.test(line) || FANOUT_LITERAL.test(line)) continue;
2304
+ const window = lines.slice(i, Math.min(lines.length, i + FANOUT_WINDOW)).filter((l) => !COMMENT_LINE.test(l));
2305
+ const text = window.join("\n");
2306
+ if (!FANOUT_MAPPED.test(text) || FANOUT_OVER_OBJECT.test(text)) continue;
2307
+ const heavy = EXPENSIVE_PER_ITEM.exec(text);
2308
+ if (heavy) out.push({ line: i + 1, detail: `one ${heavy[0].replace(/[\s(.]/g, "")} per element, and nothing says how many elements there are` });
2309
+ }
2310
+ return out;
2311
+ }
2259
2312
  var TEXTAREA = /<textarea\b/;
2260
2313
  var TEXTAREA_LOWER = /\brows\s*=|\brows:\s*\d|min-h-|min-height|minHeight|\bh-\[|\bh-\d|height\s*:\s*\d/;
2261
2314
  var TEXTAREA_UPPER = /max-h-|max-height|maxHeight/;
@@ -3120,6 +3173,7 @@ export {
3120
3173
  textareaSizeBounds,
3121
3174
  truncatedValueUnreachable,
3122
3175
  twoFactorPasswordPrompt,
3176
+ unboundedFanout,
3123
3177
  unboundedRemoteList,
3124
3178
  viewBlankedWhileLoading,
3125
3179
  wideNamedImports
package/dist/post-edit.js CHANGED
@@ -594,6 +594,7 @@ __export(guardrails_exports, {
594
594
  textareaSizeBounds: () => textareaSizeBounds,
595
595
  truncatedValueUnreachable: () => truncatedValueUnreachable,
596
596
  twoFactorPasswordPrompt: () => twoFactorPasswordPrompt,
597
+ unboundedFanout: () => unboundedFanout,
597
598
  unboundedRemoteList: () => unboundedRemoteList,
598
599
  viewBlankedWhileLoading: () => viewBlankedWhileLoading,
599
600
  wideNamedImports: () => wideNamedImports
@@ -753,6 +754,21 @@ function mutationWithoutOptimisticUpdate(content) {
753
754
  }
754
755
  return out;
755
756
  }
757
+ function unboundedFanout(content) {
758
+ if (CONCURRENCY_BOUND.test(content)) return [];
759
+ const lines = content.split("\n");
760
+ const out = [];
761
+ for (let i = 0; i < lines.length; i++) {
762
+ const line = lines[i];
763
+ if (COMMENT_LINE.test(line) || !PROMISE_FANOUT.test(line) || FANOUT_LITERAL.test(line)) continue;
764
+ const window = lines.slice(i, Math.min(lines.length, i + FANOUT_WINDOW)).filter((l) => !COMMENT_LINE.test(l));
765
+ const text = window.join("\n");
766
+ if (!FANOUT_MAPPED.test(text) || FANOUT_OVER_OBJECT.test(text)) continue;
767
+ const heavy = EXPENSIVE_PER_ITEM.exec(text);
768
+ if (heavy) out.push({ line: i + 1, detail: `one ${heavy[0].replace(/[\s(.]/g, "")} per element, and nothing says how many elements there are` });
769
+ }
770
+ return out;
771
+ }
756
772
  function textareaSizeBounds(content) {
757
773
  const lower = TEXTAREA_LOWER.test(content);
758
774
  const upper = TEXTAREA_UPPER.test(content);
@@ -1479,7 +1495,7 @@ enigma-guardrails: BLOCKED - ${r.blocks.length} convention violation(s):`);
1479
1495
  console.log(`enigma-guardrails: ${r.count} ${all ? "tracked" : "staged"} file(s) checked, no blocking violations.`);
1480
1496
  return 0;
1481
1497
  }
1482
- var COMMENT_LINE, SKELETON_GUARD_SRC, SKELETON_SIGNAL_SRC, BUILTIN_RULES, PROJECT_CHECKS, CURRENT_PASSWORD, NEW_PASSWORD, NEW_PASSWORD_PROP, NOT_A_JSX_PROP, FILE_CHECKS, FIXERS, SPAWNERS, MUTATING_REQUEST, ENTITY_WRITE, OPTIMISTIC_SIGNAL, ALLOW_SERVER_FIRST, RESULT_BINDING, BLOCK_LOOKBACK, BLOCK_LOOKAHEAD, REVALIDATE_AFTER, TEXTAREA, TEXTAREA_LOWER, TEXTAREA_UPPER, TEXTAREA_FIXED, TEXTAREA_AUTOSIZE, VIEW_GUARD, LOADING_NAME, LOADING_VALUE, VIEW_GUARD_LOADING, VIEW_CHROME_HEADING, VIEW_CHROME_TEXT, VIEW_BODY_LOOKAHEAD, ALLOW_VIEW_SKELETON, SKELETON_GUARD, SKELETON_SIGNAL, REMOTE_COLLECTION_BINDINGS, LIST_BOUNDED, ALLOW_UNBOUNDED_LIST, MENU_HANDLER, MENU_POINT, MENU_STATE, MENU_MITIGATED, SELECT_RANGE_KEY, SELECT_ADD_KEY, SELECT_STATE, SELECT_MITIGATED, COLOR_MATHS, COLOR_DRAG, COLOR_MEASURE, COLOR_STATE, COLOR_MITIGATED, LIGHTBOX_IMAGE, LIGHTBOX_WHEEL, LIGHTBOX_ZOOM, LIGHTBOX_DIALOG, LIGHTBOX_MITIGATED, VIDEO_ELEMENT, VIDEO_DRIVEN, VIDEO_TIMELINE, VIDEO_CONTROLS, VIDEO_MITIGATED, TWOFA_SUBJECT, TWOFA_ENROLL, TWOFA_PASSWORD, TWOFA_MITIGATED, TWOFA_WINDOW, LISTBOX_ROLE, LISTBOX_OPEN, LISTBOX_OPTIONS, LISTBOX_MITIGATED, TOAST_QUEUE, TOAST_TIMER, TOAST_MITIGATED, PALETTE_SHORTCUT, PALETTE_MODIFIER, PALETTE_OPEN, PALETTE_FILTER, PALETTE_MITIGATED, CLIP_ONE_LINE, DYNAMIC_CHILD, VALUE_REACHABLE, WRAPPER_LOOKBACK, ALLOW_CLIPPED_VALUE, CLIPPED_SIMPLE_VALUE, NOT_TEXT_BINDING, ASYNC_ROUTE, SERVER_DATA_AWAIT, STREAM_BOUNDARY, ALLOW_BLOCKING_PAGE, LOADING_BOUNDARY_FILE, SPECIFIER, MODULE_EXT, JS_EXT, tsconfigCache, NAMED_IMPORT, GENERIC_ACCOUNTS, INTERNAL_MODULE, globCache, DIFF_HUNK, LEDGER_MAX_BYTES, LEDGER_KEEP, LEDGER_KEEP_REPLY, grEntry, isGrEntry;
1498
+ var COMMENT_LINE, SKELETON_GUARD_SRC, SKELETON_SIGNAL_SRC, BUILTIN_RULES, PROJECT_CHECKS, CURRENT_PASSWORD, NEW_PASSWORD, NEW_PASSWORD_PROP, NOT_A_JSX_PROP, FILE_CHECKS, FIXERS, SPAWNERS, MUTATING_REQUEST, ENTITY_WRITE, OPTIMISTIC_SIGNAL, ALLOW_SERVER_FIRST, RESULT_BINDING, BLOCK_LOOKBACK, BLOCK_LOOKAHEAD, REVALIDATE_AFTER, PROMISE_FANOUT, FANOUT_LITERAL, FANOUT_MAPPED, FANOUT_OVER_OBJECT, EXPENSIVE_PER_ITEM, CONCURRENCY_BOUND, FANOUT_WINDOW, TEXTAREA, TEXTAREA_LOWER, TEXTAREA_UPPER, TEXTAREA_FIXED, TEXTAREA_AUTOSIZE, VIEW_GUARD, LOADING_NAME, LOADING_VALUE, VIEW_GUARD_LOADING, VIEW_CHROME_HEADING, VIEW_CHROME_TEXT, VIEW_BODY_LOOKAHEAD, ALLOW_VIEW_SKELETON, SKELETON_GUARD, SKELETON_SIGNAL, REMOTE_COLLECTION_BINDINGS, LIST_BOUNDED, ALLOW_UNBOUNDED_LIST, MENU_HANDLER, MENU_POINT, MENU_STATE, MENU_MITIGATED, SELECT_RANGE_KEY, SELECT_ADD_KEY, SELECT_STATE, SELECT_MITIGATED, COLOR_MATHS, COLOR_DRAG, COLOR_MEASURE, COLOR_STATE, COLOR_MITIGATED, LIGHTBOX_IMAGE, LIGHTBOX_WHEEL, LIGHTBOX_ZOOM, LIGHTBOX_DIALOG, LIGHTBOX_MITIGATED, VIDEO_ELEMENT, VIDEO_DRIVEN, VIDEO_TIMELINE, VIDEO_CONTROLS, VIDEO_MITIGATED, TWOFA_SUBJECT, TWOFA_ENROLL, TWOFA_PASSWORD, TWOFA_MITIGATED, TWOFA_WINDOW, LISTBOX_ROLE, LISTBOX_OPEN, LISTBOX_OPTIONS, LISTBOX_MITIGATED, TOAST_QUEUE, TOAST_TIMER, TOAST_MITIGATED, PALETTE_SHORTCUT, PALETTE_MODIFIER, PALETTE_OPEN, PALETTE_FILTER, PALETTE_MITIGATED, CLIP_ONE_LINE, DYNAMIC_CHILD, VALUE_REACHABLE, WRAPPER_LOOKBACK, ALLOW_CLIPPED_VALUE, CLIPPED_SIMPLE_VALUE, NOT_TEXT_BINDING, ASYNC_ROUTE, SERVER_DATA_AWAIT, STREAM_BOUNDARY, ALLOW_BLOCKING_PAGE, LOADING_BOUNDARY_FILE, SPECIFIER, MODULE_EXT, JS_EXT, tsconfigCache, NAMED_IMPORT, GENERIC_ACCOUNTS, INTERNAL_MODULE, globCache, DIFF_HUNK, LEDGER_MAX_BYTES, LEDGER_KEEP, LEDGER_KEEP_REPLY, grEntry, isGrEntry;
1483
1499
  var init_guardrails = __esm({
1484
1500
  "src/guardrails.ts"() {
1485
1501
  "use strict";
@@ -2273,6 +2289,36 @@ var init_guardrails = __esm({
2273
2289
  severity: "warn",
2274
2290
  skill: "frontend-policy"
2275
2291
  },
2292
+ {
2293
+ id: "perf-unbounded-fanout",
2294
+ label: "Parallel work over a list nobody counted",
2295
+ files: ["*.ts", "*.tsx", "*.mts", "*.cts", "*.js", "*.jsx", "*.mjs", "*.cjs"],
2296
+ excludeFiles: [
2297
+ "*.test.*",
2298
+ "*.spec.*",
2299
+ "**/tests/**",
2300
+ "**/__tests__/**",
2301
+ "**/fixtures/**",
2302
+ "*.min.js",
2303
+ "*.d.ts",
2304
+ "**/dist/**",
2305
+ "**/build/**",
2306
+ "**/node_modules/**",
2307
+ "**/vendor/**",
2308
+ "dist/**",
2309
+ "build/**",
2310
+ "node_modules/**",
2311
+ "vendor/**"
2312
+ ],
2313
+ scope: "file",
2314
+ // DIFF stage: an existing fan-out has been running in production and is somebody's
2315
+ // decision; the one a turn just wrote is the one nobody has counted yet.
2316
+ stage: "diff",
2317
+ fileCheck: "perf-unbounded-fanout",
2318
+ message: "This starts one expensive operation per element of a list whose length nothing here bounds - a process, a socket, a file handle or a database round trip, times however many the glob, the query or the response happened to return. It is fine for the ten items you have in mind and takes the machine down for the fifty thousand it may actually get. Cap the concurrency (a limiter, a `concurrency` option, or process the list in chunks) so the number is a decision instead of an accident, or say why the list is small enough not to need one.",
2319
+ severity: "warn",
2320
+ skill: "core-engineering-policy"
2321
+ },
2276
2322
  {
2277
2323
  id: "fe-textarea-size-bounds",
2278
2324
  label: "Textarea declares a minimum and a maximum size",
@@ -3533,6 +3579,7 @@ var init_guardrails = __esm({
3533
3579
  "proc-windows-hide": (content) => missingWindowsHide(content),
3534
3580
  "fe-server-first-mutation": (content) => serverFirstMutation(content),
3535
3581
  "fe-mutation-without-optimistic": (content) => mutationWithoutOptimisticUpdate(content),
3582
+ "perf-unbounded-fanout": (content) => unboundedFanout(content),
3536
3583
  "fe-textarea-size-bounds": (content) => textareaSizeBounds(content),
3537
3584
  "fe-view-blanked-while-loading": (content) => viewBlankedWhileLoading(content),
3538
3585
  "fe-truncated-value-unreachable": (content) => truncatedValueUnreachable(content),
@@ -3583,6 +3630,13 @@ var init_guardrails = __esm({
3583
3630
  BLOCK_LOOKBACK = 120;
3584
3631
  BLOCK_LOOKAHEAD = 200;
3585
3632
  REVALIDATE_AFTER = /\brouter\.refresh\s*\(|\brevalidatePath\s*\(|\brevalidateTag\s*\(|\binvalidateQueries\s*\(|\binvalidateAll\s*\(|\brefetch\s*\(|\bmutate\s*\(\s*[)"'`]/;
3633
+ PROMISE_FANOUT = /\bPromise\.(all|allSettled)\s*\(/;
3634
+ FANOUT_LITERAL = /\bPromise\.(all|allSettled)\s*\(\s*\[/;
3635
+ FANOUT_MAPPED = /\.\s*map\s*\(/;
3636
+ FANOUT_OVER_OBJECT = /\bObject\.(values|keys|entries)\s*\(/;
3637
+ EXPENSIVE_PER_ITEM = /\b(?:fetch|spawn|spawnSync|exec|execSync|execFile|execFileSync|request)\s*\(|\b(?:readFile|writeFile|readFileSync|writeFileSync|copyFile|rm|mkdir)\s*\(|\.\s*(?:query|findMany|aggregate|createMany|send|upload|download)\s*\(/;
3638
+ CONCURRENCY_BOUND = /\bp-?[Ll]imit\b|\bpMap\b|\bBottleneck\b|\bSemaphore\b|\bconcurrency\b|\bchunk(?:ed|s)?\s*\(|\bbatch(?:ed|es)?\s*\(/;
3639
+ FANOUT_WINDOW = 6;
3586
3640
  TEXTAREA = /<textarea\b/;
3587
3641
  TEXTAREA_LOWER = /\brows\s*=|\brows:\s*\d|min-h-|min-height|minHeight|\bh-\[|\bh-\d|height\s*:\s*\d/;
3588
3642
  TEXTAREA_UPPER = /max-h-|max-height|maxHeight/;
@@ -3684,6 +3738,27 @@ var init_guardrails = __esm({
3684
3738
  }
3685
3739
  });
3686
3740
 
3741
+ // src/governor.ts
3742
+ import { execFileSync as execFileSync3 } from "child_process";
3743
+ import { cpus, totalmem, freemem, setPriority } from "os";
3744
+ function priorityValue(priority) {
3745
+ return priority === "idle" ? 19 : 10;
3746
+ }
3747
+ function deprioritize(child) {
3748
+ if (child.pid === void 0) return false;
3749
+ try {
3750
+ setPriority(child.pid, priorityValue("below-normal"));
3751
+ return true;
3752
+ } catch {
3753
+ return false;
3754
+ }
3755
+ }
3756
+ var init_governor = __esm({
3757
+ "src/governor.ts"() {
3758
+ "use strict";
3759
+ }
3760
+ });
3761
+
3687
3762
  // src/repo-keyed-file.ts
3688
3763
  import { dirname as dirname3 } from "path";
3689
3764
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, renameSync, rmSync, writeFileSync as writeFileSync4 } from "fs";
@@ -3929,7 +4004,7 @@ var init_codegraph_rank = __esm({
3929
4004
  });
3930
4005
 
3931
4006
  // src/codegraph-extract.ts
3932
- import { execFileSync as execFileSync3 } from "child_process";
4007
+ import { execFileSync as execFileSync4 } from "child_process";
3933
4008
  import { extname as extname2, join as join6, relative as relative2, resolve as resolve3 } from "path";
3934
4009
  import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
3935
4010
  function symRules(lang) {
@@ -4221,7 +4296,7 @@ function extractFile(lang, content) {
4221
4296
  }
4222
4297
  function gitList(root, args) {
4223
4298
  try {
4224
- return execFileSync3("git", ["-C", root, "ls-files", ...args, "-z"], {
4299
+ return execFileSync4("git", ["-C", root, "ls-files", ...args, "-z"], {
4225
4300
  encoding: "utf8",
4226
4301
  maxBuffer: 64 * 1024 * 1024,
4227
4302
  stdio: ["ignore", "pipe", "ignore"],
@@ -5750,6 +5825,7 @@ function backgroundIndex(cg, dir) {
5750
5825
  if (cg.isManagedPath(dir) || !claimIndex(dir)) return;
5751
5826
  try {
5752
5827
  const child = spawn(process.execPath, [process.argv[1], "codegraph", "index", dir], { detached: true, stdio: "ignore", windowsHide: true });
5828
+ deprioritize(child);
5753
5829
  child.unref();
5754
5830
  } catch {
5755
5831
  }
@@ -5832,6 +5908,7 @@ var MIN_PROMPT_CHARS, PROMPT_HITS, STRONG_FLOOR, BROAD_FLOOR, INJECTED_CAP, BLAS
5832
5908
  var init_codegraph_hook = __esm({
5833
5909
  "src/codegraph-hook.ts"() {
5834
5910
  "use strict";
5911
+ init_governor();
5835
5912
  init_repo_keyed_file();
5836
5913
  MIN_PROMPT_CHARS = 12;
5837
5914
  PROMPT_HITS = 3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enigma-cli",
3
- "version": "1.46.4",
3
+ "version": "1.47.0",
4
4
  "description": "Everything you need to work with a coding agent: install shared policy skills for Claude Code, OpenAI Codex and opencode, and set up portable git security hooks.",
5
5
  "type": "module",
6
6
  "bin": {