@shrkcrft/cli 0.1.0-alpha.28 → 0.1.0-alpha.29

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 (33) hide show
  1. package/dist/commands/baseline.command.d.ts.map +1 -1
  2. package/dist/commands/baseline.command.js +42 -11
  3. package/dist/commands/changelog-data.d.ts.map +1 -1
  4. package/dist/commands/changelog-data.js +20 -0
  5. package/dist/commands/check.command.d.ts.map +1 -1
  6. package/dist/commands/check.command.js +119 -11
  7. package/dist/commands/command-catalog.d.ts.map +1 -1
  8. package/dist/commands/command-catalog.js +8 -0
  9. package/dist/commands/daily.commands.d.ts.map +1 -1
  10. package/dist/commands/daily.commands.js +11 -1
  11. package/dist/commands/gates.command.d.ts +11 -1
  12. package/dist/commands/gates.command.d.ts.map +1 -1
  13. package/dist/commands/gates.command.js +53 -10
  14. package/dist/commands/generated.command.d.ts.map +1 -1
  15. package/dist/commands/generated.command.js +36 -6
  16. package/dist/commands/help.command.d.ts.map +1 -1
  17. package/dist/commands/help.command.js +64 -2
  18. package/dist/commands/policy-lint.command.d.ts.map +1 -1
  19. package/dist/commands/policy-lint.command.js +52 -10
  20. package/dist/commands/registry.command.d.ts.map +1 -1
  21. package/dist/commands/registry.command.js +34 -9
  22. package/dist/exit-codes.d.ts +27 -7
  23. package/dist/exit-codes.d.ts.map +1 -1
  24. package/dist/exit-codes.js +47 -8
  25. package/dist/gates/gate-envelope.d.ts +64 -0
  26. package/dist/gates/gate-envelope.d.ts.map +1 -0
  27. package/dist/gates/gate-envelope.js +26 -0
  28. package/dist/gates/gate-rule-view.d.ts +1 -1
  29. package/dist/gates/gate-rule-view.d.ts.map +1 -1
  30. package/dist/gates/gate-rule-view.js +5 -4
  31. package/dist/main.d.ts.map +1 -1
  32. package/dist/main.js +2 -1
  33. package/package.json +33 -33
@@ -20,11 +20,13 @@ import { spawnSync } from 'node:child_process';
20
20
  import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from 'node:fs';
21
21
  import * as nodeOs from 'node:os';
22
22
  import * as nodePath from 'node:path';
23
+ import { failsWhenEmpty } from '@shrkcrft/core';
23
24
  import { checkProvenanceHeaders, compareGeneratedTrees, scanGeneratedFiles, } from '@shrkcrft/boundaries';
24
25
  import { resolveProjectConfig } from '@shrkcrft/inspector';
25
26
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
26
27
  import { ExitCode } from "../exit-codes.js";
27
28
  import { asJson, header, kv } from "../output/format-output.js";
29
+ import { buildGateEnvelope } from "../gates/gate-envelope.js";
28
30
  const SCHEMA = 'sharkcraft.generated-drift/v1';
29
31
  const DEFAULT_TIMEOUT_MS = 120_000;
30
32
  /** Cap on the temp tree read — a runaway regen must not be read into memory whole. */
