@devrik-tools/claude-gates 0.4.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/README.es.md +39 -4
  3. package/README.md +34 -5
  4. package/cli/config.mjs +126 -124
  5. package/cli/init.mjs +303 -276
  6. package/cli/install.mjs +281 -175
  7. package/cli/materialize.mjs +103 -102
  8. package/cli/registry.mjs +139 -136
  9. package/cli/smoke-fixtures.json +65 -0
  10. package/cli/task.mjs +140 -140
  11. package/package.json +1 -1
  12. package/plugins/gates/.claude-plugin/plugin.json +1 -1
  13. package/plugins/gates/hooks/ask-adoption.mjs +147 -147
  14. package/plugins/gates/hooks/doctor.mjs +207 -207
  15. package/plugins/gates/hooks/gates/atomic-commit/index.mjs +229 -0
  16. package/plugins/gates/hooks/gates/audit-before-build/index.mjs +110 -88
  17. package/plugins/gates/hooks/gates/autonomous-mode/index.mjs +50 -50
  18. package/plugins/gates/hooks/gates/bash-commands/index.mjs +215 -215
  19. package/plugins/gates/hooks/gates/brief-approved/index.mjs +216 -0
  20. package/plugins/gates/hooks/gates/brief-before-delegate/index.mjs +269 -265
  21. package/plugins/gates/hooks/gates/capability-map/index.mjs +701 -0
  22. package/plugins/gates/hooks/gates/circuit-breaker/index.mjs +527 -501
  23. package/plugins/gates/hooks/gates/diagnosis-before-patch/index.mjs +48 -43
  24. package/plugins/gates/hooks/gates/feature-catalog/index.mjs +83 -83
  25. package/plugins/gates/hooks/gates/force-parallel/index.mjs +134 -119
  26. package/plugins/gates/hooks/gates/forge-flow/index.mjs +134 -134
  27. package/plugins/gates/hooks/gates/implementation-pipeline/index.mjs +187 -187
  28. package/plugins/gates/hooks/gates/intent-flow/index.mjs +260 -260
  29. package/plugins/gates/hooks/gates/lint-commit/index.mjs +152 -149
  30. package/plugins/gates/hooks/gates/mandatory-flow/index.mjs +180 -180
  31. package/plugins/gates/hooks/gates/never-assume/index.mjs +59 -58
  32. package/plugins/gates/hooks/gates/no-blocking/index.mjs +163 -148
  33. package/plugins/gates/hooks/gates/no-coauthor/index.mjs +127 -0
  34. package/plugins/gates/hooks/gates/no-lint-suppression/index.mjs +183 -0
  35. package/plugins/gates/hooks/gates/protected-paths/index.mjs +149 -144
  36. package/plugins/gates/hooks/gates/recurrence-lock/index.mjs +91 -89
  37. package/plugins/gates/hooks/gates/reuse-before-build/index.mjs +263 -159
  38. package/plugins/gates/hooks/gates/risk-level/index.mjs +265 -263
  39. package/plugins/gates/hooks/gates/root-cause-first/index.mjs +57 -56
  40. package/plugins/gates/hooks/gates/root-whitelist/index.mjs +211 -131
  41. package/plugins/gates/hooks/gates/rule-skill-autodiscovery/index.mjs +181 -184
  42. package/plugins/gates/hooks/gates/sdd-specs/index.mjs +256 -256
  43. package/plugins/gates/hooks/gates/staged-lint/index.mjs +187 -0
  44. package/plugins/gates/hooks/gates/stop-pending/index.mjs +169 -164
  45. package/plugins/gates/hooks/gates/test-matrix/index.mjs +187 -187
  46. package/plugins/gates/hooks/gates/tool-map/index.mjs +168 -143
  47. package/plugins/gates/hooks/hooks.json +61 -0
  48. package/plugins/gates/hooks/lib/config.mjs +179 -172
  49. package/plugins/gates/hooks/lib/hook-io.mjs +367 -357
  50. package/plugins/gates/hooks/lib/signals.mjs +172 -127
  51. package/plugins/gates/hooks/wiring-check.mjs +227 -227
  52. package/plugins/tasks/.claude-plugin/plugin.json +1 -1
  53. package/plugins/tasks/hooks/hooks.json +26 -26
  54. package/plugins/tasks/hooks/lib/task-store.mjs +217 -197
  55. package/plugins/tasks/hooks/register-requests.mjs +145 -145
  56. package/plugins/tasks/hooks/session-tasks.mjs +108 -108
  57. package/registry.json +192 -1
