enigma-cli 1.30.4 → 1.30.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/assets/memory/AGENTS.md +18 -4
  2. package/assets/memory/CLAUDE.md +18 -4
  3. package/assets/skills/anti-overengineering-policy/skill.json +1 -1
  4. package/assets/skills/anti-overengineering-review/skill.json +1 -1
  5. package/assets/skills/backend-policy/skill.json +1 -1
  6. package/assets/skills/ciphera-style-policy/skill.json +1 -1
  7. package/assets/skills/code-review-policy/skill.json +1 -1
  8. package/assets/skills/core-engineering-policy/skill.json +1 -1
  9. package/assets/skills/database-expert/skill.json +1 -1
  10. package/assets/skills/debugging-policy/skill.json +1 -1
  11. package/assets/skills/dependency-policy/skill.json +1 -1
  12. package/assets/skills/frontend-design/skill.json +1 -1
  13. package/assets/skills/frontend-policy/SKILL.md +20 -0
  14. package/assets/skills/frontend-policy/skill.json +4 -4
  15. package/assets/skills/git-policy/skill.json +1 -1
  16. package/assets/skills/logo-sourcing-policy/skill.json +1 -1
  17. package/assets/skills/security-policy/skill.json +1 -1
  18. package/assets/skills/skill-creator/skill.json +1 -1
  19. package/assets/skills/task-completion-policy/SKILL.md +1 -0
  20. package/assets/skills/task-completion-policy/skill.json +4 -4
  21. package/assets/skills/technical-writing-policy/skill.json +1 -1
  22. package/assets/skills/testing-policy/skill.json +1 -1
  23. package/assets/skills/validation-policy/SKILL.md +1 -1
  24. package/assets/skills/validation-policy/skill.json +4 -4
  25. package/bin/checksums.json +4 -4
  26. package/bin/enigma.mjs +1 -0
  27. package/dist/guardrails.js +305 -0
  28. package/package.json +3 -2
@@ -31,12 +31,26 @@
31
31
  - End files with exactly one trailing newline and no trailing whitespace.
32
32
  - When editing existing code, match its established style instead of imposing a different one.
33
33
 
