enigma-cli 1.32.2 → 1.32.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.
Files changed (29) hide show
  1. package/assets/commands/gate.md +40 -7
  2. package/assets/memory/AGENTS.md +4 -2
  3. package/assets/memory/CLAUDE.md +4 -2
  4. package/assets/skills/anti-overengineering-policy/skill.json +1 -1
  5. package/assets/skills/anti-overengineering-review/skill.json +1 -1
  6. package/assets/skills/backend-policy/skill.json +1 -1
  7. package/assets/skills/ciphera-style-policy/skill.json +1 -1
  8. package/assets/skills/code-review-policy/skill.json +1 -1
  9. package/assets/skills/core-engineering-policy/skill.json +1 -1
  10. package/assets/skills/database-expert/skill.json +1 -1
  11. package/assets/skills/debugging-policy/skill.json +1 -1
  12. package/assets/skills/dependency-policy/skill.json +1 -1
  13. package/assets/skills/email-policy/skill.json +1 -1
  14. package/assets/skills/frontend-design/skill.json +1 -1
  15. package/assets/skills/frontend-policy/SKILL.md +24 -0
  16. package/assets/skills/frontend-policy/skill.json +3 -3
  17. package/assets/skills/git-policy/skill.json +1 -1
  18. package/assets/skills/logo-sourcing-policy/skill.json +1 -1
  19. package/assets/skills/security-policy/SKILL.md +37 -1
  20. package/assets/skills/security-policy/skill.json +3 -3
  21. package/assets/skills/skill-creator/skill.json +1 -1
  22. package/assets/skills/task-completion-policy/SKILL.md +3 -1
  23. package/assets/skills/task-completion-policy/skill.json +4 -4
  24. package/assets/skills/technical-writing-policy/skill.json +1 -1
  25. package/assets/skills/testing-policy/skill.json +1 -1
  26. package/assets/skills/validation-policy/skill.json +1 -1
  27. package/bin/checksums.json +4 -4
  28. package/dist/guardrails.js +265 -4
  29. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: Validate code changes through the enigma gate pipeline - automated review, tests, lint, docs, push, PR, and CI - before they reach the configured push target. Use when the user asks to run the gate, gate/ship/validate their changes, push safely, do a task and then validate it, or invokes /gate.
3
- argument-hint: [task] | (bare to gate already-committed work) | "skip the lint step"-style requests
2
+ description: Validate code changes through the enigma gate pipeline - automated review, tests, lint, docs, push, PR, and CI - before they reach the configured push target. Use when the user asks to run the gate, gate/ship/validate their changes, push safely, do a task and then validate it, or invokes /gate. Also the way to turn the gate on or off (/gate on, /gate off, /gate status) when the user asks to enable, disable, or stop it running automatically.
3
+ argument-hint: [task] | (bare to gate already-committed work) | on | off | status | "skip the lint step"-style requests
4
4
  ---
5
5
 
6
6
  # /gate
@@ -18,6 +18,33 @@ for something specific, translate it into the matching `axi run` flags yourself
18
18
  for example "skip the lint step" becomes `--skip=lint`. Run
19
19
  `enigma gate axi run --help` to see the flags.
20
20
 
21
+ ## Turning the gate on and off
22
+
23
+ `/gate on`, `/gate off` and `/gate status` are settings, not runs - handle them
24
+ first and stop there, without touching the pipeline.
25
+
26
+ | Invocation | Command to run |
27
+ | --- | --- |
28
+ | `/gate status` | `enigma config gate` (prints the value in force here) |
29
+ | `/gate off` | `enigma config gate off -l` |
30
+ | `/gate on` | `enigma config gate on -l` |
31
+ | `/gate off --global` (or "everywhere", "in every project") | `enigma config gate off -g` |
32
+ | `/gate on --global` | `enigma config gate on -g` |
33
+
34
+ Scope matters, so be explicit about it. Plain `/gate off` switches the gate off
35
+ for **this project only** by writing `gate: false` into its `.enigma.json` -
36
+ narrow and reversible, and it leaves every other repo alone. Only go global when
37
+ the user actually says so. Report which of the two you applied.
38
+
39
+ Toggling rewrites the agent memory file, which is read at startup: tell the user
40
+ the change takes effect in their **next** session. In this one, keep honouring
41
+ what they just asked for - if they turned the gate off, do not drive it again.
42
+
43
+ One asymmetry worth stating when it applies: a **global** off also removes this
44
+ command from the agent, so tell the user that turning it back on is
45
+ `enigma config gate on -g` from a terminal. A project-scoped off leaves `/gate`
46
+ in place, so `/gate on` works there.
47
+
21
48
  ## Two ways to invoke
22
49
 
23
50
  - **Validate-only** - bare `/gate` (optionally with flag-style requests like
@@ -27,15 +54,19 @@ for example "skip the lint step" becomes `--skip=lint`. Run
27
54
  carry out the task yourself, then validate:
28
55
  1. **Check scope.** Inspect `git status` before changing or committing anything.
29
56
  Preserve unrelated uncommitted changes; commit only what belongs to the task.
30
- 2. **Do the work**, then **commit it on a feature branch**. If the user is on
31
- the default branch, create a feature branch first - the gate validates
32
- committed history on a non-default branch.
57
+ 2. **Do the work**, then **commit it**. The gate validates committed history on
58
+ whatever branch the user is on - never switch or create a branch just to run
59
+ it.
33
60
  3. **Then validate**, passing the user's task as your `--intent` (the goal in
34
61
  their words), enriched with the decisions and tradeoffs you made.
35
62
 
36
63
  ## Before you start
37
64
 
38
- - The work must be **committed** on a **feature branch** (not the default branch).
65
+ - The work must be **committed**. Any branch qualifies, the default branch
66
+ included - there the pipeline opens no PR and its push lands directly on that
67
+ branch, which is the intended behavior. The only exception is a branch the user
68
+ listed in `gate-protected-branches`: `axi run` refuses it, and the answer is to
69
+ tell the user, not to move their work to another branch.
39
70
  - The repository must be initialized with `enigma gate init`.
40
71
 
