@justmpm/ai-tool 0.9.3 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-J3MX6NK3.js → chunk-4HAUIZQ7.js} +733 -372
- package/dist/{chunk-5RMB6OEA.js → chunk-LJZCMVX5.js} +49 -15
- package/dist/cli.js +3 -3
- package/dist/index.d.ts +79 -23
- package/dist/index.js +7 -3
- package/dist/{server-TDSMX4PG.js → server-K4Z4PEO3.js} +146 -93
- package/package.json +1 -1
|
@@ -143,9 +143,263 @@ function isCodeFile(filePath) {
|
|
|
143
143
|
return CODE_EXTENSIONS.some((ext) => filePath.endsWith(ext));
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// src/utils/hints.ts
|
|
147
|
+
var COMMAND_MAP = {
|
|
148
|
+
map: {
|
|
149
|
+
cli: "ai-tool map",
|
|
150
|
+
mcp: "analyze__aitool_project_map"
|
|
151
|
+
},
|
|
152
|
+
dead: {
|
|
153
|
+
cli: "ai-tool dead",
|
|
154
|
+
mcp: "analyze__aitool_dead_code"
|
|
155
|
+
},
|
|
156
|
+
impact: {
|
|
157
|
+
cli: "ai-tool impact <arquivo>",
|
|
158
|
+
mcp: "analyze__aitool_impact_analysis { target: '<arquivo>' }"
|
|
159
|
+
},
|
|
160
|
+
suggest: {
|
|
161
|
+
cli: "ai-tool suggest <arquivo>",
|
|
162
|
+
mcp: "analyze__aitool_suggest_reads { target: '<arquivo>' }"
|
|
163
|
+
},
|
|
164
|
+
context: {
|
|
165
|
+
cli: "ai-tool context <arquivo>",
|
|
166
|
+
mcp: "analyze__aitool_file_context { target: '<arquivo>' }"
|
|
167
|
+
},
|
|
168
|
+
area_context: {
|
|
169
|
+
cli: "ai-tool context --area=<nome>",
|
|
170
|
+
mcp: "analyze__aitool_area_context { area: '<nome>' }"
|
|
171
|
+
},
|
|
172
|
+
areas: {
|
|
173
|
+
cli: "ai-tool areas",
|
|
174
|
+
mcp: "analyze__aitool_list_areas"
|
|
175
|
+
},
|
|
176
|
+
area: {
|
|
177
|
+
cli: "ai-tool area <nome>",
|
|
178
|
+
mcp: "analyze__aitool_area_detail { target: '<nome>' }"
|
|
179
|
+
},
|
|
180
|
+
areas_init: {
|
|
181
|
+
cli: "ai-tool areas init",
|
|
182
|
+
mcp: "analyze__aitool_areas_init"
|
|
183
|
+
},
|
|
184
|
+
find: {
|
|
185
|
+
cli: "ai-tool find <termo>",
|
|
186
|
+
mcp: "analyze__aitool_find { query: '<termo>' }"
|
|
187
|
+
},
|
|
188
|
+
describe: {
|
|
189
|
+
cli: "ai-tool describe <termo>",
|
|
190
|
+
mcp: "analyze__aitool_describe { query: '<termo>' }"
|
|
191
|
+
},
|
|
192
|
+
functions: {
|
|
193
|
+
cli: "ai-tool functions",
|
|
194
|
+
mcp: "analyze__aitool_list_functions"
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
function hint(command, ctx, params) {
|
|
198
|
+
const mapping = COMMAND_MAP[command];
|
|
199
|
+
if (!mapping) return command;
|
|
200
|
+
let instruction = mapping[ctx];
|
|
201
|
+
if (params) {
|
|
202
|
+
for (const [placeholder, value] of Object.entries(params)) {
|
|
203
|
+
instruction = instruction.replaceAll(placeholder, value);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return instruction;
|
|
207
|
+
}
|
|
208
|
+
var NEXT_STEPS = {
|
|
209
|
+
map: [
|
|
210
|
+
{ command: "area", description: "ver arquivos de uma area" },
|
|
211
|
+
{ command: "suggest", description: "o que ler antes de editar" },
|
|
212
|
+
{ command: "context", description: "ver API de um arquivo" }
|
|
213
|
+
],
|
|
214
|
+
impact: [
|
|
215
|
+
{ command: "suggest", description: "o que ler antes de editar este arquivo" },
|
|
216
|
+
{ command: "context", description: "ver assinaturas dos arquivos upstream" },
|
|
217
|
+
{ command: "find", description: "localizar usos de exports especificos" }
|
|
218
|
+
],
|
|
219
|
+
suggest: [
|
|
220
|
+
{ command: "context", description: "ver assinaturas de cada arquivo sugerido" },
|
|
221
|
+
{ command: "impact", description: "ver impacto completo da mudanca" }
|
|
222
|
+
],
|
|
223
|
+
context: [
|
|
224
|
+
{ command: "impact", description: "ver quem sera afetado por mudancas" },
|
|
225
|
+
{ command: "find", description: "localizar usos de uma funcao ou tipo" },
|
|
226
|
+
{ command: "suggest", description: "o que ler antes de editar" }
|
|
227
|
+
],
|
|
228
|
+
area_context: [
|
|
229
|
+
{ command: "find", description: "buscar usos de simbolos desta area" },
|
|
230
|
+
{ command: "area", description: "ver lista de arquivos desta area" },
|
|
231
|
+
{ command: "impact", description: "ver impacto de modificar um arquivo" }
|
|
232
|
+
],
|
|
233
|
+
find: [
|
|
234
|
+
{ command: "context", description: "ver assinaturas completas do arquivo" },
|
|
235
|
+
{ command: "impact", description: "ver impacto de modificar o arquivo" },
|
|
236
|
+
{ command: "describe", description: "buscar areas por descricao" }
|
|
237
|
+
],
|
|
238
|
+
describe: [
|
|
239
|
+
{ command: "area", description: "ver detalhes de uma area" },
|
|
240
|
+
{ command: "area_context", description: "contexto completo de uma area" }
|
|
241
|
+
],
|
|
242
|
+
areas: [
|
|
243
|
+
{ command: "area", description: "ver arquivos de uma area especifica" },
|
|
244
|
+
{ command: "describe", description: "buscar areas por descricao" }
|
|
245
|
+
],
|
|
246
|
+
area: [
|
|
247
|
+
{ command: "area_context", description: "contexto consolidado (tipos, hooks, funcoes)" },
|
|
248
|
+
{ command: "context", description: "ver assinaturas de um arquivo especifico" },
|
|
249
|
+
{ command: "find", description: "buscar simbolos dentro da area" }
|
|
250
|
+
],
|
|
251
|
+
dead: [
|
|
252
|
+
{ command: "impact", description: "verificar se um arquivo morto e realmente nao usado" },
|
|
253
|
+
{ command: "map", description: "ver estrutura atualizada do projeto" }
|
|
254
|
+
],
|
|
255
|
+
functions: [
|
|
256
|
+
{ command: "find", description: "buscar uma Cloud Function especifica" },
|
|
257
|
+
{ command: "impact", description: "ver impacto de modificar uma function" },
|
|
258
|
+
{ command: "context", description: "ver assinaturas de uma function" }
|
|
259
|
+
]
|
|
260
|
+
};
|
|
261
|
+
function nextSteps(command, ctx) {
|
|
262
|
+
const steps = NEXT_STEPS[command];
|
|
263
|
+
if (!steps || steps.length === 0) return "";
|
|
264
|
+
let out = "\n\u{1F4D6} Proximos passos:\n";
|
|
265
|
+
for (const step of steps) {
|
|
266
|
+
const instruction = hint(step.command, ctx);
|
|
267
|
+
out += ` \u2192 ${instruction} - ${step.description}
|
|
268
|
+
`;
|
|
269
|
+
}
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
272
|
+
function recoveryHint(errorType, ctx, _extra) {
|
|
273
|
+
switch (errorType) {
|
|
274
|
+
case "file_not_found":
|
|
275
|
+
return `
|
|
276
|
+
\u{1F4A1} Dicas:
|
|
277
|
+
\u2192 ${hint("map", ctx)} - ver arquivos disponiveis
|
|
278
|
+
\u2192 ${hint("find", ctx)} - buscar por nome de simbolo
|
|
279
|
+
`;
|
|
280
|
+
case "area_not_found":
|
|
281
|
+
return `
|
|
282
|
+
\u{1F4A1} Dicas:
|
|
283
|
+
\u2192 ${hint("areas", ctx)} - listar areas disponiveis
|
|
284
|
+
\u2192 ${hint("describe", ctx)} - buscar areas por descricao
|
|
285
|
+
\u2192 ${hint("areas_init", ctx)} - gerar configuracao de areas
|
|
286
|
+
`;
|
|
287
|
+
case "no_results":
|
|
288
|
+
return `
|
|
289
|
+
\u{1F4A1} Dicas:
|
|
290
|
+
\u2192 ${hint("find", ctx)} - buscar com outro termo
|
|
291
|
+
\u2192 ${hint("describe", ctx)} - buscar areas por descricao
|
|
292
|
+
\u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
293
|
+
`;
|
|
294
|
+
case "no_firebase":
|
|
295
|
+
return `
|
|
296
|
+
\u{1F4A1} Este projeto nao usa Firebase. Comandos disponiveis:
|
|
297
|
+
\u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
298
|
+
\u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
299
|
+
\u2192 ${hint("areas", ctx)} - listar areas funcionais
|
|
300
|
+
`;
|
|
301
|
+
case "no_areas_configured":
|
|
302
|
+
return `
|
|
303
|
+
\u{1F4A1} Nenhuma area configurada neste projeto.
|
|
304
|
+
\u2192 ${hint("areas_init", ctx)} - gerar arquivo de configuracao
|
|
305
|
+
\u2192 Depois edite .analyze/areas.config.json com as areas do projeto
|
|
306
|
+
\u2192 ${hint("map", ctx)} - ver estrutura do projeto sem areas
|
|
307
|
+
`;
|
|
308
|
+
case "symbol_not_found":
|
|
309
|
+
return `
|
|
310
|
+
\u{1F4A1} Dicas:
|
|
311
|
+
\u2192 Verifique a ortografia do simbolo
|
|
312
|
+
\u2192 Tente buscar parte do nome
|
|
313
|
+
\u2192 ${hint("find", ctx)} - buscar com outro termo
|
|
314
|
+
\u2192 ${hint("describe", ctx)} - buscar areas por descricao
|
|
315
|
+
\u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
316
|
+
`;
|
|
317
|
+
case "index_failed":
|
|
318
|
+
return `
|
|
319
|
+
\u{1F4A1} Falha ao indexar o projeto:
|
|
320
|
+
\u2192 Verifique se tsconfig.json existe e esta valido
|
|
321
|
+
\u2192 Verifique se o projeto tem arquivos .ts ou .tsx
|
|
322
|
+
\u2192 ${hint("map", ctx)} - tente ver a estrutura basica primeiro
|
|
323
|
+
`;
|
|
324
|
+
case "generic":
|
|
325
|
+
return `
|
|
326
|
+
\u{1F4A1} Tente:
|
|
327
|
+
\u2192 ${hint("map", ctx)} - verificar estrutura do projeto
|
|
328
|
+
\u2192 Verifique se o caminho (cwd) esta correto
|
|
329
|
+
`;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/utils/similarity.ts
|
|
334
|
+
function levenshteinDistance(a, b) {
|
|
335
|
+
const matrix = [];
|
|
336
|
+
for (let i = 0; i <= b.length; i++) {
|
|
337
|
+
matrix[i] = [i];
|
|
338
|
+
}
|
|
339
|
+
for (let j = 0; j <= a.length; j++) {
|
|
340
|
+
matrix[0][j] = j;
|
|
341
|
+
}
|
|
342
|
+
for (let i = 1; i <= b.length; i++) {
|
|
343
|
+
for (let j = 1; j <= a.length; j++) {
|
|
344
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
345
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
346
|
+
} else {
|
|
347
|
+
matrix[i][j] = Math.min(
|
|
348
|
+
matrix[i - 1][j - 1] + 1,
|
|
349
|
+
// substituição
|
|
350
|
+
matrix[i][j - 1] + 1,
|
|
351
|
+
// inserção
|
|
352
|
+
matrix[i - 1][j] + 1
|
|
353
|
+
// deleção
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return matrix[b.length][a.length];
|
|
359
|
+
}
|
|
360
|
+
function findSimilar(target, candidates, options = {}) {
|
|
361
|
+
const {
|
|
362
|
+
maxDistance = 3,
|
|
363
|
+
limit = 5,
|
|
364
|
+
normalize: normalize2 = true,
|
|
365
|
+
extractKey = (item) => String(item)
|
|
366
|
+
} = options;
|
|
367
|
+
const normalizedTarget = normalize2 ? target.toLowerCase() : target;
|
|
368
|
+
const scored = candidates.map((item) => {
|
|
369
|
+
const key = extractKey(item);
|
|
370
|
+
const normalizedKey = normalize2 ? key.toLowerCase() : key;
|
|
371
|
+
const keyNoExt = normalizedKey.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
372
|
+
let score = Infinity;
|
|
373
|
+
if (normalizedKey.includes(normalizedTarget) || keyNoExt.includes(normalizedTarget)) {
|
|
374
|
+
score = 0;
|
|
375
|
+
} else {
|
|
376
|
+
score = levenshteinDistance(keyNoExt, normalizedTarget);
|
|
377
|
+
}
|
|
378
|
+
return { item, score };
|
|
379
|
+
}).filter(({ score }) => score <= maxDistance).sort((a, b) => a.score - b.score).slice(0, limit);
|
|
380
|
+
return scored.map(({ item }) => item);
|
|
381
|
+
}
|
|
382
|
+
function findBestMatch(target, candidates, extractKey = (item) => String(item)) {
|
|
383
|
+
const similar = findSimilar(target, candidates, {
|
|
384
|
+
maxDistance: 2,
|
|
385
|
+
// Mais restritivo para "você quis dizer"
|
|
386
|
+
limit: 1,
|
|
387
|
+
extractKey
|
|
388
|
+
});
|
|
389
|
+
return similar.length > 0 ? similar[0] : null;
|
|
390
|
+
}
|
|
391
|
+
function extractFileName(filePath) {
|
|
392
|
+
const fileName = filePath.split("/").pop() || filePath;
|
|
393
|
+
return fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
394
|
+
}
|
|
395
|
+
|
|
146
396
|
// src/formatters/text.ts
|
|
147
|
-
function formatMapSummary(result, areasInfo) {
|
|
397
|
+
function formatMapSummary(result, areasInfo, ctx = "cli") {
|
|
148
398
|
let out = "";
|
|
399
|
+
if (result.framework) {
|
|
400
|
+
out += `\u{1F3D7}\uFE0F Framework: ${result.framework}
|
|
401
|
+
`;
|
|
402
|
+
}
|
|
149
403
|
out += `\u{1F4CA} ${result.summary.totalFiles} arquivos | ${result.summary.totalFolders} pastas
|
|
150
404
|
`;
|
|
151
405
|
const catOrder = [
|
|
@@ -172,9 +426,27 @@ function formatMapSummary(result, areasInfo) {
|
|
|
172
426
|
}
|
|
173
427
|
out += ` ${catParts.join(", ")}
|
|
174
428
|
`;
|
|
429
|
+
const SMALL_PROJECT_THRESHOLD = 25;
|
|
430
|
+
if (result.summary.totalFiles <= SMALL_PROJECT_THRESHOLD && result.files.length > 0) {
|
|
431
|
+
out += `
|
|
432
|
+
\u{1F4C1} Arquivos:
|
|
433
|
+
`;
|
|
434
|
+
for (const file of result.files) {
|
|
435
|
+
const icon = categoryIcons[file.category];
|
|
436
|
+
out += ` ${icon} ${file.path}
|
|
437
|
+
`;
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
const topFolders = result.folders.sort((a, b) => b.fileCount - a.fileCount).slice(0, 5);
|
|
441
|
+
if (topFolders.length > 0) {
|
|
442
|
+
const folderParts = topFolders.map((f) => `${f.path}/ (${f.fileCount})`);
|
|
443
|
+
out += `\u{1F4C1} ${folderParts.join(", ")}
|
|
444
|
+
`;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
175
447
|
if (areasInfo && areasInfo.total > 0) {
|
|
176
448
|
out += `
|
|
177
|
-
\u{1F5C2}\uFE0F
|
|
449
|
+
\u{1F5C2}\uFE0F Areas: ${areasInfo.names.join(", ")}
|
|
178
450
|
`;
|
|
179
451
|
}
|
|
180
452
|
out += `
|
|
@@ -185,43 +457,30 @@ function formatMapSummary(result, areasInfo) {
|
|
|
185
457
|
`;
|
|
186
458
|
out += ` ${cloudFunctionCount} function(s) em functions/src/
|
|
187
459
|
`;
|
|
188
|
-
out += ` \u2192
|
|
460
|
+
out += ` \u2192 ${hint("functions", ctx)}
|
|
189
461
|
|
|
190
462
|
`;
|
|
191
463
|
}
|
|
192
464
|
if (result.circularDependencies.length > 0) {
|
|
193
|
-
out += `\u26A0\uFE0F ${result.circularDependencies.length}
|
|
465
|
+
out += `\u26A0\uFE0F ${result.circularDependencies.length} dependencia(s) circular(es) detectada(s)
|
|
194
466
|
`;
|
|
195
|
-
out += ` \u2192
|
|
467
|
+
out += ` \u2192 ${hint("impact", ctx)} para investigar
|
|
196
468
|
|
|
197
469
|
`;
|
|
198
470
|
}
|
|
199
471
|
if (areasInfo && areasInfo.unmappedCount > 0) {
|
|
200
|
-
out += `\u26A0\uFE0F ${areasInfo.unmappedCount} arquivo(s) sem
|
|
472
|
+
out += `\u26A0\uFE0F ${areasInfo.unmappedCount} arquivo(s) sem area definida
|
|
201
473
|
`;
|
|
202
|
-
out += ` \u2192
|
|
474
|
+
out += ` \u2192 ${hint("areas_init", ctx)} para configurar
|
|
203
475
|
|
|
204
476
|
`;
|
|
205
477
|
}
|
|
206
|
-
out +=
|
|
207
|
-
`;
|
|
208
|
-
out += ` \u2192 area <nome> - ver arquivos de uma \xE1rea
|
|
209
|
-
`;
|
|
210
|
-
out += ` \u2192 suggest <arquivo> - o que ler antes de editar
|
|
211
|
-
`;
|
|
212
|
-
out += ` \u2192 context <arquivo> - ver API de um arquivo
|
|
213
|
-
`;
|
|
478
|
+
out += nextSteps("map", ctx);
|
|
214
479
|
return out;
|
|
215
480
|
}
|
|
216
|
-
function formatMapText(result) {
|
|
481
|
+
function formatMapText(result, ctx = "cli") {
|
|
217
482
|
let out = "";
|
|
218
|
-
out +=
|
|
219
|
-
`;
|
|
220
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
221
|
-
`;
|
|
222
|
-
out += `\u2551 \u{1F4C1} PROJECT MAP \u2551
|
|
223
|
-
`;
|
|
224
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
483
|
+
out += `## \u{1F4C1} PROJECT MAP
|
|
225
484
|
|
|
226
485
|
`;
|
|
227
486
|
out += `\u{1F4CA} RESUMO
|
|
@@ -230,7 +489,7 @@ function formatMapText(result) {
|
|
|
230
489
|
`;
|
|
231
490
|
out += ` Pastas: ${result.summary.totalFolders}
|
|
232
491
|
`;
|
|
233
|
-
out += `
|
|
492
|
+
out += ` Diretorio: ${result.cwd}
|
|
234
493
|
|
|
235
494
|
`;
|
|
236
495
|
out += `\u{1F4C2} CATEGORIAS
|
|
@@ -272,7 +531,7 @@ function formatMapText(result) {
|
|
|
272
531
|
}
|
|
273
532
|
if (result.circularDependencies.length > 0) {
|
|
274
533
|
out += `
|
|
275
|
-
\u26A0\uFE0F
|
|
534
|
+
\u26A0\uFE0F DEPENDENCIAS CIRCULARES (${result.circularDependencies.length})
|
|
276
535
|
`;
|
|
277
536
|
for (const cycle of result.circularDependencies.slice(0, 5)) {
|
|
278
537
|
out += ` ${cycle.join(" \u2192 ")}
|
|
@@ -283,41 +542,37 @@ function formatMapText(result) {
|
|
|
283
542
|
`;
|
|
284
543
|
}
|
|
285
544
|
}
|
|
545
|
+
out += nextSteps("map", ctx);
|
|
286
546
|
return out;
|
|
287
547
|
}
|
|
288
|
-
function formatDeadText(result) {
|
|
548
|
+
function formatDeadText(result, ctx = "cli") {
|
|
289
549
|
let out = "";
|
|
290
|
-
out +=
|
|
291
|
-
`;
|
|
292
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
293
|
-
`;
|
|
294
|
-
out += `\u2551 \u{1F480} DEAD CODE \u2551
|
|
295
|
-
`;
|
|
296
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
550
|
+
out += `## \u{1F480} DEAD CODE
|
|
297
551
|
|
|
298
552
|
`;
|
|
299
553
|
if (result.summary.totalDead === 0) {
|
|
300
|
-
out += `\u2705 Nenhum
|
|
554
|
+
out += `\u2705 Nenhum codigo morto encontrado!
|
|
301
555
|
`;
|
|
302
|
-
out += ` Todos os arquivos e exports
|
|
556
|
+
out += ` Todos os arquivos e exports estao sendo utilizados.
|
|
303
557
|
`;
|
|
558
|
+
out += nextSteps("dead", ctx);
|
|
304
559
|
return out;
|
|
305
560
|
}
|
|
306
561
|
out += `\u{1F4CA} RESUMO
|
|
307
562
|
`;
|
|
308
|
-
out += ` Total: ${result.summary.totalDead} itens
|
|
563
|
+
out += ` Total: ${result.summary.totalDead} itens nao utilizados
|
|
309
564
|
`;
|
|
310
|
-
out += ` Arquivos
|
|
565
|
+
out += ` Arquivos orfaos: ${result.summary.byType.files}
|
|
311
566
|
`;
|
|
312
|
-
out += ` Exports
|
|
567
|
+
out += ` Exports nao usados: ${result.summary.byType.exports}
|
|
313
568
|
`;
|
|
314
|
-
out += `
|
|
569
|
+
out += ` Dependencias nao usadas: ${result.summary.byType.dependencies}
|
|
315
570
|
|
|
316
571
|
`;
|
|
317
572
|
if (result.files.length > 0) {
|
|
318
|
-
out += `\u{1F5D1}\uFE0F ARQUIVOS
|
|
573
|
+
out += `\u{1F5D1}\uFE0F ARQUIVOS ORFAOS (${result.files.length})
|
|
319
574
|
`;
|
|
320
|
-
out += ` Arquivos que
|
|
575
|
+
out += ` Arquivos que ninguem importa:
|
|
321
576
|
|
|
322
577
|
`;
|
|
323
578
|
const byCategory = /* @__PURE__ */ new Map();
|
|
@@ -344,7 +599,7 @@ function formatDeadText(result) {
|
|
|
344
599
|
}
|
|
345
600
|
}
|
|
346
601
|
if (result.exports.length > 0) {
|
|
347
|
-
out += `\u{1F4E4} EXPORTS
|
|
602
|
+
out += `\u{1F4E4} EXPORTS NAO USADOS (${result.exports.length})
|
|
348
603
|
`;
|
|
349
604
|
for (const exp of result.exports.slice(0, 10)) {
|
|
350
605
|
out += ` ${exp.file}: ${exp.export}
|
|
@@ -358,7 +613,7 @@ function formatDeadText(result) {
|
|
|
358
613
|
`;
|
|
359
614
|
}
|
|
360
615
|
if (result.dependencies.length > 0) {
|
|
361
|
-
out += `\u{1F4E6}
|
|
616
|
+
out += `\u{1F4E6} DEPENDENCIAS NAO USADAS (${result.dependencies.length})
|
|
362
617
|
`;
|
|
363
618
|
for (const dep of result.dependencies) {
|
|
364
619
|
out += ` ${dep}
|
|
@@ -394,12 +649,12 @@ function formatDeadText(result) {
|
|
|
394
649
|
`;
|
|
395
650
|
out += ` 3. Ver detalhes em JSON:
|
|
396
651
|
`;
|
|
397
|
-
out += `
|
|
652
|
+
out += ` ${hint("dead", ctx)} --format=json
|
|
398
653
|
`;
|
|
399
654
|
const suggestions = generateIgnoreSuggestions(result);
|
|
400
655
|
if (suggestions.length > 0) {
|
|
401
656
|
out += `
|
|
402
|
-
\u{1F3AF}
|
|
657
|
+
\u{1F3AF} SUGESTOES INTELIGENTES
|
|
403
658
|
|
|
404
659
|
`;
|
|
405
660
|
for (const suggestion of suggestions) {
|
|
@@ -412,6 +667,7 @@ function formatDeadText(result) {
|
|
|
412
667
|
`;
|
|
413
668
|
}
|
|
414
669
|
}
|
|
670
|
+
out += nextSteps("dead", ctx);
|
|
415
671
|
return out;
|
|
416
672
|
}
|
|
417
673
|
function generateIgnoreSuggestions(result) {
|
|
@@ -431,7 +687,7 @@ function generateIgnoreSuggestions(result) {
|
|
|
431
687
|
suggestions.push({
|
|
432
688
|
icon: "\u{1F9EA}",
|
|
433
689
|
pattern: "**/*.(test|spec).(ts|tsx|js|jsx)",
|
|
434
|
-
reason: "Arquivos de teste geralmente
|
|
690
|
+
reason: "Arquivos de teste geralmente sao entry points proprios",
|
|
435
691
|
count: testFiles.length
|
|
436
692
|
});
|
|
437
693
|
}
|
|
@@ -442,7 +698,7 @@ function generateIgnoreSuggestions(result) {
|
|
|
442
698
|
suggestions.push({
|
|
443
699
|
icon: "\u2699\uFE0F",
|
|
444
700
|
pattern: "**/*.config.(ts|js|mjs|cjs)",
|
|
445
|
-
reason: "Arquivos de
|
|
701
|
+
reason: "Arquivos de configuracao sao entry points",
|
|
446
702
|
count: configFiles.length
|
|
447
703
|
});
|
|
448
704
|
}
|
|
@@ -451,7 +707,7 @@ function generateIgnoreSuggestions(result) {
|
|
|
451
707
|
suggestions.push({
|
|
452
708
|
icon: "\u{1F4D8}",
|
|
453
709
|
pattern: "**/*.d.ts",
|
|
454
|
-
reason: "Arquivos de
|
|
710
|
+
reason: "Arquivos de definicao TypeScript",
|
|
455
711
|
count: dtsFiles.length
|
|
456
712
|
});
|
|
457
713
|
}
|
|
@@ -460,21 +716,15 @@ function generateIgnoreSuggestions(result) {
|
|
|
460
716
|
suggestions.push({
|
|
461
717
|
icon: "\u{1F4DC}",
|
|
462
718
|
pattern: "scripts/**",
|
|
463
|
-
reason: "Scripts de
|
|
719
|
+
reason: "Scripts de automacao sao entry points",
|
|
464
720
|
count: scriptFiles.length
|
|
465
721
|
});
|
|
466
722
|
}
|
|
467
723
|
return suggestions;
|
|
468
724
|
}
|
|
469
|
-
function formatImpactText(result) {
|
|
725
|
+
function formatImpactText(result, ctx = "cli") {
|
|
470
726
|
let out = "";
|
|
471
|
-
out +=
|
|
472
|
-
`;
|
|
473
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
474
|
-
`;
|
|
475
|
-
out += `\u2551 \u{1F3AF} IMPACT ANALYSIS \u2551
|
|
476
|
-
`;
|
|
477
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
727
|
+
out += `## \u{1F3AF} IMPACT ANALYSIS
|
|
478
728
|
|
|
479
729
|
`;
|
|
480
730
|
const icon = categoryIcons[result.category];
|
|
@@ -483,10 +733,7 @@ function formatImpactText(result) {
|
|
|
483
733
|
out += ` ${icon} ${result.category}
|
|
484
734
|
|
|
485
735
|
`;
|
|
486
|
-
out += `\
|
|
487
|
-
|
|
488
|
-
`;
|
|
489
|
-
out += `\u2B06\uFE0F USADO POR (${result.upstream.total} arquivo${result.upstream.total !== 1 ? "s" : ""} \xFAnico${result.upstream.total !== 1 ? "s" : ""})
|
|
736
|
+
out += `\u2B06\uFE0F USADO POR (${result.upstream.total} arquivo${result.upstream.total !== 1 ? "s" : ""} unico${result.upstream.total !== 1 ? "s" : ""})
|
|
490
737
|
`;
|
|
491
738
|
if (result.upstream.direct.length > 0 || result.upstream.indirect.length > 0) {
|
|
492
739
|
out += ` \u{1F4CD} ${result.upstream.direct.length} direto${result.upstream.direct.length !== 1 ? "s" : ""} + ${result.upstream.indirect.length} indireto${result.upstream.indirect.length !== 1 ? "s" : ""}
|
|
@@ -496,7 +743,7 @@ function formatImpactText(result) {
|
|
|
496
743
|
|
|
497
744
|
`;
|
|
498
745
|
if (result.upstream.total === 0) {
|
|
499
|
-
out += `
|
|
746
|
+
out += ` Ninguem importa este arquivo diretamente.
|
|
500
747
|
`;
|
|
501
748
|
} else {
|
|
502
749
|
for (const file of result.upstream.direct.slice(0, 10)) {
|
|
@@ -519,10 +766,8 @@ function formatImpactText(result) {
|
|
|
519
766
|
}
|
|
520
767
|
}
|
|
521
768
|
out += `
|
|
522
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
523
|
-
|
|
524
769
|
`;
|
|
525
|
-
out += `\u2B07\uFE0F
|
|
770
|
+
out += `\u2B07\uFE0F DEPENDENCIAS (${result.downstream.total} arquivo${result.downstream.total !== 1 ? "s" : ""} unico${result.downstream.total !== 1 ? "s" : ""})
|
|
526
771
|
`;
|
|
527
772
|
if (result.downstream.direct.length > 0 || result.downstream.indirect.length > 0) {
|
|
528
773
|
out += ` \u{1F4CD} ${result.downstream.direct.length} direto${result.downstream.direct.length !== 1 ? "s" : ""} + ${result.downstream.indirect.length} indireto${result.downstream.indirect.length !== 1 ? "s" : ""}
|
|
@@ -532,7 +777,7 @@ function formatImpactText(result) {
|
|
|
532
777
|
|
|
533
778
|
`;
|
|
534
779
|
if (result.downstream.total === 0) {
|
|
535
|
-
out += ` Este arquivo
|
|
780
|
+
out += ` Este arquivo nao importa nenhum arquivo local.
|
|
536
781
|
`;
|
|
537
782
|
} else {
|
|
538
783
|
for (const file of result.downstream.direct.slice(0, 10)) {
|
|
@@ -546,15 +791,13 @@ function formatImpactText(result) {
|
|
|
546
791
|
}
|
|
547
792
|
}
|
|
548
793
|
out += `
|
|
549
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
550
|
-
|
|
551
794
|
`;
|
|
552
|
-
out += `\u{1F4CA}
|
|
795
|
+
out += `\u{1F4CA} METRICAS DE IMPACTO
|
|
553
796
|
|
|
554
797
|
`;
|
|
555
|
-
out += ` Arquivos que importam este (upstream): ${result.upstream.total}
|
|
798
|
+
out += ` Arquivos que importam este (upstream): ${result.upstream.total} unico${result.upstream.total !== 1 ? "s" : ""}
|
|
556
799
|
`;
|
|
557
|
-
out += ` Arquivos que este importa (downstream): ${result.downstream.total}
|
|
800
|
+
out += ` Arquivos que este importa (downstream): ${result.downstream.total} unico${result.downstream.total !== 1 ? "s" : ""}
|
|
558
801
|
`;
|
|
559
802
|
if (result.risks.length > 0) {
|
|
560
803
|
out += `
|
|
@@ -569,7 +812,7 @@ function formatImpactText(result) {
|
|
|
569
812
|
}
|
|
570
813
|
if (result.suggestions.length > 0) {
|
|
571
814
|
out += `
|
|
572
|
-
\u{1F4A1}
|
|
815
|
+
\u{1F4A1} SUGESTOES
|
|
573
816
|
|
|
574
817
|
`;
|
|
575
818
|
for (const suggestion of result.suggestions) {
|
|
@@ -579,11 +822,11 @@ function formatImpactText(result) {
|
|
|
579
822
|
}
|
|
580
823
|
if (result.gitHistory && result.gitHistory.hasGitRepo) {
|
|
581
824
|
out += `
|
|
582
|
-
\u{1F4DC}
|
|
825
|
+
\u{1F4DC} HISTORICO GIT (ultimos ${result.gitHistory.recentCommits.length} commits)
|
|
583
826
|
|
|
584
827
|
`;
|
|
585
828
|
if (result.gitHistory.recentCommits.length === 0) {
|
|
586
|
-
out += ` Arquivo
|
|
829
|
+
out += ` Arquivo nao esta no repositorio Git ou sem historico.
|
|
587
830
|
`;
|
|
588
831
|
} else {
|
|
589
832
|
for (const commit of result.gitHistory.recentCommits) {
|
|
@@ -599,17 +842,12 @@ function formatImpactText(result) {
|
|
|
599
842
|
}
|
|
600
843
|
}
|
|
601
844
|
}
|
|
845
|
+
out += nextSteps("impact", ctx);
|
|
602
846
|
return out;
|
|
603
847
|
}
|
|
604
|
-
function formatSuggestText(result) {
|
|
848
|
+
function formatSuggestText(result, ctx = "cli") {
|
|
605
849
|
let out = "";
|
|
606
|
-
out +=
|
|
607
|
-
`;
|
|
608
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
609
|
-
`;
|
|
610
|
-
out += `\u2551 \u{1F4DA} SUGGEST \u2551
|
|
611
|
-
`;
|
|
612
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
850
|
+
out += `## \u{1F4DA} SUGGEST
|
|
613
851
|
|
|
614
852
|
`;
|
|
615
853
|
const icon = categoryIcons[result.category];
|
|
@@ -623,11 +861,9 @@ function formatSuggestText(result) {
|
|
|
623
861
|
`;
|
|
624
862
|
out += ` Este arquivo nao tem dependencias ou arquivos relacionados.
|
|
625
863
|
`;
|
|
864
|
+
out += nextSteps("suggest", ctx);
|
|
626
865
|
return out;
|
|
627
866
|
}
|
|
628
|
-
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
629
|
-
|
|
630
|
-
`;
|
|
631
867
|
const byPriority = {
|
|
632
868
|
critical: result.suggestions.filter((s) => s.priority === "critical"),
|
|
633
869
|
high: result.suggestions.filter((s) => s.priority === "high"),
|
|
@@ -698,9 +934,6 @@ function formatSuggestText(result) {
|
|
|
698
934
|
out += `
|
|
699
935
|
`;
|
|
700
936
|
}
|
|
701
|
-
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
702
|
-
|
|
703
|
-
`;
|
|
704
937
|
out += `\u{1F4CA} RESUMO
|
|
705
938
|
`;
|
|
706
939
|
out += ` Total de arquivos sugeridos: ${result.suggestions.length}
|
|
@@ -723,10 +956,7 @@ function formatSuggestText(result) {
|
|
|
723
956
|
}
|
|
724
957
|
if (result.testSuggestions.length > 0) {
|
|
725
958
|
out += `
|
|
726
|
-
\
|
|
727
|
-
|
|
728
|
-
`;
|
|
729
|
-
out += `\u{1F9EA} TESTES E VERIFICA\xC7\xD5ES
|
|
959
|
+
\u{1F9EA} TESTES E VERIFICACOES
|
|
730
960
|
|
|
731
961
|
`;
|
|
732
962
|
for (const testSuggestion of result.testSuggestions) {
|
|
@@ -734,17 +964,12 @@ function formatSuggestText(result) {
|
|
|
734
964
|
`;
|
|
735
965
|
}
|
|
736
966
|
}
|
|
967
|
+
out += nextSteps("suggest", ctx);
|
|
737
968
|
return out;
|
|
738
969
|
}
|
|
739
|
-
function formatContextText(result) {
|
|
970
|
+
function formatContextText(result, ctx = "cli") {
|
|
740
971
|
let out = "";
|
|
741
|
-
out +=
|
|
742
|
-
`;
|
|
743
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
744
|
-
`;
|
|
745
|
-
out += `\u2551 \u{1F4C4} CONTEXT \u2551
|
|
746
|
-
`;
|
|
747
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
972
|
+
out += `## \u{1F4C4} CONTEXT
|
|
748
973
|
|
|
749
974
|
`;
|
|
750
975
|
const icon = categoryIcons[result.category];
|
|
@@ -752,9 +977,6 @@ function formatContextText(result) {
|
|
|
752
977
|
`;
|
|
753
978
|
out += ` ${icon} ${result.category}
|
|
754
979
|
|
|
755
|
-
`;
|
|
756
|
-
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
757
|
-
|
|
758
980
|
`;
|
|
759
981
|
if (result.imports.length > 0) {
|
|
760
982
|
out += `\u{1F4E5} IMPORTS (${result.imports.length})
|
|
@@ -798,10 +1020,10 @@ function formatContextText(result) {
|
|
|
798
1020
|
`;
|
|
799
1021
|
for (const fn of result.functions) {
|
|
800
1022
|
const exported = fn.isExported ? "export " : "";
|
|
801
|
-
const
|
|
1023
|
+
const asyncLabel = fn.isAsync ? "async " : "";
|
|
802
1024
|
const arrow = fn.isArrowFunction ? " =>" : "";
|
|
803
1025
|
const params = fn.params.map((p) => `${p.name}: ${p.type}`).join(", ");
|
|
804
|
-
out += ` ${exported}${
|
|
1026
|
+
out += ` ${exported}${asyncLabel}${fn.name}(${params})${arrow}: ${fn.returnType}
|
|
805
1027
|
`;
|
|
806
1028
|
if (fn.jsdoc) {
|
|
807
1029
|
out += ` /** ${fn.jsdoc} */
|
|
@@ -811,9 +1033,18 @@ function formatContextText(result) {
|
|
|
811
1033
|
`;
|
|
812
1034
|
}
|
|
813
1035
|
}
|
|
814
|
-
|
|
1036
|
+
if (result.constants && result.constants.length > 0) {
|
|
1037
|
+
out += `\u{1F4CC} CONSTANTS (${result.constants.length})
|
|
815
1038
|
|
|
816
1039
|
`;
|
|
1040
|
+
for (const c of result.constants) {
|
|
1041
|
+
const exported = c.isExported ? "export " : "";
|
|
1042
|
+
out += ` ${exported}${c.name}: ${c.type}
|
|
1043
|
+
`;
|
|
1044
|
+
}
|
|
1045
|
+
out += `
|
|
1046
|
+
`;
|
|
1047
|
+
}
|
|
817
1048
|
out += `\u{1F4CA} RESUMO
|
|
818
1049
|
`;
|
|
819
1050
|
out += ` Imports: ${result.imports.length}
|
|
@@ -824,35 +1055,60 @@ function formatContextText(result) {
|
|
|
824
1055
|
`;
|
|
825
1056
|
out += ` Functions: ${result.functions.length}
|
|
826
1057
|
`;
|
|
827
|
-
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
|
|
1058
|
+
if (result.constants && result.constants.length > 0) {
|
|
1059
|
+
out += ` Constants: ${result.constants.length}
|
|
1060
|
+
`;
|
|
1061
|
+
}
|
|
831
1062
|
out += `
|
|
1063
|
+
\u{1F4D6} Proximos passos:
|
|
832
1064
|
`;
|
|
833
|
-
|
|
1065
|
+
const exportedFns = result.functions.filter((f) => f.isExported);
|
|
1066
|
+
const exportedTypes = result.types.filter((t) => t.isExported);
|
|
1067
|
+
if (exportedFns.length > 0) {
|
|
1068
|
+
const mainExport = exportedFns[0].name;
|
|
1069
|
+
out += ` \u2192 ${hint("find", ctx, { "<termo>": mainExport })} - ver onde ${mainExport} e usado
|
|
1070
|
+
`;
|
|
1071
|
+
}
|
|
1072
|
+
const isHook = result.category === "hook" || result.functions.some((f) => f.name.startsWith("use"));
|
|
1073
|
+
if (isHook) {
|
|
1074
|
+
out += ` \u2192 ${hint("impact", ctx, { "<arquivo>": result.file })} - ver quem usa este hook
|
|
834
1075
|
`;
|
|
835
|
-
|
|
1076
|
+
}
|
|
1077
|
+
if (result.category === "service") {
|
|
1078
|
+
out += ` \u2192 ${hint("impact", ctx, { "<arquivo>": result.file })} - ver quem usa este service
|
|
836
1079
|
`;
|
|
837
|
-
|
|
1080
|
+
}
|
|
1081
|
+
if (exportedTypes.length > 0) {
|
|
1082
|
+
const mainType = exportedTypes[0].name;
|
|
1083
|
+
out += ` \u2192 ${hint("find", ctx, { "<termo>": mainType })} - ver onde ${mainType} e usado
|
|
1084
|
+
`;
|
|
1085
|
+
}
|
|
1086
|
+
if (!isHook && result.category !== "service") {
|
|
1087
|
+
out += ` \u2192 ${hint("impact", ctx, { "<arquivo>": result.file })} - ver quem sera afetado por mudancas
|
|
1088
|
+
`;
|
|
1089
|
+
}
|
|
1090
|
+
out += ` \u2192 ${hint("suggest", ctx, { "<arquivo>": result.file })} - o que ler antes de editar
|
|
1091
|
+
`;
|
|
1092
|
+
return out;
|
|
1093
|
+
}
|
|
1094
|
+
function formatAreasText(result, ctx = "cli") {
|
|
1095
|
+
let out = "";
|
|
1096
|
+
out += `## \u{1F4E6} PROJECT AREAS
|
|
838
1097
|
|
|
839
1098
|
`;
|
|
840
1099
|
out += `\u{1F4CA} RESUMO
|
|
841
1100
|
`;
|
|
842
|
-
out += `
|
|
1101
|
+
out += ` Areas: ${result.summary.totalAreas}
|
|
843
1102
|
`;
|
|
844
1103
|
out += ` Arquivos: ${result.summary.totalFiles}
|
|
845
1104
|
`;
|
|
846
1105
|
if (result.summary.unmappedCount > 0) {
|
|
847
|
-
out += ` \u26A0\uFE0F Sem
|
|
1106
|
+
out += ` \u26A0\uFE0F Sem area: ${result.summary.unmappedCount}
|
|
848
1107
|
`;
|
|
849
1108
|
}
|
|
850
1109
|
out += `
|
|
851
1110
|
`;
|
|
852
|
-
out += `\
|
|
853
|
-
|
|
854
|
-
`;
|
|
855
|
-
out += `\u{1F4E6} \xC1REAS DETECTADAS
|
|
1111
|
+
out += `\u{1F4E6} AREAS DETECTADAS
|
|
856
1112
|
|
|
857
1113
|
`;
|
|
858
1114
|
for (const area2 of result.areas) {
|
|
@@ -872,15 +1128,12 @@ function formatAreasText(result) {
|
|
|
872
1128
|
`;
|
|
873
1129
|
}
|
|
874
1130
|
if (result.unmapped.length > 0) {
|
|
875
|
-
out += `\
|
|
876
|
-
|
|
877
|
-
`;
|
|
878
|
-
out += `\u26A0\uFE0F ARQUIVOS SEM \xC1REA (${result.unmapped.length})
|
|
1131
|
+
out += `\u26A0\uFE0F ARQUIVOS SEM AREA (${result.unmapped.length})
|
|
879
1132
|
|
|
880
1133
|
`;
|
|
881
1134
|
for (const file of result.unmapped.slice(0, 10)) {
|
|
882
|
-
const
|
|
883
|
-
out += ` ${
|
|
1135
|
+
const fileIcon = categoryIcons[file.category];
|
|
1136
|
+
out += ` ${fileIcon} ${file.path}
|
|
884
1137
|
`;
|
|
885
1138
|
}
|
|
886
1139
|
if (result.unmapped.length > 10) {
|
|
@@ -888,32 +1141,19 @@ function formatAreasText(result) {
|
|
|
888
1141
|
`;
|
|
889
1142
|
}
|
|
890
1143
|
out += `
|
|
891
|
-
\u{1F4A1} Adicione
|
|
1144
|
+
\u{1F4A1} Adicione padroes em .analyze/areas.config.json
|
|
892
1145
|
`;
|
|
893
|
-
out += ` ou execute
|
|
1146
|
+
out += ` ou execute ${hint("areas_init", ctx)} para gerar configuracao
|
|
894
1147
|
`;
|
|
895
1148
|
}
|
|
896
|
-
out +=
|
|
897
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
898
|
-
|
|
899
|
-
`;
|
|
900
|
-
out += `\u{1F4A1} Use 'ai-tool area <nome>' para ver detalhes de uma \xE1rea
|
|
901
|
-
`;
|
|
902
|
-
out += ` Exemplo: ai-tool area auth
|
|
903
|
-
`;
|
|
1149
|
+
out += nextSteps("areas", ctx);
|
|
904
1150
|
return out;
|
|
905
1151
|
}
|
|
906
|
-
function formatAreaDetailText(result, options = {}) {
|
|
1152
|
+
function formatAreaDetailText(result, options = {}, ctx = "cli") {
|
|
907
1153
|
const { full = false, filterType } = options;
|
|
908
1154
|
const { area: area2, byCategory } = result;
|
|
909
1155
|
let out = "";
|
|
910
|
-
out +=
|
|
911
|
-
`;
|
|
912
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
913
|
-
`;
|
|
914
|
-
out += `\u2551 \u{1F4E6} AREA DETAIL \u2551
|
|
915
|
-
`;
|
|
916
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
1156
|
+
out += `## \u{1F4E6} AREA DETAIL
|
|
917
1157
|
|
|
918
1158
|
`;
|
|
919
1159
|
out += `\u{1F4E6} ${area2.name}
|
|
@@ -951,9 +1191,6 @@ function formatAreaDetailText(result, options = {}) {
|
|
|
951
1191
|
out += catParts.join(" ");
|
|
952
1192
|
out += `
|
|
953
1193
|
|
|
954
|
-
`;
|
|
955
|
-
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
956
|
-
|
|
957
1194
|
`;
|
|
958
1195
|
if (filterType) {
|
|
959
1196
|
const files = byCategory[filterType] || [];
|
|
@@ -965,6 +1202,7 @@ function formatAreaDetailText(result, options = {}) {
|
|
|
965
1202
|
out += ` ${file.path}${desc}
|
|
966
1203
|
`;
|
|
967
1204
|
}
|
|
1205
|
+
out += nextSteps("area", ctx);
|
|
968
1206
|
return out;
|
|
969
1207
|
}
|
|
970
1208
|
const maxFilesPerCategory = full ? 100 : 8;
|
|
@@ -996,17 +1234,12 @@ function formatAreaDetailText(result, options = {}) {
|
|
|
996
1234
|
out += `\u{1F4A1} Use --full para ver todos os arquivos
|
|
997
1235
|
`;
|
|
998
1236
|
}
|
|
1237
|
+
out += nextSteps("area", ctx);
|
|
999
1238
|
return out;
|
|
1000
1239
|
}
|
|
1001
|
-
function formatFindText(result) {
|
|
1240
|
+
function formatFindText(result, ctx = "cli", symbolNames) {
|
|
1002
1241
|
let out = "";
|
|
1003
|
-
out +=
|
|
1004
|
-
`;
|
|
1005
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
1006
|
-
`;
|
|
1007
|
-
out += `\u2551 \u{1F50D} FIND \u2551
|
|
1008
|
-
`;
|
|
1009
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
1242
|
+
out += `## \u{1F50D} FIND
|
|
1010
1243
|
|
|
1011
1244
|
`;
|
|
1012
1245
|
if (result.query) {
|
|
@@ -1021,7 +1254,7 @@ function formatFindText(result) {
|
|
|
1021
1254
|
|
|
1022
1255
|
`;
|
|
1023
1256
|
} else {
|
|
1024
|
-
out += `\u{1F4CB} Listando todos os
|
|
1257
|
+
out += `\u{1F4CB} Listando todos os simbolos do tipo: ${result.filters.type || "all"}
|
|
1025
1258
|
|
|
1026
1259
|
`;
|
|
1027
1260
|
}
|
|
@@ -1031,33 +1264,45 @@ function formatFindText(result) {
|
|
|
1031
1264
|
|
|
1032
1265
|
`;
|
|
1033
1266
|
} else {
|
|
1034
|
-
out += `\u274C Nenhum
|
|
1267
|
+
out += `\u274C Nenhum simbolo do tipo "${result.filters.type}" encontrado
|
|
1035
1268
|
|
|
1036
1269
|
`;
|
|
1037
1270
|
}
|
|
1271
|
+
if (symbolNames && symbolNames.length > 0 && result.query) {
|
|
1272
|
+
const similar = findSimilar(result.query, symbolNames, { maxDistance: 3, limit: 5 });
|
|
1273
|
+
if (similar.length > 0) {
|
|
1274
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
1275
|
+
`;
|
|
1276
|
+
for (const name of similar) {
|
|
1277
|
+
out += ` \u2192 ${hint("find", ctx, { "<termo>": name })}
|
|
1278
|
+
`;
|
|
1279
|
+
}
|
|
1280
|
+
out += `
|
|
1281
|
+
`;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1038
1284
|
out += `\u{1F4A1} Dicas:
|
|
1039
1285
|
`;
|
|
1040
|
-
out += ` \
|
|
1286
|
+
out += ` \u2192 Verifique a ortografia
|
|
1287
|
+
`;
|
|
1288
|
+
out += ` \u2192 Tente buscar parte do nome
|
|
1041
1289
|
`;
|
|
1042
|
-
out += ` \
|
|
1290
|
+
out += ` \u2192 Remova filtros de tipo ou area
|
|
1043
1291
|
`;
|
|
1044
|
-
out += ` \
|
|
1292
|
+
out += ` \u2192 ${hint("describe", ctx)} - buscar areas por descricao
|
|
1045
1293
|
`;
|
|
1046
1294
|
return out;
|
|
1047
1295
|
}
|
|
1048
|
-
out += `\u{1F4CA} ${result.summary.definitions}
|
|
1049
|
-
|
|
1050
|
-
`;
|
|
1051
|
-
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1296
|
+
out += `\u{1F4CA} ${result.summary.definitions} definicao, ${result.summary.references} referencias em ${result.summary.files} arquivos
|
|
1052
1297
|
|
|
1053
1298
|
`;
|
|
1054
1299
|
if (result.definition) {
|
|
1055
|
-
out += `\u{1F4CD}
|
|
1300
|
+
out += `\u{1F4CD} DEFINICAO
|
|
1056
1301
|
|
|
1057
1302
|
`;
|
|
1058
1303
|
const def = result.definition;
|
|
1059
|
-
const
|
|
1060
|
-
out += ` ${
|
|
1304
|
+
const defIcon = categoryIcons[def.category];
|
|
1305
|
+
out += ` ${defIcon} ${def.file}:${def.line}
|
|
1061
1306
|
`;
|
|
1062
1307
|
out += ` ${def.code}
|
|
1063
1308
|
`;
|
|
@@ -1072,8 +1317,8 @@ function formatFindText(result) {
|
|
|
1072
1317
|
|
|
1073
1318
|
`;
|
|
1074
1319
|
for (const ref of imports.slice(0, 10)) {
|
|
1075
|
-
const
|
|
1076
|
-
out += ` ${
|
|
1320
|
+
const refIcon = categoryIcons[ref.category];
|
|
1321
|
+
out += ` ${refIcon} ${ref.file}:${ref.line}
|
|
1077
1322
|
`;
|
|
1078
1323
|
out += ` ${ref.code}
|
|
1079
1324
|
`;
|
|
@@ -1090,8 +1335,8 @@ function formatFindText(result) {
|
|
|
1090
1335
|
|
|
1091
1336
|
`;
|
|
1092
1337
|
for (const ref of usages.slice(0, 15)) {
|
|
1093
|
-
const
|
|
1094
|
-
out += ` ${
|
|
1338
|
+
const refIcon = categoryIcons[ref.category];
|
|
1339
|
+
out += ` ${refIcon} ${ref.file}:${ref.line}
|
|
1095
1340
|
`;
|
|
1096
1341
|
out += ` ${ref.code}
|
|
1097
1342
|
`;
|
|
@@ -1106,15 +1351,9 @@ function formatFindText(result) {
|
|
|
1106
1351
|
}
|
|
1107
1352
|
return out;
|
|
1108
1353
|
}
|
|
1109
|
-
function formatAreaContextText(result) {
|
|
1354
|
+
function formatAreaContextText(result, ctx = "cli") {
|
|
1110
1355
|
let out = "";
|
|
1111
|
-
out +=
|
|
1112
|
-
`;
|
|
1113
|
-
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
1114
|
-
`;
|
|
1115
|
-
out += `\u2551 \u{1F4E6} AREA CONTEXT \u2551
|
|
1116
|
-
`;
|
|
1117
|
-
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
1356
|
+
out += `## \u{1F4E6} AREA CONTEXT
|
|
1118
1357
|
|
|
1119
1358
|
`;
|
|
1120
1359
|
out += `\u{1F4E6} ${result.area.name} - Contexto Consolidado (${result.area.fileCount} arquivos)
|
|
@@ -1124,9 +1363,6 @@ function formatAreaContextText(result) {
|
|
|
1124
1363
|
`;
|
|
1125
1364
|
}
|
|
1126
1365
|
out += `
|
|
1127
|
-
`;
|
|
1128
|
-
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1129
|
-
|
|
1130
1366
|
`;
|
|
1131
1367
|
if (result.types.length > 0) {
|
|
1132
1368
|
out += `\u{1F3F7}\uFE0F TYPES (${result.types.length})
|
|
@@ -1232,9 +1468,6 @@ function formatAreaContextText(result) {
|
|
|
1232
1468
|
`;
|
|
1233
1469
|
}
|
|
1234
1470
|
}
|
|
1235
|
-
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1236
|
-
|
|
1237
|
-
`;
|
|
1238
1471
|
out += `\u{1F4CA} RESUMO
|
|
1239
1472
|
`;
|
|
1240
1473
|
out += ` Types: ${result.types.length}
|
|
@@ -1253,6 +1486,7 @@ function formatAreaContextText(result) {
|
|
|
1253
1486
|
out += ` Triggers: ${result.triggers.length}
|
|
1254
1487
|
`;
|
|
1255
1488
|
}
|
|
1489
|
+
out += nextSteps("area_context", ctx);
|
|
1256
1490
|
return out;
|
|
1257
1491
|
}
|
|
1258
1492
|
|
|
@@ -1829,10 +2063,37 @@ function formatOutput(result, format, textFormatter, cacheMarker) {
|
|
|
1829
2063
|
}
|
|
1830
2064
|
|
|
1831
2065
|
// src/commands/map.ts
|
|
2066
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2067
|
+
import { join as join4 } from "path";
|
|
2068
|
+
function detectFramework(cwd) {
|
|
2069
|
+
if (existsSync4(join4(cwd, "next.config.js")) || existsSync4(join4(cwd, "next.config.ts")) || existsSync4(join4(cwd, "next.config.mjs"))) {
|
|
2070
|
+
return "Next.js";
|
|
2071
|
+
}
|
|
2072
|
+
if (existsSync4(join4(cwd, "vite.config.ts")) || existsSync4(join4(cwd, "vite.config.js"))) {
|
|
2073
|
+
return "Vite";
|
|
2074
|
+
}
|
|
2075
|
+
if (existsSync4(join4(cwd, "remix.config.js")) || existsSync4(join4(cwd, "remix.config.ts"))) {
|
|
2076
|
+
return "Remix";
|
|
2077
|
+
}
|
|
2078
|
+
if (existsSync4(join4(cwd, "astro.config.mjs")) || existsSync4(join4(cwd, "astro.config.ts"))) {
|
|
2079
|
+
return "Astro";
|
|
2080
|
+
}
|
|
2081
|
+
if (existsSync4(join4(cwd, "nuxt.config.ts")) || existsSync4(join4(cwd, "nuxt.config.js"))) {
|
|
2082
|
+
return "Nuxt";
|
|
2083
|
+
}
|
|
2084
|
+
if (existsSync4(join4(cwd, "svelte.config.js")) || existsSync4(join4(cwd, "svelte.config.ts"))) {
|
|
2085
|
+
return "SvelteKit";
|
|
2086
|
+
}
|
|
2087
|
+
if (existsSync4(join4(cwd, ".firebaserc")) || existsSync4(join4(cwd, "firebase.json"))) {
|
|
2088
|
+
return "Firebase";
|
|
2089
|
+
}
|
|
2090
|
+
return void 0;
|
|
2091
|
+
}
|
|
1832
2092
|
async function map(options = {}) {
|
|
1833
2093
|
const { cwd, format } = parseCommandOptions(options);
|
|
1834
2094
|
const useCache = options.cache !== false;
|
|
1835
2095
|
const full = options.full ?? false;
|
|
2096
|
+
const ctx = options.ctx || "cli";
|
|
1836
2097
|
if (useCache && isCacheValid(cwd)) {
|
|
1837
2098
|
const cached = getCachedMapResult(cwd);
|
|
1838
2099
|
if (cached) {
|
|
@@ -1841,10 +2102,10 @@ async function map(options = {}) {
|
|
|
1841
2102
|
return JSON.stringify(result, null, 2);
|
|
1842
2103
|
}
|
|
1843
2104
|
if (full) {
|
|
1844
|
-
return formatMapText(result) + "\n\n\u{1F4E6} (resultado do cache)";
|
|
2105
|
+
return formatMapText(result, ctx) + "\n\n\u{1F4E6} (resultado do cache)";
|
|
1845
2106
|
}
|
|
1846
2107
|
const areasInfo = detectAreasInfo(cwd, result.files.map((f) => f.path));
|
|
1847
|
-
return formatMapSummary(result, areasInfo) + "\n\u{1F4E6} (cache)";
|
|
2108
|
+
return formatMapSummary(result, areasInfo, ctx) + "\n\u{1F4E6} (cache)";
|
|
1848
2109
|
}
|
|
1849
2110
|
}
|
|
1850
2111
|
try {
|
|
@@ -1898,10 +2159,12 @@ async function map(options = {}) {
|
|
|
1898
2159
|
categories[file.category] = (categories[file.category] || 0) + 1;
|
|
1899
2160
|
}
|
|
1900
2161
|
const circular = findCircularDependencies();
|
|
2162
|
+
const framework = detectFramework(cwd);
|
|
1901
2163
|
const result = {
|
|
1902
2164
|
version: "1.0.0",
|
|
1903
2165
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1904
2166
|
cwd,
|
|
2167
|
+
framework,
|
|
1905
2168
|
summary: {
|
|
1906
2169
|
totalFiles: files.length,
|
|
1907
2170
|
totalFolders: folderMap.size,
|
|
@@ -1919,10 +2182,10 @@ async function map(options = {}) {
|
|
|
1919
2182
|
return JSON.stringify(result, null, 2);
|
|
1920
2183
|
}
|
|
1921
2184
|
if (full) {
|
|
1922
|
-
return formatMapText(result);
|
|
2185
|
+
return formatMapText(result, ctx);
|
|
1923
2186
|
}
|
|
1924
2187
|
const areasInfo = detectAreasInfo(cwd, files.map((f) => f.path));
|
|
1925
|
-
return formatMapSummary(result, areasInfo);
|
|
2188
|
+
return formatMapSummary(result, areasInfo, ctx);
|
|
1926
2189
|
} catch (error) {
|
|
1927
2190
|
const message = error instanceof Error ? error.message : String(error);
|
|
1928
2191
|
throw new Error(`Erro ao executar map: ${message}`);
|
|
@@ -1960,8 +2223,8 @@ function detectAreasInfo(cwd, filePaths) {
|
|
|
1960
2223
|
|
|
1961
2224
|
// src/commands/dead.ts
|
|
1962
2225
|
import { execSync } from "child_process";
|
|
1963
|
-
import { writeFileSync as writeFileSync3, unlinkSync, existsSync as
|
|
1964
|
-
import { join as
|
|
2226
|
+
import { writeFileSync as writeFileSync3, unlinkSync, existsSync as existsSync5 } from "fs";
|
|
2227
|
+
import { join as join5 } from "path";
|
|
1965
2228
|
function generateKnipConfig(cwd) {
|
|
1966
2229
|
const ignorePatterns = getIgnorePatterns(cwd);
|
|
1967
2230
|
if (ignorePatterns.length === 0) {
|
|
@@ -1971,7 +2234,7 @@ function generateKnipConfig(cwd) {
|
|
|
1971
2234
|
$schema: "https://unpkg.com/knip@5/schema.json",
|
|
1972
2235
|
ignore: ignorePatterns
|
|
1973
2236
|
};
|
|
1974
|
-
const configPath =
|
|
2237
|
+
const configPath = join5(cwd, ".knip.ai-tool.json");
|
|
1975
2238
|
writeFileSync3(configPath, JSON.stringify(knipConfig, null, 2), "utf-8");
|
|
1976
2239
|
return configPath;
|
|
1977
2240
|
}
|
|
@@ -2009,7 +2272,7 @@ async function dead(options = {}) {
|
|
|
2009
2272
|
knipOutput = {};
|
|
2010
2273
|
}
|
|
2011
2274
|
} finally {
|
|
2012
|
-
if (knipConfigPath &&
|
|
2275
|
+
if (knipConfigPath && existsSync5(knipConfigPath)) {
|
|
2013
2276
|
try {
|
|
2014
2277
|
unlinkSync(knipConfigPath);
|
|
2015
2278
|
} catch {
|
|
@@ -2091,93 +2354,34 @@ ${output}`;
|
|
|
2091
2354
|
// src/commands/impact.ts
|
|
2092
2355
|
import skott2 from "skott";
|
|
2093
2356
|
|
|
2094
|
-
// src/utils/similarity.ts
|
|
2095
|
-
function levenshteinDistance(a, b) {
|
|
2096
|
-
const matrix = [];
|
|
2097
|
-
for (let i = 0; i <= b.length; i++) {
|
|
2098
|
-
matrix[i] = [i];
|
|
2099
|
-
}
|
|
2100
|
-
for (let j = 0; j <= a.length; j++) {
|
|
2101
|
-
matrix[0][j] = j;
|
|
2102
|
-
}
|
|
2103
|
-
for (let i = 1; i <= b.length; i++) {
|
|
2104
|
-
for (let j = 1; j <= a.length; j++) {
|
|
2105
|
-
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
2106
|
-
matrix[i][j] = matrix[i - 1][j - 1];
|
|
2107
|
-
} else {
|
|
2108
|
-
matrix[i][j] = Math.min(
|
|
2109
|
-
matrix[i - 1][j - 1] + 1,
|
|
2110
|
-
// substituição
|
|
2111
|
-
matrix[i][j - 1] + 1,
|
|
2112
|
-
// inserção
|
|
2113
|
-
matrix[i - 1][j] + 1
|
|
2114
|
-
// deleção
|
|
2115
|
-
);
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
return matrix[b.length][a.length];
|
|
2120
|
-
}
|
|
2121
|
-
function findSimilar(target, candidates, options = {}) {
|
|
2122
|
-
const {
|
|
2123
|
-
maxDistance = 3,
|
|
2124
|
-
limit = 5,
|
|
2125
|
-
normalize: normalize2 = true,
|
|
2126
|
-
extractKey = (item) => String(item)
|
|
2127
|
-
} = options;
|
|
2128
|
-
const normalizedTarget = normalize2 ? target.toLowerCase() : target;
|
|
2129
|
-
const scored = candidates.map((item) => {
|
|
2130
|
-
const key = extractKey(item);
|
|
2131
|
-
const normalizedKey = normalize2 ? key.toLowerCase() : key;
|
|
2132
|
-
const keyNoExt = normalizedKey.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
2133
|
-
let score = Infinity;
|
|
2134
|
-
if (normalizedKey.includes(normalizedTarget) || keyNoExt.includes(normalizedTarget)) {
|
|
2135
|
-
score = 0;
|
|
2136
|
-
} else {
|
|
2137
|
-
score = levenshteinDistance(keyNoExt, normalizedTarget);
|
|
2138
|
-
}
|
|
2139
|
-
return { item, score };
|
|
2140
|
-
}).filter(({ score }) => score <= maxDistance).sort((a, b) => a.score - b.score).slice(0, limit);
|
|
2141
|
-
return scored.map(({ item }) => item);
|
|
2142
|
-
}
|
|
2143
|
-
function findBestMatch(target, candidates, extractKey = (item) => String(item)) {
|
|
2144
|
-
const similar = findSimilar(target, candidates, {
|
|
2145
|
-
maxDistance: 2,
|
|
2146
|
-
// Mais restritivo para "você quis dizer"
|
|
2147
|
-
limit: 1,
|
|
2148
|
-
extractKey
|
|
2149
|
-
});
|
|
2150
|
-
return similar.length > 0 ? similar[0] : null;
|
|
2151
|
-
}
|
|
2152
|
-
function extractFileName(filePath) {
|
|
2153
|
-
const fileName = filePath.split("/").pop() || filePath;
|
|
2154
|
-
return fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2157
2357
|
// src/utils/errors.ts
|
|
2158
|
-
var
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2358
|
+
var COMMAND_REFERENCE_KEYS = ["map", "areas", "area", "suggest", "context", "impact", "dead", "find", "describe"];
|
|
2359
|
+
var COMMAND_DESCRIPTIONS = {
|
|
2360
|
+
map: "Resumo do projeto",
|
|
2361
|
+
areas: "Listar todas as areas",
|
|
2362
|
+
area: "Arquivos de uma area especifica",
|
|
2162
2363
|
suggest: "O que ler antes de editar",
|
|
2163
2364
|
context: "API/assinaturas de um arquivo",
|
|
2164
2365
|
impact: "Quem usa este arquivo",
|
|
2165
|
-
dead: "
|
|
2366
|
+
dead: "Codigo morto",
|
|
2367
|
+
find: "Buscar simbolos no codigo",
|
|
2368
|
+
describe: "Buscar areas por descricao"
|
|
2166
2369
|
};
|
|
2167
|
-
function getCommandReferenceSection(excludeCommand) {
|
|
2370
|
+
function getCommandReferenceSection(ctx, excludeCommand) {
|
|
2168
2371
|
let out = `
|
|
2169
|
-
\u{1F4CC} Comandos
|
|
2372
|
+
\u{1F4CC} Comandos uteis:
|
|
2170
2373
|
`;
|
|
2171
|
-
for (const
|
|
2374
|
+
for (const cmd of COMMAND_REFERENCE_KEYS) {
|
|
2172
2375
|
if (cmd !== excludeCommand) {
|
|
2173
|
-
|
|
2376
|
+
const desc = COMMAND_DESCRIPTIONS[cmd];
|
|
2377
|
+
out += ` \u2192 ${hint(cmd, ctx)} - ${desc}
|
|
2174
2378
|
`;
|
|
2175
2379
|
}
|
|
2176
2380
|
}
|
|
2177
2381
|
return out;
|
|
2178
2382
|
}
|
|
2179
2383
|
function formatFileNotFound(options) {
|
|
2180
|
-
const { target, allFiles, command } = options;
|
|
2384
|
+
const { target, allFiles, command, ctx = "cli" } = options;
|
|
2181
2385
|
const similarFiles = findSimilar(target, allFiles, {
|
|
2182
2386
|
maxDistance: 3,
|
|
2183
2387
|
limit: 5,
|
|
@@ -2185,14 +2389,20 @@ function formatFileNotFound(options) {
|
|
|
2185
2389
|
});
|
|
2186
2390
|
const bestMatch = findBestMatch(target, allFiles, extractFileName);
|
|
2187
2391
|
let out = `
|
|
2188
|
-
\u274C Arquivo
|
|
2392
|
+
\u274C Arquivo nao encontrado: "${target}"
|
|
2189
2393
|
|
|
2190
2394
|
`;
|
|
2191
2395
|
out += `\u{1F4CA} Total de arquivos indexados: ${allFiles.length}
|
|
2192
2396
|
|
|
2193
2397
|
`;
|
|
2194
|
-
if (bestMatch) {
|
|
2195
|
-
out += `\u{1F4A1}
|
|
2398
|
+
if (bestMatch && command) {
|
|
2399
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2400
|
+
`;
|
|
2401
|
+
out += ` \u2192 ${hint(command, ctx, { "<arquivo>": bestMatch, "<termo>": bestMatch })}
|
|
2402
|
+
|
|
2403
|
+
`;
|
|
2404
|
+
} else if (bestMatch) {
|
|
2405
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2196
2406
|
`;
|
|
2197
2407
|
out += ` \u2192 ${bestMatch}
|
|
2198
2408
|
|
|
@@ -2215,15 +2425,15 @@ function formatFileNotFound(options) {
|
|
|
2215
2425
|
`;
|
|
2216
2426
|
out += ` \u2022 Ou apenas o nome do arquivo: Header
|
|
2217
2427
|
`;
|
|
2218
|
-
out += ` \u2022 Verifique se o arquivo
|
|
2428
|
+
out += ` \u2022 Verifique se o arquivo esta em uma pasta incluida no scan
|
|
2219
2429
|
`;
|
|
2220
2430
|
if (command) {
|
|
2221
|
-
out += getCommandReferenceSection(command);
|
|
2431
|
+
out += getCommandReferenceSection(ctx, command);
|
|
2222
2432
|
}
|
|
2223
2433
|
return out;
|
|
2224
2434
|
}
|
|
2225
2435
|
function formatAreaNotFound(options) {
|
|
2226
|
-
const { target, availableAreas } = options;
|
|
2436
|
+
const { target, availableAreas, ctx = "cli" } = options;
|
|
2227
2437
|
const areaIds = availableAreas.map((a) => a.id);
|
|
2228
2438
|
const bestMatchId = findBestMatch(target, areaIds);
|
|
2229
2439
|
const similarAreaIds = findSimilar(target, areaIds, {
|
|
@@ -2231,18 +2441,18 @@ function formatAreaNotFound(options) {
|
|
|
2231
2441
|
limit: 5
|
|
2232
2442
|
});
|
|
2233
2443
|
let out = `
|
|
2234
|
-
\u274C
|
|
2444
|
+
\u274C Area nao encontrada: "${target}"
|
|
2235
2445
|
|
|
2236
2446
|
`;
|
|
2237
2447
|
if (bestMatchId) {
|
|
2238
|
-
out += `\u{1F4A1}
|
|
2448
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2239
2449
|
`;
|
|
2240
|
-
out += ` \u2192
|
|
2450
|
+
out += ` \u2192 ${hint("area", ctx, { "<nome>": bestMatchId })}
|
|
2241
2451
|
|
|
2242
2452
|
`;
|
|
2243
2453
|
}
|
|
2244
2454
|
if (availableAreas.length > 0) {
|
|
2245
|
-
out += `\u{1F4E6}
|
|
2455
|
+
out += `\u{1F4E6} Areas disponiveis:
|
|
2246
2456
|
|
|
2247
2457
|
`;
|
|
2248
2458
|
if (similarAreaIds.length > 0 && !bestMatchId) {
|
|
@@ -2271,67 +2481,57 @@ function formatAreaNotFound(options) {
|
|
|
2271
2481
|
}
|
|
2272
2482
|
out += `\u{1F4D6} Dicas:
|
|
2273
2483
|
`;
|
|
2274
|
-
out += ` \
|
|
2275
|
-
`;
|
|
2276
|
-
out += ` \u2022 Use 'ai-tool areas' para listar todas as \xE1reas
|
|
2484
|
+
out += ` \u2192 ${hint("areas", ctx)} - listar todas as areas
|
|
2277
2485
|
`;
|
|
2278
|
-
out += ` \
|
|
2486
|
+
out += ` \u2192 ${hint("describe", ctx)} - buscar areas por descricao
|
|
2279
2487
|
`;
|
|
2280
|
-
out += `
|
|
2281
|
-
\u{1F4CC} Comandos relacionados:
|
|
2282
|
-
`;
|
|
2283
|
-
out += ` ai-tool areas Listar todas as \xE1reas
|
|
2284
|
-
`;
|
|
2285
|
-
out += ` ai-tool map Ver estrutura do projeto
|
|
2488
|
+
out += ` \u2192 IDs sao case-sensitive (Auth \u2260 auth)
|
|
2286
2489
|
`;
|
|
2287
2490
|
return out;
|
|
2288
2491
|
}
|
|
2289
|
-
function formatMissingTarget(command) {
|
|
2492
|
+
function formatMissingTarget(command, ctx = "cli") {
|
|
2290
2493
|
let out = `
|
|
2291
|
-
\u274C Erro:
|
|
2494
|
+
\u274C Erro: parametro "target" e OBRIGATORIO para o comando "${command}".
|
|
2292
2495
|
|
|
2293
2496
|
`;
|
|
2294
2497
|
out += `\u{1F4DD} Exemplos:
|
|
2295
2498
|
`;
|
|
2296
2499
|
if (command === "area") {
|
|
2297
|
-
out += `
|
|
2500
|
+
out += ` ${hint("area", ctx, { "<nome>": "auth" })}
|
|
2298
2501
|
`;
|
|
2299
|
-
out += `
|
|
2300
|
-
`;
|
|
2301
|
-
out += ` ai-tool area billing --type=hook
|
|
2502
|
+
out += ` ${hint("area", ctx, { "<nome>": "dashboard" })}
|
|
2302
2503
|
|
|
2303
2504
|
`;
|
|
2304
|
-
out += `\u{1F4A1}
|
|
2505
|
+
out += `\u{1F4A1} ${hint("areas", ctx)} - listar todas as areas disponiveis
|
|
2305
2506
|
`;
|
|
2306
2507
|
} else {
|
|
2307
|
-
out += `
|
|
2308
|
-
`;
|
|
2309
|
-
out += ` ai-tool ${command} Button.tsx
|
|
2508
|
+
out += ` ${hint(command, ctx, { "<arquivo>": "useAuth", "<termo>": "useAuth" })}
|
|
2310
2509
|
`;
|
|
2311
|
-
out += `
|
|
2510
|
+
out += ` ${hint(command, ctx, { "<arquivo>": "Button.tsx", "<termo>": "Button" })}
|
|
2312
2511
|
`;
|
|
2313
2512
|
}
|
|
2314
|
-
out += getCommandReferenceSection(command);
|
|
2513
|
+
out += getCommandReferenceSection(ctx, command);
|
|
2315
2514
|
return out;
|
|
2316
2515
|
}
|
|
2317
|
-
function formatInvalidCommand(command) {
|
|
2318
|
-
const validCommands = Object.keys(
|
|
2516
|
+
function formatInvalidCommand(command, ctx = "cli") {
|
|
2517
|
+
const validCommands = Object.keys(COMMAND_DESCRIPTIONS);
|
|
2319
2518
|
const bestMatch = findBestMatch(command, validCommands);
|
|
2320
2519
|
let out = `
|
|
2321
|
-
\u274C Comando
|
|
2520
|
+
\u274C Comando invalido: "${command}"
|
|
2322
2521
|
|
|
2323
2522
|
`;
|
|
2324
2523
|
if (bestMatch) {
|
|
2325
|
-
out += `\u{1F4A1}
|
|
2524
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2326
2525
|
`;
|
|
2327
|
-
out += ` \u2192
|
|
2526
|
+
out += ` \u2192 ${hint(bestMatch, ctx)}
|
|
2328
2527
|
|
|
2329
2528
|
`;
|
|
2330
2529
|
}
|
|
2331
|
-
out += `\u{1F4CC} Comandos
|
|
2530
|
+
out += `\u{1F4CC} Comandos disponiveis:
|
|
2332
2531
|
`;
|
|
2333
|
-
for (const
|
|
2334
|
-
|
|
2532
|
+
for (const cmd of COMMAND_REFERENCE_KEYS) {
|
|
2533
|
+
const desc = COMMAND_DESCRIPTIONS[cmd];
|
|
2534
|
+
out += ` \u2192 ${hint(cmd, ctx)} - ${desc}
|
|
2335
2535
|
`;
|
|
2336
2536
|
}
|
|
2337
2537
|
return out;
|
|
@@ -2387,10 +2587,10 @@ function findTargetFile(target, allFiles) {
|
|
|
2387
2587
|
}
|
|
2388
2588
|
|
|
2389
2589
|
// src/integrations/git.ts
|
|
2390
|
-
import { existsSync as
|
|
2590
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2391
2591
|
import { execSync as execSync2 } from "child_process";
|
|
2392
2592
|
function hasGitRepo(cwd) {
|
|
2393
|
-
return
|
|
2593
|
+
return existsSync6(cwd + "/.git");
|
|
2394
2594
|
}
|
|
2395
2595
|
async function getCommitsForFile(filePath, cwd, limit = 10) {
|
|
2396
2596
|
if (!hasGitRepo(cwd)) {
|
|
@@ -2429,6 +2629,7 @@ async function getCommitsForFile(filePath, cwd, limit = 10) {
|
|
|
2429
2629
|
async function impact(target, options = {}) {
|
|
2430
2630
|
const { cwd, format } = parseCommandOptions(options);
|
|
2431
2631
|
const useCache = options.cache !== false;
|
|
2632
|
+
const ctx = options.ctx || "cli";
|
|
2432
2633
|
if (!target) {
|
|
2433
2634
|
throw new Error("Target \xE9 obrigat\xF3rio. Exemplo: ai-tool impact src/components/Button.tsx");
|
|
2434
2635
|
}
|
|
@@ -2474,7 +2675,7 @@ async function impact(target, options = {}) {
|
|
|
2474
2675
|
}
|
|
2475
2676
|
const targetPath = findTargetFile(target, allFiles);
|
|
2476
2677
|
if (!targetPath) {
|
|
2477
|
-
return formatFileNotFound({ target, allFiles, command: "impact" });
|
|
2678
|
+
return formatFileNotFound({ target, allFiles, command: "impact", ctx });
|
|
2478
2679
|
}
|
|
2479
2680
|
const { directUpstream, indirectUpstream, directDownstream, indirectDownstream } = calculateDependencies(targetPath, graph);
|
|
2480
2681
|
const dependingOn = [...directUpstream, ...indirectUpstream];
|
|
@@ -2507,7 +2708,7 @@ async function impact(target, options = {}) {
|
|
|
2507
2708
|
suggestions,
|
|
2508
2709
|
gitHistory
|
|
2509
2710
|
};
|
|
2510
|
-
return formatOutput(result, format, formatImpactText, fromCache);
|
|
2711
|
+
return formatOutput(result, format, (r) => formatImpactText(r, ctx), fromCache);
|
|
2511
2712
|
} catch (error) {
|
|
2512
2713
|
const message = error instanceof Error ? error.message : String(error);
|
|
2513
2714
|
throw new Error(`Erro ao executar impact: ${message}`);
|
|
@@ -2669,6 +2870,7 @@ async function suggest(target, options = {}) {
|
|
|
2669
2870
|
const { cwd, format } = parseCommandOptions(options);
|
|
2670
2871
|
const useCache = options.cache !== false;
|
|
2671
2872
|
const limit = options.limit || 10;
|
|
2873
|
+
const ctx = options.ctx || "cli";
|
|
2672
2874
|
if (!target) {
|
|
2673
2875
|
throw new Error("Target e obrigatorio. Exemplo: ai-tool suggest src/components/Button.tsx");
|
|
2674
2876
|
}
|
|
@@ -2710,7 +2912,7 @@ async function suggest(target, options = {}) {
|
|
|
2710
2912
|
}
|
|
2711
2913
|
const targetPath = findTargetFile(target, allFiles);
|
|
2712
2914
|
if (!targetPath) {
|
|
2713
|
-
return formatFileNotFound({ target, allFiles, command: "suggest" });
|
|
2915
|
+
return formatFileNotFound({ target, allFiles, command: "suggest", ctx });
|
|
2714
2916
|
}
|
|
2715
2917
|
const suggestions = collectSuggestions(targetPath, graph, allFiles, limit);
|
|
2716
2918
|
const testSuggestions = generateTestSuggestions(suggestions, allFiles);
|
|
@@ -2722,7 +2924,7 @@ async function suggest(target, options = {}) {
|
|
|
2722
2924
|
suggestions,
|
|
2723
2925
|
testSuggestions
|
|
2724
2926
|
};
|
|
2725
|
-
return formatOutput(result, format, formatSuggestText, fromCache);
|
|
2927
|
+
return formatOutput(result, format, (r) => formatSuggestText(r, ctx), fromCache);
|
|
2726
2928
|
} catch (error) {
|
|
2727
2929
|
const message = error instanceof Error ? error.message : String(error);
|
|
2728
2930
|
throw new Error(`Erro ao executar suggest: ${message}`);
|
|
@@ -2917,8 +3119,9 @@ function generateTestSuggestions(suggestions, allFiles) {
|
|
|
2917
3119
|
}
|
|
2918
3120
|
|
|
2919
3121
|
// src/commands/context.ts
|
|
2920
|
-
import { existsSync as
|
|
2921
|
-
import { join as
|
|
3122
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
3123
|
+
import { join as join7, resolve as resolve2, basename, extname as extname3 } from "path";
|
|
3124
|
+
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
2922
3125
|
|
|
2923
3126
|
// src/ts/extractor.ts
|
|
2924
3127
|
import { Project, SyntaxKind } from "ts-morph";
|
|
@@ -3153,7 +3356,7 @@ import { SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
|
3153
3356
|
|
|
3154
3357
|
// src/ts/index.ts
|
|
3155
3358
|
import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
3156
|
-
import { join as
|
|
3359
|
+
import { join as join6, extname as extname2 } from "path";
|
|
3157
3360
|
import { Project as Project2 } from "ts-morph";
|
|
3158
3361
|
|
|
3159
3362
|
// src/utils/logger.ts
|
|
@@ -3268,7 +3471,7 @@ function getAllCodeFiles(dir, files = [], baseDir = dir) {
|
|
|
3268
3471
|
try {
|
|
3269
3472
|
const entries = readdirSync2(dir);
|
|
3270
3473
|
for (const entry of entries) {
|
|
3271
|
-
const fullPath =
|
|
3474
|
+
const fullPath = join6(dir, entry);
|
|
3272
3475
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) {
|
|
3273
3476
|
continue;
|
|
3274
3477
|
}
|
|
@@ -3784,6 +3987,7 @@ function indexProject(cwd) {
|
|
|
3784
3987
|
// src/commands/context.ts
|
|
3785
3988
|
async function context(target, options = {}) {
|
|
3786
3989
|
const { cwd, format } = parseCommandOptions(options);
|
|
3990
|
+
const ctx = options.ctx || "cli";
|
|
3787
3991
|
if (!target) {
|
|
3788
3992
|
throw new Error("Target e obrigatorio. Exemplo: ai-tool context src/components/Button.tsx");
|
|
3789
3993
|
}
|
|
@@ -3791,7 +3995,7 @@ async function context(target, options = {}) {
|
|
|
3791
3995
|
const targetPath = findTargetFile2(target, cwd);
|
|
3792
3996
|
if (!targetPath) {
|
|
3793
3997
|
const allFiles = getAllCodeFiles2(cwd);
|
|
3794
|
-
return formatFileNotFound({ target, allFiles, command: "context" });
|
|
3998
|
+
return formatFileNotFound({ target, allFiles, command: "context", ctx });
|
|
3795
3999
|
}
|
|
3796
4000
|
const project = createProject(cwd);
|
|
3797
4001
|
const absolutePath = resolve2(cwd, targetPath);
|
|
@@ -3800,6 +4004,24 @@ async function context(target, options = {}) {
|
|
|
3800
4004
|
const functions2 = extractFunctions(sourceFile);
|
|
3801
4005
|
const types = extractTypes(sourceFile);
|
|
3802
4006
|
const exports = extractExports(sourceFile);
|
|
4007
|
+
const constants = [];
|
|
4008
|
+
for (const varStatement of sourceFile.getVariableStatements()) {
|
|
4009
|
+
if (!varStatement.isExported()) continue;
|
|
4010
|
+
for (const decl of varStatement.getDeclarations()) {
|
|
4011
|
+
const init = decl.getInitializer();
|
|
4012
|
+
if (init) {
|
|
4013
|
+
const kind = init.getKind();
|
|
4014
|
+
if (kind === SyntaxKind3.ArrowFunction || kind === SyntaxKind3.FunctionExpression) {
|
|
4015
|
+
continue;
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
constants.push({
|
|
4019
|
+
name: decl.getName(),
|
|
4020
|
+
type: simplifyType(decl.getType().getText()),
|
|
4021
|
+
isExported: true
|
|
4022
|
+
});
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
3803
4025
|
const result = {
|
|
3804
4026
|
version: "1.0.0",
|
|
3805
4027
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3808,9 +4030,10 @@ async function context(target, options = {}) {
|
|
|
3808
4030
|
imports,
|
|
3809
4031
|
exports,
|
|
3810
4032
|
functions: functions2,
|
|
3811
|
-
types
|
|
4033
|
+
types,
|
|
4034
|
+
constants: constants.length > 0 ? constants : void 0
|
|
3812
4035
|
};
|
|
3813
|
-
return formatOutput(result, format, formatContextText);
|
|
4036
|
+
return formatOutput(result, format, (r) => formatContextText(r, ctx));
|
|
3814
4037
|
} catch (error) {
|
|
3815
4038
|
const message = error instanceof Error ? error.message : String(error);
|
|
3816
4039
|
throw new Error(`Erro ao executar context: ${message}`);
|
|
@@ -3820,12 +4043,12 @@ var CODE_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".
|
|
|
3820
4043
|
function findTargetFile2(target, cwd) {
|
|
3821
4044
|
const normalizedTarget = target.replace(/\\/g, "/");
|
|
3822
4045
|
const directPath = resolve2(cwd, normalizedTarget);
|
|
3823
|
-
if (
|
|
4046
|
+
if (existsSync7(directPath) && isCodeFile2(directPath)) {
|
|
3824
4047
|
return normalizedTarget;
|
|
3825
4048
|
}
|
|
3826
4049
|
for (const ext of CODE_EXTENSIONS3) {
|
|
3827
4050
|
const withExt = directPath + ext;
|
|
3828
|
-
if (
|
|
4051
|
+
if (existsSync7(withExt)) {
|
|
3829
4052
|
return normalizedTarget + ext;
|
|
3830
4053
|
}
|
|
3831
4054
|
}
|
|
@@ -3855,7 +4078,7 @@ function getAllCodeFiles2(dir, files = [], baseDir = dir) {
|
|
|
3855
4078
|
try {
|
|
3856
4079
|
const entries = readdirSync3(dir);
|
|
3857
4080
|
for (const entry of entries) {
|
|
3858
|
-
const fullPath =
|
|
4081
|
+
const fullPath = join7(dir, entry);
|
|
3859
4082
|
if (shouldIgnore(entry)) {
|
|
3860
4083
|
continue;
|
|
3861
4084
|
}
|
|
@@ -3892,10 +4115,32 @@ async function areaContext(areaName, options = {}) {
|
|
|
3892
4115
|
const cwd = options.cwd || process.cwd();
|
|
3893
4116
|
const format = options.format || "text";
|
|
3894
4117
|
const useCache = options.cache !== false;
|
|
4118
|
+
const ctx = options.ctx || "cli";
|
|
3895
4119
|
if (!areaName) {
|
|
3896
4120
|
throw new Error("Nome da \xE1rea \xE9 obrigat\xF3rio. Exemplo: ai-tool context --area=auth");
|
|
3897
4121
|
}
|
|
3898
4122
|
try {
|
|
4123
|
+
if (!configExists(cwd) || Object.keys(readConfig(cwd).areas).length === 0) {
|
|
4124
|
+
let out = `\u26A0\uFE0F Nenhuma area configurada neste projeto.
|
|
4125
|
+
|
|
4126
|
+
`;
|
|
4127
|
+
out += `O comando area_context requer areas configuradas em .analyze/areas.config.json
|
|
4128
|
+
`;
|
|
4129
|
+
out += `Para configurar:
|
|
4130
|
+
`;
|
|
4131
|
+
out += ` 1. ${hint("areas_init", ctx)} - gerar arquivo de configuracao
|
|
4132
|
+
`;
|
|
4133
|
+
out += ` 2. Edite .analyze/areas.config.json com as areas do projeto
|
|
4134
|
+
|
|
4135
|
+
`;
|
|
4136
|
+
out += `Enquanto isso, use:
|
|
4137
|
+
`;
|
|
4138
|
+
out += ` \u2192 ${hint("context", ctx)} - ver assinaturas de um arquivo especifico
|
|
4139
|
+
`;
|
|
4140
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4141
|
+
`;
|
|
4142
|
+
return out;
|
|
4143
|
+
}
|
|
3899
4144
|
const config = readConfig(cwd);
|
|
3900
4145
|
let index;
|
|
3901
4146
|
if (useCache && isCacheValid(cwd)) {
|
|
@@ -3927,9 +4172,19 @@ async function areaContext(areaName, options = {}) {
|
|
|
3927
4172
|
}
|
|
3928
4173
|
}
|
|
3929
4174
|
if (areaFiles.length === 0) {
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
4175
|
+
const availableAreas = /* @__PURE__ */ new Map();
|
|
4176
|
+
for (const filePath of Object.keys(index.files)) {
|
|
4177
|
+
if (isFileIgnored(filePath, config)) continue;
|
|
4178
|
+
const fileAreas = detectFileAreas(filePath, config);
|
|
4179
|
+
for (const areaId of fileAreas) {
|
|
4180
|
+
availableAreas.set(areaId, (availableAreas.get(areaId) || 0) + 1);
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
const areaList = [...availableAreas.entries()].map(([id, count]) => ({ id, count })).sort((a, b) => b.count - a.count);
|
|
4184
|
+
if (format === "json") {
|
|
4185
|
+
return JSON.stringify({ error: `Area nao encontrada: "${areaName}"`, availableAreas: areaList });
|
|
4186
|
+
}
|
|
4187
|
+
return formatAreaNotFound({ target: areaName, availableAreas: areaList, ctx });
|
|
3933
4188
|
}
|
|
3934
4189
|
const types = [];
|
|
3935
4190
|
const hooks = [];
|
|
@@ -4035,7 +4290,7 @@ async function areaContext(areaName, options = {}) {
|
|
|
4035
4290
|
if (format === "json") {
|
|
4036
4291
|
return JSON.stringify(result, null, 2);
|
|
4037
4292
|
}
|
|
4038
|
-
return formatAreaContextText(result);
|
|
4293
|
+
return formatAreaContextText(result, ctx);
|
|
4039
4294
|
} catch (error) {
|
|
4040
4295
|
const message = error instanceof Error ? error.message : String(error);
|
|
4041
4296
|
throw new Error(`Erro ao executar context --area: ${message}`);
|
|
@@ -4062,7 +4317,7 @@ function findRealAreaIdFromIndex(target, areaFiles, config) {
|
|
|
4062
4317
|
|
|
4063
4318
|
// src/commands/areas.ts
|
|
4064
4319
|
import { readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
4065
|
-
import { join as
|
|
4320
|
+
import { join as join8, extname as extname4 } from "path";
|
|
4066
4321
|
var CODE_EXTENSIONS4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4067
4322
|
var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
4068
4323
|
"node_modules",
|
|
@@ -4079,6 +4334,7 @@ var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
4079
4334
|
async function areas(options = {}) {
|
|
4080
4335
|
const cwd = options.cwd || process.cwd();
|
|
4081
4336
|
const format = options.format || "text";
|
|
4337
|
+
const ctx = options.ctx || "cli";
|
|
4082
4338
|
try {
|
|
4083
4339
|
const config = readConfig(cwd);
|
|
4084
4340
|
const allFiles = getAllCodeFiles3(cwd);
|
|
@@ -4140,7 +4396,7 @@ async function areas(options = {}) {
|
|
|
4140
4396
|
if (format === "json") {
|
|
4141
4397
|
return JSON.stringify(result, null, 2);
|
|
4142
4398
|
}
|
|
4143
|
-
return formatAreasText(result);
|
|
4399
|
+
return formatAreasText(result, ctx);
|
|
4144
4400
|
} catch (error) {
|
|
4145
4401
|
const message = error instanceof Error ? error.message : String(error);
|
|
4146
4402
|
throw new Error(`Erro ao executar areas: ${message}`);
|
|
@@ -4150,7 +4406,7 @@ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
|
|
|
4150
4406
|
try {
|
|
4151
4407
|
const entries = readdirSync4(dir);
|
|
4152
4408
|
for (const entry of entries) {
|
|
4153
|
-
const fullPath =
|
|
4409
|
+
const fullPath = join8(dir, entry);
|
|
4154
4410
|
if (IGNORED_DIRS2.has(entry) || entry.startsWith(".")) {
|
|
4155
4411
|
continue;
|
|
4156
4412
|
}
|
|
@@ -4175,7 +4431,7 @@ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
|
|
|
4175
4431
|
|
|
4176
4432
|
// src/commands/area.ts
|
|
4177
4433
|
import { readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
4178
|
-
import { join as
|
|
4434
|
+
import { join as join9, extname as extname5 } from "path";
|
|
4179
4435
|
var CODE_EXTENSIONS5 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4180
4436
|
var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
|
|
4181
4437
|
"node_modules",
|
|
@@ -4224,10 +4480,32 @@ async function area(target, options = {}) {
|
|
|
4224
4480
|
const format = options.format || "text";
|
|
4225
4481
|
const filterType = options.type;
|
|
4226
4482
|
const full = options.full ?? false;
|
|
4483
|
+
const ctx = options.ctx || "cli";
|
|
4227
4484
|
if (!target) {
|
|
4228
4485
|
throw new Error("Nome da \xE1rea \xE9 obrigat\xF3rio. Exemplo: ai-tool area auth");
|
|
4229
4486
|
}
|
|
4230
4487
|
try {
|
|
4488
|
+
if (!configExists(cwd) || Object.keys(readConfig(cwd).areas).length === 0) {
|
|
4489
|
+
let out = `\u26A0\uFE0F Nenhuma area configurada neste projeto.
|
|
4490
|
+
|
|
4491
|
+
`;
|
|
4492
|
+
out += `O comando area requer areas configuradas em .analyze/areas.config.json
|
|
4493
|
+
`;
|
|
4494
|
+
out += `Para configurar:
|
|
4495
|
+
`;
|
|
4496
|
+
out += ` 1. ${hint("areas_init", ctx)} - gerar arquivo de configuracao
|
|
4497
|
+
`;
|
|
4498
|
+
out += ` 2. Edite .analyze/areas.config.json com as areas do projeto
|
|
4499
|
+
|
|
4500
|
+
`;
|
|
4501
|
+
out += `Enquanto isso, use:
|
|
4502
|
+
`;
|
|
4503
|
+
out += ` \u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
4504
|
+
`;
|
|
4505
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4506
|
+
`;
|
|
4507
|
+
return out;
|
|
4508
|
+
}
|
|
4231
4509
|
const config = readConfig(cwd);
|
|
4232
4510
|
const allFiles = getAllCodeFiles4(cwd);
|
|
4233
4511
|
const resolvedTarget = resolveAreaId(target, config, allFiles);
|
|
@@ -4257,7 +4535,7 @@ async function area(target, options = {}) {
|
|
|
4257
4535
|
}
|
|
4258
4536
|
if (areaFiles.length === 0) {
|
|
4259
4537
|
const availableAreas = getAvailableAreas(filteredFiles, config);
|
|
4260
|
-
return formatAreaNotFound2(target, availableAreas);
|
|
4538
|
+
return formatAreaNotFound2(target, availableAreas, ctx);
|
|
4261
4539
|
}
|
|
4262
4540
|
const byCategory = {};
|
|
4263
4541
|
const categories = {};
|
|
@@ -4294,7 +4572,7 @@ async function area(target, options = {}) {
|
|
|
4294
4572
|
if (format === "json") {
|
|
4295
4573
|
return JSON.stringify(result, null, 2);
|
|
4296
4574
|
}
|
|
4297
|
-
const output = formatAreaDetailText(result, { full, filterType });
|
|
4575
|
+
const output = formatAreaDetailText(result, { full, filterType }, ctx);
|
|
4298
4576
|
return nameConversionMsg + output;
|
|
4299
4577
|
} catch (error) {
|
|
4300
4578
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -4329,14 +4607,14 @@ function getAvailableAreas(allFiles, config) {
|
|
|
4329
4607
|
}
|
|
4330
4608
|
return [...areaCounts.entries()].map(([id, count]) => ({ id, count })).sort((a, b) => b.count - a.count);
|
|
4331
4609
|
}
|
|
4332
|
-
function formatAreaNotFound2(target, availableAreas) {
|
|
4333
|
-
return formatAreaNotFound({ target, availableAreas });
|
|
4610
|
+
function formatAreaNotFound2(target, availableAreas, ctx = "cli") {
|
|
4611
|
+
return formatAreaNotFound({ target, availableAreas, ctx });
|
|
4334
4612
|
}
|
|
4335
4613
|
function getAllCodeFiles4(dir, files = [], baseDir = dir) {
|
|
4336
4614
|
try {
|
|
4337
4615
|
const entries = readdirSync5(dir);
|
|
4338
4616
|
for (const entry of entries) {
|
|
4339
|
-
const fullPath =
|
|
4617
|
+
const fullPath = join9(dir, entry);
|
|
4340
4618
|
if (IGNORED_DIRS3.has(entry) || entry.startsWith(".")) {
|
|
4341
4619
|
continue;
|
|
4342
4620
|
}
|
|
@@ -4361,7 +4639,7 @@ function getAllCodeFiles4(dir, files = [], baseDir = dir) {
|
|
|
4361
4639
|
|
|
4362
4640
|
// src/commands/areas-init.ts
|
|
4363
4641
|
import { readdirSync as readdirSync6, statSync as statSync6 } from "fs";
|
|
4364
|
-
import { join as
|
|
4642
|
+
import { join as join10, extname as extname6 } from "path";
|
|
4365
4643
|
var CODE_EXTENSIONS6 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4366
4644
|
var IGNORED_DIRS4 = /* @__PURE__ */ new Set([
|
|
4367
4645
|
"node_modules",
|
|
@@ -4377,7 +4655,7 @@ var IGNORED_DIRS4 = /* @__PURE__ */ new Set([
|
|
|
4377
4655
|
]);
|
|
4378
4656
|
function createInitialConfig(files) {
|
|
4379
4657
|
const suggestedIgnore = detectSuggestedIgnorePatterns(files);
|
|
4380
|
-
const framework =
|
|
4658
|
+
const framework = detectFramework2(files);
|
|
4381
4659
|
return {
|
|
4382
4660
|
$schema: "./areas.schema.json",
|
|
4383
4661
|
version: "1.0.0",
|
|
@@ -4391,7 +4669,7 @@ function createInitialConfig(files) {
|
|
|
4391
4669
|
}
|
|
4392
4670
|
};
|
|
4393
4671
|
}
|
|
4394
|
-
function
|
|
4672
|
+
function detectFramework2(files) {
|
|
4395
4673
|
const hasAppDir = files.some((f) => f.startsWith("app/"));
|
|
4396
4674
|
const hasPagesDir = files.some((f) => f.startsWith("pages/"));
|
|
4397
4675
|
const hasSrcDir = files.some((f) => f.startsWith("src/"));
|
|
@@ -4546,7 +4824,7 @@ Ou edite manualmente o arquivo existente.
|
|
|
4546
4824
|
const allFiles = getAllCodeFiles5(cwd);
|
|
4547
4825
|
const newConfig = createInitialConfig(allFiles);
|
|
4548
4826
|
writeConfig(cwd, newConfig);
|
|
4549
|
-
const framework =
|
|
4827
|
+
const framework = detectFramework2(allFiles);
|
|
4550
4828
|
const frameworkName = {
|
|
4551
4829
|
"nextjs-app": "Next.js (App Router)",
|
|
4552
4830
|
"nextjs-pages": "Next.js (Pages Router)",
|
|
@@ -4650,7 +4928,7 @@ function getAllCodeFiles5(dir, files = [], baseDir = dir) {
|
|
|
4650
4928
|
try {
|
|
4651
4929
|
const entries = readdirSync6(dir);
|
|
4652
4930
|
for (const entry of entries) {
|
|
4653
|
-
const fullPath =
|
|
4931
|
+
const fullPath = join10(dir, entry);
|
|
4654
4932
|
if (IGNORED_DIRS4.has(entry) || entry.startsWith(".")) {
|
|
4655
4933
|
continue;
|
|
4656
4934
|
}
|
|
@@ -4681,13 +4959,42 @@ async function functions(options = {}) {
|
|
|
4681
4959
|
const format = options.format || "text";
|
|
4682
4960
|
const useCache = options.cache !== false;
|
|
4683
4961
|
const filterTrigger = options.trigger;
|
|
4962
|
+
const ctx = options.ctx || "cli";
|
|
4684
4963
|
if (!isFirebaseProject(cwd)) {
|
|
4685
|
-
const errorMsg = "Este
|
|
4686
|
-
|
|
4964
|
+
const errorMsg = "Este nao e um projeto Firebase (nao encontrou .firebaserc ou firebase.json)";
|
|
4965
|
+
if (format === "json") {
|
|
4966
|
+
return JSON.stringify({ error: errorMsg });
|
|
4967
|
+
}
|
|
4968
|
+
let out = `\u274C ${errorMsg}
|
|
4969
|
+
`;
|
|
4970
|
+
out += `
|
|
4971
|
+
\u{1F4A1} Comandos disponiveis para este projeto:
|
|
4972
|
+
`;
|
|
4973
|
+
out += ` \u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
4974
|
+
`;
|
|
4975
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4976
|
+
`;
|
|
4977
|
+
out += ` \u2192 ${hint("areas", ctx)} - listar areas funcionais
|
|
4978
|
+
`;
|
|
4979
|
+
return out;
|
|
4687
4980
|
}
|
|
4688
4981
|
if (!hasFirebaseFunctions(cwd)) {
|
|
4689
|
-
const errorMsg = "Projeto Firebase sem Cloud Functions
|
|
4690
|
-
|
|
4982
|
+
const errorMsg = "Projeto Firebase detectado, mas sem Cloud Functions";
|
|
4983
|
+
if (format === "json") {
|
|
4984
|
+
return JSON.stringify({ error: errorMsg });
|
|
4985
|
+
}
|
|
4986
|
+
let out = `\u274C ${errorMsg}
|
|
4987
|
+
`;
|
|
4988
|
+
out += ` Nao foi encontrado functions/src/index.ts
|
|
4989
|
+
|
|
4990
|
+
`;
|
|
4991
|
+
out += `\u{1F4A1} Para adicionar Cloud Functions:
|
|
4992
|
+
`;
|
|
4993
|
+
out += ` \u2192 firebase init functions
|
|
4994
|
+
`;
|
|
4995
|
+
out += ` \u2192 Documentacao: https://firebase.google.com/docs/functions
|
|
4996
|
+
`;
|
|
4997
|
+
return out;
|
|
4691
4998
|
}
|
|
4692
4999
|
try {
|
|
4693
5000
|
let index;
|
|
@@ -4754,13 +5061,13 @@ async function functions(options = {}) {
|
|
|
4754
5061
|
if (format === "json") {
|
|
4755
5062
|
return JSON.stringify(result, null, 2);
|
|
4756
5063
|
}
|
|
4757
|
-
return formatFunctionsText(result);
|
|
5064
|
+
return formatFunctionsText(result, ctx);
|
|
4758
5065
|
} catch (error) {
|
|
4759
5066
|
const message = error instanceof Error ? error.message : String(error);
|
|
4760
5067
|
throw new Error(`Erro ao executar functions: ${message}`);
|
|
4761
5068
|
}
|
|
4762
5069
|
}
|
|
4763
|
-
function formatFunctionsText(result) {
|
|
5070
|
+
function formatFunctionsText(result, ctx = "cli") {
|
|
4764
5071
|
let out = "";
|
|
4765
5072
|
out += `
|
|
4766
5073
|
`;
|
|
@@ -4777,42 +5084,35 @@ function formatFunctionsText(result) {
|
|
|
4777
5084
|
`;
|
|
4778
5085
|
out += ` Exportadas: ${result.summary.exported}
|
|
4779
5086
|
`;
|
|
4780
|
-
if (result.summary.total > 0) {
|
|
4781
|
-
out += `
|
|
4782
|
-
\u{1F4A1} Filtros dispon\xEDveis:
|
|
4783
|
-
`;
|
|
4784
|
-
out += ` ai-tool functions --trigger=onCall
|
|
4785
|
-
`;
|
|
4786
|
-
out += ` ai-tool functions --trigger=onDocumentCreated
|
|
4787
|
-
`;
|
|
4788
|
-
}
|
|
4789
5087
|
out += `
|
|
4790
5088
|
`;
|
|
4791
5089
|
if (result.summary.total === 0) {
|
|
4792
5090
|
out += ` \u26A0\uFE0F NENHUMA CLOUD FUNCTION DETECTADA
|
|
4793
5091
|
|
|
4794
5092
|
`;
|
|
4795
|
-
out += `
|
|
4796
|
-
`;
|
|
4797
|
-
out += ` 1. O projeto n\xE3o \xE9 Firebase (n\xE3o encontrou .firebaserc ou firebase.json)
|
|
5093
|
+
out += ` Possiveis causas:
|
|
4798
5094
|
`;
|
|
4799
|
-
out += `
|
|
5095
|
+
out += ` 1. O projeto nao e Firebase (nao encontrou .firebaserc ou firebase.json)
|
|
4800
5096
|
`;
|
|
4801
|
-
out += `
|
|
5097
|
+
out += ` 2. Nao ha arquivo functions/src/index.ts
|
|
4802
5098
|
`;
|
|
4803
|
-
out += `
|
|
5099
|
+
out += ` 3. Os triggers nao usam padroes v2 (onCall, onDocumentCreated, etc)
|
|
4804
5100
|
`;
|
|
4805
|
-
out += `
|
|
5101
|
+
out += ` 4. O cache esta desatualizado
|
|
4806
5102
|
|
|
4807
5103
|
`;
|
|
4808
|
-
out += `
|
|
5104
|
+
out += ` Padroes suportados:
|
|
4809
5105
|
`;
|
|
4810
5106
|
out += ` export const minhaFunc = onCall((request) => { ... })
|
|
4811
5107
|
`;
|
|
4812
5108
|
out += ` export const minhaFunc = onDocumentCreated("path", (event) => { ... })
|
|
4813
5109
|
|
|
4814
5110
|
`;
|
|
4815
|
-
out +=
|
|
5111
|
+
out += `\u{1F4A1} Dicas:
|
|
5112
|
+
`;
|
|
5113
|
+
out += ` \u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
5114
|
+
`;
|
|
5115
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4816
5116
|
`;
|
|
4817
5117
|
return out;
|
|
4818
5118
|
}
|
|
@@ -4882,6 +5182,7 @@ function formatFunctionsText(result) {
|
|
|
4882
5182
|
out += `
|
|
4883
5183
|
`;
|
|
4884
5184
|
}
|
|
5185
|
+
out += nextSteps("functions", ctx);
|
|
4885
5186
|
return out;
|
|
4886
5187
|
}
|
|
4887
5188
|
function getTriggerIcon(trigger) {
|
|
@@ -4899,6 +5200,8 @@ function getTriggerIcon(trigger) {
|
|
|
4899
5200
|
}
|
|
4900
5201
|
|
|
4901
5202
|
// src/commands/find.ts
|
|
5203
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
5204
|
+
import { join as join11 } from "path";
|
|
4902
5205
|
async function find(query, options = {}) {
|
|
4903
5206
|
const { cwd, format } = parseCommandOptions(options);
|
|
4904
5207
|
const filterType = options.type || "all";
|
|
@@ -4906,6 +5209,7 @@ async function find(query, options = {}) {
|
|
|
4906
5209
|
const defOnly = options.def ?? false;
|
|
4907
5210
|
const refsOnly = options.refs ?? false;
|
|
4908
5211
|
const useCache = options.cache !== false;
|
|
5212
|
+
const ctx = options.ctx || "cli";
|
|
4909
5213
|
const listAllMode = !query && defOnly && filterType && filterType !== "all";
|
|
4910
5214
|
if (!query && !listAllMode) {
|
|
4911
5215
|
throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool find useAuth\n Ou use --def para listar todos de um tipo: ai-tool find --type=trigger --def");
|
|
@@ -4946,10 +5250,22 @@ async function find(query, options = {}) {
|
|
|
4946
5250
|
}
|
|
4947
5251
|
}
|
|
4948
5252
|
if (allowedFiles.size === 0) {
|
|
4949
|
-
|
|
5253
|
+
const availableAreas = /* @__PURE__ */ new Map();
|
|
5254
|
+
for (const filePath of Object.keys(index.files)) {
|
|
5255
|
+
if (isFileIgnored(filePath, config)) continue;
|
|
5256
|
+
const fileAreas = detectFileAreas(filePath, config);
|
|
5257
|
+
for (const areaId of fileAreas) {
|
|
5258
|
+
availableAreas.set(areaId, (availableAreas.get(areaId) || 0) + 1);
|
|
5259
|
+
}
|
|
5260
|
+
}
|
|
5261
|
+
const areaList = [...availableAreas.entries()].map(([id, count]) => ({ id, count })).sort((a, b) => b.count - a.count);
|
|
5262
|
+
if (format === "json") {
|
|
5263
|
+
return JSON.stringify({ error: `Nenhum arquivo encontrado na area "${filterArea}"`, availableAreas: areaList });
|
|
5264
|
+
}
|
|
5265
|
+
return formatAreaNotFound({ target: filterArea, availableAreas: areaList, ctx });
|
|
4950
5266
|
}
|
|
4951
5267
|
}
|
|
4952
|
-
const matches = searchInIndex(index, query, filterType, allowedFiles);
|
|
5268
|
+
const matches = searchInIndex(index, query, filterType, allowedFiles, cwd);
|
|
4953
5269
|
let definition = null;
|
|
4954
5270
|
let references = [];
|
|
4955
5271
|
for (const match of matches) {
|
|
@@ -4991,13 +5307,14 @@ async function find(query, options = {}) {
|
|
|
4991
5307
|
},
|
|
4992
5308
|
fromCache
|
|
4993
5309
|
};
|
|
4994
|
-
|
|
5310
|
+
const allSymbolNames = Object.keys(index.symbolsByName);
|
|
5311
|
+
return formatOutput(result, format, (r) => formatFindText(r, ctx, allSymbolNames), fromCache);
|
|
4995
5312
|
} catch (error) {
|
|
4996
5313
|
const message = error instanceof Error ? error.message : String(error);
|
|
4997
5314
|
throw new Error(`Erro ao executar find: ${message}`);
|
|
4998
5315
|
}
|
|
4999
5316
|
}
|
|
5000
|
-
function searchInIndex(index, query, filterType, allowedFiles) {
|
|
5317
|
+
function searchInIndex(index, query, filterType, allowedFiles, cwd) {
|
|
5001
5318
|
const matches = [];
|
|
5002
5319
|
const queryLower = query?.toLowerCase() || "";
|
|
5003
5320
|
const processedSymbols = /* @__PURE__ */ new Set();
|
|
@@ -5050,6 +5367,45 @@ function searchInIndex(index, query, filterType, allowedFiles) {
|
|
|
5050
5367
|
}
|
|
5051
5368
|
}
|
|
5052
5369
|
}
|
|
5370
|
+
if (!listAllMode && query) {
|
|
5371
|
+
const importFiles = matches.filter((m) => m.matchType === "import").map((m) => m.file);
|
|
5372
|
+
const baseCwd = cwd || "";
|
|
5373
|
+
const MAX_USAGE_FILES = 10;
|
|
5374
|
+
const MAX_USAGES_PER_FILE = 3;
|
|
5375
|
+
for (const filePath of importFiles.slice(0, MAX_USAGE_FILES)) {
|
|
5376
|
+
try {
|
|
5377
|
+
const fullPath = baseCwd ? join11(baseCwd, filePath) : filePath;
|
|
5378
|
+
const content = readFileSync4(fullPath, "utf-8");
|
|
5379
|
+
const lines = content.split("\n");
|
|
5380
|
+
const usageRegex = new RegExp(`\\b${escapeRegex(query)}\\b`);
|
|
5381
|
+
let usagesFound = 0;
|
|
5382
|
+
for (let i = 0; i < lines.length && usagesFound < MAX_USAGES_PER_FILE; i++) {
|
|
5383
|
+
const line = lines[i].trim();
|
|
5384
|
+
if (line.startsWith("import ") || line.startsWith("export ") || line.startsWith("//") || line.startsWith("*") || line.startsWith("/*")) {
|
|
5385
|
+
continue;
|
|
5386
|
+
}
|
|
5387
|
+
if (usageRegex.test(line)) {
|
|
5388
|
+
const key = `usage:${filePath}:${i + 1}:${query}`;
|
|
5389
|
+
if (processedSymbols.has(key)) continue;
|
|
5390
|
+
processedSymbols.add(key);
|
|
5391
|
+
const fileData = index.files[filePath];
|
|
5392
|
+
const codeLine = line.length > 100 ? line.substring(0, 100) + "..." : line;
|
|
5393
|
+
matches.push({
|
|
5394
|
+
file: filePath,
|
|
5395
|
+
line: i + 1,
|
|
5396
|
+
column: 0,
|
|
5397
|
+
code: codeLine,
|
|
5398
|
+
matchType: "usage",
|
|
5399
|
+
symbolType: inferSymbolTypeFromName(query),
|
|
5400
|
+
category: fileData?.category || "other"
|
|
5401
|
+
});
|
|
5402
|
+
usagesFound++;
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
} catch {
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5053
5409
|
matches.sort((a, b) => {
|
|
5054
5410
|
const order = { definition: 0, import: 1, usage: 2 };
|
|
5055
5411
|
const orderDiff = order[a.matchType] - order[b.matchType];
|
|
@@ -5058,6 +5414,9 @@ function searchInIndex(index, query, filterType, allowedFiles) {
|
|
|
5058
5414
|
});
|
|
5059
5415
|
return matches;
|
|
5060
5416
|
}
|
|
5417
|
+
function escapeRegex(str) {
|
|
5418
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5419
|
+
}
|
|
5061
5420
|
function matchesType(kind, filter) {
|
|
5062
5421
|
if (filter === "all") return true;
|
|
5063
5422
|
switch (filter) {
|
|
@@ -5118,6 +5477,13 @@ export {
|
|
|
5118
5477
|
categoryIcons,
|
|
5119
5478
|
isEntryPoint,
|
|
5120
5479
|
isCodeFile,
|
|
5480
|
+
hint,
|
|
5481
|
+
nextSteps,
|
|
5482
|
+
recoveryHint,
|
|
5483
|
+
levenshteinDistance,
|
|
5484
|
+
findSimilar,
|
|
5485
|
+
findBestMatch,
|
|
5486
|
+
extractFileName,
|
|
5121
5487
|
isFirebaseProject,
|
|
5122
5488
|
hasFirebaseFunctions,
|
|
5123
5489
|
isExportedCloudFunction,
|
|
@@ -5146,11 +5512,6 @@ export {
|
|
|
5146
5512
|
map,
|
|
5147
5513
|
dead,
|
|
5148
5514
|
deadFix,
|
|
5149
|
-
levenshteinDistance,
|
|
5150
|
-
findSimilar,
|
|
5151
|
-
findBestMatch,
|
|
5152
|
-
extractFileName,
|
|
5153
|
-
COMMAND_REFERENCE,
|
|
5154
5515
|
formatFileNotFound,
|
|
5155
5516
|
formatAreaNotFound,
|
|
5156
5517
|
formatMissingTarget,
|