34
+ ### Engineering Defaults (Always-On)
35
+
36
+ Non-negotiable, language-agnostic defaults - apply them by default without being asked, using the stack's idiomatic tool. They restate the cores of validation-policy, backend-policy and frontend-policy so they hold even when a skill does not load.
37
+
38
+ - Validate EVERY external input (request body, query, params, event payload, form field, CLI arg, webhook/message) against an explicit schema before use - Zod (TS/JS), Pydantic (Python), the language's equivalent elsewhere. Never consume an unvalidated shape or leave it open-ended. When the input is a tagged/event union, validate the discriminant AND that specific variant's body, with the expected fields typed.
39
+ - Frontend forms: validate in real time against the same schema, and use optimistic UI with rollback on failure for user-facing mutations.
40
+ - When a value must be unique within a set the client already holds (a list of names, slugs, emails, tags the user just rendered or is editing), check uniqueness/availability in real time against that loaded data on every change and block the conflicting submit BEFORE any server call - instant feedback for the user, no wasted round-trip or DB query. Apply this whenever duplicates are disallowed (unique names, one-per-parent, "already in use"); mirror the server's exact rule (trim, case-fold, scope, reserved values) and exclude the record's own current value when editing so renaming to the same value is not flagged. The backend still re-validates as the authority (client data can be stale).
41
+ - Cache reads on the client (localStorage/sessionStorage, or the data layer's cache) with a short TTL (~30s or more) to avoid redundant queries and survive rate limits; invalidate on write.
42
+ - Build reusable, composable components instead of duplicating UI - e.g. a single Input that renders a show/hide toggle when the type is password. Reuse before writing new.
43
+ - Never use the browser's native `alert`/`confirm`/`prompt` - use a dialog/modal component that matches the page design.
44
+ - Display dates/timestamps as localized, auto-updating values - never hand-rolled `toLocaleString`/"X ago" math scattered across components, nor a heavy date library just to format one. On the web use `<relative-time>` (`@github/relative-time-element`): `<relative-time datetime="<ISO-8601>">fallback</relative-time>` renders relative phrasing that updates itself, localizes to the user's timezone/locale via `Intl`, is accessible, and SSR-caches with a graceful no-JS fallback (the slotted text); switch relative vs. absolute with `format`/`tense`/`precision`/`threshold`. Keep raw ISO-8601/UTC in your data; localize only at render. Off the web, centralize formatting in one shared `Intl`-based helper with the same "store UTC, localize at render" rule.
45
+
34
46
  ### Task Execution (Always-On)
35
47
 
36
- - For long or complex tasks - or any task you judge to warrant it - break the work into smaller, well-scoped subtasks and complete them incrementally, validating each subtask before moving to the next.
37
- - Map the dependencies between subtasks before starting, and do only the decomposition the task genuinely needs - never over-decompose simple work.
38
- - For multi-item work (ports, migrations, batch changes), enumerate the FULL inventory of work units with deterministic commands before implementing, persist it as a checklist (file or todo system), and mark a unit done only after verifying it - never because a similar unit worked.
39
- - Never declare a task complete while any unit is pending, stubbed, or unverified. Before saying "done": reconcile counts against the inventory, build/typecheck the whole artifact, and grep for TODO/stub markers you introduced. If anything remains, say exactly what remains instead of rounding up to "done". Never silently skip or stub an item - record it with a reason and report it.
48
+ - A message that bundles several asks, questions, or items is a MULTI-PART task - even if it is just two, three, or four things. Before doing anything, extract EVERY distinct ask into an explicit list (the runtime's todo system when it has one, else a written checklist) and treat the request as unfinished until every item on that list is addressed. Never answer the first ask and drop, summarize away, or postpone the rest. When you present a plan, execute the whole plan - do not stop after listing it.
49
+ - For long or complex tasks - or any task you judge to warrant it - break the work into smaller, well-scoped subtasks and complete them incrementally, validating each subtask before moving to the next. Map the dependencies between subtasks first, and do only the decomposition the task genuinely needs - never over-decompose simple work.
50
+ - For multi-item work (ports, migrations, batch changes), enumerate the FULL inventory of work units with deterministic commands before implementing, persist it as a checklist (file or todo system), and mark a unit done only after verifying it - never because a similar unit worked. This is the task-completion-policy skill; load it for any task that spans many files/items or bundles several asks.
51
+ - "Pending", "pendiente", "TODO", "left as a follow-up", "next step: ...", or "you can do X yourself" is NOT an acceptable way to end a turn for work you are able to perform now. Do that work in this same turn. The only reasons to stop short are a genuine blocker - missing credentials or access, an irreversible or destructive choice, a business decision, or something the user explicitly approved deferring - and then you must name the blocker explicitly, never leave the item silently unfinished.
52
+ - Do not stop early because a task is long, tedious, or the context is filling up. Keep going until every enumerated item is finished or truly blocked. If work is genuinely paused, the checklist holds the remaining items - on resume, re-read it FIRST and continue from it; never reconstruct progress from memory, that is where items get dropped.
53
+ - Never declare a task complete while any item is pending, stubbed, or unverified. Before saying "done": reconcile against the checklist, build/typecheck the whole artifact, and grep for TODO/stub markers you introduced. If anything remains, say exactly what remains instead of rounding up to "done". Never silently skip or stub an item - record it with a reason and report it.
40
54
  - Never offload doable work to the user: "you can adjust/refresh X yourself" in a final report is a hidden deferral. If you can execute the action, do it before reporting; hand off only what genuinely requires the user (credentials, irreversible/destructive choices, business decisions) or what they explicitly approved deferring.
41
55
 
42
56
  <!-- enigma:parallel-subagents:start -->
@@ -31,12 +31,26 @@
31
31
  - End files with exactly one trailing newline and no trailing whitespace.
32
32
  - When editing existing code, match its established style instead of imposing a different one.
33
33
 
34
+ ### Engineering Defaults (Always-On)
35
+
36
+ Non-negotiable, language-agnostic defaults - apply them by default without being asked, using the stack's idiomatic tool. They restate the cores of validation-policy, backend-policy and frontend-policy so they hold even when a skill does not load.
37
+
38
+ - Validate EVERY external input (request body, query, params, event payload, form field, CLI arg, webhook/message) against an explicit schema before use - Zod (TS/JS), Pydantic (Python), the language's equivalent elsewhere. Never consume an unvalidated shape or leave it open-ended. When the input is a tagged/event union, validate the discriminant AND that specific variant's body, with the expected fields typed.
39
+ - Frontend forms: validate in real time against the same schema, and use optimistic UI with rollback on failure for user-facing mutations.
40
+ - When a value must be unique within a set the client already holds (a list of names, slugs, emails, tags the user just rendered or is editing), check uniqueness/availability in real time against that loaded data on every change and block the conflicting submit BEFORE any server call - instant feedback for the user, no wasted round-trip or DB query. Apply this whenever duplicates are disallowed (unique names, one-per-parent, "already in use"); mirror the server's exact rule (trim, case-fold, scope, reserved values) and exclude the record's own current value when editing so renaming to the same value is not flagged. The backend still re-validates as the authority (client data can be stale).
41
+ - Cache reads on the client (localStorage/sessionStorage, or the data layer's cache) with a short TTL (~30s or more) to avoid redundant queries and survive rate limits; invalidate on write.
42
+ - Build reusable, composable components instead of duplicating UI - e.g. a single Input that renders a show/hide toggle when the type is password. Reuse before writing new.
43
+ - Never use the browser's native `alert`/`confirm`/`prompt` - use a dialog/modal component that matches the page design.
44
+ - Display dates/timestamps as localized, auto-updating values - never hand-rolled `toLocaleString`/"X ago" math scattered across components, nor a heavy date library just to format one. On the web use `<relative-time>` (`@github/relative-time-element`): `<relative-time datetime="<ISO-8601>">fallback</relative-time>` renders relative phrasing that updates itself, localizes to the user's timezone/locale via `Intl`, is accessible, and SSR-caches with a graceful no-JS fallback (the slotted text); switch relative vs. absolute with `format`/`tense`/`precision`/`threshold`. Keep raw ISO-8601/UTC in your data; localize only at render. Off the web, centralize formatting in one shared `Intl`-based helper with the same "store UTC, localize at render" rule.
45
+
34
46
  ### Task Execution (Always-On)
35
47
 
36
- - For long or complex tasks - or any task you judge to warrant it - break the work into smaller, well-scoped subtasks and complete them incrementally, validating each subtask before moving to the next.
37
- - Map the dependencies between subtasks before starting, and do only the decomposition the task genuinely needs - never over-decompose simple work.
38
- - For multi-item work (ports, migrations, batch changes), enumerate the FULL inventory of work units with deterministic commands before implementing, persist it as a checklist (file or todo system), and mark a unit done only after verifying it - never because a similar unit worked.
39
- - Never declare a task complete while any unit is pending, stubbed, or unverified. Before saying "done": reconcile counts against the inventory, build/typecheck the whole artifact, and grep for TODO/stub markers you introduced. If anything remains, say exactly what remains instead of rounding up to "done". Never silently skip or stub an item - record it with a reason and report it.
48
+ - A message that bundles several asks, questions, or items is a MULTI-PART task - even if it is just two, three, or four things. Before doing anything, extract EVERY distinct ask into an explicit list (the runtime's todo system when it has one, else a written checklist) and treat the request as unfinished until every item on that list is addressed. Never answer the first ask and drop, summarize away, or postpone the rest. When you present a plan, execute the whole plan - do not stop after listing it.
49
+ - For long or complex tasks - or any task you judge to warrant it - break the work into smaller, well-scoped subtasks and complete them incrementally, validating each subtask before moving to the next. Map the dependencies between subtasks first, and do only the decomposition the task genuinely needs - never over-decompose simple work.
50
+ - For multi-item work (ports, migrations, batch changes), enumerate the FULL inventory of work units with deterministic commands before implementing, persist it as a checklist (file or todo system), and mark a unit done only after verifying it - never because a similar unit worked. This is the task-completion-policy skill; load it for any task that spans many files/items or bundles several asks.
51
+ - "Pending", "pendiente", "TODO", "left as a follow-up", "next step: ...", or "you can do X yourself" is NOT an acceptable way to end a turn for work you are able to perform now. Do that work in this same turn. The only reasons to stop short are a genuine blocker - missing credentials or access, an irreversible or destructive choice, a business decision, or something the user explicitly approved deferring - and then you must name the blocker explicitly, never leave the item silently unfinished.
52
+ - Do not stop early because a task is long, tedious, or the context is filling up. Keep going until every enumerated item is finished or truly blocked. If work is genuinely paused, the checklist holds the remaining items - on resume, re-read it FIRST and continue from it; never reconstruct progress from memory, that is where items get dropped.
53
+ - Never declare a task complete while any item is pending, stubbed, or unverified. Before saying "done": reconcile against the checklist, build/typecheck the whole artifact, and grep for TODO/stub markers you introduced. If anything remains, say exactly what remains instead of rounding up to "done". Never silently skip or stub an item - record it with a reason and report it.
40
54
  - Never offload doable work to the user: "you can adjust/refresh X yourself" in a final report is a hidden deferral. If you can execute the action, do it before reporting; hand off only what genuinely requires the user (credentials, irreversible/destructive choices, business decisions) or what they explicitly approved deferring.
41
55
 
42
56
  <!-- enigma:parallel-subagents:start -->
@@ -7,6 +7,6 @@
7
7
  "minimalCode"
8
8
  ],
9
9
  "updated": "2026-07-17T00:17:02+02:00",
10
- "cliVersion": "1.30.4",
10
+ "cliVersion": "1.30.6",
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.30.4",
7
+ "cliVersion": "1.30.6",
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, API and request optimization, server-side caching (Redis), and Zod boundary validation.",
6
6
  "updated": "2026-06-16T12:06:06+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "a46c3cd00aa5f47adb1e7907f1d2bc6f5562f7a272890dee9b1121976ac04ae1"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Ciphera code style conventions (formatting, naming, imports, comments, code-level anti-patterns; TypeScript-first, language-agnostic).",
6
6
  "updated": "2026-06-26T13:40:52+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "dc9ceb784004b05a0117c464e4bed05946835a04acea282586c0b735ee7c2284"
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-06-01T00:45:28+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "3d3bbe0602d5bbb4afe37648fe3c2fa39376b1bcbac5d8c441f01fad1e866ed0"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Core engineering execution policy and harness orchestration (highest-authority rules).",
6
6
  "updated": "2026-07-16T22:43:53+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "d132f20db08806054d95e58d3d56c2aa7ebc8e6dd24d902b0a0ed9ddfae216c5"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Senior database architecture policy: query optimization, anti-duplication/normalization, scalability, and RGPD/GDPR encryption.",
6
6
  "updated": "2026-06-03T14:19:50+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "2883bcecb3202683ae6f81b073c3d6a9cec9c55029e011bdd06ba7ac3537297e"
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-06-01T00:45:28+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "14b0064c8b33a0dc85e51464b05005cf5801c756b1101789a6924b9548420f6b"
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.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "6375d835c2aef2c9bd31ce116444dc3d796f510f9970a213aa3ac4696d7e21b9"
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-06-25T15:51:43-04:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "fb78be3233bf9caa67d1f522c19831542b23f47e440b02211b5a950c898318b9"
9
9
  }
@@ -97,6 +97,16 @@ Decide per case which mode fits; when in doubt for simple single-user forms and
97
97
 
98
98
  ---
99
99
 
100
+ ## Real-Time Uniqueness Against Loaded Data
101
+
102
+ When the user edits a value that must be unique within a set the client already holds in memory (the list of names, slugs, tags, emails it just rendered), validate uniqueness against that loaded data on every change instead of waiting for a server round-trip to report "already taken". The set is already loaded - reuse it (per Client-Side Caching): the user gets instant inline feedback as they type, and a redundant request (and its downstream DB query) is skipped. This is a default to apply without being asked, not a feature to wait for the user to request.
103
+
104
+ - Apply whenever duplicates are disallowed (unique names, slugs, one-per-parent constraints, "already in use", reserved values). Skip it when repeats are legitimate - never gate a value the model has no basis to treat as unique.
105
+ - Check on every change/blur and block submission while a conflict stands; surface the conflict inline, not only on submit.
106
+ - The cross-record rule itself - mirror the server's exact check (trim, case-fold, scope, reserved values), exclude the edited record's own value, and keep the server as the authority since client data can be stale - is owned by validation-policy. This client check is a UX and request-saving accelerator, never the sole validation layer.
107
+
108
+ ---
109
+
100
110
  ## Client-Side Caching (Reduce Server Load)
101
111
 
102
112
  Cache on the client to avoid redundant server round-trips and to keep the app usable under rate limits. The goal is to reach the backend (and therefore Redis/DB) as rarely as correctness allows.
@@ -168,6 +178,16 @@ Never render an unbounded or large dataset in one shot (no fetch-everything then
168
178
 
169
179
  ---
170
180
 
181
+ ## Dates & Timestamps
182
+
183
+ Render dates and timestamps as localized, auto-updating values. Do not hand-roll formatting (`new Date().toLocaleString`, ad-hoc "X ago" math) scattered across components, and do not pull in a heavy date library just to display a time.
184
+
185
+ - On the web, use the `<relative-time>` element (`@github/relative-time-element`, MIT, dependency-light): `<relative-time datetime="<ISO-8601>">fallback text</relative-time>`. It renders relative phrasing that updates itself ("3 minutes ago" -> "4 minutes ago"), localizes to the user's timezone and locale via `Intl`, is accessible, and works server-rendered with a graceful no-JS fallback (the slotted text is what the server caches). Switch relative vs. absolute with `format`, `tense`, `precision`, and `threshold`, and style it through `::part(root)`.
186
+ - Keep the raw ISO-8601 / UTC value in data and state; localize only at the render boundary. Never store or compare pre-formatted date strings.
187
+ - Where a custom element is not available (React Native, non-web surfaces), centralize formatting in one shared helper built on the platform `Intl` APIs rather than repeating format calls, and keep the same "store UTC, localize at render" rule.
188
+
189
+ ---
190
+
171
191
  ## Accessibility & Resilience
172
192
 
173
193
  - Use semantic markup and accessible interactive elements by default.
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "frontend-policy",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Frontend architecture: reusable components, abstraction thresholds, state management, no-op save detection, large-list rendering (infinite scroll/pagination, virtualization, skeletons, progressive loading), and optimistic UI with rollback.",
6
- "updated": "2026-06-26T13:57:02+02:00",
7
- "cliVersion": "1.30.4",
8
- "sha": "41cb938c9022efcdadc3056417cc7d442511adee40b09a141f11bb5fd9d56514"
6
+ "updated": "2026-07-22T00:21:54+02:00",
7
+ "cliVersion": "1.30.6",
8
+ "sha": "aac4a6fa8ef875ce968ef35613361c78d1935972bf224770ca286998f8f053e5"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Git & contribution policy (senior engineering standards).",
6
6
  "updated": "2026-07-16T22:44:02+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "e6dfbc33884000d9d25841bd9c5a84d6558ffd374882cb7b34451eb2cebc2161"
9
9
  }
@@ -7,6 +7,6 @@
7
7
  "logoColorPolicy"
8
8
  ],
9
9
  "updated": "2026-07-17T00:17:02+02:00",
10
- "cliVersion": "1.30.4",
10
+ "cliVersion": "1.30.6",
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), OWASP Top 10, transport/crypto baseline, secure logging, and agent/MCP/tool-use safety.",
6
6
  "updated": "2026-06-01T00:45:28+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "9971e9d9127397d0152e89d24aad3191e2935e55a8483db7fd15f5d4d7a60e7a"
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-06-16T16:39:13+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "699586cce82ec0a5458288b598ee7e5ebdddb3dfcf19db354d8bc5e85e47c1c7"
9
9
  }
