@justmpm/ai-tool 0.7.0 → 0.7.2
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.
|
@@ -379,12 +379,93 @@ function formatDeadText(result) {
|
|
|
379
379
|
out += `
|
|
380
380
|
`;
|
|
381
381
|
}
|
|
382
|
-
out += `\u{1F4A1}
|
|
382
|
+
out += `\u{1F4A1} COMO RESOLVER
|
|
383
|
+
|
|
384
|
+
`;
|
|
385
|
+
out += ` 1. Falsos positivos? Adicione ao .analyze/areas.config.json:
|
|
386
|
+
`;
|
|
387
|
+
out += ` { "ignore": ["functions/lib/**", "**/*.test.ts"] }
|
|
388
|
+
|
|
389
|
+
`;
|
|
390
|
+
out += ` 2. Remover automaticamente:
|
|
383
391
|
`;
|
|
384
|
-
out += `
|
|
392
|
+
out += ` npx knip --fix
|
|
393
|
+
|
|
394
|
+
`;
|
|
395
|
+
out += ` 3. Ver detalhes em JSON:
|
|
396
|
+
`;
|
|
397
|
+
out += ` ai-tool dead --format=json
|
|
385
398
|
`;
|
|
399
|
+
const suggestions = generateIgnoreSuggestions(result);
|
|
400
|
+
if (suggestions.length > 0) {
|
|
401
|
+
out += `
|
|
402
|
+
\u{1F3AF} SUGEST\xD5ES INTELIGENTES
|
|
403
|
+
|
|
404
|
+
`;
|
|
405
|
+
for (const suggestion of suggestions) {
|
|
406
|
+
out += ` ${suggestion.icon} ${suggestion.pattern}
|
|
407
|
+
`;
|
|
408
|
+
out += ` Motivo: ${suggestion.reason}
|
|
409
|
+
`;
|
|
410
|
+
out += ` Arquivos afetados: ${suggestion.count}
|
|
411
|
+
|
|
412
|
+
`;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
386
415
|
return out;
|
|
387
416
|
}
|
|
417
|
+
function generateIgnoreSuggestions(result) {
|
|
418
|
+
const suggestions = [];
|
|
419
|
+
const files = result.files.map((f) => f.path);
|
|
420
|
+
const libFiles = files.filter((f) => f.includes("functions/lib/"));
|
|
421
|
+
if (libFiles.length > 0) {
|
|
422
|
+
suggestions.push({
|
|
423
|
+
icon: "\u{1F4E6}",
|
|
424
|
+
pattern: "functions/lib/**",
|
|
425
|
+
reason: "Build compilado do Firebase Functions",
|
|
426
|
+
count: libFiles.length
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
const testFiles = files.filter((f) => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f));
|
|
430
|
+
if (testFiles.length > 3) {
|
|
431
|
+
suggestions.push({
|
|
432
|
+
icon: "\u{1F9EA}",
|
|
433
|
+
pattern: "**/*.(test|spec).(ts|tsx|js|jsx)",
|
|
434
|
+
reason: "Arquivos de teste geralmente s\xE3o entry points pr\xF3prios",
|
|
435
|
+
count: testFiles.length
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
const configFiles = files.filter(
|
|
439
|
+
(f) => f.includes("vite.config") || f.includes("next.config") || f.includes("tailwind.config") || f.includes("jest.config") || f.includes("eslint.config")
|
|
440
|
+
);
|
|
441
|
+
if (configFiles.length > 0) {
|
|
442
|
+
suggestions.push({
|
|
443
|
+
icon: "\u2699\uFE0F",
|
|
444
|
+
pattern: "**/*.config.(ts|js|mjs|cjs)",
|
|
445
|
+
reason: "Arquivos de configura\xE7\xE3o s\xE3o entry points",
|
|
446
|
+
count: configFiles.length
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
const dtsFiles = files.filter((f) => f.endsWith(".d.ts"));
|
|
450
|
+
if (dtsFiles.length > 0) {
|
|
451
|
+
suggestions.push({
|
|
452
|
+
icon: "\u{1F4D8}",
|
|
453
|
+
pattern: "**/*.d.ts",
|
|
454
|
+
reason: "Arquivos de defini\xE7\xE3o TypeScript",
|
|
455
|
+
count: dtsFiles.length
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
const scriptFiles = files.filter((f) => f.startsWith("scripts/") || f.includes("/scripts/"));
|
|
459
|
+
if (scriptFiles.length > 0) {
|
|
460
|
+
suggestions.push({
|
|
461
|
+
icon: "\u{1F4DC}",
|
|
462
|
+
pattern: "scripts/**",
|
|
463
|
+
reason: "Scripts de automa\xE7\xE3o s\xE3o entry points",
|
|
464
|
+
count: scriptFiles.length
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return suggestions;
|
|
468
|
+
}
|
|
388
469
|
function formatImpactText(result) {
|
|
389
470
|
let out = "";
|
|
390
471
|
out += `
|
|
@@ -893,20 +974,32 @@ function formatFindText(result) {
|
|
|
893
974
|
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
|
|
894
975
|
|
|
895
976
|
`;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
977
|
+
if (result.query) {
|
|
978
|
+
out += `\u{1F50D} "${result.query}"`;
|
|
979
|
+
if (result.filters.type) {
|
|
980
|
+
out += ` [type: ${result.filters.type}]`;
|
|
981
|
+
}
|
|
982
|
+
if (result.filters.area) {
|
|
983
|
+
out += ` [area: ${result.filters.area}]`;
|
|
984
|
+
}
|
|
985
|
+
out += `
|
|
986
|
+
|
|
987
|
+
`;
|
|
988
|
+
} else {
|
|
989
|
+
out += `\u{1F4CB} Listando todos os s\xEDmbolos do tipo: ${result.filters.type || "all"}
|
|
990
|
+
|
|
991
|
+
`;
|
|
902
992
|
}
|
|
903
|
-
|
|
993
|
+
if (!result.definition && result.references.length === 0 && result.summary.definitions === 0) {
|
|
994
|
+
if (result.query) {
|
|
995
|
+
out += `\u274C Nenhum resultado encontrado para "${result.query}"
|
|
904
996
|
|
|
905
997
|
`;
|
|
906
|
-
|
|
907
|
-
|
|
998
|
+
} else {
|
|
999
|
+
out += `\u274C Nenhum s\xEDmbolo do tipo "${result.filters.type}" encontrado
|
|
908
1000
|
|
|
909
1001
|
`;
|
|
1002
|
+
}
|
|
910
1003
|
out += `\u{1F4A1} Dicas:
|
|
911
1004
|
`;
|
|
912
1005
|
out += ` \u2022 Verifique a ortografia
|
|
@@ -1234,25 +1327,52 @@ var GRAPH_FILE = "graph.json";
|
|
|
1234
1327
|
var MAP_FILE = "map.json";
|
|
1235
1328
|
var DEAD_FILE = "dead.json";
|
|
1236
1329
|
var SYMBOLS_FILE = "symbols.json";
|
|
1330
|
+
var HASH_IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
1331
|
+
"node_modules",
|
|
1332
|
+
".git",
|
|
1333
|
+
".next",
|
|
1334
|
+
"dist",
|
|
1335
|
+
"build",
|
|
1336
|
+
".analyze",
|
|
1337
|
+
".vercel",
|
|
1338
|
+
".turbo",
|
|
1339
|
+
".cache",
|
|
1340
|
+
"coverage",
|
|
1341
|
+
"functions/lib",
|
|
1342
|
+
"lib",
|
|
1343
|
+
".output",
|
|
1344
|
+
"out",
|
|
1345
|
+
".firebase"
|
|
1346
|
+
]);
|
|
1237
1347
|
function calculateFilesHash(cwd) {
|
|
1238
|
-
const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1239
|
-
|
|
1240
|
-
|
|
1348
|
+
const extensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
1349
|
+
let hashAccumulator = 0;
|
|
1350
|
+
let fileCount = 0;
|
|
1351
|
+
let maxTimestamp = 0;
|
|
1352
|
+
const MAX_DEPTH = 6;
|
|
1353
|
+
function scanDir(dir, depth) {
|
|
1354
|
+
if (depth > MAX_DEPTH) return;
|
|
1241
1355
|
try {
|
|
1242
1356
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
1243
1357
|
for (const entry of entries) {
|
|
1358
|
+
if (entry.name.startsWith(".")) continue;
|
|
1244
1359
|
const fullPath = join2(dir, entry.name);
|
|
1245
1360
|
if (entry.isDirectory()) {
|
|
1246
|
-
if (
|
|
1247
|
-
|
|
1361
|
+
if (HASH_IGNORED_DIRS.has(entry.name)) continue;
|
|
1362
|
+
if (depth < MAX_DEPTH) {
|
|
1363
|
+
scanDir(fullPath, depth + 1);
|
|
1248
1364
|
}
|
|
1249
|
-
scanDir(fullPath);
|
|
1250
1365
|
} else if (entry.isFile()) {
|
|
1251
1366
|
const ext = extname(entry.name).toLowerCase();
|
|
1252
|
-
if (extensions.
|
|
1367
|
+
if (extensions.has(ext)) {
|
|
1253
1368
|
try {
|
|
1254
1369
|
const stat = statSync(fullPath);
|
|
1255
|
-
|
|
1370
|
+
const mtime = stat.mtimeMs;
|
|
1371
|
+
hashAccumulator ^= Math.floor(mtime);
|
|
1372
|
+
fileCount++;
|
|
1373
|
+
if (mtime > maxTimestamp) {
|
|
1374
|
+
maxTimestamp = mtime;
|
|
1375
|
+
}
|
|
1256
1376
|
} catch {
|
|
1257
1377
|
}
|
|
1258
1378
|
}
|
|
@@ -1261,17 +1381,16 @@ function calculateFilesHash(cwd) {
|
|
|
1261
1381
|
} catch {
|
|
1262
1382
|
}
|
|
1263
1383
|
}
|
|
1264
|
-
scanDir(cwd);
|
|
1384
|
+
scanDir(cwd, 0);
|
|
1265
1385
|
try {
|
|
1266
1386
|
const configPath = join2(cwd, CACHE_DIR, "areas.config.json");
|
|
1267
1387
|
if (existsSync2(configPath)) {
|
|
1268
1388
|
const stat = statSync(configPath);
|
|
1269
|
-
|
|
1389
|
+
hashAccumulator ^= Math.floor(stat.mtimeMs);
|
|
1270
1390
|
}
|
|
1271
1391
|
} catch {
|
|
1272
1392
|
}
|
|
1273
|
-
|
|
1274
|
-
return `${timestamps.length}-${Math.floor(sum)}`;
|
|
1393
|
+
return `${fileCount}-${hashAccumulator}-${maxTimestamp}`;
|
|
1275
1394
|
}
|
|
1276
1395
|
function getCacheDir(cwd) {
|
|
1277
1396
|
return join2(cwd, CACHE_DIR);
|
|
@@ -2615,27 +2734,49 @@ function findCircularFromGraph(graph) {
|
|
|
2615
2734
|
return cycles;
|
|
2616
2735
|
}
|
|
2617
2736
|
function findTargetFile(target, allFiles) {
|
|
2618
|
-
const normalizedTarget = target.replace(/\\/g, "/");
|
|
2737
|
+
const normalizedTarget = target.replace(/\\/g, "/").toLowerCase();
|
|
2619
2738
|
if (allFiles.includes(normalizedTarget)) {
|
|
2620
2739
|
return normalizedTarget;
|
|
2621
2740
|
}
|
|
2622
|
-
const
|
|
2741
|
+
const exactMatch = allFiles.find((f) => f.toLowerCase() === normalizedTarget);
|
|
2742
|
+
if (exactMatch) {
|
|
2743
|
+
return exactMatch;
|
|
2744
|
+
}
|
|
2745
|
+
const targetParts = normalizedTarget.split("/");
|
|
2746
|
+
const targetName = targetParts.pop() || "";
|
|
2623
2747
|
const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
2748
|
+
const targetDir = targetParts.join("/");
|
|
2624
2749
|
const matches = [];
|
|
2625
2750
|
for (const file of allFiles) {
|
|
2626
|
-
const
|
|
2751
|
+
const fileLower = file.toLowerCase();
|
|
2752
|
+
const fileParts = fileLower.split("/");
|
|
2753
|
+
const fileName = fileParts.pop() || "";
|
|
2627
2754
|
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2755
|
+
const fileDir = fileParts.join("/");
|
|
2756
|
+
if (fileLower === normalizedTarget) {
|
|
2757
|
+
matches.push({ file, priority: 1 });
|
|
2758
|
+
} else if (fileNameNoExt === targetNameNoExt) {
|
|
2759
|
+
if (targetDir && fileDir.includes(targetDir)) {
|
|
2760
|
+
matches.push({ file, priority: 2 });
|
|
2761
|
+
} else if (targetDir && normalizedTarget.includes(fileDir)) {
|
|
2762
|
+
matches.push({ file, priority: 3 });
|
|
2763
|
+
} else {
|
|
2764
|
+
matches.push({ file, priority: 4 });
|
|
2765
|
+
}
|
|
2766
|
+
} else if (fileLower.includes(normalizedTarget)) {
|
|
2767
|
+
matches.push({ file, priority: 5 });
|
|
2632
2768
|
}
|
|
2633
2769
|
}
|
|
2634
|
-
if (matches.length ===
|
|
2635
|
-
|
|
2770
|
+
if (matches.length === 0) {
|
|
2771
|
+
for (const file of allFiles) {
|
|
2772
|
+
if (file.toLowerCase().includes(targetNameNoExt)) {
|
|
2773
|
+
matches.push({ file, priority: 6 });
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2636
2776
|
}
|
|
2637
|
-
if (matches.length >
|
|
2638
|
-
|
|
2777
|
+
if (matches.length > 0) {
|
|
2778
|
+
matches.sort((a, b) => a.priority - b.priority);
|
|
2779
|
+
return matches[0].file;
|
|
2639
2780
|
}
|
|
2640
2781
|
return null;
|
|
2641
2782
|
}
|
|
@@ -2868,27 +3009,49 @@ function findRelatedTests(targetPath, allFiles) {
|
|
|
2868
3009
|
return tests;
|
|
2869
3010
|
}
|
|
2870
3011
|
function findTargetFile2(target, allFiles) {
|
|
2871
|
-
const normalizedTarget = target.replace(/\\/g, "/");
|
|
3012
|
+
const normalizedTarget = target.replace(/\\/g, "/").toLowerCase();
|
|
2872
3013
|
if (allFiles.includes(normalizedTarget)) {
|
|
2873
3014
|
return normalizedTarget;
|
|
2874
3015
|
}
|
|
2875
|
-
const
|
|
3016
|
+
const exactMatch = allFiles.find((f) => f.toLowerCase() === normalizedTarget);
|
|
3017
|
+
if (exactMatch) {
|
|
3018
|
+
return exactMatch;
|
|
3019
|
+
}
|
|
3020
|
+
const targetParts = normalizedTarget.split("/");
|
|
3021
|
+
const targetName = targetParts.pop() || "";
|
|
2876
3022
|
const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
3023
|
+
const targetDir = targetParts.join("/");
|
|
2877
3024
|
const matches = [];
|
|
2878
3025
|
for (const file of allFiles) {
|
|
2879
|
-
const
|
|
3026
|
+
const fileLower = file.toLowerCase();
|
|
3027
|
+
const fileParts = fileLower.split("/");
|
|
3028
|
+
const fileName = fileParts.pop() || "";
|
|
2880
3029
|
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
3030
|
+
const fileDir = fileParts.join("/");
|
|
3031
|
+
if (fileLower === normalizedTarget) {
|
|
3032
|
+
matches.push({ file, priority: 1 });
|
|
3033
|
+
} else if (fileNameNoExt === targetNameNoExt) {
|
|
3034
|
+
if (targetDir && fileDir.includes(targetDir)) {
|
|
3035
|
+
matches.push({ file, priority: 2 });
|
|
3036
|
+
} else if (targetDir && normalizedTarget.includes(fileDir)) {
|
|
3037
|
+
matches.push({ file, priority: 3 });
|
|
3038
|
+
} else {
|
|
3039
|
+
matches.push({ file, priority: 4 });
|
|
3040
|
+
}
|
|
3041
|
+
} else if (fileLower.includes(normalizedTarget)) {
|
|
3042
|
+
matches.push({ file, priority: 5 });
|
|
2885
3043
|
}
|
|
2886
3044
|
}
|
|
2887
|
-
if (matches.length ===
|
|
2888
|
-
|
|
3045
|
+
if (matches.length === 0) {
|
|
3046
|
+
for (const file of allFiles) {
|
|
3047
|
+
if (file.toLowerCase().includes(targetNameNoExt)) {
|
|
3048
|
+
matches.push({ file, priority: 6 });
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
2889
3051
|
}
|
|
2890
|
-
if (matches.length >
|
|
2891
|
-
|
|
3052
|
+
if (matches.length > 0) {
|
|
3053
|
+
matches.sort((a, b) => a.priority - b.priority);
|
|
3054
|
+
return matches[0].file;
|
|
2892
3055
|
}
|
|
2893
3056
|
return null;
|
|
2894
3057
|
}
|
|
@@ -3166,6 +3329,12 @@ import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
|
3166
3329
|
import { join as join4, extname as extname2, resolve } from "path";
|
|
3167
3330
|
import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
3168
3331
|
var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
3332
|
+
var DEBUG = process.env.DEBUG_ANALYZE === "true";
|
|
3333
|
+
function debugLog(...args) {
|
|
3334
|
+
if (DEBUG) {
|
|
3335
|
+
console.error("[analyze:debug]", ...args);
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3169
3338
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
3170
3339
|
"node_modules",
|
|
3171
3340
|
"dist",
|
|
@@ -3176,7 +3345,17 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
3176
3345
|
"coverage",
|
|
3177
3346
|
".turbo",
|
|
3178
3347
|
".vercel",
|
|
3179
|
-
".analyze"
|
|
3348
|
+
".analyze",
|
|
3349
|
+
// Firebase Functions output
|
|
3350
|
+
"functions/lib",
|
|
3351
|
+
"lib",
|
|
3352
|
+
// Outros outputs comuns
|
|
3353
|
+
".output",
|
|
3354
|
+
"out",
|
|
3355
|
+
".firebase",
|
|
3356
|
+
"firebase-debug.log",
|
|
3357
|
+
"firestore-debug.log",
|
|
3358
|
+
"pubsub-debug.log"
|
|
3180
3359
|
]);
|
|
3181
3360
|
var FIREBASE_V2_TRIGGERS = /* @__PURE__ */ new Set([
|
|
3182
3361
|
// HTTPS (firebase-functions/v2/https)
|
|
@@ -3236,6 +3415,11 @@ var FIREBASE_V2_TRIGGERS = /* @__PURE__ */ new Set([
|
|
|
3236
3415
|
]);
|
|
3237
3416
|
function indexProject(cwd) {
|
|
3238
3417
|
const allFiles = getAllCodeFiles(cwd);
|
|
3418
|
+
debugLog(`Indexando ${allFiles.length} arquivos em ${cwd}`);
|
|
3419
|
+
const functionFiles = allFiles.filter((f) => f.includes("functions/src/"));
|
|
3420
|
+
if (functionFiles.length > 0) {
|
|
3421
|
+
debugLog(`Encontrados ${functionFiles.length} arquivos em functions/src/:`, functionFiles);
|
|
3422
|
+
}
|
|
3239
3423
|
const project = createProject2(cwd);
|
|
3240
3424
|
for (const file of allFiles) {
|
|
3241
3425
|
try {
|
|
@@ -3337,6 +3521,13 @@ function indexProject(cwd) {
|
|
|
3337
3521
|
}
|
|
3338
3522
|
} else if (initKind === SyntaxKind2.CallExpression) {
|
|
3339
3523
|
const triggerName = extractFirebaseTriggerName(init);
|
|
3524
|
+
if (DEBUG && filePath.includes("functions/src/")) {
|
|
3525
|
+
const initText = init.getText().slice(0, 50);
|
|
3526
|
+
debugLog(`[CF] Analisando: ${name} = ${initText}...`);
|
|
3527
|
+
if (triggerName) {
|
|
3528
|
+
debugLog(`[CF] \u2713 Trigger detectado: ${triggerName}`);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3340
3531
|
if (triggerName && FIREBASE_V2_TRIGGERS.has(triggerName)) {
|
|
3341
3532
|
const triggerInfo = extractTriggerInfo(init, triggerName);
|
|
3342
3533
|
const symbol = {
|
|
@@ -3534,7 +3725,7 @@ function inferSymbolKind(name, context2) {
|
|
|
3534
3725
|
function extractFirebaseTriggerName(init) {
|
|
3535
3726
|
const text = init.getText();
|
|
3536
3727
|
for (const trigger of FIREBASE_V2_TRIGGERS) {
|
|
3537
|
-
const pattern = new RegExp(`(
|
|
3728
|
+
const pattern = new RegExp(`(?:^|\\.|\\s)${trigger}(?:<[^>]*>)?\\s*\\(`);
|
|
3538
3729
|
if (pattern.test(text)) {
|
|
3539
3730
|
return trigger;
|
|
3540
3731
|
}
|
|
@@ -4032,6 +4223,42 @@ var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
|
|
|
4032
4223
|
".vercel",
|
|
4033
4224
|
".analyze"
|
|
4034
4225
|
]);
|
|
4226
|
+
function resolveAreaId(target, config, allFiles) {
|
|
4227
|
+
const targetLower = target.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4228
|
+
if (config.areas[target]) {
|
|
4229
|
+
return target;
|
|
4230
|
+
}
|
|
4231
|
+
for (const [id, areaConfig] of Object.entries(config.areas)) {
|
|
4232
|
+
const name = areaConfig.name?.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4233
|
+
if (name === targetLower) {
|
|
4234
|
+
return id;
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
for (const [id, name] of Object.entries(AREA_NAMES)) {
|
|
4238
|
+
const nameNormalized = name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4239
|
+
if (nameNormalized === targetLower) {
|
|
4240
|
+
return id;
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
for (const id of Object.keys(config.areas)) {
|
|
4244
|
+
if (id.toLowerCase().includes(targetLower)) {
|
|
4245
|
+
return id;
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
const detectedAreas = /* @__PURE__ */ new Set();
|
|
4249
|
+
for (const filePath of allFiles) {
|
|
4250
|
+
const areas2 = detectFileAreas(filePath, config);
|
|
4251
|
+
for (const areaId of areas2) {
|
|
4252
|
+
if (areaId.toLowerCase().includes(targetLower)) {
|
|
4253
|
+
detectedAreas.add(areaId);
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
if (detectedAreas.size > 0) {
|
|
4258
|
+
return [...detectedAreas][0];
|
|
4259
|
+
}
|
|
4260
|
+
return target;
|
|
4261
|
+
}
|
|
4035
4262
|
async function area(target, options = {}) {
|
|
4036
4263
|
const cwd = options.cwd || process.cwd();
|
|
4037
4264
|
const format = options.format || "text";
|
|
@@ -4043,9 +4270,10 @@ async function area(target, options = {}) {
|
|
|
4043
4270
|
try {
|
|
4044
4271
|
const config = readConfig(cwd);
|
|
4045
4272
|
const allFiles = getAllCodeFiles4(cwd);
|
|
4273
|
+
const resolvedTarget = resolveAreaId(target, config, allFiles);
|
|
4046
4274
|
const filteredFiles = allFiles.filter((filePath) => !isFileIgnored(filePath, config));
|
|
4047
4275
|
const areaFiles = [];
|
|
4048
|
-
const targetLower =
|
|
4276
|
+
const targetLower = resolvedTarget.toLowerCase();
|
|
4049
4277
|
for (const filePath of filteredFiles) {
|
|
4050
4278
|
const fileAreas = detectFileAreas(filePath, config);
|
|
4051
4279
|
const belongsToArea = fileAreas.some(
|
|
@@ -4083,15 +4311,19 @@ async function area(target, options = {}) {
|
|
|
4083
4311
|
for (const cat of Object.keys(byCategory)) {
|
|
4084
4312
|
byCategory[cat].sort((a, b) => a.path.localeCompare(b.path));
|
|
4085
4313
|
}
|
|
4086
|
-
const realAreaId = findRealAreaId(target, filteredFiles, config);
|
|
4314
|
+
const realAreaId = resolvedTarget !== target ? resolvedTarget : findRealAreaId(target, filteredFiles, config);
|
|
4315
|
+
const finalAreaId = realAreaId || resolvedTarget;
|
|
4316
|
+
const nameConversionMsg = resolvedTarget !== target ? `
|
|
4317
|
+
\u{1F4A1} Buscando \xE1rea "${getAreaName(finalAreaId, config)}" (ID: ${finalAreaId})
|
|
4318
|
+
` : "";
|
|
4087
4319
|
const detectedArea = {
|
|
4088
|
-
id:
|
|
4089
|
-
name: getAreaName(
|
|
4090
|
-
description: getAreaDescription(
|
|
4320
|
+
id: finalAreaId,
|
|
4321
|
+
name: getAreaName(finalAreaId, config),
|
|
4322
|
+
description: getAreaDescription(finalAreaId, config),
|
|
4091
4323
|
files: areaFiles,
|
|
4092
4324
|
fileCount: areaFiles.length,
|
|
4093
4325
|
categories,
|
|
4094
|
-
isAutoDetected: !config.areas[
|
|
4326
|
+
isAutoDetected: !config.areas[finalAreaId]
|
|
4095
4327
|
};
|
|
4096
4328
|
const result = {
|
|
4097
4329
|
version: "1.0.0",
|
|
@@ -4102,7 +4334,8 @@ async function area(target, options = {}) {
|
|
|
4102
4334
|
if (format === "json") {
|
|
4103
4335
|
return JSON.stringify(result, null, 2);
|
|
4104
4336
|
}
|
|
4105
|
-
|
|
4337
|
+
const output = formatAreaDetailText(result, { full, filterType });
|
|
4338
|
+
return nameConversionMsg + output;
|
|
4106
4339
|
} catch (error) {
|
|
4107
4340
|
const message = error instanceof Error ? error.message : String(error);
|
|
4108
4341
|
throw new Error(`Erro ao executar area: ${message}`);
|
|
@@ -4217,9 +4450,11 @@ Ou edite manualmente o arquivo existente.
|
|
|
4217
4450
|
patterns
|
|
4218
4451
|
};
|
|
4219
4452
|
}
|
|
4453
|
+
const suggestedIgnore = detectSuggestedIgnorePatterns(allFiles);
|
|
4220
4454
|
const newConfig = {
|
|
4221
4455
|
$schema: "./areas.schema.json",
|
|
4222
4456
|
version: "1.0.0",
|
|
4457
|
+
ignore: suggestedIgnore,
|
|
4223
4458
|
areas: generatedAreas,
|
|
4224
4459
|
descriptions: {},
|
|
4225
4460
|
settings: {
|
|
@@ -4234,7 +4469,12 @@ Ou edite manualmente o arquivo existente.
|
|
|
4234
4469
|
\u2705 Arquivo criado: .analyze/areas.config.json
|
|
4235
4470
|
|
|
4236
4471
|
\u{1F4E6} \xC1reas detectadas: ${sortedAreas.length}
|
|
4237
|
-
|
|
4472
|
+
`;
|
|
4473
|
+
if (suggestedIgnore.length > 0) {
|
|
4474
|
+
out += `\u{1F6AB} Padr\xF5es ignorados: ${suggestedIgnore.length}
|
|
4475
|
+
`;
|
|
4476
|
+
}
|
|
4477
|
+
out += `
|
|
4238
4478
|
`;
|
|
4239
4479
|
for (const [areaId, files] of sortedAreas.slice(0, 15)) {
|
|
4240
4480
|
const name = getAreaName(areaId, newConfig);
|
|
@@ -4254,6 +4494,15 @@ Ou edite manualmente o arquivo existente.
|
|
|
4254
4494
|
Use 'ai-tool areas' para ver detalhes
|
|
4255
4495
|
`;
|
|
4256
4496
|
}
|
|
4497
|
+
if (suggestedIgnore.length > 0) {
|
|
4498
|
+
out += `
|
|
4499
|
+
\u{1F4CB} Padr\xF5es adicionados ao ignore:
|
|
4500
|
+
`;
|
|
4501
|
+
for (const pattern of suggestedIgnore) {
|
|
4502
|
+
out += ` \u2022 ${pattern}
|
|
4503
|
+
`;
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4257
4506
|
out += `
|
|
4258
4507
|
\u{1F4A1} Edite o arquivo para:
|
|
4259
4508
|
- Renomear \xE1reas (campo "name")
|
|
@@ -4295,6 +4544,28 @@ function inferPatternsFromFiles(files) {
|
|
|
4295
4544
|
}
|
|
4296
4545
|
return [...patterns].sort();
|
|
4297
4546
|
}
|
|
4547
|
+
function detectSuggestedIgnorePatterns(files) {
|
|
4548
|
+
const patterns = [];
|
|
4549
|
+
if (files.some((f) => f.includes("functions/lib/"))) {
|
|
4550
|
+
patterns.push("functions/lib/**");
|
|
4551
|
+
}
|
|
4552
|
+
const testCount = files.filter((f) => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f)).length;
|
|
4553
|
+
if (testCount > 3) {
|
|
4554
|
+
patterns.push("**/*.test.{ts,tsx,js,jsx}");
|
|
4555
|
+
patterns.push("**/*.spec.{ts,tsx,js,jsx}");
|
|
4556
|
+
}
|
|
4557
|
+
const dtsCount = files.filter((f) => f.endsWith(".d.ts")).length;
|
|
4558
|
+
if (dtsCount > 2) {
|
|
4559
|
+
patterns.push("**/*.d.ts");
|
|
4560
|
+
}
|
|
4561
|
+
const configCount = files.filter(
|
|
4562
|
+
(f) => /\.(config|conf)\.(ts|js|mjs|cjs)$/.test(f)
|
|
4563
|
+
).length;
|
|
4564
|
+
if (configCount > 2) {
|
|
4565
|
+
patterns.push("**/*.config.{ts,js,mjs,cjs}");
|
|
4566
|
+
}
|
|
4567
|
+
return patterns;
|
|
4568
|
+
}
|
|
4298
4569
|
function getAllCodeFiles5(dir, files = [], baseDir = dir) {
|
|
4299
4570
|
try {
|
|
4300
4571
|
const entries = readdirSync6(dir);
|
|
@@ -4423,12 +4694,43 @@ function formatFunctionsText(result) {
|
|
|
4423
4694
|
out += ` Total: ${result.summary.total} functions
|
|
4424
4695
|
`;
|
|
4425
4696
|
out += ` Exportadas: ${result.summary.exported}
|
|
4426
|
-
|
|
4697
|
+
`;
|
|
4698
|
+
if (result.summary.total > 0) {
|
|
4699
|
+
out += `
|
|
4700
|
+
\u{1F4A1} Filtros dispon\xEDveis:
|
|
4701
|
+
`;
|
|
4702
|
+
out += ` ai-tool functions --trigger=onCall
|
|
4703
|
+
`;
|
|
4704
|
+
out += ` ai-tool functions --trigger=onDocumentCreated
|
|
4705
|
+
`;
|
|
4706
|
+
}
|
|
4707
|
+
out += `
|
|
4427
4708
|
`;
|
|
4428
4709
|
if (result.summary.total === 0) {
|
|
4429
|
-
out += ` \u26A0\uFE0F
|
|
4710
|
+
out += ` \u26A0\uFE0F NENHUMA CLOUD FUNCTION DETECTADA
|
|
4711
|
+
|
|
4712
|
+
`;
|
|
4713
|
+
out += ` Poss\xEDveis causas:
|
|
4714
|
+
`;
|
|
4715
|
+
out += ` 1. O projeto n\xE3o \xE9 Firebase (n\xE3o encontrou .firebaserc ou firebase.json)
|
|
4716
|
+
`;
|
|
4717
|
+
out += ` 2. N\xE3o h\xE1 arquivo functions/src/index.ts
|
|
4718
|
+
`;
|
|
4719
|
+
out += ` 3. Os triggers n\xE3o usam padr\xF5es v2 (onCall, onDocumentCreated, etc)
|
|
4720
|
+
`;
|
|
4721
|
+
out += ` 4. O cache est\xE1 desatualizado \u2192 tente: ai-tool functions --no-cache
|
|
4722
|
+
`;
|
|
4723
|
+
out += ` 5. Para debug: DEBUG_ANALYZE=true ai-tool functions
|
|
4724
|
+
|
|
4430
4725
|
`;
|
|
4431
|
-
out += `
|
|
4726
|
+
out += ` Padr\xF5es suportados:
|
|
4727
|
+
`;
|
|
4728
|
+
out += ` export const minhaFunc = onCall((request) => { ... })
|
|
4729
|
+
`;
|
|
4730
|
+
out += ` export const minhaFunc = onDocumentCreated("path", (event) => { ... })
|
|
4731
|
+
|
|
4732
|
+
`;
|
|
4733
|
+
out += ` Documenta\xE7\xE3o: https://firebase.google.com/docs/functions
|
|
4432
4734
|
`;
|
|
4433
4735
|
return out;
|
|
4434
4736
|
}
|
|
@@ -4523,8 +4825,9 @@ async function find(query, options = {}) {
|
|
|
4523
4825
|
const defOnly = options.def ?? false;
|
|
4524
4826
|
const refsOnly = options.refs ?? false;
|
|
4525
4827
|
const useCache = options.cache !== false;
|
|
4526
|
-
|
|
4527
|
-
|
|
4828
|
+
const listAllMode = !query && defOnly && filterType && filterType !== "all";
|
|
4829
|
+
if (!query && !listAllMode) {
|
|
4830
|
+
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");
|
|
4528
4831
|
}
|
|
4529
4832
|
try {
|
|
4530
4833
|
let index;
|
|
@@ -4618,11 +4921,13 @@ async function find(query, options = {}) {
|
|
|
4618
4921
|
}
|
|
4619
4922
|
function searchInIndex(index, query, filterType, allowedFiles) {
|
|
4620
4923
|
const matches = [];
|
|
4621
|
-
const queryLower = query
|
|
4924
|
+
const queryLower = query?.toLowerCase() || "";
|
|
4622
4925
|
const processedSymbols = /* @__PURE__ */ new Set();
|
|
4926
|
+
const listAllMode = !query && filterType && filterType !== "all";
|
|
4623
4927
|
for (const [name, symbols] of Object.entries(index.symbolsByName)) {
|
|
4624
4928
|
const nameLower = name.toLowerCase();
|
|
4625
|
-
|
|
4929
|
+
const shouldInclude = listAllMode || nameLower === queryLower || nameLower.includes(queryLower);
|
|
4930
|
+
if (shouldInclude) {
|
|
4626
4931
|
for (const symbol of symbols) {
|
|
4627
4932
|
if (allowedFiles && !allowedFiles.has(symbol.file)) continue;
|
|
4628
4933
|
if (!matchesType(symbol.kind, filterType)) continue;
|
|
@@ -4642,25 +4947,27 @@ function searchInIndex(index, query, filterType, allowedFiles) {
|
|
|
4642
4947
|
}
|
|
4643
4948
|
}
|
|
4644
4949
|
}
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
for (const
|
|
4649
|
-
const
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4950
|
+
if (!listAllMode) {
|
|
4951
|
+
for (const [filePath, fileData] of Object.entries(index.files)) {
|
|
4952
|
+
if (allowedFiles && !allowedFiles.has(filePath)) continue;
|
|
4953
|
+
for (const imp of fileData.imports) {
|
|
4954
|
+
for (const spec of imp.specifiers) {
|
|
4955
|
+
const specLower = spec.toLowerCase();
|
|
4956
|
+
if (specLower === queryLower || specLower.includes(queryLower)) {
|
|
4957
|
+
const key = `import:${filePath}:${spec}:${imp.source}`;
|
|
4958
|
+
if (processedSymbols.has(key)) continue;
|
|
4959
|
+
processedSymbols.add(key);
|
|
4960
|
+
matches.push({
|
|
4961
|
+
file: filePath,
|
|
4962
|
+
line: 1,
|
|
4963
|
+
// Imports geralmente estão no topo
|
|
4964
|
+
column: 0,
|
|
4965
|
+
code: `import { ${spec} } from '${imp.source}'`,
|
|
4966
|
+
matchType: "import",
|
|
4967
|
+
symbolType: inferSymbolTypeFromName(spec),
|
|
4968
|
+
category: fileData.category
|
|
4969
|
+
});
|
|
4970
|
+
}
|
|
4664
4971
|
}
|
|
4665
4972
|
}
|
|
4666
4973
|
}
|
package/dist/cli.js
CHANGED
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
impact,
|
|
14
14
|
map,
|
|
15
15
|
suggest
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-XEFS66GU.js";
|
|
17
17
|
|
|
18
18
|
// src/cli.ts
|
|
19
|
+
import { resolve } from "path";
|
|
19
20
|
var HELP = `
|
|
20
21
|
ai-tool v${VERSION} - Analise de dependencias e impacto
|
|
21
22
|
|
|
@@ -107,7 +108,7 @@ async function main() {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
if (flags.mcp) {
|
|
110
|
-
const { startMcpServer } = await import("./server-
|
|
111
|
+
const { startMcpServer } = await import("./server-XR6C3XM3.js");
|
|
111
112
|
await startMcpServer();
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
@@ -122,7 +123,7 @@ async function main() {
|
|
|
122
123
|
const command = positional[0];
|
|
123
124
|
const target = positional[1];
|
|
124
125
|
const format = flags.format || "text";
|
|
125
|
-
const cwd = flags.cwd
|
|
126
|
+
const cwd = flags.cwd ? resolve(flags.cwd) : process.cwd();
|
|
126
127
|
const cache = !flags["no-cache"];
|
|
127
128
|
try {
|
|
128
129
|
let result;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED