@fiodos/cli 0.1.0 → 0.1.3

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.
@@ -315,7 +315,7 @@ function mapParams(targetParams, manifestParams) {
315
315
  if (mp.required && !targetNames.has(mp.name)) {
316
316
  return {
317
317
  ok: false,
318
- reason: `el parámetro requerido '${mp.name}' del manifiesto no existe en la firma de la función`,
318
+ reason: `required parameter '${mp.name}' from the manifest does not exist in the function signature`,
319
319
  };
320
320
  }
321
321
  }
@@ -329,7 +329,7 @@ function mapParams(targetParams, manifestParams) {
329
329
  }
330
330
  return {
331
331
  ok: false,
332
- reason: 'la función usa desestructuración o un parámetro rest que no se puede mapear con seguridad',
332
+ reason: 'the function uses destructuring or a rest parameter that cannot be mapped safely',
333
333
  };
334
334
  }
335
335
  if (manifestNames.has(tp.name)) {
@@ -339,7 +339,7 @@ function mapParams(targetParams, manifestParams) {
339
339
  } else {
340
340
  return {
341
341
  ok: false,
342
- reason: `el parámetro '${tp.name}' de la función no tiene un equivalente en el manifiesto`,
342
+ reason: `function parameter '${tp.name}' has no equivalent in the manifest`,
343
343
  };
344
344
  }
345
345
  }