@@ -8,6 +8,7 @@ description: Exhaustive completion discipline for long, complex, or multi-item t
8
8
  ## Activation Scope
9
9
 
10
10
  - Apply to any task with more than a handful of work units: 1:1 ports, language/framework migrations, repo-wide refactors, "implement all X", multi-feature builds, large integrations.
11
+ - Also apply when a single user message bundles multiple distinct asks or questions, even just two, three, or four of them: enumerate every ask up front and cover all of them. Answering the first and silently dropping, postponing, or leaving the rest "pending" is exactly the failure this policy exists to prevent.
11
12
  - Owns inventory, coverage tracking, and completion claims. Subtask decomposition lives in core-engineering-policy; per-change review lives in code-review-policy; test strategy lives in testing-policy.
12
13
 
13
14
  ---
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "task-completion-policy",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Exhaustive completion discipline for long/multi-item tasks - inventory, coverage ledger, verified done.",
6
- "updated": "2026-06-10T22:11:09+02:00",
7
- "cliVersion": "1.30.4",
8
- "sha": "6e3facba307eb2b55cefbab2e4b2a346a2b82f93c3ef47e11ebeb78c3c9453a8"
6
+ "updated": "2026-07-21T20:25:56+02:00",
7
+ "cliVersion": "1.30.6",
8
+ "sha": "feaf44a9b3ab9676c8ebd3c2a799f156d28419998f5a5355250fe95a28cd5a01"
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.",
6
6
  "updated": "2026-06-26T14:09:06+02:00",