@@ -1,127 +1,172 @@
1
- // signals.mjs — central catalog of LEXICAL prose signals, each covering Spanish AND
2
- // English, for every gate that matches free text (briefs, delegation prompts, written
3
- // content). Written once here; gates import instead of keeping their own monolingual or
4
- // partially-bilingual copy.
5
- //
6
- // ── Scope: lexical only ──────────────────────────────────────────────────────────────
7
- // A gate whose real signal lives in the ACTION (a shell command, a tool name, a file
8
- // path) keeps using structural detection for that part — this module does not replace
9
- // toolInGroups/writtenPathOf/etc., and a gate should prefer the structural check first
10
- // wherever the signal is genuinely structural. This module exists for the remaining
11
- // case: prose written by a human or a model, in either language, that a gate must
12
- // recognize regardless of which language it was written in.
13
- //
14
- // ── Honest limitation ────────────────────────────────────────────────────────────────
15
- // Coverage is ES + EN only. A prompt written in a third language (or a language-neutral
16
- // paraphrase using none of these terms) is NOT covered by any pattern here — this module
17
- // closes the specific ES/EN gap the gates had, it does not claim language-agnostic
18
- // detection. Where the underlying signal can instead be read off the action itself
19
- // (structural), that stays the primary defense; lexical matching is the fallback layer
20
- // for prose that never had a structural signal to read.
21
- //
22
- // ── Word-boundary and accent handling ────────────────────────────────────────────────
23
- // `withUnicodeWordBoundary` uses lookarounds over `\p{L}|\p{N}|_` (not `\b`, which does
24
- // not treat accented letters as word characters) so short/dangerous terms ("auth",
25
- // "prod") require a full word match and do not fire inside "autor" or "producto".
26
- // Spanish terms that carry an accent in correct spelling (contraseña, sesión, migración)
27
- // are matched with an optional accent (`sesi[oó]n`) because normalized/ASCII input is
28
- // common in prompts and commit messages.
29
-
30
- /** Unicode-aware word boundary: matches only when the alternation is not adjacent to
31
- * another letter/digit/underscore, so short terms cannot match as a substring of a
32
- * longer unrelated word. */
33
- export function withUnicodeWordBoundary(alternatives) {
34
- return new RegExp(
35
- `(?<![\\p{L}\\p{N}_])(?:${alternatives})(?![\\p{L}\\p{N}_])`,
36
- 'iu',
37
- );
38
- }
39
-
40
- // ── RISK_SIGNAL: high-impact mutation area (money / auth / destructive-deploy) ───────
41
- // Used where a gate must recognize that a request touches a sensitive domain,
42
- // regardless of whether it was phrased in Spanish or English. Three sub-domains kept
43
- // separate so a gate can compose only the ones it needs, and one combined export for
44
- // gates that want the whole class.
45
-
46
- const MONEY_TERMS =
47
- 'money|payment|pago|dinero|cobro|charge|invoice|factura|precio|price|saldo|balance|amount|monto';
48
-
49
- const AUTH_TERMS =
50
- 'auth|autenticaci[oó]n|authentication|credencial|credential|contrase[nñ]a|password|' +
51
- 'token|sesi[oó]n|session|permiso|permission|role|rol';
52
-
53
- const DESTRUCTIVE_DEPLOY_TERMS =
54
- 'deploy|desplieg(?:a|ue|ar)?|producci[oó]n|production|prod|borrar|delete|drop|truncate|' +
55
- 'migraci[oó]n|migration|migrate|migr(?:a|ar)|eliminar';
56
-
57
- export const MONEY_SIGNAL = withUnicodeWordBoundary(MONEY_TERMS);
58
- export const AUTH_SIGNAL = withUnicodeWordBoundary(AUTH_TERMS);
59
- export const DESTRUCTIVE_DEPLOY_SIGNAL = withUnicodeWordBoundary(
60
- DESTRUCTIVE_DEPLOY_TERMS,
61
- );
62
-
63
- /** Combined high-impact mutation signal: money OR auth OR a destructive/deploy verb. */
64
- export const RISK_SIGNAL = withUnicodeWordBoundary(
65
- `${MONEY_TERMS}|${AUTH_TERMS}|${DESTRUCTIVE_DEPLOY_TERMS}`,
66
- );
67
-
68
- /** RISK_SIGNAL as plain regex-source alternatives (no boundary/flags), for gates that
69
- * build their own combined pattern (e.g. joined with other domain-specific sources). */
70
- export const RISK_SIGNAL_SOURCES = [MONEY_TERMS, AUTH_TERMS, DESTRUCTIVE_DEPLOY_TERMS];
71
-
72
- // ── MUTATION_RISK_SIGNAL: broader than RISK_SIGNAL, used only to VOID an exemption ──
73
- // Several gates let a whitelisted read-only subagent name (or an exempt-query verb)
74
- // skip their check — but only when the prompt itself carries no mutation risk. That
75
- // exemption-voiding check is deliberately broader than RISK_SIGNAL: it also counts a
76
- // bare "data"/"datos"/"write"/"escrib*" mention, because the name/label is unverified
77
- // and a false positive here only means the underlying check still runs (cheap), while a
78
- // false negative would let a real mutator dodge the check via a trusted-sounding label.
79
- // Kept as a distinct export from RISK_SIGNAL because the two answer different
80
- // questions (RISK_SIGNAL: "is this domain high-impact?" vs MUTATION_RISK_SIGNAL: "is it
81
- // unsafe to trust this label/verb at all?").
82
- const MUTATION_RISK_TERMS =
83
- `${MONEY_TERMS}|${AUTH_TERMS}|credencial|credential|data|datos|borrar|delete|drop|` +
84
- `write|escrib|${DESTRUCTIVE_DEPLOY_TERMS}`;
85
-
86
- export const MUTATION_RISK_SIGNAL = withUnicodeWordBoundary(MUTATION_RISK_TERMS);
87
-
88
- // ── CONJECTURE: never-assume phrasing ────────────────────────────────────────────────
89
- // Prose stating an unverified assumption instead of a checked fact.
90
-
91
- export const CONJECTURE_SOURCES = [
92
- 'i assume',
93
- 'assuming that',
94
- 'probably',
95
- 'i guess',
96
- 'should be',
97
- "i'll default to",
98
- 'might be',
99
- 'supongo',
100
- 'asumo',
101
- 'asumiendo que',
102
- 'probablemente',
103
- 'deber[ií]a ser',
104
- 'quiz[aá]s',
105
- 'tal vez',
106
- 'creo que',
107
- 'me imagino que',
108
- ];
109
-
110
- export const CONJECTURE = withUnicodeWordBoundary(CONJECTURE_SOURCES.join('|'));
111
-
112
- // ── PERSISTENCE_VERB: an instruction to save/persist/store something ────────────────
113
- // Used to detect a REAL persistence instruction nearby a memory-dependency phrase (see
114
- // no-memory-dependency), so a bare mention of a memory phrase with a genuine "save this
115
- // to <file>" nearby is not flagged as depending on model memory.
116
-
117
- export const PERSISTENCE_VERB_SOURCES = [
118
- 'save|persist|store',
119
- 'guarda(?:l[oa])?|guard[aá]|persist[eií]|persistir|persistido|almacena|almacenar',
120
- 'escrib(?:e|í|i) .{0,20}en|escribir .{0,20}en|write .{0,20}(?:to|in)',
121
- 'anota|anotar|registra|registrar',
122
- ];
123
-
124
- export const PERSISTENCE_VERB = new RegExp(
125
- PERSISTENCE_VERB_SOURCES.join('|'),
126
- 'iu',
127
- );
1
+ // signals.mjs — central catalog of LEXICAL prose signals, each covering Spanish AND
2
+ // English, for every gate that matches free text (briefs, delegation prompts, written
3
+ // content). Written once here; gates import instead of keeping their own monolingual or
4
+ // partially-bilingual copy.
5
+ //
6
+ // ── Scope: lexical only ──────────────────────────────────────────────────────────────
7
+ // A gate whose real signal lives in the ACTION (a shell command, a tool name, a file
8
+ // path) keeps using structural detection for that part — this module does not replace
9
+ // toolInGroups/writtenPathOf/etc., and a gate should prefer the structural check first
10
+ // wherever the signal is genuinely structural. This module exists for the remaining
11
+ // case: prose written by a human or a model, in either language, that a gate must
12
+ // recognize regardless of which language it was written in.
13
+ //
14
+ // ── Honest limitation ────────────────────────────────────────────────────────────────
15
+ // Coverage is ES + EN only. A prompt written in a third language (or a language-neutral
16
+ // paraphrase using none of these terms) is NOT covered by any pattern here — this module
17
+ // closes the specific ES/EN gap the gates had, it does not claim language-agnostic
18
+ // detection. Where the underlying signal can instead be read off the action itself
19
+ // (structural), that stays the primary defense; lexical matching is the fallback layer
20
+ // for prose that never had a structural signal to read.
21
+ //
22
+ // ── Word-boundary and accent handling ────────────────────────────────────────────────
23
+ // `withUnicodeWordBoundary` uses lookarounds over `\p{L}|\p{N}|_` (not `\b`, which does
24
+ // not treat accented letters as word characters) so short/dangerous terms ("auth",
25
+ // "prod") require a full word match and do not fire inside "autor" or "producto".
26
+ // Spanish terms that carry an accent in correct spelling (contraseña, sesión, migración)
27
+ // are matched with an optional accent (`sesi[oó]n`) because normalized/ASCII input is
28
+ // common in prompts and commit messages.
29
+
30
+ /** Unicode-aware word boundary: matches only when the alternation is not adjacent to
31
+ * another letter/digit/underscore, so short terms cannot match as a substring of a
32
+ * longer unrelated word. */
33
+ export function withUnicodeWordBoundary(alternatives) {
34
+ return new RegExp(
35
+ `(?<![\\p{L}\\p{N}_])(?:${alternatives})(?![\\p{L}\\p{N}_])`,
36
+ 'iu',
37
+ );
38
+ }
39
+
40
+ // ── RISK_SIGNAL: high-impact mutation area (money / auth / destructive-deploy) ───────
41
+ // Used where a gate must recognize that a request touches a sensitive domain,
42
+ // regardless of whether it was phrased in Spanish or English. Three sub-domains kept
43
+ // separate so a gate can compose only the ones it needs, and one combined export for
44
+ // gates that want the whole class.
45
+
46
+ const MONEY_TERMS =
47
+ 'money|payment|pago|dinero|cobro|charge|invoice|factura|precio|price|saldo|balance|amount|monto';
48
+
49
+ const AUTH_TERMS =
50
+ 'auth|autenticaci[oó]n|authentication|credencial|credential|contrase[nñ]a|password|' +
51
+ 'token|sesi[oó]n|session|permiso|permission|role|rol';
52
+
53
+ const DESTRUCTIVE_DEPLOY_TERMS =
54
+ 'deploy|desplieg(?:a|ue|ar)?|producci[oó]n|production|prod|borrar|delete|drop|truncate|' +
55
+ 'migraci[oó]n|migration|migrate|migr(?:a|ar)|eliminar';
56
+
57
+ export const MONEY_SIGNAL = withUnicodeWordBoundary(MONEY_TERMS);
58
+ export const AUTH_SIGNAL = withUnicodeWordBoundary(AUTH_TERMS);
59
+ export const DESTRUCTIVE_DEPLOY_SIGNAL = withUnicodeWordBoundary(
60
+ DESTRUCTIVE_DEPLOY_TERMS,
61
+ );
62
+
63
+ /** Combined high-impact mutation signal: money OR auth OR a destructive/deploy verb. */
64
+ export const RISK_SIGNAL = withUnicodeWordBoundary(
65
+ `${MONEY_TERMS}|${AUTH_TERMS}|${DESTRUCTIVE_DEPLOY_TERMS}`,
66
+ );
67
+
68
+ /** RISK_SIGNAL as plain regex-source alternatives (no boundary/flags), for gates that
69
+ * build their own combined pattern (e.g. joined with other domain-specific sources). */
70
+ export const RISK_SIGNAL_SOURCES = [
71
+ MONEY_TERMS,
72
+ AUTH_TERMS,
73
+ DESTRUCTIVE_DEPLOY_TERMS,
74
+ ];
75
+
76
+ // ── MUTATION_RISK_SIGNAL: broader than RISK_SIGNAL, used only to VOID an exemption ──
77
+ // Several gates let a whitelisted read-only subagent name (or an exempt-query verb)
78
+ // skip their check but only when the prompt itself carries no mutation risk. That
79
+ // exemption-voiding check is deliberately broader than RISK_SIGNAL: it also counts a
80
+ // bare "data"/"datos"/"write"/"escrib*" mention, because the name/label is unverified
81
+ // and a false positive here only means the underlying check still runs (cheap), while a
82
+ // false negative would let a real mutator dodge the check via a trusted-sounding label.
83
+ // Kept as a distinct export from RISK_SIGNAL because the two answer different
84
+ // questions (RISK_SIGNAL: "is this domain high-impact?" vs MUTATION_RISK_SIGNAL: "is it
85
+ // unsafe to trust this label/verb at all?").
86
+ const MUTATION_RISK_TERMS =
87
+ `${MONEY_TERMS}|${AUTH_TERMS}|credencial|credential|data|datos|borrar|delete|drop|` +
88
+ `write|escrib|${DESTRUCTIVE_DEPLOY_TERMS}`;
89
+
90
+ export const MUTATION_RISK_SIGNAL =
91
+ withUnicodeWordBoundary(MUTATION_RISK_TERMS);
92
+
93
+ // ── CONJECTURE: never-assume phrasing ────────────────────────────────────────────────
94
+ // Prose stating an unverified assumption instead of a checked fact.
95
+
96
+ export const CONJECTURE_SOURCES = [
97
+ 'i assume',
98
+ 'assuming that',
99
+ 'probably',
100
+ 'i guess',
101
+ 'should be',
102
+ "i'll default to",
103
+ 'might be',
104
+ 'supongo',
105
+ 'asumo',
106
+ 'asumiendo que',
107
+ 'probablemente',
108
+ 'deber[ií]a ser',
109
+ 'quiz[aá]s',
110
+ 'tal vez',
111
+ 'creo que',
112
+ 'me imagino que',
113
+ ];
114
+
115
+ export const CONJECTURE = withUnicodeWordBoundary(CONJECTURE_SOURCES.join('|'));
116
+
117
+ // ── PERSISTENCE_VERB: an instruction to save/persist/store something ────────────────
118
+ // Used to detect a REAL persistence instruction nearby a memory-dependency phrase (see
119
+ // no-memory-dependency), so a bare mention of a memory phrase with a genuine "save this
120
+ // to <file>" nearby is not flagged as depending on model memory.
121
+
122
+ export const PERSISTENCE_VERB_SOURCES = [
123
+ 'save|persist|store',
124
+ 'guarda(?:l[oa])?|guard[aá]|persist[eií]|persistir|persistido|almacena|almacenar',
125
+ 'escrib(?:e|í|i) .{0,20}en|escribir .{0,20}en|write .{0,20}(?:to|in)',
126
+ 'anota|anotar|registra|registrar',
127
+ ];
128
+
129
+ export const PERSISTENCE_VERB = new RegExp(
130
+ PERSISTENCE_VERB_SOURCES.join('|'),
131
+ 'iu',
132
+ );
133
+
134
+ // ── BUILD_INTENT: a request to CREATE a tool/helper (reuse-before-build) ─────────────
135
+ // A verb of creation FOLLOWED SHORTLY BY a "buildable thing" noun, in Spanish AND English.
136
+ // The old pattern was English-only (write|create|build + script|gate|hook...), so a Spanish
137
+ // brief ("armá un verificador", "hacé un helper") never tripped the reuse check.
138
+ //
139
+ // Split into two small regexes (a creation verb, and a tool noun) checked with a bounded gap
140
+ // between them, rather than one large alternation. One monolithic pattern tripped the linter's
141
+ // regex-complexity budget; two short ones stay well under it and read more clearly. The gap
142
+ // (up to ~24 chars) lets an article/adjective sit between them ("build a new tool", "armá un
143
+ // verificador nuevo") while keeping the noun anchored to the verb so "write a report" /
144
+ // "escribí un correo" (non-tool nouns) do not match. Each side is word-boundary wrapped.
145
+ const BUILD_VERB = withUnicodeWordBoundary(
146
+ 'write|create|build|implement|add|make|' +
147
+ 'escribe|escribi|escribir|crea|construye|construir|implementa|implementar|' +
148
+ 'agrega|arma|hace|genera|generar|' +
149
+ // voseo / accented imperative forms spelled as literals (no [aá] class, which the linter
150
+ // counts against regex complexity): armá, hacé, creá, agregá, generá, escribí, construí.
151
+ 'armá|hacé|creá|agregá|generá|escribí|construí',
152
+ );
153
+ const BUILDABLE_NOUN = withUnicodeWordBoundary(
154
+ 'scripts?|verifiers?|gates?|hooks?|linters?|checkers?|tools?|helpers?|utilit(?:y|ies)|' +
155
+ 'utils?|composables?|components?|services?|wrappers?|' +
156
+ 'verificador(?:es)?|chequeador(?:es)?|herramientas?|utilidades?|ayudantes?|' +
157
+ 'envoltorios?|componentes?|servicios?',
158
+ );
159
+ const BUILD_INTENT_MAX_GAP = 24;
160
+
161
+ /** Whether the text expresses intent to CREATE a tool/helper (a creation verb closely
162
+ * followed by a tool noun), in Spanish or English. A method, not a bare regex, so each side
163
+ * stays a small pattern and the "verb → noun proximity" rule is explicit. */
164
+ export function isBuildIntent(text) {
165
+ const verbMatch = BUILD_VERB.exec(text);
166
+ if (!verbMatch) return false;
167
+ const after = text.slice(
168
+ verbMatch.index + verbMatch[0].length,
169
+ verbMatch.index + verbMatch[0].length + BUILD_INTENT_MAX_GAP,
170
+ );
171
+ return BUILDABLE_NOUN.test(after);
172
+ }