@justmpm/ai-tool 3.23.0 → 4.0.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.
@@ -0,0 +1,387 @@
1
+ import {
2
+ detectFileAreas,
3
+ findSimilar,
4
+ formatOutput,
5
+ getProjectIndexArtifact,
6
+ getWorkspaceFiles,
7
+ hint,
8
+ isFileIgnored,
9
+ nextSteps,
10
+ parseCommandOptions,
11
+ readConfig
12
+ } from "./chunk-BFOWLQIL.js";
13
+
14
+ // src/commands/describe.ts
15
+ var STOPWORDS = /* @__PURE__ */ new Set([
16
+ // PT-BR
17
+ "de",
18
+ "da",
19
+ "do",
20
+ "das",
21
+ "dos",
22
+ "para",
23
+ "com",
24
+ "em",
25
+ "uma",
26
+ "um",
27
+ "o",
28
+ "a",
29
+ "os",
30
+ "as",
31
+ "no",
32
+ "na",
33
+ "nos",
34
+ "nas",
35
+ "pelo",
36
+ "pela",
37
+ "que",
38
+ "e",
39
+ "ou",
40
+ "se",
41
+ "ao",
42
+ "aos",
43
+ // EN
44
+ "the",
45
+ "of",
46
+ "in",
47
+ "for",
48
+ "with",
49
+ "on",
50
+ "at",
51
+ "to",
52
+ "and",
53
+ "or",
54
+ "is",
55
+ "are",
56
+ "was",
57
+ "by",
58
+ "an"
59
+ ]);
60
+ function removeStopwords(words) {
61
+ const filtered = words.filter((w) => !STOPWORDS.has(w) && w.length > 1);
62
+ return filtered.length > 0 ? filtered : words;
63
+ }
64
+ function calculatePartialScore(queryWords, searchableText) {
65
+ if (queryWords.length === 0) return 1;
66
+ const fullQuery = queryWords.join(" ");
67
+ if (searchableText.includes(fullQuery)) return 0;
68
+ let found = 0;
69
+ for (const word of queryWords) {
70
+ if (searchableText.includes(word)) {
71
+ found++;
72
+ }
73
+ }
74
+ return 1 - found / queryWords.length;
75
+ }
76
+ function buildAreaFileMap(allFiles, config) {
77
+ const map = /* @__PURE__ */ new Map();
78
+ for (const filePath of allFiles) {
79
+ if (isFileIgnored(filePath, config)) continue;
80
+ const fileAreas = detectFileAreas(filePath, config);
81
+ for (const areaId of fileAreas) {
82
+ if (!map.has(areaId)) map.set(areaId, []);
83
+ map.get(areaId).push(filePath);
84
+ }
85
+ }
86
+ return map;
87
+ }
88
+ var GENERIC_PARAMS = /* @__PURE__ */ new Set([
89
+ "id",
90
+ "data",
91
+ "item",
92
+ "row",
93
+ "key",
94
+ "val",
95
+ "value",
96
+ "obj",
97
+ "obj",
98
+ "arr",
99
+ "list",
100
+ "map",
101
+ "set",
102
+ "x",
103
+ "y",
104
+ "z",
105
+ "a",
106
+ "b",
107
+ "c",
108
+ "n",
109
+ "i",
110
+ "j",
111
+ "k",
112
+ "err",
113
+ "e",
114
+ "res",
115
+ "req",
116
+ "next"
117
+ ]);
118
+ function buildSearchableTextAndMatches(candidate, config, areaFileMap, index, queryWords) {
119
+ const symbolMatches = [];
120
+ const metadata = `${candidate.id} ${candidate.name} ${candidate.description}`;
121
+ const keywords = config.areas[candidate.id]?.keywords?.join(" ") ?? "";
122
+ const areaFiles = areaFileMap.get(candidate.id) ?? [];
123
+ const fileNames = areaFiles.map((f) => {
124
+ const name = f.split("/").pop() ?? "";
125
+ return name.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
126
+ }).join(" ");
127
+ const symbolNames = [];
128
+ const symbolSignatures = [];
129
+ const symbolParams = [];
130
+ const symbolReturnTypes = [];
131
+ for (const filePath of areaFiles) {
132
+ const fileData = index.files[filePath];
133
+ if (fileData?.symbols) {
134
+ for (const symbol of fileData.symbols) {
135
+ if (!symbol.isExported) continue;
136
+ symbolNames.push(symbol.name);
137
+ const MAX_SIGNATURE_CHARS = 200;
138
+ const sig = symbol.signature.length > MAX_SIGNATURE_CHARS ? symbol.signature.slice(0, MAX_SIGNATURE_CHARS) + "..." : symbol.signature;
139
+ symbolSignatures.push(sig);
140
+ if (symbol.params && symbol.params.length > 0) {
141
+ const filteredParams = symbol.params.filter((p) => !GENERIC_PARAMS.has(p));
142
+ const paramsText = filteredParams.slice(0, 10).join(" ");
143
+ if (paramsText) {
144
+ symbolParams.push(paramsText);
145
+ }
146
+ }
147
+ if (symbol.returnType) {
148
+ symbolReturnTypes.push(symbol.returnType);
149
+ }
150
+ for (const word of queryWords) {
151
+ if (symbol.signature.toLowerCase().includes(word)) {
152
+ const cleanSig = symbol.signature.replace(/export\s+/g, "").replace(/\s+/g, " ");
153
+ symbolMatches.push({
154
+ name: symbol.name,
155
+ file: filePath,
156
+ signature: cleanSig,
157
+ matchType: "signature",
158
+ matchedText: word
159
+ });
160
+ } else if (symbol.params?.some((p) => p.toLowerCase().includes(word))) {
161
+ const cleanSig = symbol.signature.replace(/export\s+/g, "").replace(/\s+/g, " ");
162
+ symbolMatches.push({
163
+ name: symbol.name,
164
+ file: filePath,
165
+ signature: cleanSig,
166
+ matchType: "params",
167
+ matchedText: word
168
+ });
169
+ } else if (symbol.returnType?.toLowerCase().includes(word)) {
170
+ const cleanSig = symbol.signature.replace(/export\s+/g, "").replace(/\s+/g, " ");
171
+ symbolMatches.push({
172
+ name: symbol.name,
173
+ file: filePath,
174
+ signature: cleanSig,
175
+ matchType: "returnType",
176
+ matchedText: word
177
+ });
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ const searchableText = [
184
+ metadata,
185
+ keywords,
186
+ fileNames,
187
+ symbolNames.join(" "),
188
+ symbolSignatures.join(" "),
189
+ symbolParams.join(" "),
190
+ symbolReturnTypes.join(" ")
191
+ ].join(" ").toLowerCase();
192
+ return { searchableText, symbolMatches };
193
+ }
194
+ async function findAreaMatches(normalizedQuery, candidates, config, cwd) {
195
+ const allFiles = await getWorkspaceFiles(cwd);
196
+ const artifact = await getProjectIndexArtifact(cwd, { useCache: true });
197
+ const index = artifact.index;
198
+ const areaFileMap = buildAreaFileMap(allFiles, config);
199
+ const queryWords = removeStopwords(normalizedQuery.split(/\s+/));
200
+ const matches = [];
201
+ for (const candidate of candidates) {
202
+ const { searchableText, symbolMatches } = buildSearchableTextAndMatches(
203
+ candidate,
204
+ config,
205
+ areaFileMap,
206
+ index,
207
+ queryWords
208
+ );
209
+ const score = calculatePartialScore(queryWords, searchableText);
210
+ if (score < 0.6) {
211
+ const areaFiles = areaFileMap.get(candidate.id) ?? [];
212
+ const seen = /* @__PURE__ */ new Set();
213
+ const uniqueSymbolMatches = symbolMatches.filter((m) => {
214
+ const key = `${m.file}:${m.signature}`;
215
+ if (seen.has(key)) return false;
216
+ seen.add(key);
217
+ return true;
218
+ });
219
+ matches.push({
220
+ id: candidate.id,
221
+ name: candidate.name,
222
+ description: candidate.description || "Sem descricao",
223
+ files: areaFiles,
224
+ fileCount: areaFiles.length,
225
+ score,
226
+ symbolMatches: uniqueSymbolMatches.length > 0 ? uniqueSymbolMatches : void 0
227
+ });
228
+ }
229
+ }
230
+ return matches.sort((a, b) => a.score - b.score);
231
+ }
232
+ async function describe(query, options = {}) {
233
+ const { cwd, format } = parseCommandOptions(options);
234
+ const ctx = options.ctx || "cli";
235
+ if (!query || query.trim().length === 0) {
236
+ throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool describe 'autentica\xE7\xE3o'");
237
+ }
238
+ try {
239
+ const config = readConfig(cwd);
240
+ const hasAreas = Object.keys(config.areas).length > 0;
241
+ if (!hasAreas) {
242
+ let out = `\u26A0\uFE0F Nenhuma area configurada neste projeto.
243
+
244
+ `;
245
+ out += `O comando describe busca em areas configuradas.
246
+ `;
247
+ out += `Para configurar areas:
248
+ `;
249
+ out += ` 1. ${hint("areas_init", ctx)} - gerar arquivo de configuracao
250
+ `;
251
+ out += ` 2. Edite .analyze/areas.config.json com as areas do projeto
252
+
253
+ `;
254
+ out += `Enquanto isso, use:
255
+ `;
256
+ out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
257
+ `;
258
+ out += ` \u2192 ${hint("map", ctx)} - ver estrutura do projeto
259
+ `;
260
+ return out;
261
+ }
262
+ const normalizedQuery = query.toLowerCase().trim();
263
+ const candidates = Object.entries(config.areas).map(([id, area]) => ({
264
+ id,
265
+ name: area.name,
266
+ description: area.description || ""
267
+ }));
268
+ const matches = await findAreaMatches(normalizedQuery, candidates, config, cwd);
269
+ const suggestions = [];
270
+ if (matches.length === 0) {
271
+ const similarAreaIds = findSimilar(
272
+ normalizedQuery,
273
+ candidates.map((c) => c.id),
274
+ { maxDistance: 2, limit: 3 }
275
+ );
276
+ const similarNames = findSimilar(
277
+ normalizedQuery,
278
+ candidates.map((c) => c.name),
279
+ { maxDistance: 2, limit: 3 }
280
+ );
281
+ suggestions.push(
282
+ ...similarAreaIds.map((id) => `\u2192 ${hint("describe", ctx, { "<termo>": id })}`),
283
+ ...similarNames.map((name) => `\u2192 ${hint("describe", ctx, { "<termo>": `"${name}"` })}`)
284
+ );
285
+ }
286
+ const result = {
287
+ version: "1.0.0",
288
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
289
+ query,
290
+ areas: matches,
291
+ suggestions: suggestions.length > 0 ? suggestions : void 0
292
+ };
293
+ return formatOutput(result, format, (r) => formatDescribeText(r, ctx));
294
+ } catch (error) {
295
+ const message = error instanceof Error ? error.message : String(error);
296
+ throw new Error(`Erro ao executar describe: ${message}`);
297
+ }
298
+ }
299
+ function formatDescribeText(result, ctx = "cli") {
300
+ let out = "";
301
+ if (result.areas.length === 0) {
302
+ out += `\u274C Nenhuma area encontrada para: "${result.query}"
303
+
304
+ `;
305
+ if (result.suggestions && result.suggestions.length > 0) {
306
+ out += `\u{1F4A1} Voce quis dizer?
307
+ `;
308
+ for (const suggestion of result.suggestions) {
309
+ out += ` ${suggestion}
310
+ `;
311
+ }
312
+ out += `
313
+ `;
314
+ }
315
+ out += `\u{1F4D6} Dicas:
316
+ `;
317
+ out += ` \u2192 ${hint("areas", ctx)} - listar todas as areas disponiveis
318
+ `;
319
+ out += ` \u2192 ${hint("find", ctx)} - buscar simbolos por nome
320
+ `;
321
+ return out;
322
+ }
323
+ out += `\u{1F50D} Busca: "${result.query}"
324
+
325
+ `;
326
+ for (const area of result.areas) {
327
+ out += `## ${area.name} (${area.id})
328
+ `;
329
+ out += `${area.description}
330
+ `;
331
+ out += `\u{1F4C1} ${area.fileCount} arquivo(s)
332
+ `;
333
+ if (area.symbolMatches && area.symbolMatches.length > 0) {
334
+ out += `
335
+ \u{1F517} Simbolos que correspondem a busca:
336
+ `;
337
+ const byFile = /* @__PURE__ */ new Map();
338
+ for (const match of area.symbolMatches) {
339
+ if (!byFile.has(match.file)) byFile.set(match.file, []);
340
+ byFile.get(match.file).push(match);
341
+ }
342
+ const MAX_SYMBOLS = 5;
343
+ let count = 0;
344
+ for (const [file, matches] of byFile) {
345
+ for (const match of matches) {
346
+ if (count >= MAX_SYMBOLS) break;
347
+ const matchLabel = match.matchType === "signature" ? "\u21B3" : match.matchType === "params" ? "\u2192 (param)" : "\u2192 (return)";
348
+ out += ` ${matchLabel} ${file}
349
+ `;
350
+ out += ` ${match.signature}
351
+ `;
352
+ count++;
353
+ }
354
+ if (count >= MAX_SYMBOLS) break;
355
+ }
356
+ if (area.symbolMatches.length > MAX_SYMBOLS) {
357
+ out += ` ... e mais ${area.symbolMatches.length - MAX_SYMBOLS} simbolo(s)
358
+ `;
359
+ }
360
+ }
361
+ out += "\n";
362
+ if (area.files.length > 0) {
363
+ const MAX_FILES = 5;
364
+ const filesToShow = area.files.slice(0, MAX_FILES);
365
+ const remaining = area.files.length - filesToShow.length;
366
+ out += `Arquivos:
367
+ `;
368
+ for (const file of filesToShow) {
369
+ out += ` \u2022 ${file}
370
+ `;
371
+ }
372
+ if (remaining > 0) {
373
+ out += ` ... e mais ${remaining} arquivo(s)
374
+ `;
375
+ out += ` \u2192 ${hint("area", ctx, { "<nome>": area.id })} - ver todos
376
+ `;
377
+ }
378
+ out += "\n";
379
+ }
380
+ }
381
+ out += nextSteps("describe", ctx);
382
+ return out;
383
+ }
384
+
385
+ export {
386
+ describe
387
+ };
package/dist/cli.js CHANGED
@@ -1,10 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- depsApi,
4
- depsInfo,
5
- depsSearch,
6
3
  describe
7
- } from "./chunk-4Z6PUAFR.js";
4
+ } from "./chunk-OQSQWBJV.js";
8
5
  import {
9
6
  VERSION,
10
7
  area,
@@ -20,7 +17,7 @@ import {
20
17
  impact,
21
18
  map,
22
19
  suggest
23
- } from "./chunk-WTRV35AO.js";
20
+ } from "./chunk-BFOWLQIL.js";
24
21
 
25
22
  // src/cli.ts
26
23
  import { resolve } from "path";
@@ -65,12 +62,7 @@ CHANGES:
65
62
  changes --unstaged Mudancas no working directory
66
63
  changes --file=src/types/auth.ts Detalhar um arquivo especifico
67
64
 
68
- DEPS:
69
- deps info <pacote> Informacoes gerais de um pacote instalado
70
- deps api <pacote> API publica (funcoes, tipos, interfaces)
71
- deps search <pacote> <termo> Busca simbolo especifico no pacote
72
-
73
- MODOS:
65
+ MODOS:
74
66
  --mcp Inicia servidor MCP para integracao com Claude Desktop
75
67
 
76
68
  OPCOES:
@@ -131,7 +123,7 @@ async function main() {
131
123
  }
132
124
  }
133
125
  if (flags.mcp) {
134
- const { startMcpServer } = await import("./server-3HLEATUD.js");
126
+ const { startMcpServer } = await import("./server-QFWWHDJY.js");
135
127
  await startMcpServer();
136
128
  return;
137
129
  }
@@ -262,52 +254,6 @@ async function main() {
262
254
  file: flags.file
263
255
  });
264
256
  break;
265
- case "deps":
266
- if (!target) {
267
- console.error("Erro: subcomando deps obrigatorio");
268
- console.error(" Exemplo: ai-tool deps info @mui/material");
269
- console.error(" Exemplo: ai-tool deps api zustand");
270
- console.error(" Exemplo: ai-tool deps search @mui/material Button");
271
- process.exit(1);
272
- }
273
- switch (target) {
274
- case "info": {
275
- const pkg = positional[2];
276
- if (!pkg) {
277
- console.error("Erro: nome do pacote obrigatorio para deps info");
278
- console.error(" Exemplo: ai-tool deps info @mui/material");
279
- process.exit(1);
280
- }
281
- result = await depsInfo(pkg, { format, cwd, ctx: "cli" });
282
- break;
283
- }
284
- case "api": {
285
- const pkg = positional[2];
286
- if (!pkg) {
287
- console.error("Erro: nome do pacote obrigatorio para deps api");
288
- console.error(" Exemplo: ai-tool deps api zustand");
289
- process.exit(1);
290
- }
291
- result = await depsApi(pkg, { format, cwd, limit: flags.limit ? Number(flags.limit) : void 0, kind: flags.kind, ctx: "cli" });
292
- break;
293
- }
294
- case "search": {
295
- const pkg = positional[2];
296
- const query = positional[3];
297
- if (!pkg || !query) {
298
- console.error("Erro: pacote e termo de busca obrigatorios para deps search");
299
- console.error(" Exemplo: ai-tool deps search @mui/material Button");
300
- process.exit(1);
301
- }
302
- result = await depsSearch(pkg, query, { format, cwd, kind: flags.kind, ctx: "cli" });
303
- break;
304
- }
305
- default:
306
- console.error(`Subcomando deps desconhecido: ${target}`);
307
- console.error(" Subcomandos disponiveis: info, api, search");
308
- process.exit(1);
309
- }
310
- break;
311
257
  default:
312
258
  console.error(`\u274C Comando desconhecido: ${command}`);
313
259
  console.error(" Use 'ai-tool --help' para ver comandos dispon\xEDveis.");
package/dist/index.d.ts CHANGED
@@ -568,7 +568,7 @@ declare function nextSteps(command: string, ctx: HintContext): string;
568
568
  * @param extra - Informação extra sobre o erro
569
569
  * @returns String formatada com dica de recuperação
570
570
  */
571
- type RecoveryErrorType = "file_not_found" | "area_not_found" | "no_results" | "no_firebase" | "no_areas_configured" | "symbol_not_found" | "package_not_found" | "index_failed" | "generic";
571
+ type RecoveryErrorType = "file_not_found" | "area_not_found" | "no_results" | "no_firebase" | "no_areas_configured" | "symbol_not_found" | "index_failed" | "generic";
572
572
  declare function recoveryHint(errorType: RecoveryErrorType, ctx: HintContext, _extra?: {
573
573
  command?: string;
574
574
  }): string;
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  setFileDescription,
48
48
  suggest,
49
49
  writeConfig
50
- } from "./chunk-WTRV35AO.js";
50
+ } from "./chunk-BFOWLQIL.js";
51
51
  export {
52
52
  VERSION,
53
53
  area,
@@ -1,9 +1,6 @@
1
1
  import {
2
- depsApi,
3
- depsInfo,
4
- depsSearch,
5
2
  describe
6
- } from "./chunk-4Z6PUAFR.js";
3
+ } from "./chunk-OQSQWBJV.js";
7
4
  import {
8
5
  VERSION,
9
6
  area,
@@ -19,7 +16,7 @@ import {
19
16
  map,
20
17
  recoveryHint,
21
18
  suggest
22
- } from "./chunk-WTRV35AO.js";
19
+ } from "./chunk-BFOWLQIL.js";
23
20
 
24
21
  // src/mcp/server.ts
25
22
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -626,158 +623,6 @@ Se os entries nao mostrarem descricoes, execute find { query: "<qualquer simbolo
626
623
  const hint = recoveryHint("generic", "mcp");
627
624
  return {
628
625
  content: [{ type: "text", text: `Erro ao executar changes: ${msg}
629
- ${hint}` }],
630
- isError: true
631
- };
632
- }
633
- }
634
- );
635
- server2.registerTool(
636
- "aitool_deps_info",
637
- {
638
- title: "Package Info",
639
- description: `Informacoes de um pacote instalado no node_modules: versao, descricao, entry points, dependencias.
640
- Le o package.json REAL do node_modules (versao instalada, nao do registry).
641
-
642
- Quando usar: descobrir versao instalada de uma lib, encontrar entry points, ver dependencias peer, verificar se o pacote tem types.
643
- NAO use para: ver a API publica (use deps_api) ou buscar simbolo especifico (use deps_search).
644
-
645
- Workflow: deps_info (overview) -> deps_api (API completa) -> deps_search (simbolo especifico)
646
- Dica: Se o pacote nao estiver instalado, instale-o antes (npm/yarn/bun/pnpm).`,
647
- inputSchema: {
648
- package: z.string().min(1).describe("Nome do pacote (ex: @mui/material, zustand)"),
649
- format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
650
- cwd: z.string().optional().describe("Diretorio do projeto a analisar")
651
- },
652
- annotations: {
653
- title: "Package Info",
654
- readOnlyHint: true,
655
- destructiveHint: false,
656
- idempotentHint: true,
657
- openWorldHint: false
658
- }
659
- },
660
- async (params) => {
661
- try {
662
- const result = await depsInfo(params.package, {
663
- format: params.format,
664
- cwd: params.cwd,
665
- ctx: "mcp"
666
- });
667
- return { content: [{ type: "text", text: result }] };
668
- } catch (error) {
669
- const msg = error instanceof Error ? error.message : String(error);
670
- const hint = recoveryHint("package_not_found", "mcp");
671
- return {
672
- content: [{ type: "text", text: `Erro ao executar deps info: ${msg}
673
- ${hint}` }],
674
- isError: true
675
- };
676
- }
677
- }
678
- );
679
- server2.registerTool(
680
- "aitool_deps_api",
681
- {
682
- title: "Package API",
683
- description: `API publica de um pacote instalado: funcoes, interfaces, tipos, enums exportados.
684
- Extrai assinaturas dos arquivos .d.ts do node_modules local (diferencial vs ferramentas online).
685
-
686
- Quando usar: aprender API de lib nova, ver tipos exportados, entender o que uma lib oferece.
687
- NAO use para: ver metadados (use deps_info) ou buscar simbolo especifico (use deps_search).
688
-
689
- Workflow: deps_info (overview) -> deps_api (API completa) -> deps_search (simbolo especifico)
690
- Dica: Se a API estiver truncada, use deps_search com wildcards: "Button" (exato), "Button*" (prefixo), "*Button*" (cont\xE9m).
691
- Dica: Para libs muito grandes (MUI, React), use --limit para controlar o output.`,
692
- inputSchema: {
693
- package: z.string().min(1).describe("Nome do pacote"),
694
- limit: z.number().int().min(1).max(500).optional().describe("Limite de simbolos por tipo (default: 50)"),
695
- offset: z.number().int().min(0).optional().describe("Offset para paginacao (default: 0)"),
696
- entry: z.string().optional().describe("Sub-export especifico (ex: 'auth' para firebase/firestore)"),
697
- kind: z.enum(["component", "hook", "function", "type", "interface", "enum", "const"]).optional().describe("Filtrar por tipo de simbolo (component|hook|function|type|interface|enum|const)"),
698
- format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
699
- cwd: z.string().optional().describe("Diretorio do projeto a analisar")
700
- },
701
- annotations: {
702
- title: "Package API",
703
- readOnlyHint: true,
704
- destructiveHint: false,
705
- idempotentHint: true,
706
- openWorldHint: false
707
- }
708
- },
709
- async (params) => {
710
- try {
711
- const result = await depsApi(params.package, {
712
- format: params.format,
713
- cwd: params.cwd,
714
- limit: params.limit,
715
- offset: params.offset,
716
- entry: params.entry,
717
- kind: params.kind,
718
- ctx: "mcp"
719
- });
720
- return { content: [{ type: "text", text: result }] };
721
- } catch (error) {
722
- const msg = error instanceof Error ? error.message : String(error);
723
- const hint = recoveryHint("package_not_found", "mcp");
724
- return {
725
- content: [{ type: "text", text: `Erro ao executar deps api: ${msg}
726
- ${hint}` }],
727
- isError: true
728
- };
729
- }
730
- }
731
- );
732
- server2.registerTool(
733
- "aitool_deps_search",
734
- {
735
- title: "Search Package Symbol",
736
- description: `Busca simbolo especifico na API de um pacote instalado.
737
- Procura por nome em funcoes, tipos, interfaces, enums e constantes exportados.
738
-
739
- Suporta wildcards: "Button" (exato), "Button*" (prefixo), "*Button" (sufixo), "*Button*" (cont\xE9m/substring).
740
-
741
- Quando usar: verificar se uma lib exporta algo, encontrar um componente ou tipo especifico.
742
- NAO use para: ver toda a API (use deps_api) ou metadados (use deps_info).
743
-
744
- Dica: Sem wildcard = match exato. Use "*Button*" para buscar tudo que contem "Button".
745
- Dica: Use deps_search para verificar imports antes de usar um simbolo.
746
-
747
- Workflow: deps_info (overview) -> deps_api (API completa) -> deps_search (simbolo especifico)`,
748
- inputSchema: {
749
- package: z.string().min(1).describe("Nome do pacote"),
750
- query: z.string().min(1).describe("Termo de busca (nome do simbolo)"),
751
- kind: z.enum(["component", "hook", "function", "type", "interface", "enum", "const"]).optional().describe("Filtrar por tipo de simbolo (component|hook|function|type|interface|enum|const)"),
752
- offset: z.number().int().min(0).optional().describe("Offset para paginacao (default: 0)"),
753
- limit: z.number().int().min(1).max(200).optional().describe("Limite de resultados (default: 15)"),
754
- format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
755
- cwd: z.string().optional().describe("Diretorio do projeto a analisar")
756
- },
757
- annotations: {
758
- title: "Search Package Symbol",
759
- readOnlyHint: true,
760
- destructiveHint: false,
761
- idempotentHint: true,
762
- openWorldHint: false
763
- }
764
- },
765
- async (params) => {
766
- try {
767
- const result = await depsSearch(params.package, params.query, {
768
- format: params.format,
769
- cwd: params.cwd,
770
- kind: params.kind,
771
- offset: params.offset,
772
- limit: params.limit,
773
- ctx: "mcp"
774
- });
775
- return { content: [{ type: "text", text: result }] };
776
- } catch (error) {
777
- const msg = error instanceof Error ? error.message : String(error);
778
- const hint = recoveryHint("package_not_found", "mcp");
779
- return {
780
- content: [{ type: "text", text: `Erro ao executar deps search: ${msg}
781
626
  ${hint}` }],
782
627
  isError: true
783
628
  };
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@justmpm/ai-tool",
3
- "version": "3.23.0",
4
- "description": "Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript. Usa Skott + Knip internamente. Inclui busca por descrição, integração Git e testes inteligentes.",
3
+ "version": "4.0.0",
4
+ "description": "Ferramenta de análise de código para projetos TypeScript/JavaScript. Mapa do projeto, impacto de mudanças, código morto, Cloud Functions, busca de símbolos e áreas funcionais. Usa Skott + Knip + ts-morph internamente.",
5
5
  "keywords": [
6
6
  "dependency-analysis",
7
7
  "impact-analysis",
8
8
  "dead-code",
9
+ "code-analysis",
10
+ "symbol-search",
9
11
  "typescript",
10
12
  "javascript",
11
13
  "skott",