@@ -157,7 +159,7 @@ function evaluateRule(cwd, rule, excludeDirs, headersOnly) {
157
159
  const scan = scanGeneratedFiles(cwd, rule, excludeDirs);
158
160
  const severity = rule.severity ?? 'error';
159
161
  if (scan.generated.size === 0) {
160
- const failed = rule.failOnEmpty === true;
162
+ const failed = failsWhenEmpty(rule);
161
163
  return {
162
164
  rule,
163
165
  status: failed ? 'failed' : 'skipped',
@@ -235,7 +237,7 @@ async function prepare(args) {
235
237
  process.stdout.write(asJson({ schema: SCHEMA, error: loaded.message }) + '\n');
236
238
  else
237
239
  process.stderr.write(`Could not load config: ${loaded.message}\n Run \`shrk doctor\` for details.\n`);
238
- return { ok: false, code: ExitCode.NotVerified };
240
+ return { ok: false, code: ExitCode.UsageError };
239
241
  }
240
242
  const id = flagString(args, 'id');
241
243
  let rules = loaded.value.rules;
@@ -245,7 +247,7 @@ async function prepare(args) {
245
247
  const unknown = wanted.filter((w) => !known.has(w));
246
248
  if (unknown.length > 0) {
247
249
  process.stderr.write(`Unknown generated-artifact id(s): ${unknown.join(', ')}. Declared: ${[...known].join(', ') || '(none)'}\n`);
248
- return { ok: false, code: ExitCode.NotVerified };
250
+ return { ok: false, code: ExitCode.UsageError };
249
251
  }
250
252
  rules = rules.filter((r) => wanted.includes(r.id));
251
253
  }
@@ -329,9 +331,10 @@ export const generatedCheckCommand = {
329
331
  const outcomes = prep.rules.map((r) => evaluateRule(prep.cwd, r, prep.excludeDirs, headersOnly));
330
332
  const failed = outcomes.filter((o) => o.status === 'failed' || (o.status === 'error' && (o.rule.severity ?? 'error') === 'error'));
331
333
  const evaluated = outcomes.filter((o) => o.status !== 'skipped').length;
334
+ const skippedCount = outcomes.length - evaluated;
332
335
  const exit = failed.length > 0
333
336
  ? ExitCode.Failure
334
- : evaluated === 0
337
+ : evaluated === 0 || skippedCount > 0
335
338
  ? ExitCode.NotVerified
336
339
  : ExitCode.VerifiedPass;
337
340
  if (json) {
@@ -343,6 +346,33 @@ export const generatedCheckCommand = {
343
346
  skipped: outcomes.filter((o) => o.status === 'skipped').length,
344
347
  verdict: failed.length > 0 ? 'errors' : evaluated === 0 ? 'not-verified' : 'pass',
345
348
  diagnostics: prep.planeDiagnostics,
349
+ gate: buildGateEnvelope('generated check', exit, outcomes.map((o) => ({
350
+ id: o.rule.id,
351
+ type: 'generated',
352
+ status: o.status,
353
+ severity: o.rule.severity ?? 'error',
354
+ counts: {
355
+ files: o.committedCount,
356
+ differences: o.treeDiff?.differences.length ?? 0,
357
+ provenance: o.provenance.length,
358
+ },
359
+ violations: [
360
+ ...(o.treeDiff?.differences ?? []).map((d) => ({
361
+ id: d.file,
362
+ file: d.file,
363
+ message: d.kind,
364
+ hint: hintFor(o.rule),
365
+ })),
366
+ ...o.provenance.map((f) => ({
367
+ id: f.file,
368
+ file: f.file,
369
+ message: f.message,
370
+ hint: hintFor(o.rule),
371
+ })),
372
+ ],
373
+ ...(o.skipReason ? { skipReason: o.skipReason } : {}),
374
+ ...(o.error ? { error: o.error } : {}),
375
+ }))),
346
376
  }) + '\n');
347
377
  return exit;
348
378
  }
@@ -460,7 +490,7 @@ export const generatedExplainCommand = {
460
490
  const id = flagString(args, 'id') ?? args.positional[0];
461
491
  if (!id) {
462
492
  process.stderr.write('Usage: shrk generated explain --id <id>\n');
463
- return ExitCode.NotVerified;
493
+ return ExitCode.UsageError;
464
494
  }
465
495
  const prep = await prepare(args);
466
496
  if (!prep.ok)
@@ -468,7 +498,7 @@ export const generatedExplainCommand = {
468
498
  const rule = prep.all.find((r) => r.id === id);
469
499
  if (!rule) {
470
500
  process.stderr.write(`No generated-artifact rule "${id}". Declared: ${prep.all.map((r) => r.id).join(', ') || '(none)'}\n`);
471
- return ExitCode.NotVerified;
501
+ return ExitCode.UsageError;
472
502
  }
473
503
  // explain never spawns — the point is to show what the rule SEES.
474
504
  const scan = scanGeneratedFiles(prep.cwd, rule, prep.excludeDirs);
@@ -1 +1 @@
1
- {"version":3,"file":"help.command.d.ts","sourceRoot":"","sources":["../../src/commands/help.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAqF9D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAwC1C;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe;;;;cAK3C;QAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAA;KAAE,GAAG,MAAM;EAqJpF"}
1
+ {"version":3,"file":"help.command.d.ts","sourceRoot":"","sources":["../../src/commands/help.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AA4H9D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAwC1C;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe;;;;cAK3C;QAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAA;KAAE,GAAG,MAAM;EA2KpF"}
@@ -1,5 +1,45 @@
1
1
  import { header } from "../output/format-output.js";
2
2
  import { COMMAND_CATALOG, defaultShowInHelp, listExplainFamily } from "./command-catalog.js";
3
+ /**
4
+ * Every multi-token command path the catalog documents. These are real,
5
+ * callable verbs that the command trie never sees, because their parent
6
+ * dispatches them from a positional argument rather than registering them.
7
+ */
8
+ function catalogHelpPaths() {
9
+ const paths = new Set();
10
+ for (const entry of COMMAND_CATALOG) {
11
+ // Strip the flag/argument tail a few catalog entries carry in `command`.
12
+ const clean = entry.command.split(/\s+--/)[0].trim();
13
+ if (clean.length > 0)
14
+ paths.add(clean);
15
+ }
16
+ return paths;
17
+ }
18
+ /**
19
+ * Render help for a command path the trie could not resolve but the catalog
20
+ * documents, plus its sibling verbs under the same parent so the family is
21
+ * discoverable from any one member.
22
+ */
23
+ function renderCatalogHelp(tokens) {
24
+ const want = tokens.join(' ');
25
+ const entry = COMMAND_CATALOG.find((e) => e.command.split(/\s+--/)[0].trim() === want);
26
+ if (!entry)
27
+ return undefined;
28
+ let out = `${want} — ${entry.description}\n`;
29
+ const extra = EXTRA_HELP_LINES[want];
30
+ if (extra)
31
+ out += extra.join('\n') + '\n';
32
+ const parent = tokens.slice(0, -1).join(' ');
33
+ if (parent.length > 0) {
34
+ const siblings = [...catalogHelpPaths()]
35
+ .filter((p) => p !== want && p.startsWith(parent + ' ') && !p.slice(parent.length + 1).includes(' '))
36
+ .sort();
37
+ if (siblings.length > 0) {
38
+ out += `\nSiblings: ${siblings.join(', ')}\n`;
39
+ }
40
+ }
41
+ return out;
42
+ }
3
43
  /** First sentence of a catalog description, for the compact explain-family list. */
4
44
  function firstSentence(description) {
5
45
  const dot = description.indexOf('. ');
@@ -156,8 +196,17 @@ export function makeHelpCommand(registry) {
156
196
  // that exits 0. Error out honestly instead, with a did-you-mean when
157
197
  // a real topic is a near-typo of the request.
158
198
  const attempt = tokens.join(' ');
199
+ // The trie matched nothing, but the CATALOG may still document this
200
+ // path — a documented verb whose parent isn't a registered command
201
+ // is still real and callable.
202
+ const viaCatalog = renderCatalogHelp(tokens);
203
+ if (viaCatalog !== undefined) {
204
+ process.stdout.write(viaCatalog);
205
+ return 0;
206
+ }
159
207
  process.stderr.write(`no such help topic: '${attempt}'\n`);
160
- const suggestion = nearestHelpTopic(attempt, realHelpTopics(registry));
208
+ const suggestion = nearestHelpTopic(attempt, realHelpTopics(registry)) ??
209
+ nearestHelpTopic(attempt, catalogHelpPaths());
161
210
  if (suggestion)
162
211
  process.stderr.write(`Did you mean: ${suggestion}?\n`);
163
212
  return 1;
@@ -199,7 +248,20 @@ export function makeHelpCommand(registry) {
199
248
  }
200
249
  return 0;
201
250
  }
202
- process.stderr.write(`Unknown command: ${tokens.join(' ')}\n`);
251
+ // The trie could not resolve the full path, but the CATALOG may still
252
+ // document it. Verbs like `check wiring` are dispatched from inside
253
+ // their parent's handler on a positional, so they are real, callable
254
+ // and documented — yet never trie nodes. Falling through to "Unknown
255
+ // command" made an entire documented surface look non-existent.
256
+ const catalogHelp = renderCatalogHelp(tokens);
257
+ if (catalogHelp !== undefined) {
258
+ process.stdout.write(catalogHelp);
259
+ return 0;
260
+ }
261
+ process.stderr.write(`no such help topic: '${tokens.join(' ')}'\n`);
262
+ const near = nearestHelpTopic(tokens.join(' '), catalogHelpPaths());
263
+ if (near)
264
+ process.stderr.write(`Did you mean: shrk help ${near}?\n`);
203
265
  return 1;
204
266
  }
205
267
  if (!wantsFull) {
@@ -1 +1 @@
1
- {"version":3,"file":"policy-lint.command.d.ts","sourceRoot":"","sources":["../../src/commands/policy-lint.command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAMhC,eAAO,MAAM,qBAAqB,EAAG,8BAAuC,CAAC;AAE7E,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,qBAAqB,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,iDAAiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,cAAc,CAqBhB;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CA0CtF;AAGD,eAAO,MAAM,wBAAwB,EAAE,eA8BtC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,eAmN/B,CAAC"}
1
+ {"version":3,"file":"policy-lint.command.d.ts","sourceRoot":"","sources":["../../src/commands/policy-lint.command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAQhC,eAAO,MAAM,qBAAqB,EAAG,8BAAuC,CAAC;AAE7E,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,qBAAqB,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,iDAAiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,cAAc,CAqBhB;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CA0CtF;AAGD,eAAO,MAAM,wBAAwB,EAAE,eA8BtC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,eAoQ/B,CAAC"}
@@ -2,6 +2,8 @@ import * as nodePath from 'node:path';
2
2
  import { runPolicyLint, } from '@shrkcrft/boundaries';
3
3
  import { classifyChangedScope, resolveChangedFiles, resolveProjectConfig } from '@shrkcrft/inspector';
4
4
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
5
+ import { ExitCode } from "../exit-codes.js";
6
+ import { buildGateEnvelope } from "../gates/gate-envelope.js";
5
7
  import { asJson, header, kv } from "../output/format-output.js";
6
8
  const VALID_SURFACES = new Set(['template', 'style', 'ts']);
7
9
  export const POLICY_EXPLAIN_SCHEMA = 'sharkcraft.policy-explain/v1';
@@ -86,19 +88,19 @@ export const policyLintExplainCommand = {
86
88
  const id = args.positional[0] ?? flagString(args, 'id');
87
89
  if (!id) {
88
90
  process.stderr.write('Usage: shrk policy-lint explain <ruleId> [--json]\n');
89
- return 2;
91
+ return ExitCode.UsageError;
90
92
  }
91
93
  const cwd = resolveCwd(args);
92
94
  const loaded = await resolveProjectConfig(cwd);
93
95
  if (!loaded.ok) {
94
96
  process.stderr.write(`Could not load config: ${loaded.error.message}\n`);
95
- return 2;
97
+ return ExitCode.UsageError;
96
98
  }
97
99
  const rules = loaded.value.config.policyRules ?? [];
98
100
  const rule = rules.find((r) => r.id === id);
99
101
  if (!rule) {
100
102
  process.stderr.write(`No policy rule "${id}". Configured: ${rules.map((r) => r.id).join(', ') || '(none)'}\n`);
101
- return 2;
103
+ return ExitCode.UsageError;
102
104
  }
103
105
  const rel = nodePath.relative(cwd, loaded.value.sharkcraftDir).split(nodePath.sep).join('/');
104
106
  const excludeDirs = rel && !rel.startsWith('..') ? [rel] : [];
@@ -127,7 +129,7 @@ export const policyLintCommand = {
127
129
  const bad = parts.filter((s) => !VALID_SURFACES.has(s));
128
130
  if (bad.length > 0) {
129
131
  process.stderr.write(`Unknown --surface "${bad.join(', ')}". Use template | style | ts.\n`);
130
- return 2;
132
+ return ExitCode.UsageError;
131
133
  }
132
134
  surfaces = parts;
133
135
  }
@@ -137,11 +139,12 @@ export const policyLintCommand = {
137
139
  const msg = loaded.error.message;
138
140
  if (wantJson) {
139
141
  process.stdout.write(asJson({ schema: 'sharkcraft.policy-lint/v1', error: msg, rules: [], findings: [], diagnostics: [msg], evaluated: 0, verdict: 'errors' }) + '\n');
140
- return 1;
142
+ return ExitCode.UsageError;
141
143
  }
142
144
  process.stdout.write(header('Policy lint'));
143
145
  process.stdout.write(` ✗ Could not load config: ${msg}\n Run \`shrk doctor\` for details.\n`);
144
- return 1;
146
+ // A broken config is a USAGE error (3), not "violations found" (1).
147
+ return ExitCode.UsageError;
145
148
  }
146
149
  const rules = loaded.value.config.policyRules ?? [];
147
150
  const planeDiagnostics = loaded.value.planeDiagnostics;
@@ -162,7 +165,7 @@ export const policyLintCommand = {
162
165
  const unknown = requested.filter((id) => !known.has(id));
163
166
  if (unknown.length > 0) {
164
167
  process.stderr.write(`Unknown --only rule id(s): ${unknown.join(', ')}. Configured: ${[...known].join(', ') || '(none)'}\n`);
165
- return 2;
168
+ return ExitCode.UsageError;
166
169
  }
167
170
  }
168
171
  let changedFiles;
@@ -218,8 +221,41 @@ export const policyLintCommand = {
218
221
  report = { ...report, findings: newFindings, verdict };
219
222
  }
220
223
  if (wantJson) {
221
- process.stdout.write(asJson({ ...report, ...(newOnly ? { newOnly: true, hiddenBaseline } : {}) }) + '\n');
222
- return report.verdict === 'errors' ? 1 : 0;
224
+ const exit = report.verdict === 'errors' || report.skipped.some((sk) => sk.failed)
225
+ ? ExitCode.Failure
226
+ : report.evaluated === 0 || report.skipped.length > 0
227
+ ? ExitCode.NotVerified
228
+ : ExitCode.VerifiedPass;
229
+ process.stdout.write(asJson({
230
+ ...report,
231
+ ...(newOnly ? { newOnly: true, hiddenBaseline } : {}),
232
+ gate: buildGateEnvelope('policy-lint', exit, report.rules.map((r) => {
233
+ const skip = report.skipped.find((s) => s.ruleId === r.ruleId);
234
+ return {
235
+ id: r.ruleId,
236
+ type: 'policy',
237
+ status: r.status,
238
+ severity: r.severity,
239
+ counts: {
240
+ units: r.unitsScanned,
241
+ findings: r.findingCount,
242
+ suppressed: r.suppressedCount,
243
+ },
244
+ violations: report.findings
245
+ .filter((f) => f.ruleId === r.ruleId)
246
+ .map((f) => ({
247
+ id: f.match,
248
+ file: f.file,
249
+ line: f.line,
250
+ message: f.message,
251
+ ...(f.suggest ? { hint: f.suggest } : {}),
252
+ })),
253
+ ...(skip ? { skipReason: skip.reason } : {}),
254
+ ...(r.error ? { error: r.error } : {}),
255
+ };
256
+ })),
257
+ }) + '\n');
258
+ return exit;
223
259
  }
224
260
  process.stdout.write(header('Policy lint'));
225
261
  // `evaluated` counts rules that actually scanned ≥1 file. When 0 rules
@@ -254,9 +290,15 @@ export const policyLintCommand = {
254
290
  process.stdout.write(` ! ${d}\n`);
255
291
  }
256
292
  if (report.skipped.some((sk) => sk.failed)) {
257
- process.stdout.write('\nA rule with `failOnEmpty: true` matched nothing — that is a bug in the rule, not a pass.\n');
293
+ process.stdout.write('\nA rule that matched nothing is a bug in the rule, not a pass. (`error`-severity\n' +
294
+ ' rules fail on empty by default — set `failOnEmpty: false` if the set may be empty.)\n');
258
295
  return 1;
259
296
  }
297
+ // A non-failing skip still means "partially verified" — never a green 0.
298
+ if (report.skipped.length > 0 && report.findings.length === 0) {
299
+ process.stdout.write(`\n${report.skipped.length} rule(s) scanned nothing — partially verified, not a full green.\n`);
300
+ return 2;
301
+ }
260
302
  if (report.findings.length === 0 && report.diagnostics.length === 0) {
261
303
  process.stdout.write(newOnly
262
304
  ? `\nNo NEW policy violations from this change${hiddenBaseline > 0 ? ` (${hiddenBaseline} pre-existing hidden)` : ''}. ✓\n`
@@ -1 +1 @@
1
- {"version":3,"file":"registry.command.d.ts","sourceRoot":"","sources":["../../src/commands/registry.command.ts"],"names":[],"mappings":"AA8BA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,wBAAwB,EAAE,eA8CtC,CAAC;AAoLF,eAAO,MAAM,eAAe,EAAE,eA4B7B,CAAC"}
1
+ {"version":3,"file":"registry.command.d.ts","sourceRoot":"","sources":["../../src/commands/registry.command.ts"],"names":[],"mappings":"AA8BA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAQhC,eAAO,MAAM,wBAAwB,EAAE,eA8CtC,CAAC;AAkMF,eAAO,MAAM,eAAe,EAAE,eA0C7B,CAAC"}
@@ -20,6 +20,8 @@ import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
20
20
  import { ExitCode } from "../exit-codes.js";
21
21
  import { asJson } from "../output/format-output.js";
22
22
  import { resolveRegistryNoun } from "./registry-resolve.js";
23
+ /** The inventory verbs, used to detect (and forgive) a verb-first invocation. */
24
+ const INVENTORY_VERBS = new Set(['list', 'exists', 'where', 'duplicates']);
23
25
  export const registryLifecycleCommand = {
24
26
  name: 'lifecycle',
25
27
  description: 'Scan the workspace for register/remove symmetry. Read-only.',
@@ -98,11 +100,20 @@ async function runRegistryInventory(args, name) {
98
100
  if (!decl) {
99
101
  const names = loaded.registries.map((r) => r.name);
100
102
  const avail = names.length > 0 ? `Declared registries: ${names.join(', ')}.` : 'No registries declared in sharkcraft.config.ts `registries[]`.';
101
- if (json)
102
- process.stdout.write(asJson({ error: `unknown registry "${name}"`, available: names }) + '\n');
103
- else
103
+ // Typing the verb first is the common stumble (siblings are verb-first).
104
+ // Name the correct grammar instead of only reporting the miss.
105
+ const verbFirst = INVENTORY_VERBS.has(name);
106
+ if (json) {
107
+ process.stdout.write(asJson({ error: `unknown registry "${name}"`, available: names, ...(verbFirst ? { hint: `did you mean 'registry <name> ${name}'?` } : {}) }) + '\n');
108
+ }
109
+ else {
104
110
  process.stderr.write(`No registry named "${name}". ${avail}\n`);
105
- return 2;
111
+ if (verbFirst) {
112
+ const example = names[0] ?? '<name>';
113
+ process.stderr.write(`"${name}" is a verb, not a registry — did you mean \`shrk registry ${example} ${name}\`?\n`);
114
+ }
115
+ }
116
+ return ExitCode.UsageError;
106
117
  }
107
118
  const inventory = scanRegistry(cwd, decl);
108
119
  if (action === 'list' || action === undefined) {
@@ -125,13 +136,13 @@ async function runRegistryInventory(args, name) {
125
136
  if (action === 'exists') {
126
137
  if (!id) {
127
138
  process.stderr.write(`Usage: shrk registry ${name} exists <id> [--resolve] [--fail-if-taken|--fail-if-missing]\n`);
128
- return 2;
139
+ return ExitCode.UsageError;
129
140
  }
130
141
  const failIfTaken = flagBool(args, 'fail-if-taken');
131
142
  const failIfMissing = flagBool(args, 'fail-if-missing');
132
143
  if (failIfTaken && failIfMissing) {
133
144
  process.stderr.write('Pass at most one of --fail-if-taken / --fail-if-missing.\n');
134
- return 2;
145
+ return ExitCode.UsageError;
135
146
  }
136
147
  // `--resolve` maps a human noun to the canonical registered id before the
137
148
  // existence test — via the registry's declared `aliases` map AND generic
@@ -201,7 +212,7 @@ async function runRegistryInventory(args, name) {
201
212
  if (action === 'where') {
202
213
  if (!id) {
203
214
  process.stderr.write(`Usage: shrk registry ${name} where <id>\n`);
204
- return 2;
215
+ return ExitCode.UsageError;
205
216
  }
206
217
  const entry = registryWhere(inventory, id);
207
218
  if (json) {
@@ -220,7 +231,7 @@ async function runRegistryInventory(args, name) {
220
231
  return 0;
221
232
  }
222
233
  process.stderr.write(`Unknown action "${action}". Usage: shrk registry ${name} list | exists <id> | where <id> | duplicates\n`);
223
- return 2;
234
+ return ExitCode.UsageError;
224
235
  }
225
236
  export const registryCommand = {
226
237
  name: 'registry',
@@ -237,7 +248,21 @@ export const registryCommand = {
237
248
  return registryLifecycleCommand.run(args);
238
249
  }
239
250
  if (sub !== undefined && sub.length > 0) {
240
- // `<name> list | exists <id> | where <id>` sub is the registry name.
251
+ // Grammar is `registry <name> <verb>`, but `baseline`/`generated` read
252
+ // verb-first, so `registry list <name>` is the instinctive form. Accept
253
+ // BOTH: when arg1 is a known verb and arg2 names a declared registry,
254
+ // swap them. The canonical order is unchanged; the stumble is removed.
255
+ const second = args.positional[1];
256
+ if (INVENTORY_VERBS.has(sub) && second !== undefined && second.length > 0) {
257
+ const loaded = await loadRegistries(resolveCwd(args));
258
+ const known = loaded.ok && findRegistry(loaded.registries, second) !== undefined;
259
+ if (known) {
260
+ // [verb, name, ...rest] → [name, verb, ...rest]
261
+ args.positional = [second, sub, ...args.positional.slice(2)];
262
+ return runRegistryInventory(args, second);
263
+ }
264
+ }
265
+ // `<name> list | exists <id> | where <id> | duplicates` — sub is the name.
241
266
  return runRegistryInventory(args, sub);
242
267
  }
243
268
  const cwd = resolveCwd(args);
@@ -12,12 +12,19 @@
12
12
  * 0 VerifiedPass — checks ran over a NON-EMPTY scope and passed. Never
13
13
  * returned when zero units were evaluated.
14
14
  * 1 Failure — checks ran and found violations.
15
- * 2 NotVerified — indeterminate: empty evaluation scope, degraded
16
- * fallback, short-circuit, timeout, or "refused to run".
17
- * Distinct from both pass and fail so a chain can branch
18
- * on it (`|| handle-indeterminate`). This is also the
19
- * code the CLI already uses for usage errors — both mean
20
- * "did not produce a verified result".
15
+ * 2 NotVerified — indeterminate: empty evaluation scope, all rules
16
+ * skipped, degraded fallback, short-circuit, timeout, or
17
+ * "refused to run". Distinct from both pass and fail so a
18
+ * chain can branch on it (`|| handle-indeterminate`).
19
+ * 3 UsageError — the request itself was malformed: unloadable config, an
20
+ * unknown rule id, a bad flag value. Split out of `2` on
21
+ * the GATE verbs because the two demand different
22
+ * responses: `2` means "the gate ran but proved nothing"
23
+ * (investigate the rules), `3` means "the gate never
24
+ * started" (fix the invocation or the config). Non-gate
25
+ * verbs keep returning `2` for usage errors — widening the
26
+ * split across the whole CLI would churn a documented
27
+ * contract far beyond what it buys.
21
28
  *
22
29
  * The `gen --typecheck` pre-write gate already refuses-to-nonzero rather than
23
30
  * emit an unverified artifact; this generalizes that instinct across the gate
@@ -27,7 +34,8 @@
27
34
  export declare enum ExitCode {
28
35
  VerifiedPass = 0,
29
36
  Failure = 1,
30
- NotVerified = 2
37
+ NotVerified = 2,
38
+ UsageError = 3
31
39
  }
32
40
  /**
33
41
  * Promote a NotVerified (`2`) exit into a Failure-class nonzero (`1`) when the
@@ -61,12 +69,24 @@ export declare function argvHasExitTrailer(argv: readonly string[]): boolean;
61
69
  * so `check boundaries --json` (2 tokens) and a bare `finish` (1) both resolve.
62
70
  */
63
71
  export declare function isGateVerb(commandPath: string): boolean;
72
+ /**
73
+ * True when the argv carries the global `--no-hints` (before the `--`
74
+ * sentinel). Suppresses advisory one-liners like the piped-exit note while
75
+ * leaving every real diagnostic — and the `--exit-trailer` machine channel —
76
+ * untouched. For a caller that has already internalised the warning and just
77
+ * wants clean stderr in captured logs.
78
+ */
79
+ export declare function argvHasNoHints(argv: readonly string[]): boolean;
80
+ /** Reset the one-time hint latch. Test-only seam. */
81
+ export declare function resetPipeHintLatch(): void;
64
82
  /** Injectable surface for {@link emitPipeExitSignal} (isTTY + writer + trailer). */
65
83
  export interface IPipeExitOptions {
66
84
  /** True when shrk's stdout is NOT a terminal (i.e. piped/redirected). */
67
85
  readonly piped: boolean;
68
86
  /** True when `--exit-trailer` was requested. */
69
87
  readonly trailer: boolean;
88
+ /** True when `--no-hints` was requested — suppress the advisory note only. */
89
+ readonly noHints?: boolean;
70
90
  /** stderr writer; defaults to `process.stderr.write`. Overridable for tests. */
71
91
  readonly write?: (s: string) => void;
72
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"exit-codes.d.ts","sourceRoot":"","sources":["../src/exit-codes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,oBAAY,QAAQ;IAClB,YAAY,IAAI;IAChB,OAAO,IAAI;IACX,WAAW,IAAI;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAGtE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM9D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAMnE;AA4BD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAMvD;AAED,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,gBAAgB,GACrB,IAAI,CAWN"}
1
+ {"version":3,"file":"exit-codes.d.ts","sourceRoot":"","sources":["../src/exit-codes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,oBAAY,QAAQ;IAClB,YAAY,IAAI;IAChB,OAAO,IAAI;IACX,WAAW,IAAI;IACf,UAAU,IAAI;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAGtE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM9D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAMnE;AA4BD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAMvD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM/D;AASD,qDAAqD;AACrD,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,gBAAgB,GACrB,IAAI,CAgBN"}
@@ -12,12 +12,19 @@
12
12
  * 0 VerifiedPass — checks ran over a NON-EMPTY scope and passed. Never
13
13
  * returned when zero units were evaluated.
14
14
  * 1 Failure — checks ran and found violations.
15
- * 2 NotVerified — indeterminate: empty evaluation scope, degraded
16
- * fallback, short-circuit, timeout, or "refused to run".
17
- * Distinct from both pass and fail so a chain can branch
18
- * on it (`|| handle-indeterminate`). This is also the
19
- * code the CLI already uses for usage errors — both mean
20
- * "did not produce a verified result".
15
+ * 2 NotVerified — indeterminate: empty evaluation scope, all rules
16
+ * skipped, degraded fallback, short-circuit, timeout, or
17
+ * "refused to run". Distinct from both pass and fail so a
18
+ * chain can branch on it (`|| handle-indeterminate`).
19
+ * 3 UsageError — the request itself was malformed: unloadable config, an
20
+ * unknown rule id, a bad flag value. Split out of `2` on
21
+ * the GATE verbs because the two demand different
22
+ * responses: `2` means "the gate ran but proved nothing"
23
+ * (investigate the rules), `3` means "the gate never
24
+ * started" (fix the invocation or the config). Non-gate
25
+ * verbs keep returning `2` for usage errors — widening the
26
+ * split across the whole CLI would churn a documented
27
+ * contract far beyond what it buys.
21
28
  *
22
29
  * The `gen --typecheck` pre-write gate already refuses-to-nonzero rather than
23
30
  * emit an unverified artifact; this generalizes that instinct across the gate
@@ -29,6 +36,7 @@ export var ExitCode;
29
36
  ExitCode[ExitCode["VerifiedPass"] = 0] = "VerifiedPass";
30
37
  ExitCode[ExitCode["Failure"] = 1] = "Failure";
31
38
  ExitCode[ExitCode["NotVerified"] = 2] = "NotVerified";
39
+ ExitCode[ExitCode["UsageError"] = 3] = "UsageError";
32
40
  })(ExitCode || (ExitCode = {}));
33
41
  /**
34
42
  * Promote a NotVerified (`2`) exit into a Failure-class nonzero (`1`) when the
@@ -116,6 +124,32 @@ export function isGateVerb(commandPath) {
116
124
  return true;
117
125
  return false;
118
126
  }
127
+ /**
128
+ * True when the argv carries the global `--no-hints` (before the `--`
129
+ * sentinel). Suppresses advisory one-liners like the piped-exit note while
130
+ * leaving every real diagnostic — and the `--exit-trailer` machine channel —
131
+ * untouched. For a caller that has already internalised the warning and just
132
+ * wants clean stderr in captured logs.
133
+ */
134
+ export function argvHasNoHints(argv) {
135
+ for (const t of argv) {
136
+ if (t === '--')
137
+ break;
138
+ if (t === '--no-hints')
139
+ return true;
140
+ }
141
+ return false;
142
+ }
143
+ /**
144
+ * Process-lifetime latch for the piped-exit hint. The note is advisory, so it
145
+ * pays rent once: a command that emits several gate verdicts in one process
146
+ * (or a `--watch` loop) should not repeat the same paragraph every cycle.
147
+ */
148
+ let pipeHintEmitted = false;
149
+ /** Reset the one-time hint latch. Test-only seam. */
150
+ export function resetPipeHintLatch() {
151
+ pipeHintEmitted = false;
152
+ }
119
153
  /**
120
154
  * Keep the honest `0`/`1`/`2` exit code READABLE through the shape agents reach
121
155
  * for first — the trailing pipe. `<gate> | head` reports `head`'s `$?`, so a true
@@ -136,9 +170,14 @@ export function emitPipeExitSignal(commandPath, code, opts) {
136
170
  if (!isGateVerb(commandPath))
137
171
  return;
138
172
  const write = opts.write ?? ((s) => void process.stderr.write(s));
139
- if (opts.piped && code !== 0) {
173
+ // Advisory, and therefore rationed: stderr only, only when a NON-zero verdict
174
+ // would actually be lost to the pipe, at most once per process, and never
175
+ // under `--no-hints`. The structured channel (`--exit-trailer`) is unaffected
176
+ // by all of these — it is opt-in and always emitted when asked.
177
+ if (opts.piped && code !== 0 && !opts.noHints && !pipeHintEmitted) {
178
+ pipeHintEmitted = true;
140
179
  write(`note: stdout is piped — $? reflects the downstream command, not shrk (exit ${code}); ` +
141
- `use PIPESTATUS[0] or --exit-trailer to read shrk's verdict.\n`);
180
+ `use PIPESTATUS[0] or --exit-trailer to read shrk's verdict (--no-hints silences this).\n`);
142
181
  }
143
182
  // The trailer is written LAST so it is the final stderr line a caller reads.
144
183
  if (opts.trailer)
@@ -0,0 +1,64 @@
1
+ /**
2
+ * One machine-readable shape for every gate verb.
3
+ *
4
+ * `--json` has always been available on each plane, but each emitted its own
5
+ * schema (`sharkcraft.wiring/v1`, `sharkcraft.baseline/v1`, …), so a CI step or
6
+ * agent had to parse four shapes to answer one question: which rules ran, which
7
+ * failed, and why. This envelope is that answer, identical across planes.
8
+ *
9
+ * It is ADDITIVE. The per-plane payloads are published, documented, and asserted
10
+ * by tests; replacing them would break every existing consumer. The envelope
11
+ * rides along under a `gate` key, so `jq .gate` is the uniform read and nothing
12
+ * that worked before stops working.
13
+ */
14
+ export declare const GATE_ENVELOPE_SCHEMA: "sharkcraft.gate/v1";
15
+ /** Which data-defined plane produced a rule result. */
16
+ export type GateRuleType = 'wiring' | 'policy' | 'registry' | 'registration' | 'baseline' | 'generated';
17
+ /**
18
+ * Per-rule outcome, uniform across planes. `skipped` is deliberately a
19
+ * first-class status, not folded into `passed` — a rule that matched nothing
20
+ * enforced nothing.
21
+ */
22
+ export type GateRuleStatus = 'passed' | 'failed' | 'skipped' | 'error';
23
+ /** One violation, normalized. `id` is the offending token / entry / file. */
24
+ export interface IGateViolation {
25
+ readonly id: string;
26
+ readonly file?: string;
27
+ readonly line?: number;
28
+ readonly message?: string;
29
+ readonly hint?: string;
30
+ }
31
+ /** One rule's result in the shared envelope. */
32
+ export interface IGateRuleResult {
33
+ readonly id: string;
34
+ readonly type: GateRuleType;
35
+ readonly status: GateRuleStatus;
36
+ readonly severity: 'error' | 'warning';
37
+ /**
38
+ * Plane-appropriate match counts — e.g. `{declared, registered}` for wiring,
39
+ * `{committed, current}` for baseline, `{units, findings}` for policy. Always
40
+ * present so "what did this rule actually see?" is answerable uniformly.
41
+ */
42
+ readonly counts: Readonly<Record<string, number>>;
43
+ readonly violations: readonly IGateViolation[];
44
+ /** Why the rule checked nothing, when `status` is `skipped`. */
45
+ readonly skipReason?: string;
46
+ /** Set when the rule is misconfigured (`status: 'error'`). */
47
+ readonly error?: string;
48
+ }
49
+ /** The envelope emitted under the `gate` key of every gate verb's `--json`. */
50
+ export interface IGateEnvelope {
51
+ readonly schema: typeof GATE_ENVELOPE_SCHEMA;
52
+ /** The verb that produced this, space-joined (e.g. `check wiring`). */
53
+ readonly verb: string;
54
+ /** The exit code this run returns — the same number the process exits with. */
55
+ readonly exit: number;
56
+ readonly rules: readonly IGateRuleResult[];
57
+ /** Rules that ran a real comparison (status !== 'skipped'). */
58
+ readonly evaluated: number;
59
+ readonly skipped: number;
60
+ readonly failed: number;
61
+ }
62
+ /** Build the envelope from already-normalized per-rule results. */
63
+ export declare function buildGateEnvelope(verb: string, exit: number, rules: readonly IGateRuleResult[]): IGateEnvelope;
64
+ //# sourceMappingURL=gate-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate-envelope.d.ts","sourceRoot":"","sources":["../../src/gates/gate-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,EAAG,oBAA6B,CAAC;AAElE,uDAAuD;AACvD,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,cAAc,GACd,UAAU,GACV,WAAW,CAAC;AAEhB;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvE,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,CAAC;IAC/C,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,oBAAoB,CAAC;IAC7C,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAC3C,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,eAAe,EAAE,GAChC,aAAa,CAUf"}