41
72
  If any precondition fails, `axi run` returns an `error:` with the exact fix -
@@ -92,7 +123,9 @@ uses `--intent` to tell a deliberate choice from a mistake.
92
123
  driving. Tell the user the PR is ready and ask them to review and merge it
93
124
  (link in the `help` line). The gate keeps monitoring the PR in the
94
125
  background; do not poll for the merge.
95
- - `passed` - cleared the gate and the PR was merged or closed.
126
+ - `passed` - cleared the gate and the PR was merged or closed. On the default
127
+ branch this is the normal ending: no PR is opened and CI monitoring is
128
+ skipped, so the run finishes here once the push lands.
96
129
  - `failed` / `cancelled` - read the output, fix what it points at, commit on
97
130
  the same branch, and drive again (`axi run` for a fresh run, or
98
131
  `enigma gate rerun`). Do not leave the user at a failed outcome without
@@ -49,6 +49,7 @@ Non-negotiable, language-agnostic defaults - apply them by default without being
49
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
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
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
+ - Never end a turn asking permission to continue with work that was already asked for - "shall I continue with 5-8?", "¿sigo con las tareas 5-8 en este orden, o prefieres otro?", "do you want me to keep going?", "which should I do first?". The answer is always yes, so asking only costs the user a turn to say it: pick the most sensible order yourself and keep working until everything is done. Order, sequencing and priority among requested items are YOUR judgment calls, not the user's. Stop and ask only for a genuine blocker - access or credentials you lack, an irreversible or destructive action, a decision that is genuinely the user's (business, legal, cost) - and then NAME the blocker and what you finished before it, instead of asking whether to proceed. Resolve real ambiguity before starting, never as a way to pause mid-task. This one is enforced, not advisory: `enigma verify` denies the stop on a turn that ends by asking to continue.
52
53
  - 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
54
  - 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 run `enigma verify` - it checks what you actually produced for unfinished work and runs the project's verification command. For a port, clone, or migration also run `enigma verify parity <source> <target>`, which reports any module that was never carried over. 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.
54
55
  - Implement what was asked at the difficulty it actually has. Never quietly substitute a simplified stand-in because the real thing is tedious or hard - no regex where a real parser is required, no hardcoded special case where the general logic was asked for, no empty module, no "equivalent for now". If a faithful implementation is genuinely impossible here, say so explicitly and say why; downgrading it silently and then reporting success is the single worst outcome.
@@ -82,8 +83,9 @@ Non-negotiable, language-agnostic defaults - apply them by default without being
82
83
  <!-- enigma:gate:start -->
83
84
  ### AI Quality Gate (Automatic)
84
85
 
85
- - The AI quality gate is active. When you finish a code task on a feature branch (changes committed, non-default branch), drive the gate yourself before reporting the work done - the user should not have to ask for it or run any setup command.
86
- - Scope is per project: skip automatic driving when the repo's `.enigma.json` sets `gate: false`, when there is nothing committed to validate, or when you are on the default branch (commit the work to a feature branch first).
86
+ - The AI quality gate is active. When you finish a code task and the changes are committed, drive the gate yourself before reporting the work done - on WHATEVER branch the work is on, the default branch included. The user should not have to ask for it or run any setup command.
87
+ - Skip automatic driving only when: the user told you to skip it, the repo's `.enigma.json` sets `gate: false`, there is nothing committed to validate, or `axi run` refuses the branch as protected (`gate-protected-branches`) - then say so and leave the work unvalidated rather than switching branches on your own. To stop it for good the user runs `/gate off` (this project) or `enigma config gate off -g` (everywhere); offer that instead of quietly not running it.
88
+ - On the default branch the pipeline opens no PR and its push lands directly on that branch. That is the intended behavior there; report it as the outcome instead of waiting for a PR link.
87
89
  - If the repo is not initialized yet (`enigma gate axi` says so), run `enigma gate init` once yourself, then proceed. If `enigma gate` itself misbehaves, `enigma gate doctor` reports why.
88
90
  - Drive it through the `/gate` workflow (`enigma gate axi run --intent "<what the user set out to accomplish>"`): authorize `auto-fix` and `no-op` findings on your own judgment, but STOP and escalate every `ask-user` finding verbatim. Do NOT pass `--yes` and do NOT merge the PR yourself - on `checks-passed`, leave the PR ready and ask the user to review and merge it.
89
91
  - While a run is active never edit code to fix a finding; the pipeline owns the fixes (respond with `--action fix`). The full command reference lives in the gate skill / `/gate` command.
@@ -49,6 +49,7 @@ Non-negotiable, language-agnostic defaults - apply them by default without being
49
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
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
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
+ - Never end a turn asking permission to continue with work that was already asked for - "shall I continue with 5-8?", "¿sigo con las tareas 5-8 en este orden, o prefieres otro?", "do you want me to keep going?", "which should I do first?". The answer is always yes, so asking only costs the user a turn to say it: pick the most sensible order yourself and keep working until everything is done. Order, sequencing and priority among requested items are YOUR judgment calls, not the user's. Stop and ask only for a genuine blocker - access or credentials you lack, an irreversible or destructive action, a decision that is genuinely the user's (business, legal, cost) - and then NAME the blocker and what you finished before it, instead of asking whether to proceed. Resolve real ambiguity before starting, never as a way to pause mid-task. This one is enforced, not advisory: `enigma verify` denies the stop on a turn that ends by asking to continue.
52
53
  - 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
54
  - 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 run `enigma verify` - it checks what you actually produced for unfinished work and runs the project's verification command. For a port, clone, or migration also run `enigma verify parity <source> <target>`, which reports any module that was never carried over. 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.
54
55
  - Implement what was asked at the difficulty it actually has. Never quietly substitute a simplified stand-in because the real thing is tedious or hard - no regex where a real parser is required, no hardcoded special case where the general logic was asked for, no empty module, no "equivalent for now". If a faithful implementation is genuinely impossible here, say so explicitly and say why; downgrading it silently and then reporting success is the single worst outcome.
@@ -82,8 +83,9 @@ Non-negotiable, language-agnostic defaults - apply them by default without being
82
83
  <!-- enigma:gate:start -->
