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.
- package/assets/skills/anti-overengineering-policy/skill.json +1 -1
- package/assets/skills/anti-overengineering-review/skill.json +1 -1
- package/assets/skills/backend-policy/skill.json +1 -1
- package/assets/skills/ciphera-style-policy/skill.json +1 -1
- package/assets/skills/code-review-policy/skill.json +1 -1
- package/assets/skills/core-engineering-policy/skill.json +1 -1
- package/assets/skills/database-expert/skill.json +1 -1
- package/assets/skills/debugging-policy/skill.json +1 -1
- package/assets/skills/dependency-policy/skill.json +1 -1
- package/assets/skills/email-policy/skill.json +1 -1
- package/assets/skills/frontend-design/skill.json +1 -1
- package/assets/skills/frontend-policy/skill.json +1 -1
- package/assets/skills/git-policy/skill.json +1 -1
- package/assets/skills/logo-sourcing-policy/skill.json +1 -1
- package/assets/skills/security-policy/skill.json +1 -1
- package/assets/skills/skill-creator/skill.json +1 -1
- package/assets/skills/task-completion-policy/skill.json +1 -1
- package/assets/skills/technical-writing-policy/skill.json +1 -1
- package/assets/skills/testing-policy/skill.json +1 -1
- package/assets/skills/validation-policy/skill.json +1 -1
- package/bin/checksums.json +4 -4
- package/dist/codegraph-hook.js +21 -2
- package/dist/guardrails.js +54 -0
- package/dist/post-edit.js +80 -3
- package/package.json +1 -1
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
7
|
+
"cliVersion": "1.47.0",
|
|
8
8
|
"sha": "6b8a924cc11b61d4b45b73384aeb095ce622229359b7cc2c6ce4bb0cf3015ed7"
|
|
9
9
|
}
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
7
|
+
"cliVersion": "1.47.0",
|
|
8
8
|
"sha": "cb320a6a5fd3279604ad7920b877c1a36e48f4f39aeead1a4c4daa89901d9ce0"
|
|
9
9
|
}
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
7
|
+
"cliVersion": "1.47.0",
|
|
8
8
|
"sha": "24928a4c9eed28ebede9f23f746b09a9d3d8176f8fcca61a7780a853fa5bed4e"
|
|
9
9
|
}
|
package/bin/checksums.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"enigma-darwin-arm64": "
|
|
3
|
-
"enigma-linux-arm64": "
|
|
4
|
-
"enigma-linux-x64": "
|
|
5
|
-
"enigma-win32-x64.exe": "
|
|
2
|
+
"enigma-darwin-arm64": "a7fb66c4f1d421d71822eb1179118e59b35fb77062c53d4453b880b7fe78b2da",
|
|
3
|
+
"enigma-linux-arm64": "3b0fd51bf5f6160215d0a00f4fb9225b4b6551b5a046250ed457310fc10775c1",
|
|
4
|
+
"enigma-linux-x64": "b4ca6b52c7a752e45fbc3be2fe3a8cf066c5d214dd184d3e7d9a752781604ab3",
|
|
5
|
+
"enigma-win32-x64.exe": "c95d6ffbe7985e30f5b0ce761eb1a8e5f230a9c2f6f377324275d114bd7da041"
|
|
6
6
|
}
|
package/dist/codegraph-hook.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/dist/guardrails.js
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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": {
|