@mmnto/cli 1.51.0 → 1.52.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.
@@ -0,0 +1,472 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+ import { knownGateEvents, TotemError } from '@mmnto/totem';
7
+ import { ejectCommand } from './eject.js';
8
+ import { gateInstallCommand, resolveGateEvents } from './gate.js';
9
+ import { commandInstallsGate, GATE_WRAPPER_REL, installGates } from './gate-install.js';
10
+ import { initCommand } from './init.js';
11
+ import { CLAUDE_GATE_WRAPPER } from './init-templates.js';
12
+ /**
13
+ * CLI-seam tests for `totem gate install` + the parameterized gate wrapper
14
+ * (PR-C, mmnto-ai/totem#2048).
15
+ *
16
+ * Locks the invariants in spec 2048.md §"Invariants the tests lock":
17
+ * - idempotent merge (re-run is a no-op)
18
+ * - `--all` enumerates `knownGateEvents()`; unknown `--<name>` / `--gates=`
19
+ * member fails loud (no default-install)
20
+ * - the wrapper's disposition → exit-code map, including the LOAD-BEARING
21
+ * empty-subsystem pass-through and the applicable-gate-source-broken
22
+ * fail-closed
23
+ * - `eject` removes the gate entry (parity with install)
24
+ * - `init --gates=` routes through the SAME installer as the verb
25
+ *
26
+ * The engine itself (allow/deny/no-file/side-effect-free) is covered by
27
+ * `@mmnto/totem`'s gate-engine.test.ts and is NOT duplicated here.
28
+ */
29
+ function makeTmpDir() {
30
+ // `.native` expands Windows 8.3 short names so process.cwd()/realpath agree.
31
+ return fs.realpathSync.native(fs.mkdtempSync(path.join(os.tmpdir(), 'totem-gate-install-')));
32
+ }
33
+ function readSettings(cwd) {
34
+ const raw = fs.readFileSync(path.join(cwd, '.claude', 'settings.json'), 'utf-8');
35
+ return JSON.parse(raw);
36
+ }
37
+ function preToolUseEntries(cwd) {
38
+ const parsed = readSettings(cwd);
39
+ const hooks = (parsed.hooks ?? {});
40
+ return (hooks.PreToolUse ?? []);
41
+ }
42
+ /** Count Write|Edit PreToolUse entries whose command references a given gate. */
43
+ function gateEntryCount(cwd, event) {
44
+ return preToolUseEntries(cwd).filter((e) => e.matcher === 'Write|Edit' &&
45
+ Array.isArray(e.hooks) &&
46
+ e.hooks.some((h) => {
47
+ const cmd = typeof h === 'string' ? h : (h?.command ?? '');
48
+ // Collision-safe exact --event match (tier-independent).
49
+ return commandInstallsGate(cmd, event);
50
+ })).length;
51
+ }
52
+ /** The single baked command string for a gate (or undefined if none/many). */
53
+ function gateCommandFor(cwd, event) {
54
+ const cmds = [];
55
+ for (const e of preToolUseEntries(cwd)) {
56
+ if (e.matcher !== 'Write|Edit' || !Array.isArray(e.hooks))
57
+ continue;
58
+ for (const h of e.hooks) {
59
+ const cmd = typeof h === 'string' ? h : (h?.command ?? '');
60
+ if (commandInstallsGate(cmd, event))
61
+ cmds.push(cmd);
62
+ }
63
+ }
64
+ return cmds.length === 1 ? cmds[0] : undefined;
65
+ }
66
+ describe('installGates / gate install (settings merge)', () => {
67
+ let cwd;
68
+ let originalCwd;
69
+ beforeEach(() => {
70
+ cwd = makeTmpDir();
71
+ originalCwd = process.cwd();
72
+ process.chdir(cwd);
73
+ });
74
+ afterEach(() => {
75
+ process.chdir(originalCwd);
76
+ fs.rmSync(cwd, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
77
+ });
78
+ it('scaffolds the wrapper and merges one PreToolUse entry per gate', () => {
79
+ const results = installGates(cwd, ['freeze-check']);
80
+ // wrapper scaffold + one entry merge
81
+ expect(results.some((r) => r.file === GATE_WRAPPER_REL && r.action === 'created')).toBe(true);
82
+ expect(fs.existsSync(path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs'))).toBe(true);
83
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
84
+ });
85
+ it('is idempotent: a second install of the same gate is a no-op', () => {
86
+ installGates(cwd, ['freeze-check']);
87
+ const before = JSON.stringify(readSettings(cwd));
88
+ const second = installGates(cwd, ['freeze-check']);
89
+ const entryResult = second.find((r) => r.event === 'freeze-check');
90
+ expect(entryResult?.action).toBe('skipped');
91
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
92
+ expect(JSON.stringify(readSettings(cwd))).toBe(before);
93
+ });
94
+ it('keys idempotency on the per-gate command substring (distinct gates → distinct entries)', () => {
95
+ // freeze-check installed; a hypothetical future gate keyed on a different
96
+ // --event substring produces a SECOND entry rather than colliding.
97
+ installGates(cwd, ['freeze-check']);
98
+ installGates(cwd, ['some-future-gate']);
99
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
100
+ expect(gateEntryCount(cwd, 'some-future-gate')).toBe(1);
101
+ // Two distinct Write|Edit gate entries under one matcher.
102
+ const gateEntries = preToolUseEntries(cwd).filter((e) => e.matcher === 'Write|Edit' &&
103
+ Array.isArray(e.hooks) &&
104
+ e.hooks.some((h) => {
105
+ const cmd = typeof h === 'string' ? h : (h?.command ?? '');
106
+ return cmd.includes('gate-wrapper.cjs --event ');
107
+ }));
108
+ expect(gateEntries.length).toBe(2);
109
+ });
110
+ it('preserves a pre-existing user PreToolUse entry when merging', () => {
111
+ fs.mkdirSync(path.join(cwd, '.claude'), { recursive: true });
112
+ fs.writeFileSync(path.join(cwd, '.claude', 'settings.json'), JSON.stringify({
113
+ hooks: {
114
+ PreToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'my-hook' }] }],
115
+ },
116
+ }));
117
+ installGates(cwd, ['freeze-check']);
118
+ const entries = preToolUseEntries(cwd);
119
+ expect(entries.some((e) => e.matcher === 'Bash')).toBe(true);
120
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
121
+ });
122
+ it('gateInstallCommand --all installs every known gate', async () => {
123
+ await gateInstallCommand({ all: true });
124
+ for (const event of knownGateEvents()) {
125
+ expect(gateEntryCount(cwd, event)).toBe(1);
126
+ }
127
+ });
128
+ });
129
+ // ─── FIX A: tier-AWARE upsert (update in place, never silent no-op/dup) ──
130
+ //
131
+ // Locks the tier-update behavior so it can never regress to either failure
132
+ // mode the pre-fix tier-INDEPENDENT probe had: (a) a tier switch silently
133
+ // dropped as a "skipped — no change" no-op, or (b) a second duplicate entry
134
+ // for the same gate. The invariant: EXACTLY ONE entry per gate, ever, and the
135
+ // baked tier flag flips in place on a different-tier re-install.
136
+ describe('installGates tier-aware upsert (FIX A)', () => {
137
+ let cwd;
138
+ let originalCwd;
139
+ beforeEach(() => {
140
+ cwd = makeTmpDir();
141
+ originalCwd = process.cwd();
142
+ process.chdir(cwd);
143
+ });
144
+ afterEach(() => {
145
+ process.chdir(originalCwd);
146
+ fs.rmSync(cwd, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
147
+ });
148
+ it('default(strict) then re-install --strict → skipped, one entry, command keeps --strict', () => {
149
+ installGates(cwd, ['freeze-check']); // default tier === strict
150
+ const second = installGates(cwd, ['freeze-check'], 'strict');
151
+ const entryResult = second.find((r) => r.event === 'freeze-check');
152
+ expect(entryResult?.action).toBe('skipped');
153
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
154
+ expect(gateCommandFor(cwd, 'freeze-check')).toContain('--strict');
155
+ });
156
+ it('--pilot then --strict → updated, ONE entry, command flips to --strict (not --pilot)', () => {
157
+ installGates(cwd, ['freeze-check'], 'pilot');
158
+ expect(gateCommandFor(cwd, 'freeze-check')).toContain('--pilot');
159
+ const switched = installGates(cwd, ['freeze-check'], 'strict');
160
+ const entryResult = switched.find((r) => r.event === 'freeze-check');
161
+ expect(entryResult?.action).toBe('updated');
162
+ // Exactly one entry — no duplicate created by the tier switch.
163
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
164
+ const cmd = gateCommandFor(cwd, 'freeze-check');
165
+ expect(cmd).toContain('--strict');
166
+ expect(cmd).not.toContain('--pilot');
167
+ });
168
+ it('--strict then --pilot → updated back to --pilot, one entry', () => {
169
+ installGates(cwd, ['freeze-check'], 'strict');
170
+ const switched = installGates(cwd, ['freeze-check'], 'pilot');
171
+ const entryResult = switched.find((r) => r.event === 'freeze-check');
172
+ expect(entryResult?.action).toBe('updated');
173
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
174
+ const cmd = gateCommandFor(cwd, 'freeze-check');
175
+ expect(cmd).toContain('--pilot');
176
+ expect(cmd).not.toContain('--strict');
177
+ });
178
+ it('init --gates= routes the tier through the same upsert (default then --pilot re-init)', async () => {
179
+ // First init at default (strict).
180
+ await initCommand({ bare: true, gates: 'freeze-check' });
181
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
182
+ expect(gateCommandFor(cwd, 'freeze-check')).toContain('--strict');
183
+ // Re-init with --pilot → routes through the same installGates upsert:
184
+ // one entry at the right (pilot) tier, NOT a duplicate.
185
+ await initCommand({ bare: true, gates: 'freeze-check', pilot: true });
186
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
187
+ const cmd = gateCommandFor(cwd, 'freeze-check');
188
+ expect(cmd).toContain('--pilot');
189
+ expect(cmd).not.toContain('--strict');
190
+ });
191
+ it('coexists with a pre-existing PreWriteShield Write|Edit entry (the post-init layout)', () => {
192
+ // Realistic post-`totem init` state: a PreWriteShield hook already sits in
193
+ // committed settings.json under the SAME 'Write|Edit' matcher the gate uses.
194
+ // The upsert must install the gate as a DISTINCT entry and, on a tier
195
+ // switch, update ONLY the gate entry — never touching PreWriteShield.
196
+ fs.mkdirSync(path.join(cwd, '.claude'), { recursive: true });
197
+ const shieldCommand = 'node .claude/hooks/PreWriteShield.cjs';
198
+ fs.writeFileSync(path.join(cwd, '.claude', 'settings.json'), JSON.stringify({
199
+ hooks: {
200
+ PreToolUse: [
201
+ { matcher: 'Write|Edit', hooks: [{ type: 'command', command: shieldCommand }] },
202
+ ],
203
+ },
204
+ }));
205
+ const hasShield = () => preToolUseEntries(cwd).some((e) => e.matcher === 'Write|Edit' &&
206
+ Array.isArray(e.hooks) &&
207
+ e.hooks.some((h) => {
208
+ const c = typeof h === 'string' ? h : (h?.command ?? '');
209
+ return c === shieldCommand;
210
+ }));
211
+ // Install the gate at pilot → a distinct second Write|Edit entry.
212
+ installGates(cwd, ['freeze-check'], 'pilot');
213
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
214
+ expect(gateCommandFor(cwd, 'freeze-check')).toContain('--pilot');
215
+ expect(hasShield()).toBe(true);
216
+ expect(preToolUseEntries(cwd).filter((e) => e.matcher === 'Write|Edit').length).toBe(2);
217
+ // Tier switch → updates ONLY the gate entry in place; PreWriteShield stays.
218
+ const switched = installGates(cwd, ['freeze-check'], 'strict');
219
+ expect(switched.find((r) => r.event === 'freeze-check')?.action).toBe('updated');
220
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
221
+ const cmd2 = gateCommandFor(cwd, 'freeze-check');
222
+ expect(cmd2).toContain('--strict');
223
+ expect(cmd2).not.toContain('--pilot');
224
+ expect(hasShield()).toBe(true);
225
+ expect(preToolUseEntries(cwd).filter((e) => e.matcher === 'Write|Edit').length).toBe(2);
226
+ });
227
+ });
228
+ describe('resolveGateEvents (registry-driven validation)', () => {
229
+ it('--all enumerates knownGateEvents()', async () => {
230
+ expect(await resolveGateEvents({ all: true })).toEqual(knownGateEvents());
231
+ });
232
+ it('a known --<name> resolves to itself', async () => {
233
+ expect(await resolveGateEvents({ name: 'freeze-check' })).toEqual(['freeze-check']);
234
+ });
235
+ it('an unknown --<name> fails loud (never default-install)', async () => {
236
+ await expect(resolveGateEvents({ name: 'made-up-gate' })).rejects.toBeInstanceOf(TotemError);
237
+ await expect(resolveGateEvents({ name: 'made-up-gate' })).rejects.toThrow(/unknown gate/i);
238
+ });
239
+ it('no --all and no --<name> fails loud (no default-install)', async () => {
240
+ await expect(resolveGateEvents({})).rejects.toThrow(/no gate selected/i);
241
+ });
242
+ });
243
+ // ─── The parameterized wrapper's disposition → exit-code map ───────────
244
+ //
245
+ // We render the wrapper template to a temp dir and drive it via stdin with
246
+ // synthetic PreToolUse envelopes. A stub `node_modules/@mmnto/cli/dist/index.js`
247
+ // stands in for the local CLI: it echoes a verdict / exit code driven by env
248
+ // vars so the test controls each disposition deterministically WITHOUT the
249
+ // real engine (the engine is covered by gate-engine.test.ts).
250
+ describe('gate-wrapper.cjs disposition → exit code', () => {
251
+ let cwd;
252
+ beforeEach(() => {
253
+ cwd = makeTmpDir();
254
+ // Render the wrapper exactly as `installGates` would.
255
+ fs.mkdirSync(path.join(cwd, '.claude', 'hooks'), { recursive: true });
256
+ fs.writeFileSync(path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs'), CLAUDE_GATE_WRAPPER);
257
+ });
258
+ afterEach(() => {
259
+ fs.rmSync(cwd, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
260
+ });
261
+ /** Install a stub local CLI that emits a controlled verdict / exit code. */
262
+ function writeStubCli(opts) {
263
+ const distDir = path.join(cwd, 'node_modules', '@mmnto', 'cli', 'dist');
264
+ fs.mkdirSync(distDir, { recursive: true });
265
+ const verdictJson = opts.verdict === undefined ? '' : JSON.stringify(opts.verdict);
266
+ const exitCode = opts.exit ?? 0;
267
+ // CommonJS stub (the wrapper invokes via `node <path>`); .js is fine here
268
+ // because there is no package.json type:module in the temp dir.
269
+ const stub = [
270
+ '"use strict";',
271
+ `const out = ${JSON.stringify(verdictJson)};`,
272
+ 'if (out) process.stdout.write(out + "\\n");',
273
+ `process.exit(${exitCode});`,
274
+ '',
275
+ ].join('\n');
276
+ fs.writeFileSync(path.join(distDir, 'index.js'), stub);
277
+ }
278
+ /** Run the rendered wrapper with the given envelope + baked extra args. */
279
+ function runWrapper(envelope, extraArgs = []) {
280
+ const wrapperPath = path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs');
281
+ const res = spawnSync(process.execPath, [wrapperPath, '--event', 'freeze-check', ...extraArgs], {
282
+ cwd,
283
+ input: JSON.stringify(envelope),
284
+ encoding: 'utf-8',
285
+ timeout: 30000,
286
+ });
287
+ return { status: res.status, stderr: res.stderr ?? '' };
288
+ }
289
+ const EDIT_NO_SUBSYSTEM = { tool_name: 'Edit', tool_input: { file_path: 'src/foo.ts' } };
290
+ const DECLARED = { tool_name: 'Edit', tool_input: { subsystem: 'rule-compilation' } };
291
+ it('allow → exit 0 (silent)', () => {
292
+ writeStubCli({ verdict: { disposition: 'allow', reason: 'ok', provenance: {} }, exit: 0 });
293
+ const { status } = runWrapper(DECLARED);
294
+ expect(status).toBe(0);
295
+ });
296
+ it('warn → exit 0 + reason/provenance to stderr (advisory, never blocks)', () => {
297
+ writeStubCli({
298
+ verdict: { disposition: 'warn', reason: 'heads up', provenance: { source: 's' } },
299
+ exit: 0,
300
+ });
301
+ const { status, stderr } = runWrapper(DECLARED);
302
+ expect(status).toBe(0);
303
+ expect(stderr).toMatch(/warn/i);
304
+ expect(stderr).toContain('heads up');
305
+ });
306
+ it('deny → exit 2 under --strict (default) + stderr', () => {
307
+ writeStubCli({
308
+ verdict: { disposition: 'deny', reason: 'frozen', provenance: { matched: 'x' } },
309
+ exit: 0,
310
+ });
311
+ const { status, stderr } = runWrapper(DECLARED);
312
+ expect(status).toBe(2);
313
+ expect(stderr).toContain('frozen');
314
+ });
315
+ it('deny → exit 0 under --pilot + stderr (advisory tier)', () => {
316
+ writeStubCli({
317
+ verdict: { disposition: 'deny', reason: 'frozen', provenance: {} },
318
+ exit: 0,
319
+ });
320
+ const { status, stderr } = runWrapper(DECLARED, ['--pilot']);
321
+ expect(status).toBe(0);
322
+ expect(stderr).toContain('frozen');
323
+ });
324
+ it('no-declared-subsystem Edit → exit 0 pass-through (gate NOT invoked)', () => {
325
+ // Stub emits a DENY; if the wrapper invoked it, exit would be 2. The
326
+ // empty-subsystem guardrail must short-circuit to exit 0 WITHOUT shelling
327
+ // out (LOAD-BEARING: protects every ordinary edit from being blocked).
328
+ writeStubCli({ verdict: { disposition: 'deny', reason: 'should not run', provenance: {} } });
329
+ const { status } = runWrapper(EDIT_NO_SUBSYSTEM);
330
+ expect(status).toBe(0);
331
+ });
332
+ it('applicable-gate-source-broken (non-zero gate check) → exit 2 fail-closed', () => {
333
+ // A declared subsystem IS present (gate applies) and `gate check` exits
334
+ // non-zero (corrupt freeze.json etc.) → fail-closed block.
335
+ writeStubCli({ exit: 1 });
336
+ const { status, stderr } = runWrapper(DECLARED);
337
+ expect(status).toBe(2);
338
+ expect(stderr).toMatch(/fail-closed/i);
339
+ });
340
+ it('malformed stdin envelope → exit 0 fail-soft', () => {
341
+ writeStubCli({ verdict: { disposition: 'deny' } });
342
+ const wrapperPath = path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs');
343
+ const res = spawnSync(process.execPath, [wrapperPath, '--event', 'freeze-check'], {
344
+ cwd,
345
+ input: '{ not valid json',
346
+ encoding: 'utf-8',
347
+ timeout: 30000,
348
+ });
349
+ expect(res.status).toBe(0);
350
+ });
351
+ // ─── FIX 2: applicable gate but no local CLI dist → fail-closed ────────
352
+ it('applicable gate but the local CLI dist is missing → exit 2 fail-closed (FIX 2)', () => {
353
+ // Deliberately do NOT write the stub CLI. A declared subsystem makes the
354
+ // gate APPLY, but @mmnto/cli is not resolvable. freeze-check has no
355
+ // commit-time hard floor, so an applicable-but-unevaluable gate must fail
356
+ // closed (exit 2) rather than silently allow (exit 0).
357
+ const { status, stderr } = runWrapper(DECLARED);
358
+ expect(status).toBe(2);
359
+ expect(stderr).toMatch(/not resolvable|not installed|failing closed/i);
360
+ });
361
+ it('gate check exits 0 with unparseable stdout → exit 2 fail-closed', () => {
362
+ // verdict undefined → stub emits nothing; the wrapper cannot parse a
363
+ // verdict from a 0-exit gate → fail-closed.
364
+ writeStubCli({ verdict: undefined, exit: 0 });
365
+ const { status, stderr } = runWrapper(DECLARED);
366
+ expect(status).toBe(2);
367
+ expect(stderr).toMatch(/unparseable|parse/i);
368
+ });
369
+ it('well-formed verdict with an unknown disposition → exit 2 fail-closed', () => {
370
+ writeStubCli({ verdict: { disposition: 'bogus', reason: 'x', provenance: {} }, exit: 0 });
371
+ const { status, stderr } = runWrapper(DECLARED);
372
+ expect(status).toBe(2);
373
+ expect(stderr).toMatch(/unknown disposition/i);
374
+ });
375
+ // ─── FIX 3: valid JSON but non-object stdin → fail-soft ────────────────
376
+ it('stdin is valid JSON but non-object (the bytes `null`) → exit 0 fail-soft (FIX 3)', () => {
377
+ writeStubCli({ verdict: { disposition: 'deny' } });
378
+ const wrapperPath = path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs');
379
+ const res = spawnSync(process.execPath, [wrapperPath, '--event', 'freeze-check'], {
380
+ cwd,
381
+ input: 'null',
382
+ encoding: 'utf-8',
383
+ timeout: 30000,
384
+ });
385
+ expect(res.status).toBe(0);
386
+ });
387
+ });
388
+ // ─── eject parity ──────────────────────────────────────────────────────
389
+ describe('eject removes the gate entry (parity with install)', () => {
390
+ let cwd;
391
+ let originalCwd;
392
+ beforeEach(() => {
393
+ cwd = makeTmpDir();
394
+ originalCwd = process.cwd();
395
+ process.chdir(cwd);
396
+ fs.mkdirSync(path.join(cwd, '.git', 'hooks'), { recursive: true });
397
+ });
398
+ afterEach(() => {
399
+ process.chdir(originalCwd);
400
+ fs.rmSync(cwd, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
401
+ });
402
+ it('scrubs the gate PreToolUse entry and the wrapper script on eject', async () => {
403
+ installGates(cwd, ['freeze-check']);
404
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
405
+ expect(fs.existsSync(path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs'))).toBe(true);
406
+ await ejectCommand({ force: true });
407
+ expect(fs.existsSync(path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs'))).toBe(false);
408
+ // Settings file may be removed entirely (if it only held the gate entry)
409
+ // or scrubbed of the gate entry — either way no gate entry survives.
410
+ if (fs.existsSync(path.join(cwd, '.claude', 'settings.json'))) {
411
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(0);
412
+ }
413
+ });
414
+ it('preserves a user PreToolUse entry while scrubbing the gate entry', async () => {
415
+ fs.mkdirSync(path.join(cwd, '.claude'), { recursive: true });
416
+ fs.writeFileSync(path.join(cwd, '.claude', 'settings.json'), JSON.stringify({
417
+ hooks: { PreToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'mine' }] }] },
418
+ }));
419
+ installGates(cwd, ['freeze-check']);
420
+ await ejectCommand({ force: true });
421
+ expect(fs.existsSync(path.join(cwd, '.claude', 'settings.json'))).toBe(true);
422
+ const entries = preToolUseEntries(cwd);
423
+ expect(entries.some((e) => e.matcher === 'Bash')).toBe(true);
424
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(0);
425
+ });
426
+ });
427
+ // ─── init --gates= routes through the SAME installer ───────────────────
428
+ describe('init --gates= routes through the shared installer', () => {
429
+ let cwd;
430
+ let originalCwd;
431
+ beforeEach(() => {
432
+ cwd = makeTmpDir();
433
+ originalCwd = process.cwd();
434
+ process.chdir(cwd);
435
+ });
436
+ afterEach(() => {
437
+ process.chdir(originalCwd);
438
+ fs.rmSync(cwd, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
439
+ });
440
+ it('init --gates=freeze-check installs the same entry installGates would', async () => {
441
+ await initCommand({ bare: true, gates: 'freeze-check' });
442
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(1);
443
+ expect(fs.existsSync(path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs'))).toBe(true);
444
+ });
445
+ it('init --gates=all installs every known gate', async () => {
446
+ await initCommand({ bare: true, gates: 'all' });
447
+ for (const event of knownGateEvents()) {
448
+ expect(gateEntryCount(cwd, event)).toBe(1);
449
+ }
450
+ });
451
+ it('init --gates= with an unknown member fails loud', async () => {
452
+ await expect(initCommand({ bare: true, gates: 'made-up-gate' })).rejects.toThrow(/unknown gate/i);
453
+ });
454
+ // ─── FIX 4: empty --gates= selection fails loud, installs nothing ──────
455
+ it('init --gates=, (empty after parse) throws GATE_INVALID and writes no gate wrapper/entry', async () => {
456
+ const err = await initCommand({ bare: true, gates: ',' }).catch((e) => e);
457
+ expect(err).toBeInstanceOf(TotemError);
458
+ expect(err.code).toBe('GATE_INVALID');
459
+ // No orphan wrapper scaffolded, no PreToolUse gate entry merged.
460
+ expect(fs.existsSync(path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs'))).toBe(false);
461
+ if (fs.existsSync(path.join(cwd, '.claude', 'settings.json'))) {
462
+ expect(gateEntryCount(cwd, 'freeze-check')).toBe(0);
463
+ }
464
+ });
465
+ it('init --gates= (whitespace-only) throws GATE_INVALID and writes no gate wrapper/entry', async () => {
466
+ const err = await initCommand({ bare: true, gates: ' ' }).catch((e) => e);
467
+ expect(err).toBeInstanceOf(TotemError);
468
+ expect(err.code).toBe('GATE_INVALID');
469
+ expect(fs.existsSync(path.join(cwd, '.claude', 'hooks', 'gate-wrapper.cjs'))).toBe(false);
470
+ });
471
+ });
472
+ //# sourceMappingURL=gate-install.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate-install.test.js","sourceRoot":"","sources":["../../src/commands/gate-install.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;;;;;;;;;;;;;;;GAgBG;AAEH,SAAS,UAAU;IACjB,6EAA6E;IAC7E,OAAO,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IACpC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAC;IAC9D,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,CAAwD,CAAC;AACzF,CAAC;AAED,iFAAiF;AACjF,SAAS,cAAc,CAAC,GAAW,EAAE,KAAa;IAChD,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,CAClC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,OAAO,KAAK,YAAY;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QACtB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA0B,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;YACrF,yDAAyD;YACzD,OAAO,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC,CAAC,CACL,CAAC,MAAM,CAAC;AACX,CAAC;AAED,8EAA8E;AAC9E,SAAS,cAAc,CAAC,GAAW,EAAE,KAAa;IAChD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,OAAO,KAAK,YAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,SAAS;QACpE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA0B,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;YACrF,IAAI,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED,QAAQ,CAAC,8CAA8C,EAAE,GAAG,EAAE;IAC5D,IAAI,GAAW,CAAC;IAChB,IAAI,WAAmB,CAAC;IAExB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,UAAU,EAAE,CAAC;QACnB,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3B,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACpD,qCAAqC;QACrC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzF,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAEjD,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC,CAAC;QACnE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wFAAwF,EAAE,GAAG,EAAE;QAChG,0EAA0E;QAC1E,mEAAmE;QACnE,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACpC,YAAY,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxD,0DAA0D;QAC1D,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,CAC/C,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,OAAO,KAAK,YAAY;YAC1B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YACtB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;gBACjB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA0B,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;gBACrF,OAAO,GAAG,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC;YACnD,CAAC,CAAC,CACL,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,EAC1C,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE;gBACL,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;aACpF;SACF,CAAC,CACH,CAAC;QACF,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,kBAAkB,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,EAAE,CAAC;YACtC,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,4EAA4E;AAC5E,8EAA8E;AAC9E,iEAAiE;AACjE,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,IAAI,GAAW,CAAC;IAChB,IAAI,WAAmB,CAAC;IAExB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,UAAU,EAAE,CAAC;QACnB,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3B,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE;QAC/F,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,0BAA0B;QAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC7D,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC,CAAC;QACnE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qFAAqF,EAAE,GAAG,EAAE;QAC7F,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAEjE,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC,CAAC;QACrE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,+DAA+D;QAC/D,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9D,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC,CAAC;QACrE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,KAAK,IAAI,EAAE;QACpG,kCAAkC;QAClC,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAElE,sEAAsE;QACtE,wDAAwD;QACxD,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qFAAqF,EAAE,GAAG,EAAE;QAC7F,2EAA2E;QAC3E,6EAA6E;QAC7E,sEAAsE;QACtE,sEAAsE;QACtE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,aAAa,GAAG,uCAAuC,CAAC;QAC9D,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,EAC1C,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE;gBACL,UAAU,EAAE;oBACV,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE;iBAChF;aACF;SACF,CAAC,CACH,CAAC;QACF,MAAM,SAAS,GAAG,GAAY,EAAE,CAC9B,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,OAAO,KAAK,YAAY;YAC1B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YACtB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;gBACjB,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA0B,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;gBACnF,OAAO,CAAC,KAAK,aAAa,CAAC;YAC7B,CAAC,CAAC,CACL,CAAC;QAEJ,kEAAkE;QAClE,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACjE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAExF,4EAA4E;QAC5E,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjF,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,CAAC,MAAM,iBAAiB,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,CAAC,MAAM,iBAAiB,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAC7F,MAAM,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,0EAA0E;AAC1E,EAAE;AACF,2EAA2E;AAC3E,iFAAiF;AACjF,6EAA6E;AAC7E,2EAA2E;AAC3E,8DAA8D;AAC9D,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACxD,IAAI,GAAW,CAAC;IAEhB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,UAAU,EAAE,CAAC;QACnB,sDAAsD;QACtD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,EAAE,mBAAmB,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,SAAS,YAAY,CAAC,IAA0C;QAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACxE,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;QAChC,0EAA0E;QAC1E,gEAAgE;QAChE,MAAM,IAAI,GAAG;YACX,eAAe;YACf,eAAe,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG;YAC7C,6CAA6C;YAC7C,gBAAgB,QAAQ,IAAI;YAC5B,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;IAED,2EAA2E;IAC3E,SAAS,UAAU,CACjB,QAAiB,EACjB,YAAsB,EAAE;QAExB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;QAC3E,MAAM,GAAG,GAAG,SAAS,CACnB,OAAO,CAAC,QAAQ,EAChB,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,SAAS,CAAC,EACtD;YACE,GAAG;YACH,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;YAC/B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;SACf,CACF,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,iBAAiB,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,CAAC;IACzF,MAAM,QAAQ,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,CAAC;IAEtF,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,YAAY,CAAC;YACX,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACjF,IAAI,EAAE,CAAC;SACR,CAAC,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,YAAY,CAAC;YACX,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;YAChF,IAAI,EAAE,CAAC;SACR,CAAC,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,YAAY,CAAC;YACX,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;YAClE,IAAI,EAAE,CAAC;SACR,CAAC,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,qEAAqE;QACrE,0EAA0E;QAC1E,uEAAuE;QACvE,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7F,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,wEAAwE;QACxE,2DAA2D;QAC3D,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;QAC3E,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE;YAChF,GAAG;YACH,KAAK,EAAE,kBAAkB;YACzB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,yEAAyE;QACzE,oEAAoE;QACpE,0EAA0E;QAC1E,uDAAuD;QACvD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,qEAAqE;QACrE,4CAA4C;QAC5C,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1F,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;QAC3E,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE;YAChF,GAAG;YACH,KAAK,EAAE,MAAM;YACb,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,0EAA0E;AAC1E,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAClE,IAAI,GAAW,CAAC;IAChB,IAAI,WAAmB,CAAC;IAExB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,UAAU,EAAE,CAAC;QACnB,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3B,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEzF,MAAM,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEpC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1F,yEAAyE;QACzE,qEAAqE;QACrE,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;YAC9D,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,EAC1C,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE;SAC5F,CAAC,CACH,CAAC;QACF,YAAY,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QAEpC,MAAM,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEpC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,0EAA0E;AAC1E,QAAQ,CAAC,mDAAmD,EAAE,GAAG,EAAE;IACjE,IAAI,GAAW,CAAC;IAChB,IAAI,WAAmB,CAAC;IAExB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,UAAU,EAAE,CAAC;QACnB,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3B,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,EAAE,CAAC;YACtC,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC9E,eAAe,CAChB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,EAAE,CAAC,yFAAyF,EAAE,KAAK,IAAI,EAAE;QACvG,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACnF,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAE,GAAkB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACtD,iEAAiE;QACjE,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1F,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;YAC9D,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,KAAK,IAAI,EAAE;QACpG,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAE,GAAkB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACtD,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -2,6 +2,37 @@ export interface GateCheckCommandOptions {
2
2
  event: string;
3
3
  payload: string;
4
4
  }
5
+ export interface GateInstallCommandOptions {
6
+ /** Install every known gate (`knownGateEvents()`). */
7
+ all?: boolean;
8
+ /** Install a single named gate (validated against `knownGateEvents()`). */
9
+ name?: string;
10
+ /**
11
+ * Install under the advisory pilot tier (deny → exit 0 + stderr). Default
12
+ * (omitted) bakes `--strict` (deny → exit 2), so a default install is
13
+ * enforcement-immune. `--strict` may be passed for explicitness.
14
+ */
15
+ pilot?: boolean;
16
+ /** Explicit strict tier (the default; accepted for symmetry with `--pilot`). */
17
+ strict?: boolean;
18
+ }
19
+ /**
20
+ * Resolve + validate the gate events to install from the CLI options. The
21
+ * `knownGateEvents()` registry is the single source of truth: `--all`
22
+ * enumerates it, and a named gate must be a member or we throw (mirror the
23
+ * engine's no-default-allow — never silently install nothing).
24
+ */
25
+ export declare function resolveGateEvents(opts: GateInstallCommandOptions): Promise<string[]>;
26
+ /**
27
+ * `totem gate install [--all | --<name>]`
28
+ *
29
+ * Idempotently merges one PreToolUse entry per selected gate into committed
30
+ * `.claude/settings.json` and scaffolds the shared parameterized wrapper to
31
+ * `.claude/hooks/gate-wrapper.cjs`. Thin caller of the shared `installGates`
32
+ * merger (the same path `init --gates=` routes through) — no second copy of
33
+ * the merge logic. Fails loud on an unknown `--<name>` (no default-install).
34
+ */
35
+ export declare function gateInstallCommand(opts: GateInstallCommandOptions): Promise<void>;
5
36
  /**
6
37
  * `totem gate check --event <type> --payload <json>`
7
38
  *
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/commands/gate.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBnF"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/commands/gate.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,sDAAsD;IACtD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAOD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA0B1F;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BvF;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBnF"}
@@ -1,5 +1,75 @@
1
1
  import * as path from 'node:path';
2
2
  import { isGlobalConfigPath, loadConfig, resolveConfigPath } from '../utils.js';
3
+ import { installGates } from './gate-install.js';
4
+ /**
5
+ * Command-specific log tag for non-error output (log.success / log.dim).
6
+ * `log.error` keeps the mandatory fixed literal `'Totem Error'` tag per the
7
+ * repo styleguide (command-specific tags for info/success/dim, the unified
8
+ * error tag for log.error).
9
+ */
10
+ const TAG = 'Gate';
11
+ /** Derive the install-time tier from the CLI options (default strict). */
12
+ function resolveTier(opts) {
13
+ return opts.pilot ? 'pilot' : 'strict';
14
+ }
15
+ /**
16
+ * Resolve + validate the gate events to install from the CLI options. The
17
+ * `knownGateEvents()` registry is the single source of truth: `--all`
18
+ * enumerates it, and a named gate must be a member or we throw (mirror the
19
+ * engine's no-default-allow — never silently install nothing).
20
+ */
21
+ export async function resolveGateEvents(opts) {
22
+ const { knownGateEvents, TotemError } = await import('@mmnto/totem');
23
+ const known = knownGateEvents();
24
+ if (opts.all) {
25
+ return known;
26
+ }
27
+ const name = opts.name?.trim();
28
+ if (!name) {
29
+ throw new TotemError('GATE_INVALID', 'No gate selected: pass --all or --<name>.', `Use --all or one of: ${known.join(', ')}.`);
30
+ }
31
+ if (!known.includes(name)) {
32
+ throw new TotemError('GATE_INVALID', `Unknown gate "${name}". Known gates: ${known.join(', ')}.`, 'Use --all or one of the known gate names.');
33
+ }
34
+ return [name];
35
+ }
36
+ /**
37
+ * `totem gate install [--all | --<name>]`
38
+ *
39
+ * Idempotently merges one PreToolUse entry per selected gate into committed
40
+ * `.claude/settings.json` and scaffolds the shared parameterized wrapper to
41
+ * `.claude/hooks/gate-wrapper.cjs`. Thin caller of the shared `installGates`
42
+ * merger (the same path `init --gates=` routes through) — no second copy of
43
+ * the merge logic. Fails loud on an unknown `--<name>` (no default-install).
44
+ */
45
+ export async function gateInstallCommand(opts) {
46
+ const events = await resolveGateEvents(opts);
47
+ const { log } = await import('../ui.js');
48
+ const cwd = process.cwd();
49
+ const tier = resolveTier(opts);
50
+ const results = installGates(cwd, events, tier);
51
+ for (const result of results) {
52
+ if (result.err) {
53
+ log.error('Totem Error', `Gate install failed for ${result.file}: ${result.err}`);
54
+ continue;
55
+ }
56
+ const label = result.event ? `${result.file} (${result.event})` : result.file;
57
+ if (result.action === 'created') {
58
+ log.success(TAG, `Scaffolded ${label}`);
59
+ }
60
+ else if (result.action === 'merged') {
61
+ log.success(TAG, `Installed gate entry into ${label}`);
62
+ }
63
+ else if (result.action === 'updated') {
64
+ // Tier switch: the one existing entry's command was rewritten in place.
65
+ log.success(TAG, `Updated ${result.event ?? result.file} tier to ${tier}`);
66
+ }
67
+ else {
68
+ // Genuine same-tier no-op — the ONLY case that prints "no change".
69
+ log.dim(TAG, `${label} already present — no change`);
70
+ }
71
+ }
72
+ }
3
73
  /**
4
74
  * `totem gate check --event <type> --payload <json>`
5
75
  *
@@ -1 +1 @@
1
- {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/commands/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOhF;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAA6B;IAClE,2EAA2E;IAC3E,yEAAyE;IACzE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;IAElE,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAClB,cAAc,EACd,wBAAwB,EACxB,uEAAuE,EACvE,GAAG,CACJ,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACnF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAExD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACvD,CAAC"}
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/commands/gate.ts"],"names":[],"mappings":"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"}