83
84
  ### AI Quality Gate (Automatic)
84
85
 
85
- - The AI quality gate is active. When you finish a code task on a feature branch (changes committed, non-default branch), drive the gate yourself before reporting the work done - the user should not have to ask for it or run any setup command.
86
- - Scope is per project: skip automatic driving when the repo's `.enigma.json` sets `gate: false`, when there is nothing committed to validate, or when you are on the default branch (commit the work to a feature branch first).
86
+ - The AI quality gate is active. When you finish a code task and the changes are committed, drive the gate yourself before reporting the work done - on WHATEVER branch the work is on, the default branch included. The user should not have to ask for it or run any setup command.
87
+ - Skip automatic driving only when: the user told you to skip it, the repo's `.enigma.json` sets `gate: false`, there is nothing committed to validate, or `axi run` refuses the branch as protected (`gate-protected-branches`) - then say so and leave the work unvalidated rather than switching branches on your own. To stop it for good the user runs `/gate off` (this project) or `enigma config gate off -g` (everywhere); offer that instead of quietly not running it.
88
+ - On the default branch the pipeline opens no PR and its push lands directly on that branch. That is the intended behavior there; report it as the outcome instead of waiting for a PR link.
87
89
  - If the repo is not initialized yet (`enigma gate axi` says so), run `enigma gate init` once yourself, then proceed. If `enigma gate` itself misbehaves, `enigma gate doctor` reports why.
88
90
  - Drive it through the `/gate` workflow (`enigma gate axi run --intent "<what the user set out to accomplish>"`): authorize `auto-fix` and `no-op` findings on your own judgment, but STOP and escalate every `ask-user` finding verbatim. Do NOT pass `--yes` and do NOT merge the PR yourself - on `checks-passed`, leave the PR ready and ask the user to review and merge it.
89
91
  - While a run is active never edit code to fix a finding; the pipeline owns the fixes (respond with `--action fix`). The full command reference lives in the gate skill / `/gate` command.
@@ -7,6 +7,6 @@
7
7
  "minimalCode"
8
8
  ],
9
9
  "updated": "2026-07-17T00:17:02+02:00",
10
- "cliVersion": "1.32.2",
10
+ "cliVersion": "1.32.4",
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.32.2",
7
+ "cliVersion": "1.32.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, API and request optimization (batching, avoiding redundant calls, skipping no-op writes), server-side caching (Redis), and Zod boundary validation.",
6
6
  "updated": "2026-07-23T04:41:39+02:00",
7
- "cliVersion": "1.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "d091e8708888d23ee14cdd6c0e71f386861f2252d95360895a31c73db090a689"
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, comments, code-level anti-patterns; TypeScript-first, language-agnostic).",
6
6
  "updated": "2026-07-30T15:48:04+02:00",
7
- "cliVersion": "1.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "05dc812da459071110d96ee41c5201b1ee230471d635352415e10ef8b220c267"
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.32.2",
7
+ "cliVersion": "1.32.4",
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-30T19:29:19+02:00",
7
- "cliVersion": "1.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "6a881d8589926fa7f48058314fd26d7042fd2ac82f1c87a6c11ffb54d1fda22b"
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.32.2",
7
+ "cliVersion": "1.32.4",
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.32.2",
7
+ "cliVersion": "1.32.4",
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.32.2",
7
+ "cliVersion": "1.32.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.32.2",
7
+ "cliVersion": "1.32.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.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "9e30ee7d8a1a1e8c6e7f4e043857cd01841c68a427752e45bc0cad9ec5cfa279"
9
9
  }
@@ -134,6 +134,16 @@ Navigation and any other persistent chrome must stay where the user left it whil
134
134
  - The same rule covers a sticky header, toolbar, filter rail or side panel: pinned, bounded, and independently scrollable. Keep pinned chrome shallow - it eats vertical space on small screens, so collapse it into a drawer or a top bar there rather than pinning it over half the viewport.
135
135
  - Verify by scrolling to the BOTTOM of a long page and confirming the sidebar is still on screen with its last entry reachable. Mechanically: the sidebar's `getBoundingClientRect().top` stays at its offset as the page scrolls, and `scrollHeight <= clientHeight` holds for it, or it can scroll itself.
136
136
 
137
+ ### On a phone the sidebar takes the whole screen
138
+
139
+ A sidebar keeps its desktop width only while there is a desktop to put it in. On a phone it becomes a full-screen surface, unless the design or the user says otherwise. Apply this by default to any off-canvas chrome: the nav sidebar, a filter rail, a details drawer, a settings panel.
140
+
141
+ - Full width AND full height: `w-full` (`100%`, `100vw`) with the desktop width added at a breakpoint (`w-full md:w-80`) or capped by `max-w-*`. Prefer `100dvh` over `100vh` for the height so the mobile browser's collapsing toolbar does not cut the panel off.
142
+ - A fixed width on a phone is the defect either way it lands: 320px on a 360px screen leaves a useless sliver of dead content, and anything wider than the viewport is simply cut off.
143
+ - The stacked variant counts as full width: when the layout collapses to one column and the sidebar returns to normal flow, it already spans the screen. Nothing more to do there.
144
+ - Being full screen makes it modal, so treat it as one: a visible close control, dismissal by backdrop tap and Escape, focus moved into the panel and trapped while it is open, focus restored to the trigger on close, and the page behind it locked from scrolling.
145
+ - The exceptions are real but explicit: a design that deliberately keeps a peek of the content behind, and a panel that is hidden on phones entirely because a different component serves that size. Say so in the code rather than leaving it to be read as an oversight.
146
+
137
147
  ---
138
148
 
139
149
  ## Links In Copy Are Links
@@ -222,6 +232,20 @@ When the user edits a value that must be unique within a set the client already
222
232
 
223
233
  ---
224
234
 
