@trazum/cli 1.8.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.
package/src/i18n/es.ts ADDED
@@ -0,0 +1,838 @@
1
+ import type { CliMessages } from './types.js';
2
+
3
+ /** "(hace 46 días)", o nada cuando no se sabe la antigüedad. */
4
+ const hace = (days: number | null): string =>
5
+ days === null ? '' : days === 0 ? ' (hoy)' : days === 1 ? ' (hace 1 día)' : ` (hace ${days} días)`;
6
+
7
+
8
+ /** Spanish catalogue. Mirrors `en.ts`; see that file for the contract. */
9
+ export const es: CliMessages = {
10
+ locale: 'es',
11
+ numberLocale: 'es-ES',
12
+
13
+ help: (d, bold) => `${bold('trazum')} — reduce el coste de tus prompts sin perder lo que piden.
14
+
15
+ ${bold('USO')}
16
+ trazum optimize <fichero|-> [opciones]
17
+ trazum check <fichero|dir|-> --max-tokens <n> [opciones]
18
+ trazum baseline [dir] [opciones]
19
+ trazum eval <fichero> --cases <fichero> [opciones]
20
+ trazum eval <fichero> --cases <fichero> --export promptfoo -o suite.json
21
+ trazum diff <antes> <después> [opciones]
22
+ trazum diff --all <dir> <dir> [opciones]
23
+ trazum rank <dir> [opciones]
24
+ trazum doctor [dir] [opciones]
25
+ trazum blame <fichero> [opciones]
26
+ trazum prune <fichero> --cases <fichero> --yes
27
+ trazum where [fichero]
28
+ trazum models
29
+ trazum rules
30
+
31
+ ${bold('OPCIONES DE prune')}
32
+ --cases <fichero> Una entrada por línea, o un array JSON. Obligatorio.
33
+ --yes Gasta las llamadas de verdad. Sin él se imprime la
34
+ estimación y no se llama a nada.
35
+ --concurrency <n> Llamadas en vuelo a la vez. Por defecto: 3.
36
+ --json La medición como datos.
37
+
38
+ Retira cada ejemplo few-shot por turnos y mide si las respuestas se mueven más
39
+ de lo que el prompt ya se mueve por su cuenta. La factura es
40
+ (2 + ejemplos) x casos, y por eso es el único comando que pregunta antes.
41
+
42
+ Informa de "sin efecto en estas entradas", nunca de "bórralo": un ejemplo puede
43
+ existir para un caso que tus entradas no contienen. No se edita nada.
44
+
45
+ ${bold('OPCIONES DE eval')}
46
+ --cases <fichero> Una entrada por línea, o un array JSON. Obligatorio.
47
+ --level <safe|aggressive> Qué reescritura juzgar. Por defecto: safe.
48
+ --concurrency <n> Llamadas simultáneas. Por defecto: 3.
49
+ --export promptfoo Escribe una suite de promptfoo en vez de ejecutar
50
+ nada: los dos prompts, todos los casos, sin clave
51
+ de API y sin gastar ninguna llamada. Las aserciones
52
+ son tuyas: esto existe para la pregunta que la
53
+ concordancia no puede contestar.
54
+ -o, --out <fichero> Dónde escribirla. Por defecto, stdout.
55
+
56
+ ${bold('OPCIONES DE rank')}
57
+ --level <safe|aggressive> Qué reglas cuentan como recuperables. Por defecto: safe.
58
+ --model, --calls, Calcula el coste de los tokens recuperables, como
59
+ --output-tokens, --batch en optimize.
60
+ --prompt <nombre> Qué prompt marcado tomar de cada fichero de código.
61
+ --markdown-out <fichero> Escribe además el ranking en Markdown, para el
62
+ resumen de un job de CI o un comentario de PR.
63
+ --json El ranking como datos.
64
+
65
+ No hay puntuación. Los prompts se ordenan por lo que las reglas recuperarían
66
+ de verdad, midiéndolo al ejecutarlas; las demás columnas explican esa posición.
67
+
68
+ ${bold('OPCIONES DE doctor')}
69
+ --level <safe|aggressive> Qué reglas contar. Por defecto: safe.
70
+ --model, --calls, Calcula el coste de los hallazgos, igual que optimize.
71
+ --output-tokens, --batch
72
+ --prompt <nombre> Qué prompt marcado tomar de cada fichero de código.
73
+ --otlp-out <fichero> Escribe el reconocimiento como métricas OpenTelemetry
74
+ (JSON OTLP/HTTP). Trazum escribe el fichero; tu
75
+ pipeline lo envía.
76
+ --json El reconocimiento como datos.
77
+
78
+ Revisa un workspace completo: qué prompts no vigila nada, cuáles ya se pasan del
79
+ presupuesto y cuánto suman los avisos en todos ellos. Cada hallazgo es un aviso
80
+ que trazum optimize da en ese prompt por separado, así que cualquier línea se
81
+ puede comprobar contra un solo fichero. No hay puntuación.
82
+
83
+ Sale con 0 aunque encuentre cosas. trazum check es la puerta.
84
+
85
+ ${bold('OPCIONES DE blame')}
86
+ --limit <n> Revisiones a recorrer. Por defecto: 20, máximo 500.
87
+ --prompt <nombre> Sigue un prompt marcado dentro de un fichero fuente,
88
+ para que refactorizar los imports no cuente como
89
+ crecimiento del prompt.
90
+ --model, --calls, Calcula el coste del movimiento, igual que optimize.
91
+ --output-tokens, --batch
92
+ --markdown-out <fichero> Escribe además el historial en Markdown, para el
93
+ resumen de un job de CI o un comentario de PR.
94
+ --json El historial como datos.
95
+
96
+ Después de "--" las rutas se toman literales: trazum blame -- --nombre-raro.txt
97
+
98
+ ${bold('OPCIONES DE optimize')}
99
+ --level <safe|aggressive> Agresividad de las reglas. Por defecto: safe.
100
+ --model <id> Modelo para calcular el coste. Por defecto: ${d.model}.
101
+ --calls <n> Llamadas al mes. Por defecto: ${d.callsPerMonth}.
102
+ --output-tokens <n> Tokens de salida medios. Por defecto: ${d.avgOutputTokens}.
103
+ --cache-hit-rate <0-1> Tasa de acierto de caché estimada. Por defecto: ${d.cacheHitRate}.
104
+ --batch El trabajo tolera latencia (Batch API, 50% menos).
105
+ --disable <id,id> Desactiva reglas concretas (ver "trazum rules").
106
+ --suggest Pide al LLM reescrituras a nivel de frase y las lista
107
+ con lo que ahorra cada una. Por sí solo no cambia
108
+ nada: cada propuesta se comprueba contra tu prompt y
109
+ se descarta si no sobrevive.
110
+ --apply-suggestions Las aplica. Solo con --suggest; a secas es un error,
111
+ no un flag que se ejecuta sin hacer nada.
112
+ --cache-suggestions Responde a --suggest desde una caché local cuando ya
113
+ se preguntó por el mismo prompt, en vez de pagar otra
114
+ vez la llamada. Desactivado por defecto: un acierto es
115
+ lo que el modelo dijo la última vez, y eso debe
116
+ elegirse. En $XDG_CACHE_HOME/trazum, 0600, 7 días.
117
+ --reorder Mueve las instrucciones estables delante del primer
118
+ marcador, para que la caché de prompts las alcance. Esto
119
+ MUEVE texto en vez de borrarlo: lee el diff y decide si
120
+ el orden importaba. Se niega con cualquier bloque que
121
+ haga referencia hacia atrás ("el texto anterior"), y
122
+ dice qué frase lo ha impedido.
123
+ --llm Añade una pasada por el LLM configurado por entorno.
124
+ --exact-tokens Cuenta tokens con la API oficial en vez de la heurística.
125
+ --tokens-only Informa del ahorro de tokens y de ningún importe. Es
126
+ lo que hace por defecto dentro de Claude Code, Codex
127
+ o Cursor, donde una suscripción significa que no hay
128
+ factura que reducir.
129
+ --cost Muestra el dinero también ahí: el host dice dónde se
130
+ ejecuta Trazum, no a dónde va tu prompt.
131
+ --prompt <nombre> Qué prompt marcado optimizar, cuando un fichero
132
+ fuente tiene más de uno. Ver "trazum where".
133
+ --diff Muestra el diff línea a línea.
134
+ --json Vuelca el informe completo en JSON.
135
+ --locale <${d.locales.join('|')}> Idioma del informe. Por defecto: el del sistema.
136
+ -o, --out <fichero> Escribe el prompt optimizado a un fichero.
137
+ -h, --help Esta ayuda.
138
+ --clear-suggestion-cache Vacía la caché de --cache-suggestions y dice cuánto
139
+ ocupaba. Es un recado, no un modo: no necesita
140
+ comando ni lee el config.
141
+
142
+ ${bold('OPCIONES DE check')}
143
+ --max-tokens <n> Presupuesto de tokens de entrada. Obligatorio salvo que el config cubra el fichero.
144
+ --level <safe|aggressive> Nivel al calcular si el prompt optimizado cabría.
145
+ --exact-tokens Recuento exacto (necesita ANTHROPIC_API_KEY).
146
+ --json Resultado en JSON.
147
+ --markdown-out <fichero> Escribe además el informe en Markdown, para el resumen
148
+ de un job de CI o un comentario de pull request.
149
+ --baseline Aplica la línea base registrada. Activo por defecto siempre
150
+ que el config declare una, así CI no necesita argumentos; la
151
+ forma útil es --no-baseline, que la omite en una ejecución.
152
+
153
+ Pensado para CI: sale con código 1 si el prompt supera el presupuesto,
154
+ así una plantilla que crece sin control rompe la build en vez de la factura.
155
+
156
+ Si le das un directorio, comprueba todos los prompts que hay dentro contra
157
+ los patrones de "budgets" de ${bold('trazum.config.json')} — un solo paso de CI para
158
+ todo un repositorio de prompts. Un fichero que ningún patrón cubre sale
159
+ listado como sin presupuesto, no omitido en silencio, y una ejecución en la
160
+ que no se ha presupuestado nada es un error: "0 fallos" de una comprobación
161
+ que no ha medido nada es lo más engañoso que podría decirte.
162
+
163
+ ${bold('OPCIONES DE baseline')}
164
+ Registra lo que cuestan ahora mismo los prompts de un directorio, en un fichero
165
+ del que haces commit. Después "check" tumba la build cuando el repositorio se
166
+ desvía de él: la pregunta que los presupuestos no pueden responder, porque un
167
+ repositorio al 95% de todos sus presupuestos pasa siempre mientras un PR añade
168
+ cuatrocientos tokens repartidos en doce ficheros.
169
+
170
+ -o, --out <fichero> Dónde escribirlo. Por defecto: baseline.path del config,
171
+ o trazum.baseline.json.
172
+ --model, --calls, --output-tokens, --cache-hit-rate, --batch
173
+ El escenario con el que se registra la cifra mensual. Se
174
+ registra para que una comparación posterior pueda decir si el
175
+ dinero es comparable — la puerta va en tokens, así que
176
+ cambiar el precio de un modelo nunca tumba una build por sí solo.
177
+ --exact-tokens Recuentos exactos (necesita ANTHROPIC_API_KEY).
178
+ --json Resultado en JSON.
179
+
180
+ Nunca falla. Registrar no es un veredicto, y un comando que pudiera fallar
181
+ mientras escribe aquello con lo que arreglarías el fallo es un bucle.
182
+
183
+ ${bold('OPCIONES DE eval')}
184
+ --cases <fichero> Entradas a probar, una por línea o array JSON. Obligatorio.
185
+ --level <safe|aggressive> Nivel con el que optimizar antes de comparar.
186
+ --concurrency <n> Casos en paralelo. Por defecto: 3.
187
+ --json Resultado en JSON.
188
+
189
+ Ejecuta las dos versiones del prompt sobre tus casos y dice si la
190
+ optimización ha cambiado las respuestas. Cuesta TRES llamadas por caso: el
191
+ original dos veces, para medir la varianza propia del modelo, y el optimizado
192
+ una. Esa base es la vara de medir: sin ella, un porcentaje de divergencia no
193
+ significa nada. Sale con código 1 cuando las respuestas divergen de verdad.
194
+
195
+ ${bold('OPCIONES DE diff')}
196
+ --max-growth <n> Falla si el prompt ha crecido más de n tokens.
197
+ --all Compara dos directorios de prompts, emparejados por
198
+ ruta relativa. Los prompts que solo están en un lado
199
+ se nombran, nunca se cuentan: una eliminación es una
200
+ pregunta, no un ahorro. --max-growth se aplica
201
+ entonces por prompt, no al total.
202
+ --optimized Mide lo que dejarían las reglas, no lo escrito.
203
+ --level <safe|aggressive> Nivel para las reglas y los avisos.
204
+ --model <id> Modelo con el que calcular el coste.
205
+ --calls <n> Llamadas al mes, para la cifra de coste.
206
+ --json Resultado en JSON.
207
+ --markdown-out <fichero> Escribe además el informe en Markdown, para el resumen
208
+ de un job de CI o un comentario de pull request.
209
+
210
+ Compara dos versiones de un prompt: cómo se ha movido el recuento de tokens,
211
+ cuánto cuesta eso, qué avisos ha introducido o resuelto la edición. Toda
212
+ cifra es un delta y positivo significa peor. Informa y sale con 0 salvo que
213
+ des --max-growth: decidir que crecer es inaceptable es cosa tuya, no nuestra.
214
+
215
+ ${bold('trazum where')}
216
+ Dice a qué proveedor van de verdad los prompts de un fichero, y cómo lo sabe:
217
+ un import del SDK, una base URL, un id de modelo entrecomillado, o "model=" en
218
+ un marcador trazum:prompt. Toda respuesta nombra la línea de la que sale.
219
+
220
+ Se niega cuando un fichero nombra dos proveedores en vez de elegir uno. Dos
221
+ respuestas no son una versión débil de una, y elegir en silencio es como
222
+ alguien acaba presupuestando contra el proveedor equivocado durante un mes.
223
+
224
+ Una base URL gana al SDK al que apunta: Moonshot, DeepSeek, xAI y Groq se
225
+ llaman con el SDK de OpenAI y otra base_url, así que tratarlo como
226
+ contradicción sería negarse a preciar un cliente perfectamente normal.
227
+
228
+ Sin fichero, informa solo de dentro de qué herramienta se ejecuta Trazum — y
229
+ avisa cuando esa herramienta cobra por suscripción, porque ahí un ahorro
230
+ mensual es aritmética sobre tokens, no dinero que recuperes.
231
+
232
+ ${bold('FICHERO DE CONFIGURACIÓN')}
233
+ ${bold('trazum.config.json')}, que se busca subiendo desde el directorio de trabajo
234
+ y parando en la raíz del repositorio. Todas las claves son opcionales:
235
+
236
+ level, locale, disable, maxGrowth, extensions
237
+ usage { model, callsPerMonth, avgOutputTokens, cacheHitRate, batchEligible }
238
+ budgets { "prompts/**": 2000, "prompts/system.txt": 4000 }
239
+ baseline { "path": "trazum.baseline.json", "maxGrowthTokens": 0, "maxGrowthPct": 5 }
240
+ pricing "./prices.json" — correcciones locales de precios, ver abajo
241
+
242
+ Las opciones ganan al config; el config gana a los valores por defecto. Los
243
+ presupuestos se resuelven con el patrón más específico que encaje — gana el
244
+ que tenga más caracteres literales. Un booleano que el config haya activado se
245
+ desactiva con --no-<opción>, por ejemplo --no-batch.
246
+
247
+ ${bold('budgets')} es un techo; ${bold('baseline')} es una puerta. Uno pregunta si un
248
+ fichero cabe, la otra si el repositorio ha empeorado respecto al commit que
249
+ alguien registró con "trazum baseline". Un repositorio al 95% de todos sus
250
+ presupuestos pasa siempre, mientras un PR añade cuatrocientos tokens
251
+ repartidos en doce ficheros. Con baseline en el config, "check" sobre un
252
+ directorio lo lee y lo aplica sin ninguna opción: una puerta que exige
253
+ acordarse de pasar un argumento se ejecuta en la terminal del autor y no en
254
+ CI. Los umbrales van en tokens, nunca en dólares — si no, cambiar el precio de
255
+ un modelo tumbaría una build por algo que nadie tocó.
256
+
257
+ Un config que no valide es un error, incluida una clave desconocida. Un
258
+ parser permisivo restauraría los valores por defecto en silencio, y para un
259
+ presupuesto el valor por defecto es "sin presupuesto": una build en verde de
260
+ un prompt que nadie ha medido.
261
+
262
+ --config <fichero> Usa este config en vez de buscar uno.
263
+
264
+ ${bold('PRECIOS')}
265
+ Los precios cambian en el calendario de otros, así que corregir uno no exige
266
+ actualizar Trazum. Un overlay de precios es un JSON que se superpone al
267
+ catálogo incluido:
268
+
269
+ { "lastReviewed": "2027-01-15",
270
+ "models": { "claude-opus-5": { "inputPerMTok": 6 } } }
271
+
272
+ Solo cambian los campos que nombres. Un modelo que no esté en el catálogo
273
+ incluido tiene que venir completo: uno definido a medias costaría cero por
274
+ algún lado y anunciaría un ahorro que no existe. "promo": null retira una
275
+ promoción.
276
+
277
+ Todo informe dice cuándo se han usado precios de overlay y qué modelos cubren:
278
+ una cifra del catálogo incluido y una de tu JSON son indistinguibles si no.
279
+
280
+ --pricing <fichero> Usa este overlay, por delante del del config.
281
+ --pricing-live Toma los precios de OpenRouter en vez de la tabla
282
+ incluida: cifras de hoy para cientos de modelos de
283
+ docenas de proveedores. Opt-in, porque es una llamada
284
+ de red — el núcleo determinista no hace ninguna.
285
+ Un fichero --pricing gana sobre esto.
286
+
287
+ Esa fuente no publica si un modelo tiene caché de
288
+ prompt ni el mínimo a partir del cual cachea. Así que
289
+ los modelos que añade no reciben ningún consejo de
290
+ caché, en vez de una suposición: afirmar que cachea
291
+ ofrecería un ahorro que nadie puede comprar, y
292
+ afirmar que no esconde el mayor ahorro que hay.
293
+
294
+ ${bold('LLM OPCIONAL')}
295
+ El núcleo es determinista y gratis. Con --llm se añade una pasada de
296
+ compresión semántica usando el proveedor que configures por entorno:
297
+
298
+ TRAZUM_LLM_PROVIDER openai | anthropic (por defecto: openai)
299
+ TRAZUM_LLM_BASE_URL https://tu-llm/v1
300
+ TRAZUM_LLM_API_KEY tu clave
301
+ TRAZUM_LLM_MODEL identificador del modelo
302
+
303
+ El resultado del LLM solo se acepta si es más corto y conserva intacto
304
+ el código, las URLs y los marcadores de plantilla.
305
+
306
+ ${bold('IDIOMA')}
307
+ El idioma del informe sale de --locale, luego TRAZUM_LOCALE, luego LANG, y por
308
+ último del fichero de configuración — así un proyecto puede fijar el idioma en
309
+ el que se leen sus logs de CI sin pisar el idioma de quien está al teclado.
310
+ Solo cambia el informe: el mismo prompt se optimiza siempre igual.
311
+
312
+ ${bold('EJEMPLOS')}
313
+ trazum optimize prompt.txt --calls 50000 --diff
314
+ cat prompt.md | trazum optimize - --level aggressive --json
315
+ trazum optimize prompt.txt --reorder --diff
316
+ trazum optimize prompt.txt --llm -o prompt.optimizado.txt
317
+ trazum eval prompt.txt --cases casos.txt --level aggressive
318
+ trazum diff prompts/system.txt prompts/system.new.txt --max-growth 10
319
+ trazum check prompts/
320
+ `,
321
+
322
+ cache: {
323
+ cleared: (entries: number, bytes: number, dir: string) =>
324
+ entries === 0
325
+ ? `No hay sugerencias en caché que eliminar (${dir}).`
326
+ : `Se eliminaron ${entries} ${entries === 1 ? 'respuesta' : 'respuestas'} en caché (${(bytes / 1024).toFixed(1)} KB) de ${dir}.`,
327
+ used: (hits: number, misses: number) =>
328
+ `Sugerencias: ${hits} desde la caché, ${misses} consultadas. Las respuestas en caché son lo que el modelo dijo la última vez; usa --clear-suggestion-cache para empezar de nuevo.`,
329
+ },
330
+
331
+ errors: {
332
+ livePricingFailed: (url: string, detail: string) =>
333
+ `No se han podido cargar los precios en vivo desde ${url}: ${detail}. Los precios incluidos siguen ahí — quita --pricing-live para usarlos.`,
334
+ optionNeedsValue: (name) => `La opción --${name} necesita un valor.`,
335
+ mustBeNonNegative: (name, raw) =>
336
+ `--${name} debe ser un número no negativo (recibido: "${raw}").`,
337
+ badLevel: (received) => `--level debe ser "safe" o "aggressive" (recibido: "${received}").`,
338
+ unknownRuleInDisable: (id) =>
339
+ `Regla desconocida en --disable: "${id}". Lista completa: trazum rules`,
340
+ unknownCommand: (command) => `Comando desconocido: "${command}". Prueba con "trazum --help".`,
341
+ missingInputFile: () =>
342
+ 'Falta el fichero de entrada. Usa "-" para leer de la entrada estándar.',
343
+ applyNeedsSuggest: () =>
344
+ '--apply-suggestions no tiene nada que aplicar sin --suggest. Por su cuenta habría '
345
+ + 'funcionado en silencio sin cambiar nada, y eso no es una respuesta.',
346
+ llmNotConfigured: () =>
347
+ 'Has pedido --llm pero no hay proveedor configurado.\n' +
348
+ 'Define TRAZUM_LLM_BASE_URL y TRAZUM_LLM_MODEL (endpoint compatible con OpenAI),\n' +
349
+ 'o TRAZUM_LLM_PROVIDER=anthropic con TRAZUM_LLM_API_KEY.',
350
+ exactTokensNeedsKey: () => '--exact-tokens necesita ANTHROPIC_API_KEY en el entorno.',
351
+ checkNeedsMaxTokens: () => 'trazum check necesita --max-tokens <n>.',
352
+ evalNeedsCases: () => 'trazum eval necesita --cases <fichero>.',
353
+ unknownExportFormat: (received, allowed) =>
354
+ `Formato de exportación desconocido "${received}". Disponibles: ${allowed}.`,
355
+ evalNoCases: (path) => `No se ha encontrado ningún caso en "${path}".`,
356
+ unknownFlag: (name, allowed) =>
357
+ `Opción desconocida --${name}. Este comando acepta: ${allowed}.`,
358
+ unknownFlagDidYouMean: (name, suggestion) =>
359
+ `Opción desconocida --${name}. ¿Querías decir --${suggestion}?`,
360
+ diffNeedsTwoFiles: () =>
361
+ 'trazum diff necesita dos ficheros: trazum diff <antes> <después>.',
362
+ cannotNegate: (name) => `--no-${name} no tiene sentido: --${name} lleva un valor.`,
363
+ noPromptsFound: (directory, extensions) =>
364
+ `No hay ficheros de prompt en "${directory}". Se han buscado: ${extensions}.`,
365
+ noBudgetsApply: (directory, configFile) =>
366
+ `Ningún presupuesto cubre nada dentro de "${directory}". Añade uno en ${configFile}, en "budgets", o pasa --max-tokens. ` +
367
+ 'Decir "0 fallos" de unos ficheros que nadie ha medido sería peor que este error.',
368
+ baselineMissing: (path) =>
369
+ `El config declara una l\u00ednea base en "${path}" y no est\u00e1. Registra una con "trazum baseline" y hazle commit. Es un error y no una comprobaci\u00f3n omitida: una puerta que el config ha pedido y no se ha podido ejecutar no es un aprobado.`,
370
+ baselineTooBig: (path, limit) =>
371
+ `"${path}" supera el l\u00edmite de ${limit} bytes para una l\u00ednea base. En esa ruta hay algo que no es una l\u00ednea base.`,
372
+ errorLabel: () => 'Error',
373
+ },
374
+
375
+ report: {
376
+ inputTokens: () => 'Tokens de entrada',
377
+ estimated: (offFamily) =>
378
+ offFamily === null
379
+ ? ' (estimado, ±15%)'
380
+ : ` (estimado — el contador está calibrado sobre Claude, no sobre ${offFamily})`,
381
+ exactCount: () => ' (recuento exacto)',
382
+ rulesApplied: () => 'Reglas aplicadas',
383
+ nothingToTrim: () => ' Ninguna regla ha encontrado nada que recortar.',
384
+ dictionaryCoverage: (languages) =>
385
+ ` Los diccionarios de frases cubren ${languages}. Un prompt en otro idioma `
386
+ + 'no es necesariamente eficiente: puede ser simplemente uno que Trazum '
387
+ + 'todavía no sabe leer.',
388
+ levelAggressive: () => '[agresiva]',
389
+ levelSafe: () => '[segura]',
390
+ ruleHits: (hits, tokensSaved) => `(${hits}×, ~${tokensSaved} tokens)`,
391
+ moreChanges: (count) => `+${count} más sin mostrar`,
392
+ llmPass: () => 'Pasada por LLM',
393
+ examplesReview: () => 'Ejemplos que el modelo considera redundantes',
394
+ examplesReviewNote: (provider, model, count) =>
395
+ `${count} ejemplos revisados por ${provider}/${model}. Es una sugerencia que leer, no un cambio hecho.`,
396
+ exampleRedundant: (redundant, keep) =>
397
+ `El ejemplo ${redundant.map((i) => i + 1).join(', ')} repite el ejemplo ${keep + 1}`,
398
+ llmApplied: (provider, model, before, after) =>
399
+ `aplicada vía ${provider}/${model}: ${before} → ${after} tokens`,
400
+ llmRejected: (reason) => `descartada: ${reason}`,
401
+ costWith: (modelName) => `Coste con ${modelName}`,
402
+ usageLine: (calls, outputTokens, batch) =>
403
+ `${calls} llamadas/mes · ${outputTokens} tokens de salida por llamada${
404
+ batch ? ' · Batch API' : ''
405
+ }`,
406
+ perMonthSaving: (saving, pct) => `ahorro ${saving}/mes (${pct}%)`,
407
+ beyondShortening: () => 'Además de acortar el prompt',
408
+ biggestLever: () => 'Empieza por aquí:',
409
+ biggestLeverDetail: (title, amount, times) =>
410
+ // El título conserva sus mayúsculas: pasarlo a minúsculas convertía
411
+ // "Claude Opus 5" en "claude opus 5", un nombre de producto destrozado.
412
+ `"${title}" — ${amount}/mes` +
413
+ (times !== null && times >= 2 ? `, ${times}× lo que han ahorrado las reglas.` : '.'),
414
+ perMonthSuffix: (amount) => ` ~${amount}/mes`,
415
+ diff: () => 'Diff',
416
+ tokensOnlyHeading: (host) => `Qué ganas con esto en ${host}`,
417
+ tokensOnlyWhy: (host) =>
418
+ `${host} cobra por suscripción, así que no hay factura que reducir ni cifra mensual que imprimir.`,
419
+ tokensOnlyAsked: () => 'Costes ocultos porque has pedido solo tokens.',
420
+ tokensSaved: (tokens) =>
421
+ `${tokens} token${tokens === '1' ? '' : 's'} de vuelta, en cada llamada.`,
422
+ windowUse: (before, after, model, window) =>
423
+ `Ventana de contexto: ${before} → ${after} de los ${window} tokens de ${model} — sitio que se lleva la conversación.`,
424
+ tokensOnlyCost: () => 'Usa --cost si este prompt va a una API de pago por uso.',
425
+ pricingOverlaid: (models, lastReviewed) =>
426
+ `Los precios de ${models} vienen de un overlay local revisado el ${lastReviewed}, no del catálogo incluido.`,
427
+ reorderHeading: () => 'Reordenado para la caché',
428
+ reorderMoved: (blocks, tokens) =>
429
+ `Se ${
430
+ blocks === 1 ? 'ha movido 1 bloque' : `han movido ${blocks} bloques`
431
+ } (~${tokens} tokens) delante del primer marcador.`,
432
+ reorderPrefix: (before, after) => `Prefijo cacheable ${before} → ${after} tokens.`,
433
+ reorderDeclined: (count) =>
434
+ `Se ${
435
+ count === 1 ? 'ha dejado 1 bloque donde estaba' : `han dejado ${count} bloques donde estaban`
436
+ }:`,
437
+ reorderDeclinedRef: (phrase, excerpt) => `hace referencia hacia atrás ("${phrase}"): ${excerpt}`,
438
+ reorderDeclinedAfter: (excerpt) => `va después de un bloque que tenía que quedarse: ${excerpt}`,
439
+ reorderDeclinedScript: (script) =>
440
+ `este prompt está escrito en ${script}, y Trazum no tiene frases de referencia hacia ` +
441
+ `atrás para ese alfabeto. No puede distinguir "resume el texto de arriba" de una ` +
442
+ `instrucción que sí se puede mover, así que no ha movido nada. Añadir un idioma es ` +
443
+ `añadir un array a phrases.ts.`,
444
+ reorderDeclinedMore: (count) => `…y ${count} más, en el fichero de salida.`,
445
+ reorderPiped: (moved, tokens, declined) => {
446
+ const head =
447
+ moved === 0
448
+ ? 'no se ha podido mover nada con seguridad'
449
+ : `se ${
450
+ moved === 1 ? 'ha movido 1 bloque' : `han movido ${moved} bloques`
451
+ } (~${tokens} tokens) al prefijo cacheable`;
452
+ const tail =
453
+ declined === 0
454
+ ? ''
455
+ : `; ${
456
+ declined === 1 ? 'se ha dejado 1 bloque' : `se han dejado ${declined} bloques`
457
+ } en su sitio`;
458
+ return `trazum: ${head}${tail}. Ejecuta sin redirigir la salida para ver los motivos.`;
459
+ },
460
+ reorderNothing: () => 'No se ha podido mover nada con seguridad.',
461
+ suggestHeading: () => 'Reescrituras sugeridas',
462
+ suggestOffered: (count, tokens) =>
463
+ `${count} ${count === 1 ? 'frase podría decir' : 'frases podrían decir'} lo mismo con ~${tokens} tokens menos:`,
464
+ suggestApplied: (count, tokens) =>
465
+ `Aplicadas ${count} ${count === 1 ? 'reescritura' : 'reescrituras'} (~${tokens} tokens). Lee el diff.`,
466
+ suggestNothing: (provider, model) =>
467
+ `${provider} (${model}) no ha encontrado nada que reescribir que las reglas no hubieran cogido ya.`,
468
+ suggestRejected: (count) =>
469
+ `${count} ${count === 1 ? 'propuesta no ha superado' : 'propuestas no han superado'} la comprobación contra tu prompt.`,
470
+ suggestRemoved: () => '(eliminado)',
471
+ suggestHowToApply: () => 'No se ha cambiado nada. Añade --apply-suggestions para aplicarlas.',
472
+ reorderReview: () =>
473
+ 'Lee el diff: esto ha movido texto en vez de borrarlo, así que la pregunta es si el orden importaba.',
474
+ diffTooLarge: (lines, max) =>
475
+ ` Diff omitido: ${lines} líneas supera el límite de ${max}, y alinearlas costaría más memoria de lo que vale la respuesta.`,
476
+ wroteTo: (path) => `Prompt optimizado escrito en ${path}`,
477
+ },
478
+
479
+ where: {
480
+ hostHeading: () => 'Ejecutándose dentro de',
481
+ subscription: (host) =>
482
+ `${host} cobra por suscripción, no por token. Un ahorro mensual de los de abajo es aritmética sobre tokens, no dinero que recuperes: lo que ganas es margen de ventana de contexto y de rate limit.`,
483
+ noTarget: () => 'Pasa un fichero fuente para ver a qué proveedor van sus prompts.',
484
+ sourceHeading: (path) => `Los prompts de ${path} van a`,
485
+ conflict: () => 'No se puede saber: el fichero nombra más de un proveedor.',
486
+ conflictFallback: () =>
487
+ 'No se ha asumido nada. Define "usage.model" en trazum.config.json, o pasa --model.',
488
+ nothingFound: () => 'Nada en este fichero dice a qué proveedor llama.',
489
+ providerOnly: () => ' (solo el proveedor — nada nombra un modelo)',
490
+ evidenceLine: (line, kind, detail) => `línea ${line} ${kind}: ${detail}`,
491
+ pricedAs: () => 'Se cobra como',
492
+ fromConfig: () => '(de trazum.config.json)',
493
+ fromDetection: () => '(leído del código)',
494
+ fromProviderDefault: (provider) =>
495
+ `(${provider} se ha leído del código; nada nombra un modelo, así que este es el suyo)`,
496
+ fromDefault: () => '(el valor por defecto — nada dijo otra cosa)',
497
+ },
498
+
499
+ pricing: {
500
+ liveLoaded: (added: number, refreshed: number, skipped: number) =>
501
+ `Precios en vivo: ${refreshed} actualizados, ${added} modelos añadidos, ${skipped} descartados por no traer precio o ventana de contexto utilizables. Esta fuente no publica los mínimos de caché, así que el consejo de caché se omite para los modelos añadidos.`,
502
+ },
503
+
504
+ models: {
505
+ title: () => 'Modelos y precios',
506
+ unit: () => ' (USD por millón de tokens)',
507
+ reviewedOn: (date, days) =>
508
+ ` Tabla revisada el ${date}${hace(days)}. Verifica antes de presupuestar.`,
509
+ columns: {
510
+ model: 'modelo',
511
+ input: 'entrada',
512
+ output: 'salida',
513
+ context: 'contexto',
514
+ cacheMin: 'mín. caché',
515
+ },
516
+ promoNote: () => ' Los precios entre paréntesis son el precio tras acabar la promoción.',
517
+ cacheNote: () =>
518
+ ' Caché: leer cuesta el 10% de la entrada; escribir, el 125% (5 min) o 200% (1 h).',
519
+ batchNote: () => ' Batch API: 50% de descuento sobre entrada y salida.',
520
+ },
521
+
522
+ rank: {
523
+ heading: (root, count) =>
524
+ `${count} ${count === 1 ? 'prompt' : 'prompts'} en ${root}, primero los más recuperables`,
525
+ subheading: (model, calls) => `Calculado con ${model} y ${calls} llamadas al mes.`,
526
+ columns: {
527
+ recoverable: 'Recupera',
528
+ tokensBack: 'Tokens',
529
+ tokens: 'Tamaño',
530
+ density: 'Tok/fra',
531
+ notes: 'Prompt',
532
+ },
533
+ noteExamples: (count, tokens) => `${count} ejemplos, ~${tokens} tokens`,
534
+ noteFormat: (tokens) => `~${tokens} tokens repitiendo el formato de salida`,
535
+ noteProtected: (pct) => `el ${pct}% es código o URLs, que no se pueden recortar`,
536
+ skipped: (count) =>
537
+ `Se ${count === 1 ? 'ha omitido' : 'han omitido'} ${count} fichero${count === 1 ? '' : 's'} de código sin marcador `
538
+ + '`// trazum:prompt`: sus prompts no están en este ranking.',
539
+ densityNote: () =>
540
+ 'Tok/fra son tokens por frase: verbosidad independiente de la longitud. No hay puntuación: cada columna es una medida que puedes comprobar contra el fichero.',
541
+ recoverableNote: () =>
542
+ 'Recupera es lo que quitarían las reglas deterministas en este nivel, valorado con el perfil de uso, con el número de tokens al lado: ahorrar un token son veinticinco céntimos y ningún trabajo que merezca la pena. Se obtiene ejecutando las reglas, no con una fórmula.',
543
+ },
544
+
545
+ blame: {
546
+ heading: (path, revisions) =>
547
+ `${path} — ${revisions} ${revisions === 1 ? 'revisión' : 'revisiones'}`,
548
+ notARepository: () =>
549
+ 'blame lee el historial de un fichero desde git, y este directorio no está dentro de un repositorio.',
550
+ outsideRepository: (path) =>
551
+ `${path} está fuera del repositorio, así que no hay historial que leer.`,
552
+ noHistory: (path) => `git no tiene commits que toquen ${path}.`,
553
+ gitMissing: () => 'git no está en el PATH, y blame no tiene de dónde leer el historial.',
554
+ columns: { when: 'Fecha', tokens: 'Tokens', change: 'Cambio', who: 'Autor', commit: 'Commit' },
555
+ net: (first, last, delta, pct) =>
556
+ `Neto en este historial: ${first} → ${last} tokens (${delta}, ${pct}).`,
557
+ netCost: (amount, model, calls) =>
558
+ `Ese movimiento son ${amount} al mes en ${model} con ${calls} llamadas.`,
559
+ biggestRise: () => 'Mayor subida en un solo commit',
560
+ biggestRiseDetail: (tokens, author, subject, sha) =>
561
+ `+${tokens} tokens — ${author}, "${subject}" (${sha})`,
562
+ addedAt: () => 'añadido',
563
+ goneAt: () => 'no existía',
564
+ truncated: (shown) =>
565
+ `Mostrando las ${shown} más recientes. Usa --limit para ver más.`,
566
+ followedRename: (from) => `Se ha seguido un renombrado: las revisiones anteriores son ${from}.`,
567
+ estimateNote: () =>
568
+ 'Los recuentos son estimaciones (±15%). Lo que importa es la tendencia, no las cifras absolutas.',
569
+ },
570
+
571
+ languages: {
572
+ and: 'y',
573
+ en: 'inglés',
574
+ es: 'español',
575
+ fr: 'francés',
576
+ de: 'alemán',
577
+ pt: 'portugués',
578
+ it: 'italiano',
579
+ nl: 'neerlandés',
580
+ },
581
+
582
+ rules: {
583
+ title: () => 'Reglas disponibles',
584
+ disableHint: () => ' Desactiva las que no quieras con --disable id1,id2',
585
+ },
586
+
587
+
588
+ doctor: {
589
+ heading: (root, prompts) => `${root} — ${prompts} prompt${prompts === 1 ? '' : 's'}`,
590
+ subheading: (model, calls) => `Con precios de ${model} y ${calls} llamadas al mes.`,
591
+ pricesReviewed: (date, days) => `Precios revisados el ${date}${hace(days)}.`,
592
+ budgetsHeading: () => 'Presupuestos',
593
+ everyPromptBudgeted: (count) =>
594
+ count === 1
595
+ ? 'El prompt tiene presupuesto y está dentro.'
596
+ : `Los ${count} prompts tienen presupuesto y están dentro.`,
597
+ unbudgeted: (count, total) =>
598
+ `${count} de ${total} prompt${count === 1 ? '' : 's'} sin presupuesto, así que nada los vigila`,
599
+ overBudget: (count) =>
600
+ count === 1
601
+ ? '1 prompt ya se pasa de su presupuesto — trazum check fallaría con él'
602
+ : `${count} prompts ya se pasan de su presupuesto — trazum check fallaría con ellos`,
603
+ andMore: (count) => `y ${count} más`,
604
+ findingsHeading: () => 'Qué merecería la pena arreglar',
605
+ acrossPrompts: (count) => `${count} prompt${count === 1 ? '' : 's'}`,
606
+ findingsNote: () =>
607
+ 'Cada línea es el mismo aviso que trazum optimize da en esos prompts, sumado. '
608
+ + 'Ejecútalo sobre cualquiera de ellos para ver la cifra por separado.',
609
+ notAGate: () =>
610
+ 'Nada de esto hace fallar un build. trazum check es la puerta; esto es el '
611
+ + 'reconocimiento — la recomendación de modelo es una heurística de palabras clave, '
612
+ + 'y un build que dependa de una enseña a repetir hasta que salga verde.',
613
+
614
+ sharedPrefixHeading: () => 'Preámbulos que podrían compartir caché y no lo hacen',
615
+ sharedPrefixGroup: (count, tokens, drift) =>
616
+ `${count} prompts empiezan con el mismo preámbulo de ${tokens} tokens, `
617
+ + (drift === 'whitespace'
618
+ ? 'y solo difieren en espaciado'
619
+ : 'y difieren en redacción, mayúsculas o puntuación'),
620
+ sharedPrefixFix: (drift) =>
621
+ drift === 'whitespace'
622
+ ? 'Lo arregla un formateador: el texto ya coincide, el espaciado no.'
623
+ : 'Alguien tiene que elegir una redacción — difiere el texto, no solo su espaciado.',
624
+ sharedPrefixNoFigure: () =>
625
+ 'No se adjunta cifra, a propósito. El caché compara bytes, así que estos prompts '
626
+ + 'ocupan una entrada cada uno en lugar de una entre todos — pero lo que eso cuesta '
627
+ + 'depende de cómo se reparten las llamadas dentro del grupo, y Trazum aplica un '
628
+ + 'único --cache-hit-rate a todos. Cifrarlo sería inventarse tu tráfico.',
629
+ },
630
+
631
+ prune: {
632
+ needsExamples: () =>
633
+ 'Este prompt tiene menos de dos ejemplos few-shot, así que no hay nada que comparar.',
634
+ estimate: (examples, cases, calls) =>
635
+ `${examples} ejemplos × ${cases} casos: ${calls} llamadas al proveedor `
636
+ + `(2 de referencia por caso, y luego una por ejemplo retirado).`,
637
+ needsConsent: () =>
638
+ 'No se ha llamado a nada. Añade --yes para gastarlo. Es el único comando que '
639
+ + 'pregunta, porque es el único cuya factura crece con la longitud de tu prompt.',
640
+ heading: (model) => `Qué hace cada ejemplo, medido en ${model}`,
641
+ selfAgreement: (pct) =>
642
+ `El prompt coincide consigo mismo el ${pct} de las veces. Ese es el patrón de medida: `
643
+ + 'una retirada que mueva la respuesta menos que eso no movió nada atribuible al ejemplo.',
644
+ line: (n, tokens, pct) => `ejemplo ${n} — ${tokens} tokens, ${pct} de coincidencia sin él`,
645
+ verdictNeeded: () => 'hace falta aquí',
646
+ verdictRecoverable: () => 'sin efecto en estas entradas',
647
+ verdictUnknown: () => 'no concluyente',
648
+ recoverable: (tokens) =>
649
+ `${tokens} tokens están en ejemplos cuya retirada no cambió nada medible aquí.`,
650
+ caveat: () =>
651
+ 'Lo cual no es lo mismo que "bórralos". Un ejemplo puede existir para un caso que '
652
+ + 'estas entradas no contienen — la condición límite que alguien encontró en '
653
+ + 'producción y para la que añadió una demostración. Esto mide las entradas que le '
654
+ + 'diste, y solo tú sabes si cubren lo que importa. No se ha editado nada.',
655
+ },
656
+
657
+ eval: {
658
+ nothingToCompare: () =>
659
+ 'Las reglas no han cambiado nada en este prompt, así que no hay nada que comparar. Prueba con --level aggressive.',
660
+ starting: (cases, calls, model) =>
661
+ `Ejecutando ${cases} casos con ${model}: ${calls} llamadas (el original dos veces por caso, para medir su propia varianza, y el optimizado una).`,
662
+ heading: () => 'Coincidencia',
663
+ selfAgreement: (pct) => `${pct} del prompt original consigo mismo ${'\u2190'} la vara de medir`,
664
+ crossAgreement: (pct) => `${pct} del prompt optimizado con el original`,
665
+ verdict: (kind) =>
666
+ ({
667
+ indistinguishable: {
668
+ label: 'Indistinguible',
669
+ detail: 'Todas las respuestas coinciden. Sobre este conjunto, la optimización no ha cambiado nada.',
670
+ },
671
+ 'within-noise': {
672
+ label: 'Dentro del propio ruido del modelo',
673
+ detail:
674
+ 'El prompt optimizado discrepa del original más o menos tanto como el original discrepa de sí mismo, así que la diferencia no es atribuible a la reescritura. Amplía el conjunto antes de fiarte.',
675
+ },
676
+ diverges: {
677
+ label: 'Diverge',
678
+ detail:
679
+ 'El modelo es consistente consigo mismo y bastante menos con la reescritura, así que la optimización ha cambiado lo que pide el prompt. Lee los casos de abajo y el diff antes de subir esto.',
680
+ },
681
+ inconclusive: {
682
+ label: 'No concluyente',
683
+ detail:
684
+ 'El prompt original no coincide consigo mismo lo suficiente como para juzgar nada contra él. Baja la temperatura, o puede que la tarea sea demasiado abierta para esta prueba.',
685
+ },
686
+ })[kind],
687
+ mostChanged: () => 'Casos que más han cambiado',
688
+ caseAgreement: (cross, self) => `${cross} de coincidencia con el original (que consigo mismo dio ${self})`,
689
+ exportWarnings: (count) =>
690
+ `${count} ${count === 1 ? 'cosa' : 'cosas'} que saber antes de fiarte de la ejecución:`,
691
+ exportWrote: (path, cases, assertions) =>
692
+ `Escrito ${path}: dos prompts, ${cases} ${cases === 1 ? 'caso' : 'casos'}, ` +
693
+ `${assertions === 0 ? 'sin aserciones' : `${assertions} aserción${assertions === 1 ? '' : 'es'} (el prompt pide JSON)`}. ` +
694
+ `Añade las tuyas y ejecuta: npx promptfoo eval -c ${path}`,
695
+ callsMade: (count) => `${count} llamadas al proveedor.`,
696
+ },
697
+
698
+
699
+ diff: {
700
+ heading: (before, after) => `${before} → ${after}`,
701
+ measuringOptimised: () =>
702
+ 'Midiendo lo que dejarían las reglas, no lo que está escrito.',
703
+ monthly: (delta, calls, model) =>
704
+ `${delta}/mes con ${calls} llamadas y ${model}`,
705
+ advisoriesAppeared: () => 'Problemas nuevos',
706
+ advisoriesResolved: () => 'Resueltos',
707
+ rulesNewlyFiring: () => 'Reglas que ahora encuentran algo:',
708
+ rulesNoLongerFiring: () => 'Reglas que ya no encuentran nada:',
709
+ overLimit: (delta, max) =>
710
+ `Ha crecido ${delta} tokens, por encima del límite de ${max}.`,
711
+ someOverLimit: (count, max) =>
712
+ `${count} prompt${count === 1 ? '' : 's'} por encima del límite por prompt de ${max} tokens:`,
713
+ allSubheading: (prompts) => `${prompts} prompt${prompts === 1 ? '' : 's'} en ambos lados.`,
714
+ allTotal: (delta, prompts) =>
715
+ `${delta} tokens en ${prompts} prompt${prompts === 1 ? '' : 's'}`,
716
+ signConvention: () =>
717
+ 'Toda cifra es después menos antes, así que positivo significa peor — lo contrario que en el resto de Trazum.',
718
+ onlyBefore: () => 'solo antes ',
719
+ onlyAfter: () => 'solo después',
720
+ onlyOneSideNote: () =>
721
+ 'No entran en los totales. Un prompt que desaparece es una pregunta, no un ahorro.',
722
+ },
723
+
724
+ markdown: {
725
+ checkHeading: (target) => `Trazum — presupuestos de tokens en ${target}`,
726
+ baselineGrew: (delta, pct) => `Esta rama a\u00f1ade ${delta} tokens (${pct}) a los prompts de aqu\u00ed`,
727
+ baselineShrank: (delta, pct) => `Esta rama quita ${delta} tokens (${pct}) de los prompts de aqu\u00ed`,
728
+ baselineUnchanged: () => 'Sin cambios frente a la l\u00ednea base registrada',
729
+ baselineOverLimit: (limits) => `supera el l\u00edmite de ${limits}`,
730
+ baselineLimitTokens: (limit) => `${limit} tokens`,
731
+ baselineLimitPct: (limit) => `${limit}%`,
732
+ baselineColumnBefore: () => 'L\u00ednea base',
733
+ baselineColumnAfter: () => 'Ahora',
734
+ baselineMoney: (before, after, delta) => `Coste mensual **${before} \u2192 ${after}** (${delta})`,
735
+ baselineMoneyIncomparable: () =>
736
+ 'El escenario o el cat\u00e1logo de precios han cambiado desde que se registr\u00f3 la l\u00ednea base, as\u00ed que las dos cifras mensuales no son la misma medida y aqu\u00ed no se restan. La comparaci\u00f3n de tokens de arriba no se ve afectada.',
737
+ baselineReRecord: (command, path) =>
738
+ `Si este crecimiento es intencionado, vuelve a registrar con \`${command}\` y haz commit de \`${path}\`.`,
739
+ diffHeading: (before, after) => `Trazum — ${before} → ${after}`,
740
+ rankHeading: (root, count) =>
741
+ `Trazum — qué arreglar primero en ${root} (${count} ${count === 1 ? "prompt" : "prompts"})`,
742
+ blameHeading: (path) => `Trazum — historial de tokens de ${path}`,
743
+ rankLevel: (level) => `Medido al nivel de reglas \`${level}\`.`,
744
+ columnFile: () => 'Prompt',
745
+ columnTokens: () => 'Tokens',
746
+ columnBudget: () => 'Presupuesto',
747
+ columnMetric: () => 'Métrica',
748
+ columnChange: () => 'Cambio',
749
+ allWithin: (budgeted) =>
750
+ budgeted === 1
751
+ ? 'El prompt está dentro de presupuesto.'
752
+ : `Los ${budgeted} prompts con presupuesto están dentro de presupuesto.`,
753
+ overBudget: (failures, budgeted) => `${failures} de ${budgeted} por encima del presupuesto`,
754
+ noBudget: () => '—',
755
+ unbudgetedNote: (count) =>
756
+ count === 1
757
+ ? 'Hay 1 prompt que ningún patrón de presupuesto cubre, así que nadie lo está vigilando.'
758
+ : `Hay ${count} prompts que ningún patrón de presupuesto cubre, así que nadie los está vigilando.`,
759
+ whatWouldHelp: () => 'Qué ayudaría',
760
+ wouldFit: (level, optimizedTokens) =>
761
+ `optimizar con \`${level}\` lo dejaría en ~${optimizedTokens} tokens, y sí cabría`,
762
+ stillTooBig: (optimizedTokens) =>
763
+ `ni optimizado cabe (~${optimizedTokens} tokens): hay que recortar contenido a mano`,
764
+ truncated: () =>
765
+ 'Se ha parado antes de tiempo: el directorio supera el límite de recorrido, así que esto no es el cuadro completo.',
766
+ footer: (source, level) => `Recuento de tokens ${source} · nivel de reglas \`${level}\``,
767
+ pricingOverlaid: (count, lastReviewed) =>
768
+ `Los precios de ${count} ${count === 1 ? 'modelo' : 'modelos'} vienen de un overlay local revisado el ${lastReviewed}.`,
769
+ sourceEstimated: () => 'estimado, ±15%',
770
+ sourceExact: () => 'exacto',
771
+ measuringOptimised: () =>
772
+ 'Se mide lo que dejarían las reglas, no lo que está escrito en el fichero.',
773
+ metricTokens: (before, after) => `Tokens de entrada (${before} → ${after})`,
774
+ metricMonthly: (calls, model) => `Coste al mes con ${calls} llamadas y ${model}`,
775
+ deltaConvention: () =>
776
+ 'Toda cifra es un delta: después menos antes, así que <strong>positivo significa peor</strong>. ' +
777
+ 'Es lo contrario que en el resto de Trazum, donde toda cifra es un ahorro.',
778
+ advisoriesAppeared: () => 'Problemas que ha introducido esta edición',
779
+ advisoriesResolved: () => 'Problemas que ha resuelto esta edición',
780
+ rulesNewlyFiring: () => 'Reglas que ahora encuentran algo',
781
+ rulesNoLongerFiring: () => 'Reglas que ya no encuentran nada',
782
+ collapsedNote: () => 'nada por encima del presupuesto, despliega para ver las cifras',
783
+ trimNotice: () =>
784
+ '_Recortado para que quepa en un comentario. El informe completo está en el resumen de la ejecución._',
785
+ commentTitle: () => 'Trazum',
786
+ },
787
+
788
+ check: {
789
+ okLabel: () => 'OK',
790
+ embeddedHeading: (path, count) =>
791
+ `${path} — ${count} ${count === 1 ? 'prompt marcado' : 'prompts marcados'}`,
792
+ declinedHeading: (count) =>
793
+ `No se ${count === 1 ? 'ha podido leer 1 marcador' : `han podido leer ${count} marcadores`}:`,
794
+ declinedAt: (line, detail) => `línea ${line}: ${detail}`,
795
+ failedLabel: () => 'FALLO',
796
+ ok: (tokens, budget) => `${tokens} tokens dentro del presupuesto de ${budget}.`,
797
+ failed: (tokens, budget) => `${tokens} tokens supera el presupuesto de ${budget}.`,
798
+ wouldFit: (level, optimizedTokens) =>
799
+ ` Optimizado con "trazum optimize --level ${level}" quedaría en ~${optimizedTokens} tokens y sí cabría.`,
800
+ stillTooBig: (optimizedTokens) =>
801
+ ` Ni optimizado cabe (~${optimizedTokens} tokens): hay que recortar contenido a mano.`,
802
+ directoryHeading: (directory, files) =>
803
+ `${directory} — ${files} ${files === 1 ? 'prompt' : 'prompts'}`,
804
+ directorySummary: (failures, files) =>
805
+ failures === 0
806
+ ? `Los ${files} dentro de presupuesto.`
807
+ : `${failures} de ${files} por encima del presupuesto.`,
808
+ noBudget: () => '(sin presupuesto)',
809
+ walkTruncated: () =>
810
+ 'Se ha parado antes de tiempo: el directorio supera el límite de recorrido, así que esto no es el cuadro completo.',
811
+ exactCountsCost: (files) =>
812
+ `Contando ${files} ${files === 1 ? 'fichero' : 'ficheros'} con la API, una llamada por cada uno. Esto tarda un momento.`,
813
+ },
814
+ baseline: {
815
+ recorded: (path, files, tokens) =>
816
+ `Registrados ${files} prompts, ${tokens} tokens, en ${path}. Haz commit: la puerta compara el \u00e1rbol con lo que est\u00e9 commiteado.`,
817
+ recordedMoney: (monthly, model, calls) =>
818
+ `Son ${monthly} al mes con ${model} y ${calls} llamadas. Es informativo, no la puerta: los umbrales van en tokens, as\u00ed que cambiar el precio de un modelo nunca tumba una build por s\u00ed solo.`,
819
+ heading: () => 'Frente a la l\u00ednea base',
820
+ unchanged: (tokens) => `sin cambios, ${tokens} tokens`,
821
+ grew: (delta, pct, tokens) => `ha crecido ${delta} tokens (${pct}) hasta ${tokens}`,
822
+ shrank: (delta, pct, tokens) => `ha bajado ${delta} tokens (${pct}) hasta ${tokens}`,
823
+ entry: (path, before, after, delta) => `${path} ${before} \u2192 ${after} (${delta})`,
824
+ addedHeading: (count) => `Nuevos desde la l\u00ednea base (${count})`,
825
+ removedHeading: (count) => `Desaparecidos desde la l\u00ednea base (${count})`,
826
+ grownHeading: (count) => `Han crecido (${count})`,
827
+ breachTokens: (actual, limit) =>
828
+ `un crecimiento de ${actual} tokens supera el l\u00edmite de ${limit}`,
829
+ breachPct: (actual, limit) => `un crecimiento de ${actual} supera el l\u00edmite de ${limit}`,
830
+ reRecord: (path) =>
831
+ `Si el crecimiento es intencionado, vuelve a registrar con "trazum baseline" y haz commit de ${path}.`,
832
+ money: (before, after, delta) => `Coste mensual ${before} \u2192 ${after} (${delta})`,
833
+ moneyIncomparableScenario: () =>
834
+ 'El escenario de uso ha cambiado desde que se registr\u00f3 la l\u00ednea base, as\u00ed que las dos cifras mensuales no son la misma medida. La comparaci\u00f3n de tokens no se ve afectada.',
835
+ moneyIncomparablePricing: (was, now) =>
836
+ `Los precios se revisaron el ${was} cuando se registr\u00f3 la l\u00ednea base y el ${now} ahora, as\u00ed que las cifras mensuales no son la misma medida. La comparaci\u00f3n de tokens no se ve afectada.`,
837
+ },
838
+ };