xonecode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +167 -0
  2. package/dist/agent/authEnDisco.js +56 -0
  3. package/dist/agent/authEnDisco.js.map +1 -0
  4. package/dist/agent/configEnDisco.js +109 -0
  5. package/dist/agent/configEnDisco.js.map +1 -0
  6. package/dist/agent/crearProyecto.js +34 -0
  7. package/dist/agent/crearProyecto.js.map +1 -0
  8. package/dist/agent/entorno.js +124 -0
  9. package/dist/agent/entorno.js.map +1 -0
  10. package/dist/agent/guionizado.js +53 -0
  11. package/dist/agent/guionizado.js.map +1 -0
  12. package/dist/agent/instantanea.js +129 -0
  13. package/dist/agent/instantanea.js.map +1 -0
  14. package/dist/agent/interrupts.js +83 -0
  15. package/dist/agent/interrupts.js.map +1 -0
  16. package/dist/agent/mensajes.js +24 -0
  17. package/dist/agent/mensajes.js.map +1 -0
  18. package/dist/agent/modelos.js +42 -0
  19. package/dist/agent/modelos.js.map +1 -0
  20. package/dist/agent/normalizar.js +20 -0
  21. package/dist/agent/normalizar.js.map +1 -0
  22. package/dist/agent/perfiles.js +103 -0
  23. package/dist/agent/perfiles.js.map +1 -0
  24. package/dist/agent/proyecto.js +107 -0
  25. package/dist/agent/proyecto.js.map +1 -0
  26. package/dist/agent/puente.js +142 -0
  27. package/dist/agent/puente.js.map +1 -0
  28. package/dist/agent/resumenDeTool.js +48 -0
  29. package/dist/agent/resumenDeTool.js.map +1 -0
  30. package/dist/agent/skills.js +65 -0
  31. package/dist/agent/skills.js.map +1 -0
  32. package/dist/agent/textoDeTool.js +113 -0
  33. package/dist/agent/textoDeTool.js.map +1 -0
  34. package/dist/agent/turnoReal.js +173 -0
  35. package/dist/agent/turnoReal.js.map +1 -0
  36. package/dist/agent/verificador.js +65 -0
  37. package/dist/agent/verificador.js.map +1 -0
  38. package/dist/agent/xoneAgent.js +106 -0
  39. package/dist/agent/xoneAgent.js.map +1 -0
  40. package/dist/bin.js +9 -0
  41. package/dist/bin.js.map +1 -0
  42. package/dist/cli/aprobar.js +82 -0
  43. package/dist/cli/aprobar.js.map +1 -0
  44. package/dist/cli/config.js +133 -0
  45. package/dist/cli/config.js.map +1 -0
  46. package/dist/cli/consola.js +346 -0
  47. package/dist/cli/consola.js.map +1 -0
  48. package/dist/cli/describe.js +53 -0
  49. package/dist/cli/describe.js.map +1 -0
  50. package/dist/cli/doctor.js +45 -0
  51. package/dist/cli/doctor.js.map +1 -0
  52. package/dist/cli/main.js +520 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/cli/markdown.js +90 -0
  55. package/dist/cli/markdown.js.map +1 -0
  56. package/dist/cli/run.js +164 -0
  57. package/dist/cli/run.js.map +1 -0
  58. package/dist/cli/spinner.js +89 -0
  59. package/dist/cli/spinner.js.map +1 -0
  60. package/dist/cli/stdio.js +193 -0
  61. package/dist/cli/stdio.js.map +1 -0
  62. package/dist/cli/tema.js +40 -0
  63. package/dist/cli/tema.js.map +1 -0
  64. package/dist/cli/verify.js +62 -0
  65. package/dist/cli/verify.js.map +1 -0
  66. package/dist/core/bitacora.js +30 -0
  67. package/dist/core/bitacora.js.map +1 -0
  68. package/dist/core/config.js +236 -0
  69. package/dist/core/config.js.map +1 -0
  70. package/dist/core/contextos.js +56 -0
  71. package/dist/core/contextos.js.map +1 -0
  72. package/dist/core/deps.js +51 -0
  73. package/dist/core/deps.js.map +1 -0
  74. package/dist/core/diff.js +79 -0
  75. package/dist/core/diff.js.map +1 -0
  76. package/dist/core/esqueleto.js +366 -0
  77. package/dist/core/esqueleto.js.map +1 -0
  78. package/dist/core/events.js +2 -0
  79. package/dist/core/events.js.map +1 -0
  80. package/dist/core/modelos.js +85 -0
  81. package/dist/core/modelos.js.map +1 -0
  82. package/dist/core/notify.js +106 -0
  83. package/dist/core/notify.js.map +1 -0
  84. package/dist/core/ports.js +114 -0
  85. package/dist/core/ports.js.map +1 -0
  86. package/dist/core/turno.js +127 -0
  87. package/dist/core/turno.js.map +1 -0
  88. package/dist/vendor/hitl.js +103 -0
  89. package/dist/vendor/hitl.js.map +1 -0
  90. package/dist/vendor/skillLoaders/catalog.js +74 -0
  91. package/dist/vendor/skillLoaders/catalog.js.map +1 -0
  92. package/dist/vendor/tokenTracking.js +58 -0
  93. package/dist/vendor/tokenTracking.js.map +1 -0
  94. package/package.json +41 -0
  95. package/skills/xone-debugging/SKILL.md +52 -0
  96. package/skills/xone-debugging/references/faq.md +561 -0
  97. package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
  98. package/skills/xone-development/SKILL.md +105 -0
  99. package/skills/xone-development/references/anti-patrones.md +94 -0
  100. package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
  101. package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
  102. package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
  103. package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
  104. package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
  105. package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
  106. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
  107. package/skills/xone-development/references/datos/appdata.md +611 -0
  108. package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
  109. package/skills/xone-development/references/datos/http.md +366 -0
  110. package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
  111. package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
  112. package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
  113. package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
  114. package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
  115. package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
  116. package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
  117. package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
  118. package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
  119. package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
  120. package/skills/xone-development/references/indice-completo.md +77 -0
  121. package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
  122. package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
  123. package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
  124. package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
  125. package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
  126. package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
  127. package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
  128. package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
  129. package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
  130. package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
  131. package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
  132. package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
  133. package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
  134. package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
  135. package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
  136. package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
  137. package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
  138. package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
  139. package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
  140. package/skills/xone-development/references/tipos-de-prop.md +28 -0
  141. package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
  142. package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
  143. package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
  144. package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
  145. package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
  146. package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
  147. package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
  148. package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
  149. package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
  150. package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
  151. package/skills/xone-development/references/xml-ui/mapas.md +685 -0
  152. package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
  153. package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
  154. package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
  155. package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
  156. package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
  157. package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
  158. package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
  159. package/skills/xone-plan-builder/SKILL.md +183 -0
  160. package/skills/xone-plan-builder/agents/openai.yaml +5 -0
  161. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
  162. package/skills/xone-project-generator/SKILL.md +111 -0
  163. package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
  164. package/skills/xone-project-generator/references/colecciones-base.md +16 -0
  165. package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
  166. package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
  167. package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
  168. package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
  169. package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
  170. package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
  171. package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
  172. package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
  173. package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
  174. package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
  175. package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
  176. package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
  177. package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
  178. package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
  179. package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
  180. package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
  181. package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
  182. package/skills/xone-review/SKILL.md +189 -0
  183. package/skills/xone-spec-builder/SKILL.md +235 -0
  184. package/skills/xone-spec-builder/agents/openai.yaml +5 -0
  185. package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
  186. package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
  187. package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
