@justmpm/ai-tool 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1327,25 +1327,52 @@ var GRAPH_FILE = "graph.json";
1327
1327
  var MAP_FILE = "map.json";
1328
1328
  var DEAD_FILE = "dead.json";
1329
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
+ ]);
1330
1347
  function calculateFilesHash(cwd) {
1331
- const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
1332
- const timestamps = [];
1333
- function scanDir(dir) {
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;
1334
1355
  try {
1335
1356
  const entries = readdirSync(dir, { withFileTypes: true });
1336
1357
  for (const entry of entries) {
1358
+ if (entry.name.startsWith(".")) continue;
1337
1359
  const fullPath = join2(dir, entry.name);
1338
1360
  if (entry.isDirectory()) {
1339
- if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".next" || entry.name === "dist" || entry.name === ".analyze") {
1340
- continue;
1361
+ if (HASH_IGNORED_DIRS.has(entry.name)) continue;
1362
+ if (depth < MAX_DEPTH) {
1363
+ scanDir(fullPath, depth + 1);
1341
1364
  }
1342
- scanDir(fullPath);
1343
1365
  } else if (entry.isFile()) {
1344
1366
  const ext = extname(entry.name).toLowerCase();
1345
- if (extensions.includes(ext)) {
1367
+ if (extensions.has(ext)) {
1346
1368
  try {
1347
1369
  const stat = statSync(fullPath);
1348
- timestamps.push(stat.mtimeMs);
1370
+ const mtime = stat.mtimeMs;
1371
+ hashAccumulator ^= Math.floor(mtime);
1372
+ fileCount++;
1373
+ if (mtime > maxTimestamp) {
1374
+ maxTimestamp = mtime;
1375
+ }
1349
1376
  } catch {
1350
1377
  }
1351
1378
  }
@@ -1354,17 +1381,16 @@ function calculateFilesHash(cwd) {
1354
1381
  } catch {
1355
1382
  }
1356
1383
  }
1357
- scanDir(cwd);
1384
+ scanDir(cwd, 0);
1358
1385
  try {
1359
1386
  const configPath = join2(cwd, CACHE_DIR, "areas.config.json");
1360
1387
  if (existsSync2(configPath)) {
1361
1388
  const stat = statSync(configPath);
1362
- timestamps.push(stat.mtimeMs);
1389
+ hashAccumulator ^= Math.floor(stat.mtimeMs);
1363
1390
  }
1364
1391
  } catch {
1365
1392
  }
1366
- const sum = timestamps.reduce((a, b) => a + b, 0);
1367
- return `${timestamps.length}-${Math.floor(sum)}`;
1393
+ return `${fileCount}-${hashAccumulator}-${maxTimestamp}`;
1368
1394
  }
1369
1395
  function getCacheDir(cwd) {
1370
1396
  return join2(cwd, CACHE_DIR);
@@ -2708,27 +2734,49 @@ function findCircularFromGraph(graph) {
2708
2734
  return cycles;
2709
2735
  }
2710
2736
  function findTargetFile(target, allFiles) {
2711
- const normalizedTarget = target.replace(/\\/g, "/");
2737
+ const normalizedTarget = target.replace(/\\/g, "/").toLowerCase();
2712
2738
  if (allFiles.includes(normalizedTarget)) {
2713
2739
  return normalizedTarget;
2714
2740
  }
2715
- const targetName = normalizedTarget.split("/").pop()?.toLowerCase() || "";
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() || "";
2716
2747
  const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
2748
+ const targetDir = targetParts.join("/");
2717
2749
  const matches = [];
2718
2750
  for (const file of allFiles) {
2719
- const fileName = file.split("/").pop()?.toLowerCase() || "";
2751
+ const fileLower = file.toLowerCase();
2752
+ const fileParts = fileLower.split("/");
2753
+ const fileName = fileParts.pop() || "";
2720
2754
  const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
2721
- if (fileNameNoExt === targetNameNoExt) {
2722
- matches.unshift(file);
2723
- } else if (file.toLowerCase().includes(normalizedTarget.toLowerCase())) {
2724
- matches.push(file);
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 });
2725
2768
  }
2726
2769
  }
2727
- if (matches.length === 1) {
2728
- return matches[0];
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
+ }
2729
2776
  }
2730
- if (matches.length > 1) {
2731
- return matches[0];
2777
+ if (matches.length > 0) {
2778
+ matches.sort((a, b) => a.priority - b.priority);
2779
+ return matches[0].file;
2732
2780
  }
2733
2781
  return null;
2734
2782
  }
@@ -2961,27 +3009,49 @@ function findRelatedTests(targetPath, allFiles) {
2961
3009
  return tests;
2962
3010
  }
2963
3011
  function findTargetFile2(target, allFiles) {
2964
- const normalizedTarget = target.replace(/\\/g, "/");
3012
+ const normalizedTarget = target.replace(/\\/g, "/").toLowerCase();
2965
3013
  if (allFiles.includes(normalizedTarget)) {
2966
3014
  return normalizedTarget;
2967
3015
  }
2968
- const targetName = normalizedTarget.split("/").pop()?.toLowerCase() || "";
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() || "";
2969
3022
  const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
3023
+ const targetDir = targetParts.join("/");
2970
3024
  const matches = [];
2971
3025
  for (const file of allFiles) {
2972
- const fileName = file.split("/").pop()?.toLowerCase() || "";
3026
+ const fileLower = file.toLowerCase();
3027
+ const fileParts = fileLower.split("/");
3028
+ const fileName = fileParts.pop() || "";
2973
3029
  const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
2974
- if (fileNameNoExt === targetNameNoExt) {
2975
- matches.unshift(file);
2976
- } else if (file.toLowerCase().includes(normalizedTarget.toLowerCase())) {
2977
- matches.push(file);
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 });
2978
3043
  }
2979
3044
  }
2980
- if (matches.length === 1) {
2981
- return matches[0];
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
+ }
2982
3051
  }
2983
- if (matches.length > 1) {
2984
- return matches[0];
3052
+ if (matches.length > 0) {
3053
+ matches.sort((a, b) => a.priority - b.priority);
3054
+ return matches[0].file;
2985
3055
  }
2986
3056
  return null;
2987
3057
  }
@@ -3260,11 +3330,17 @@ import { join as join4, extname as extname2, resolve } from "path";
3260
3330
  import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
3261
3331
  var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3262
3332
  var DEBUG = process.env.DEBUG_ANALYZE === "true";
3333
+ var DEBUG_FUNCTIONS = process.env.DEBUG_FUNCTIONS === "true" || DEBUG;
3263
3334
  function debugLog(...args) {
3264
3335
  if (DEBUG) {
3265
3336
  console.error("[analyze:debug]", ...args);
3266
3337
  }
3267
3338
  }
3339
+ function debugFunctions(...args) {
3340
+ if (DEBUG_FUNCTIONS) {
3341
+ console.error("[functions:debug]", ...args);
3342
+ }
3343
+ }
3268
3344
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
3269
3345
  "node_modules",
3270
3346
  "dist",
@@ -3275,7 +3351,17 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
3275
3351
  "coverage",
3276
3352
  ".turbo",
3277
3353
  ".vercel",
3278
- ".analyze"
3354
+ ".analyze",
3355
+ // Firebase Functions output
3356
+ "functions/lib",
3357
+ "lib",
3358
+ // Outros outputs comuns
3359
+ ".output",
3360
+ "out",
3361
+ ".firebase",
3362
+ "firebase-debug.log",
3363
+ "firestore-debug.log",
3364
+ "pubsub-debug.log"
3279
3365
  ]);