@@ -364,16 +364,16 @@ function relImport(fromDirRel, toFileRel) {
364
364
  * and every identifier the call reads is either imported or a safe global.
365
365
  */
366
366
  function buildAiEntry({ appRoot, fyodosDirRel, base, aiW, manifestParams }) {
367
- if (!aiW || typeof aiW !== 'object') return { reason: 'la IA no propuso cableado para esta acción' };
367
+ if (!aiW || typeof aiW !== 'object') return { reason: 'the AI proposed no wiring for this action' };
368
368
  if (aiW.strategy === 'bridge') {
369
369
  return buildAiBridgeEntry({ appRoot, fyodosDirRel, base, aiW });
370
370
  }
371
371
  if (aiW.strategy && aiW.strategy !== 'module') {
372
- return { reason: `estrategia de cableado desconocida: '${aiW.strategy}'` };
372
+ return { reason: `unknown wiring strategy: '${aiW.strategy}'` };
373
373
  }
374
374
 
375
375
  const call = String(aiW.call || '').trim();
376
- if (!call) return { reason: 'la IA no aportó una expresión de llamada (call) verificable' };
376
+ if (!call) return { reason: 'the AI did not provide a verifiable call expression' };
377
377
 
378
378
  const rawImports = Array.isArray(aiW.imports) ? aiW.imports : [];
379
379
  // 1) Every required symbol must really exist where the AI says it does.
@@ -381,7 +381,7 @@ function buildAiEntry({ appRoot, fyodosDirRel, base, aiW, manifestParams }) {
381
381
  for (const r of required) {
382
382
  if (!fileHasSymbol(appRoot, r && r.file, r && r.name)) {
383
383
  return {
384
- reason: `el verificador no encontró el símbolo '${r && r.name}' en '${normRel(r && r.file)}' (la IA lo propuso pero no existe en el código)`,
384
+ reason: `the verifier did not find symbol '${r && r.name}' in '${normRel(r && r.file)}' (the AI proposed it but it does not exist in the code)`,
385
385
  };
386
386
  }
387
387
  }
@@ -393,14 +393,14 @@ function buildAiEntry({ appRoot, fyodosDirRel, base, aiW, manifestParams }) {
393
393
  const name = String((imp && imp.name) || '').trim();
394
394
  const from = normRel(imp && imp.from);
395
395
  const kind = (imp && imp.kind) || 'named';
396
- if (!name || !from) return { reason: 'un import propuesto por la IA está incompleto (falta name/from)' };
396
+ if (!name || !from) return { reason: 'an import proposed by the AI is incomplete (missing name/from)' };
397
397
  if (!fs.existsSync(path.join(appRoot, from))) {
398
- return { reason: `el import propuesto apunta a un archivo inexistente: '${from}'` };
398
+ return { reason: `the proposed import points to a non-existent file: '${from}'` };
399
399
  }
400
400
  // For named imports the symbol must be present in that file; default/namespace
401
401
  // bind the module's default/whole export so only file existence is checked.
402
402
  if (kind === 'named' && !fileHasSymbol(appRoot, from, name)) {
403
- return { reason: `el import nombrado '${name}' no existe en '${from}'` };
403
+ return { reason: `named import '${name}' does not exist in '${from}'` };
404
404
  }
405
405
  imports.push({ kind, name, importPath: relImport(fyodosDirRel, from) });
406
406
  importedNames.add(name);
@@ -411,10 +411,10 @@ function buildAiEntry({ appRoot, fyodosDirRel, base, aiW, manifestParams }) {
411
411
  if (detached) {
412
412
  return {
413
413
  reason:
414
- `la expresión instancia '${detached}' con 'new' y la usa como receptor; ` +
415
- 'si es un servicio/estado inyectado (DI) o un singleton, esa instancia nueva ' +
416
- 'queda desconectada de la que ve la UI y la acción no tendría efecto observable. ' +
417
- 'Necesita un puente al singleton real en vez de instanciarlo.',
414
+ `the expression instantiates '${detached}' with 'new' and uses it as the receiver; ` +
415
+ 'if it is an injected service/state (DI) or a singleton, that new instance ' +
416
+ 'is disconnected from the one the UI sees and the action would have no observable effect. ' +
417
+ 'It needs a bridge to the real singleton instead of instantiating it.',
418
418
  };
419
419
  }
420
420
 
@@ -424,7 +424,7 @@ function buildAiEntry({ appRoot, fyodosDirRel, base, aiW, manifestParams }) {
424
424
  for (const id of freeIdentifiers(call)) {
425
425
  if (JS_KEYWORDS.has(id) || importedNames.has(id) || SAFE_GLOBALS.has(id) || locals.has(id)) continue;
426
426
  return {
427
- reason: `la expresión de la IA referencia '${id}', que no se importa ni es un global/variable local conocido; no se cablea para no llamar a algo inexistente`,
427
+ reason: `the AI's expression references '${id}', which is neither imported nor a known global/local variable; not wired to avoid calling something that doesn't exist`,
428
428
  };
429
429
  }
430
430
 
@@ -459,21 +459,21 @@ function buildAiBridgeEntry({ appRoot, fyodosDirRel, base, aiW }) {
459
459
  const invoke = String(b.invoke || '').trim();
460
460
  const why = aiW.bridgeReason ? ` (${aiW.bridgeReason})` : '';
461
461
 
462
- if (!file) return { reason: `la IA marcó puente pero no indicó el archivo del componente${why}` };
462
+ if (!file) return { reason: `the AI marked a bridge but did not indicate the component file${why}` };
463
463
  const content = readFileSafe(appRoot, file);
464
- if (content == null) return { reason: `no se pudo leer el componente '${file}' para aplicar el puente` };
465
- if (!invoke) return { reason: `la IA no aportó la expresión 'invoke' del puente para '${file}'` };
466
- if (!anchor) return { reason: `la IA no aportó un ancla de inserción en '${file}'` };
464
+ if (content == null) return { reason: `could not read the component '${file}' to apply the bridge` };
465
+ if (!invoke) return { reason: `the AI did not provide the bridge 'invoke' expression for '${file}'` };
466
+ if (!anchor) return { reason: `the AI did not provide an insertion anchor in '${file}'` };
467
467
 
468
468
  const occ = countOccurrences(content, anchor);
469
- if (occ === 0) return { reason: `el ancla de inserción no aparece en '${file}'; no se puede ubicar el puente con seguridad` };
470
- if (occ > 1) return { reason: `el ancla de inserción aparece ${occ} veces en '${file}'; es ambigua, no se inserta a ciegas` };
469
+ if (occ === 0) return { reason: `the insertion anchor does not appear in '${file}'; cannot place the bridge safely` };
470
+ if (occ > 1) return { reason: `the insertion anchor appears ${occ} times in '${file}'; it is ambiguous, not inserted blindly` };
471
471
 
472
472
  // requiredSymbols must really exist.
473
473
  const required = Array.isArray(aiW.requiredSymbols) ? aiW.requiredSymbols : [];
474
474
  for (const r of required) {
475
475
  if (!fileHasSymbol(appRoot, r && r.file, r && r.name)) {
476
- return { reason: `el verificador no encontró el símbolo '${r && r.name}' en '${normRel(r && r.file)}' (puente no verificable)` };
476
+ return { reason: `the verifier did not find symbol '${r && r.name}' in '${normRel(r && r.file)}' (bridge not verifiable)` };
477
477
  }
478
478
  }
479
479
 
@@ -482,8 +482,8 @@ function buildAiBridgeEntry({ appRoot, fyodosDirRel, base, aiW }) {
482
482
  if (detached) {
483
483
  return {
484
484
  reason:
485
- `el puente instancia '${detached}' con 'new' y la usa como receptor; eso crea una ` +
486
- 'instancia desconectada de la que ve la UI. Debe usar la instancia real (p. ej. this.<servicio>), no instanciarla.',
485
+ `the bridge instantiates '${detached}' with 'new' and uses it as the receiver; that creates an ` +
486
+ 'instance disconnected from the one the UI sees. It must use the real instance (e.g. this.<service>), not instantiate it.',
487
487
  };
488
488
  }
489
489
 
@@ -494,9 +494,9 @@ function buildAiBridgeEntry({ appRoot, fyodosDirRel, base, aiW }) {
494
494
  const name = String((imp && imp.name) || '').trim();
495
495
  const from = normRel(imp && imp.from);
496
496
  const kind = (imp && imp.kind) || 'named';
497
- if (!name || !from) return { reason: 'un import del puente está incompleto (falta name/from)' };
498
- if (!fs.existsSync(path.join(appRoot, from))) return { reason: `el import del puente apunta a un archivo inexistente: '${from}'` };
499
- if (kind === 'named' && !fileHasSymbol(appRoot, from, name)) return { reason: `el import nombrado '${name}' no existe en '${from}'` };
497
+ if (!name || !from) return { reason: 'a bridge import is incomplete (missing name/from)' };
498
+ if (!fs.existsSync(path.join(appRoot, from))) return { reason: `the bridge import points to a non-existent file: '${from}'` };
499
+ if (kind === 'named' && !fileHasSymbol(appRoot, from, name)) return { reason: `named import '${name}' does not exist in '${from}'` };
500
500
  imports.push({ kind, name, importPath: relImport(path.dirname(file), from) });
501
501
  importedNames.add(name);
502
502
  }
@@ -509,7 +509,7 @@ function buildAiBridgeEntry({ appRoot, fyodosDirRel, base, aiW }) {
509
509
  if (id === 'args' || JS_KEYWORDS.has(id) || SAFE_GLOBALS.has(id) || locals.has(id) || importedNames.has(id)) continue;
510
510
  if (fileHasSymbol(appRoot, file, id)) continue;
511
511
  return {
512
- reason: `el puente referencia '${id}', que no está en el ámbito del componente '${file}' ni se importa; no se cablea para no romper el código`,
512
+ reason: `the bridge references '${id}', which is not in the component '${file}' scope and is not imported; not wired to avoid breaking the code`,
513
513
  };
514
514
  }
515
515
 
@@ -541,7 +541,7 @@ function buildAiBridgeEntry({ appRoot, fyodosDirRel, base, aiW }) {
541
541
  */
542
542
  function buildMechanicalEntry({ appRoot, fyodosDirRel, base, manifestParams }) {
543
543
  const { file: evFile, handler } = base;
544
- if (!evFile) return { reason: 'el análisis no aportó un archivo de evidencia para esta acción' };
544
+ if (!evFile) return { reason: 'the analysis did not provide an evidence file for this action' };
545
545
  const absFile = path.join(appRoot, evFile);
546
546
  let content = null;
547
547
  if (fs.existsSync(absFile)) {
@@ -551,21 +551,21 @@ function buildMechanicalEntry({ appRoot, fyodosDirRel, base, manifestParams }) {
551
551
  content = null;
552
552
  }
553
553
  }
554
- if (!content) return { reason: `no se pudo leer el archivo de evidencia '${evFile}'` };
554
+ if (!content) return { reason: `could not read the evidence file '${evFile}'` };
555
555
 
556
556
  const exported = findExportedFunction(content, handler);
557
557
  const store = exported ? null : findStoreMethod(content, handler);
558
558
  if (!exported && !store) {
559
559
  return {
560
560
  reason:
561
- `no se encontró '${handler}' como función exportada ni como método de store en '${evFile}'. ` +
562
- 'Puede vivir en un componente, una clase o un hook/contexto. No se cablea a ciegas.',
561
+ `'${handler}' was not found as an exported function or a store method in '${evFile}'. ` +
562
+ 'It may live in a component, a class or a hook/context. Not wired blindly.',
563
563
  };
564
564
  }
565
565
 
566
566
  const targetParams = (exported || store).params;
567
567
  const mapped = mapParams(targetParams, manifestParams);
568
- if (!mapped.ok) return { reason: `firma no mapeable con seguridad: ${mapped.reason}` };
568
+ if (!mapped.ok) return { reason: `signature not safely mappable: ${mapped.reason}` };
569
569
 
570
570
  const importPath = relImport(fyodosDirRel, evFile);
571
571
  if (exported) {
@@ -724,7 +724,7 @@ function buildRegistryModule(plan, opts = {}) {
724
724
  const takesParams = e.usesParams != null ? e.usesParams : /\bparams\b/.test(e.callExpr);
725
725
  const arg = takesParams ? `params${paramsType}` : `_params${paramsType}`;
726
726
  const confNote = e.requireConfirmation
727
- ? '\n // Acción con confirmación: el motor de Fiodos ya pidió confirmación ANTES de llamar aquí.'
727
+ ? '\n // Confirmation action: the Fiodos engine already asked for confirmation BEFORE calling here.'
728
728
  : '';
729
729
  // `: unknown` lets the truthiness test below be legal even when the wired
730
730
  // call returns void (e.g. db.delete(...)) — otherwise TS errors on `result &&`.
@@ -751,13 +751,12 @@ function buildRegistryModule(plan, opts = {}) {
751
751
  const header =
752
752
  `/**\n` +
753
753
  ` * ${GENERATED_MARKER}.\n` +
754
- ` * No edites a mano: re-ejecuta el instalador de Fiodos para regenerarlo.\n` +
755
- ` * Lee ${DOC_BASENAME} (misma carpeta) para saber qué cablea y por qué.\n` +
754
+ ` * Do not edit by hand: re-run the Fiodos installer to regenerate it.\n` +
755
+ ` * Read ${DOC_BASENAME} (same folder) to see what it wires and why.\n` +
756
756
  ` *\n` +
757
- ` * SEGURIDAD: este archivo solo LLAMA a tus funciones. Nunca decide si una\n` +
758
- ` * acción sensible se ejecuta el motor de Fiodos aplica las confirmaciones\n` +
759
- ` * del manifiesto (requireConfirmation / voiceConfirmation) ANTES de invocar\n` +
760
- ` * cualquier handler de aquí.\n` +
757
+ ` * SECURITY: this file only CALLS your functions. It never decides whether a\n` +
758
+ ` * sensitive action runsthe Fiodos engine applies the manifest confirmations\n` +
759
+ ` * (requireConfirmation / voiceConfirmation) BEFORE invoking any handler here.\n` +
761
760
  ` */\n`;
762
761
 
763
762
  const decl = esm
@@ -773,8 +772,8 @@ function buildRegistryModule(plan, opts = {}) {
773
772
  `${handlerText}\n` +
774
773
  (manualNotes.length ? `${manualNotes.join('\n')}\n` : '') +
775
774
  ` },\n` +
776
- ` // Los idempotency checkers leen el estado de tu app; impleméntalos a mano si\n` +
777
- ` // alguna acción declara idempotencyCheck (ver ${DOC_BASENAME}).\n` +
775
+ ` // Idempotency checkers read your app state; implement them by hand if\n` +
776
+ ` // an action declares idempotencyCheck (see ${DOC_BASENAME}).\n` +
778
777
  ` idempotencyCheckers: {},\n` +
779
778
  `};\n` +
780
779
  (esm ? '' : `\nmodule.exports = { ${REGISTRY_EXPORT} };\n`);
@@ -794,9 +793,9 @@ function buildBridgeModule(opts = {}) {
794
793
  const header =
795
794
  `/**\n` +
796
795
  ` * ${GENERATED_MARKER} (bridge holder).\n` +
797
- ` * No edites a mano. Tu componente registra aquí sus funciones reales y los\n` +
798
- ` * handlers generados las invocan. Es lo que conecta el agente con el estado\n` +
799
- ` * que vive dentro de tus componentes, sin crear instancias desconectadas.\n` +
796
+ ` * Do not edit by hand. Your component registers its real functions here and the\n` +
797
+ ` * generated handlers invoke them. This is what connects the agent to the state\n` +
798
+ ` * living inside your components, without creating disconnected instances.\n` +
800
799
  ` */\n`;
801
800
  if (ts) {
802
801
  return (
@@ -916,130 +915,130 @@ function planComponentEdits(appRoot, plan, opts = {}) {
916
915
  // ── Document codegen ───────────────────────────────────────────────────────────
917
916
 
918
917
  function confLabel(e) {
919
- if (!e.requireConfirmation) return 'no requiere confirmación';
920
- const mode = e.voiceConfirmation ? ` (voz: ${e.voiceConfirmation})` : '';
921
- return `requiere confirmación${mode}`;
918
+ if (!e.requireConfirmation) return 'no confirmation required';
919
+ const mode = e.voiceConfirmation ? ` (voice: ${e.voiceConfirmation})` : '';
920
+ return `requires confirmation${mode}`;
922
921
  }
923
922
 
924
923
  function buildHandlerDoc(plan, ctx = {}) {
925
924
  const {
926
- appName = 'tu app', framework = 'web', registryRel = '', mountSnippet = '',
925
+ appName = 'your app', framework = 'web', registryRel = '', mountSnippet = '',
927
926
  bridgeRel = '', edits = [], verification = null,
928
927
  } = ctx;
929
928
  const L = [];
930
- L.push('# Cableado de acciones de Fiodos (handler wiring)');
929
+ L.push('# Fiodos action wiring (handler wiring)');
931
930
  L.push('');
932
931
  L.push(
933
- 'Este documento describe **exactamente** qué hará Fiodos para conectar las ' +
934
- 'acciones detectadas en tu app con las funciones reales que las ejecutan. ' +
935
- '**Nada se aplica hasta que aceptes en el terminal.**',
932
+ 'This document describes **exactly** what Fiodos will do to connect the ' +
933
+ 'actions detected in your app with the real functions that run them. ' +
934
+ '**Nothing is applied until you accept in the terminal.**',
936
935
  );
937
936
  L.push('');
938
937
  const review = plan.review || plan.manual || [];
939
938
  const aiCount = plan.auto.filter((e) => e.source === 'ai').length;
940
939
  L.push(`- App: **${appName}**`);
941
- L.push(`- Framework detectado: **${framework}**`);
942
- L.push(`- Acciones que se cablearán automáticamente: **${plan.auto.length}** ` +
943
- `(${aiCount} cableadas por la IA, ${plan.auto.length - aiCount} por el detector mecánico de respaldo)`);
944
- L.push(`- Acciones que necesitan revisión: **${review.length}**`);
940
+ L.push(`- Detected framework: **${framework}**`);
941
+ L.push(`- Actions that will be wired automatically: **${plan.auto.length}** ` +
942
+ `(${aiCount} wired by the AI, ${plan.auto.length - aiCount} by the mechanical fallback detector)`);
943
+ L.push(`- Actions that need review: **${review.length}**`);
945
944
  L.push('');
946
- L.push('## ¿Cómo se genera este cableado?');
945
+ L.push('## How is this wiring generated?');
947
946
  L.push('');
948
947
  L.push(
949
- 'La **misma IA** que analizó tu código para detectar las acciones también propuso ' +
950
- 'cómo ejecutarlas: qué función/módulo real llamar y cómo mapear los parámetros. ' +
951
- 'Después, un **verificador mecánico** comprueba que cada símbolo referenciado ' +
952
- 'existe de verdad en tu código antes de escribir nada. Si la IA propone algo que ' +
953
- 'el verificador no encuentra, esa acción queda **para revisión**, nunca se cablea a ciegas.',
948
+ 'The **same AI** that analyzed your code to detect the actions also proposed ' +
949
+ 'how to run them: which real function/module to call and how to map the parameters. ' +
950
+ 'Then a **mechanical verifier** checks that every referenced symbol ' +
951
+ 'really exists in your code before writing anything. If the AI proposes something ' +
952
+ 'the verifier cannot find, that action is left **for review**, never wired blindly.',
954
953
  );
955
954
  L.push('');
956
955
  if (Array.isArray(verification) && verification.length) {
957
- L.push('## Verificación automática (efecto real + reintentos)');
956
+ L.push('## Automatic verification (real effect + retries)');
958
957
  L.push('');
959
958
  L.push(
960
- 'Tras tu "yes", la instalación **no se fía** de que el cableado compile: por cada ' +
961
- 'acción ejecuta un bucle **cablearverificardiagnosticarcorregir** sin ' +
962
- 'intervención humana. La verificación comprueba (1) que el proyecto **sigue ' +
963
- 'compilando** con esa acción cableada y, cuando es seguro simularlo, (2) que al ' +
964
- 'invocar el handler **el estado real cambia** (la tarea se añade, etc.). Si una ' +
965
- 'acción falla, la IA recibe el **error concreto** y reintenta con un cableado ' +
966
- 'corregido (hasta varios intentos). Una acción solo se marca **lista** cuando pasa ' +
967
- 'la verificación; si tras los reintentos no se logra, se **revierte solo esa ' +
968
- 'acción** y se marca abajo como "no se pudo cablear" — nunca se deja fingiendo.',
959
+ 'After your "yes", the install **does not trust** that the wiring compiles: for each ' +
960
+ 'action it runs a **wireverifydiagnosecorrect** loop with no ' +
961
+ 'human intervention. Verification checks (1) that the project **still ' +
962
+ 'builds** with that action wired and, when it is safe to simulate, (2) that ' +
963
+ 'invoking the handler **changes the real state** (the task is added, etc.). If an ' +
964
+ 'action fails, the AI receives the **concrete error** and retries with corrected ' +
965
+ 'wiring (up to several attempts). An action is only marked **ready** when it passes ' +
966
+ 'verification; if it is not achieved after the retries, **only that action is ' +
967
+ 'reverted** and marked below as "could not be wired" — never left faking it.',
969
968
  );
970
969
  L.push('');
971
- L.push('| Acción (intent) | Resultado | Intentos | Verificación | Detalle |');
970
+ L.push('| Action (intent) | Result | Attempts | Verification | Detail |');
972
971
  L.push('| --- | --- | --- | --- | --- |');
973
972
  for (const v of verification) {
974
- const res = v.status === 'ready' ? '✅ lista' : '❌ no se pudo';
973
+ const res = v.status === 'ready' ? '✅ ready' : '❌ could not';
975
974
  let lvl = '—';
976
975
  if (v.status === 'ready') {
977
- lvl = v.level === 'effect' ? 'efecto real (UI)'
978
- : v.level === 'unverifiable' ? '⚠️ efecto NO verificable (revisar a mano)'
979
- : 'compilación';
976
+ lvl = v.level === 'effect' ? 'real effect (UI)'
977
+ : v.level === 'unverifiable' ? '⚠️ effect NOT verifiable (review by hand)'
978
+ : 'compilation';
980
979
  }
981
980
  const detail = String(v.detail || v.error || '').replace(/\|/g, '\\|').slice(0, 160);
982
981
  L.push(`| \`${v.intent}\` | ${res} | ${v.attempts} | ${lvl} | ${detail} |`);
983
982
  }
984
983
  L.push('');
985
- L.push('_"efecto real (UI)" = se montó el componente real en un DOM de prueba (jsdom) con ' +
986
- 'la herramienta estándar del framework (React: react-dom; Vue: vue + @vue/compiler-sfc), ' +
987
- 'se invocó el handler cableado y se **observó el cambio en la UI/estado** (la tarea aparece, ' +
988
- 'el item se alterna, etc.). Para la capa de datos (Dexie/store) el efecto se observa ejecutando ' +
989
- 'la acción contra una instancia real. "compilación" = compila y el símbolo real existe y se ' +
990
- 'invoca, pero el efecto no pudo simularse de forma segura aquí. "⚠️ efecto NO verificable" = ' +
991
- 'el cableado se aplicó pero el efecto real no se pudo confirmar automáticamente (componente no ' +
992
- 'montable en aislamiento, o acción sensible que no se dispara) — **revisar/probar a mano**. Las ' +
993
- 'acciones sensibles (con confirmación) **nunca** se disparan en la prueba._');
984
+ L.push('_"real effect (UI)" = the real component was mounted in a test DOM (jsdom) with ' +
985
+ 'the framework\'s standard tooling (React: react-dom; Vue: vue + @vue/compiler-sfc), ' +
986
+ 'the wired handler was invoked and the **change in the UI/state was observed** (the task appears, ' +
987
+ 'the item toggles, etc.). For the data layer (Dexie/store) the effect is observed by running ' +
988
+ 'the action against a real instance. "compilation" = it compiles and the real symbol exists and is ' +
989
+ 'invoked, but the effect could not be safely simulated here. "⚠️ effect NOT verifiable" = ' +
990
+ 'the wiring was applied but the real effect could not be confirmed automatically (component not ' +
991
+ 'mountable in isolation, or a sensitive action that is not fired) — **review/test by hand**. ' +
992
+ 'Sensitive actions (with confirmation) are **never** fired in the test._');
994
993
  L.push('');
995
994
  }
996
995
 
997
- L.push('## ¿Por qué es necesario?');
996
+ L.push('## Why is it needed?');
998
997
  L.push('');
999
998
  L.push(
1000
- 'El orbe ya está montado, pero el agente no puede **ejecutar** acciones hasta ' +
1001
- 'que cada acción del manifiesto esté conectada a la función real de tu app. ' +
1002
- 'Este cableado es ese puente: traduce cada acción (p. ej. `addToCart`) en una ' +
1003
- 'llamada a tu función (p. ej. `useShop.getState().addToCart(...)`).',
999
+ 'The orb is already mounted, but the agent cannot **run** actions until ' +
1000
+ 'each manifest action is connected to your app\'s real function. ' +
1001
+ 'This wiring is that bridge: it translates each action (e.g. `addToCart`) into a ' +
1002
+ 'call to your function (e.g. `useShop.getState().addToCart(...)`).',
1004
1003
  );
1005
1004
  L.push('');
1006
1005
  const bridgeEntries = plan.auto.filter((e) => e.kind === 'bridge');
1007
- L.push('## Qué archivos se crean / editan');
1006
+ L.push('## Which files are created / edited');
1008
1007
  L.push('');
1009
- L.push(`- **CREA** \`${registryRel}\` — el registro de handlers generado.`);
1008
+ L.push(`- **CREATES** \`${registryRel}\` — the generated handler registry.`);
1010
1009
  if (bridgeEntries.length && bridgeRel) {
1011
- L.push(`- **CREA** \`${bridgeRel}\` — el "puente" donde tu componente registra sus funciones reales.`);
1010
+ L.push(`- **CREATES** \`${bridgeRel}\` — the "bridge" where your component registers its real functions.`);
1012
1011
  }
1013
1012
  if (edits.length) {
1014
- L.push('- **EDITA los siguientes archivos TUYOS** (solo añade líneas marcadas con ' +
1015
- '`FYODOS:BRIDGE`, reversibles):');
1016
- for (const ed of edits) L.push(` - \`${ed.file}\` — registra: ${ed.intents.map((i) => `\`${i}\``).join(', ')}`);
1013
+ L.push('- **EDITS the following files OF YOURS** (only adds lines marked with ' +
1014
+ '`FYODOS:BRIDGE`, reversible):');
1015
+ for (const ed of edits) L.push(` - \`${ed.file}\` — registers: ${ed.intents.map((i) => `\`${i}\``).join(', ')}`);
1017
1016
  } else {
1018
- L.push('- **No se edita ningún archivo existente tuyo** (todas las acciones eran ' +
1019
- 'alcanzables desde un módulo).');
1017
+ L.push('- **No existing file of yours is edited** (all actions were ' +
1018
+ 'reachable from a module).');
1020
1019
  }
1021
- L.push('- Para activarlo, pasa el registro generado a `<FiodosAgent/>` (ver "Cómo activarlo").');
1020
+ L.push('- To enable it, pass the generated registry to `<FiodosAgent/>` (see "How to enable it").');
1022
1021
  L.push('');
1023
1022
 
1024
1023
  if (edits.length) {
1025
- L.push('## Cambios EXACTOS dentro de tus componentes');
1024
+ L.push('## EXACT changes inside your components');
1026
1025
  L.push('');
1027
- L.push('Estas son las líneas que Fiodos añadirá a tus archivos (cada bloque va entre ' +
1028
- 'marcas `FYODOS:BRIDGE:START` / `END`, así puedes quitarlas o revertirlas sin tocar ' +
1029
- 'tu propio código):');
1026
+ L.push('These are the lines Fiodos will add to your files (each block sits between ' +
1027
+ '`FYODOS:BRIDGE:START` / `END` markers, so you can remove or revert them without touching ' +
1028
+ 'your own code):');
1030
1029
  L.push('');
1031
1030
  for (const ed of edits) {
1032
1031
  L.push(`### \`${ed.file}\``);
1033
1032
  L.push('');
1034
- L.push('Tras la última línea `import`, se añade:');
1033
+ L.push('After the last `import` line, this is added:');
1035
1034
  L.push('');
1036
1035
  L.push('```ts');
1037
1036
  L.push(ed.importBlock);
1038
1037
  L.push('```');
1039
1038
  L.push('');
1040
1039
  L.push(ed.file.endsWith('.vue')
1041
- ? 'Justo antes de `</script>`, se añade:'
1042
- : `Junto al ancla \`${(ed.anchor || '').trim().slice(0, 80)}\`, se añade:`);
1040
+ ? 'Right before `</script>`, this is added:'
1041
+ : `Next to the anchor \`${(ed.anchor || '').trim().slice(0, 80)}\`, this is added:`);
1043
1042
  L.push('');
1044
1043
  L.push('```ts');
1045
1044
  L.push(ed.registerBlock.replace(/^\s+/gm, (s) => s)); // keep as-is
@@ -1049,9 +1048,9 @@ function buildHandlerDoc(plan, ctx = {}) {
1049
1048
  }
1050
1049
 
1051
1050
  if (plan.auto.length) {
1052
- L.push(`## Acciones que se cablearán automáticamente (${plan.auto.length})`);
1051
+ L.push(`## Actions that will be wired automatically (${plan.auto.length})`);
1053
1052
  L.push('');
1054
- L.push('| Acción (intent) | Cómo se ejecuta | Dónde | Parámetros | Tipo | Seguridad |');
1053
+ L.push('| Action (intent) | How it runs | Where | Parameters | Type | Security |');
1055
1054
  L.push('| --- | --- | --- | --- | --- | --- |');
1056
1055
  for (const e of plan.auto) {
1057
1056
  const expr = e.kind === 'bridge' ? e.bridge.invoke : e.callExpr;
@@ -1061,81 +1060,81 @@ function buildHandlerDoc(plan, ctx = {}) {
1061
1060
  ? e.manifestParams.map((p) => `${p.name}${p.required ? '*' : ''}`).join(', ')
1062
1061
  : '—';
1063
1062
  const kind = e.kind === 'bridge'
1064
- ? 'puente (edita tu componente)'
1065
- : (e.source === 'ai' ? 'módulo (IA)' : 'módulo (mecánico)');
1063
+ ? 'bridge (edits your component)'
1064
+ : (e.source === 'ai' ? 'module (AI)' : 'module (mechanical)');
1066
1065
  L.push(`| \`${e.intent}\` | ${call} | ${where} | ${params} | ${kind} | ${confLabel(e)} |`);
1067
1066
  }
1068
1067
  L.push('');
1069
- L.push('_`*` = parámetro requerido. Cada llamada fue propuesta por la IA leyendo tu ' +
1070
- 'código y **confirmada por el verificador** (los símbolos existen). Los parámetros ' +
1071
- 'se mapean **por nombre**, nunca por posición._');
1068
+ L.push('_`*` = required parameter. Each call was proposed by the AI reading your ' +
1069
+ 'code and **confirmed by the verifier** (the symbols exist). Parameters ' +
1070
+ 'are mapped **by name**, never by position._');
1072
1071
  L.push('');
1073
1072
  }
1074
1073
 
1075
1074
  if (review.length) {
1076
- L.push(`## Acciones que necesitan REVISIÓN (${review.length})`);
1075
+ L.push(`## Actions that need REVIEW (${review.length})`);
1077
1076
  L.push('');
1078
1077
  L.push(
1079
- 'Para estas acciones, ni la IA propuso un cableado **verificable** ni el detector ' +
1080
- 'mecánico de respaldo encontró la función con seguridad. Cablearlas mal sería ' +
1081
- 'peor que dejarlas sin cablear, así que se marcan para revisión y te explicamos por qué:',
1078
+ 'For these actions, neither did the AI propose **verifiable** wiring nor did the ' +
1079
+ 'mechanical fallback detector find the function safely. Wiring them wrong would be ' +
1080
+ 'worse than leaving them unwired, so they are marked for review and we explain why:',
1082
1081
  );
1083
1082
  L.push('');
1084
1083
  for (const e of review) {
1085
1084
  L.push(`### \`${e.intent}\` — ${e.label}`);
1086
1085
  L.push('');
1087
- L.push(`- Handler esperado: \`${e.handler}\``);
1088
- if (e.file) L.push(`- Archivo de evidencia: \`${e.file}\``);
1089
- L.push(`- Motivo: ${e.reason}`);
1086
+ L.push(`- Expected handler: \`${e.handler}\``);
1087
+ if (e.file) L.push(`- Evidence file: \`${e.file}\``);
1088
+ L.push(`- Reason: ${e.reason}`);
1090
1089
  L.push(
1091
- `- Qué hacer: añade un handler \`${e.handler}\` en \`${registryRel}\` (o en tu ` +
1092
- 'propio registro) que llame a la función real y devuelva ' +
1093
- '`{ success: true }` (o `{ success: false, message }`).',
1090
+ `- What to do: add a handler \`${e.handler}\` in \`${registryRel}\` (or in your ` +
1091
+ 'own registry) that calls the real function and returns ' +
1092
+ '`{ success: true }` (or `{ success: false, message }`).',
1094
1093
  );
1095
1094
  L.push('');
1096
1095
  }
1097
1096
  }
1098
1097
 
1099
1098
  const confirmed = plan.entries.filter((e) => e.requireConfirmation);
1100
- L.push('## Seguridad');
1099
+ L.push('## Security');
1101
1100
  L.push('');
1102
1101
  L.push(
1103
- '- El cableado generado **solo llama** a tus funciones. **Nunca** decide si una ' +
1104
- 'acción sensible se ejecuta: el motor de Fiodos aplica las confirmaciones del ' +
1105
- 'manifiesto (`requireConfirmation` / `voiceConfirmation`) **antes** de invocar ' +
1106
- 'el handler. Es imposible que el auto-cableado salte una confirmación.',
1102
+ '- The generated wiring **only calls** your functions. It **never** decides whether a ' +
1103
+ 'sensitive action runs: the Fiodos engine applies the manifest confirmations ' +
1104
+ '(`requireConfirmation` / `voiceConfirmation`) **before** invoking ' +
1105
+ 'the handler. It is impossible for the auto-wiring to skip a confirmation.',
1107
1106
  );
1108
1107
  if (confirmed.length) {
1109
- L.push('- Acciones protegidas por confirmación (el agente pedirá confirmar antes de ejecutarlas):');
1108
+ L.push('- Actions protected by confirmation (the agent will ask to confirm before running them):');
1110
1109
  for (const e of confirmed) {
1111
1110
  L.push(` - \`${e.intent}\` — ${confLabel(e)}.`);
1112
1111
  }
1113
1112
  } else {
1114
- L.push('- Ninguna de las acciones detectadas requiere confirmación en el manifiesto.');
1113
+ L.push('- None of the detected actions require confirmation in the manifest.');
1115
1114
  }
1116
- L.push('- El cableado no lee, incrusta ni expone secretos; solo referencia nombres de');
1117
- L.push(' función y los parámetros que el manifiesto ya declara.');
1115
+ L.push('- The wiring does not read, embed or expose secrets; it only references function');
1116
+ L.push(' names and the parameters the manifest already declares.');
1118
1117
  L.push('');
1119
1118
 
1120
- L.push('## Cómo activarlo');
1119
+ L.push('## How to enable it');
1121
1120
  L.push('');
1122
1121
  if (mountSnippet) {
1123
- L.push('Pasa el registro generado a tu `<FiodosAgent/>`:');
1122
+ L.push('Pass the generated registry to your `<FiodosAgent/>`:');
1124
1123
  L.push('');
1125
1124
  L.push('```tsx');
1126
1125
  L.push(mountSnippet);
1127
1126
  L.push('```');
1128
1127
  } else {
1129
- L.push('Importa `' + REGISTRY_EXPORT + '` desde el archivo generado y pásalo como ' +
1130
- '`registries` a tu `<FiodosAgent/>`.');
1128
+ L.push('Import `' + REGISTRY_EXPORT + '` from the generated file and pass it as ' +
1129
+ '`registries` to your `<FiodosAgent/>`.');
1131
1130
  }
1132
1131
  L.push('');
1133
- L.push('## Si dijiste "no"');
1132
+ L.push('## If you said "no"');
1134
1133
  L.push('');
1135
1134
  L.push(
1136
- 'No se ha tocado tu código. Puedes implementar el cableado mismo siguiendo ' +
1137
- 'este documento, o volver a ejecutar el instalador para aceptar el auto-cableado ' +
1138
- '(`--wire-yes` para aceptarlo sin preguntar).',
1135
+ 'Your code was not touched. You can implement the wiring yourself following ' +
1136
+ 'this document, or re-run the installer to accept the auto-wiring ' +
1137
+ '(`--wire-yes` to accept without prompting).',
1139
1138
  );
1140
1139
  L.push('');
1141
1140
  return L.join('\n');
@@ -1365,7 +1364,7 @@ async function runAutoCorrectionLoop(ctx) {
1365
1364
  } = ctx;
1366
1365
 
1367
1366
  const log = (m) => console.error(`${tag} · ${dim || ''}${m}${reset || ''}`);
1368
- log('verificando y auto-corrigiendo cada acción (puede tardar)…');
1367
+ log('Verifying and auto-correcting each action (may take a while)…');
1369
1368
  const baseline = await testRunner(appRoot, { framework });
1370
1369
 
1371
1370
  const ready = [];
@@ -1384,7 +1383,7 @@ async function runAutoCorrectionLoop(ctx) {
1384
1383
  const { entry, review } = planOneAction({ appRoot, manifest, evidence, candidate: wiringMap[intent], intent, fyodosDirRel });
1385
1384
 
1386
1385
  if (!entry) {
1387
- lastError = (review && review.reason) || 'no verificable mecánicamente';
1386
+ lastError = (review && review.reason) || 'not mechanically verifiable';
1388
1387
  history.push({ attempt, stage: 'mechanical', ok: false, error: lastError });
1389
1388
  } else {
1390
1389
  const iso = writeIsolated({ appRoot, fyodosDirAbs, entry, ts, esm, ext });
@@ -1393,10 +1392,10 @@ async function runAutoCorrectionLoop(ctx) {
1393
1392
  try {
1394
1393
  const after = await testRunner(appRoot, { framework });
1395
1394
  const reg = compileRegressed(baseline.output, after.output, iso.touched);
1396
- if (!reg.ok) { ok = false; lastError = `compilación: ${reg.newErrors.join(' | ').slice(0, 600)}`; }
1395
+ if (!reg.ok) { ok = false; lastError = `compilation: ${reg.newErrors.join(' | ').slice(0, 600)}`; }
1397
1396
  if (ok) {
1398
1397
  const eff = await probeEffect(appRoot, entry, { fyodosDirRel, framework, sources: files, sensitive: entry.requireConfirmation });
1399
- if (eff.status === 'fail') { ok = false; stage = 'effect'; lastError = `efecto: ${eff.detail}`; }
1398
+ if (eff.status === 'fail') { ok = false; stage = 'effect'; lastError = `effect: ${eff.detail}`; }
1400
1399
  else {
1401
1400
  const level = eff.status === 'effect-pass' ? 'effect'
1402
1401
  : eff.status === 'unverifiable' ? 'unverifiable'
@@ -1405,7 +1404,7 @@ async function runAutoCorrectionLoop(ctx) {
1405
1404
  }
1406
1405
  }
1407
1406
  } catch (err) {
1408
- ok = false; lastError = `verificación lanzó: ${err && err.message}`;
1407
+ ok = false; lastError = `verification threw: ${err && err.message}`;
1409
1408
  } finally {
1410
1409
  revertAll(iso.backups, iso.generated);
1411
1410
  }
@@ -1414,14 +1413,14 @@ async function runAutoCorrectionLoop(ctx) {
1414
1413
  }
1415
1414
 
1416
1415
  if (attempt >= maxAttempts || typeof corrector !== 'function') break;
1417
- log(`acción '${intent}' falló (intento ${attempt}): ${lastError.slice(0, 120)} → pidiendo corrección a la IA…`);
1416
+ log(`Action '${intent}' failed (attempt ${attempt}): ${lastError.slice(0, 120)} → asking the AI to correct it…`);
1418
1417
  try {
1419
1418
  const relevantFiles = pickRelevantFiles(files, evidence, wiringMap[intent], intent);
1420
1419
  const corrected = await corrector({ action, intent, evidence: (evidence && evidence.actions) || {}, relevantFiles, prevWiring: wiringMap[intent], errorText: lastError, attempt });
1421
1420
  if (corrected && corrected[intent]) wiringMap[intent] = corrected[intent];
1422
- else { history.push({ attempt, stage: 'correct', ok: false, error: 'la IA no devolvió cableado corregido' }); break; }
1421
+ else { history.push({ attempt, stage: 'correct', ok: false, error: 'the AI did not return corrected wiring' }); break; }
1423
1422
  } catch (err) {
1424
- history.push({ attempt, stage: 'correct', ok: false, error: `corrector falló: ${err && err.message}` });
1423
+ history.push({ attempt, stage: 'correct', ok: false, error: `corrector failed: ${err && err.message}` });
1425
1424
  break;
1426
1425
  }
1427
1426
  }
@@ -1429,11 +1428,11 @@ async function runAutoCorrectionLoop(ctx) {
1429
1428
  if (resolved) {
1430
1429
  ready.push(resolved.entry);
1431
1430
  attemptsLog.push({ intent, attempts: attempt, status: 'ready', level: resolved.level, detail: resolved.detail, history });
1432
- log(`✓ '${intent}' verificada en ${attempt} intento(s) [${resolved.level === 'effect' ? 'efecto real' : 'compilación'}].`);
1431
+ log(`✓ '${intent}' verified in ${attempt} attempt(s) [${resolved.level === 'effect' ? 'real effect' : 'compilation'}].`);
1433
1432
  } else {
1434
1433
  dropped.push({ ...action, intent, reason: lastError, attempts: attempt, handler: action.handler || intent, label: action.label || intent, manifestParams: Object.entries(action.parameters || {}).map(([name, spec]) => ({ name, required: spec && spec.required === true })) });
1435
1434
  attemptsLog.push({ intent, attempts: attempt, status: 'dropped', error: lastError, history });
1436
- log(`✗ '${intent}' no se pudo cablear tras ${attempt} intento(s): ${lastError.slice(0, 140)}`);
1435
+ log(`✗ '${intent}' could not be wired after ${attempt} attempt(s): ${lastError.slice(0, 140)}`);
1437
1436
  }
1438
1437
  }
1439
1438
 
@@ -1518,31 +1517,31 @@ async function wireHandlers(appRoot, opts = {}) {
1518
1517
  const loopEnabled = typeof corrector === 'function' && runTest && testRunner;
1519
1518
  if (!plan.auto.length && !loopEnabled) {
1520
1519
  console.error(
1521
- `\n${tag} · ${dim || ''}preparé el cableado de acciones, pero ninguna de las ` +
1522
- `${plan.manual.length} acción(es) se puede conectar con confianza.${reset || ''}`,
1520
+ `\n${tag} · ${dim || ''}Prepared the action wiring, but none of the ` +
1521
+ `${plan.manual.length} action(s) can be connected with confidence.${reset || ''}`,
1523
1522
  );
1524
1523
  console.error(
1525
- `${tag} · revisa qué hace falta y cómo hacerlo a mano en: ${docRel}`,
1524
+ `${tag} · See what is needed and how to do it by hand in: ${docRel}`,
1526
1525
  );
1527
1526
  return { status: 'manual-only', docPath: docAbs, autoCount: 0, manualCount: plan.manual.length, plan };
1528
1527
  }
1529
1528
 
1530
1529
  // 3) Second, separate confirmation (after the orb's), referencing the doc.
1531
1530
  const touchNote = componentEdits.length
1532
- ? ` Se editarán ${componentEdits.length} archivo(s) TUYO(s) (con marcas reversibles).`
1531
+ ? ` ${componentEdits.length} file(s) of YOURS will be edited (with reversible markers).`
1533
1532
  : '';
1534
1533
  const prompt =
1535
- `\n${tag} · El cableado de acciones necesario para que el agente ejecute funciones ` +
1536
- `está listo (${plan.auto.length} automática(s), ${plan.manual.length} en revisión).${touchNote}\n` +
1537
- `${tag} · Puedes revisar exactamente qué cambios se harán en tu código en este archivo:\n` +
1534
+ `\n${tag} · The action wiring that lets the agent run your functions is ` +
1535
+ `ready (${plan.auto.length} automatic, ${plan.manual.length} for review).${touchNote}\n` +
1536
+ `${tag} · Review the exact changes that will be made to your code in:\n` +
1538
1537
  ` ${docRel}\n` +
1539
- `${tag} · ¿Aceptas aplicar estos cambios después de revisarlo? [yes/no] `;
1538
+ `${tag} · Apply these changes after reviewing? [yes/no] `;
1540
1539
 
1541
1540
  if (!assumeYes && !process.stdin.isTTY) {
1542
1541
  console.error(prompt.trimEnd());
1543
1542
  console.error(
1544
- `${tag} · ${dim || ''}terminal no interactivo: no toco tu código. El documento ` +
1545
- `queda en ${docRel}. Re-ejecuta con --wire-yes para aplicarlo.${reset || ''}`,
1543
+ `${tag} · ${dim || ''}Non-interactive terminal: leaving your code untouched. The document ` +
1544
+ `is at ${docRel}. Re-run with --wire-yes to apply it.${reset || ''}`,
1546
1545
  );
1547
1546
  return { status: 'declined-noninteractive', docPath: docAbs, autoCount: plan.auto.length, manualCount: plan.manual.length, plan };
1548
1547
  }
@@ -1550,8 +1549,8 @@ async function wireHandlers(appRoot, opts = {}) {
1550
1549
  const yes = assumeYes || (await askYesNo(prompt));
1551
1550
  if (!yes) {
1552
1551
  console.error(
1553
- `${tag} · ${dim || ''}no modifico tu código. El documento queda en ${docRel} ` +
1554
- `por si quieres cablearlo a mano o re-ejecutar con --wire-yes.${reset || ''}`,
1552
+ `${tag} · ${dim || ''}Leaving your code untouched. The document stays at ${docRel} ` +
1553
+ `in case you want to wire it by hand or re-run with --wire-yes.${reset || ''}`,
1555
1554
  );
1556
1555
  return { status: 'declined', docPath: docAbs, autoCount: plan.auto.length, manualCount: plan.manual.length, plan };
1557
1556
  }
@@ -1561,7 +1560,7 @@ async function wireHandlers(appRoot, opts = {}) {
1561
1560
  // (don't revert a good wiring over a pre-existing failure).
1562
1561
  let baseline = null;
1563
1562
  if (runTest && testRunner) {
1564
- console.error(`${tag} · ${dim || ''}comprobando el estado del build ANTES de cablear (baseline)…${reset || ''}`);
1563
+ console.error(`${tag} · ${dim || ''}Checking the build baseline BEFORE wiring…${reset || ''}`);
1565
1564
  try {
1566
1565
  baseline = await testRunner(appRoot, { framework });
1567
1566
  } catch (err) {
@@ -1587,7 +1586,7 @@ async function wireHandlers(appRoot, opts = {}) {
1587
1586
  const droppedReview = loop.dropped.map((d) => ({
1588
1587
  ...d,
1589
1588
  confidence: 'review',
1590
- reason: `no se pudo cablear automáticamente tras ${d.attempts} intento(s): ${d.reason}`,
1589
+ reason: `could not be wired automatically after ${d.attempts} attempt(s): ${d.reason}`,
1591
1590
  }));
1592
1591
  const finalPlan = {
1593
1592
  auto: finalAuto, review: droppedReview, manual: droppedReview,
@@ -1693,7 +1692,7 @@ async function wireHandlers(appRoot, opts = {}) {
1693
1692
  if (baseline && baseline.ok === false && baseline.stage !== 'skipped-no-deps') {
1694
1693
  return { status: 'applied-untested', ...baseResult, test: baseline, preexistingFailure: true };
1695
1694
  }
1696
- console.error(`${tag} · ${dim || ''}probando que la app sigue compilando tras el cableado…${reset || ''}`);
1695
+ console.error(`${tag} · ${dim || ''}Checking the app still builds after wiring…${reset || ''}`);
1697
1696
  let test;
1698
1697
  try {
1699
1698
  test = await testRunner(appRoot, { framework });
@@ -1719,45 +1718,45 @@ function reportHandlerResult(result, colors = {}) {
1719
1718
  case 'applied':
1720
1719
  case 'applied-untested': {
1721
1720
  const rel = result.registryRel || result.registryFile;
1722
- console.error(`${tag} · ✓ cableado de handlers aplicado → ${rel} (${result.autoCount} acción(es)).`);
1721
+ console.error(`${tag} · ✓ Handler wiring applied → ${rel} (${result.autoCount} action(s)).`);
1723
1722
  if (Array.isArray(result.verification) && result.verification.length) {
1724
1723
  const ready = result.verification.filter((v) => v.status === 'ready');
1725
1724
  const corrected = ready.filter((v) => v.attempts > 1).length;
1726
1725
  const firstTry = ready.length - corrected;
1727
1726
  const eff = ready.filter((v) => v.level === 'effect').length;
1728
- console.error(`${tag} · ${dim || ''}verificación: ${ready.length} acción(es) listas (${firstTry} a la primera, ${corrected} tras auto-corrección, ${eff} con efecto real comprobado).${reset || ''}`);
1727
+ console.error(`${tag} · ${dim || ''}Verification: ${ready.length} action(s) ready (${firstTry} on first try, ${corrected} after auto-correction, ${eff} with real effect confirmed).${reset || ''}`);
1729
1728
  }
1730
1729
  if (result.editedFiles && result.editedFiles.length) {
1731
- console.error(`${tag} · ${dim || ''}puentes añadidos (reversibles) en: ${result.editedFiles.join(', ')}.${reset || ''}`);
1730
+ console.error(`${tag} · ${dim || ''}Bridges added (reversible) in: ${result.editedFiles.join(', ')}.${reset || ''}`);
1732
1731
  }
1733
1732
  if (Array.isArray(result.dropped) && result.dropped.length) {
1734
- console.error(`${tag} · ${dim || ''}${result.dropped.length} acción(es) NO se pudieron cablear tras los reintentos (revertidas, ver documento): ${result.dropped.map((d) => d.intent).join(', ')}.${reset || ''}`);
1733
+ console.error(`${tag} · ${dim || ''}${result.dropped.length} action(s) could NOT be wired after retries (reverted, see the document): ${result.dropped.map((d) => d.intent).join(', ')}.${reset || ''}`);
1735
1734
  } else if (result.manualCount) {
1736
- console.error(`${tag} · ${dim || ''}${result.manualCount} acción(es) quedaron para revisión (ver el documento).${reset || ''}`);
1735
+ console.error(`${tag} · ${dim || ''}${result.manualCount} action(s) left for review (see the document).${reset || ''}`);
1737
1736
  }
1738
1737
  if (result.test && result.test.ok) {
1739
- console.error(`${tag} · ✓ test post-cableado OK (${result.test.stage}).`);
1738
+ console.error(`${tag} · ✓ Post-wiring build check OK (${result.test.stage}).`);
1740
1739
  } else if (result.preexistingFailure) {
1741
- console.error(`${tag} · ${dim || ''}aviso: tu app YA no compilaba ANTES del cableado (${result.test && result.test.stage}); no es por Fiodos, así que conservo el cableado pero no puedo verificarlo por build.${reset || ''}`);
1740
+ console.error(`${tag} · ${dim || ''}Note: your app was ALREADY not building BEFORE wiring (${result.test && result.test.stage}); not caused by Fiodos, so the wiring is kept but cannot be build-verified.${reset || ''}`);
1742
1741
  } else if (result.status === 'applied-untested') {
1743
- console.error(`${tag} · ${dim || ''}aviso: no pude verificar el build (${result.test && result.test.stage}); revísalo.${reset || ''}`);
1742
+ console.error(`${tag} · ${dim || ''}Note: could not verify the build (${result.test && result.test.stage}); please check it.${reset || ''}`);
1744
1743
  }
1745
- console.error(`${tag} · ${dim || ''}actívalo pasando registries={fyodosGeneratedRegistries} a <FiodosAgent/> (snippet en el documento).${reset || ''}`);
1744
+ console.error(`${tag} · ${dim || ''}Enable it by passing registries={fyodosGeneratedRegistries} to <FiodosAgent/> (snippet in the document).${reset || ''}`);
1746
1745
  break;
1747
1746
  }
1748
1747
  case 'reverted':
1749
- console.error(`${tag} · ✗ el test post-cableado falló (${result.test && result.test.stage}); REVERTÍ todos los cambios para no dejar tu app rota.`);
1750
- console.error(`${tag} · ${dim || ''}detalle en el documento; nada quedó modificado en tu código.${reset || ''}`);
1748
+ console.error(`${tag} · ✗ Post-wiring build check failed (${result.test && result.test.stage}) reverted all changes, your app is untouched.`);
1749
+ console.error(`${tag} · ${dim || ''}Details in the document; nothing was modified in your code.${reset || ''}`);
1751
1750
  break;
1752
1751
  case 'manual-only':
1753
- console.error(`${tag} · ${dim || ''}cableado documentado; aplícalo a mano siguiendo ${result.docPath}.${reset || ''}`);
1752
+ console.error(`${tag} · ${dim || ''}Wiring documented; apply it by hand following ${result.docPath}.${reset || ''}`);
1754
1753
  break;
1755
1754
  case 'declined':
1756
1755
  case 'declined-noninteractive':
1757
1756
  // already reported inline
1758
1757
  break;
1759
1758
  case 'failed':
1760
- console.error(`${tag} · ✗ no se pudo escribir el registro de handlers: ${result.error && result.error.message}`);
1759
+ console.error(`${tag} · ✗ Could not write the handler registry: ${result.error && result.error.message}`);
1761
1760
  break;
1762
1761
  case 'no-actions':
1763
1762
  case 'skipped':