@@ -0,0 +1,164 @@
1
+ import { correrTurno } from "../core/turno.js";
2
+ import { pedirDecisiones, preguntarPorStdin } from "./aprobar.js";
3
+ import { crearPielStdio, escribirEnStdout } from "./stdio.js";
4
+ import { AgenteGuionizado } from "../agent/guionizado.js";
5
+ import { esDoble } from "../core/ports.js";
6
+ import { inspeccionar } from "../agent/entorno.js";
7
+ import { tomarInstantanea } from "../agent/instantanea.js";
8
+ import { SkillsEnDisco } from "../agent/skills.js";
9
+ import { Modelos } from "../agent/modelos.js";
10
+ import { abrirSesionReal } from "../agent/turnoReal.js";
11
+ /** La cabecera del turno real: qué hay detrás antes de que empiece a gastar. */
12
+ function cabecera(escribir, modelos, skills, instantanea, entorno) {
13
+ const desc = modelos.descripcion();
14
+ const papeles = ["rapido", "trabajo", "afilado"];
15
+ escribir("--- turno real ---\n");
16
+ for (const p of papeles)
17
+ escribir(` ${p.padEnd(8)} ${desc[p]}\n`);
18
+ escribir(` skills: ${skills.catalogo().length}\n`);
19
+ escribir(` foto del ANTES: ${instantanea.via === "git" ? "git" : "huellas de fichero"}\n`);
20
+ if (entorno.vistasAplanadas.length > 0) {
21
+ escribir(` ${entorno.vistasAplanadas.length} vista(s) aplanada(s) .xml ocultas al agente\n`);
22
+ }
23
+ escribir("\n");
24
+ }
25
+ /**
26
+ * Un disparo: una petición, un turno, y a la calle. Pipeable.
27
+ *
28
+ * Devuelve el código de salida. **Una pausa sin humano no es un éxito**: en modo un
29
+ * disparo no hay a quién preguntar, así que se sale con código distinto de cero para que
30
+ * CI no lea como «hecho» un turno que se quedó esperando una aprobación que nadie iba a
31
+ * dar. No se aprueba por omisión: aprobar EJECUTA, y ante la duda va la opción recuperable.
32
+ */
33
+ export async function cmdRun(opciones) {
34
+ const escribir = opciones.escribir ?? escribirEnStdout;
35
+ if (opciones.real)
36
+ return correrReal(opciones, escribir);
37
+ const agente = new AgenteGuionizado(opciones.retardoMs ?? 0);
38
+ const piel = crearPielStdio(escribir);
39
+ if (esDoble(agente)) {
40
+ escribir("⚠ AGENTE DE PEGA: esto es un guion, no ha corrido ningún modelo.\n\n");
41
+ }
42
+ let pausado = false;
43
+ const pielQueApunta = {
44
+ ...piel,
45
+ pausa: (pendientes) => {
46
+ pausado = true;
47
+ piel.pausa(pendientes);
48
+ },
49
+ };
50
+ const bitacora = await correrTurno(agente.turno(opciones.peticion), pielQueApunta, {
51
+ avisos: (b) => (b.corrio("verify") && esDoble(agente) ? ["⚠ El veredicto es de pega."] : []),
52
+ });
53
+ if (pausado) {
54
+ escribir("\n(sin humano en modo `run`: nada se aprobó y nada se aplicó)\n");
55
+ return 2;
56
+ }
57
+ return bitacora.corrio("bloqueado") ? 1 : 0;
58
+ }
59
+ /**
60
+ * Excepción centinela del corte «sin humano»: privada a este fichero, nunca una genérica
61
+ * (un `Error` suelto se confundiría con un fallo real del turno).
62
+ *
63
+ * Para qué existe: `sesion.turno` SIEMPRE reanuda el grafo tras `pedirAprobacion`, incluso
64
+ * si todas las decisiones son rechazos —es lo correcto para la consola, donde el modelo
65
+ * tiene que saber que se rechazó. Pero el `run` de un disparo sin terminal es otro caso:
66
+ * los rechazos no los dio un humano sino el fail-closed, reanudar gastaría una llamada más
67
+ * al modelo sin que nadie vaya a cambiar de respuesta, y el comportamiento exigido es NO
68
+ * reanudar, dar el diff y salir con 2. La única forma de colgar la reanudación sin tocar
69
+ * `turnoReal.ts` es lanzar en el callback: el corte ocurre DESPUÉS de preguntar y decidir,
70
+ * o sea la pregunta y el «→ rechazado» se ven igual que siempre.
71
+ */
72
+ class CorteSinHumano extends Error {
73
+ }
74
+ /** El turno real, en el orden del plan: entorno → foto → sesión → cabecera → turno → cambios. */
75
+ async function correrReal(opciones, escribir) {
76
+ const raiz = process.cwd();
77
+ const piel = crearPielStdio(escribir);
78
+ // 1. ¿Hay un proyecto aquí? Mismo diagnóstico que `doctor`, misma frase: no construir
79
+ // NADA sobre un sitio que no es un proyecto XOne.
80
+ const entorno = await inspeccionar(raiz);
81
+ if (!entorno.esProyectoXone) {
82
+ escribir(`${entorno.raiz} (no hay app.xml aquí)\n`);
83
+ escribir("✗ falta algo imprescindible: xonecode no puede trabajar aquí.\n");
84
+ return 1;
85
+ }
86
+ // 2. La foto del ANTES, ANTES de construir nada: además de anunciarla en la cabecera, es
87
+ // la fuente del diff de la rama «sin humano» —esa rama sale por la centinela ANTES de
88
+ // que el turno devuelva nada, así que no puede usar el diff por-turno de la sesión.
89
+ const instantanea = await tomarInstantanea(raiz, entorno.git);
90
+ // 3. Lo que la sesión necesita. Los ficheros del proyecto NO se calculan aquí: los
91
+ // recorre la propia sesión al construir el agente.
92
+ const skills = new SkillsEnDisco();
93
+ const modelos = new Modelos(opciones.fuentes ?? {});
94
+ // 4. La cabecera va ANTES del turno: qué modelo, cuántas skills, cómo se tomó la foto.
95
+ cabecera(escribir, modelos, skills, instantanea, entorno);
96
+ // Con TTY el Enter aprueba; sin él NO, y es deliberado: en un pipe o en CI una línea en
97
+ // blanco no demuestra que haya nadie mirando, y esto aprueba escrituras sobre un
98
+ // proyecto real. La regla vive en `interpretAnswer`; aquí solo se le dice si hay TTY.
99
+ const interactivo = process.stdin.isTTY === true;
100
+ const preguntar = preguntarPorStdin();
101
+ let sinHumano = false;
102
+ const sesion = await abrirSesionReal({
103
+ raiz,
104
+ modelos,
105
+ skills,
106
+ entorno,
107
+ pedirAprobacion: async (lista, ficheros, diffs) => {
108
+ // **Se pregunta SIEMPRE, con TTY o sin él.** Cortar aquí sin preguntar dejaría
109
+ // muerto el conjunto de respuestas sin-TTY de `interpretAnswer`, que existe
110
+ // precisamente para que en un pipe o en CI se pueda aprobar con un `s` EXPLÍCITO.
111
+ // La garantía no la da negarse a preguntar, la da la asimetría: sin un sí
112
+ // explícito, es rechazo. Y si stdin está cerrado, `preguntarPorStdin` resuelve con
113
+ // cadena vacía — o sea, rechazo.
114
+ if (!interactivo) {
115
+ escribir(`\n⏸ ${lista.length} escritura(s) piden aprobación, y no hay terminal.\n`);
116
+ escribir(" Sin un «s» explícito se rechazan (una línea en blanco NO aprueba).\n");
117
+ }
118
+ const decisiones = await pedirDecisiones(lista, preguntar, escribir, {
119
+ interactive: interactivo,
120
+ fichero: (id) => ficheros.get(id),
121
+ diff: (id) => diffs.get(id),
122
+ });
123
+ if (!interactivo && [...decisiones.values()].every((d) => d.type === "reject")) {
124
+ sinHumano = true;
125
+ throw new CorteSinHumano();
126
+ }
127
+ return decisiones;
128
+ },
129
+ });
130
+ // 5-6. El turno: la sesión conduce el stream y el bucle de aprobación — el tope de
131
+ // rondas lo controla ella. La centinela recorta el caso «sin humano» antes de la
132
+ // reanudación; cualquier otro throw sigue siendo un fallo real y se propaga.
133
+ let bitacora = null;
134
+ let cambios;
135
+ let cortadoPorTope = false;
136
+ try {
137
+ ({ bitacora, cambios, cortadoPorTope } = await sesion.turno(opciones.peticion, piel));
138
+ }
139
+ catch (e) {
140
+ if (!(e instanceof CorteSinHumano))
141
+ throw e;
142
+ cambios = await instantanea.cambios();
143
+ }
144
+ // 7. El diff contra la foto. Un turno que no tocó nada TIENE que verse igual.
145
+ escribir("\n--- cambios en el proyecto ---\n");
146
+ if (cambios.length === 0) {
147
+ escribir("sin cambios en el proyecto\n");
148
+ }
149
+ else {
150
+ for (const c of cambios)
151
+ escribir(` [${c.clase}] ${c.ruta}\n`);
152
+ }
153
+ if (sinHumano) {
154
+ escribir("\n(sin humano en modo `run`: nada se aprobó y nada se aplicó)\n");
155
+ return 2;
156
+ }
157
+ // Agotar el tope de rondas es el MISMO desenlace que quedarse sin humano: hay escrituras
158
+ // sin resolver y nada se aplicó. Sin esta línea caía al camino normal y salía 0 — o sea,
159
+ // CI habría leído como éxito un turno que se quedó a medias.
160
+ if (cortadoPorTope)
161
+ return 2;
162
+ return bitacora.corrio("bloqueado") ? 1 : 0;
163
+ }
164
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * El spinner de fase: `⠋ planificando (12s)` animando LA ÚLTIMA línea.
3
+ *
4
+ * En una consola que no repinta, solo la última línea escrita se puede volver a
5
+ * pintar (con `\r`), y solo mientras nadie más escriba: eso es exactamente una fase
6
+ * del agente, que llena los segundos en que no pasa nada visible. La disciplina de
7
+ * «una sola línea al fondo» es la cascada: **cualquier otra escritura termina el
8
+ * spinner primero**, y el historial no se toca — la fase queda como línea estática,
9
+ * igual que ha salido siempre.
10
+ *
11
+ * Sin TTY (tema sin color) no hay nada que animar: la misma línea estática de
12
+ * siempre, y ningún temporizador.
13
+ */
14
+ const CUADROS = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
15
+ /** Los segundos que lleva la fase: vacío hasta que cumple el primer segundo. */
16
+ export function sufijoDeTiempo(ms) {
17
+ const s = Math.floor(ms / 1000);
18
+ return s > 0 ? ` (${s}s)` : "";
19
+ }
20
+ export class AnimadorDeFase {
21
+ escribir;
22
+ tema;
23
+ intervalo;
24
+ reloj;
25
+ cuadro = 0;
26
+ texto = "";
27
+ t0 = 0;
28
+ temporizador;
29
+ constructor(escribir, tema,
30
+ /** Cómo de seguido repinta el fotograma. Entra por parámetro para los tests. */
31
+ intervalo = 120,
32
+ /** El reloj, inyectado por la misma razón: un test mueve el tiempo, no lo espera. */
33
+ reloj = Date.now) {
34
+ this.escribir = escribir;
35
+ this.tema = tema;
36
+ this.intervalo = intervalo;
37
+ this.reloj = reloj;
38
+ }
39
+ activo() {
40
+ return this.texto !== "";
41
+ }
42
+ /**
43
+ * Empieza a animar una fase. Sin color es la línea estática de siempre y no arranca
44
+ * nada; con color, la fase anterior (si la había) queda como línea estática.
45
+ */
46
+ empieza(texto) {
47
+ if (this.tema.borrar === "") {
48
+ this.escribir(` · ${texto}\n`);
49
+ return;
50
+ }
51
+ this.termina();
52
+ this.texto = texto;
53
+ this.t0 = this.reloj();
54
+ this.cuadro = 0;
55
+ this.pinta();
56
+ this.temporizador = setInterval(() => this.pinta(), this.intervalo);
57
+ // Un spinner olvidado no puede colgar el proceso: el temporizador no mantiene el
58
+ // bucle de eventos vivo por sí solo.
59
+ this.temporizador.unref();
60
+ }
61
+ /**
62
+ * Repinta el fotograma. Público a propósito: el `setInterval` llama aquí, y los
63
+ * tests también — sin esperar 120 ms de verdad.
64
+ */
65
+ pinta() {
66
+ if (!this.activo())
67
+ return;
68
+ // Se pinta el cuadro y DESPUÉS se avanza: el primer fotograma es el ⠋, no el
69
+ // segundo — la fase que empieza no debería llegar ya movida.
70
+ this.escribir(`\r ${this.tema.mudo}${CUADROS[this.cuadro]} ${this.texto}` +
71
+ `${sufijoDeTiempo(this.reloj() - this.t0)}${this.tema.reset}`);
72
+ this.cuadro = (this.cuadro + 1) % CUADROS.length;
73
+ }
74
+ /**
75
+ * Deja la fase como línea estática en el historial y para el reloj. Sin fase activa
76
+ * no escribe nada: la cascada lo llama antes de CUALQUIER otra escritura.
77
+ */
78
+ termina() {
79
+ if (!this.activo())
80
+ return;
81
+ if (this.temporizador !== undefined)
82
+ clearInterval(this.temporizador);
83
+ // El borrado limpia lo que el fotograma animado dejara escrito más allá de la
84
+ // línea estática: «(12s)» es más largo que el punto de la línea definitiva.
85
+ this.escribir(`\r${this.tema.borrar} · ${this.texto}\n`);
86
+ this.texto = "";
87
+ }
88
+ }
89
+ //# sourceMappingURL=spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/cli/spinner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,193 @@
1
+ import { crearTema } from "./tema.js";
2
+ import { RenderizadorDeMarkdown, puntoSeguro } from "./markdown.js";
3
+ import { AnimadorDeFase } from "./spinner.js";
4
+ /**
5
+ * Por debajo de esto, un párrafo que aún no ha visto su `\n` puede esperar: fluye al
6
+ * confirmarse la línea. Por encima, se corta por un punto seguro (`puntoSeguro`) para
7
+ * que el streaming siga vivo en los párrafos largos.
8
+ */
9
+ const MINIMO_DE_FLUJO = 80;
10
+ /**
11
+ * La piel de terminal.
12
+ *
13
+ * El streaming del asistente se suelta por LÍNEA CONFIRMADA (y, en párrafos largos, por
14
+ * cortes que no rompen marcadores), y no por trozo: una consola que NO repinta solo
15
+ * puede escribir una línea cuando su texto es definitivo — un `**` partido entre dos
16
+ * escrituras queda literal para siempre. Sigue siendo streaming: la línea confirmada
17
+ * se pinta en el acto, y un colchón que solo se suelta al final sería un `ainvoke`
18
+ * disfrazado.
19
+ *
20
+ * Las otras dos cosas medidas, con el mismo síntoma (el streaming no se ve) y causas
21
+ * distintas:
22
+ *
23
+ * 1. **`flush` explícito** (en `escribirEnStdout`). Sin él la salida se queda en el
24
+ * buffer y no aparece hasta que se vacía sola — un fallo que solo se manifiesta en
25
+ * procesos de larga duración, o sea justo en el caso de uso real.
26
+ * 2. **Un solo `escribir` por línea confirmada**: juntar trozos en un buffer y volcar
27
+ * al final también evita frases partidas, pero deja de ser streaming.
28
+ */
29
+ export function crearPielStdio(escribir, tema = crearTema(process.stdout.isTTY === true)) {
30
+ const render = new RenderizadorDeMarkdown(tema);
31
+ let colchon = "";
32
+ let inicioDeLinea = true;
33
+ // El spinner de fase: anima la ÚLTIMA línea mientras una fase dura. La cascada es
34
+ // esta `firme`: CUALQUIER otra escritura termina el spinner primero — así solo hay
35
+ // una línea viva al fondo, y el historial queda con la fase como línea estática.
36
+ const animador = new AnimadorDeFase(escribir, tema);
37
+ const firme = (t) => {
38
+ animador.termina();
39
+ escribir(t);
40
+ };
41
+ const volcar = (linea, inicio, conSalto) => {
42
+ firme(render.linea(linea, inicio) + (conSalto ? "\n" : ""));
43
+ };
44
+ return {
45
+ token: (texto) => {
46
+ colchon += texto;
47
+ // Las líneas confirmadas se sueltan enteras y en el acto.
48
+ while (true) {
49
+ const salto = colchon.indexOf("\n");
50
+ if (salto === -1)
51
+ break;
52
+ volcar(colchon.slice(0, salto), inicioDeLinea, true);
53
+ colchon = colchon.slice(salto + 1);
54
+ inicioDeLinea = true;
55
+ }
56
+ // Párrafo largo sin `\n` a la vista: fluir por un corte que no rompa marcadores.
57
+ // **Sin salto final**: la continuación sigue en la MISMA línea de terminal — un
58
+ // salto aquí partiría el párrafo en líneas de la longitud del corte. Y
59
+ // `inicioDeLinea` baja a false para que la continuación no se mire como cabecera
60
+ // ni viñeta: ya no empieza línea.
61
+ const corte = puntoSeguro(colchon, MINIMO_DE_FLUJO);
62
+ if (corte !== undefined) {
63
+ volcar(colchon.slice(0, corte), inicioDeLinea, false);
64
+ colchon = colchon.slice(corte);
65
+ inicioDeLinea = false;
66
+ }
67
+ },
68
+ cerrarLinea: () => {
69
+ // Lo que quede en el colchón es el final de la última línea. Si el mensaje acabó
70
+ // justo en un salto, no hay nada que añadir: esa línea ya se escribió entera.
71
+ if (colchon !== "")
72
+ volcar(colchon, inicioDeLinea, true);
73
+ colchon = "";
74
+ inicioDeLinea = true;
75
+ },
76
+ linea: (texto) => firme(` ${texto}\n`),
77
+ /**
78
+ * La fase, animada: sin TTY el propio animador escribe la línea estática de
79
+ * siempre y no arranca nada.
80
+ */
81
+ fase: (texto) => animador.empieza(texto),
82
+ pausa: (pendientes) => {
83
+ // Quien pinta el detalle de cada pendiente (origen, descripción, fichero) es
84
+ // `pedirDecisiones`, al preguntar una a una. Aquí sería redundante, y el viejo
85
+ // «responde: approve / reject» además mentía: `interpretAnswer` acepta
86
+ // s/si/sí/y/yes, no esas palabras.
87
+ // En el modo de un disparo lo que sigue a la pausa NO es la respuesta del usuario:
88
+ // es la aprobación, que conduce `pedirDecisiones`. Decir «lo siguiente que escribas
89
+ // es la respuesta» sería mentira y despistaría justo en el paso delicado.
90
+ firme(`\n(turno pausado: ${pendientes.length} aprobación(es) pendiente(s))\n`);
91
+ },
92
+ fin: (ms) => firme(`\n(${(ms / 1000).toFixed(1)}s)\n`),
93
+ };
94
+ }
95
+ /**
96
+ * El `escribir` de producción. El `flush` es implícito en Node —`process.stdout.write`
97
+ * escribe ya—, pero se pasa por aquí para que exista UN solo sitio que cambiar si algún
98
+ * día hace falta forzarlo.
99
+ */
100
+ export const escribirEnStdout = (texto) => {
101
+ process.stdout.write(texto);
102
+ };
103
+ /**
104
+ * El `preguntar` de producción para la consola: usa el ÚNICO `readline.Interface`
105
+ * compartido con el lazo de líneas y con `leerSecreto`. Tres lectores de stdin no pueden
106
+ * competir por sus eventos `data`; con un solo `rl`, `rl.question` se intercala sin
107
+ * robarle líneas al `for await` del lazo (es readline quien arbitra internamente).
108
+ *
109
+ * **Si el rl ya está cerrado (o se cierra a media pregunta), resuelve con cadena vacía
110
+ * y no lanza.** Es el EOF de un pipe que se agota mientras el turno corre — medido en
111
+ * e2e: la aprobación pregunta 20 segundos después de que stdin hiciera EOF, y el
112
+ * `readline was closed` abortaba el turno con el interrupt colgado. Y la cadena vacía
113
+ * es lo correcto además de lo seguro: `interpretAnswer` sin un «s» explícito RECHAZA.
114
+ * Mismo pacto que `leerSecreto` con su `alCerrar`.
115
+ */
116
+ export function crearPreguntar(rl) {
117
+ // `rl.closed` existe en runtime pero no está en los tipos de esta versión de
118
+ // @types/node — mismo caso que `rl.history` abajo, y mismo cast justificado.
119
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
+ const cerrado = () => rl.closed === true;
121
+ return (pregunta) => new Promise((resolver) => {
122
+ const alCerrar = () => resolver("");
123
+ rl.once("close", alCerrar);
124
+ if (cerrado()) {
125
+ rl.off("close", alCerrar);
126
+ resolver("");
127
+ return;
128
+ }
129
+ rl.question(pregunta, (respuesta) => {
130
+ rl.off("close", alCerrar);
131
+ resolver(respuesta);
132
+ });
133
+ });
134
+ }
135
+ /**
136
+ * `leerSecreto` de producción (para `/provider <nombre>`): mismo `rl` que el lazo y
137
+ * `preguntar`, con el eco apagado mientras dura la pregunta.
138
+ *
139
+ * **No hay API pública de readline para silenciar el eco por tecla manteniendo la edición
140
+ * de línea** (backspace, etc.): la única costura es `_writeToOutput`, un método interno y
141
+ * no documentado — verificado contra Node 22 (existe, y es la vía que usan las
142
+ * herramientas de terminal que piden contraseña sin librería aparte). Se sustituye para
143
+ * dejar pasar SOLO el propio prompt (para que se vea la pregunta) y silenciar cualquier
144
+ * otra escritura —el eco de cada tecla— hasta que la pregunta termine. `rl.history`
145
+ * tampoco está en los tipos de esta versión de @types/node aunque existe en runtime
146
+ * (documentado desde Node 15.8), así que comparte el mismo cast justificado.
147
+ *
148
+ * Se restaura SIEMPRE, y por DOS vías, porque un terminal que se queda sin eco obliga al
149
+ * usuario a cerrarlo:
150
+ * 1. En el callback de `rl.question`: el camino normal.
151
+ * 2. En un `close` de `rl` a media pregunta (Ctrl-D/EOF sin dar línea): sin esto la
152
+ * promesa no se resuelve nunca y el manejador de `/provider` se queda colgado para
153
+ * siempre dentro del lazo de la consola.
154
+ *
155
+ * Y se borra la clave de `rl.history` tras leerla: readline añade la respuesta de
156
+ * `question` al historial igual que cualquier línea, y una flecha-arriba la recuperaría
157
+ * en claro en la MISMA sesión.
158
+ */
159
+ export function crearLeerSecreto(rl) {
160
+ return (pregunta) => new Promise((resolve) => {
161
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
+ const rlAny = rl;
163
+ const escribirOriginal = rlAny._writeToOutput.bind(rlAny);
164
+ let restaurado = false;
165
+ const restaurar = () => {
166
+ if (restaurado)
167
+ return;
168
+ restaurado = true;
169
+ rlAny._writeToOutput = escribirOriginal;
170
+ rl.off("close", alCerrar);
171
+ };
172
+ const alCerrar = () => {
173
+ restaurar();
174
+ resolve("");
175
+ };
176
+ rl.once("close", alCerrar);
177
+ rlAny._writeToOutput = (cadena) => {
178
+ if (cadena === pregunta)
179
+ escribirOriginal(cadena);
180
+ };
181
+ rl.question(pregunta, (respuesta) => {
182
+ restaurar();
183
+ // El eco muteado se comió el '\r\n' que readline escribe al aceptar la línea: sin
184
+ // esto, la salida siguiente aparecería pegada a la pregunta.
185
+ escribirOriginal("\n");
186
+ const indice = rlAny.history.indexOf(respuesta);
187
+ if (indice !== -1)
188
+ rlAny.history.splice(indice, 1);
189
+ resolve(respuesta);
190
+ });
191
+ });
192
+ }
193
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/cli/stdio.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * El vocabulario de color de las pieles: tokens SEMÁNTICOS, nunca colores.
3
+ *
4
+ * Una piel pide significado (`tema.exito`, `tema.mudo`) igual que la TUI de
5
+ * qwen-code pide `theme.status.success`: si mañana hay temas de usuario o el diff
6
+ * quiere fondos en vez de texto, se cambia ESTE fichero y ninguna piel se entera.
7
+ * Es el mismo principio que `core/ports.ts` aplicado al color: lo que puede cambiar
8
+ * entra por una costura, no se pega a quien lo usa.
9
+ *
10
+ * La otra mitad del contrato: **con `conColor: false`, TODO token es cadena vacía.**
11
+ * Las pieles no ramifican por TTY — componen `${t.mudo}texto${t.reset}` igual en una
12
+ * tubería que en un terminal, y lo que mantiene limpios los pipes y los logs de CI es
13
+ * que el tema apagado no tiene nada que poner. (Y `src/cli/tema.test.ts` recorre
14
+ * `src/` entera y falla si alguien escribe un ANSI fuera de aquí.)
15
+ */
16
+ const CON_COLOR = {
17
+ texto: "",
18
+ mudo: "\x1b[2m",
19
+ negrita: "\x1b[1m",
20
+ exito: "\x1b[32m",
21
+ aviso: "\x1b[33m",
22
+ grave: "\x1b[31m",
23
+ // Mismo código que exito/grave a propósito: son tokens distintos porque significan
24
+ // cosas distintas. Si el diff algún día quiere fondo de color, cambia aquí.
25
+ anadido: "\x1b[32m",
26
+ quitado: "\x1b[31m",
27
+ prompt: "\x1b[36m",
28
+ reset: "\x1b[0m",
29
+ borrar: "\x1b[K",
30
+ };
31
+ /**
32
+ * El tema apagado se DERIVA del encendido, no se escribe aparte: un token nuevo que
33
+ * se olvide de apagarse falla en el test que recorre `Object.entries`, no en un pipe
34
+ * de CI a las tantas.
35
+ */
36
+ const SIN_COLOR = Object.fromEntries(Object.keys(CON_COLOR).map((nombre) => [nombre, ""]));
37
+ export function crearTema(conColor) {
38
+ return conColor ? CON_COLOR : SIN_COLOR;
39
+ }
40
+ //# sourceMappingURL=tema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tema.js","sourceRoot":"","sources":["../../src/cli/tema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAwBH,MAAM,SAAS,GAAS;IACtB,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,UAAU;IACjB,mFAAmF;IACnF,4EAA4E;IAC5E,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,UAAU;IACnB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF;;;;GAIG;AACH,MAAM,SAAS,GAAS,MAAM,CAAC,WAAW,CACxC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAClC,CAAC;AAErB,MAAM,UAAU,SAAS,CAAC,QAAiB;IACzC,OAAO,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { SimuladorVerifier, ErrorDelSimulador } from "../agent/verificador.js";
2
+ import { huella } from "../core/ports.js";
3
+ import { escribirEnStdout } from "./stdio.js";
4
+ const MARCA = {
5
+ error: "✗",
6
+ warning: "⚠",
7
+ info: "·",
8
+ };
9
+ /** `code fichero:linea` — y sin la línea cuando no viene, que es el caso normal. */
10
+ function donde(h) {
11
+ if (!h.fichero)
12
+ return "";
13
+ return h.linea === undefined ? ` ${h.fichero}` : ` ${h.fichero}:${h.linea}`;
14
+ }
15
+ /**
16
+ * Corre el verificador REAL sobre un proyecto y cuenta lo que encuentra.
17
+ *
18
+ * Sale con 1 si hay errores. Los avisos no tumban el comando: son de otra severidad y
19
+ * colapsarlos con los errores es una decisión de producto que nadie ha tomado.
20
+ */
21
+ export async function cmdVerify(ruta, escribir = escribirEnStdout, verificador = new SimuladorVerifier()) {
22
+ let informe;
23
+ try {
24
+ informe = await verificador.verificar(ruta);
25
+ }
26
+ catch (e) {
27
+ // Que no esté el binario NO es un fallo del proyecto. Distinguirlo importa: si no,
28
+ // un entorno mal montado se lee como «tu proyecto está roto».
29
+ escribir(`✗ no se pudo verificar: ${e instanceof Error ? e.message : String(e)}\n`);
30
+ // La pista solo si viene al caso. Un `ErrorDelSimulador` significa que el binario
31
+ // CORRIÓ y contestó —lo que falla es la ruta, o el proyecto—, así que preguntar por
32
+ // la instalación manda a mirar donde no es: una pista falsa cuesta más que ninguna.
33
+ if (!(e instanceof ErrorDelSimulador)) {
34
+ escribir(" ¿está `xone-simulator` instalado? Compruébalo con `xonecode doctor`.\n");
35
+ }
36
+ return 70; // EX_SOFTWARE: es del entorno, no del proyecto
37
+ }
38
+ const errores = informe.hallazgos.filter((h) => h.severidad === "error");
39
+ const avisos = informe.hallazgos.filter((h) => h.severidad !== "error");
40
+ for (const h of informe.hallazgos) {
41
+ escribir(` ${MARCA[h.severidad]} ${h.code}${donde(h)}\n`);
42
+ escribir(` ${h.mensaje}\n`);
43
+ }
44
+ if (informe.verde) {
45
+ escribir(`\n✓ verde: ${ruta}\n`);
46
+ return 0;
47
+ }
48
+ // Las huellas REPETIDAS son el dato que delata que reparar no avanza. Se cuenta aquí
49
+ // porque es lo que el lazo (fase 7) va a usar para bloquear antes de gastar el tope.
50
+ const repetidas = new Set();
51
+ const vistas = new Set();
52
+ for (const h of informe.hallazgos) {
53
+ const f = huella(h);
54
+ if (vistas.has(f))
55
+ repetidas.add(f);
56
+ vistas.add(f);
57
+ }
58
+ escribir(`\n✗ ${errores.length} error(es), ${avisos.length} aviso(s)`);
59
+ escribir(repetidas.size > 0 ? `, ${repetidas.size} huella(s) repetida(s)\n` : "\n");
60
+ return errores.length > 0 ? 1 : 0;
61
+ }
62
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/cli/verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * La bitácora de UN turno. Se construye al empezarlo y muere con él.
3
+ *
4
+ * El alcance es la mitad del valor: una bitácora de proceso haría que los avisos de
5
+ * honestidad («el verificador es de pega») salieran en todos los turnos posteriores al
6
+ * primero, incluido un «cuéntame un chiste» que no verificó nada. Un aviso que salta
7
+ * cuando no ha pasado nada enseña al usuario a ignorarlo, que es lo contrario de lo que
8
+ * se compra con él.
9
+ *
10
+ * Por eso `corrio()` pregunta por lo que ha pasado AHORA, y nunca se le pregunta al objeto
11
+ * de dependencias, que vive en el proceso entero.
12
+ */
13
+ export class Bitacora {
14
+ lineas = [];
15
+ /** `nodo` es la clave que luego consulta `corrio()`; `detalle` es para el humano. */
16
+ anota(nodo, detalle = "") {
17
+ this.lineas.push(detalle ? `${nodo}: ${detalle}` : `${nodo}:`);
18
+ }
19
+ /** ¿Ha corrido este nodo en ESTE turno? */
20
+ corrio(nodo) {
21
+ return this.lineas.some((l) => l.startsWith(`${nodo}:`));
22
+ }
23
+ get todo() {
24
+ return this.lineas;
25
+ }
26
+ get vacia() {
27
+ return this.lineas.length === 0;
28
+ }
29
+ }
30
+ //# sourceMappingURL=bitacora.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitacora.js","sourceRoot":"","sources":["../../src/core/bitacora.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,QAAQ;IACF,MAAM,GAAa,EAAE,CAAC;IAEvC,qFAAqF;IACrF,KAAK,CAAC,IAAY,EAAE,OAAO,GAAG,EAAE;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;IACjE,CAAC;IAED,2CAA2C;IAC3C,MAAM,CAAC,IAAY;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;IAClC,CAAC;CACF"}