7
- "cliVersion": "1.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "e750988b8de51d8a69be621673dceca5413ebad7e1a48e40acd18aad526d9928"
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.30.4",
7
+ "cliVersion": "1.30.6",
8
8
  "sha": "3bdf591057b760f674fb2b1425f63acb426cda2c4f042e1a74c5a5d3807df664"
9
9
  }
@@ -33,7 +33,7 @@ description: Strict frontend + backend schema validation (Zod or equivalent), sc
33
33
  - Use schema-driven validation (e.g. Zod or equivalent).
34
34
  - Validation must prevent invalid state before submission.
35
35
  - UI must reflect validation state immediately and clearly.
36
- - Validate cross-record constraints (uniqueness, availability, "already in use") in real time too, not just per-field type/format. When the client already holds the relevant set (the list of accounts, profiles, names, slugs it just rendered), check the input against that loaded data on every change and block submission on a conflict - do not defer the duplicate check to the server round-trip. The server still re-validates as the authority (client checks can be stale), but the user must see the conflict as they type. Mirror the server's exact rule (same pattern, case-folding, reserved values, and scope - e.g. unique per parent vs. globally) so the two never disagree; exclude the record's own current value when editing so renaming to the same name is not flagged.
36
+ - Validate cross-record constraints (uniqueness, availability, "already in use") in real time too, not just per-field type/format. When the client already holds the relevant set (the list of accounts, profiles, names, slugs it just rendered), check the input against that loaded data on every change and block submission on a conflict - do not defer the duplicate check to the server round-trip (this gives instant feedback and spares a redundant request and its DB query). The server still re-validates as the authority (client checks can be stale), but the user must see the conflict as they type. Mirror the server's exact rule (same pattern, case-folding, reserved values, and scope - e.g. unique per parent vs. globally) so the two never disagree; exclude the record's own current value when editing so renaming to the same name is not flagged.
37
37
 
