@justmpm/ai-tool 0.9.0 → 0.9.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.
@@ -0,0 +1,145 @@
1
+ import {
2
+ findSimilar,
3
+ formatOutput,
4
+ parseCommandOptions,
5
+ readConfig
6
+ } from "./chunk-6ZWPDZDS.js";
7
+
8
+ // src/commands/describe.ts
9
+ async function describe(query, options = {}) {
10
+ const { cwd, format } = parseCommandOptions(options);
11
+ if (!query || query.trim().length === 0) {
12
+ throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool describe 'autentica\xE7\xE3o'");
13
+ }
14
+ try {
15
+ const config = readConfig(cwd);
16
+ const normalizedQuery = query.toLowerCase().trim();
17
+ const candidates = Object.entries(config.areas).map(([id, area]) => ({
18
+ id,
19
+ name: area.name,
20
+ description: area.description || ""
21
+ }));
22
+ const matches = findAreaMatches(normalizedQuery, candidates, config);
23
+ const suggestions = [];
24
+ if (matches.length === 0) {
25
+ const similarAreaIds = findSimilar(
26
+ normalizedQuery,
27
+ candidates.map((c) => c.id),
28
+ { maxDistance: 2, limit: 3 }
29
+ );
30
+ const similarNames = findSimilar(
31
+ normalizedQuery,
32
+ candidates.map((c) => c.name),
33
+ { maxDistance: 2, limit: 3 }
34
+ );
35
+ suggestions.push(
36
+ ...similarAreaIds.map((id) => `\u2192 ai-tool describe ${id}`),
37
+ ...similarNames.map((name) => `\u2192 ai-tool describe "${name}"`)
38
+ );
39
+ }
40
+ const result = {
41
+ version: "1.0.0",
42
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
43
+ query,
44
+ areas: matches,
45
+ suggestions: suggestions.length > 0 ? suggestions : void 0
46
+ };
47
+ return formatOutput(result, format, formatDescribeText);
48
+ } catch (error) {
49
+ const message = error instanceof Error ? error.message : String(error);
50
+ throw new Error(`Erro ao executar describe: ${message}`);
51
+ }
52
+ }
53
+ function findAreaMatches(normalizedQuery, candidates, config) {
54
+ const matches = [];
55
+ for (const candidate of candidates) {
56
+ const searchableText = `${candidate.id} ${candidate.name} ${candidate.description}`.toLowerCase();
57
+ const hasDirectMatch = searchableText.includes(normalizedQuery);
58
+ const queryWords = normalizedQuery.split(/\s+/);
59
+ const allWordsMatch = queryWords.every((word) => searchableText.includes(word));
60
+ if (hasDirectMatch || allWordsMatch) {
61
+ const areaFiles = getAreaFiles(candidate.id, config);
62
+ const score = calculateRelevanceScore(normalizedQuery, searchableText);
63
+ matches.push({
64
+ id: candidate.id,
65
+ name: candidate.name,
66
+ description: candidate.description || "Sem descri\xE7\xE3o",
67
+ files: areaFiles,
68
+ fileCount: areaFiles.length,
69
+ score
70
+ });
71
+ }
72
+ }
73
+ return matches.sort((a, b) => a.score - b.score);
74
+ }
75
+ function getAreaFiles(areaId, config) {
76
+ const files = [];
77
+ const areaConfig = config.areas[areaId];
78
+ if (areaConfig?.patterns) {
79
+ files.push(`[Use 'ai-tool area ${areaId}' para ver arquivos completos]`);
80
+ }
81
+ return files;
82
+ }
83
+ function calculateRelevanceScore(query, text) {
84
+ if (text.includes(query)) {
85
+ return 0;
86
+ }
87
+ const queryWords = query.split(/\s+/).filter(Boolean);
88
+ const allWordsPresent = queryWords.every((word) => text.includes(word));
89
+ if (allWordsPresent) {
90
+ return 1;
91
+ }
92
+ return 10;
93
+ }
94
+ function formatDescribeText(result) {
95
+ let out = "";
96
+ if (result.areas.length === 0) {
97
+ out += `\u274C Nenhuma \xE1rea encontrada para: "${result.query}"
98
+
99
+ `;
100
+ if (result.suggestions && result.suggestions.length > 0) {
101
+ out += `\u{1F4A1} Voc\xEA quis dizer?
102
+ `;
103
+ for (const suggestion of result.suggestions) {
104
+ out += ` ${suggestion}
105
+ `;
106
+ }
107
+ out += `
108
+ `;
109
+ }
110
+ out += `\u{1F4D6} Dica: Use 'ai-tool areas' para listar todas as \xE1reas dispon\xEDveis`;
111
+ return out;
112
+ }
113
+ out += `\u{1F50D} Busca: "${result.query}"
114
+
115
+ `;
116
+ for (const area of result.areas) {
117
+ out += `## ${area.name} (${area.id})
118
+ `;
119
+ out += `${area.description}
120
+ `;
121
+ out += `\u{1F4C1} ${area.fileCount} arquivo(s)
122
+
123
+ `;
124
+ if (area.files.length > 0) {
125
+ out += `Arquivos:
126
+ `;
127
+ for (const file of area.files) {
128
+ out += ` \u2022 ${file}
129
+ `;
130
+ }
131
+ out += "\n";
132
+ }
133
+ }
134
+ out += `\u{1F4D6} Pr\xF3ximos passos:
135
+ `;
136
+ out += ` \u2192 ai-tool area <id> - ver detalhes de uma \xE1rea
137
+ `;
138
+ out += ` \u2192 ai-tool context --area=<id> - contexto completo de uma \xE1rea
139
+ `;
140
+ return out;
141
+ }
142
+
143
+ export {
144
+ describe
145
+ };
@@ -3486,20 +3486,21 @@ function extractTriggerInfo(init, triggerName) {
3486
3486
 
3487
3487
  // src/ts/cache.ts
3488
3488
  function indexProject(cwd) {
3489
- const allFiles = getAllCodeFiles(cwd);
3490
- logger.debug(`Indexando ${allFiles.length} arquivos em ${cwd}`);
3489
+ const resolvedCwd = cwd || process.cwd();
3490
+ const allFiles = getAllCodeFiles(resolvedCwd);
3491
+ logger.debug(`Indexando ${allFiles.length} arquivos em ${resolvedCwd}`);
3491
3492
  const functionFiles = allFiles.filter((f) => f.includes("functions/src/"));
3492
3493
  if (functionFiles.length > 0) {
3493
3494
  logger.debug(`Encontrados ${functionFiles.length} arquivos em functions/src/:`, functionFiles);
3494
3495
  logger.debugFunctions(`Arquivos em functions/src/:`);
3495
3496
  functionFiles.forEach((f) => logger.debugFunctions(` - ${f}`));
3496
3497
  }
3497
- const project = createProject2(cwd);
3498
+ const project = createProject2(resolvedCwd);
3498
3499
  let addedCount = 0;
3499
3500
  let errorCount = 0;
3500
3501
  for (const file of allFiles) {
3501
3502
  try {
3502
- project.addSourceFileAtPath(resolve(cwd, file));
3503
+ project.addSourceFileAtPath(resolve(resolvedCwd, file));
3503
3504
  addedCount++;
3504
3505
  } catch {
3505
3506
  errorCount++;
@@ -3524,7 +3525,7 @@ function indexProject(cwd) {
3524
3525
  let symbolCount = 0;
3525
3526
  for (const sourceFile of project.getSourceFiles()) {
3526
3527
  let filePath = sourceFile.getFilePath().replace(/\\/g, "/");
3527
- const cwdNormalized = cwd.replace(/\\/g, "/");
3528
+ const cwdNormalized = resolvedCwd.replace(/\\/g, "/");
3528
3529
  if (filePath.startsWith(cwdNormalized + "/")) {
3529
3530
  filePath = filePath.slice(cwdNormalized.length + 1);
3530
3531
  } else if (filePath.startsWith(cwdNormalized)) {
package/dist/cli.js CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ describe
4
+ } from "./chunk-2ONTJF3R.js";
2
5
  import {
3
6
  VERSION,
4
7
  area,
@@ -13,7 +16,7 @@ import {
13
16
  impact,
14
17
  map,
15
18
  suggest
16
- } from "./chunk-NVJXCSJF.js";
19
+ } from "./chunk-6ZWPDZDS.js";
17
20
 
18
21
  // src/cli.ts
19
22
  import { resolve } from "path";
@@ -30,6 +33,7 @@ COMANDOS:
30
33
  context <arquivo> Extrai assinaturas de um arquivo (funcoes, tipos)
31
34
  context --area=<nome> Contexto consolidado de toda uma area
32
35
  find <termo> Busca simbolos no codigo (funcoes, tipos, etc)
36
+ describe <termo> Busca areas por descricao em linguagem natural
33
37
 
34
38
  AREAS:
35
39
  areas Lista todas as areas/dominios do projeto
@@ -83,6 +87,7 @@ EXEMPLOS:
83
87
  ai-tool find useAuth # Busca definicao e usos
84
88
  ai-tool find User --type=type # Busca apenas tipos
85
89
  ai-tool find login --area=auth # Busca na area auth
90
+ ai-tool describe cache # Busca areas por descricao
86
91
  ai-tool areas
87
92
  ai-tool area auth
88
93
  ai-tool area auth --type=hook
@@ -108,7 +113,7 @@ async function main() {
108
113
  }
109
114
  }
110
115
  if (flags.mcp) {
111
- const { startMcpServer } = await import("./server-L24EN4AN.js");
116
+ const { startMcpServer } = await import("./server-CMKKGDE4.js");
112
117
  await startMcpServer();
113
118
  return;
114
119
  }
@@ -222,6 +227,15 @@ async function main() {
222
227
  trigger: flags.trigger
223
228
  });
224
229
  break;
230
+ case "describe":
231
+ if (!target) {
232
+ console.error("\u274C Erro: termo de busca \xE9 obrigat\xF3rio para o comando describe");
233
+ console.error(" Exemplo: ai-tool describe cache");
234
+ console.error(" Exemplo: ai-tool describe login");
235
+ process.exit(1);
236
+ }
237
+ result = await describe(target, { format, cwd });
238
+ break;
225
239
  default:
226
240
  console.error(`\u274C Comando desconhecido: ${command}`);
227
241
  console.error(" Use 'ai-tool --help' para ver comandos dispon\xEDveis.");
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ import {
44
44
  setFileDescription,
45
45
  suggest,
46
46
  writeConfig
47
- } from "./chunk-NVJXCSJF.js";
47
+ } from "./chunk-6ZWPDZDS.js";
48
48
  export {
49
49
  COMMAND_REFERENCE,
50
50
  VERSION,
@@ -1,3 +1,6 @@
1
+ import {
2
+ describe
3
+ } from "./chunk-2ONTJF3R.js";
1
4
  import {
2
5
  VERSION,
3
6
  area,
@@ -7,15 +10,11 @@ import {
7
10
  context,
8
11
  dead,
9
12
  find,
10
- findSimilar,
11
- formatOutput,
12
13
  functions,
13
14
  impact,
14
15
  map,
15
- parseCommandOptions,
16
- readConfig,
17
16
  suggest
18
- } from "./chunk-NVJXCSJF.js";
17
+ } from "./chunk-6ZWPDZDS.js";
19
18
 
20
19
  // src/mcp/server.ts
21
20
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -23,143 +22,6 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
23
22
 
24
23
  // src/mcp/tools.ts
25
24
  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
25
  function registerAllTools(server2) {
164
26
  server2.registerTool(
165
27
  "aitool_project_map",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justmpm/ai-tool",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
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",
@@ -39,7 +39,7 @@
39
39
  "build": "tsup src/index.ts src/cli.ts --format esm --dts --clean",
40
40
  "dev": "tsup src/index.ts src/cli.ts --format esm --dts --watch",
41
41
  "prepublishOnly": "npm run build",
42
- "test": "node --test",
42
+ "test": "tsx --test",
43
43
  "typecheck": "tsc --noEmit"
44
44
  },
45
45
  "dependencies": {
@@ -52,6 +52,7 @@
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/node": "^22.15.21",
55
+ "tsx": "^4.19.0",
55
56
  "tsup": "^8.5.0",
56
57
  "typescript": "^5.8.3"
57
58
  },