235
+ ## Sign-In, Sign-Up and Recovery Screens
236
+
237
+ Auth is the first screen a user meets and the one most often shipped half-built. Treat the four screens as one flow: sign in, sign up, forgot password, set a new password. The server-side rules (token lifetime, rate limits, what an answer may reveal) are owned by security-policy; what follows is the UI half.
238
+
239
+ - Every sign-in form with a password field carries a visible "Forgot your password?" link next to that field, leading to a real reset flow. Building the login screen without it is shipping a dead end.
240
+ - The reset request screen confirms in the same words whether or not the address is registered ("If that address has an account, we have sent a link"). Never render "no account with that email" - the screen would be an account-existence oracle.
241
+ - The new-password screen validates in real time against the same schema the server uses, uses the shared Input (which brings the show/hide toggle), and compares the confirmation field as the user types. Keep Submit disabled until both are valid, with the reason visible.
242
+ - After sign-up the user lands inside the app, already signed in. If the account still needs email verification, say so in the app with a way to resend, and block only the actions that need it.
243
+ - Surface throttling honestly. On a `429`, show how long the wait is (from `Retry-After`), keep the button disabled with a countdown, and never swallow the response into a generic "something went wrong".
244
+ - A one-time-code field is one input with `autocomplete="one-time-code"`, `inputmode="numeric"`, paste of the whole code, and no clearing of what the user typed on a wrong attempt. Say how many attempts are left only if the server chose to reveal it.
245
+ - Never keep a password, token, or code in `localStorage`, a query string, or an analytics payload. A reset token in the URL stays out of logs and out of any third-party script on the page.
246
+
247
+ ---
248
+
225
249
  ## Client-Side Caching (Reduce Server Load)
226
250
 
227
251
  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.
@@ -3,7 +3,7 @@
3
3
  "version": "1.20.0",
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Frontend architecture: reusable components, abstraction thresholds, state management, 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/pagination, virtualization, skeletons, progressive loading), 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), variable-length text (min-width:0 in flex/grid, wrap vs truncate, long unbroken strings, worst-case content checks), and AI chat/agent interfaces via Vercel's AI Elements registry instead of hand-rolled message threads.",
6
- "updated": "2026-07-30T19:29:05+02:00",
7
- "cliVersion": "1.32.2",
8
- "sha": "a9750bd74505373aea8d9265515fc3b7f0fddf3e822d9e046248223e24ff96af"
6
+ "updated": "2026-07-31T16:26:45+02:00",
7
+ "cliVersion": "1.32.4",
8
+ "sha": "39a47d023d64ea1bb9b72fa872276b617cb8c85d910e5b8fb2f23d4ae44f5ef5"
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.32.2",
7
+ "cliVersion": "1.32.4",
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.32.2",
10
+ "cliVersion": "1.32.4",
11
11
  "sha": "09cdbefd98625b02a7d03685e5deed128238ff8454a83fe22279610fe3ef8ddf"
12
12
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: security-policy
3
- description: Application and AI-agent security - secrets management, authentication and authorization (least privilege), OWASP Top 10 mitigations, transport and crypto baseline, secure logging, and agent/MCP/tool-use safety (prompt injection, untrusted tool output, permission boundaries). Use when handling secrets, auth, permissions, untrusted data or tool output, or any security-sensitive code, config, or infrastructure.
3
+ description: Application and AI-agent security - secrets management, authentication and authorization (least privilege), credential flows (sign-in, sign-up that establishes the session, password reset, 2FA, and rate limiting per IP and per account), OWASP Top 10 mitigations, transport and crypto baseline, secure logging, and agent/MCP/tool-use safety (prompt injection, untrusted tool output, permission boundaries). Use when handling secrets, auth, login or registration screens, permissions, untrusted data or tool output, or any security-sensitive code, config, or infrastructure.
4
4
  ---
5
5
 
6
6
  # Security Policy
@@ -37,6 +37,42 @@ description: Application and AI-agent security - secrets management, authenticat
37
37
 
38
38
  ---
39
39
 
40
+ ## Credential Flows (Sign-In, Sign-Up, Reset, 2FA)
41
+
42
+ These four screens are one system: an attacker who cannot guess a password will try to register, reset, or brute-force a second factor instead. Build them together.
43
+
44
+ ### Sign-up establishes the session
45
+
46
+ - A successful registration already proves the credentials: create the session there and land the user in the app. Never redirect them to the sign-in form to retype what they just typed.
47
+ - Keep email verification asynchronous. Let them in unverified, ask for confirmation, and gate only the actions that genuinely need a verified address (billing, invites, outbound mail).
48
+ - The exception is an account created by someone else (admin provisioning, an approval queue). Then say so in the code, because the redirect looks like the defect.
49
+
50
+ ### Password recovery is part of every password login
51
+
52
+ - Every sign-in form with a password field needs a visible recovery entry point. A login screen with no way out of a forgotten password is an incomplete flow, not a simpler one.
53
+ - The request step answers identically whether or not the account exists, and takes the same time. "If that address has an account, we sent a link" is the whole response; never confirm or deny.
54
+ - The token is high-entropy, single-use, stored hashed, and expires in 15 to 60 minutes. Issuing a new one invalidates the previous one.
55
+ - On a successful reset: consume the token, rotate the session, and invalidate every other active session and refresh token for that account. Notify the account by email that the password changed.
56
+ - Never send the new password by email, and never embed credentials in the link. The link proves control of the address, nothing more.
57
+
58
+ ### Rate-limit by IP AND by account
59
+
60
+ - Limit both dimensions on every credential endpoint: sign-in, sign-up, password reset request, reset confirmation, email verification, 2FA/OTP verification, and any "does this identifier exist" helper the sign-in page calls.
61
+ - By IP stops the broad sweep. By account or identifier stops the slow distributed attack that spreads one attempt per IP across a botnet, which the IP limit never sees. One without the other is not rate limiting.
62
+ - Count FAILURES, not requests, so a person typing their password wrong twice is not treated like an attack while a scripted run is stopped early.
63
+ - Back off exponentially and answer `429` with `Retry-After`. Keep every counter server-side; a client-held attempt count is decoration.
64
+ - Cap second factors hard: a handful of attempts per code, then invalidate the code and require a new one. OTP codes are single-use with a short TTL, and backup codes are single-use and stored hashed.
65
+ - After a threshold of failures, lock the account temporarily and tell the owner by email. An unbounded lock is a denial-of-service someone else can trigger, so prefer a timed lock with a clear unlock path.
66
+ - Derive the client IP from the trusted proxy chain, never from a raw client-supplied header. `X-Forwarded-For` is attacker-controlled unless your edge rewrites it.
67
+ - The limiter must not become an oracle: an unknown account and a known one get the same response shape, status, and timing. Always run the password hash, even when the user does not exist, so the timing does not answer the question the error message refused to.
68
+ - Registration and any endpoint that sends mail also carry a cost and abuse surface. Limit them per IP, per address, and globally.
69
+
70
+ ### Uniform answers
71
+
72
+ - Wrong password, unknown account, locked account, and unverified account all return the same generic failure to the client. The specifics belong in the server log, not the response.
73
+
74
+ ---
75
+
40
76
  ## OWASP Top 10 Baseline