38
38
  ### Backend / API Validation (Mandatory)
39
39
 
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "validation-policy",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Strict frontend + backend schema validation, schema consistency, and safe client-facing error handling.",
6
- "updated": "2026-06-25T18:21:50-04:00",
7
- "cliVersion": "1.30.4",
8
- "sha": "50349f5b3c55cc337f828dd510c163550614832acb28714871210a7c115a1501"
6
+ "updated": "2026-07-22T00:21:54+02:00",
7
+ "cliVersion": "1.30.6",
8
+ "sha": "24cda38efc700bed1fcdaca006834b8df6daf75ac5a8b75b00f68728471a26e1"
9
9
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "enigma-darwin-arm64": "cffde287e26a9e1c37f656180ac7fb51e4bba3f2163688678ceae4e7fe7eb6d1",
3
- "enigma-linux-arm64": "b2bf20035d7ed5fa8592e5bef50dd5b32c1c400d80636c38ccae547ce2eb05c8",
4
- "enigma-linux-x64": "66962b8368b820611a63e7275f2b9cdd468020aba658cde0f5b126833519be01",
5
- "enigma-win32-x64.exe": "0c44610724181b0246120134293eb1a2507d266a07b210ef58598504c4a2875f"
2
+ "enigma-darwin-arm64": "f9d771e9f5230cef78d37f428db4f9fd0d5eba50157665153656d5ea15753a0c",
3
+ "enigma-linux-arm64": "b823ffa1f2059dda5ff80559228268ecde03c69ff64240282cb4421caf635509",
4
+ "enigma-linux-x64": "08a0fdb81242947331fb98a2d7f361bdfdd19616fcc6233b830092dbd4444557",
5
+ "enigma-win32-x64.exe": "f59e9668e991f82eb1aeb00237c607a7a81df75fd8f57a3cf7d97c48e7ee70b0"
6
6
  }
