enigma-cli 1.46.2 → 1.46.4
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/bin/download.mjs +55 -6
- package/bin/platform.mjs +39 -3
- package/dist/guardrails.js +38 -0
- package/dist/post-edit.js +39 -1
- 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.46.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.46.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.46.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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.
|
|
7
|
+
"cliVersion": "1.46.4",
|
|
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": "a9965c664e520e3280236ed055301152093e91e1017c03b61aa8eca776462042",
|
|
3
|
+
"enigma-linux-arm64": "bf9533b3cc8a1850e6e5efd97923c2404deb4635e46e0ce1c4d1df44bd4a464f",
|
|
4
|
+
"enigma-linux-x64": "ecfd3464f10cd78b071ed8f85c44ef110634d36e6d69c91fc3c71d4dbb031395",
|
|
5
|
+
"enigma-win32-x64.exe": "30b495218a27b9f71ceb20b748a92bb665336ab5dc5f1b3a2ff63c8fc747b3b6"
|
|
6
6
|
}
|
package/bin/download.mjs
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* Used by bin/postinstall.mjs (at install) and bin/enigma.mjs (lazy, first run when
|
|
12
12
|
* install scripts were skipped). Node builtins only.
|
|
13
13
|
*/
|
|
14
|
-
import { dirname } from "node:path";
|
|
15
14
|
import { createHash } from "node:crypto";
|
|
16
|
-
import {
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
19
|
ARCH,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
assetName,
|
|
22
22
|
binTargetPath,
|
|
23
23
|
isWindows,
|
|
24
|
+
legacyBinTargetPath,
|
|
24
25
|
loadChecksums,
|
|
25
26
|
packageVersion,
|
|
26
27
|
platformKeys
|
|
@@ -52,6 +53,54 @@ function sidecarPath() {
|
|
|
52
53
|
return `${binTargetPath()}.sha256`;
|
|
53
54
|
};
|
|
54
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Move an existing binary aside instead of deleting it.
|
|
58
|
+
*
|
|
59
|
+
* Windows refuses to delete or overwrite an `.exe` that a running process has mapped as an
|
|
60
|
+
* image, and it DOES allow renaming one: the live processes keep the image they already
|
|
61
|
+
* mapped, and the next launch picks up the new file. Deleting first - what this did - failed
|
|
62
|
+
* precisely when an agent session was open, which is most of the time.
|
|
63
|
+
*/
|
|
64
|
+
function retireBinary(target) {
|
|
65
|
+
if (!existsSync(target)) return;
|
|
66
|
+
try {
|
|
67
|
+
renameSync(target, `${target}.old-${Date.now()}`);
|
|
68
|
+
} catch {
|
|
69
|
+
// No rename either (a permission problem rather than a mapped image): fall back to the
|
|
70
|
+
// delete, and let the rename that follows surface whatever is really wrong.
|
|
71
|
+
try { unlinkSync(target); } catch { /* reported by the caller's rename */ }
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Delete what this version has finished with. Best-effort: a file still mapped by a live
|
|
77
|
+
* process refuses to go and is left for the next run.
|
|
78
|
+
*
|
|
79
|
+
* Deliberately NOT the sibling version directories. A machine can have enigma installed under
|
|
80
|
+
* two Node versions at two different versions - which is exactly the setup that made the
|
|
81
|
+
* per-version directory necessary - and an install that deleted its neighbour's binary would
|
|
82
|
+
* have the two of them take turns re-downloading 96 MB forever. Superseded versions are left
|
|
83
|
+
* on disk and reclaimed by `enigma resources`, where the user is already deleting things on
|
|
84
|
+
* purpose.
|
|
85
|
+
*/
|
|
86
|
+
function sweepStaleBinaries(target) {
|
|
87
|
+
const versionDir = dirname(target);
|
|
88
|
+
for (const name of safeReaddir(versionDir)) {
|
|
89
|
+
if (!/\.old-\d+$/.test(name)) continue;
|
|
90
|
+
try { unlinkSync(join(versionDir, name)); } catch { /* still mapped by a live process */ }
|
|
91
|
+
}
|
|
92
|
+
// The pre-1.46.4 location, inside the npm package: dead weight now, and the file npm used
|
|
93
|
+
// to trip over on upgrade.
|
|
94
|
+
for (const path of [legacyBinTargetPath(), `${legacyBinTargetPath()}.sha256`]) {
|
|
95
|
+
try { unlinkSync(path); } catch { /* absent, or still mapped by an older launcher */ }
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/** readdirSync that answers with an empty list instead of throwing on a missing directory. */
|
|
100
|
+
function safeReaddir(dir) {
|
|
101
|
+
try { return readdirSync(dir); } catch { return []; }
|
|
102
|
+
};
|
|
103
|
+
|
|
55
104
|
/** The sha256 this package version expects for the host's asset, or null if none is known. */
|
|
56
105
|
function expectedSha() {
|
|
57
106
|
const choice = selectAsset();
|
|
@@ -103,19 +152,19 @@ export async function downloadBinary({ log = () => {} } = {}) {
|
|
|
103
152
|
|
|
104
153
|
const actual = createHash("sha256").update(buffer).digest("hex");
|
|
105
154
|
if (actual !== choice.sha256) throw new Error(`Checksum mismatch for ${choice.asset}: expected ${choice.sha256}, got ${actual}.`);
|
|
106
|
-
|
|
155
|
+
|
|
107
156
|
const target = binTargetPath();
|
|
108
157
|
mkdirSync(dirname(target), { recursive: true });
|
|
109
158
|
const tmp = `${target}.download-${process.pid}`;
|
|
110
159
|
writeFileSync(tmp, buffer);
|
|
111
160
|
if (!isWindows) chmodSync(tmp, 0o755);
|
|
112
|
-
|
|
113
|
-
if (existsSync(target)) try { unlinkSync(target); } catch { /* in use; rename will surface the error */ }
|
|
161
|
+
retireBinary(target);
|
|
114
162
|
renameSync(tmp, target);
|
|
163
|
+
sweepStaleBinaries(target);
|
|
115
164
|
// Record the installed sha so installedBinary() can detect a stale binary after an
|
|
116
165
|
// update without re-hashing the whole file on every launch. Best-effort: a missing
|
|
117
166
|
// sidecar just triggers one extra re-download next time.
|
|
118
167
|
try { writeFileSync(sidecarPath(), choice.sha256); } catch { /* sidecar is best-effort */ }
|
|
119
168
|
log(`Installed ${choice.asset} -> ${target}`);
|
|
120
169
|
return target;
|
|
121
|
-
};
|
|
170
|
+
};
|
package/bin/platform.mjs
CHANGED
|
@@ -59,9 +59,45 @@ export function assetName(key) {
|
|
|
59
59
|
return `enigma-${key}${isWindows ? ".exe" : ""}`;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
/**
|
|
62
|
+
/** File name of the compiled binary for this host. */
|
|
63
|
+
const binName = () => (isWindows ? "enigma-bin.exe" : "enigma-bin");
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Directory the compiled binaries live in, one subdirectory per package version.
|
|
67
|
+
*
|
|
68
|
+
* Deliberately OUTSIDE the npm package, and this is the whole point of it. npm copies a
|
|
69
|
+
* package's directory aside before replacing it, and Windows refuses to copy or delete an
|
|
70
|
+
* `.exe` that a running process has mapped as an image - so keeping a 96 MB binary in
|
|
71
|
+
* `<pkg>/bin` made `npm i -g enigma-cli@latest` fail with EBUSY for anyone holding an agent
|
|
72
|
+
* session open, which on a machine running several is always. Nothing npm touches can hold
|
|
73
|
+
* the binary any more.
|
|
74
|
+
*
|
|
75
|
+
* Per VERSION because a machine can have enigma installed under more than one Node version
|
|
76
|
+
* (nvm), and a single shared path would have the two installs overwrite each other's binary
|
|
77
|
+
* and re-download on every launch, each seeing the other's checksum as stale.
|
|
78
|
+
*/
|
|
79
|
+
export function binDir() {
|
|
80
|
+
return join(process.env.ENIGMA_CONFIG_HOME || os.homedir(), ".enigma", "bin");
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* On-disk path of the installed binary for THIS package version.
|
|
85
|
+
*
|
|
86
|
+
* The version is read defensively: this path is resolved on every launch, and an unreadable
|
|
87
|
+
* package.json must degrade to one shared directory rather than throw and take the CLI with it.
|
|
88
|
+
*/
|
|
63
89
|
export function binTargetPath() {
|
|
64
|
-
|
|
90
|
+
let version = "unknown";
|
|
91
|
+
try { version = packageVersion(); } catch { /* fall back to the shared directory */ }
|
|
92
|
+
return join(binDir(), version, binName());
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Where releases up to 1.46.3 put the binary: inside the package. Kept so a stale copy can be
|
|
97
|
+
* swept after an upgrade - it is dead weight, and the thing npm used to trip over.
|
|
98
|
+
*/
|
|
99
|
+
export function legacyBinTargetPath() {
|
|
100
|
+
return join(pkgRoot, "bin", binName());
|
|
65
101
|
};
|
|
66
102
|
|
|
67
103
|
/** This package's declared version (release tag is `v<version>`). */
|
|
@@ -78,4 +114,4 @@ export function loadChecksums() {
|
|
|
78
114
|
} catch {
|
|
79
115
|
return {};
|
|
80
116
|
}
|
|
81
|
-
};
|
|
117
|
+
};
|
package/dist/guardrails.js
CHANGED
|
@@ -777,6 +777,25 @@ var BUILTIN_RULES = [
|
|
|
777
777
|
severity: "block",
|
|
778
778
|
skill: "frontend-policy"
|
|
779
779
|
},
|
|
780
|
+
{
|
|
781
|
+
id: "fe-mutation-without-optimistic",
|
|
782
|
+
label: "A mutation that leaves the UI waiting",
|
|
783
|
+
files: ["*.tsx", "*.jsx", "*.vue", "*.svelte"],
|
|
784
|
+
excludeFiles: ["*.test.*", "*.spec.*", "**/tests/**", "**/__tests__/**", "**/dist/**", "dist/**", "**/build/**", "build/**", "**/.next/**", ".next/**", "**/node_modules/**"],
|
|
785
|
+
scope: "file",
|
|
786
|
+
// DIFF stage for the same reason as the rule above, and more urgently: the measured
|
|
787
|
+
// backlog is 116 of 140 mutating files with no optimistic update at all, so an
|
|
788
|
+
// edit-stage version of this would fire on almost every UI file forever. Against the
|
|
789
|
+
// lines a turn ADDED there is no backlog - it fires on a handler just written.
|
|
790
|
+
stage: "diff",
|
|
791
|
+
fileCheck: "fe-mutation-without-optimistic",
|
|
792
|
+
message: "This handler sends the mutation and then updates nothing: whatever the user sees has to come back from the server, so the interface sits still for the whole round trip on an action that usually cannot fail in an interesting way. Apply the change to local state FIRST, then send the request, and on failure restore the value you saved and say what failed - a silent revert is worse than the wait. A refetch, a revalidation or a router refresh after the await is the same wait wearing a different name. Mark the line `enigma:allow-server-first` when the response is genuinely required before the UI may change (a payment, a server-assigned identifier, an irreversible action) (frontend-policy).",
|
|
793
|
+
// Warn, not block: waiting is a defect for a save, a toggle or a delete and correct for
|
|
794
|
+
// a payment, and nothing in the file separates them. The sibling rule blocks because it
|
|
795
|
+
// has evidence; this one only has an absence, so it says its piece and gets out of the way.
|
|
796
|
+
severity: "warn",
|
|
797
|
+
skill: "frontend-policy"
|
|
798
|
+
},
|
|
780
799
|
{
|
|
781
800
|
id: "fe-textarea-size-bounds",
|
|
782
801
|
label: "Textarea declares a minimum and a maximum size",
|
|
@@ -2048,6 +2067,7 @@ function newPasswordAffordanceOnSignIn(content) {
|
|
|
2048
2067
|
var FILE_CHECKS = {
|
|
2049
2068
|
"proc-windows-hide": (content) => missingWindowsHide(content),
|
|
2050
2069
|
"fe-server-first-mutation": (content) => serverFirstMutation(content),
|
|
2070
|
+
"fe-mutation-without-optimistic": (content) => mutationWithoutOptimisticUpdate(content),
|
|
2051
2071
|
"fe-textarea-size-bounds": (content) => textareaSizeBounds(content),
|
|
2052
2072
|
"fe-view-blanked-while-loading": (content) => viewBlankedWhileLoading(content),
|
|
2053
2073
|
"fe-truncated-value-unreachable": (content) => truncatedValueUnreachable(content),
|
|
@@ -2219,6 +2239,23 @@ function serverFirstMutation(content) {
|
|
|
2219
2239
|
}
|
|
2220
2240
|
return out;
|
|
2221
2241
|
}
|
|
2242
|
+
var REVALIDATE_AFTER = /\brouter\.refresh\s*\(|\brevalidatePath\s*\(|\brevalidateTag\s*\(|\binvalidateQueries\s*\(|\binvalidateAll\s*\(|\brefetch\s*\(|\bmutate\s*\(\s*[)"'`]/;
|
|
2243
|
+
function mutationWithoutOptimisticUpdate(content) {
|
|
2244
|
+
if (OPTIMISTIC_SIGNAL.test(content)) return [];
|
|
2245
|
+
const lines = content.split("\n");
|
|
2246
|
+
const out = [];
|
|
2247
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2248
|
+
const line = lines[i];
|
|
2249
|
+
if (COMMENT_LINE.test(line) || !MUTATING_REQUEST.test(line)) continue;
|
|
2250
|
+
const { start, end } = enclosingBlock(lines, i);
|
|
2251
|
+
const body = lines.slice(start, end + 1);
|
|
2252
|
+
if (body.some((l) => ALLOW_SERVER_FIRST.test(l))) continue;
|
|
2253
|
+
if (body.some((l) => !COMMENT_LINE.test(l) && ENTITY_WRITE.test(l))) continue;
|
|
2254
|
+
const reload = lines.slice(i + 1, end + 1).find((l) => !COMMENT_LINE.test(l) && REVALIDATE_AFTER.test(l));
|
|
2255
|
+
if (reload) out.push({ line: i + 1, detail: `the screen only changes once the server answers: ${reload.trim().slice(0, 80)}` });
|
|
2256
|
+
}
|
|
2257
|
+
return out;
|
|
2258
|
+
}
|
|
2222
2259
|
var TEXTAREA = /<textarea\b/;
|
|
2223
2260
|
var TEXTAREA_LOWER = /\brows\s*=|\brows:\s*\d|min-h-|min-height|minHeight|\bh-\[|\bh-\d|height\s*:\s*\d/;
|
|
2224
2261
|
var TEXTAREA_UPPER = /max-h-|max-height|maxHeight/;
|
|
@@ -3067,6 +3104,7 @@ export {
|
|
|
3067
3104
|
loadRules,
|
|
3068
3105
|
missingPathAlias,
|
|
3069
3106
|
missingWindowsHide,
|
|
3107
|
+
mutationWithoutOptimisticUpdate,
|
|
3070
3108
|
newPasswordAffordanceOnSignIn,
|
|
3071
3109
|
operatorHomePathLeak,
|
|
3072
3110
|
pageAwaitWithoutBoundary,
|
package/dist/post-edit.js
CHANGED
|
@@ -578,6 +578,7 @@ __export(guardrails_exports, {
|
|
|
578
578
|
loadRules: () => loadRules,
|
|
579
579
|
missingPathAlias: () => missingPathAlias,
|
|
580
580
|
missingWindowsHide: () => missingWindowsHide,
|
|
581
|
+
mutationWithoutOptimisticUpdate: () => mutationWithoutOptimisticUpdate,
|
|
581
582
|
newPasswordAffordanceOnSignIn: () => newPasswordAffordanceOnSignIn,
|
|
582
583
|
operatorHomePathLeak: () => operatorHomePathLeak,
|
|
583
584
|
pageAwaitWithoutBoundary: () => pageAwaitWithoutBoundary,
|
|
@@ -736,6 +737,22 @@ function serverFirstMutation(content) {
|
|
|
736
737
|
}
|
|
737
738
|
return out;
|
|
738
739
|
}
|
|
740
|
+
function mutationWithoutOptimisticUpdate(content) {
|
|
741
|
+
if (OPTIMISTIC_SIGNAL.test(content)) return [];
|
|
742
|
+
const lines = content.split("\n");
|
|
743
|
+
const out = [];
|
|
744
|
+
for (let i = 0; i < lines.length; i++) {
|
|
745
|
+
const line = lines[i];
|
|
746
|
+
if (COMMENT_LINE.test(line) || !MUTATING_REQUEST.test(line)) continue;
|
|
747
|
+
const { start, end } = enclosingBlock(lines, i);
|
|
748
|
+
const body = lines.slice(start, end + 1);
|
|
749
|
+
if (body.some((l) => ALLOW_SERVER_FIRST.test(l))) continue;
|
|
750
|
+
if (body.some((l) => !COMMENT_LINE.test(l) && ENTITY_WRITE.test(l))) continue;
|
|
751
|
+
const reload = lines.slice(i + 1, end + 1).find((l) => !COMMENT_LINE.test(l) && REVALIDATE_AFTER.test(l));
|
|
752
|
+
if (reload) out.push({ line: i + 1, detail: `the screen only changes once the server answers: ${reload.trim().slice(0, 80)}` });
|
|
753
|
+
}
|
|
754
|
+
return out;
|
|
755
|
+
}
|
|
739
756
|
function textareaSizeBounds(content) {
|
|
740
757
|
const lower = TEXTAREA_LOWER.test(content);
|
|
741
758
|
const upper = TEXTAREA_UPPER.test(content);
|
|
@@ -1462,7 +1479,7 @@ enigma-guardrails: BLOCKED - ${r.blocks.length} convention violation(s):`);
|
|
|
1462
1479
|
console.log(`enigma-guardrails: ${r.count} ${all ? "tracked" : "staged"} file(s) checked, no blocking violations.`);
|
|
1463
1480
|
return 0;
|
|
1464
1481
|
}
|
|
1465
|
-
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, 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;
|
|
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;
|
|
1466
1483
|
var init_guardrails = __esm({
|
|
1467
1484
|
"src/guardrails.ts"() {
|
|
1468
1485
|
"use strict";
|
|
@@ -2237,6 +2254,25 @@ var init_guardrails = __esm({
|
|
|
2237
2254
|
severity: "block",
|
|
2238
2255
|
skill: "frontend-policy"
|
|
2239
2256
|
},
|
|
2257
|
+
{
|
|
2258
|
+
id: "fe-mutation-without-optimistic",
|
|
2259
|
+
label: "A mutation that leaves the UI waiting",
|
|
2260
|
+
files: ["*.tsx", "*.jsx", "*.vue", "*.svelte"],
|
|
2261
|
+
excludeFiles: ["*.test.*", "*.spec.*", "**/tests/**", "**/__tests__/**", "**/dist/**", "dist/**", "**/build/**", "build/**", "**/.next/**", ".next/**", "**/node_modules/**"],
|
|
2262
|
+
scope: "file",
|
|
2263
|
+
// DIFF stage for the same reason as the rule above, and more urgently: the measured
|
|
2264
|
+
// backlog is 116 of 140 mutating files with no optimistic update at all, so an
|
|
2265
|
+
// edit-stage version of this would fire on almost every UI file forever. Against the
|
|
2266
|
+
// lines a turn ADDED there is no backlog - it fires on a handler just written.
|
|
2267
|
+
stage: "diff",
|
|
2268
|
+
fileCheck: "fe-mutation-without-optimistic",
|
|
2269
|
+
message: "This handler sends the mutation and then updates nothing: whatever the user sees has to come back from the server, so the interface sits still for the whole round trip on an action that usually cannot fail in an interesting way. Apply the change to local state FIRST, then send the request, and on failure restore the value you saved and say what failed - a silent revert is worse than the wait. A refetch, a revalidation or a router refresh after the await is the same wait wearing a different name. Mark the line `enigma:allow-server-first` when the response is genuinely required before the UI may change (a payment, a server-assigned identifier, an irreversible action) (frontend-policy).",
|
|
2270
|
+
// Warn, not block: waiting is a defect for a save, a toggle or a delete and correct for
|
|
2271
|
+
// a payment, and nothing in the file separates them. The sibling rule blocks because it
|
|
2272
|
+
// has evidence; this one only has an absence, so it says its piece and gets out of the way.
|
|
2273
|
+
severity: "warn",
|
|
2274
|
+
skill: "frontend-policy"
|
|
2275
|
+
},
|
|
2240
2276
|
{
|
|
2241
2277
|
id: "fe-textarea-size-bounds",
|
|
2242
2278
|
label: "Textarea declares a minimum and a maximum size",
|
|
@@ -3496,6 +3532,7 @@ var init_guardrails = __esm({
|
|
|
3496
3532
|
FILE_CHECKS = {
|
|
3497
3533
|
"proc-windows-hide": (content) => missingWindowsHide(content),
|
|
3498
3534
|
"fe-server-first-mutation": (content) => serverFirstMutation(content),
|
|
3535
|
+
"fe-mutation-without-optimistic": (content) => mutationWithoutOptimisticUpdate(content),
|
|
3499
3536
|
"fe-textarea-size-bounds": (content) => textareaSizeBounds(content),
|
|
3500
3537
|
"fe-view-blanked-while-loading": (content) => viewBlankedWhileLoading(content),
|
|
3501
3538
|
"fe-truncated-value-unreachable": (content) => truncatedValueUnreachable(content),
|
|
@@ -3545,6 +3582,7 @@ var init_guardrails = __esm({
|
|
|
3545
3582
|
RESULT_BINDING = /(?:const|let|var)\s+(\w+)\s*=\s*(?:await\s+)?/;
|
|
3546
3583
|
BLOCK_LOOKBACK = 120;
|
|
3547
3584
|
BLOCK_LOOKAHEAD = 200;
|
|
3585
|
+
REVALIDATE_AFTER = /\brouter\.refresh\s*\(|\brevalidatePath\s*\(|\brevalidateTag\s*\(|\binvalidateQueries\s*\(|\binvalidateAll\s*\(|\brefetch\s*\(|\bmutate\s*\(\s*[)"'`]/;
|
|
3548
3586
|
TEXTAREA = /<textarea\b/;
|
|
3549
3587
|
TEXTAREA_LOWER = /\brows\s*=|\brows:\s*\d|min-h-|min-height|minHeight|\bh-\[|\bh-\d|height\s*:\s*\d/;
|
|
3550
3588
|
TEXTAREA_UPPER = /max-h-|max-height|maxHeight/;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "enigma-cli",
|
|
3
|
-
"version": "1.46.
|
|
3
|
+
"version": "1.46.4",
|
|
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": {
|