41
77
 
42
78
  - Injection (SQL/NoSQL/command/LDAP): use parameterized queries and safe APIs; never build queries or shell commands by string concatenation (query specifics in database-expert).
@@ -3,7 +3,7 @@
3
3
  "version": "1.0.0",
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
- "updated": "2026-06-01T00:45:28+02:00",
7
- "cliVersion": "1.32.2",
8
- "sha": "9971e9d9127397d0152e89d24aad3191e2935e55a8483db7fd15f5d4d7a60e7a"
6
+ "updated": "2026-07-31T16:26:45+02:00",
7
+ "cliVersion": "1.32.4",
8
+ "sha": "e5e7ac2791919e4b8483115ee8b83800c5ebdf4eebde019650d974b400417c55"
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-07-29T01:18:36+02:00",
7
- "cliVersion": "1.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "13d24c217bdb6fe83fe16835d8f5c3d397a9f3338f16876c61ef96e97f34c90a"
9
9
  }
@@ -35,6 +35,8 @@ description: Exhaustive completion discipline for long, complex, or multi-item t
35
35
 
36
36
  - Work unit by unit (or in coherent batches). Flip a unit to `done` only after its own verification - it compiles/typechecks, its tests pass, or a smoke check ran - never because a similar unit worked.
37
37
  - Never silently skip, stub, or simplify a unit. A stub, TODO, or partial implementation keeps the unit `pending` or `blocked` with the reason recorded. Schedule hard units early; difficulty is a reason to start sooner, not to defer.
38
+ - Never pause the run to ask which unit comes next or in what order. The ledger already answers that: order the units yourself (dependencies first, hard ones early) and keep working. "Shall I continue with the rest?", "¿sigo con las tareas 5-8, o prefieres otro orden?", "which should I start with?" are not checkpoints - they hand an unfinished task back to the user, who already asked for all of it, and cost a turn to answer something with one possible answer.
39
+ - The only legitimate pause is a genuine blocker: access or credentials you lack, an irreversible or destructive action, or a decision that is genuinely the user's (business, legal, cost). Record it as `blocked(<reason>)`, report the blocker by name along with everything finished before it, and never phrase it as a request for permission to keep going. Real ambiguity about scope is resolved BEFORE the inventory, not used to pause mid-run.
38
40
  - On context compaction, session resume, or sub-agent handback: re-read the ledger FIRST and continue from it. Never reconstruct progress from memory - that is where items get dropped.
39
41
  - Sub-agents must report which ledger units they completed and how each was verified; unverified claims stay `pending`.
40
42
 
@@ -53,7 +55,7 @@ A completion claim is forbidden unless ALL of these hold:
53
55
 
54
56
  - If any check fails, the task is NOT done: state exactly what remains and keep working (or report the blocker). Never say "everything is complete", "fully ported", or "all done" while the ledger has open units.
55
57
  - Words like "complete", "all", "every", and "fully" in a final report are claims that must be backed by checks 1-5.
56
- - These checks also run automatically at turn end: when a final message claims the work is finished, enigma re-runs them and denies the stop if the evidence contradicts the claim (see the verify concept). Treat that as a backstop for accidents, never as the thing that does the checking - a claim it has to catch was one that should never have been made.
58
+ - These checks also run automatically at turn end: when a final message claims the work is finished, enigma re-runs them and denies the stop if the evidence contradicts the claim - and it likewise denies a stop whose final message asks whether, or in which order, to continue (see the verify concept). Treat that as a backstop for accidents, never as the thing that does the checking - a claim it has to catch was one that should never have been made.
57
59
 
58
60
  ---
59
61
 
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "task-completion-policy",
3
- "version": "1.3.0",
3
+ "version": "1.4.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-07-27T17:22:17+02:00",
7
- "cliVersion": "1.32.2",
8
- "sha": "b9046c15fd636057a8e42e199d2eeb47297477f38a0a26a07a7b0bf71d61fcb4"
6
+ "updated": "2026-07-30T20:10:24+02:00",
7
+ "cliVersion": "1.32.4",
8
+ "sha": "2f82288d83dca3676d6bc49955c89477cc298f77fa56b4e414cd95ff858fa261"
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-07-28T20:30:23+02:00",
7
- "cliVersion": "1.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "a4b792103eb1f9dad93b9d70ea79dc18fe9cbbc318facf5adb47ae5907d842f9"
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.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "3bdf591057b760f674fb2b1425f63acb426cda2c4f042e1a74c5a5d3807df664"
9
9
  }
@@ -4,6 +4,6 @@
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Strict frontend + backend schema validation, schema consistency, and safe client-facing error handling.",
6
6
  "updated": "2026-07-22T01:41:06+02:00",
7
- "cliVersion": "1.32.2",
7
+ "cliVersion": "1.32.4",
8
8
  "sha": "d937df0052d1ec151728a28f6567744d9e7ca0a65d84b4d5c9a697f1e40d704f"