package/bin/enigma.mjs CHANGED
@@ -78,6 +78,7 @@ try {
78
78
  const env = { ...process.env };
79
79
  env.ENIGMA_ASSETS_DIR = join(pkgRoot, "assets");
80
80
  env.ENIGMA_GUARD_PATH = join(pkgRoot, "dist", "guard.js");
81
+ env.ENIGMA_GUARDRAILS_PATH = join(pkgRoot, "dist", "guardrails.js");
81
82
  try {
82
83
  env.ENIGMA_VERSION = packageVersion();
83
84
  } catch { /* version is best-effort */ }
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/guardrails.ts
4
+ import { homedir } from "os";
5
+ import { fileURLToPath } from "url";
6
+ import { execFileSync } from "child_process";
7
+ import { readFileSync, statSync, existsSync } from "fs";
8
+ import { dirname, join, resolve } from "path";
9
+ var COMMENT_LINE = /^\s*(\/\/|#|\*|--|<!--|\{?\/\*)/;
10
+ var BUILTIN_RULES = [
11
+ {
12
+ id: "db-uuid-pk",
13
+ label: "UUID primary keys",
14
+ // Basename globs (no slash) match at any depth, including the repo root; the dir
15
+ // globs additionally catch non-standard extensions under those folders.
16
+ files: ["*.prisma", "*.sql", "*.entity.ts", "**/migrations/**", "**/entities/**", "**/models/**"],
17
+ scope: "file",
18
+ // Matches only explicit auto-increment identity signals across engines/ORMs.
19
+ // Deliberately does NOT match a plain `INTEGER PRIMARY KEY` (valid in many cases,
20
+ // and used on purpose by enigma's own recall SQLite store).
21
+ pattern: `\\b(?:BIG|SMALL)?SERIAL\\b|\\bAUTO_INCREMENT\\b|\\bIDENTITY\\s*\\(|\\bGENERATED\\s+(?:ALWAYS|BY\\s+DEFAULT)\\s+AS\\s+IDENTITY\\b|@default\\(autoincrement\\(\\)\\)|@PrimaryGeneratedColumn\\(\\s*(?:\\)|["']increment["'])`,
22
+ flags: "i",
23
+ message: "Use UUID primary keys, never auto-increment / SERIAL / IDENTITY / AUTO_INCREMENT (database-expert). Generate a UUID (prefer UUIDv7 or ULID) at the application layer or via a database uuid default.",
24
+ severity: "block",
25
+ skill: "database-expert"
26
+ },
27
+ {
28
+ id: "db-ts-orm-prisma",
29
+ label: "Prisma as the default ORM (TypeScript)",
30
+ files: ["package.json", "*.sql", "schema.ts", "ormconfig.*", "data-source.ts", "knexfile.*", "drizzle.config.*"],
31
+ scope: "project",
32
+ check: "ts-relational-no-prisma",
33
+ message: "This is a TypeScript project on a relational datastore without Prisma. Prefer Prisma as the default ORM for new TypeScript work (database-expert).",
34
+ severity: "warn",
35
+ skill: "database-expert"
36
+ },
37
+ {
38
+ id: "be-validate-input-ts",
39
+ label: "Validate request input (TypeScript)",
40
+ files: ["*.ts", "*.js", "*.mts", "*.cts"],
41
+ excludeFiles: ["*.test.*", "*.spec.*", "**/tests/**", "**/__tests__/**"],
42
+ scope: "file",
43
+ // Fires only on ASSIGNING the request BODY to a variable (where validation belongs) with
44
+ // no schema-validation signal in the file. Deliberately NOT req.query/req.params (single
45
+ // scalars, usually validated inline) and NOT a body passed as a bare arg (e.g. to a logger)
46
+ // - real-world scanning showed those are the false-positive sources. The absent set is BROAD
47
+ // (every common validator) so any validated file is skipped: precision over recall.
48
+ pattern: "=\\s*req\\.body\\b|=\\s*(await\\s+)?request\\.json\\(\\)|=\\s*ctx\\.request\\.body\\b|=\\s*await\\s+c\\.req\\.json\\(",
49
+ absent: "z\\.|\\.parse\\(|\\.safeParse\\(|\\.validate\\(|\\.assert\\(|valibot|\\byup\\b|\\bjoi\\b|\\bJoi\\b|\\bajv\\b|superstruct|typebox|@sinclair|arktype|io-ts|runtypes|@Body\\(|class-validator|express-validator|zodResolver|Type\\.Object|checkSchema|celebrate",
50
+ message: "Reads request input without validating it. Parse every input through a schema (Zod, or valibot/yup/...) - never trust an unvalidated shape. For a tagged/event union, validate the discriminant AND that variant's body (validation-policy, backend-policy).",
51
+ severity: "warn",
52
+ skill: "validation-policy"
53
+ },
54
+ {
55
+ id: "be-validate-input-py",
56
+ label: "Validate request input (Python)",
57
+ files: ["*.py"],
58
+ excludeFiles: ["test_*.py", "*_test.py", "conftest.py", "**/tests/**"],
59
+ scope: "file",
60
+ // Assigns the request body to a variable with no schema-validation signal. request.data is
61
+ // intentionally omitted (raw-bytes reads - webhook HMAC, proxying - are not schema surfaces).
62
+ // The absent set is broad to skip any validated file (Pydantic, marshmallow, Django forms, ...).
63
+ pattern: "=\\s*request\\.get_json\\(|=\\s*(await\\s+)?request\\.json\\b|=\\s*request\\.form\\b|=\\s*request\\.POST\\b",
64
+ absent: "BaseModel|pydantic|marshmallow|serializers|TypeAdapter|field_validator|@validator|model_validate|is_valid\\(|forms\\.|ModelForm|cerberus|voluptuous|jsonschema|@dataclass|\\.load\\(|Schema\\(",
65
+ message: "Reads the raw request body without a schema. Validate with Pydantic (BaseModel / model_validate) - or the stack's validator - and discriminate the payload by its type/event (validation-policy, backend-policy).",
66
+ severity: "warn",
67
+ skill: "validation-policy"
68
+ },
69
+ // NOTE: no Go/Rust input-validation rule. Go's manual validation (`if in.X == ""`) is
70
+ // idiomatic and has no detectable signature, and Rust's serde typed deserialization already
71
+ // enforces shape - a rule for either would false-positive. The generic "validate every input"
72
+ // principle for those languages lives in the always-on memory kernel instead.
73
+ {
74
+ id: "fe-password-input",
75
+ label: "Reusable password input (show/hide)",
76
+ files: ["*.tsx", "*.jsx"],
77
+ scope: "file",
78
+ // A raw lowercase <input type="password"> (not a component) with no show/hide toggle in the
79
+ // file. flags:"" = case-sensitive so a capitalized <Input> component is NOT matched; a
80
+ // literal type="password" only, so a dynamic type={visible?...} toggle is not matched either.
81
+ pattern: `<input\\b[^>]*type=["']password["']`,
82
+ flags: "",
83
+ absent: "showPassword|setShowPassword|togglePassword|revealPassword|passwordVisible|isPasswordVisible|showPw|hidePassword",
84
+ message: 'Raw <input type="password">: use the shared reusable Input component (which renders a show/hide toggle for passwords) instead of a bare input, or add the toggle (frontend-policy).',
85
+ severity: "warn",
86
+ skill: "frontend-policy"
87
+ },
88
+ {
89
+ id: "fe-no-native-dialog",
90
+ label: "No native browser dialogs",
91
+ files: ["*.tsx", "*.jsx", "*.ts", "*.js", "*.mts", "*.cts", "*.vue", "*.svelte", "*.astro"],
92
+ excludeFiles: ["*.test.*", "*.spec.*", "**/tests/**", "**/__tests__/**"],
93
+ scope: "file",
94
+ // window.(alert|confirm|prompt)( is unambiguously the native dialog (window is browser-only,
95
+ // so no false positive in a Node file). Bare alert/confirm/prompt is matched ONLY with a
96
+ // string-literal arg - native dialogs take a string, while CLI libs (clack/inquirer) and
97
+ // custom design-system dialogs take a {config} object, and an AI `prompt` value is a string
98
+ // that is passed, not called with a string literal. flags:"" is case-sensitive so a
99
+ // capitalized custom <Alert>/Confirm() is not matched; (?<![.\w]) excludes method calls.
100
+ pattern: `\\bwindow\\.(alert|confirm|prompt)\\s*\\(|(?<![.\\w])(alert|confirm|prompt)\\s*\\(\\s*["']`,
101
+ flags: "",
102
+ message: "Native browser dialog (alert/confirm/prompt) - use a dialog/modal component that matches the page design instead of the browser's built-in (frontend-policy).",
103
+ severity: "warn",
104
+ skill: "frontend-policy"
105
+ }
106
+ ];
107
+ var PROJECT_CHECKS = {
108
+ "ts-relational-no-prisma": (root) => {
109
+ const pkg = readPkgDeps(root);
110
+ if (!pkg) return false;
111
+ const hasTs = "typescript" in pkg || existsSync(join(root, "tsconfig.json"));
112
+ if (!hasTs) return false;
113
+ const relational = ["typeorm", "sequelize", "knex", "drizzle-orm", "pg", "mysql", "mysql2", "better-sqlite3", "@mikro-orm/core"];
114
+ if (!relational.some((d) => d in pkg)) return false;
115
+ return !("prisma" in pkg || "@prisma/client" in pkg);
116
+ }
117
+ };
118
+ function readPkgDeps(root) {
119
+ try {
120
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
121
+ return { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.optionalDependencies, ...pkg.peerDependencies };
122
+ } catch {
123
+ return null;
124
+ }
125
+ }
126
+ function globToRegExp(glob) {
127
+ const esc = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&");
128
+ const body = esc.replace(/\*\*/g, " ").replace(/\*/g, "[^/]*").replace(/ /g, ".*").replace(/\?/g, "[^/]");
129
+ return new RegExp(glob.includes("/") ? `^${body}$` : `(^|/)${body}$`);
130
+ }
131
+ function guardrailsConfigPath() {
132
+ return process.env.ENIGMA_GUARDRAILS_CONFIG || join(homedir(), ".enigma-guardrails.json");
133
+ }
134
+ function isValidRule(r) {
135
+ const x = r;
136
+ if (!x || typeof x.id !== "string" || !Array.isArray(x.files) || typeof x.message !== "string") return false;
137
+ if (x.severity !== "block" && x.severity !== "warn") return false;
138
+ if (x.scope === "file") return typeof x.pattern === "string";
139
+ if (x.scope === "project") return typeof x.check === "string";
140
+ return false;
141
+ }
142
+ function loadRules() {
143
+ let disabled = [];
144
+ let custom = [];
145
+ try {
146
+ const raw = JSON.parse(readFileSync(guardrailsConfigPath(), "utf8"));
147
+ if (Array.isArray(raw.disabled)) disabled = raw.disabled.filter((s) => typeof s === "string");
148
+ if (Array.isArray(raw.rules)) custom = raw.rules.filter(isValidRule);
149
+ } catch {
150
+ }
151
+ const off = new Set(disabled);
152
+ return [...BUILTIN_RULES.filter((r) => !off.has(r.id)), ...custom];
153
+ }
154
+ function findProjectRoot(file) {
155
+ let dir = dirname(resolve(file));
156
+ for (let i = 0; i < 40; i++) {
157
+ const isProj = existsSync(join(dir, "package.json")) || existsSync(join(dir, ".enigma.json"));
158
+ let hasGit = false;
159
+ try {
160
+ hasGit = statSync(join(dir, ".git")).isDirectory();
161
+ } catch {
162
+ }
163
+ if (isProj || hasGit) return dir;
164
+ const parent = dirname(dir);
165
+ if (parent === dir) break;
166
+ dir = parent;
167
+ }
168
+ return null;
169
+ }
170
+ function checkFile(file, content, projectRoot) {
171
+ const norm = file.replace(/\\/g, "/");
172
+ const out = [];
173
+ for (const rule of loadRules()) {
174
+ if (!rule.files.some((g) => globToRegExp(g).test(norm))) continue;
175
+ if (rule.excludeFiles?.some((g) => globToRegExp(g).test(norm))) continue;
176
+ const base = { ruleId: rule.id, severity: rule.severity, file: norm, message: rule.message, skill: rule.skill };
177
+ if (rule.scope === "file" && rule.pattern) {
178
+ if (rule.absent) {
179
+ try {
180
+ if (new RegExp(rule.absent, "i").test(content)) continue;
181
+ } catch {
182
+ }
183
+ }
184
+ let re;
185
+ try {
186
+ re = new RegExp(rule.pattern, (rule.flags ?? "i").replace(/g/g, ""));
187
+ } catch {
188
+ continue;
189
+ }
190
+ const lines = content.split("\n");
191
+ for (let i = 0; i < lines.length; i++) {
192
+ if (COMMENT_LINE.test(lines[i])) continue;
193
+ if (re.test(lines[i])) out.push({ ...base, line: i + 1 });
194
+ }
195
+ } else if (rule.scope === "project" && rule.check && projectRoot) {
196
+ const check = PROJECT_CHECKS[rule.check];
197
+ if (check && check(projectRoot)) out.push({ ...base });
198
+ }
199
+ }
200
+ return out;
201
+ }
202
+ function formatFindings(findings) {
203
+ return findings.map((f) => {
204
+ const tag = f.severity === "block" ? "MUST FIX" : "SUGGESTED";
205
+ const loc = f.line ? `:${f.line}` : "";
206
+ const skill = f.skill ? ` [${f.skill}]` : "";
207
+ return `${tag} ${f.file}${loc} (${f.ruleId})${skill}: ${f.message}`;
208
+ }).join("\n");
209
+ }
210
+ function checkPath(file) {
211
+ let content;
212
+ try {
213
+ content = readFileSync(file, "utf8");
214
+ } catch {
215
+ return [];
216
+ }
217
+ if (content.includes("\0")) return [];
218
+ return checkFile(file, content, findProjectRoot(file));
219
+ }
220
+ function runGuardrailsHook(payload) {
221
+ let file;
222
+ try {
223
+ file = JSON.parse(payload ?? readFileSync(0, "utf8"))?.tool_input?.file_path;
224
+ } catch {
225
+ }
226
+ if (!file || typeof file !== "string") return 0;
227
+ const findings = checkPath(file);
228
+ if (!findings.length) return 0;
229
+ const warns = findings.filter((f) => f.severity === "warn");
230
+ const blocks = findings.filter((f) => f.severity === "block");
231
+ if (warns.length) process.stdout.write(`enigma guardrails (suggestions)
232
+ ${formatFindings(warns)}
233
+ `);
234
+ if (blocks.length) {
235
+ process.stderr.write(`enigma guardrails
236
+ ${formatFindings(blocks)}
237
+ Fix the above before continuing.
238
+ `);
239
+ return 2;
240
+ }
241
+ return 0;
242
+ }
243
+ function gitFiles(all) {
244
+ const out = execFileSync("git", all ? ["ls-files"] : ["diff", "--cached", "--name-only", "--diff-filter=ACM"], { encoding: "utf8" });
245
+ return out.split("\n").map((s) => s.trim()).filter(Boolean);
246
+ }
247
+ function runGuardrailsScan(all) {
248
+ let files;
249
+ try {
250
+ files = gitFiles(all);
251
+ } catch {
252
+ return { ok: true, blocks: [], warns: [], count: 0, notRepo: true };
253
+ }
254
+ const root = process.cwd();
255
+ const blocks = [];
256
+ const warns = [];
257
+ for (const file of files) {
258
+ let content;
259
+ try {
260
+ content = readFileSync(file, "utf8");
261
+ } catch {
262
+ continue;
263
+ }
264
+ if (content.includes("\0")) continue;
265
+ for (const f of checkFile(file, content, root)) (f.severity === "block" ? blocks : warns).push(f);
266
+ }
267
+ return { ok: blocks.length === 0, blocks, warns, count: files.length };
268
+ }
269
+ function runGuardrailsScanCli(all) {
270
+ const r = runGuardrailsScan(all);
271
+ if (r.notRepo) {
272
+ console.error("enigma-guardrails: not a git repository; nothing to check.");
273
+ return 0;
274
+ }
275
+ if (r.warns.length) {
276
+ console.error(`enigma-guardrails: ${r.warns.length} suggestion(s):`);
277
+ for (const w of r.warns) console.error(` ! ${formatFindings([w])}`);
278
+ }
279
+ if (r.blocks.length) {
280
+ console.error(`
281
+ enigma-guardrails: BLOCKED - ${r.blocks.length} convention violation(s):`);
282
+ for (const b of r.blocks) console.error(` x ${formatFindings([b])}`);
283
+ console.error("\nTo bypass intentionally for one commit: git commit --no-verify");
284
+ return 1;
285
+ }
286
+ console.log(`enigma-guardrails: ${r.count} ${all ? "tracked" : "staged"} file(s) checked, no blocking violations.`);
287
+ return 0;
288
+ }
289
+ var grEntry = process.argv[1] ?? "";
290
+ var isGrEntry = /(^|[\\/])guardrails\.[mc]?[jt]s$/.test(grEntry);
291
+ if (isGrEntry && fileURLToPath(import.meta.url) === grEntry) {
292
+ process.exit(runGuardrailsScanCli(process.argv.includes("--all")));
293
+ }
294
+ export {
295
+ BUILTIN_RULES,
296
+ PROJECT_CHECKS,
297
+ checkFile,
298
+ checkPath,
299
+ findProjectRoot,
300
+ formatFindings,
301
+ loadRules,
302
+ runGuardrailsHook,
303
+ runGuardrailsScan,
304
+ runGuardrailsScanCli
305
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enigma-cli",
3
- "version": "1.30.4",
3
+ "version": "1.30.6",
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": {
@@ -17,7 +17,8 @@
17
17
  "seal": "tsx src/bin/enigma.ts seal",
18
18
  "check": "tsx src/bin/enigma.ts check",
19
19
  "guard": "tsx src/guard.ts --all",
20
- "verify": "npm run typecheck && npm run check && npm run guard",
20
+ "guardrails": "tsx src/guardrails.ts --all",
21
+ "verify": "npm run typecheck && npm run check && npm run guard && npm run guardrails",
21
22
  "prepublishOnly": "npm run verify && npm run build",
22
23
  "postinstall": "node bin/postinstall.mjs"
23
24
  },