@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 +136 -79
- package/dist/{chunk-BIT47QXF.js → chunk-EONUMGCN.js} +139 -22
- package/dist/cli.js +6 -6
- package/dist/index.d.ts +48 -2
- package/dist/index.js +11 -1
- package/dist/{server-OAPPO22L.js → server-K55EXKYX.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
# ai-tool
|
|
2
2
|
|
|
3
|
-
Ferramenta de
|
|
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)
|
|
5
|
+
Usa [Skott](https://github.com/antoine-coulon/skott) + [Knip](https://knip.dev) + [ts-morph](https://ts-morph.com) internamente.
|
|
6
6
|
|
|
7
|
-
##
|
|
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
|
|
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
|
-
-
|
|
33
|
+
- Categorizacao automatica (component, hook, service, util, etc.)
|
|
36
34
|
- Estrutura de pastas
|
|
37
|
-
-
|
|
35
|
+
- Dependencias circulares detectadas
|
|
38
36
|
|
|
39
|
-
### `dead` -
|
|
37
|
+
### `dead` - Codigo Morto
|
|
40
38
|
|
|
41
|
-
Detecta arquivos, exports e
|
|
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
|
|
51
|
-
- Exports
|
|
52
|
-
-
|
|
48
|
+
- Arquivos orfaos (ninguem importa)
|
|
49
|
+
- Exports nao utilizados
|
|
50
|
+
- Dependencias npm nao usadas
|
|
53
51
|
|
|
54
|
-
### `impact` -
|
|
52
|
+
### `impact` - Analise de Impacto
|
|
55
53
|
|
|
56
|
-
Analisa o impacto de modificar um arquivo
|
|
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
|
|
66
|
-
- **Downstream**: O que este arquivo importa (
|
|
67
|
-
- **Riscos**: Arquivo
|
|
68
|
-
- **
|
|
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
|
-
|
|
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
|
-
//
|
|
156
|
+
// Codigo morto
|
|
79
157
|
const deadCode = await dead({ format: "json" });
|
|
80
158
|
|
|
81
|
-
//
|
|
82
|
-
const analysis = await impact("
|
|
83
|
-
|
|
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
|
-
##
|
|
169
|
+
## Opcoes
|
|
88
170
|
|
|
89
|
-
|
|
|
171
|
+
| Opcao | Descricao | Default |
|
|
90
172
|
|-------|-----------|---------|
|
|
91
|
-
| `--format=text\|json` | Formato de
|
|
92
|
-
| `--cwd=<path>` |
|
|
93
|
-
| `--
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
| Categoria | Descrição |
|
|
182
|
+
| Categoria | Descricao |
|
|
100
183
|
|-----------|-----------|
|
|
101
|
-
| `page` |
|
|
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` |
|
|
107
|
-
| `store` | Estado global
|
|
108
|
-
| `util` |
|
|
189
|
+
| `service` | Servicos/API |
|
|
190
|
+
| `store` | Estado global |
|
|
191
|
+
| `util` | Utilitarios |
|
|
109
192
|
| `type` | Tipos TypeScript |
|
|
110
|
-
| `config` |
|
|
193
|
+
| `config` | Configuracoes |
|
|
111
194
|
| `test` | Testes |
|
|
112
195
|
| `other` | Outros |
|
|
113
196
|
|
|
114
|
-
##
|
|
197
|
+
## Cache
|
|
115
198
|
|
|
116
|
-
|
|
199
|
+
Resultados sao salvos em `.analyze/` para acelerar execucoes futuras.
|
|
117
200
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
208
|
+
- Projeto TypeScript/JavaScript
|
|
153
209
|
|
|
154
|
-
##
|
|
210
|
+
## Creditos
|
|
155
211
|
|
|
156
|
-
- [Skott](https://github.com/antoine-coulon/skott) -
|
|
157
|
-
- [Knip](https://knip.dev) -
|
|
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
|
-
##
|
|
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 =
|
|
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
|
|
731
|
+
return join2(cwd, CACHE_DIR);
|
|
630
732
|
}
|
|
631
733
|
function isCacheValid(cwd) {
|
|
632
734
|
const cacheDir = getCacheDir(cwd);
|
|
633
|
-
const metaPath =
|
|
634
|
-
if (!
|
|
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(
|
|
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 =
|
|
647
|
-
if (!
|
|
748
|
+
const cachePath = join2(getCacheDir(cwd), file);
|
|
749
|
+
if (!existsSync2(cachePath)) {
|
|
648
750
|
return null;
|
|
649
751
|
}
|
|
650
752
|
try {
|
|
651
|
-
return JSON.parse(
|
|
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 (!
|
|
760
|
+
if (!existsSync2(cacheDir)) {
|
|
659
761
|
mkdirSync(cacheDir, { recursive: true });
|
|
660
762
|
}
|
|
661
|
-
const cachePath =
|
|
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 =
|
|
703
|
-
|
|
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
|
|
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
|
|
1460
|
-
import { join as
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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.
|
|
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-
|
|
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.
|
|
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-
|
|
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
|
};
|
package/package.json
CHANGED