3280
3366
  var FIREBASE_V2_TRIGGERS = /* @__PURE__ */ new Set([
3281
3367
  // HTTPS (firebase-functions/v2/https)
@@ -3339,6 +3425,8 @@ function indexProject(cwd) {
3339
3425
  const functionFiles = allFiles.filter((f) => f.includes("functions/src/"));
3340
3426
  if (functionFiles.length > 0) {
3341
3427
  debugLog(`Encontrados ${functionFiles.length} arquivos em functions/src/:`, functionFiles);
3428
+ debugFunctions(`Arquivos em functions/src/:`);
3429
+ functionFiles.forEach((f) => debugFunctions(` - ${f}`));
3342
3430
  }
3343
3431
  const project = createProject2(cwd);
3344
3432
  for (const file of allFiles) {
@@ -3441,11 +3529,14 @@ function indexProject(cwd) {
3441
3529
  }
3442
3530
  } else if (initKind === SyntaxKind2.CallExpression) {
3443
3531
  const triggerName = extractFirebaseTriggerName(init);
3444
- if (DEBUG && filePath.includes("functions/src/")) {
3445
- const initText = init.getText().slice(0, 50);
3446
- debugLog(`[CF] Analisando: ${name} = ${initText}...`);
3532
+ if (filePath.includes("functions/src/")) {
3533
+ const initText = init.getText().slice(0, 80).replace(/\s+/g, " ");
3534
+ debugFunctions(`Analisando: ${filePath}:${varDecl.getStartLineNumber()} - ${name} = ${initText}...`);
3447
3535
  if (triggerName) {
3448
- debugLog(`[CF] \u2713 Trigger detectado: ${triggerName}`);
3536
+ debugFunctions(` \u2713 Trigger detectado: ${triggerName}`);
3537
+ } else {
3538
+ const allText = init.getText().slice(0, 100);
3539
+ debugFunctions(` \u2717 N\xE3o \xE9 trigger. Texto: ${allText}...`);
3449
3540
  }
3450
3541
  }
3451
3542
  if (triggerName && FIREBASE_V2_TRIGGERS.has(triggerName)) {
@@ -4545,11 +4636,15 @@ async function functions(options = {}) {
4545
4636
  updateCacheMeta(cwd);
4546
4637
  }
4547
4638
  }
4639
+ const functionFiles = Object.values(index.files).filter((f) => f.path.includes("functions/src/"));
4548
4640
  const funcs = [];
4549
- for (const fileData of Object.values(index.files)) {
4550
- if (!fileData.path.includes("functions/src/")) continue;
4641
+ let totalSymbolsInFunctions = 0;
4642
+ let triggerSymbolsFound = 0;
4643
+ for (const fileData of functionFiles) {
4644
+ totalSymbolsInFunctions += fileData.symbols.length;
4551
4645
  for (const symbol of fileData.symbols) {
4552
4646
  if (symbol.kind === "trigger") {
4647
+ triggerSymbolsFound++;
4553
4648
  funcs.push({
4554
4649
  name: symbol.name,
4555
4650
  file: symbol.file,
@@ -4562,6 +4657,18 @@ async function functions(options = {}) {
4562
4657
  }
4563
4658
  }
4564
4659
  }
4660
+ if (funcs.length === 0 && (process.env.DEBUG_FUNCTIONS === "true" || process.env.DEBUG_ANALYZE === "true")) {
4661
+ console.error(`[functions:debug] Total de arquivos indexados: ${Object.keys(index.files).length}`);
4662
+ console.error(`[functions:debug] Arquivos em functions/src/: ${functionFiles.length}`);
4663
+ console.error(`[functions:debug] Total de s\xEDmbolos em functions/src/: ${totalSymbolsInFunctions}`);
4664
+ console.error(`[functions:debug] S\xEDmbolos do tipo 'trigger': ${triggerSymbolsFound}`);
4665
+ functionFiles.forEach((f) => {
4666
+ console.error(`[functions:debug] ${f.path}: ${f.symbols.length} s\xEDmbolos`);
4667
+ f.symbols.forEach((s) => {
4668
+ console.error(`[functions:debug] - ${s.name} (${s.kind})${s.isExported ? " [exported]" : ""}`);
4669
+ });
4670
+ });
4671
+ }
4565
4672
  const filtered = filterTrigger ? funcs.filter(
4566
4673
  (f) => f.triggerType.toLowerCase().includes(filterTrigger.toLowerCase())
4567
4674
  ) : funcs;
package/dist/cli.js CHANGED
@@ -13,9 +13,10 @@ import {
13
13
  impact,
14
14
  map,
15
15
  suggest
16
- } from "./chunk-SVY7A47I.js";
16
+ } from "./chunk-GYLHEJ4E.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-URRJAR75.js");
111
+ const { startMcpServer } = await import("./server-R6ARDHBW.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 || process.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
@@ -47,7 +47,7 @@ import {
47
47
  setFileDescription,
48
48
  suggest,
49
49
  writeConfig
50
- } from "./chunk-SVY7A47I.js";
50
+ } from "./chunk-GYLHEJ4E.js";
51
51
  export {
52
52
  AREA_DESCRIPTIONS,
53
53
  AREA_NAMES,
@@ -11,7 +11,7 @@ import {
11
11
  impact,
12
12
  map,
13
13
  suggest
14
- } from "./chunk-SVY7A47I.js";
14
+ } from "./chunk-GYLHEJ4E.js";
15
15
 
16
16
  // src/mcp/server.ts
17
17
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justmpm/ai-tool",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript. Usa Skott + Knip internamente.",
5
5
  "keywords": [
6
6
  "dependency-analysis",