agy-insights 0.3.0 → 0.7.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/README.es.md +172 -0
- package/README.md +95 -77
- package/agy-insights.example.json +2 -2
- package/agy-insights.js +51 -28
- package/docs/how-it-works.md +102 -0
- package/lib/findings.js +45 -61
- package/lib/fixes.js +10 -9
- package/lib/history.js +84 -18
- package/lib/i18n.js +357 -0
- package/lib/map.js +8 -7
- package/lib/report.js +224 -59
- package/package.json +8 -4
- package/docs/como-funciona.md +0 -88
package/lib/i18n.js
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
// User-facing text in English and Spanish. The language comes from --lang, the "lang" config key, or the
|
|
2
|
+
// system locale (Spanish if it starts with "es", else English). Modules read strings through `t`, which
|
|
3
|
+
// always points at the current language, so setLang() must run before any report is built.
|
|
4
|
+
|
|
5
|
+
const plural = (n, one, many) => `${n} ${n === 1 ? one : many}`;
|
|
6
|
+
|
|
7
|
+
const en = {
|
|
8
|
+
locale: 'en-US',
|
|
9
|
+
help: (dir) => `Usage: agy-insights [options] (or npx agy-insights, or node agy-insights.js)
|
|
10
|
+
|
|
11
|
+
Analyzes your Antigravity CLI history and opens a report in the browser.
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--project <folder> Only conversations that touched that project ("." = current folder)
|
|
15
|
+
--text Summary in the terminal instead of the HTML report
|
|
16
|
+
--paths With --text, show paths and the proposed exclusions
|
|
17
|
+
--map Print a project map (--project, or the current folder) for AGENTS.md
|
|
18
|
+
--out <file> Where to save the HTML report (default: the temp folder)
|
|
19
|
+
--no-open Don't open the browser
|
|
20
|
+
--since <YYYY-MM-DD> Only conversations active since that date
|
|
21
|
+
--days <n> Only conversations active in the last n days
|
|
22
|
+
--lang <en|es> Language of the output (default: the system's)
|
|
23
|
+
--dir <folder> Another history folder (default ${dir})
|
|
24
|
+
--config <file> Configuration (default ~/.agy-insights.json if it exists)
|
|
25
|
+
--help This help
|
|
26
|
+
`,
|
|
27
|
+
missingValue: (opt) => `Missing value for ${opt}`,
|
|
28
|
+
unknownOption: (opt) => `Unknown option: ${opt}. See --help.`,
|
|
29
|
+
badLang: (v) => `Unsupported language: ${v}. Use en or es.`,
|
|
30
|
+
badDate: (v) => `Invalid date: ${v}. Use YYYY-MM-DD.`,
|
|
31
|
+
badDays: (v) => `Invalid number of days: ${v}.`,
|
|
32
|
+
configNotFound: (f) => `Configuration file not found: ${f}`,
|
|
33
|
+
cannotRead: (f, msg) => `Cannot read ${f}: ${msg}`,
|
|
34
|
+
mapDone: (tokens, file) => `\n~${tokens} tokens on every request. Paste it into ${file} if you like it; agy-insights does not write it.`,
|
|
35
|
+
reportName: 'report',
|
|
36
|
+
done: (convs, found, out) => `${plural(convs, 'conversation', 'conversations')} analyzed, ${plural(found, 'finding', 'findings')}.\nReport: ${out}`,
|
|
37
|
+
|
|
38
|
+
kinds: {
|
|
39
|
+
build: 'build output (dist, bin, obj…)', logs: 'logs, maps, minified', generated: 'generated code / migrations',
|
|
40
|
+
other: 'other', code: (ext) => `code ${ext}`,
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
fixes: {
|
|
44
|
+
denyTitle: 'Deny reading it (Antigravity permission)',
|
|
45
|
+
denyWhere: 'inside "permissions" → "deny"',
|
|
46
|
+
denyNote: 'This is the only thing that actually blocks the read. It applies to all your projects, also blocks writing to that path, and agy must be restarted.',
|
|
47
|
+
ignoreTitle: 'Add to .antigravityignore',
|
|
48
|
+
ignoreWhere: 'at the end of the file (create it if missing)',
|
|
49
|
+
ignoreNote: 'Hides it from @ completion; it is not documented to stop the agent from reading it.',
|
|
50
|
+
ruleTitle: 'Add a rule to AGENTS.md',
|
|
51
|
+
ruleWhere: 'at the end of the file',
|
|
52
|
+
ruleNote: 'It is an instruction to the model, not a block. It is sent with every request: keep it short. AGENTS.md is usually in the team repo.',
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
findings: {
|
|
56
|
+
formatTitle: 'The history format is not recognized',
|
|
57
|
+
formatWhy: (steps) => `There are ${steps} steps but no recognizable tool call. agy has probably changed how it stores conversations.`,
|
|
58
|
+
formatSuggestion: 'The results below are not reliable. Update agy-insights (npx agy-insights@latest) or open an issue on GitHub.',
|
|
59
|
+
rereadsTitle: 'The agent re-reads what it already had',
|
|
60
|
+
rereadsWhy: (n) => `${plural(n, 'read repeats', 'reads repeat')} a chunk already read in the same session. That content enters the context twice.`,
|
|
61
|
+
rereadsSuggestion: 'It usually happens in long sessions or after editing. Shorter sessions, one per task, or /compact reduce it. Do not exclude these files: the agent needs them.',
|
|
62
|
+
rereads: (n) => plural(n, 're-read', 're-reads'),
|
|
63
|
+
reads: (n) => plural(n, 'read', 'reads'),
|
|
64
|
+
ruleFolder: (rel, kind) => `Don't read \`${rel}/\`: it is ${kind}; work with the source code.`,
|
|
65
|
+
kindPattern: (project, kind) => `${project} › everything of type "${kind}"`,
|
|
66
|
+
kindPatternDetail: 'pattern for any folder in the project',
|
|
67
|
+
ruleKind: (kind, patterns) => `Don't read ${kind} (${patterns}) unless the task requires it.`,
|
|
68
|
+
nonSourceTitle: 'Generated, build or dependency files are being read',
|
|
69
|
+
nonSourceWhy: 'Build output, generated code, dependencies, lockfiles and logs are rarely needed to understand or change the code, and they tend to be large.',
|
|
70
|
+
nonSourceSuggestion: 'Check whether any of these reads was necessary. If not, you can block them with an Antigravity permission.',
|
|
71
|
+
crossTitle: 'The same files enter many sessions',
|
|
72
|
+
crossWhy: (n, min) => `${plural(n, 'file was', 'files were')} read in ${min} or more different conversations.`,
|
|
73
|
+
crossSuggestion: 'If they are the core of the project, that is normal. If they are large and the agent only needs part of them, telling it which part to look at saves reads.',
|
|
74
|
+
sessions: (n) => plural(n, 'session', 'sessions'),
|
|
75
|
+
heavyTitle: 'Commands with very long output',
|
|
76
|
+
heavyWhy: 'The full output of each command enters the context and is resent on the following turns.',
|
|
77
|
+
heavySuggestion: "Ask it to run only what is needed (one test, one project) or to filter the output. If it always happens, a rule in the project's AGENTS.md reminds it every session.",
|
|
78
|
+
heavyDetail: (calls, avg) => `${plural(calls, 'time', 'times')} · ~${avg} tokens each`,
|
|
79
|
+
heavyExample: (cmd) => `- When running \`${cmd}\`, limit the output: only the affected test or project, or filter the relevant lines.`,
|
|
80
|
+
longTitle: 'Very long sessions',
|
|
81
|
+
longWhy: (n, min) => `${plural(n, 'conversation exceeds', 'conversations exceed')} ${min} tokens. In a long session, everything accumulated is resent on every turn.`,
|
|
82
|
+
longSuggestion: 'A new conversation per task, or /compact when you change topic.',
|
|
83
|
+
conversation: (id) => `conversation ${id}`,
|
|
84
|
+
steps: (n) => plural(n, 'step', 'steps'),
|
|
85
|
+
longUsage: (calls, real, cached) => ` · ${plural(calls, 'model call', 'model calls')}, ${real} real tokens (${cached} with cache)`,
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
map: {
|
|
89
|
+
title: '## Project map',
|
|
90
|
+
intro: '(Generated by agy-insights. To find something, start from the matching folder and search with grep.)',
|
|
91
|
+
files: (n) => (n === 1 ? 'file' : 'files'),
|
|
92
|
+
large: 'Large files: search with grep and read only the range you need, not the whole file.',
|
|
93
|
+
lines: (n) => `~${n} lines`,
|
|
94
|
+
dontRead: (list) => `Don't read (generated or dependencies): ${list}`,
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
report: {
|
|
98
|
+
applied: 'already applied',
|
|
99
|
+
in: 'In',
|
|
100
|
+
copy: 'Copy',
|
|
101
|
+
copied: 'Copied',
|
|
102
|
+
howToExclude: 'How to exclude it',
|
|
103
|
+
exampleRule: 'Example rule for AGENTS.md',
|
|
104
|
+
exampleHint: "Add it to the project's AGENTS.md only if this happens often: rules are sent with every request.",
|
|
105
|
+
cols: ['What', '~tokens', '%', 'Times', 'Sessions'],
|
|
106
|
+
modelsTitle: 'Models and effort',
|
|
107
|
+
modelsHint: 'Model and effort level (reasoning) of each model call, as agy records them. Higher effort usually means more tokens per call.',
|
|
108
|
+
modelCols: ['Model', 'Effort', 'Calls', 'Tokens', '%', 'Per call', 'With cache', 'Sessions'],
|
|
109
|
+
unknownModel: 'unknown',
|
|
110
|
+
modelsText: 'Models and effort:',
|
|
111
|
+
modelLine: (model, effort, calls, tokens, pct, perCall) => `${model} · ${effort} ${plural(calls, 'call', 'calls')}, ${tokens} tokens (${pct}), ~${perCall} per call`,
|
|
112
|
+
sessionModel: 'Model',
|
|
113
|
+
otherModels: (n) => ` +${n} more`,
|
|
114
|
+
efforts: { minimal: 'Minimal', low: 'Low', medium: 'Medium', high: 'High', xhigh: 'Extra high', 'extra high': 'Extra high', max: 'Max' },
|
|
115
|
+
sessionsTitle: 'Sessions (real usage)',
|
|
116
|
+
sessionCols: ['Conversation', 'Model', 'Tokens', '%', 'With cache', 'Calls', 'Errors'],
|
|
117
|
+
noConversations: (scope, dir) => `No conversations${scope ? ` for <code>${scope}</code>` : ''} in <code>${dir}</code>.`,
|
|
118
|
+
tileReal: 'real tokens (input + output)',
|
|
119
|
+
tileCache: (calls) => `including cache · ${plural(calls, 'call', 'calls')}`,
|
|
120
|
+
tileContext: '~tokens in context',
|
|
121
|
+
tileTop5: 'in the 5 largest sessions',
|
|
122
|
+
tileReads: 'file reads',
|
|
123
|
+
tileRereads: 're-reads of the same chunk',
|
|
124
|
+
happening: "What's happening",
|
|
125
|
+
happeningHint: 'This page changes nothing. Each proposal tells you which file it goes in and what to paste; you decide.',
|
|
126
|
+
nothing: 'No repeated or unnecessary context detected.',
|
|
127
|
+
details: 'Details',
|
|
128
|
+
byProject: 'By project', outsideGit: 'outside a git repository',
|
|
129
|
+
byKind: 'By file type', byFolder: 'By folder', files: 'Files', tools: 'Tools', commands: 'Commands',
|
|
130
|
+
title: 'Context usage in Antigravity',
|
|
131
|
+
project: 'Project', wholeHistory: 'Whole history',
|
|
132
|
+
convs: (n) => plural(n, 'conversation', 'conversations'),
|
|
133
|
+
generated: (date) => `generated ${date}`,
|
|
134
|
+
filterLabel: 'Period',
|
|
135
|
+
rangeAll: 'All history',
|
|
136
|
+
rangeDays: (n) => `Last ${n} days`,
|
|
137
|
+
chartTitleDay: 'Token usage per day',
|
|
138
|
+
chartTitleWeek: 'Token usage per week',
|
|
139
|
+
chartHint: 'Real tokens (input + output) of the model calls. Hover a column for details.',
|
|
140
|
+
chartTable: 'View as table',
|
|
141
|
+
chartCols: ['Day', 'Tokens', 'With cache', 'Calls'],
|
|
142
|
+
chartColsWeek: ['Week of', 'Tokens', 'With cache', 'Calls'],
|
|
143
|
+
chartCalls: (n) => plural(n, 'call', 'calls'),
|
|
144
|
+
weekOf: (d) => `week of ${d}`,
|
|
145
|
+
withCacheShort: 'with cache',
|
|
146
|
+
period: (from, to) => (from === to ? `on ${from}` : `from ${from} to ${to}`),
|
|
147
|
+
since: (date) => `active since ${date}`,
|
|
148
|
+
olderSkipped: (n) => `${plural(n, 'older conversation', 'older conversations')} left out by the date filter`,
|
|
149
|
+
periodLine: (period, since) => `Period: ${period}${since ? ` (filter: ${since})` : ''}`,
|
|
150
|
+
footerReal: (missing) => `Real tokens: the ones Antigravity records in each conversation's log (subagents included)${missing ? `; ${plural(missing, 'conversation has', 'conversations have')} no log and ${missing === 1 ? 'is' : 'are'} not counted` : ''}.`,
|
|
151
|
+
footerCache: 'It is unknown whether the quota counts cache the same as new input, so both figures are shown.',
|
|
152
|
+
footerEstimate: '~tokens = stored size of each step / 4: useful to compare files and folders with each other.',
|
|
153
|
+
footerPrivacy: 'The history was read from a copy and was not modified. This page contains paths from your projects: review it before sharing.',
|
|
154
|
+
// Text summary
|
|
155
|
+
history: 'History', projectLower: 'project',
|
|
156
|
+
summary: (convs, steps, tokens) => `${plural(convs, 'conversation', 'conversations')}, ${plural(steps, 'step', 'steps')}, ~${tokens} tokens in context (estimated)`,
|
|
157
|
+
realUsage: (real, input, output, cached, calls) => `Real usage: ${real} tokens (input ${input} + output ${output}), ${cached} including cache, in ${plural(calls, 'model call', 'model calls')}`,
|
|
158
|
+
noLog: (n) => ` (${plural(n, 'conversation', 'conversations')} without a log)`,
|
|
159
|
+
topSessions: (n) => (n === 1 ? ' The session that uses the most:' : ` The ${n} sessions that use the most:`),
|
|
160
|
+
sessionLine: (id, calls, errors, cached, model) => `conversation ${id}${model ? ` (${model})` : ''} ${plural(calls, 'call', 'calls')}${errors ? `, ${plural(errors, 'error', 'errors')}` : ''}, ${cached} with cache`,
|
|
161
|
+
top5: (pct, reads, rereads, rpct) => `The 5 largest sessions account for ${pct}. Reads: ${reads}, re-reads ${rereads} (${rpct})`,
|
|
162
|
+
pathHidden: '(path hidden, use --paths)',
|
|
163
|
+
byKindText: 'By file type:',
|
|
164
|
+
textFooter: 'Nothing was modified. Real usage = agy log; ~tokens = step size / 4 (estimated).',
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const es = {
|
|
169
|
+
locale: 'es-ES',
|
|
170
|
+
help: (dir) => `Uso: agy-insights [opciones] (o npx agy-insights, o node agy-insights.js)
|
|
171
|
+
|
|
172
|
+
Analiza tu historial de Antigravity CLI y abre un informe en el navegador.
|
|
173
|
+
|
|
174
|
+
Opciones:
|
|
175
|
+
--project <carpeta> Solo conversaciones que tocaron ese proyecto ("." = carpeta actual)
|
|
176
|
+
--text Resumen en la terminal en vez del informe HTML
|
|
177
|
+
--paths Con --text, muestra rutas y las exclusiones propuestas
|
|
178
|
+
--map Imprime un mapa del proyecto (--project, o la carpeta actual) para AGENTS.md
|
|
179
|
+
--out <fichero> Dónde guardar el informe HTML (por defecto, en la carpeta temporal)
|
|
180
|
+
--no-open No abrir el navegador
|
|
181
|
+
--since <AAAA-MM-DD> Solo conversaciones con actividad desde esa fecha
|
|
182
|
+
--days <n> Solo conversaciones con actividad en los últimos n días
|
|
183
|
+
--lang <en|es> Idioma de la salida (por defecto, el del sistema)
|
|
184
|
+
--dir <carpeta> Otra carpeta de historial (por defecto ${dir})
|
|
185
|
+
--config <fichero> Configuración (por defecto ~/.agy-insights.json si existe)
|
|
186
|
+
--help Esta ayuda
|
|
187
|
+
`,
|
|
188
|
+
missingValue: (opt) => `Falta el valor de ${opt}`,
|
|
189
|
+
unknownOption: (opt) => `Opción desconocida: ${opt}. Usa --help.`,
|
|
190
|
+
badLang: (v) => `Idioma no soportado: ${v}. Usa en o es.`,
|
|
191
|
+
badDate: (v) => `Fecha no válida: ${v}. Usa AAAA-MM-DD.`,
|
|
192
|
+
badDays: (v) => `Número de días no válido: ${v}.`,
|
|
193
|
+
configNotFound: (f) => `No existe el fichero de configuración ${f}`,
|
|
194
|
+
cannotRead: (f, msg) => `No se puede leer ${f}: ${msg}`,
|
|
195
|
+
mapDone: (tokens, file) => `\n~${tokens} tokens en cada petición. Pégalo en ${file} si te convence; agy-insights no lo escribe.`,
|
|
196
|
+
reportName: 'informe',
|
|
197
|
+
done: (convs, found, out) => `${plural(convs, 'conversación analizada', 'conversaciones analizadas')}, ${plural(found, 'hallazgo', 'hallazgos')}.\nInforme: ${out}`,
|
|
198
|
+
|
|
199
|
+
kinds: {
|
|
200
|
+
build: 'build output (dist, bin, obj…)', logs: 'logs, maps, minificados', generated: 'código generado / migraciones',
|
|
201
|
+
other: 'otros', code: (ext) => `código ${ext}`,
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
fixes: {
|
|
205
|
+
denyTitle: 'Denegar la lectura (permiso de Antigravity)',
|
|
206
|
+
denyWhere: 'dentro de "permissions" → "deny"',
|
|
207
|
+
denyNote: 'Es lo único que impide la lectura. Afecta a todos tus proyectos, también impide escribir en esa ruta, y hay que reiniciar agy.',
|
|
208
|
+
ignoreTitle: 'Añadir a .antigravityignore',
|
|
209
|
+
ignoreWhere: 'al final del fichero (créalo si no existe)',
|
|
210
|
+
ignoreNote: 'Lo oculta del autocompletado con @; no está documentado que impida leerlo.',
|
|
211
|
+
ruleTitle: 'Añadir una regla a AGENTS.md',
|
|
212
|
+
ruleWhere: 'al final del fichero',
|
|
213
|
+
ruleNote: 'Es una indicación al modelo, no un bloqueo. Se envía en cada petición: mantenla corta. AGENTS.md suele estar en el repo del equipo.',
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
findings: {
|
|
217
|
+
formatTitle: 'No se reconoce el formato del historial',
|
|
218
|
+
formatWhy: (steps) => `Hay ${steps} pasos pero ninguna llamada a herramienta reconocible. Probablemente agy ha cambiado cómo guarda las conversaciones.`,
|
|
219
|
+
formatSuggestion: 'Los resultados de abajo no son fiables. Actualiza agy-insights (npx agy-insights@latest) o abre una incidencia en GitHub.',
|
|
220
|
+
rereadsTitle: 'El agente vuelve a leer lo que ya tenía',
|
|
221
|
+
rereadsWhy: (n) => `${plural(n, 'lectura repite', 'lecturas repiten')} un trozo que ya se había leído en la misma sesión. Ese contenido entra dos veces en el contexto.`,
|
|
222
|
+
rereadsSuggestion: 'Suele pasar en sesiones largas o después de editar. Sesiones más cortas por tarea, o /compact, lo reducen. No conviene excluir estos ficheros: el agente los necesita.',
|
|
223
|
+
rereads: (n) => plural(n, 'relectura', 'relecturas'),
|
|
224
|
+
reads: (n) => plural(n, 'lectura', 'lecturas'),
|
|
225
|
+
ruleFolder: (rel, kind) => `No leas \`${rel}/\`: es ${kind}; trabaja con el código fuente.`,
|
|
226
|
+
kindPattern: (project, kind) => `${project} › todo lo de tipo «${kind}»`,
|
|
227
|
+
kindPatternDetail: 'patrón para cualquier carpeta del proyecto',
|
|
228
|
+
ruleKind: (kind, patterns) => `No leas ${kind} (${patterns}) salvo que la tarea lo pida.`,
|
|
229
|
+
nonSourceTitle: 'Se leen ficheros generados, compilados o de dependencias',
|
|
230
|
+
nonSourceWhy: 'Salidas de build, código generado, dependencias, lockfiles y logs casi nunca hacen falta para entender o cambiar el código, y suelen ser grandes.',
|
|
231
|
+
nonSourceSuggestion: 'Revisa si alguna de estas lecturas era necesaria. Si no, puedes impedirlas con un permiso de Antigravity.',
|
|
232
|
+
crossTitle: 'Los mismos ficheros entran en muchas sesiones',
|
|
233
|
+
crossWhy: (n, min) => `${plural(n, 'fichero se ha leído', 'ficheros se han leído')} en ${min} o más conversaciones distintas.`,
|
|
234
|
+
crossSuggestion: 'Si son el núcleo del proyecto es normal. Si son grandes y el agente solo necesita una parte, decirle qué trozo mirar ahorra lecturas.',
|
|
235
|
+
sessions: (n) => plural(n, 'sesión', 'sesiones'),
|
|
236
|
+
heavyTitle: 'Comandos con salidas muy largas',
|
|
237
|
+
heavyWhy: 'La salida completa de cada comando entra en el contexto y se reenvía en los turnos siguientes.',
|
|
238
|
+
heavySuggestion: 'Pide ejecutar solo lo necesario (un test concreto, un proyecto concreto) o filtrar la salida. Si pasa siempre, una regla en el AGENTS.md del proyecto lo recuerda en cada sesión.',
|
|
239
|
+
heavyDetail: (calls, avg) => `${plural(calls, 'vez', 'veces')} · ~${avg} tokens cada una`,
|
|
240
|
+
heavyExample: (cmd) => `- Al ejecutar \`${cmd}\`, limita la salida: solo el test o proyecto afectado, o filtra las líneas relevantes.`,
|
|
241
|
+
longTitle: 'Sesiones muy largas',
|
|
242
|
+
longWhy: (n, min) => `${plural(n, 'conversación supera', 'conversaciones superan')} ${min} tokens. En una sesión larga, todo lo acumulado se vuelve a enviar en cada turno.`,
|
|
243
|
+
longSuggestion: 'Una conversación nueva por tarea, o /compact cuando cambias de tema.',
|
|
244
|
+
conversation: (id) => `conversación ${id}`,
|
|
245
|
+
steps: (n) => plural(n, 'paso', 'pasos'),
|
|
246
|
+
longUsage: (calls, real, cached) => ` · ${plural(calls, 'llamada', 'llamadas')} al modelo, ${real} tokens reales (${cached} con caché)`,
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
map: {
|
|
250
|
+
title: '## Mapa del proyecto',
|
|
251
|
+
intro: '(Generado por agy-insights. Para encontrar algo, empieza por la carpeta que corresponda y busca con grep.)',
|
|
252
|
+
files: (n) => (n === 1 ? 'fichero' : 'ficheros'),
|
|
253
|
+
large: 'Ficheros grandes: busca con grep y lee solo el rango que necesites, no el fichero entero.',
|
|
254
|
+
lines: (n) => `~${n} líneas`,
|
|
255
|
+
dontRead: (list) => `No leas (generado o dependencias): ${list}`,
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
report: {
|
|
259
|
+
applied: 'ya aplicado',
|
|
260
|
+
in: 'En',
|
|
261
|
+
copy: 'Copiar',
|
|
262
|
+
copied: 'Copiado',
|
|
263
|
+
howToExclude: 'Cómo excluirlo',
|
|
264
|
+
exampleRule: 'Regla de ejemplo para AGENTS.md',
|
|
265
|
+
exampleHint: 'Ponla en el AGENTS.md del proyecto solo si te pasa a menudo: las reglas se envían en cada petición.',
|
|
266
|
+
cols: ['Qué', '~tokens', '%', 'Veces', 'Sesiones'],
|
|
267
|
+
modelsTitle: 'Modelos y esfuerzo',
|
|
268
|
+
modelsHint: 'Modelo y nivel de esfuerzo (razonamiento) de cada llamada al modelo, tal como los anota agy. Más esfuerzo suele suponer más tokens por llamada.',
|
|
269
|
+
modelCols: ['Modelo', 'Esfuerzo', 'Llamadas', 'Tokens', '%', 'Por llamada', 'Con caché', 'Sesiones'],
|
|
270
|
+
unknownModel: 'desconocido',
|
|
271
|
+
modelsText: 'Modelos y esfuerzo:',
|
|
272
|
+
modelLine: (model, effort, calls, tokens, pct, perCall) => `${model} · ${effort} ${plural(calls, 'llamada', 'llamadas')}, ${tokens} tokens (${pct}), ~${perCall} por llamada`,
|
|
273
|
+
sessionModel: 'Modelo',
|
|
274
|
+
otherModels: (n) => ` +${n} más`,
|
|
275
|
+
efforts: { minimal: 'Mínimo', low: 'Bajo', medium: 'Medio', high: 'Alto', xhigh: 'Muy alto', 'extra high': 'Muy alto', max: 'Máximo' },
|
|
276
|
+
sessionsTitle: 'Sesiones (consumo real)',
|
|
277
|
+
sessionCols: ['Conversación', 'Modelo', 'Tokens', '%', 'Con caché', 'Llamadas', 'Errores'],
|
|
278
|
+
noConversations: (scope, dir) => `No hay conversaciones${scope ? ` de <code>${scope}</code>` : ''} en <code>${dir}</code>.`,
|
|
279
|
+
tileReal: 'tokens reales (entrada + salida)',
|
|
280
|
+
tileCache: (calls) => `contando la caché · ${plural(calls, 'llamada', 'llamadas')}`,
|
|
281
|
+
tileContext: '~tokens en contexto',
|
|
282
|
+
tileTop5: 'en las 5 sesiones más grandes',
|
|
283
|
+
tileReads: 'lecturas de ficheros',
|
|
284
|
+
tileRereads: 'relecturas del mismo trozo',
|
|
285
|
+
happening: 'Qué está pasando',
|
|
286
|
+
happeningHint: 'Esta página no cambia nada. Cada propuesta te dice en qué fichero va y qué pegar; decides tú.',
|
|
287
|
+
nothing: 'No se ha detectado contexto repetido ni innecesario.',
|
|
288
|
+
details: 'Detalle',
|
|
289
|
+
byProject: 'Por proyecto', outsideGit: 'fuera de un repositorio git',
|
|
290
|
+
byKind: 'Por tipo de fichero', byFolder: 'Por carpeta', files: 'Ficheros', tools: 'Herramientas', commands: 'Comandos',
|
|
291
|
+
title: 'Uso de contexto en Antigravity',
|
|
292
|
+
project: 'Proyecto', wholeHistory: 'Todo el historial',
|
|
293
|
+
convs: (n) => plural(n, 'conversación', 'conversaciones'),
|
|
294
|
+
generated: (date) => `generado ${date}`,
|
|
295
|
+
filterLabel: 'Periodo',
|
|
296
|
+
rangeAll: 'Todo el historial',
|
|
297
|
+
rangeDays: (n) => `Últimos ${n} días`,
|
|
298
|
+
chartTitleDay: 'Consumo de tokens por día',
|
|
299
|
+
chartTitleWeek: 'Consumo de tokens por semana',
|
|
300
|
+
chartHint: 'Tokens reales (entrada + salida) de las llamadas al modelo. Pasa el ratón por una columna para ver el detalle.',
|
|
301
|
+
chartTable: 'Ver como tabla',
|
|
302
|
+
chartCols: ['Día', 'Tokens', 'Con caché', 'Llamadas'],
|
|
303
|
+
chartColsWeek: ['Semana del', 'Tokens', 'Con caché', 'Llamadas'],
|
|
304
|
+
chartCalls: (n) => plural(n, 'llamada', 'llamadas'),
|
|
305
|
+
weekOf: (d) => `semana del ${d}`,
|
|
306
|
+
withCacheShort: 'con caché',
|
|
307
|
+
period: (from, to) => (from === to ? `el ${from}` : `del ${from} al ${to}`),
|
|
308
|
+
since: (date) => `con actividad desde el ${date}`,
|
|
309
|
+
olderSkipped: (n) => `${plural(n, 'conversación anterior queda fuera', 'conversaciones anteriores quedan fuera')} por el filtro de fechas`,
|
|
310
|
+
periodLine: (period, since) => `Periodo: ${period}${since ? ` (filtro: ${since})` : ''}`,
|
|
311
|
+
footerReal: (missing) => `Tokens reales: los que Antigravity anota en el registro de cada conversación (incluidos subagentes)${missing ? `; ${missing === 1 ? '1 conversación no tiene registro y no cuenta' : `${missing} conversaciones no tienen registro y no cuentan`}` : ''}.`,
|
|
312
|
+
footerCache: 'No se sabe si la cuota cuenta la caché igual que la entrada nueva: por eso se muestran las dos cifras.',
|
|
313
|
+
footerEstimate: '~tokens = tamaño guardado de cada paso / 4: sirve para comparar ficheros y carpetas entre sí.',
|
|
314
|
+
footerPrivacy: 'El historial se ha leído desde una copia y no se ha modificado. Esta página contiene rutas de tus proyectos: no la compartas sin revisarla.',
|
|
315
|
+
history: 'Historial', projectLower: 'proyecto',
|
|
316
|
+
summary: (convs, steps, tokens) => `${plural(convs, 'conversación', 'conversaciones')}, ${plural(steps, 'paso', 'pasos')}, ~${tokens} tokens en contexto (estimado)`,
|
|
317
|
+
realUsage: (real, input, output, cached, calls) => `Consumo real: ${real} tokens (entrada ${input} + salida ${output}), ${cached} contando la caché, en ${plural(calls, 'llamada', 'llamadas')} al modelo`,
|
|
318
|
+
noLog: (n) => ` (${plural(n, 'conversación', 'conversaciones')} sin registro)`,
|
|
319
|
+
topSessions: (n) => (n === 1 ? ' La sesión que más gasta:' : ` Las ${n} sesiones que más gastan:`),
|
|
320
|
+
sessionLine: (id, calls, errors, cached, model) => `conversación ${id}${model ? ` (${model})` : ''} ${plural(calls, 'llamada', 'llamadas')}${errors ? `, ${plural(errors, 'error', 'errores')}` : ''}, ${cached} con caché`,
|
|
321
|
+
top5: (pct, reads, rereads, rpct) => `Las 5 sesiones más grandes suman el ${pct}. Lecturas: ${reads}, relecturas ${rereads} (${rpct})`,
|
|
322
|
+
pathHidden: '(ruta oculta, usa --paths)',
|
|
323
|
+
byKindText: 'Por tipo de fichero:',
|
|
324
|
+
textFooter: 'No se ha modificado nada. Consumo real = registro de agy; ~tokens = tamaño del paso / 4 (estimado).',
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const LANGS = { en, es };
|
|
329
|
+
let current = 'en';
|
|
330
|
+
|
|
331
|
+
// "es", "es-ES", "es_ES.UTF-8"… → es; anything else → en.
|
|
332
|
+
function detect(env = process.env) {
|
|
333
|
+
const sys = env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE || Intl.DateTimeFormat().resolvedOptions().locale || '';
|
|
334
|
+
return /^es\b|^es[-_]/i.test(sys) ? 'es' : 'en';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function setLang(lang) {
|
|
338
|
+
if (!LANGS[lang]) return false;
|
|
339
|
+
current = lang;
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const t = new Proxy({}, { get: (_, k) => LANGS[current][k] });
|
|
344
|
+
const lang = () => current;
|
|
345
|
+
const num = (n) => Math.round(n).toLocaleString(t.locale);
|
|
346
|
+
const date = (ms) => new Date(ms).toLocaleDateString(t.locale, { day: 'numeric', month: 'short', year: 'numeric' });
|
|
347
|
+
|
|
348
|
+
// Built-in kinds are stable ids (see history.js kind()); custom kinds from the config are shown as written.
|
|
349
|
+
function kindLabel(k) {
|
|
350
|
+
if (k.startsWith('code ')) return t.kinds.code(k.slice(5));
|
|
351
|
+
return t.kinds[k] || k;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// "High" / "high" → "High" or "Alto"; levels agy may add later are shown as written.
|
|
355
|
+
const effortLabel = (e) => (e ? t.report.efforts[e.toLowerCase()] || e : t.report.unknownModel);
|
|
356
|
+
|
|
357
|
+
module.exports = { t, setLang, lang, detect, num, date, kindLabel, effortLabel, LANGS };
|
package/lib/map.js
CHANGED
|
@@ -5,9 +5,10 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
const { kind, NON_SOURCE } = require('./history');
|
|
8
|
+
const { t, num } = require('./i18n');
|
|
8
9
|
|
|
9
|
-
const START = '<!-- agy-insights:
|
|
10
|
-
const END = '<!-- agy-insights:
|
|
10
|
+
const START = '<!-- agy-insights:map:start -->';
|
|
11
|
+
const END = '<!-- agy-insights:map:end -->';
|
|
11
12
|
const DEFAULTS = { largeLines: 500, maxLarge: 10, maxFolders: 15, maxNames: 6 };
|
|
12
13
|
// Never walked when git is not available; with git, ignored files are skipped anyway.
|
|
13
14
|
const SKIP_DIRS = new Set(['.git', 'node_modules', '.vs', '.vscode', '.idea', '.gemini', '.agents']);
|
|
@@ -83,18 +84,18 @@ function buildMap(root, opts = {}) {
|
|
|
83
84
|
.sort((a, b) => b.reads - a.reads || b.lines - a.lines)
|
|
84
85
|
.slice(0, o.maxLarge);
|
|
85
86
|
|
|
86
|
-
const lines = [
|
|
87
|
+
const lines = [t.map.title, '', t.map.intro, ''];
|
|
87
88
|
for (const { folder, files, subdirs } of mainFolders(source, o.maxFolders)) {
|
|
88
89
|
const names = subdirs.length ? `: ${subdirs.slice(0, o.maxNames).join(', ')}${subdirs.length > o.maxNames ? '…' : ''}` : '';
|
|
89
|
-
lines.push(`- ${folder}${names} (${files} ${files
|
|
90
|
+
lines.push(`- ${folder}${names} (${files} ${t.map.files(files)})`);
|
|
90
91
|
}
|
|
91
92
|
if (large.length) {
|
|
92
|
-
lines.push('',
|
|
93
|
-
for (const x of large) lines.push(`- ${x.file} (
|
|
93
|
+
lines.push('', t.map.large);
|
|
94
|
+
for (const x of large) lines.push(`- ${x.file} (${t.map.lines(num(x.lines))})`);
|
|
94
95
|
}
|
|
95
96
|
if (generated.size) {
|
|
96
97
|
const top = [...generated].filter((g) => ![...generated].some((h) => h !== g && g.startsWith(h))).sort();
|
|
97
|
-
lines.push('',
|
|
98
|
+
lines.push('', t.map.dontRead(top.join(', ')));
|
|
98
99
|
}
|
|
99
100
|
const text = lines.join('\n');
|
|
100
101
|
return { text, block: `${START}\n${text}\n${END}`, tokens: Math.round(text.length / BYTES_PER_TOKEN), files: all.length, large };
|