9
9
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "enigma-darwin-arm64": "8221580f5a8bb0a1b9400c88d52765573cde4185501310b8b6bbd6f257cb604b",
3
- "enigma-linux-arm64": "492da75deb97e22217255d6477097450c23392cabd90e21c6ad5edf152b650d1",
4
- "enigma-linux-x64": "2f60fc015b2933523b211a8df4cacef015be6c2141535f4fbc3ca99bdc341c4d",
5
- "enigma-win32-x64.exe": "556cf44c2ce5fc7a55872078069f24a5fc30adea90259637f5e9e7baed43794a"
2
+ "enigma-darwin-arm64": "62e27c388131ff0ea2095e60a85ab8d5172fc6cb548bd4890c7db22778e92e58",
3
+ "enigma-linux-arm64": "491e5d5856333e28c6d05213cd320622d3b5277f7502c5a9d399e93996c7fc65",
4
+ "enigma-linux-x64": "60f16b62280f94415edf43c45dbc29898ad2d944b5536fdcb98f9654a9b768af",
5
+ "enigma-win32-x64.exe": "a75e6dd25028491d5e6c86a6577a884e6265074c74b037dead0fa46de6635e6a"
6
6
  }
@@ -463,6 +463,267 @@ var BUILTIN_RULES = [
463
463
  fileCheck: "proc-windows-hide",
464
464
  message: "Process spawned without windowsHide. On Windows a console child started by a process that has no console of its own - a daemon, an editor hook, a detached background task - pops a real console window on screen and closes it again, which reads as something crashing. Add `windowsHide: true` to the options object; it is inert on macOS and Linux, and inert on Windows when the parent already has a console, so it is safe on every call that is not deliberately opening a terminal for the user. For one that IS (a login flow that must show a terminal), mark the call with an `enigma:` note.",
465
465
  severity: "block"
466
+ },
467
+ {
468
+ id: "fe-mobile-drawer-full-width",
469
+ label: "An off-canvas panel fills the phone screen",
470
+ files: ["*.tsx", "*.jsx", "*.vue", "*.svelte", "*.astro", "*.html", "*.htm"],
471
+ excludeFiles: [
472
+ "*.test.*",
473
+ "*.spec.*",
474
+ "**/tests/**",
475
+ "**/__tests__/**",
476
+ "**/stories/**",
477
+ "*.stories.*",
478
+ "*.min.js",
479
+ "**/dist/**",
480
+ "**/build/**",
481
+ "**/node_modules/**",
482
+ "**/vendor/**",
483
+ "dist/**",
484
+ "build/**",
485
+ "node_modules/**",
486
+ "vendor/**"
487
+ ],
488
+ scope: "file",
489
+ // An off-canvas panel (fixed/absolute, pinned for the full height) whose width is a fixed
490
+ // desktop size with no full-width base. Utility-class frameworks only: the same defect in
491
+ // a stylesheet lives inside a @media block, which a line scanner cannot see.
492
+ // PRECISION, measured over the reference corpus: the width must be a PANEL width - 100px
493
+ // or more, 10rem or more, a Tailwind step of 40 (10rem) or more, or a fraction of the
494
+ // viewport. Every false positive found was a hairline (`w-px` chart cursors, a `w-px`
495
+ // resize handle, a `before:w-0.5` guide line), and that one bound removed all of them.
496
+ // The negative lookahead carries the "unless the design says otherwise" cases: an
497
+ // already-full-width base, a width capped by `max-w-full`, and a panel that is hidden on
498
+ // phones anyway (the desktop half of a hidden/drawer pair).
499
+ pattern: "^(?!.*enigma:)(?!.*(?:\\bw-full\\b|\\bmax-w-full\\b|\\bw-screen\\b|\\bhidden\\s+(?:sm|md|lg|xl):|\\bmd:hidden\\b))(?:.*\\b(?:fixed|absolute)\\b[^\"'`]*\\b(?:inset-y-0|h-full|h-screen)\\b[^\"'`]*\\bw-(?:\\[(?:\\d{3,}px|\\d{2,}rem)\\]|[4-9]\\d\\b|\\d{3}\\b|\\d/\\d\\b)|.*\\bw-(?:\\[(?:\\d{3,}px|\\d{2,}rem)\\]|[4-9]\\d\\b|\\d{3}\\b|\\d/\\d\\b)[^\"'`]*\\b(?:fixed|absolute)\\b[^\"'`]*\\b(?:inset-y-0|h-full|h-screen)\\b)",
500
+ absent: "enigma:allow-partial-drawer",
501
+ message: "Off-canvas panel with a desktop width. On a phone a sidebar, drawer or nav panel takes the WHOLE screen - `w-full` as the base, with the desktop width added at a breakpoint (`w-full md:w-80`) or capped by `max-w-*`. A 320px panel on a 360px screen leaves a sliver of dead content behind it, and a panel wider than the viewport is simply cut off. Keep it dismissible: a close control, the backdrop, and Escape. If the design deliberately wants a partial-width panel on mobile, put an `enigma:` note on the line or `enigma:allow-partial-drawer` in the file (frontend-policy).",
502
+ severity: "block",
503
+ skill: "frontend-policy"
504
+ },
505
+ // AUTH. Three defects an agent building a sign-in flow reproduces constantly, each with a
506
+ // file-local signature. They fire only in files NAMED for the flow they belong to
507
+ // (*login*, *register*, *2fa*, ...), which is what keeps them off the rest of a codebase:
508
+ // "password" and "redirect to /login" appear everywhere, but not in a file called login.tsx
509
+ // that is not the sign-in surface. The semantic half of each - token lifetime, lockout
510
+ // policy, what a reset email may reveal - has no signature and lives in security-policy.
511
+ {
512
+ id: "auth-password-reset-entry",
513
+ label: "Login form offers a way to recover the password",
514
+ ignoreFileCase: true,
515
+ // Named for the flow, either way a project spells it: in the FILE name (login-form.tsx)
516
+ // or in the DIRECTORY (app/(auth)/login/page.tsx, the Next App Router shape - a basename
517
+ // glob would miss every one of those). Dir globs are listed twice because `**/x/**` does
518
+ // not match a root-level `x/`, the same gotcha the excludes above carry.
519
+ files: [
520
+ "*login*.tsx",
521
+ "*login*.jsx",
522
+ "*login*.vue",
523
+ "*login*.svelte",
524
+ "*login*.astro",
525
+ "*login*.html",
526
+ "*signin*.tsx",
527
+ "*signin*.jsx",
528
+ "*signin*.vue",
529
+ "*signin*.svelte",
530
+ "*signin*.astro",
531
+ "*signin*.html",
532
+ "*sign-in*.tsx",
533
+ "*sign-in*.jsx",
534
+ "*sign-in*.vue",
535
+ "*sign-in*.svelte",
536
+ "*sign-in*.astro",
537
+ "*sign-in*.html",
538
+ "**/login/**",
539
+ "login/**",
540
+ "**/signin/**",
541
+ "signin/**",
542
+ "**/sign-in/**",
543
+ "sign-in/**"
544
+ ],
545
+ excludeFiles: [
546
+ "*.test.*",
547
+ "*.spec.*",
548
+ "**/tests/**",
549
+ "**/__tests__/**",
550
+ "**/stories/**",
551
+ "*.stories.*",
552
+ "**/dist/**",
553
+ "**/build/**",
554
+ "**/node_modules/**",
555
+ "**/vendor/**",
556
+ "dist/**",
557
+ "build/**",
558
+ "node_modules/**",
559
+ "vendor/**"
560
+ ],
561
+ scope: "file",
562
+ // The password field is what makes this THE sign-in surface rather than a wrapper or a
563
+ // route file; a login page that only renders <LoginForm/> has no password field and is
564
+ // correctly left alone (the form itself is the file that must carry the link).
565
+ pattern: `type=["']password["']|type=\\{["']password["']\\}`,
566
+ // Any recovery affordance clears the file: the link, the route, or a handler named for it.
567
+ absent: "forgot|reset[-_ ]?password|password[-_ ]?reset|recover|olvid|recuperar|magic[-_ ]?link|enigma:allow-no-reset",
568
+ message: 'Sign-in form with no way out of a forgotten password. Every login form needs a visible "Forgot your password?" entry point next to the password field, leading to a real reset flow: ask for the identifier, always answer the same way (never reveal whether the account exists), email a single-use token that expires in ~15-60 minutes, and on success invalidate that token plus every other active session. If this screen is deliberately reset-less (an internal tool, SSO-only, a passwordless magic-link form), mark the file with an `enigma:allow-no-reset` note (frontend-policy, security-policy).',
569
+ severity: "block",
570
+ skill: "security-policy"
571
+ },
572
+ {
573
+ id: "auth-signup-auto-login",
574
+ label: "Registration signs the user in",
575
+ ignoreFileCase: true,
576
+ files: [
577
+ "*register*.tsx",
578
+ "*register*.jsx",
579
+ "*register*.ts",
580
+ "*register*.js",
581
+ "*register*.vue",
582
+ "*register*.svelte",
583
+ "*register*.astro",
584
+ "*signup*.tsx",
585
+ "*signup*.jsx",
586
+ "*signup*.ts",
587
+ "*signup*.js",
588
+ "*signup*.vue",
589
+ "*signup*.svelte",
590
+ "*signup*.astro",
591
+ "*sign-up*.tsx",
592
+ "*sign-up*.jsx",
593
+ "*sign-up*.ts",
594
+ "*sign-up*.js",
595
+ "*sign-up*.vue",
596
+ "*sign-up*.svelte",
597
+ "*sign-up*.astro",
598
+ "**/register/**",
599
+ "register/**",
600
+ "**/signup/**",
601
+ "signup/**",
602
+ "**/sign-up/**",
603
+ "sign-up/**"
604
+ ],
605
+ excludeFiles: [
606
+ "*.test.*",
607
+ "*.spec.*",
608
+ "**/tests/**",
609
+ "**/__tests__/**",
610
+ "**/stories/**",
611
+ "*.stories.*",
612
+ "*.d.ts",
613
+ "**/dist/**",
614
+ "**/build/**",
615
+ "**/node_modules/**",
616
+ "**/vendor/**",
617
+ "dist/**",
618
+ "build/**",
619
+ "node_modules/**",
620
+ "vendor/**"
621
+ ],
622
+ scope: "file",
623
+ // Only a PROGRAMMATIC redirect to the sign-in route counts. An `href="/login"` is the
624
+ // "already have an account?" link every sign-up form carries and is not the defect, so
625
+ // the pattern requires a navigation CALL - which is the code path that runs after the
626
+ // account is created.
627
+ pattern: "(?:push|replace|redirect|navigate|goto|assign)\\(\\s*[\"'`][^\"'`]*/(?:login|signin|sign-in)\\b|location(?:\\.href)?\\s*=\\s*[\"'`][^\"'`]*/(?:login|signin|sign-in)\\b|(?:push|replace|navigate)\\(\\s*\\{[^}]*name:\\s*[\"'`](?:login|signin|sign-in)[\"'`]",
628
+ // Any session-establishing call in the file means the redirect is some other path
629
+ // (an already-registered branch, an error case), so the file is cleared.
630
+ absent: "signIn\\(|signInWith|createSession|setSession|startSession|newSession|setAuthCookie|setAuthToken|setToken\\(|setAuth\\(|login\\(|logIn\\(|authenticate\\(|session\\.save|sessionStorage\\.setItem\\([\"'`](?:token|session)|cookies\\(\\)\\.set|setUser\\(|enigma:allow-login-redirect",
631
+ message: "Registration sends the user to the sign-in screen instead of signing them in. A successful sign-up already proves the credentials: establish the session right there and land the user in the app. Keep email verification asynchronous (let them in, ask them to confirm, and gate only the actions that need a verified address) rather than parking them on a login form to type what they just typed. If this redirect is deliberate (an admin creating someone else's account, an approval queue), mark the file with an `enigma:allow-login-redirect` note (backend-policy, security-policy).",
632
+ severity: "block",
633
+ skill: "security-policy"
634
+ },
635
+ {
636
+ id: "auth-rate-limit",
637
+ label: "Rate-limit the credential endpoints",
638
+ ignoreFileCase: true,
639
+ files: [
640
+ "*login*.ts",
641
+ "*login*.js",
642
+ "*login*.mts",
643
+ "*login*.cts",
644
+ "*login*.py",
645
+ "*signin*.ts",
646
+ "*signin*.js",
647
+ "*sign-in*.ts",
648
+ "*sign-in*.js",
649
+ "*signin*.py",
650
+ "*sign-in*.py",
651
+ "*register*.ts",
652
+ "*register*.js",
653
+ "*signup*.ts",
654
+ "*signup*.js",
655
+ "*sign-up*.ts",
656
+ "*sign-up*.js",
657
+ "*register*.py",
658
+ "*signup*.py",
659
+ "*sign-up*.py",
660
+ "*2fa*.ts",
661
+ "*2fa*.js",
662
+ "*2fa*.py",
663
+ "*mfa*.ts",
664
+ "*mfa*.js",
665
+ "*mfa*.py",
666
+ "*otp*.ts",
667
+ "*otp*.js",
668
+ "*otp*.py",
669
+ "*forgot-password*.ts",
670
+ "*forgot-password*.js",
671
+ "*forgot-password*.py",
672
+ "*reset-password*.ts",
673
+ "*reset-password*.js",
674
+ "*reset-password*.py",
675
+ "**/login/**",
676
+ "login/**",
677
+ "**/signin/**",
678
+ "signin/**",
679
+ "**/sign-in/**",
680
+ "sign-in/**",
681
+ "**/register/**",
682
+ "register/**",
683
+ "**/signup/**",
684
+ "signup/**",
685
+ "**/sign-up/**",
686
+ "sign-up/**",
687
+ "**/2fa/**",
688
+ "2fa/**",
689
+ "**/mfa/**",
690
+ "mfa/**",
691
+ "**/otp/**",
692
+ "otp/**",
693
+ "**/forgot-password/**",
694
+ "forgot-password/**",
695
+ "**/reset-password/**",
696
+ "reset-password/**"
697
+ ],
698
+ excludeFiles: [
699
+ "*.test.*",
700
+ "*.spec.*",
701
+ "**/tests/**",
702
+ "**/__tests__/**",
703
+ "test_*.py",
704
+ "*_test.py",
705
+ "*.d.ts",
706
+ "**/dist/**",
707
+ "**/build/**",
708
+ "**/node_modules/**",
709
+ "**/vendor/**",
710
+ "dist/**",
711
+ "build/**",
712
+ "node_modules/**",
713
+ "vendor/**"
714
+ ],
715
+ scope: "file",
716
+ // A server-side handler for the flow: the route export/registration, a framework
717
+ // decorator, or a "use server" module (a Next server action reachable from the sign-in
718
+ // page is an unauthenticated endpoint like any other, and it is how App Router projects
719
+ // write this). A client component calling fetch() is deliberately not matched - it
720
+ // cannot enforce a limit, and the file that must is the one defining the endpoint.
721
+ pattern: `export\\s+(?:async\\s+)?function\\s+(?:POST|PUT|PATCH)\\b|export\\s+const\\s+(?:POST|PUT|PATCH)\\s*[:=]|\\b(?:router|app|api|server|fastify)\\.(?:post|put|patch)\\s*\\(|@(?:app|router|bp|blueprint)\\.(?:post|route)\\s*\\(|@Post\\s*\\(|^["']use server["']`,
722
+ // Cleared by any limiter in the file, whatever the library or the wrapper name.
723
+ absent: "rate[-_]?limit|ratelimit|Ratelimit|RateLimiter|limiter|throttle|slowDown|slow_down|bottleneck|arcjet|leaky|token[-_]?bucket|attempts?[-_]?(?:left|remaining|count)|lockout|too[-_ ]?many[-_ ]?requests|429|enigma:allow-unlimited-auth",
724
+ message: "Credential endpoint with no rate limiting. Login, registration, password reset and every 2FA/OTP verification are guessing surfaces: limit them BY IP (blunt, stops the broad sweep) AND BY ACCOUNT or identifier (stops the slow distributed attack the IP limit misses), count failures rather than requests, back off exponentially, and answer 429 with Retry-After. Keep the accounting server-side and identical for an unknown account, so the limiter itself does not become an account-existence oracle. If the limit is enforced upstream (gateway, middleware, WAF), note it in the file with an `enigma:allow-unlimited-auth` marker (security-policy, backend-policy).",
725
+ severity: "block",
726
+ skill: "security-policy"
466
727
  }
