@justmpm/ai-tool 0.8.1 → 0.9.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 +47 -1
- package/dist/{chunk-OMEBMR2V.js → chunk-NVJXCSJF.js} +652 -480
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +51 -3
- package/dist/index.js +1 -1
- package/dist/server-L24EN4AN.js +649 -0
- package/package.json +2 -2
- package/dist/server-JNIIN5S4.js +0 -487
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VERSION,
|
|
3
|
+
area,
|
|
4
|
+
areaContext,
|
|
5
|
+
areas,
|
|
6
|
+
areasInit,
|
|
7
|
+
context,
|
|
8
|
+
dead,
|
|
9
|
+
find,
|
|
10
|
+
findSimilar,
|
|
11
|
+
formatOutput,
|
|
12
|
+
functions,
|
|
13
|
+
impact,
|
|
14
|
+
map,
|
|
15
|
+
parseCommandOptions,
|
|
16
|
+
readConfig,
|
|
17
|
+
suggest
|
|
18
|
+
} from "./chunk-NVJXCSJF.js";
|
|
19
|
+
|
|
20
|
+
// src/mcp/server.ts
|
|
21
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
22
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
23
|
+
|
|
24
|
+
// src/mcp/tools.ts
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
|
|
27
|
+
// src/commands/describe.ts
|
|
28
|
+
async function describe(query, options = {}) {
|
|
29
|
+
const { cwd, format } = parseCommandOptions(options);
|
|
30
|
+
if (!query || query.trim().length === 0) {
|
|
31
|
+
throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool describe 'autentica\xE7\xE3o'");
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const config = readConfig(cwd);
|
|
35
|
+
const normalizedQuery = query.toLowerCase().trim();
|
|
36
|
+
const candidates = Object.entries(config.areas).map(([id, area2]) => ({
|
|
37
|
+
id,
|
|
38
|
+
name: area2.name,
|
|
39
|
+
description: area2.description || ""
|
|
40
|
+
}));
|
|
41
|
+
const matches = findAreaMatches(normalizedQuery, candidates, config);
|
|
42
|
+
const suggestions = [];
|
|
43
|
+
if (matches.length === 0) {
|
|
44
|
+
const similarAreaIds = findSimilar(
|
|
45
|
+
normalizedQuery,
|
|
46
|
+
candidates.map((c) => c.id),
|
|
47
|
+
{ maxDistance: 2, limit: 3 }
|
|
48
|
+
);
|
|
49
|
+
const similarNames = findSimilar(
|
|
50
|
+
normalizedQuery,
|
|
51
|
+
candidates.map((c) => c.name),
|
|
52
|
+
{ maxDistance: 2, limit: 3 }
|
|
53
|
+
);
|
|
54
|
+
suggestions.push(
|
|
55
|
+
...similarAreaIds.map((id) => `\u2192 ai-tool describe ${id}`),
|
|
56
|
+
...similarNames.map((name) => `\u2192 ai-tool describe "${name}"`)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const result = {
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62
|
+
query,
|
|
63
|
+
areas: matches,
|
|
64
|
+
suggestions: suggestions.length > 0 ? suggestions : void 0
|
|
65
|
+
};
|
|
66
|
+
return formatOutput(result, format, formatDescribeText);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
throw new Error(`Erro ao executar describe: ${message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function findAreaMatches(normalizedQuery, candidates, config) {
|
|
73
|
+
const matches = [];
|
|
74
|
+
for (const candidate of candidates) {
|
|
75
|
+
const searchableText = `${candidate.id} ${candidate.name} ${candidate.description}`.toLowerCase();
|
|
76
|
+
const hasDirectMatch = searchableText.includes(normalizedQuery);
|
|
77
|
+
const queryWords = normalizedQuery.split(/\s+/);
|
|
78
|
+
const allWordsMatch = queryWords.every((word) => searchableText.includes(word));
|
|
79
|
+
if (hasDirectMatch || allWordsMatch) {
|
|
80
|
+
const areaFiles = getAreaFiles(candidate.id, config);
|
|
81
|
+
const score = calculateRelevanceScore(normalizedQuery, searchableText);
|
|
82
|
+
matches.push({
|
|
83
|
+
id: candidate.id,
|
|
84
|
+
name: candidate.name,
|
|
85
|
+
description: candidate.description || "Sem descri\xE7\xE3o",
|
|
86
|
+
files: areaFiles,
|
|
87
|
+
fileCount: areaFiles.length,
|
|
88
|
+
score
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return matches.sort((a, b) => a.score - b.score);
|
|
93
|
+
}
|
|
94
|
+
function getAreaFiles(areaId, config) {
|
|
95
|
+
const files = [];
|
|
96
|
+
const areaConfig = config.areas[areaId];
|
|
97
|
+
if (areaConfig?.patterns) {
|
|
98
|
+
files.push(`[Use 'ai-tool area ${areaId}' para ver arquivos completos]`);
|
|
99
|
+
}
|
|
100
|
+
return files;
|
|
101
|
+
}
|
|
102
|
+
function calculateRelevanceScore(query, text) {
|
|
103
|
+
if (text.includes(query)) {
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
const queryWords = query.split(/\s+/).filter(Boolean);
|
|
107
|
+
const allWordsPresent = queryWords.every((word) => text.includes(word));
|
|
108
|
+
if (allWordsPresent) {
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
return 10;
|
|
112
|
+
}
|
|
113
|
+
function formatDescribeText(result) {
|
|
114
|
+
let out = "";
|
|
115
|
+
if (result.areas.length === 0) {
|
|
116
|
+
out += `\u274C Nenhuma \xE1rea encontrada para: "${result.query}"
|
|
117
|
+
|
|
118
|
+
`;
|
|
119
|
+
if (result.suggestions && result.suggestions.length > 0) {
|
|
120
|
+
out += `\u{1F4A1} Voc\xEA quis dizer?
|
|
121
|
+
`;
|
|
122
|
+
for (const suggestion of result.suggestions) {
|
|
123
|
+
out += ` ${suggestion}
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
out += `
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
out += `\u{1F4D6} Dica: Use 'ai-tool areas' para listar todas as \xE1reas dispon\xEDveis`;
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
out += `\u{1F50D} Busca: "${result.query}"
|
|
133
|
+
|
|
134
|
+
`;
|
|
135
|
+
for (const area2 of result.areas) {
|
|
136
|
+
out += `## ${area2.name} (${area2.id})
|
|
137
|
+
`;
|
|
138
|
+
out += `${area2.description}
|
|
139
|
+
`;
|
|
140
|
+
out += `\u{1F4C1} ${area2.fileCount} arquivo(s)
|
|
141
|
+
|
|
142
|
+
`;
|
|
143
|
+
if (area2.files.length > 0) {
|
|
144
|
+
out += `Arquivos:
|
|
145
|
+
`;
|
|
146
|
+
for (const file of area2.files) {
|
|
147
|
+
out += ` \u2022 ${file}
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
out += "\n";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
out += `\u{1F4D6} Pr\xF3ximos passos:
|
|
154
|
+
`;
|
|
155
|
+
out += ` \u2192 ai-tool area <id> - ver detalhes de uma \xE1rea
|
|
156
|
+
`;
|
|
157
|
+
out += ` \u2192 ai-tool context --area=<id> - contexto completo de uma \xE1rea
|
|
158
|
+
`;
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/mcp/tools.ts
|
|
163
|
+
function registerAllTools(server2) {
|
|
164
|
+
server2.registerTool(
|
|
165
|
+
"aitool_project_map",
|
|
166
|
+
{
|
|
167
|
+
title: "Project Map",
|
|
168
|
+
description: `Mapeia projeto e retorna resumo: contagens por categoria, areas detectadas, alertas.
|
|
169
|
+
Use no inicio da sessao. Para detalhes: area_detail, file_context, suggest_reads.
|
|
170
|
+
|
|
171
|
+
Parametros:
|
|
172
|
+
- format: text (legivel) ou json (estruturado)
|
|
173
|
+
- cwd: Diretorio do projeto a analisar`,
|
|
174
|
+
inputSchema: {
|
|
175
|
+
format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
|
|
176
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
177
|
+
},
|
|
178
|
+
annotations: {
|
|
179
|
+
title: "Project Map",
|
|
180
|
+
readOnlyHint: true,
|
|
181
|
+
destructiveHint: false,
|
|
182
|
+
idempotentHint: true,
|
|
183
|
+
openWorldHint: false
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
async (params) => {
|
|
187
|
+
try {
|
|
188
|
+
const result = await map({
|
|
189
|
+
format: params.format,
|
|
190
|
+
cwd: params.cwd,
|
|
191
|
+
full: false
|
|
192
|
+
});
|
|
193
|
+
return { content: [{ type: "text", text: result }] };
|
|
194
|
+
} catch (error) {
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text", text: `Erro ao executar map: ${error instanceof Error ? error.message : String(error)}` }],
|
|
197
|
+
isError: true
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
server2.registerTool(
|
|
203
|
+
"aitool_dead_code",
|
|
204
|
+
{
|
|
205
|
+
title: "Dead Code Detector",
|
|
206
|
+
description: `Detecta codigo morto: arquivos orfaos, exports nao usados, deps npm mortas.
|
|
207
|
+
Use antes de refatoracoes ou periodicamente para limpeza.
|
|
208
|
+
|
|
209
|
+
Parametros:
|
|
210
|
+
- format: text (legivel) ou json (estruturado)
|
|
211
|
+
- cwd: Diretorio do projeto a analisar`,
|
|
212
|
+
inputSchema: {
|
|
213
|
+
format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
|
|
214
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
215
|
+
},
|
|
216
|
+
annotations: {
|
|
217
|
+
title: "Dead Code Detector",
|
|
218
|
+
readOnlyHint: true,
|
|
219
|
+
destructiveHint: false,
|
|
220
|
+
idempotentHint: true,
|
|
221
|
+
openWorldHint: false
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
async (params) => {
|
|
225
|
+
try {
|
|
226
|
+
const result = await dead({
|
|
227
|
+
format: params.format,
|
|
228
|
+
cwd: params.cwd
|
|
229
|
+
});
|
|
230
|
+
return { content: [{ type: "text", text: result }] };
|
|
231
|
+
} catch (error) {
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: `Erro ao executar dead: ${error instanceof Error ? error.message : String(error)}` }],
|
|
234
|
+
isError: true
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
server2.registerTool(
|
|
240
|
+
"aitool_impact_analysis",
|
|
241
|
+
{
|
|
242
|
+
title: "Impact Analysis",
|
|
243
|
+
description: `Analisa impacto de modificar um arquivo: upstream (quem importa), downstream (o que importa), riscos.
|
|
244
|
+
Use ANTES de editar arquivos para planejar mudancas seguras.
|
|
245
|
+
|
|
246
|
+
Parametros:
|
|
247
|
+
- target: Arquivo a analisar (caminho completo, parcial ou nome)
|
|
248
|
+
- format: text (legivel) ou json (estruturado)
|
|
249
|
+
- cwd: Diretorio do projeto a analisar`,
|
|
250
|
+
inputSchema: {
|
|
251
|
+
target: z.string().min(1).describe("Arquivo a analisar: caminho completo, parcial ou nome do arquivo"),
|
|
252
|
+
format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
|
|
253
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
254
|
+
},
|
|
255
|
+
annotations: {
|
|
256
|
+
title: "Impact Analysis",
|
|
257
|
+
readOnlyHint: true,
|
|
258
|
+
destructiveHint: false,
|
|
259
|
+
idempotentHint: true,
|
|
260
|
+
openWorldHint: false
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
async (params) => {
|
|
264
|
+
try {
|
|
265
|
+
const result = await impact(params.target, {
|
|
266
|
+
format: params.format,
|
|
267
|
+
cwd: params.cwd
|
|
268
|
+
});
|
|
269
|
+
return { content: [{ type: "text", text: result }] };
|
|
270
|
+
} catch (error) {
|
|
271
|
+
return {
|
|
272
|
+
content: [{ type: "text", text: `Erro ao executar impact: ${error instanceof Error ? error.message : String(error)}` }],
|
|
273
|
+
isError: true
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
server2.registerTool(
|
|
279
|
+
"aitool_suggest_reads",
|
|
280
|
+
{
|
|
281
|
+
title: "Suggest Files to Read",
|
|
282
|
+
description: `Sugere arquivos para ler ANTES de modificar um arquivo.
|
|
283
|
+
Prioriza: tipos usados, dependencias diretas, upstream, testes.
|
|
284
|
+
|
|
285
|
+
Parametros:
|
|
286
|
+
- target: Arquivo que sera modificado (caminho completo, parcial ou nome)
|
|
287
|
+
- limit: Numero maximo de sugestoes (default: 10, max: 50)`,
|
|
288
|
+
inputSchema: {
|
|
289
|
+
target: z.string().min(1).describe("Arquivo que sera modificado: caminho completo, parcial ou nome"),
|
|
290
|
+
limit: z.number().int().min(1).max(50).default(10).describe("Numero maximo de sugestoes (default: 10, max: 50)"),
|
|
291
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
292
|
+
},
|
|
293
|
+
annotations: {
|
|
294
|
+
title: "Suggest Files to Read",
|
|
295
|
+
readOnlyHint: true,
|
|
296
|
+
destructiveHint: false,
|
|
297
|
+
idempotentHint: true,
|
|
298
|
+
openWorldHint: false
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
async (params) => {
|
|
302
|
+
try {
|
|
303
|
+
const result = await suggest(params.target, {
|
|
304
|
+
limit: params.limit,
|
|
305
|
+
cwd: params.cwd,
|
|
306
|
+
format: "text"
|
|
307
|
+
});
|
|
308
|
+
return { content: [{ type: "text", text: result }] };
|
|
309
|
+
} catch (error) {
|
|
310
|
+
return {
|
|
311
|
+
content: [{ type: "text", text: `Erro ao executar suggest: ${error instanceof Error ? error.message : String(error)}` }],
|
|
312
|
+
isError: true
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
server2.registerTool(
|
|
318
|
+
"aitool_file_context",
|
|
319
|
+
{
|
|
320
|
+
title: "Extract File Context",
|
|
321
|
+
description: `Extrai assinaturas de funcoes e tipos de um arquivo (sem implementacao).
|
|
322
|
+
Use para entender a API publica antes de usar ou modificar.
|
|
323
|
+
|
|
324
|
+
Parametros:
|
|
325
|
+
- target: Arquivo para extrair contexto (caminho completo, parcial ou nome)
|
|
326
|
+
- cwd: Diretorio do projeto a analisar`,
|
|
327
|
+
inputSchema: {
|
|
328
|
+
target: z.string().min(1).describe("Arquivo para extrair contexto: caminho completo, parcial ou nome"),
|
|
329
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
330
|
+
},
|
|
331
|
+
annotations: {
|
|
332
|
+
title: "Extract File Context",
|
|
333
|
+
readOnlyHint: true,
|
|
334
|
+
destructiveHint: false,
|
|
335
|
+
idempotentHint: true,
|
|
336
|
+
openWorldHint: false
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
async (params) => {
|
|
340
|
+
try {
|
|
341
|
+
const result = await context(params.target, {
|
|
342
|
+
cwd: params.cwd,
|
|
343
|
+
format: "text"
|
|
344
|
+
});
|
|
345
|
+
return { content: [{ type: "text", text: result }] };
|
|
346
|
+
} catch (error) {
|
|
347
|
+
return {
|
|
348
|
+
content: [{ type: "text", text: `Erro ao executar context: ${error instanceof Error ? error.message : String(error)}` }],
|
|
349
|
+
isError: true
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
server2.registerTool(
|
|
355
|
+
"aitool_list_areas",
|
|
356
|
+
{
|
|
357
|
+
title: "List Project Areas",
|
|
358
|
+
description: `Lista areas/dominios funcionais do projeto (auth, pets, stripe...).
|
|
359
|
+
Diferente de categorias (hook, component). Use area_detail para ver arquivos.
|
|
360
|
+
|
|
361
|
+
Parametros:
|
|
362
|
+
- format: text (legivel) ou json (estruturado)
|
|
363
|
+
- cwd: Diretorio do projeto a analisar`,
|
|
364
|
+
inputSchema: {
|
|
365
|
+
format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
|
|
366
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
367
|
+
},
|
|
368
|
+
annotations: {
|
|
369
|
+
title: "List Project Areas",
|
|
370
|
+
readOnlyHint: true,
|
|
371
|
+
destructiveHint: false,
|
|
372
|
+
idempotentHint: true,
|
|
373
|
+
openWorldHint: false
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
async (params) => {
|
|
377
|
+
try {
|
|
378
|
+
const result = await areas({
|
|
379
|
+
format: params.format,
|
|
380
|
+
cwd: params.cwd
|
|
381
|
+
});
|
|
382
|
+
return { content: [{ type: "text", text: result }] };
|
|
383
|
+
} catch (error) {
|
|
384
|
+
return {
|
|
385
|
+
content: [{ type: "text", text: `Erro ao executar areas: ${error instanceof Error ? error.message : String(error)}` }],
|
|
386
|
+
isError: true
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
server2.registerTool(
|
|
392
|
+
"aitool_area_detail",
|
|
393
|
+
{
|
|
394
|
+
title: "Area Detail",
|
|
395
|
+
description: `Mostra arquivos de uma area especifica, agrupados por categoria.
|
|
396
|
+
Use ID (ex: auth) ou Name (ex: Autentica\xE7\xE3o) para identificar a area.
|
|
397
|
+
|
|
398
|
+
Parametros:
|
|
399
|
+
- target: Nome da area (ex: auth, dashboard, billing)
|
|
400
|
+
- type: Filtrar por categoria (page, component, hook, service, etc)
|
|
401
|
+
- full: Mostrar todos os arquivos (default: resumido)
|
|
402
|
+
- cwd: Diretorio do projeto a analisar`,
|
|
403
|
+
inputSchema: {
|
|
404
|
+
target: z.string().min(1).describe("Nome da area: auth, dashboard, billing, etc"),
|
|
405
|
+
type: z.enum(["page", "layout", "route", "component", "hook", "service", "store", "util", "type", "config", "test", "other"]).optional().describe("Filtrar por categoria especifica"),
|
|
406
|
+
full: z.boolean().default(false).describe("Mostrar todos os arquivos (default: resumido)"),
|
|
407
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
408
|
+
},
|
|
409
|
+
annotations: {
|
|
410
|
+
title: "Area Detail",
|
|
411
|
+
readOnlyHint: true,
|
|
412
|
+
destructiveHint: false,
|
|
413
|
+
idempotentHint: true,
|
|
414
|
+
openWorldHint: false
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
async (params) => {
|
|
418
|
+
try {
|
|
419
|
+
const result = await area(params.target, {
|
|
420
|
+
type: params.type,
|
|
421
|
+
full: params.full,
|
|
422
|
+
cwd: params.cwd,
|
|
423
|
+
format: "text"
|
|
424
|
+
});
|
|
425
|
+
return { content: [{ type: "text", text: result }] };
|
|
426
|
+
} catch (error) {
|
|
427
|
+
return {
|
|
428
|
+
content: [{ type: "text", text: `Erro ao executar area: ${error instanceof Error ? error.message : String(error)}` }],
|
|
429
|
+
isError: true
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
server2.registerTool(
|
|
435
|
+
"aitool_areas_init",
|
|
436
|
+
{
|
|
437
|
+
title: "Initialize Areas Config",
|
|
438
|
+
description: `Gera .analyze/areas.config.json para customizar deteccao de areas.
|
|
439
|
+
Use quando houver arquivos sem area ou precisar ajustar deteccao.
|
|
440
|
+
|
|
441
|
+
Parametros:
|
|
442
|
+
- force: Sobrescrever config existente
|
|
443
|
+
- cwd: Diretorio do projeto`,
|
|
444
|
+
inputSchema: {
|
|
445
|
+
force: z.boolean().default(false).describe("Sobrescrever config existente"),
|
|
446
|
+
cwd: z.string().optional().describe("Diretorio do projeto")
|
|
447
|
+
},
|
|
448
|
+
annotations: {
|
|
449
|
+
title: "Initialize Areas Config",
|
|
450
|
+
readOnlyHint: false,
|
|
451
|
+
destructiveHint: false,
|
|
452
|
+
idempotentHint: false,
|
|
453
|
+
openWorldHint: false
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
async (params) => {
|
|
457
|
+
try {
|
|
458
|
+
const result = await areasInit({
|
|
459
|
+
force: params.force,
|
|
460
|
+
cwd: params.cwd
|
|
461
|
+
});
|
|
462
|
+
return { content: [{ type: "text", text: result }] };
|
|
463
|
+
} catch (error) {
|
|
464
|
+
return {
|
|
465
|
+
content: [{ type: "text", text: `Erro ao executar areas init: ${error instanceof Error ? error.message : String(error)}` }],
|
|
466
|
+
isError: true
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
server2.registerTool(
|
|
472
|
+
"aitool_find",
|
|
473
|
+
{
|
|
474
|
+
title: "Find Symbol",
|
|
475
|
+
description: `Busca simbolos no codigo: funcoes, tipos, componentes, hooks, constantes.
|
|
476
|
+
Retorna definicao + referencias/usos. Diferente de grep, entende o AST do TypeScript.
|
|
477
|
+
|
|
478
|
+
Parametros:
|
|
479
|
+
- query: Termo a buscar (ex: useAuth, User, login)
|
|
480
|
+
- type: Filtrar por tipo (function, type, const, component, hook, trigger, all)
|
|
481
|
+
- area: Buscar apenas em uma area especifica (ex: auth, dashboard)
|
|
482
|
+
- def: Mostrar apenas definicoes (onde e declarado)
|
|
483
|
+
- refs: Mostrar apenas referencias/usos`,
|
|
484
|
+
inputSchema: {
|
|
485
|
+
query: z.string().min(1).describe("Termo a buscar (nome de funcao, tipo, componente, etc)"),
|
|
486
|
+
type: z.enum(["function", "type", "const", "component", "hook", "trigger", "all"]).default("all").describe("Filtrar por tipo de simbolo (use trigger para Cloud Functions)"),
|
|
487
|
+
area: z.string().optional().describe("Filtrar busca por area especifica (ex: auth, dashboard)"),
|
|
488
|
+
def: z.boolean().default(false).describe("Mostrar apenas definicoes (onde e declarado)"),
|
|
489
|
+
refs: z.boolean().default(false).describe("Mostrar apenas referencias (onde e usado)"),
|
|
490
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
491
|
+
},
|
|
492
|
+
annotations: {
|
|
493
|
+
title: "Find Symbol",
|
|
494
|
+
readOnlyHint: true,
|
|
495
|
+
destructiveHint: false,
|
|
496
|
+
idempotentHint: true,
|
|
497
|
+
openWorldHint: false
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
async (params) => {
|
|
501
|
+
try {
|
|
502
|
+
const result = await find(params.query, {
|
|
503
|
+
type: params.type,
|
|
504
|
+
area: params.area,
|
|
505
|
+
def: params.def,
|
|
506
|
+
refs: params.refs,
|
|
507
|
+
cwd: params.cwd,
|
|
508
|
+
format: "text"
|
|
509
|
+
});
|
|
510
|
+
return { content: [{ type: "text", text: result }] };
|
|
511
|
+
} catch (error) {
|
|
512
|
+
return {
|
|
513
|
+
content: [{ type: "text", text: `Erro ao executar find: ${error instanceof Error ? error.message : String(error)}` }],
|
|
514
|
+
isError: true
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
server2.registerTool(
|
|
520
|
+
"aitool_area_context",
|
|
521
|
+
{
|
|
522
|
+
title: "Area Context",
|
|
523
|
+
description: `Contexto consolidado de toda uma area: tipos, hooks, funcoes, componentes, services, stores.
|
|
524
|
+
Uma chamada = entender toda a feature. Muito mais eficiente que chamar context em cada arquivo.
|
|
525
|
+
|
|
526
|
+
Parametros:
|
|
527
|
+
- area: Nome da area (ex: auth, dashboard, payments)
|
|
528
|
+
- cwd: Diretorio do projeto a analisar`,
|
|
529
|
+
inputSchema: {
|
|
530
|
+
area: z.string().min(1).describe("Nome da area: auth, dashboard, payments, etc"),
|
|
531
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
532
|
+
},
|
|
533
|
+
annotations: {
|
|
534
|
+
title: "Area Context",
|
|
535
|
+
readOnlyHint: true,
|
|
536
|
+
destructiveHint: false,
|
|
537
|
+
idempotentHint: true,
|
|
538
|
+
openWorldHint: false
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
async (params) => {
|
|
542
|
+
try {
|
|
543
|
+
const result = await areaContext(params.area, {
|
|
544
|
+
cwd: params.cwd,
|
|
545
|
+
format: "text"
|
|
546
|
+
});
|
|
547
|
+
return { content: [{ type: "text", text: result }] };
|
|
548
|
+
} catch (error) {
|
|
549
|
+
return {
|
|
550
|
+
content: [{ type: "text", text: `Erro ao executar area context: ${error instanceof Error ? error.message : String(error)}` }],
|
|
551
|
+
isError: true
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
server2.registerTool(
|
|
557
|
+
"aitool_list_functions",
|
|
558
|
+
{
|
|
559
|
+
title: "List Cloud Functions",
|
|
560
|
+
description: `Lista todas as Cloud Functions Firebase do projeto.
|
|
561
|
+
Agrupa por tipo de trigger (onCall, onDocumentCreated, onSchedule, etc).
|
|
562
|
+
Use para entender a arquitetura serverless antes de modificar triggers.
|
|
563
|
+
|
|
564
|
+
Parametros:
|
|
565
|
+
- trigger: Filtrar por tipo de trigger (ex: onCall, onDocumentCreated, onSchedule)
|
|
566
|
+
- format: text (legivel) ou json (estruturado)`,
|
|
567
|
+
inputSchema: {
|
|
568
|
+
trigger: z.string().optional().describe("Filtrar por tipo de trigger (ex: onCall, onDocumentCreated, onSchedule)"),
|
|
569
|
+
format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
|
|
570
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
571
|
+
},
|
|
572
|
+
annotations: {
|
|
573
|
+
title: "List Cloud Functions",
|
|
574
|
+
readOnlyHint: true,
|
|
575
|
+
destructiveHint: false,
|
|
576
|
+
idempotentHint: true,
|
|
577
|
+
openWorldHint: false
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
async (params) => {
|
|
581
|
+
try {
|
|
582
|
+
const result = await functions({
|
|
583
|
+
format: params.format,
|
|
584
|
+
trigger: params.trigger,
|
|
585
|
+
cwd: params.cwd
|
|
586
|
+
});
|
|
587
|
+
return { content: [{ type: "text", text: result }] };
|
|
588
|
+
} catch (error) {
|
|
589
|
+
return {
|
|
590
|
+
content: [{ type: "text", text: `Erro ao executar functions: ${error instanceof Error ? error.message : String(error)}` }],
|
|
591
|
+
isError: true
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
server2.registerTool(
|
|
597
|
+
"aitool_describe",
|
|
598
|
+
{
|
|
599
|
+
title: "Search by Description",
|
|
600
|
+
description: `Busca \xE1reas por descri\xE7\xE3o em linguagem natural.
|
|
601
|
+
Ex: "Onde implementou login?" \u2192 encontra \xE1rea de autentica\xE7\xE3o.
|
|
602
|
+
|
|
603
|
+
Parametros:
|
|
604
|
+
- query: Descri\xE7\xE3o ou keyword
|
|
605
|
+
- cwd: Diretorio do projeto`,
|
|
606
|
+
inputSchema: {
|
|
607
|
+
query: z.string().min(1).describe("Descri\xE7\xE3o ou keyword para buscar \xE1reas"),
|
|
608
|
+
format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
|
|
609
|
+
cwd: z.string().optional().describe("Diretorio do projeto a analisar")
|
|
610
|
+
},
|
|
611
|
+
annotations: {
|
|
612
|
+
title: "Search by Description",
|
|
613
|
+
readOnlyHint: true,
|
|
614
|
+
destructiveHint: false,
|
|
615
|
+
idempotentHint: true,
|
|
616
|
+
openWorldHint: false
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
async (params) => {
|
|
620
|
+
try {
|
|
621
|
+
const result = await describe(params.query, {
|
|
622
|
+
format: params.format,
|
|
623
|
+
cwd: params.cwd
|
|
624
|
+
});
|
|
625
|
+
return { content: [{ type: "text", text: result }] };
|
|
626
|
+
} catch (error) {
|
|
627
|
+
return {
|
|
628
|
+
content: [{ type: "text", text: `Erro ao executar describe: ${error instanceof Error ? error.message : String(error)}` }],
|
|
629
|
+
isError: true
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/mcp/server.ts
|
|
637
|
+
var server = new McpServer({
|
|
638
|
+
name: "ai-tool-mcp-server",
|
|
639
|
+
version: VERSION
|
|
640
|
+
});
|
|
641
|
+
registerAllTools(server);
|
|
642
|
+
async function startMcpServer() {
|
|
643
|
+
const transport = new StdioServerTransport();
|
|
644
|
+
await server.connect(transport);
|
|
645
|
+
console.error(`[ai-tool] MCP server v${VERSION} running via stdio`);
|
|
646
|
+
}
|
|
647
|
+
export {
|
|
648
|
+
startMcpServer
|
|
649
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justmpm/ai-tool",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript. Usa Skott + Knip internamente.",
|
|
3
|
+
"version": "0.9.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.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dependency-analysis",
|
|
7
7
|
"impact-analysis",
|