@justmpm/ai-tool 0.5.5 → 0.6.0
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 +51 -9
- package/dist/chunk-M7JM3XRW.js +219 -0
- package/dist/{chunk-XVNZLNUT.js → chunk-UDT7TLSN.js} +753 -54
- package/dist/cli.js +46 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/{server-5ZBF3F6D.js → server-VKLU25E2.js} +89 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,6 +103,7 @@ Extrai assinaturas de funcoes e tipos SEM a implementacao.
|
|
|
103
103
|
```bash
|
|
104
104
|
ai-tool context Button
|
|
105
105
|
ai-tool context src/hooks/useAuth.ts --format=json
|
|
106
|
+
ai-tool context --area=auth # Contexto consolidado de toda uma area
|
|
106
107
|
```
|
|
107
108
|
|
|
108
109
|
**Extrai:**
|
|
@@ -113,6 +114,36 @@ ai-tool context src/hooks/useAuth.ts --format=json
|
|
|
113
114
|
|
|
114
115
|
Ideal para entender rapidamente a API publica de um arquivo.
|
|
115
116
|
|
|
117
|
+
**Contexto de Area** (`--area=<nome>`):
|
|
118
|
+
- Tipos e interfaces da area
|
|
119
|
+
- Hooks com parametros e retornos
|
|
120
|
+
- Funcoes principais
|
|
121
|
+
- Componentes React
|
|
122
|
+
- Services e stores
|
|
123
|
+
- Uma chamada = entender toda a feature
|
|
124
|
+
|
|
125
|
+
### `find` - Busca de Simbolos
|
|
126
|
+
|
|
127
|
+
Busca simbolos no codigo (funcoes, tipos, componentes, hooks, constantes).
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
ai-tool find useAuth # Definicao + usos
|
|
131
|
+
ai-tool find User --type=type # Busca apenas tipos
|
|
132
|
+
ai-tool find login --area=auth # Busca na area auth
|
|
133
|
+
ai-tool find submit --def # Apenas definicoes
|
|
134
|
+
ai-tool find submit --refs # Apenas referencias/usos
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Tipos de simbolos:**
|
|
138
|
+
- `function` - Funcoes e arrow functions
|
|
139
|
+
- `type` - Types, interfaces e enums
|
|
140
|
+
- `const` - Constantes e variaveis
|
|
141
|
+
- `component` - Componentes React (funcao que retorna JSX)
|
|
142
|
+
- `hook` - React hooks (funcao que comeca com `use`)
|
|
143
|
+
- `all` - Todos os tipos (default)
|
|
144
|
+
|
|
145
|
+
**Diferente de grep:** Entende o AST do TypeScript, encontra definicoes reais e onde sao usados.
|
|
146
|
+
|
|
116
147
|
### `areas` - Areas/Dominios Funcionais
|
|
117
148
|
|
|
118
149
|
Lista todas as areas funcionais do projeto (auth, dashboard, stripe, etc).
|
|
@@ -205,14 +236,16 @@ ai-tool --mcp
|
|
|
205
236
|
```
|
|
206
237
|
|
|
207
238
|
**Tools expostas:**
|
|
208
|
-
- `aitool_project_map` - Mapa do projeto
|
|
209
|
-
- `aitool_dead_code` -
|
|
210
|
-
- `aitool_impact_analysis` - Analise de impacto
|
|
211
|
-
- `aitool_suggest_reads` -
|
|
212
|
-
- `aitool_file_context` -
|
|
213
|
-
- `aitool_list_areas` - Lista areas funcionais
|
|
214
|
-
- `aitool_area_detail` -
|
|
239
|
+
- `aitool_project_map` - Mapa do projeto (resumo compacto)
|
|
240
|
+
- `aitool_dead_code` - Detecta codigo morto
|
|
241
|
+
- `aitool_impact_analysis` - Analise de impacto antes de modificar
|
|
242
|
+
- `aitool_suggest_reads` - Sugere arquivos para ler antes de editar
|
|
243
|
+
- `aitool_file_context` - Extrai assinaturas de um arquivo
|
|
244
|
+
- `aitool_list_areas` - Lista areas funcionais do projeto
|
|
245
|
+
- `aitool_area_detail` - Arquivos de uma area especifica
|
|
215
246
|
- `aitool_areas_init` - Gera config de areas
|
|
247
|
+
- `aitool_area_context` - Contexto consolidado de toda uma area
|
|
248
|
+
- `aitool_find` - Busca simbolos no codigo (definicao + usos)
|
|
216
249
|
|
|
217
250
|
### Configuracao Claude Code
|
|
218
251
|
|
|
@@ -250,7 +283,7 @@ Adicione ao `claude_desktop_config.json`:
|
|
|
250
283
|
## Uso Programatico
|
|
251
284
|
|
|
252
285
|
```typescript
|
|
253
|
-
import { map, dead, impact, suggest, context, areas, area, areasInit } from "@justmpm/ai-tool";
|
|
286
|
+
import { map, dead, impact, suggest, context, areaContext, find, areas, area, areasInit } from "@justmpm/ai-tool";
|
|
254
287
|
|
|
255
288
|
// Mapa do projeto (resumo por padrao, full: true para lista completa)
|
|
256
289
|
const projectMap = await map({ format: "json" });
|
|
@@ -268,6 +301,12 @@ const suggestions = await suggest("Button", { limit: 5 });
|
|
|
268
301
|
// Contexto do arquivo
|
|
269
302
|
const fileContext = await context("Button", { format: "json" });
|
|
270
303
|
|
|
304
|
+
// Contexto de uma area inteira
|
|
305
|
+
const authContext = await areaContext("auth", { format: "json" });
|
|
306
|
+
|
|
307
|
+
// Busca de simbolos
|
|
308
|
+
const symbolSearch = await find("useAuth", { type: "hook", area: "auth" });
|
|
309
|
+
|
|
271
310
|
// Areas funcionais
|
|
272
311
|
const projectAreas = await areas({ format: "json" });
|
|
273
312
|
|
|
@@ -288,7 +327,10 @@ await areasInit({ force: false });
|
|
|
288
327
|
| `--full` | Lista completa (`map`: arquivos, `area`: todos) | `false` |
|
|
289
328
|
| `--fix` | Remove codigo morto (so `dead`) | `false` |
|
|
290
329
|
| `--limit=<n>` | Limite de sugestoes (so `suggest`) | `10` |
|
|
291
|
-
| `--type=<cat>` | Filtra por categoria (
|
|
330
|
+
| `--type=<cat>` | Filtra por categoria (`area`) ou tipo de simbolo (`find`) | - |
|
|
331
|
+
| `--area=<nome>` | Filtra por area (`context`, `find`) | - |
|
|
332
|
+
| `--def` | Mostra apenas definicoes (so `find`) | `false` |
|
|
333
|
+
| `--refs` | Mostra apenas referencias/usos (so `find`) | `false` |
|
|
292
334
|
| `--mcp` | Inicia servidor MCP | - |
|
|
293
335
|
|
|
294
336
|
## Categorias de Arquivos
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cacheSymbolsIndex,
|
|
3
|
+
detectFileAreas,
|
|
4
|
+
formatFindText,
|
|
5
|
+
getCachedSymbolsIndex,
|
|
6
|
+
indexProject,
|
|
7
|
+
isCacheValid,
|
|
8
|
+
isFileIgnored,
|
|
9
|
+
readConfig,
|
|
10
|
+
updateCacheMeta
|
|
11
|
+
} from "./chunk-UDT7TLSN.js";
|
|
12
|
+
|
|
13
|
+
// src/commands/find.ts
|
|
14
|
+
async function find(query, options = {}) {
|
|
15
|
+
const cwd = options.cwd || process.cwd();
|
|
16
|
+
const format = options.format || "text";
|
|
17
|
+
const filterType = options.type || "all";
|
|
18
|
+
const filterArea = options.area;
|
|
19
|
+
const defOnly = options.def ?? false;
|
|
20
|
+
const refsOnly = options.refs ?? false;
|
|
21
|
+
const useCache = options.cache !== false;
|
|
22
|
+
if (!query || query.trim().length === 0) {
|
|
23
|
+
throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool find useAuth");
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
let index;
|
|
27
|
+
let fromCache = false;
|
|
28
|
+
if (useCache && isCacheValid(cwd)) {
|
|
29
|
+
const cached = getCachedSymbolsIndex(cwd);
|
|
30
|
+
if (cached && cached.symbolsByName) {
|
|
31
|
+
index = cached;
|
|
32
|
+
fromCache = true;
|
|
33
|
+
} else {
|
|
34
|
+
index = indexProject(cwd);
|
|
35
|
+
cacheSymbolsIndex(cwd, index);
|
|
36
|
+
updateCacheMeta(cwd);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
index = indexProject(cwd);
|
|
40
|
+
if (useCache) {
|
|
41
|
+
cacheSymbolsIndex(cwd, index);
|
|
42
|
+
updateCacheMeta(cwd);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let allowedFiles = null;
|
|
46
|
+
if (filterArea) {
|
|
47
|
+
const config = readConfig(cwd);
|
|
48
|
+
const areaLower = filterArea.toLowerCase();
|
|
49
|
+
allowedFiles = /* @__PURE__ */ new Set();
|
|
50
|
+
for (const filePath of Object.keys(index.files)) {
|
|
51
|
+
if (isFileIgnored(filePath, config)) continue;
|
|
52
|
+
const fileAreas = detectFileAreas(filePath, config);
|
|
53
|
+
const belongsToArea = fileAreas.some(
|
|
54
|
+
(a) => a.toLowerCase() === areaLower || a.toLowerCase().includes(areaLower)
|
|
55
|
+
);
|
|
56
|
+
if (belongsToArea) {
|
|
57
|
+
allowedFiles.add(filePath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (allowedFiles.size === 0) {
|
|
61
|
+
return format === "json" ? JSON.stringify({ error: `Nenhum arquivo encontrado na \xE1rea "${filterArea}"` }) : `\u274C Nenhum arquivo encontrado na \xE1rea "${filterArea}"`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const matches = searchInIndex(index, query, filterType, allowedFiles);
|
|
65
|
+
let definition = null;
|
|
66
|
+
let references = [];
|
|
67
|
+
for (const match of matches) {
|
|
68
|
+
if (match.matchType === "definition") {
|
|
69
|
+
if (!definition) {
|
|
70
|
+
definition = match;
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
references.push(match);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (defOnly) {
|
|
77
|
+
references = [];
|
|
78
|
+
}
|
|
79
|
+
if (refsOnly) {
|
|
80
|
+
definition = null;
|
|
81
|
+
}
|
|
82
|
+
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
83
|
+
if (definition) uniqueFiles.add(definition.file);
|
|
84
|
+
for (const ref of references) {
|
|
85
|
+
uniqueFiles.add(ref.file);
|
|
86
|
+
}
|
|
87
|
+
const result = {
|
|
88
|
+
version: "1.0.0",
|
|
89
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90
|
+
query,
|
|
91
|
+
filters: {
|
|
92
|
+
type: filterType !== "all" ? filterType : void 0,
|
|
93
|
+
area: filterArea,
|
|
94
|
+
defOnly,
|
|
95
|
+
refsOnly
|
|
96
|
+
},
|
|
97
|
+
definition,
|
|
98
|
+
references,
|
|
99
|
+
summary: {
|
|
100
|
+
definitions: definition ? 1 : 0,
|
|
101
|
+
references: references.length,
|
|
102
|
+
files: uniqueFiles.size
|
|
103
|
+
},
|
|
104
|
+
fromCache
|
|
105
|
+
};
|
|
106
|
+
if (format === "json") {
|
|
107
|
+
return JSON.stringify(result, null, 2);
|
|
108
|
+
}
|
|
109
|
+
return formatFindText(result);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
112
|
+
throw new Error(`Erro ao executar find: ${message}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function searchInIndex(index, query, filterType, allowedFiles) {
|
|
116
|
+
const matches = [];
|
|
117
|
+
const queryLower = query.toLowerCase();
|
|
118
|
+
const processedSymbols = /* @__PURE__ */ new Set();
|
|
119
|
+
for (const [name, symbols] of Object.entries(index.symbolsByName)) {
|
|
120
|
+
const nameLower = name.toLowerCase();
|
|
121
|
+
if (nameLower === queryLower || nameLower.includes(queryLower)) {
|
|
122
|
+
for (const symbol of symbols) {
|
|
123
|
+
if (allowedFiles && !allowedFiles.has(symbol.file)) continue;
|
|
124
|
+
if (!matchesType(symbol.kind, filterType)) continue;
|
|
125
|
+
const key = `${symbol.file}:${symbol.line}:${symbol.name}`;
|
|
126
|
+
if (processedSymbols.has(key)) continue;
|
|
127
|
+
processedSymbols.add(key);
|
|
128
|
+
const fileData = index.files[symbol.file];
|
|
129
|
+
matches.push({
|
|
130
|
+
file: symbol.file,
|
|
131
|
+
line: symbol.line,
|
|
132
|
+
column: 0,
|
|
133
|
+
code: symbol.signature,
|
|
134
|
+
matchType: "definition",
|
|
135
|
+
symbolType: mapKindToSymbolType(symbol.kind),
|
|
136
|
+
category: fileData?.category || "other"
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const [filePath, fileData] of Object.entries(index.files)) {
|
|
142
|
+
if (allowedFiles && !allowedFiles.has(filePath)) continue;
|
|
143
|
+
for (const imp of fileData.imports) {
|
|
144
|
+
for (const spec of imp.specifiers) {
|
|
145
|
+
const specLower = spec.toLowerCase();
|
|
146
|
+
if (specLower === queryLower || specLower.includes(queryLower)) {
|
|
147
|
+
const key = `import:${filePath}:${spec}:${imp.source}`;
|
|
148
|
+
if (processedSymbols.has(key)) continue;
|
|
149
|
+
processedSymbols.add(key);
|
|
150
|
+
matches.push({
|
|
151
|
+
file: filePath,
|
|
152
|
+
line: 1,
|
|
153
|
+
// Imports geralmente estão no topo
|
|
154
|
+
column: 0,
|
|
155
|
+
code: `import { ${spec} } from '${imp.source}'`,
|
|
156
|
+
matchType: "import",
|
|
157
|
+
symbolType: inferSymbolTypeFromName(spec),
|
|
158
|
+
category: fileData.category
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
matches.sort((a, b) => {
|
|
165
|
+
const order = { definition: 0, import: 1, usage: 2 };
|
|
166
|
+
const orderDiff = order[a.matchType] - order[b.matchType];
|
|
167
|
+
if (orderDiff !== 0) return orderDiff;
|
|
168
|
+
return a.file.localeCompare(b.file) || a.line - b.line;
|
|
169
|
+
});
|
|
170
|
+
return matches;
|
|
171
|
+
}
|
|
172
|
+
function matchesType(kind, filter) {
|
|
173
|
+
if (filter === "all") return true;
|
|
174
|
+
switch (filter) {
|
|
175
|
+
case "function":
|
|
176
|
+
return kind === "function";
|
|
177
|
+
case "type":
|
|
178
|
+
return kind === "type" || kind === "interface" || kind === "enum";
|
|
179
|
+
case "const":
|
|
180
|
+
return kind === "const";
|
|
181
|
+
case "component":
|
|
182
|
+
return kind === "component";
|
|
183
|
+
case "hook":
|
|
184
|
+
return kind === "hook";
|
|
185
|
+
default:
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function mapKindToSymbolType(kind) {
|
|
190
|
+
switch (kind) {
|
|
191
|
+
case "function":
|
|
192
|
+
return "function";
|
|
193
|
+
case "hook":
|
|
194
|
+
return "hook";
|
|
195
|
+
case "component":
|
|
196
|
+
return "component";
|
|
197
|
+
case "type":
|
|
198
|
+
case "interface":
|
|
199
|
+
case "enum":
|
|
200
|
+
return "type";
|
|
201
|
+
case "const":
|
|
202
|
+
return "const";
|
|
203
|
+
default:
|
|
204
|
+
return "function";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function inferSymbolTypeFromName(name) {
|
|
208
|
+
if (name.startsWith("use") && name.length > 3 && name[3] === name[3].toUpperCase()) {
|
|
209
|
+
return "hook";
|
|
210
|
+
}
|
|
211
|
+
if (name[0] === name[0].toUpperCase()) {
|
|
212
|
+
return "type";
|
|
213
|
+
}
|
|
214
|
+
return "function";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export {
|
|
218
|
+
find
|
|
219
|
+
};
|