@nac3/forge-cli 1.23.7 → 1.24.1

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 (165) hide show
  1. package/dist/chat/claude.d.ts +8 -0
  2. package/dist/chat/claude.d.ts.map +1 -1
  3. package/dist/chat/claude.js.map +1 -1
  4. package/dist/chat/claude_cli.d.ts +23 -0
  5. package/dist/chat/claude_cli.d.ts.map +1 -1
  6. package/dist/chat/claude_cli.js +76 -2
  7. package/dist/chat/claude_cli.js.map +1 -1
  8. package/dist/chat/doctrine_digest.d.ts.map +1 -1
  9. package/dist/chat/doctrine_digest.js +2 -1
  10. package/dist/chat/doctrine_digest.js.map +1 -1
  11. package/dist/chat/server.d.ts.map +1 -1
  12. package/dist/chat/server.js +11 -0
  13. package/dist/chat/server.js.map +1 -1
  14. package/dist/chat/tools/pending.d.ts +11 -0
  15. package/dist/chat/tools/pending.d.ts.map +1 -1
  16. package/dist/chat/tools/pending.js +55 -8
  17. package/dist/chat/tools/pending.js.map +1 -1
  18. package/dist/chat/tools/shell.d.ts +39 -4
  19. package/dist/chat/tools/shell.d.ts.map +1 -1
  20. package/dist/chat/tools/shell.js +167 -7
  21. package/dist/chat/tools/shell.js.map +1 -1
  22. package/dist/chat/tools/urazuke.d.ts.map +1 -1
  23. package/dist/chat/tools/urazuke.js +8 -5
  24. package/dist/chat/tools/urazuke.js.map +1 -1
  25. package/dist/chat/urazuke_section.d.ts +26 -4
  26. package/dist/chat/urazuke_section.d.ts.map +1 -1
  27. package/dist/chat/urazuke_section.js +115 -9
  28. package/dist/chat/urazuke_section.js.map +1 -1
  29. package/dist/commands/pending.d.ts.map +1 -1
  30. package/dist/commands/pending.js +116 -12
  31. package/dist/commands/pending.js.map +1 -1
  32. package/dist/commands/urazuke.d.ts +3 -0
  33. package/dist/commands/urazuke.d.ts.map +1 -1
  34. package/dist/commands/urazuke.js +80 -3
  35. package/dist/commands/urazuke.js.map +1 -1
  36. package/dist/core/ascii_purity.d.ts +69 -0
  37. package/dist/core/ascii_purity.d.ts.map +1 -0
  38. package/dist/core/ascii_purity.js +141 -0
  39. package/dist/core/ascii_purity.js.map +1 -0
  40. package/dist/core/battery_stamp.d.ts +111 -0
  41. package/dist/core/battery_stamp.d.ts.map +1 -0
  42. package/dist/core/battery_stamp.js +287 -0
  43. package/dist/core/battery_stamp.js.map +1 -0
  44. package/dist/core/ci_gaps.d.ts +130 -0
  45. package/dist/core/ci_gaps.d.ts.map +1 -0
  46. package/dist/core/ci_gaps.js +353 -0
  47. package/dist/core/ci_gaps.js.map +1 -0
  48. package/dist/core/doc_citations.d.ts +133 -0
  49. package/dist/core/doc_citations.d.ts.map +1 -0
  50. package/dist/core/doc_citations.js +271 -0
  51. package/dist/core/doc_citations.js.map +1 -0
  52. package/dist/core/hook_budget.d.ts +60 -0
  53. package/dist/core/hook_budget.d.ts.map +1 -0
  54. package/dist/core/hook_budget.js +163 -0
  55. package/dist/core/hook_budget.js.map +1 -0
  56. package/dist/docs/doctrine/urazuke.md +173 -2
  57. package/dist/nac3/internal_manifest.d.ts.map +1 -1
  58. package/dist/nac3/internal_manifest.js +28 -2
  59. package/dist/nac3/internal_manifest.js.map +1 -1
  60. package/dist/pending/store.d.ts +29 -0
  61. package/dist/pending/store.d.ts.map +1 -1
  62. package/dist/pending/store.js +51 -1
  63. package/dist/pending/store.js.map +1 -1
  64. package/dist/pending/types.d.ts +23 -0
  65. package/dist/pending/types.d.ts.map +1 -1
  66. package/dist/pending/types.js.map +1 -1
  67. package/dist/telemetry/usage.d.ts +18 -0
  68. package/dist/telemetry/usage.d.ts.map +1 -1
  69. package/dist/telemetry/usage.js +16 -1
  70. package/dist/telemetry/usage.js.map +1 -1
  71. package/dist/urazuke/announce.d.ts +7 -0
  72. package/dist/urazuke/announce.d.ts.map +1 -1
  73. package/dist/urazuke/announce.js +20 -0
  74. package/dist/urazuke/announce.js.map +1 -1
  75. package/dist/urazuke/catalog.d.ts +1 -1
  76. package/dist/urazuke/catalog.d.ts.map +1 -1
  77. package/dist/urazuke/catalog.js.map +1 -1
  78. package/dist/urazuke/close_gate.d.ts +31 -4
  79. package/dist/urazuke/close_gate.d.ts.map +1 -1
  80. package/dist/urazuke/close_gate.js +70 -13
  81. package/dist/urazuke/close_gate.js.map +1 -1
  82. package/dist/urazuke/current.d.ts +28 -0
  83. package/dist/urazuke/current.d.ts.map +1 -1
  84. package/dist/urazuke/current.js +18 -5
  85. package/dist/urazuke/current.js.map +1 -1
  86. package/dist/urazuke/cycle.d.ts +3 -0
  87. package/dist/urazuke/cycle.d.ts.map +1 -1
  88. package/dist/urazuke/cycle.js +5 -3
  89. package/dist/urazuke/cycle.js.map +1 -1
  90. package/dist/urazuke/evidence.d.ts +14 -1
  91. package/dist/urazuke/evidence.d.ts.map +1 -1
  92. package/dist/urazuke/evidence.js +88 -2
  93. package/dist/urazuke/evidence.js.map +1 -1
  94. package/dist/urazuke/evidence_env.d.ts +78 -0
  95. package/dist/urazuke/evidence_env.d.ts.map +1 -0
  96. package/dist/urazuke/evidence_env.js +341 -0
  97. package/dist/urazuke/evidence_env.js.map +1 -0
  98. package/dist/urazuke/freeze.d.ts.map +1 -1
  99. package/dist/urazuke/freeze.js +16 -9
  100. package/dist/urazuke/freeze.js.map +1 -1
  101. package/dist/urazuke/gate.d.ts +48 -0
  102. package/dist/urazuke/gate.d.ts.map +1 -1
  103. package/dist/urazuke/gate.js +251 -25
  104. package/dist/urazuke/gate.js.map +1 -1
  105. package/dist/urazuke/gates_data.d.ts +30 -3
  106. package/dist/urazuke/gates_data.d.ts.map +1 -1
  107. package/dist/urazuke/gates_data.js +51 -5
  108. package/dist/urazuke/gates_data.js.map +1 -1
  109. package/dist/urazuke/gates_pull.d.ts +40 -0
  110. package/dist/urazuke/gates_pull.d.ts.map +1 -1
  111. package/dist/urazuke/gates_pull.js +118 -8
  112. package/dist/urazuke/gates_pull.js.map +1 -1
  113. package/dist/urazuke/manual_cases.d.ts +11 -1
  114. package/dist/urazuke/manual_cases.d.ts.map +1 -1
  115. package/dist/urazuke/manual_cases.js +1 -0
  116. package/dist/urazuke/manual_cases.js.map +1 -1
  117. package/dist/urazuke/overrides.d.ts +107 -0
  118. package/dist/urazuke/overrides.d.ts.map +1 -0
  119. package/dist/urazuke/overrides.js +128 -0
  120. package/dist/urazuke/overrides.js.map +1 -0
  121. package/dist/urazuke/probes.d.ts.map +1 -1
  122. package/dist/urazuke/probes.js +51 -0
  123. package/dist/urazuke/probes.js.map +1 -1
  124. package/dist/urazuke/profiles.d.ts +135 -0
  125. package/dist/urazuke/profiles.d.ts.map +1 -0
  126. package/dist/urazuke/profiles.js +470 -0
  127. package/dist/urazuke/profiles.js.map +1 -0
  128. package/dist/urazuke/receipt_audit.d.ts +67 -0
  129. package/dist/urazuke/receipt_audit.d.ts.map +1 -0
  130. package/dist/urazuke/receipt_audit.js +103 -0
  131. package/dist/urazuke/receipt_audit.js.map +1 -0
  132. package/dist/urazuke/receipts.d.ts +22 -0
  133. package/dist/urazuke/receipts.d.ts.map +1 -1
  134. package/dist/urazuke/receipts.js +44 -0
  135. package/dist/urazuke/receipts.js.map +1 -1
  136. package/dist/urazuke/report.d.ts +30 -0
  137. package/dist/urazuke/report.d.ts.map +1 -1
  138. package/dist/urazuke/report.js +39 -0
  139. package/dist/urazuke/report.js.map +1 -1
  140. package/dist/urazuke/reset.d.ts +35 -0
  141. package/dist/urazuke/reset.d.ts.map +1 -0
  142. package/dist/urazuke/reset.js +150 -0
  143. package/dist/urazuke/reset.js.map +1 -0
  144. package/dist/urazuke/retro.d.ts +5 -1
  145. package/dist/urazuke/retro.d.ts.map +1 -1
  146. package/dist/urazuke/retro.js +21 -8
  147. package/dist/urazuke/retro.js.map +1 -1
  148. package/dist/urazuke/section.d.ts.map +1 -1
  149. package/dist/urazuke/section.js +17 -2
  150. package/dist/urazuke/section.js.map +1 -1
  151. package/dist/urazuke/semantics.d.ts +173 -0
  152. package/dist/urazuke/semantics.d.ts.map +1 -0
  153. package/dist/urazuke/semantics.js +232 -0
  154. package/dist/urazuke/semantics.js.map +1 -0
  155. package/dist/urazuke/start.d.ts +8 -0
  156. package/dist/urazuke/start.d.ts.map +1 -1
  157. package/dist/urazuke/start.js +10 -2
  158. package/dist/urazuke/start.js.map +1 -1
  159. package/dist/version.d.ts +1 -1
  160. package/dist/version.js +1 -1
  161. package/dist/workflow/orchestrator.d.ts.map +1 -1
  162. package/dist/workflow/orchestrator.js +6 -7
  163. package/dist/workflow/orchestrator.js.map +1 -1
  164. package/docs/doctrine/urazuke.md +173 -2
  165. package/package.json +9 -2