467
728
  ];
468
729
  var PROJECT_CHECKS = {
@@ -533,10 +794,10 @@ function readPkgDeps(root) {
533
794
  return null;
534
795
  }
535
796
  }
536
- function globToRegExp(glob) {
797
+ function globToRegExp(glob, ignoreCase = false) {
537
798
  const esc = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&");
538
799
  const body = esc.replace(/\*\*/g, " ").replace(/\*/g, "[^/]*").replace(/ /g, ".*").replace(/\?/g, "[^/]");
539
- return new RegExp(glob.includes("/") ? `^${body}$` : `(^|/)${body}$`);
800
+ return new RegExp(glob.includes("/") ? `^${body}$` : `(^|/)${body}$`, ignoreCase ? "i" : "");
540
801
  }
541
802
  function guardrailsConfigPath() {
542
803
  return process.env.ENIGMA_GUARDRAILS_CONFIG || join(homedir(), ".enigma-guardrails.json");
@@ -581,8 +842,8 @@ function checkFile(file, content, projectRoot) {
581
842
  const norm = file.replace(/\\/g, "/");
582
843
  const out = [];
583
844
  for (const rule of loadRules()) {
584
- if (!rule.files.some((g) => globToRegExp(g).test(norm))) continue;
585
- if (rule.excludeFiles?.some((g) => globToRegExp(g).test(norm))) continue;
845
+ if (!rule.files.some((g) => globToRegExp(g, rule.ignoreFileCase).test(norm))) continue;
846
+ if (rule.excludeFiles?.some((g) => globToRegExp(g, rule.ignoreFileCase).test(norm))) continue;
586
847
  const base = { ruleId: rule.id, severity: rule.severity, file: norm, message: rule.message, skill: rule.skill };
587
848
  if (rule.scope === "file" && rule.maxBytes) {
588
849
  const bytes = Buffer.byteLength(content, "utf8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enigma-cli",
3
- "version": "1.32.2",
3
+ "version": "1.32.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": {