@justmpm/ai-tool 0.3.0 → 0.3.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.
package/README.md CHANGED
@@ -1,29 +1,27 @@
1
1
  # ai-tool
2
2
 
3
- Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript.
3
+ Ferramenta de analise de dependencias e impacto para projetos TypeScript/JavaScript.
4
4
 
5
- Usa [Skott](https://github.com/antoine-coulon/skott) + [Knip](https://knip.dev) internamente para análise precisa.
5
+ Usa [Skott](https://github.com/antoine-coulon/skott) + [Knip](https://knip.dev) + [ts-morph](https://ts-morph.com) internamente.
6
6
 
7
- ## Instalação
7
+ ## Instalacao
8
8
 
9
9
  ```bash
10
10
  # Via npx (sem instalar)
11
- npx ai-tool map
12
- npx ai-tool dead
13
- npx ai-tool impact Button
11
+ npx @justmpm/ai-tool map
14
12
 
15
13
  # Ou instalar globalmente
16
- npm install -g ai-tool
14
+ npm install -g @justmpm/ai-tool
17
15
 
18
16
  # Ou como devDependency
19
- npm install -D ai-tool
17
+ npm install -D @justmpm/ai-tool
20
18
  ```
21
19
 
22
20
  ## Comandos
23
21
 
24
22
  ### `map` - Mapa do Projeto
25
23
 
26
- Gera um mapa completo do projeto com categorização de arquivos.
24
+ Gera um mapa completo do projeto com categorizacao de arquivos.
27
25
 
28
26
  ```bash
29
27
  ai-tool map
@@ -32,13 +30,13 @@ ai-tool map --format=json
32
30
 
33
31
  **Output:**
34
32
  - Total de arquivos e pastas
35
- - Categorização automática (component, hook, service, util, etc.)
33
+ - Categorizacao automatica (component, hook, service, util, etc.)
36
34
  - Estrutura de pastas
37
- - Dependências circulares detectadas
35
+ - Dependencias circulares detectadas
38
36
 
39
- ### `dead` - Código Morto
37
+ ### `dead` - Codigo Morto
40
38
 
41
- Detecta arquivos, exports e dependências não utilizados.
39
+ Detecta arquivos, exports e dependencias nao utilizados.
42
40
 
43
41
  ```bash
44
42
  ai-tool dead
@@ -47,13 +45,13 @@ ai-tool dead --fix # Remove automaticamente
47
45
  ```
48
46
 
49
47
  **Detecta:**
50
- - Arquivos órfãos (ninguém importa)
51
- - Exports não utilizados
52
- - Dependências npm não usadas
48
+ - Arquivos orfaos (ninguem importa)
49
+ - Exports nao utilizados
50
+ - Dependencias npm nao usadas
53
51
 
54
- ### `impact` - Análise de Impacto
52
+ ### `impact` - Analise de Impacto
55
53
 
56
- Analisa o impacto de modificar um arquivo específico.
54
+ Analisa o impacto de modificar um arquivo especifico.
57
55
 
58
56
  ```bash
59
57
  ai-tool impact Button
@@ -62,100 +60,159 @@ ai-tool impact useAuth --format=json
62
60
  ```
63
61
 
64
62
  **Output:**
65
- - **Upstream**: Quem importa este arquivo (afetados por mudanças)
66
- - **Downstream**: O que este arquivo importa (dependências)
67
- - **Riscos**: Arquivo crítico, dependências circulares, etc.
68
- - **Sugestões**: Recomendações para modificação segura
63
+ - **Upstream**: Quem importa este arquivo (afetados por mudancas)
64
+ - **Downstream**: O que este arquivo importa (dependencias)
65
+ - **Riscos**: Arquivo critico, dependencias circulares, etc.
66
+ - **Sugestoes**: Recomendacoes para modificacao segura
69
67
 
70
- ## Uso Programático
68
+ ### `suggest` - Sugestao de Leitura
69
+
70
+ Sugere arquivos para ler ANTES de modificar um arquivo.
71
+
72
+ ```bash
73
+ ai-tool suggest Button
74
+ ai-tool suggest src/hooks/useAuth.ts --limit=5
75
+ ```
76
+
77
+ **Prioridades:**
78
+ - **Critical**: Tipos/interfaces usados pelo arquivo
79
+ - **High**: Dependencias diretas (imports)
80
+ - **Medium**: Upstream (quem usa o arquivo)
81
+ - **Low**: Testes relacionados
82
+
83
+ ### `context` - Contexto do Arquivo
84
+
85
+ Extrai assinaturas de funcoes e tipos SEM a implementacao.
86
+
87
+ ```bash
88
+ ai-tool context Button
89
+ ai-tool context src/hooks/useAuth.ts --format=json
90
+ ```
91
+
92
+ **Extrai:**
93
+ - Imports com specifiers
94
+ - Exports do arquivo
95
+ - Funcoes com parametros e tipos de retorno
96
+ - Interfaces, types e enums com definicoes
97
+
98
+ Ideal para entender rapidamente a API publica de um arquivo.
99
+
100
+ ## Servidor MCP
101
+
102
+ Integra com Claude Desktop e outras ferramentas MCP.
103
+
104
+ ```bash
105
+ ai-tool --mcp
106
+ ```
107
+
108
+ **Tools expostas:**
109
+ - `aitool_project_map` - Mapa do projeto
110
+ - `aitool_dead_code` - Codigo morto
111
+ - `aitool_impact_analysis` - Analise de impacto
112
+ - `aitool_suggest_reads` - Sugestao de leitura
113
+ - `aitool_file_context` - Contexto do arquivo
114
+
115
+ ### Configuracao Claude Code
116
+
117
+ Adicione ao `.mcp.json` do projeto ou ao arquivo global `~/.claude/settings.json`:
118
+
119
+ ```json
120
+ {
121
+ "mcpServers": {
122
+ "analyze": {
123
+ "command": "npx",
124
+ "args": ["-y", "@justmpm/ai-tool", "--mcp"]
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Configuracao Claude Desktop
131
+
132
+ Adicione ao `claude_desktop_config.json`:
133
+
134
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
135
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
136
+
137
+ ```json
138
+ {
139
+ "mcpServers": {
140
+ "analyze": {
141
+ "command": "npx",
142
+ "args": ["-y", "@justmpm/ai-tool", "--mcp"]
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ## Uso Programatico
71
149
 
72
150
  ```typescript
73
- import { map, dead, impact } from "ai-tool";
151
+ import { map, dead, impact, suggest, context } from "@justmpm/ai-tool";
74
152
 
75
153
  // Mapa do projeto
76
154
  const projectMap = await map({ format: "json" });
77
155
 
78
- // Código morto
156
+ // Codigo morto
79
157
  const deadCode = await dead({ format: "json" });
80
158
 
81
- // Análise de impacto
82
- const analysis = await impact("src/components/Button.tsx", {
83
- format: "json"
84
- });
159
+ // Analise de impacto
160
+ const analysis = await impact("Button", { format: "json" });
161
+
162
+ // Sugestao de leitura
163
+ const suggestions = await suggest("Button", { limit: 5 });
164
+
165
+ // Contexto do arquivo
166
+ const fileContext = await context("Button", { format: "json" });
85
167
  ```
86
168
 
87
- ## Opções
169
+ ## Opcoes
88
170
 
89
- | Opção | Descrição | Default |
171
+ | Opcao | Descricao | Default |
90
172
  |-------|-----------|---------|
91
- | `--format=text\|json` | Formato de saída | `text` |
92
- | `--cwd=<path>` | Diretório do projeto | `process.cwd()` |
93
- | `--fix` | Remove código morto (só para `dead`) | `false` |
173
+ | `--format=text\|json` | Formato de saida | `text` |
174
+ | `--cwd=<path>` | Diretorio do projeto | `process.cwd()` |
175
+ | `--no-cache` | Ignora cache | `false` |
176
+ | `--fix` | Remove codigo morto (so `dead`) | `false` |
177
+ | `--limit=<n>` | Limite de sugestoes (so `suggest`) | `10` |
178
+ | `--mcp` | Inicia servidor MCP | - |
94
179
 
95
180
  ## Categorias de Arquivos
96
181
 
97
- O ai-tool categoriza automaticamente os arquivos:
98
-
99
- | Categoria | Descrição |
182
+ | Categoria | Descricao |
100
183
  |-----------|-----------|
101
- | `page` | Páginas (Next.js, etc.) |
184
+ | `page` | Paginas (Next.js, etc.) |
102
185
  | `layout` | Layouts |
103
186
  | `route` | Rotas de API |
104
187
  | `component` | Componentes React/Vue |
105
188
  | `hook` | React Hooks |
106
- | `service` | Serviços/API |
107
- | `store` | Estado global (Redux, Zustand, Context) |
108
- | `util` | Utilitários |
189
+ | `service` | Servicos/API |
190
+ | `store` | Estado global |
191
+ | `util` | Utilitarios |
109
192
  | `type` | Tipos TypeScript |
110
- | `config` | Configurações |
193
+ | `config` | Configuracoes |
111
194
  | `test` | Testes |
112
195
  | `other` | Outros |
113
196
 
114
- ## Integração com IA
197
+ ## Cache
115
198
 
116
- Este pacote foi criado para ser usado com ferramentas de IA como Claude Code, OpenCode, etc.
199
+ Resultados sao salvos em `.analyze/` para acelerar execucoes futuras.
117
200
 
118
- Exemplo de tool para OpenCode:
119
-
120
- ```typescript
121
- import { tool } from "@opencode-ai/plugin";
122
- import { execSync } from "child_process";
123
-
124
- export default tool({
125
- description: `Analisa dependências e impacto do projeto.
126
-
127
- COMANDOS:
128
- - map: Mapa do projeto
129
- - dead: Código morto
130
- - impact <arquivo>: Análise de impacto`,
131
-
132
- args: {
133
- command: tool.schema.enum(["map", "dead", "impact"]),
134
- target: tool.schema.string().optional(),
135
- format: tool.schema.enum(["text", "json"]).optional()
136
- },
137
-
138
- async execute({ command, target, format }) {
139
- const fmt = format || "text";
140
- const cmd = target
141
- ? `npx ai-tool ${command} "${target}" --format=${fmt}`
142
- : `npx ai-tool ${command} --format=${fmt}`;
143
-
144
- return execSync(cmd, { encoding: "utf-8" });
145
- }
146
- });
147
- ```
201
+ - Cache e invalidado automaticamente quando arquivos mudam
202
+ - Use `--no-cache` para forcar regeneracao
203
+ - Adicione `.analyze/` ao `.gitignore`
148
204
 
149
205
  ## Requisitos
150
206
 
151
207
  - Node.js >= 18.0.0
152
- - TypeScript/JavaScript project
208
+ - Projeto TypeScript/JavaScript
153
209
 
154
- ## Créditos
210
+ ## Creditos
155
211
 
156
- - [Skott](https://github.com/antoine-coulon/skott) - Análise de dependências
157
- - [Knip](https://knip.dev) - Detecção de código morto
212
+ - [Skott](https://github.com/antoine-coulon/skott) - Analise de dependencias
213
+ - [Knip](https://knip.dev) - Deteccao de codigo morto
214
+ - [ts-morph](https://ts-morph.com) - Analise AST
158
215
 
159
- ## Licença
216
+ ## Licenca
160
217
 
161
218
  MIT - [Koda AI Studio](https://kodaai.app)
@@ -252,6 +252,18 @@ function formatDeadText(result) {
252
252
  `;
253
253
  }
254
254
  out += `
255
+ `;
256
+ }
257
+ if (result.filters?.firebase.detected) {
258
+ out += `\u{1F525} FIREBASE CLOUD FUNCTIONS
259
+ `;
260
+ out += ` Projeto Firebase detectado.
261
+ `;
262
+ if (result.filters.firebase.excludedCount > 0) {
263
+ out += ` ${result.filters.firebase.excludedCount} arquivo(s) filtrado(s) (exportados em functions/src/index.ts)
264
+ `;
265
+ }
266
+ out += `
255
267
  `;
256
268
  }
257
269
  out += `\u{1F4A1} SUGEST\xC3O
@@ -587,8 +599,98 @@ function formatContextText(result) {
587
599
  }
588
600
 
589
601
  // src/cache/index.ts
590
- import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync } from "fs";
591
- import { join, extname } from "path";
602
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, statSync, readdirSync } from "fs";
603
+ import { join as join2, extname } from "path";
604
+
605
+ // src/utils/firebase.ts
606
+ import { existsSync, readFileSync } from "fs";
607
+ import { join, relative, normalize } from "path";
608
+ var indexContentCache = null;
609
+ function isFirebaseProject(cwd) {
610
+ const firebaserc = join(cwd, ".firebaserc");
611
+ const firebaseJson = join(cwd, "firebase.json");
612
+ return existsSync(firebaserc) || existsSync(firebaseJson);
613
+ }
614
+ function hasFirebaseFunctions(cwd) {
615
+ if (!isFirebaseProject(cwd)) {
616
+ return false;
617
+ }
618
+ const functionsIndexPath = join(cwd, "functions", "src", "index.ts");
619
+ return existsSync(functionsIndexPath);
620
+ }
621
+ function getFunctionsExports(cwd) {
622
+ const functionsIndexPath = join(cwd, "functions", "src", "index.ts");
623
+ if (indexContentCache && indexContentCache.cwd === cwd) {
624
+ return indexContentCache.exports;
625
+ }
626
+ if (!existsSync(functionsIndexPath)) {
627
+ return /* @__PURE__ */ new Set();
628
+ }
629
+ try {
630
+ const content = readFileSync(functionsIndexPath, "utf-8");
631
+ const exports = /* @__PURE__ */ new Set();
632
+ const namedExportRegex = /export\s*\{[^}]+\}\s*from\s*["']\.\/([^"']+)["']/g;
633
+ let match;
634
+ while ((match = namedExportRegex.exec(content)) !== null) {
635
+ const modulePath = match[1].replace(/\.js$/, "").replace(/\.ts$/, "");
636
+ exports.add(modulePath);
637
+ }
638
+ const starExportRegex = /export\s*\*\s*from\s*["']\.\/([^"']+)["']/g;
639
+ while ((match = starExportRegex.exec(content)) !== null) {
640
+ const modulePath = match[1].replace(/\.js$/, "").replace(/\.ts$/, "");
641
+ exports.add(modulePath);
642
+ }
643
+ const importRegex = /import\s*(?:\{[^}]+\}|\*\s+as\s+\w+)\s*from\s*["']\.\/([^"']+)["']/g;
644
+ while ((match = importRegex.exec(content)) !== null) {
645
+ const modulePath = match[1].replace(/\.js$/, "").replace(/\.ts$/, "");
646
+ exports.add(modulePath);
647
+ }
648
+ indexContentCache = { cwd, content, exports };
649
+ return exports;
650
+ } catch {
651
+ return /* @__PURE__ */ new Set();
652
+ }
653
+ }
654
+ function isExportedCloudFunction(filePath, cwd) {
655
+ const normalized = normalize(filePath).replace(/\\/g, "/");
656
+ if (!normalized.includes("functions/src/")) {
657
+ return false;
658
+ }
659
+ if (normalized.endsWith("functions/src/index.ts")) {
660
+ return false;
661
+ }
662
+ const functionsDir = join(cwd, "functions", "src");
663
+ const relativePath = relative(functionsDir, join(cwd, filePath)).replace(/\\/g, "/").replace(/\.ts$/, "").replace(/\.js$/, "");
664
+ const exports = getFunctionsExports(cwd);
665
+ if (exports.has(relativePath)) {
666
+ return true;
667
+ }
668
+ const parts = relativePath.split("/");
669
+ if (parts.length > 1 && exports.has(parts[0])) {
670
+ return true;
671
+ }
672
+ return false;
673
+ }
674
+ function filterCloudFunctionsFalsePositives(files, cwd) {
675
+ if (!hasFirebaseFunctions(cwd)) {
676
+ return { filtered: files, excluded: [] };
677
+ }
678
+ const filtered = [];
679
+ const excluded = [];
680
+ for (const file of files) {
681
+ if (isExportedCloudFunction(file, cwd)) {
682
+ excluded.push(file);
683
+ } else {
684
+ filtered.push(file);
685
+ }
686
+ }
687
+ return { filtered, excluded };
688
+ }
689
+ function clearFirebaseCache() {
690
+ indexContentCache = null;
691
+ }
692
+
693
+ // src/cache/index.ts
592
694
  var CACHE_DIR = ".analyze";
593
695
  var META_FILE = "meta.json";
594
696
  var GRAPH_FILE = "graph.json";
@@ -601,7 +703,7 @@ function calculateFilesHash(cwd) {
601
703
  try {
602
704
  const entries = readdirSync(dir, { withFileTypes: true });
603
705
  for (const entry of entries) {
604
- const fullPath = join(dir, entry.name);
706
+ const fullPath = join2(dir, entry.name);
605
707
  if (entry.isDirectory()) {
606
708
  if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".next" || entry.name === "dist" || entry.name === ".analyze") {
607
709
  continue;
@@ -626,16 +728,16 @@ function calculateFilesHash(cwd) {
626
728
  return `${timestamps.length}-${Math.floor(sum)}`;
627
729
  }
628
730
  function getCacheDir(cwd) {
629
- return join(cwd, CACHE_DIR);
731
+ return join2(cwd, CACHE_DIR);
630
732
  }
631
733
  function isCacheValid(cwd) {
632
734
  const cacheDir = getCacheDir(cwd);
633
- const metaPath = join(cacheDir, META_FILE);
634
- if (!existsSync(metaPath)) {
735
+ const metaPath = join2(cacheDir, META_FILE);
736
+ if (!existsSync2(metaPath)) {
635
737
  return false;
636
738
  }
637
739
  try {
638
- const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
740
+ const meta = JSON.parse(readFileSync2(metaPath, "utf-8"));
639
741
  const currentHash = calculateFilesHash(cwd);
640
742
  return meta.filesHash === currentHash;
641
743
  } catch {
@@ -643,22 +745,22 @@ function isCacheValid(cwd) {
643
745
  }
644
746
  }
645
747
  function readCache(cwd, file) {
646
- const cachePath = join(getCacheDir(cwd), file);
647
- if (!existsSync(cachePath)) {
748
+ const cachePath = join2(getCacheDir(cwd), file);
749
+ if (!existsSync2(cachePath)) {
648
750
  return null;
649
751
  }
650
752
  try {
651
- return JSON.parse(readFileSync(cachePath, "utf-8"));
753
+ return JSON.parse(readFileSync2(cachePath, "utf-8"));
652
754
  } catch {
653
755
  return null;
654
756
  }
655
757
  }
656
758
  function writeCache(cwd, file, data) {
657
759
  const cacheDir = getCacheDir(cwd);
658
- if (!existsSync(cacheDir)) {
760
+ if (!existsSync2(cacheDir)) {
659
761
  mkdirSync(cacheDir, { recursive: true });
660
762
  }
661
- const cachePath = join(cacheDir, file);
763
+ const cachePath = join2(cacheDir, file);
662
764
  writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
663
765
  }
664
766
  function updateCacheMeta(cwd) {
@@ -699,8 +801,9 @@ function getCachedDeadResult(cwd) {
699
801
  return readCache(cwd, DEAD_FILE);
700
802
  }
701
803
  function invalidateCache(cwd) {
702
- const metaPath = join(getCacheDir(cwd), META_FILE);
703
- if (existsSync(metaPath)) {
804
+ const metaPath = join2(getCacheDir(cwd), META_FILE);
805
+ clearFirebaseCache();
806
+ if (existsSync2(metaPath)) {
704
807
  try {
705
808
  writeFileSync(metaPath, "{}", "utf-8");
706
809
  } catch {
@@ -839,11 +942,15 @@ async function dead(options = {}) {
839
942
  knipOutput = {};
840
943
  }
841
944
  }
842
- const deadFiles = (knipOutput.files || []).map((file) => ({
945
+ const rawFiles = knipOutput.files || [];
946
+ const { filtered: filteredFiles, excluded: excludedFunctions } = filterCloudFunctionsFalsePositives(rawFiles, cwd);
947
+ const deadFiles = filteredFiles.map((file) => ({
843
948
  path: file,
844
949
  category: detectCategory(file),
845
950
  type: "file"
846
951
  }));
952
+ const hasFirebase = hasFirebaseFunctions(cwd);
953
+ const firebaseInfo = hasFirebase ? { detected: true, excludedCount: excludedFunctions.length } : { detected: false, excludedCount: 0 };
847
954
  const deadExports = [];
848
955
  if (knipOutput.issues) {
849
956
  for (const issue of knipOutput.issues) {
@@ -873,7 +980,12 @@ async function dead(options = {}) {
873
980
  },
874
981
  files: deadFiles,
875
982
  exports: deadExports,
876
- dependencies: deadDependencies
983
+ dependencies: deadDependencies,
984
+ // Metadata sobre filtros aplicados
985
+ filters: {
986
+ firebase: firebaseInfo,
987
+ excludedFiles: excludedFunctions
988
+ }
877
989
  };
878
990
  if (useCache) {
879
991
  cacheDeadResult(cwd, result);
@@ -1456,8 +1568,8 @@ function levenshteinDistance2(a, b) {
1456
1568
  }
1457
1569
 
1458
1570
  // src/commands/context.ts
1459
- import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1460
- import { join as join2, resolve, basename, extname as extname2 } from "path";
1571
+ import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1572
+ import { join as join3, resolve, basename, extname as extname2 } from "path";
1461
1573
 
1462
1574
  // src/ts/extractor.ts
1463
1575
  import { Project, SyntaxKind } from "ts-morph";
@@ -1705,12 +1817,12 @@ var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".
1705
1817
  function findTargetFile3(target, cwd) {
1706
1818
  const normalizedTarget = target.replace(/\\/g, "/");
1707
1819
  const directPath = resolve(cwd, normalizedTarget);
1708
- if (existsSync2(directPath) && isCodeFile2(directPath)) {
1820
+ if (existsSync3(directPath) && isCodeFile2(directPath)) {
1709
1821
  return normalizedTarget;
1710
1822
  }
1711
1823
  for (const ext of CODE_EXTENSIONS2) {
1712
1824
  const withExt = directPath + ext;
1713
- if (existsSync2(withExt)) {
1825
+ if (existsSync3(withExt)) {
1714
1826
  return normalizedTarget + ext;
1715
1827
  }
1716
1828
  }
@@ -1740,7 +1852,7 @@ function getAllCodeFiles(dir, files = [], baseDir = dir) {
1740
1852
  try {
1741
1853
  const entries = readdirSync2(dir);
1742
1854
  for (const entry of entries) {
1743
- const fullPath = join2(dir, entry);
1855
+ const fullPath = join3(dir, entry);
1744
1856
  if (shouldIgnore(entry)) {
1745
1857
  continue;
1746
1858
  }
@@ -1832,13 +1944,18 @@ function levenshteinDistance3(a, b) {
1832
1944
  }
1833
1945
 
1834
1946
  // src/index.ts
1835
- var VERSION = "0.3.0";
1947
+ var VERSION = "0.3.2";
1836
1948
 
1837
1949
  export {
1838
1950
  detectCategory,
1839
1951
  categoryIcons,
1840
1952
  isEntryPoint,
1841
1953
  isCodeFile,
1954
+ isFirebaseProject,
1955
+ hasFirebaseFunctions,
1956
+ isExportedCloudFunction,
1957
+ filterCloudFunctionsFalsePositives,
1958
+ clearFirebaseCache,
1842
1959
  getCacheDir,
1843
1960
  isCacheValid,
1844
1961
  invalidateCache,
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  impact,
8
8
  map,
9
9
  suggest
10
- } from "./chunk-BIT47QXF.js";
10
+ } from "./chunk-EONUMGCN.js";
11
11
 
12
12
  // src/cli.ts
13
13
  var HELP = `
@@ -71,6 +71,11 @@ async function main() {
71
71
  positional.push(arg);
72
72
  }
73
73
  }
74
+ if (flags.mcp) {
75
+ const { startMcpServer } = await import("./server-K55EXKYX.js");
76
+ await startMcpServer();
77
+ return;
78
+ }
74
79
  if (flags.help || flags.h || positional.length === 0) {
75
80
  console.log(HELP);
76
81
  process.exit(0);
@@ -79,11 +84,6 @@ async function main() {
79
84
  console.log(`ai-tool v${VERSION}`);
80
85
  process.exit(0);
81
86
  }
82
- if (flags.mcp) {
83
- const { startMcpServer } = await import("./server-OAPPO22L.js");
84
- await startMcpServer();
85
- return;
86
- }
87
87
  const command = positional[0];
88
88
  const target = positional[1];
89
89
  const format = flags.format || "text";
package/dist/index.d.ts CHANGED
@@ -66,6 +66,14 @@ interface DeadResult {
66
66
  export: string;
67
67
  }>;
68
68
  dependencies: string[];
69
+ /** Informações sobre filtros aplicados (ex: Cloud Functions excluídas) */
70
+ filters?: {
71
+ firebase: {
72
+ detected: boolean;
73
+ excludedCount: number;
74
+ };
75
+ excludedFiles: string[];
76
+ };
69
77
  }
70
78
  interface ImpactFile {
71
79
  path: string;
@@ -162,6 +170,9 @@ declare function map(options?: MapOptions): Promise<string>;
162
170
 
163
171
  /**
164
172
  * Comando DEAD - Detecção de código morto usando Knip
173
+ *
174
+ * Inclui filtro inteligente para Firebase Cloud Functions que elimina
175
+ * falsos positivos de arquivos exportados via functions/src/index.ts
165
176
  */
166
177
 
167
178
  /**
@@ -221,6 +232,41 @@ declare function isEntryPoint(filePath: string): boolean;
221
232
  */
222
233
  declare function isCodeFile(filePath: string): boolean;
223
234
 
235
+ /**
236
+ * Utilitários para detecção de Firebase Cloud Functions
237
+ *
238
+ * Resolve falsos positivos onde arquivos em functions/src/ são marcados
239
+ * como "órfãos" pelo Knip, mas na verdade são exportados via index.ts
240
+ * e deployados como Cloud Functions.
241
+ */
242
+ /**
243
+ * Detecta se é um projeto Firebase (via .firebaserc ou firebase.json)
244
+ */
245
+ declare function isFirebaseProject(cwd: string): boolean;
246
+ /**
247
+ * Detecta se o projeto tem Firebase Cloud Functions
248
+ */
249
+ declare function hasFirebaseFunctions(cwd: string): boolean;
250
+ /**
251
+ * Verifica se um arquivo é uma Cloud Function exportada no index.ts
252
+ */
253
+ declare function isExportedCloudFunction(filePath: string, cwd: string): boolean;
254
+ /**
255
+ * Filtra arquivos órfãos removendo falsos positivos de Cloud Functions
256
+ *
257
+ * @param files - Lista de arquivos marcados como órfãos pelo Knip
258
+ * @param cwd - Diretório do projeto
259
+ * @returns Lista filtrada sem os falsos positivos
260
+ */
261
+ declare function filterCloudFunctionsFalsePositives(files: string[], cwd: string): {
262
+ filtered: string[];
263
+ excluded: string[];
264
+ };
265
+ /**
266
+ * Limpa o cache (útil para testes ou quando o index.ts muda)
267
+ */
268
+ declare function clearFirebaseCache(): void;
269
+
224
270
  /**
225
271
  * Obtém o caminho da pasta de cache
226
272
  */
@@ -254,6 +300,6 @@ declare function invalidateCache(cwd: string): void;
254
300
  * ```
255
301
  */
256
302
 
257
- declare const VERSION = "0.3.0";
303
+ declare const VERSION = "0.3.2";
258
304
 
259
- export { type CommandOptions, type ContextOptions, type ContextResult, type DeadFile, type DeadOptions, type DeadResult, type FileCategory, type FileInfo, type FolderStats, type FunctionInfo, type ImpactFile, type ImpactOptions, type ImpactResult, type ImportInfo, type MapOptions, type MapResult, type OutputFormat, type ParamInfo, type RiskInfo, type SuggestOptions, type SuggestResult, type Suggestion, type SuggestionPriority, type TypeInfo, type TypeKind, VERSION, categoryIcons, context, dead, deadFix, detectCategory, getCacheDir, impact, invalidateCache, isCacheValid, isCodeFile, isEntryPoint, map, suggest };
305
+ export { type CommandOptions, type ContextOptions, type ContextResult, type DeadFile, type DeadOptions, type DeadResult, type FileCategory, type FileInfo, type FolderStats, type FunctionInfo, type ImpactFile, type ImpactOptions, type ImpactResult, type ImportInfo, type MapOptions, type MapResult, type OutputFormat, type ParamInfo, type RiskInfo, type SuggestOptions, type SuggestResult, type Suggestion, type SuggestionPriority, type TypeInfo, type TypeKind, VERSION, categoryIcons, clearFirebaseCache, context, dead, deadFix, detectCategory, filterCloudFunctionsFalsePositives, getCacheDir, hasFirebaseFunctions, impact, invalidateCache, isCacheValid, isCodeFile, isEntryPoint, isExportedCloudFunction, isFirebaseProject, map, suggest };
package/dist/index.js CHANGED
@@ -1,32 +1,42 @@
1
1
  import {
2
2
  VERSION,
3
3
  categoryIcons,
4
+ clearFirebaseCache,
4
5
  context,
5
6
  dead,
6
7
  deadFix,
7
8
  detectCategory,
9
+ filterCloudFunctionsFalsePositives,
8
10
  getCacheDir,
11
+ hasFirebaseFunctions,
9
12
  impact,
10
13
  invalidateCache,
11
14
  isCacheValid,
12
15
  isCodeFile,
13
16
  isEntryPoint,
17
+ isExportedCloudFunction,
18
+ isFirebaseProject,
14
19
  map,
15
20
  suggest
16
- } from "./chunk-BIT47QXF.js";
21
+ } from "./chunk-EONUMGCN.js";
17
22
  export {
18
23
  VERSION,
19
24
  categoryIcons,
25
+ clearFirebaseCache,
20
26
  context,
21
27
  dead,
22
28
  deadFix,
23
29
  detectCategory,
30
+ filterCloudFunctionsFalsePositives,
24
31
  getCacheDir,
32
+ hasFirebaseFunctions,
25
33
  impact,
26
34
  invalidateCache,
27
35
  isCacheValid,
28
36
  isCodeFile,
29
37
  isEntryPoint,
38
+ isExportedCloudFunction,
39
+ isFirebaseProject,
30
40
  map,
31
41
  suggest
32
42
  };
@@ -5,7 +5,7 @@ import {
5
5
  impact,
6
6
  map,
7
7
  suggest
8
- } from "./chunk-BIT47QXF.js";
8
+ } from "./chunk-EONUMGCN.js";
9
9
 
10
10
  // src/mcp/server.ts
11
11
  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.3.0",
3
+ "version": "0.3.2",
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",