@@ -0,0 +1,111 @@
1
+ export declare const BATTERY_STAMP_REL = ".forge/battery-stamp.json";
2
+ export declare const FINGERPRINT_SPEC: string;
3
+ export interface GitState {
4
+ commit: string;
5
+ dirty: boolean;
6
+ fingerprint: string;
7
+ /** Lo que la huella cubre, en el propio sello: un dato sin su definicion
8
+ * al lado es una afirmacion que el lector no puede verificar. */
9
+ fingerprint_spec: string;
10
+ }
11
+ export interface BatteryCounts {
12
+ files: number;
13
+ passed: number;
14
+ failed: number;
15
+ skipped: number;
16
+ }
17
+ export interface BatteryStamp extends GitState {
18
+ version: 1;
19
+ ran_at: number;
20
+ duration_ms: number;
21
+ counts: BatteryCounts;
22
+ /** Verde = la bateria termino sin fallas. Un sello rojo NO se descarta:
23
+ * "la bateria corrio y fallo" es informacion, y borrarla dejaria el
24
+ * mismo silencio que no tener sello. */
25
+ green: boolean;
26
+ /** Para que el dueno pueda repetir la corrida exacta. */
27
+ invocation: string;
28
+ /** La corrida llevaba un filtro de archivos (`vitest run tests/x.test.ts`),
29
+ * asi que NO es la bateria: es una parte. Se registra porque la mitad de
30
+ * las corridas de un dia de trabajo son parciales, y un sello parcial
31
+ * que se lea como "la bateria paso" seria exactamente la clase de verde
32
+ * que afirma mas de lo que midio. */
33
+ filtered: boolean;
34
+ }
35
+ /** Si la invocacion llevaba un filtro de archivos. Mecanico: cualquier
36
+ * argumento posicional que no sea una bandera ni el subcomando `run` es un
37
+ * patron de archivos para vitest.
38
+ *
39
+ * El sesgo es a proposito: `--reporter default` (con espacio) se lee como
40
+ * filtro y el sello sale marcado parcial sin serlo. Equivocarse hacia
41
+ * "parcial" avisa de mas; equivocarse hacia "completa" es el verde que
42
+ * afirma lo que no midio. */
43
+ export declare function isFilteredInvocation(args: string[]): boolean;
44
+ /**
45
+ * EL FLAG QUE SE LLEVA EL INSTRUMENTO (medido 2026-11-01, vitest 2.x en
46
+ * este repositorio). `--reporter` en la linea de comandos NO se SUMA a los
47
+ * `reporters` de vitest.config.ts: los REEMPLAZA. Asi que
48
+ * `vitest run --reporter=basic` corre la bateria entera, sale verde, y NO
49
+ * deja sello -- y el aviso de `prepublishOnly` sigue hablando de la corrida
50
+ * anterior.
51
+ *
52
+ * Como se descubrio, que es la parte que importa: la bateria se corrio a
53
+ * las 20:25 con 508 archivos en verde y el sello en disco seguia marcando
54
+ * las 20:01 y 507 archivos. O sea que el instrumento del riesgo aceptado
55
+ * -- la unica cosa que cubre la bateria, que esta FUERA del gancho a
56
+ * proposito -- se desactivaba con el flag de conveniencia que se usaba en
57
+ * casi todas las corridas de la sesion. Es la misma familia que el resto
58
+ * del lote: un verde que no afirma lo que parece afirmar.
59
+ *
60
+ * Esto NO lo puede arreglar la config volviendo a inyectar el reporter: la
61
+ * CLI ya gano cuando la config se evalua. Lo que se puede, y es lo que se
62
+ * hace, es no callarse -- en el arranque (vitest.config.ts) y en el aviso
63
+ * del publish (renderFreshness), que son los dos momentos en que alguien
64
+ * todavia puede corregirlo.
65
+ */
66
+ export declare function reporterOverridesStamp(args: string[]): boolean;
67
+ /** El aviso del arranque, como lineas (sin saltos embebidos: este archivo
68
+ * es ASCII y los saltos los pone quien escribe). */
69
+ export declare const UNSEALED_RUN_NOTICE: readonly string[];
70
+ /** La huella y el estado de git, calculados en UN solo lugar. Si el sello y
71
+ * la verificacion los calcularan por su cuenta, un dia divergirian y la
72
+ * comparacion diria "no coincide" sin que nada hubiera cambiado -- o peor,
73
+ * al reves (simetria productor/consumidor, SQ 14). */
74
+ export declare function readGitState(cwd: string): GitState;
75
+ /** Los numeros de una corrida de vitest, del arbol de tareas que el
76
+ * reporter recibe. Pura a proposito: el arbol se puede sintetizar en un
77
+ * caso y asi el contador se prueba sin correr la bateria dentro de la
78
+ * bateria. */
79
+ export declare function countFromFiles(files: unknown[]): BatteryCounts;
80
+ export declare function writeBatteryStamp(repoRoot: string, stamp: BatteryStamp): void;
81
+ export declare function readBatteryStamp(repoRoot: string): BatteryStamp | null;
82
+ export type FreshnessVerdict = 'same_content' | 'same_commit' | 'other_commit' | 'partial' | 'red' | 'never';
83
+ export interface FreshnessReport {
84
+ verdict: FreshnessVerdict;
85
+ /** Lo que importa para el aviso: el contenido que se va a empaquetar NO
86
+ * es el que la bateria vio. */
87
+ stale: boolean;
88
+ stamp: BatteryStamp | null;
89
+ current: GitState;
90
+ }
91
+ export declare function judgeFreshness(stamp: BatteryStamp | null, current: GitState): FreshnessReport;
92
+ /** El aviso, con el formato que el dueno pidio: en que commit corrio la
93
+ * bateria por ultima vez, en que commit estas, y si no coinciden lo dice. */
94
+ export declare function renderFreshness(r: FreshnessReport): string;
95
+ /** El reporter de vitest que deja el sello. Pato-tipado (no importa tipos
96
+ * de vitest) para que este modulo siga siendo parte del producto y no de
97
+ * la bateria. */
98
+ export declare class BatteryStampReporter {
99
+ private started;
100
+ private root;
101
+ private invocation;
102
+ private filtered;
103
+ constructor(opts?: {
104
+ root?: string;
105
+ invocation?: string;
106
+ filtered?: boolean;
107
+ });
108
+ onInit(): void;
109
+ onFinished(files?: unknown[], errors?: unknown[]): void;
110
+ }
111
+ //# sourceMappingURL=battery_stamp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery_stamp.d.ts","sourceRoot":"","sources":["../../src/core/battery_stamp.ts"],"names":[],"mappings":"AA6CA,eAAO,MAAM,iBAAiB,8BAA8B,CAAC;AAE7D,eAAO,MAAM,gBAAgB,QAGsB,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB;sEACkE;IAClE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB;;6CAEyC;IACzC,KAAK,EAAE,OAAO,CAAC;IACf,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB;;;;0CAIsC;IACtC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;8BAO8B;AAC9B,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAE5D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAG9D;AAED;qDACqD;AACrD,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAShD,CAAC;AAEF;;;uDAGuD;AACvD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAsBlD;AAED;;;eAGe;AACf,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,aAAa,CAY9D;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAI7E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAQtE;AAED,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,aAAa,GACb,cAAc,GACd,SAAS,GACT,KAAK,GACL,OAAO,CAAC;AAEZ,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,CAAC;IAC1B;oCACgC;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,EAAE,OAAO,EAAE,QAAQ,GAAG,eAAe,CAW7F;AASD;8EAC8E;AAC9E,wBAAgB,eAAe,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAuD1D;AAED;;kBAEkB;AAClB,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAU;gBAEd,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE;IAO7E,MAAM,IAAI,IAAI;IAEd,UAAU,CAAC,KAAK,GAAE,OAAO,EAAO,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,IAAI;CAoBhE"}
@@ -0,0 +1,287 @@
1
+ /**
2
+ * EL SELLO DE LA BATERIA -- el instrumento del riesgo aceptado (F-15).
3
+ *
4
+ * EL RIESGO. El gancho pre-push corre su paquete (20,8 a 39,2 s medidos,
5
+ * techo declarado 35 s -- src/core/hook_budget.ts) y NO corre la
6
+ * bateria (418 s): la decision es del dueno y esta escrita en
7
+ * docs/PROGRAMMING_NORMS.md. Pero un peor caso aceptado y no instrumentado
8
+ * es dano silencioso, asi que el riesgo tiene que AVISAR cuando ocurre, y
9
+ * "ocurre" aca significa: se esta publicando codigo que la bateria nunca
10
+ * vio.
11
+ *
12
+ * EL INSTRUMENTO. Cada corrida de vitest deja un sello
13
+ * (`.forge/battery-stamp.json`) con el commit, el estado del arbol y el
14
+ * resultado. Antes de empaquetar, `scripts/battery_freshness.mjs` compara
15
+ * ese sello con el estado de AHORA y lo dice. No bloquea -- eso es decision
16
+ * de costo del dueno -- pero deja de ser una frase en un documento.
17
+ *
18
+ * POR QUE NO ALCANZA EL COMMIT. 1.20.1 salio publicada con el bug adentro
19
+ * porque el tarball llevaba el `dist/` del build inicial y las fuentes se
20
+ * editaron DURANTE la compuerta: el commit era el mismo y el contenido no.
21
+ * Asi que el sello lleva una huella del contenido, no solo el commit.
22
+ *
23
+ * LA HUELLA, AUTODESCRIPTA (la misma regla que el congelado: una huella que
24
+ * no se puede reproducir desde afuera no es evidencia):
25
+ * algoritmo: SHA-256
26
+ * codificacion: UTF-8
27
+ * forma canonica: las tres partes unidas por \n, en este orden --
28
+ * (1) el commit de HEAD (40 hex)
29
+ * (2) la salida de `git status --porcelain -uall`
30
+ * (3) la salida de `git diff HEAD`
31
+ * cubre: el contenido de todo archivo rastreado (via la parte 3)
32
+ * y la LISTA de archivos sin rastrear (via la parte 2)
33
+ * excluye: el CONTENIDO de los archivos sin rastrear, y todo lo que
34
+ * .gitignore descarta (node_modules, dist). Un cambio solo
35
+ * ahi no mueve la huella, y eso esta dicho a proposito:
36
+ * decir que cubre mas de lo que cubre es el defecto que
37
+ * este archivo existe para no repetir.
38
+ *
39
+ * ASCII-only.
40
+ */
41
+ import { createHash } from 'node:crypto';
42
+ import { execFileSync } from 'node:child_process';
43
+ import fs from 'node:fs';
44
+ import path from 'node:path';
45
+ export const BATTERY_STAMP_REL = '.forge/battery-stamp.json';
46
+ export const FINGERPRINT_SPEC = 'sha256(utf8(HEAD + "\\n" + `git status --porcelain -uall`'
47
+ + ' sin la linea del propio sello + "\\n" + `git diff HEAD`)); cubre el contenido'
48
+ + ' rastreado y la lista de archivos sin rastrear; excluye el contenido sin'
49
+ + ' rastrear, lo ignorado y el archivo del sello';
50
+ /** Si la invocacion llevaba un filtro de archivos. Mecanico: cualquier
51
+ * argumento posicional que no sea una bandera ni el subcomando `run` es un
52
+ * patron de archivos para vitest.
53
+ *
54
+ * El sesgo es a proposito: `--reporter default` (con espacio) se lee como
55
+ * filtro y el sello sale marcado parcial sin serlo. Equivocarse hacia
56
+ * "parcial" avisa de mas; equivocarse hacia "completa" es el verde que
57
+ * afirma lo que no midio. */
58
+ export function isFilteredInvocation(args) {
59
+ return args.some((a) => a !== 'run' && a !== 'watch' && !a.startsWith('-'));
60
+ }
61
+ /**
62
+ * EL FLAG QUE SE LLEVA EL INSTRUMENTO (medido 2026-11-01, vitest 2.x en
63
+ * este repositorio). `--reporter` en la linea de comandos NO se SUMA a los
64
+ * `reporters` de vitest.config.ts: los REEMPLAZA. Asi que
65
+ * `vitest run --reporter=basic` corre la bateria entera, sale verde, y NO
66
+ * deja sello -- y el aviso de `prepublishOnly` sigue hablando de la corrida
67
+ * anterior.
68
+ *
69
+ * Como se descubrio, que es la parte que importa: la bateria se corrio a
70
+ * las 20:25 con 508 archivos en verde y el sello en disco seguia marcando
71
+ * las 20:01 y 507 archivos. O sea que el instrumento del riesgo aceptado
72
+ * -- la unica cosa que cubre la bateria, que esta FUERA del gancho a
73
+ * proposito -- se desactivaba con el flag de conveniencia que se usaba en
74
+ * casi todas las corridas de la sesion. Es la misma familia que el resto
75
+ * del lote: un verde que no afirma lo que parece afirmar.
76
+ *
77
+ * Esto NO lo puede arreglar la config volviendo a inyectar el reporter: la
78
+ * CLI ya gano cuando la config se evalua. Lo que se puede, y es lo que se
79
+ * hace, es no callarse -- en el arranque (vitest.config.ts) y en el aviso
80
+ * del publish (renderFreshness), que son los dos momentos en que alguien
81
+ * todavia puede corregirlo.
82
+ */
83
+ export function reporterOverridesStamp(args) {
84
+ return args.some((a) => a === '--reporter' || a === '--reporters'
85
+ || a.startsWith('--reporter=') || a.startsWith('--reporters='));
86
+ }
87
+ /** El aviso del arranque, como lineas (sin saltos embebidos: este archivo
88
+ * es ASCII y los saltos los pone quien escribe). */
89
+ export const UNSEALED_RUN_NOTICE = [
90
+ '',
91
+ '[sello de la bateria] ESTA CORRIDA NO VA A DEJAR SELLO.',
92
+ '`--reporter` en la linea de comandos reemplaza los reporters de',
93
+ 'vitest.config.ts y se lleva el del sello, asi que el aviso del publish va',
94
+ 'a seguir hablando de la corrida anterior aunque esta salga verde.',
95
+ 'Para sellar: `pnpm test`, sin flag de reporter.',
96
+ '(YF_SELLO_NO_HACE_FALTA=1 apaga este aviso.)',
97
+ '',
98
+ ];
99
+ /** La huella y el estado de git, calculados en UN solo lugar. Si el sello y
100
+ * la verificacion los calcularan por su cuenta, un dia divergirian y la
101
+ * comparacion diria "no coincide" sin que nada hubiera cambiado -- o peor,
102
+ * al reves (simetria productor/consumidor, SQ 14). */
103
+ export function readGitState(cwd) {
104
+ const git = (args) => execFileSync('git', args, {
105
+ cwd, encoding: 'utf8', windowsHide: true, maxBuffer: 64 * 1024 * 1024,
106
+ stdio: ['ignore', 'pipe', 'ignore'],
107
+ });
108
+ const commit = git(['rev-parse', 'HEAD']).trim();
109
+ /* El sello se EXCLUYE de su propia huella. En este repositorio esta
110
+ * ignorado por git y no aparece, pero en uno que no lo ignore si: y
111
+ * entonces escribir el sello cambiaria la huella que el sello acaba de
112
+ * guardar, y el instrumento diria "no coincide" SIEMPRE. Un instrumento
113
+ * que avisa siempre deja de avisar -- se desactiva. */
114
+ const status = git(['status', '--porcelain', '-uall'])
115
+ .split(/\r?\n/)
116
+ .filter((l) => !l.includes(BATTERY_STAMP_REL) && !l.includes(BATTERY_STAMP_REL.replace(/\//g, '\\')))
117
+ .join('\n');
118
+ const diff = git(['diff', 'HEAD']);
119
+ return {
120
+ commit,
121
+ dirty: status.trim().length > 0,
122
+ fingerprint: createHash('sha256').update(commit + '\n' + status + '\n' + diff, 'utf8').digest('hex'),
123
+ fingerprint_spec: FINGERPRINT_SPEC,
124
+ };
125
+ }
126
+ /** Los numeros de una corrida de vitest, del arbol de tareas que el
127
+ * reporter recibe. Pura a proposito: el arbol se puede sintetizar en un
128
+ * caso y asi el contador se prueba sin correr la bateria dentro de la
129
+ * bateria. */
130
+ export function countFromFiles(files) {
131
+ const counts = { files: files.length, passed: 0, failed: 0, skipped: 0 };
132
+ const walk = (task) => {
133
+ const kids = Array.isArray(task.tasks) ? task.tasks : undefined;
134
+ if (kids) {
135
+ for (const k of kids)
136
+ walk(k);
137
+ return;
138
+ }
139
+ const state = task.result?.state;
140
+ if (state === 'pass')
141
+ counts.passed++;
142
+ else if (state === 'fail')
143
+ counts.failed++;
144
+ else
145
+ counts.skipped++;
146
+ };
147
+ for (const f of files)
148
+ walk(f);
149
+ return counts;
150
+ }
151
+ export function writeBatteryStamp(repoRoot, stamp) {
152
+ const file = path.join(repoRoot, BATTERY_STAMP_REL);
153
+ fs.mkdirSync(path.dirname(file), { recursive: true });
154
+ fs.writeFileSync(file, JSON.stringify(stamp, null, 2) + '\n', 'utf8');
155
+ }
156
+ export function readBatteryStamp(repoRoot) {
157
+ try {
158
+ const raw = fs.readFileSync(path.join(repoRoot, BATTERY_STAMP_REL), 'utf8');
159
+ const parsed = JSON.parse(raw);
160
+ return parsed && parsed.version === 1 ? parsed : null;
161
+ }
162
+ catch {
163
+ return null;
164
+ }
165
+ }
166
+ export function judgeFreshness(stamp, current) {
167
+ if (!stamp)
168
+ return { verdict: 'never', stale: true, stamp, current };
169
+ if (!stamp.green)
170
+ return { verdict: 'red', stale: true, stamp, current };
171
+ if (stamp.filtered)
172
+ return { verdict: 'partial', stale: true, stamp, current };
173
+ if (stamp.fingerprint === current.fingerprint) {
174
+ return { verdict: 'same_content', stale: false, stamp, current };
175
+ }
176
+ return {
177
+ verdict: stamp.commit === current.commit ? 'same_commit' : 'other_commit',
178
+ stale: true, stamp, current,
179
+ };
180
+ }
181
+ function when(ms) {
182
+ const d = new Date(ms);
183
+ const pad = (n) => String(n).padStart(2, '0');
184
+ return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
185
+ + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
186
+ }
187
+ /** El aviso, con el formato que el dueno pidio: en que commit corrio la
188
+ * bateria por ultima vez, en que commit estas, y si no coinciden lo dice. */
189
+ export function renderFreshness(r) {
190
+ const out = [];
191
+ const cur = r.current.commit.slice(0, 7) + (r.current.dirty ? ' (arbol sucio)' : '');
192
+ if (r.verdict === 'never') {
193
+ out.push('bateria: NO HAY SELLO -- en este arbol no hay registro de ninguna corrida.');
194
+ out.push('Estas en: ' + cur + '.');
195
+ out.push('Eso NO quiere decir que la bateria no corrio: quiere decir que nadie lo');
196
+ out.push('registro, que para decidir si publicar es lo mismo.');
197
+ }
198
+ else {
199
+ const s = r.stamp;
200
+ const tail = s.counts.passed + ' pasados, ' + s.counts.failed + ' fallados, '
201
+ + s.counts.skipped + ' salteados, ' + s.counts.files + ' archivos';
202
+ out.push('bateria: ultima corrida en ' + s.commit.slice(0, 7) + (s.dirty ? ' (arbol sucio)' : '')
203
+ + ' el ' + when(s.ran_at) + ' -- ' + (s.green ? 'VERDE' : 'ROJA')
204
+ + (s.filtered ? ', PARCIAL' : '') + ' (' + tail + ').');
205
+ out.push('Estas en: ' + cur + '.');
206
+ if (r.verdict === 'same_content') {
207
+ out.push('Coinciden: la bateria vio EXACTAMENTE este contenido (huella '
208
+ + s.fingerprint.slice(0, 12) + ').');
209
+ }
210
+ else if (r.verdict === 'partial') {
211
+ out.push('AVISO: LA ULTIMA CORRIDA FUE PARCIAL (' + s.invocation + '), asi que el');
212
+ out.push('sello dice que paso una PARTE. La bateria completa no tiene registro aca.');
213
+ }
214
+ else if (r.verdict === 'red') {
215
+ out.push('AVISO: el ultimo sello es ROJO. Lo que se esta por empaquetar no tiene');
216
+ out.push('una corrida verde que lo respalde.');
217
+ }
218
+ else if (r.verdict === 'same_commit') {
219
+ out.push('AVISO: MISMO COMMIT, CONTENIDO DISTINTO. Las fuentes se movieron despues');
220
+ out.push('de la corrida (huella ' + s.fingerprint.slice(0, 12) + ' contra '
221
+ + r.current.fingerprint.slice(0, 12) + ').');
222
+ out.push('Esta es la forma exacta de 1.20.1: el tarball se armo con fuentes que la');
223
+ out.push('compuerta no habia visto. No edites fuentes durante el publish.');
224
+ }
225
+ else {
226
+ out.push('AVISO: LA BATERIA CORRIO EN OTRO COMMIT. Lo que se va a empaquetar no es');
227
+ out.push('lo que la bateria verifico.');
228
+ }
229
+ }
230
+ out.push('Huella: ' + r.current.fingerprint_spec + '.');
231
+ if (r.stale) {
232
+ out.push('La bateria (418 s) queda FUERA del gancho pre-push a proposito -- la');
233
+ out.push('decision y su motivo estan en docs/PROGRAMMING_NORMS.md. Este aviso es el');
234
+ out.push('instrumento de ese riesgo aceptado: correla con `pnpm test` antes de');
235
+ out.push('publicar, o publica sabiendo lo que falta.');
236
+ /* Y LA CAUSA MAS PROBABLE DE UN SELLO VIEJO NO ES QUE NADIE CORRIO LA
237
+ * BATERIA (medido 2026-11-01): es que corrio con `--reporter` explicito.
238
+ * Ese flag REEMPLAZA los reporters de la config, se lleva puesto el
239
+ * reporter del sello, y la corrida sale verde SIN DEJAR REGISTRO. Si no
240
+ * se nombra aca, el que lea "sello viejo" despues de ver 5600 tests en
241
+ * verde concluye que el instrumento miente, y el paso siguiente es
242
+ * sacarlo de la cadena. */
243
+ out.push('SI ACABAS DE CORRER LA BATERIA Y ESTE AVISO DICE QUE EL SELLO ES VIEJO:');
244
+ out.push('la corriste con `--reporter` explicito. Ese flag reemplaza los reporters');
245
+ out.push('de la config y se lleva el del sello, asi que la corrida no deja registro.');
246
+ out.push('Corre `pnpm test` (sin flag de reporter) y el sello queda.');
247
+ }
248
+ return out.join('\n');
249
+ }
250
+ /** El reporter de vitest que deja el sello. Pato-tipado (no importa tipos
251
+ * de vitest) para que este modulo siga siendo parte del producto y no de
252
+ * la bateria. */
253
+ export class BatteryStampReporter {
254
+ started = Date.now();
255
+ root;
256
+ invocation;
257
+ filtered;
258
+ constructor(opts) {
259
+ this.root = opts?.root ?? process.cwd();
260
+ const args = process.argv.slice(2);
261
+ this.invocation = opts?.invocation ?? ('vitest ' + args.join(' ')).trim();
262
+ this.filtered = opts?.filtered ?? isFilteredInvocation(args);
263
+ }
264
+ onInit() { this.started = Date.now(); }
265
+ onFinished(files = [], errors = []) {
266
+ try {
267
+ const counts = countFromFiles(files);
268
+ const stamp = {
269
+ version: 1,
270
+ ran_at: Date.now(),
271
+ duration_ms: Date.now() - this.started,
272
+ counts,
273
+ green: counts.failed === 0 && (!errors || errors.length === 0) && counts.files > 0,
274
+ invocation: this.invocation,
275
+ filtered: this.filtered,
276
+ ...readGitState(this.root),
277
+ };
278
+ writeBatteryStamp(this.root, stamp);
279
+ }
280
+ catch {
281
+ /* Un sello que no se pudo escribir no puede voltear una corrida: el
282
+ * instrumento no es la bateria. El silencio lo cubre el otro lado --
283
+ * `battery_freshness` distingue "no hay sello" de "esta fresco". */
284
+ }
285
+ }
286
+ }
287
+ //# sourceMappingURL=battery_stamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery_stamp.js","sourceRoot":"","sources":["../../src/core/battery_stamp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * LO QUE LA CADENA NO CORRIO -- F-14 / F-15 (pedido del dueno, 2026-11-01).
3
+ *
4
+ * EL DEFECTO. Medido en el repositorio: `.github/workflows/ci.yml` instala y
5
+ * corre `typecheck`, nada mas; `prepublishOnly` corre SEIS compuertas (build,
6
+ * typecheck, catalog:lint, test, brain-matrix:gate, hito4:lint). Asi que un
7
+ * VERDE en el badge de CI no significa "la bateria paso" -- significa "los
8
+ * tipos compilan" -- y nada en la salida de la corrida lo dice. Esta forma ya
9
+ * costo una vez: 1.20.1 salio publicada con un bug adentro.
10
+ *
11
+ * El dueno NO pidio mover las compuertas pesadas a la cadena (es una decision
12
+ * suya, con su motivo: la matriz multi-OS consumia la cuota en una rafaga).
13
+ * Pidio que la corrida DECLARE lo que no corrio. Es la misma regla que el
14
+ * metodo aplica a un recibo: una corrida que no mide algo tiene que decir que
15
+ * no lo midio, porque el que lee un verde no tiene otra forma de saberlo.
16
+ *
17
+ * Y LA PAREJA QUE EL DUENO SOSPECHO Y RESULTO PEOR (F-15). `lefthook.yml`
18
+ * declaraba un `pre-push` con `pnpm test` + `brain-matrix:gate`: dos
19
+ * compuertas, no las seis del publish. Pero antes de eso: el gancho no
20
+ * estaba instalado y no podia estarlo -- `.git/hooks` vacio,
21
+ * `core.hooksPath` sin poner, `lefthook` no era dependencia. Asi que la
22
+ * "preflight local" a la que el comentario de ci.yml remite no la corria
23
+ * ningun mecanismo: era un habito. Y el circulo se cerraba solo: ci.yml
24
+ * decia que lo pesado corre local, y lefthook.yml decia "CI is the real
25
+ * gate". Cada uno senalaba al otro y ninguno lo hacia.
26
+ *
27
+ * ARREGLADO EL 2026-11-01, con la decision del dueno: `lefthook` es
28
+ * devDependency, el ciclo `prepare` lo instala, y el pre-push corre el
29
+ * su paquete, todo `prepublishOnly` menos la bateria, dentro de un
30
+ * presupuesto declarado (aviso 30 s, techo 35 s). La cadena NO
31
+ * se ensancha -- se mantiene liviana por cuota -- asi que para el trabajo en
32
+ * rama el gancho es la UNICA puerta. De ahi los dos agregados de este
33
+ * modulo: `triggers`, porque una cadena que solo dispara en main no es una
34
+ * segunda puerta para una rama; y la cobertura POR EQUIVALENCIA, porque el
35
+ * gancho invoca los scripts directo (arranca mas rapido) y `build` corre el
36
+ * mismo `tsc` que `typecheck` -- decir "no corre typecheck" ahi seria una
37
+ * falsa alarma, y una falsa alarma termina con el instrumento apagado.
38
+ *
39
+ * POR QUE ESTO SE DERIVA Y NO SE ESCRIBE A MANO. Una lista fija de "lo que
40
+ * no corre" es una segunda fuente de verdad, y se desincroniza el dia que
41
+ * alguien agrega una compuerta al publish -- el mismo defecto que este
42
+ * modulo esta reportando. Asi que las tres listas salen de los archivos que
43
+ * YA son la verdad (package.json, el workflow, lefthook.yml) y la diferencia
44
+ * se calcula. Si una de las tres no se puede leer, esto FALLA: una medicion
45
+ * que no se pudo hacer no se reporta como "no falta nada".
46
+ *
47
+ * ASCII-only.
48
+ */
49
+ export interface CiGapReport {
50
+ /** Los scripts npm que la corrida de CI ejecuta de verdad. */
51
+ ran: string[];
52
+ /** Los que la compuerta de publicacion exige y la cadena NO corre. */
53
+ not_run: string[];
54
+ /** Los que el gancho local declara (este o no instalado). Son los
55
+ * nombres de compuerta del publish que el gancho CUBRE: directamente
56
+ * (corre el script) o por equivalencia (corre otro script que invoca el
57
+ * mismo programa -- `build` corre el mismo `tsc` que `typecheck`). */
58
+ hook_declares: string[];
59
+ /** De los anteriores, los que el gancho cubre por EQUIVALENCIA y no por
60
+ * nombre. Se separan porque son la parte que hay que poder explicar: un
61
+ * "lo cubre otro" sin decir cual es una afirmacion sin respaldo. */
62
+ hook_covers_by_equivalence: Array<{
63
+ gate: string;
64
+ via: string;
65
+ }>;
66
+ /** Los del publish que el gancho local tampoco corre. */
67
+ hook_not_run: string[];
68
+ /** La cobertura de plataforma de la corrida, leida del workflow. */
69
+ os: string[];
70
+ node: string[];
71
+ /** Lo que ninguna de las tres puertas corre. */
72
+ nobody_runs: string[];
73
+ /** CUANDO dispara la cadena, leido del `on:` del workflow. El cuarto
74
+ * hueco de F-15: con `push: [main]` y `pull_request: [main]`, una rama
75
+ * sin PR no tiene NINGUNA corrida, asi que para el trabajo en rama la
76
+ * cadena no es una segunda puerta -- no es ninguna. */
77
+ triggers: {
78
+ push: string[];
79
+ pull_request: string[];
80
+ other: string[];
81
+ };
82
+ }
83
+ /** Los nombres de script que una linea de comandos invoca. Cubre las formas
84
+ * que este repositorio usa de verdad (`pnpm run x`, `pnpm x`, `npm run x`)
85
+ * y nada mas: inventar formas que nadie escribe es ruido. */
86
+ export declare function scriptsInvoked(command: string): string[];
87
+ /** Los PROGRAMAS que una linea de comandos invoca de verdad, expandiendo
88
+ * los scripts del paquete. Es lo que permite decir "el gancho cubre
89
+ * `typecheck` porque corre `build`, que corre el mismo tsc" sin escribirlo
90
+ * a mano en ninguna lista.
91
+ *
92
+ * La forma de cada invocacion: el programa, y para `node` tambien el
93
+ * archivo (`node:scripts/catalog_lint.mjs`) -- dos scripts de node
94
+ * distintos no son la misma compuerta. */
95
+ export declare function invocationsOf(command: string, scripts?: Record<string, string>, seen?: Set<string>): string[];
96
+ /** El `on:` del workflow: en que ramas dispara cada evento. Atajo sobre el
97
+ * YAML, igual que `runLines`, y por el mismo motivo: un parser completo
98
+ * seria una dependencia nueva para leer cuatro lineas, y el caso corre
99
+ * sobre el archivo real, asi que una forma que esto no entienda sale roja
100
+ * en vez de salir en silencio. */
101
+ export declare function readTriggers(yaml: string): {
102
+ push: string[];
103
+ pull_request: string[];
104
+ other: string[];
105
+ };
106
+ export interface CiGapInputs {
107
+ /** El contenido de package.json, ya parseado. */
108
+ pkg: {
109
+ scripts?: Record<string, string>;
110
+ };
111
+ /** El YAML del workflow de CI. */
112
+ workflow: string;
113
+ /** El YAML de lefthook. Opcional: un repositorio sin gancho declarado es
114
+ * un estado legitimo, y entonces las dos listas del gancho son vacias. */
115
+ lefthook?: string;
116
+ }
117
+ /** Derivar las tres listas y su diferencia. Tira cuando no puede medir: una
118
+ * medicion imposible no se reporta como "no falta nada". */
119
+ export declare function deriveCiGaps(input: CiGapInputs): CiGapReport;
120
+ /** Solo la seccion `pre-push` de lefthook.yml. El `pre-commit` corre en otro
121
+ * momento y meterlo aca diria que el push verifica mas de lo que verifica,
122
+ * que es el defecto, al revez. */
123
+ export declare function preprocessLefthookPrePush(yaml: string): string;
124
+ /** LA DECLARACION, con el formato que el dueno pidio textualmente.
125
+ *
126
+ * Primero lo que SI se verifico y despues lo que NO, en ese orden y en la
127
+ * misma salida: una lista de ausencias al final de un log de 300 lineas no
128
+ * la lee nadie. */
129
+ export declare function renderCiGaps(r: CiGapReport): string;
130
+ //# sourceMappingURL=ci_gaps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci_gaps.d.ts","sourceRoot":"","sources":["../../src/core/ci_gaps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,sEAAsE;IACtE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;2EAGuE;IACvE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB;;yEAEqE;IACrE,0BAA0B,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,yDAAyD;IACzD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oEAAoE;IACpE,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gDAAgD;IAChD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB;;;4DAGwD;IACxD,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACvE;AAED;;8DAE8D;AAC9D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAWxD;AAED;;;;;;;2CAO2C;AAC3C,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACpC,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAC5B,MAAM,EAAE,CA4CV;AAmDD;;;;mCAImC;AACnC,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CA+BtG;AAED,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,GAAG,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC1C,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB;+EAC2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;6DAC6D;AAC7D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAwD5D;AAED;;mCAEmC;AACnC,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW9D;AAED;;;;oBAIoB;AACpB,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CA8CnD"}