@justmpm/ai-tool 0.8.0 → 0.8.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.
@@ -1368,7 +1368,7 @@ function calculateFilesHash(cwd) {
1368
1368
  try {
1369
1369
  const stat = statSync(fullPath);
1370
1370
  const mtime = stat.mtimeMs;
1371
- hashAccumulator ^= Math.floor(mtime);
1371
+ hashAccumulator += Math.floor(mtime);
1372
1372
  fileCount++;
1373
1373
  if (mtime > maxTimestamp) {
1374
1374
  maxTimestamp = mtime;
@@ -1382,13 +1382,14 @@ function calculateFilesHash(cwd) {
1382
1382
  }
1383
1383
  }
1384
1384
  scanDir(cwd, 0);
1385
- try {
1386
- const configPath = join2(cwd, CACHE_DIR, "areas.config.json");
1387
- if (existsSync2(configPath)) {
1388
- const stat = statSync(configPath);
1389
- hashAccumulator ^= Math.floor(stat.mtimeMs);
1385
+ const configExists2 = existsSync2(join2(cwd, CACHE_DIR, "areas.config.json"));
1386
+ hashAccumulator += configExists2 ? 1 : 0;
1387
+ if (configExists2) {
1388
+ try {
1389
+ const stat = statSync(join2(cwd, CACHE_DIR, "areas.config.json"));
1390
+ hashAccumulator += Math.floor(stat.mtimeMs);
1391
+ } catch {
1390
1392
  }
1391
- } catch {
1392
1393
  }
1393
1394
  return `${fileCount}-${hashAccumulator}-${maxTimestamp}`;
1394
1395
  }
@@ -1563,6 +1564,10 @@ function getFileDescription(cwd, filePath) {
1563
1564
  const config = readConfig(cwd);
1564
1565
  return config.descriptions?.[filePath];
1565
1566
  }
1567
+ function getIgnorePatterns(cwd) {
1568
+ const config = readConfig(cwd);
1569
+ return config.ignore || [];
1570
+ }
1566
1571
 
1567
1572
  // src/areas/detector.ts
1568
1573
  import { minimatch } from "minimatch";
@@ -1822,6 +1827,21 @@ function detectAreasInfo(cwd, filePaths) {
1822
1827
 
1823
1828
  // src/commands/dead.ts
1824
1829
  import { execSync } from "child_process";
1830
+ import { writeFileSync as writeFileSync3, unlinkSync, existsSync as existsSync4 } from "fs";
1831
+ import { join as join4 } from "path";
1832
+ function generateKnipConfig(cwd) {
1833
+ const ignorePatterns = getIgnorePatterns(cwd);
1834
+ if (ignorePatterns.length === 0) {
1835
+ return null;
1836
+ }
1837
+ const knipConfig = {
1838
+ $schema: "https://unpkg.com/knip@5/schema.json",
1839
+ ignore: ignorePatterns
1840
+ };
1841
+ const configPath = join4(cwd, ".knip.ai-tool.json");
1842
+ writeFileSync3(configPath, JSON.stringify(knipConfig, null, 2), "utf-8");
1843
+ return configPath;
1844
+ }
1825
1845
  async function dead(options = {}) {
1826
1846
  const cwd = options.cwd || process.cwd();
1827
1847
  const format = options.format || "text";
@@ -1838,8 +1858,10 @@ async function dead(options = {}) {
1838
1858
  }
1839
1859
  try {
1840
1860
  let knipOutput;
1861
+ const knipConfigPath = generateKnipConfig(cwd);
1862
+ const configFlag = knipConfigPath ? `--config=${knipConfigPath}` : "";
1841
1863
  try {
1842
- const output = execSync("npx knip --reporter=json", {
1864
+ const output = execSync(`npx knip ${configFlag} --reporter=json`, {
1843
1865
  cwd,
1844
1866
  encoding: "utf-8",
1845
1867
  maxBuffer: 50 * 1024 * 1024,
@@ -1857,6 +1879,13 @@ async function dead(options = {}) {
1857
1879
  } else {
1858
1880
  knipOutput = {};
1859
1881
  }
1882
+ } finally {
1883
+ if (knipConfigPath && existsSync4(knipConfigPath)) {
1884
+ try {
1885
+ unlinkSync(knipConfigPath);
1886
+ } catch {
1887
+ }
1888
+ }
1860
1889
  }
1861
1890
  const rawFiles = knipOutput.files || [];
1862
1891
  const { filtered: filteredFiles, excluded: excludedFunctions } = filterCloudFunctionsFalsePositives(rawFiles, cwd);
@@ -2733,8 +2762,8 @@ function formatNotFound2(target, allFiles) {
2733
2762
  }
2734
2763
 
2735
2764
  // src/commands/context.ts
2736
- import { existsSync as existsSync4, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
2737
- import { join as join5, resolve as resolve2, basename, extname as extname3 } from "path";
2765
+ import { existsSync as existsSync5, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
2766
+ import { join as join6, resolve as resolve2, basename, extname as extname3 } from "path";
2738
2767
 
2739
2768
  // src/ts/extractor.ts
2740
2769
  import { Project, SyntaxKind } from "ts-morph";
@@ -2942,7 +2971,7 @@ function extractExports(sourceFile) {
2942
2971
 
2943
2972
  // src/ts/indexer.ts
2944
2973
  import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
2945
- import { join as join4, extname as extname2, resolve } from "path";
2974
+ import { join as join5, extname as extname2, resolve } from "path";
2946
2975
  import { Project as Project2, SyntaxKind as SyntaxKind2, Node as Node2 } from "ts-morph";
2947
2976
  var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
2948
2977
  var DEBUG = process.env.DEBUG_ANALYZE === "true";
@@ -3380,7 +3409,7 @@ function getAllCodeFiles(dir, files = [], baseDir = dir) {
3380
3409
  try {
3381
3410
  const entries = readdirSync2(dir);
3382
3411
  for (const entry of entries) {
3383
- const fullPath = join4(dir, entry);
3412
+ const fullPath = join5(dir, entry);
3384
3413
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) {
3385
3414
  continue;
3386
3415
  }
@@ -3612,12 +3641,12 @@ var CODE_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".
3612
3641
  function findTargetFile3(target, cwd) {
3613
3642
  const normalizedTarget = target.replace(/\\/g, "/");
3614
3643
  const directPath = resolve2(cwd, normalizedTarget);
3615
- if (existsSync4(directPath) && isCodeFile2(directPath)) {
3644
+ if (existsSync5(directPath) && isCodeFile2(directPath)) {
3616
3645
  return normalizedTarget;
3617
3646
  }
3618
3647
  for (const ext of CODE_EXTENSIONS3) {
3619
3648
  const withExt = directPath + ext;
3620
- if (existsSync4(withExt)) {
3649
+ if (existsSync5(withExt)) {
3621
3650
  return normalizedTarget + ext;
3622
3651
  }
3623
3652
  }
@@ -3647,7 +3676,7 @@ function getAllCodeFiles2(dir, files = [], baseDir = dir) {
3647
3676
  try {
3648
3677
  const entries = readdirSync3(dir);
3649
3678
  for (const entry of entries) {
3650
- const fullPath = join5(dir, entry);
3679
+ const fullPath = join6(dir, entry);
3651
3680
  if (shouldIgnore(entry)) {
3652
3681
  continue;
3653
3682
  }
@@ -3858,7 +3887,7 @@ function findRealAreaIdFromIndex(target, areaFiles, config) {
3858
3887
 
3859
3888
  // src/commands/areas.ts
3860
3889
  import { readdirSync as readdirSync4, statSync as statSync4 } from "fs";
3861
- import { join as join6, extname as extname4 } from "path";
3890
+ import { join as join7, extname as extname4 } from "path";
3862
3891
  var CODE_EXTENSIONS4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3863
3892
  var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
3864
3893
  "node_modules",
@@ -3946,7 +3975,7 @@ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
3946
3975
  try {
3947
3976
  const entries = readdirSync4(dir);
3948
3977
  for (const entry of entries) {
3949
- const fullPath = join6(dir, entry);
3978
+ const fullPath = join7(dir, entry);
3950
3979
  if (IGNORED_DIRS2.has(entry) || entry.startsWith(".")) {
3951
3980
  continue;
3952
3981
  }
@@ -3971,7 +4000,7 @@ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
3971
4000
 
3972
4001
  // src/commands/area.ts
3973
4002
  import { readdirSync as readdirSync5, statSync as statSync5 } from "fs";
3974
- import { join as join7, extname as extname5 } from "path";
4003
+ import { join as join8, extname as extname5 } from "path";
3975
4004
  var CODE_EXTENSIONS5 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3976
4005
  var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
3977
4006
  "node_modules",
@@ -4132,7 +4161,7 @@ function getAllCodeFiles4(dir, files = [], baseDir = dir) {
4132
4161
  try {
4133
4162
  const entries = readdirSync5(dir);
4134
4163
  for (const entry of entries) {
4135
- const fullPath = join7(dir, entry);
4164
+ const fullPath = join8(dir, entry);
4136
4165
  if (IGNORED_DIRS3.has(entry) || entry.startsWith(".")) {
4137
4166
  continue;
4138
4167
  }
@@ -4157,7 +4186,7 @@ function getAllCodeFiles4(dir, files = [], baseDir = dir) {
4157
4186
 
4158
4187
  // src/commands/areas-init.ts
4159
4188
  import { readdirSync as readdirSync6, statSync as statSync6 } from "fs";
4160
- import { join as join8, extname as extname6 } from "path";
4189
+ import { join as join9, extname as extname6 } from "path";
4161
4190
  var CODE_EXTENSIONS6 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
4162
4191
  var IGNORED_DIRS4 = /* @__PURE__ */ new Set([
4163
4192
  "node_modules",
@@ -4446,7 +4475,7 @@ function getAllCodeFiles5(dir, files = [], baseDir = dir) {
4446
4475
  try {
4447
4476
  const entries = readdirSync6(dir);
4448
4477
  for (const entry of entries) {
4449
- const fullPath = join8(dir, entry);
4478
+ const fullPath = join9(dir, entry);
4450
4479
  if (IGNORED_DIRS4.has(entry) || entry.startsWith(".")) {
4451
4480
  continue;
4452
4481
  }
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  impact,
14
14
  map,
15
15
  suggest
16
- } from "./chunk-FO7NK3UM.js";
16
+ } from "./chunk-OMEBMR2V.js";
17
17
 
18
18
  // src/cli.ts
19
19
  import { resolve } from "path";
@@ -108,7 +108,7 @@ async function main() {
108
108
  }
109
109
  }
110
110
  if (flags.mcp) {
111
- const { startMcpServer } = await import("./server-VTMQAF6Z.js");
111
+ const { startMcpServer } = await import("./server-JLK3OH5F.js");
112
112
  await startMcpServer();
113
113
  return;
114
114
  }
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ import {
44
44
  setFileDescription,
45
45
  suggest,
46
46
  writeConfig
47
- } from "./chunk-FO7NK3UM.js";
47
+ } from "./chunk-OMEBMR2V.js";
48
48
  export {
49
49
  COMMAND_REFERENCE,
50
50
  VERSION,
@@ -11,7 +11,7 @@ import {
11
11
  impact,
12
12
  map,
13
13
  suggest
14
- } from "./chunk-FO7NK3UM.js";
14
+ } from "./chunk-OMEBMR2V.js";
15
15
 
16
16
  // src/mcp/server.ts
17
17
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -26,7 +26,11 @@ server.registerTool(
26
26
  {
27
27
  title: "Project Map",
28
28
  description: `Mapeia projeto e retorna resumo: contagens por categoria, areas detectadas, alertas.
29
- Use no inicio da sessao. Para detalhes: area_detail, file_context, suggest_reads.`,
29
+ Use no inicio da sessao. Para detalhes: area_detail, file_context, suggest_reads.
30
+
31
+ Parametros:
32
+ - format: text (legivel) ou json (estruturado)
33
+ - cwd: Diretorio do projeto a analisar`,
30
34
  inputSchema: {
31
35
  format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
32
36
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
@@ -65,7 +69,11 @@ server.registerTool(
65
69
  {
66
70
  title: "Dead Code Detector",
67
71
  description: `Detecta codigo morto: arquivos orfaos, exports nao usados, deps npm mortas.
68
- Use antes de refatoracoes ou periodicamente para limpeza.`,
72
+ Use antes de refatoracoes ou periodicamente para limpeza.
73
+
74
+ Parametros:
75
+ - format: text (legivel) ou json (estruturado)
76
+ - cwd: Diretorio do projeto a analisar`,
69
77
  inputSchema: {
70
78
  format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
71
79
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
@@ -103,7 +111,12 @@ server.registerTool(
103
111
  {
104
112
  title: "Impact Analysis",
105
113
  description: `Analisa impacto de modificar um arquivo: upstream (quem importa), downstream (o que importa), riscos.
106
- Use ANTES de editar arquivos para planejar mudancas seguras.`,
114
+ Use ANTES de editar arquivos para planejar mudancas seguras.
115
+
116
+ Parametros:
117
+ - target: Arquivo a analisar (caminho completo, parcial ou nome)
118
+ - format: text (legivel) ou json (estruturado)
119
+ - cwd: Diretorio do projeto a analisar`,
107
120
  inputSchema: {
108
121
  target: z.string().min(1).describe(
109
122
  "Arquivo a analisar: caminho completo, parcial ou nome do arquivo"
@@ -144,12 +157,16 @@ server.registerTool(
144
157
  {
145
158
  title: "Suggest Files to Read",
146
159
  description: `Sugere arquivos para ler ANTES de modificar um arquivo.
147
- Prioriza: tipos usados, dependencias diretas, upstream, testes.`,
160
+ Prioriza: tipos usados, dependencias diretas, upstream, testes.
161
+
162
+ Parametros:
163
+ - target: Arquivo que sera modificado (caminho completo, parcial ou nome)
164
+ - limit: Numero maximo de sugestoes (default: 10, max: 50)`,
148
165
  inputSchema: {
149
166
  target: z.string().min(1).describe(
150
167
  "Arquivo que sera modificado: caminho completo, parcial ou nome"
151
168
  ),
152
- limit: z.number().int().min(1).max(50).default(10).describe("Numero maximo de sugestoes (default: 10)"),
169
+ limit: z.number().int().min(1).max(50).default(10).describe("Numero maximo de sugestoes (default: 10, max: 50)"),
153
170
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
154
171
  },
155
172
  annotations: {
@@ -186,7 +203,11 @@ server.registerTool(
186
203
  {
187
204
  title: "Extract File Context",
188
205
  description: `Extrai assinaturas de funcoes e tipos de um arquivo (sem implementacao).
189
- Use para entender a API publica antes de usar ou modificar.`,
206
+ Use para entender a API publica antes de usar ou modificar.
207
+
208
+ Parametros:
209
+ - target: Arquivo para extrair contexto (caminho completo, parcial ou nome)
210
+ - cwd: Diretorio do projeto a analisar`,
190
211
  inputSchema: {
191
212
  target: z.string().min(1).describe(
192
213
  "Arquivo para extrair contexto: caminho completo, parcial ou nome"
@@ -226,7 +247,11 @@ server.registerTool(
226
247
  {
227
248
  title: "List Project Areas",
228
249
  description: `Lista areas/dominios funcionais do projeto (auth, pets, stripe...).
229
- Diferente de categorias (hook, component). Use area_detail para ver arquivos.`,
250
+ Diferente de categorias (hook, component). Use area_detail para ver arquivos.
251
+
252
+ Parametros:
253
+ - format: text (legivel) ou json (estruturado)
254
+ - cwd: Diretorio do projeto a analisar`,
230
255
  inputSchema: {
231
256
  format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
232
257
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
@@ -264,7 +289,13 @@ server.registerTool(
264
289
  {
265
290
  title: "Area Detail",
266
291
  description: `Mostra arquivos de uma area especifica, agrupados por categoria.
267
- Parametros: target (nome da area), type (filtrar categoria), full (todos arquivos).`,
292
+ Use ID (ex: auth) ou Name (ex: Autentica\xE7\xE3o) para identificar a area.
293
+
294
+ Parametros:
295
+ - target: Nome da area (ex: auth, dashboard, billing)
296
+ - type: Filtrar por categoria (page, component, hook, service, etc)
297
+ - full: Mostrar todos os arquivos (default: resumido)
298
+ - cwd: Diretorio do projeto a analisar`,
268
299
  inputSchema: {
269
300
  target: z.string().min(1).describe("Nome da area: auth, dashboard, billing, etc"),
270
301
  type: z.enum([
@@ -319,7 +350,11 @@ server.registerTool(
319
350
  {
320
351
  title: "Initialize Areas Config",
321
352
  description: `Gera .analyze/areas.config.json para customizar deteccao de areas.
322
- Use quando houver arquivos sem area ou precisar ajustar deteccao.`,
353
+ Use quando houver arquivos sem area ou precisar ajustar deteccao.
354
+
355
+ Parametros:
356
+ - force: Sobrescrever config existente
357
+ - cwd: Diretorio do projeto`,
323
358
  inputSchema: {
324
359
  force: z.boolean().default(false).describe("Sobrescrever config existente"),
325
360
  cwd: z.string().optional().describe("Diretorio do projeto")
@@ -357,11 +392,18 @@ server.registerTool(
357
392
  {
358
393
  title: "Find Symbol",
359
394
  description: `Busca simbolos no codigo: funcoes, tipos, componentes, hooks, constantes.
360
- Retorna definicao + referencias/usos. Diferente de grep, entende o AST do TypeScript.`,
395
+ Retorna definicao + referencias/usos. Diferente de grep, entende o AST do TypeScript.
396
+
397
+ Parametros:
398
+ - query: Termo a buscar (ex: useAuth, User, login)
399
+ - type: Filtrar por tipo (function, type, const, component, hook, trigger, all)
400
+ - area: Buscar apenas em uma area especifica (ex: auth, dashboard)
401
+ - def: Mostrar apenas definicoes (onde e declarado)
402
+ - refs: Mostrar apenas referencias/usos`,
361
403
  inputSchema: {
362
404
  query: z.string().min(1).describe("Termo a buscar (nome de funcao, tipo, componente, etc)"),
363
- type: z.enum(["function", "type", "const", "component", "hook", "all"]).default("all").describe("Filtrar por tipo de simbolo"),
364
- area: z.string().optional().describe("Filtrar busca por area especifica"),
405
+ type: z.enum(["function", "type", "const", "component", "hook", "trigger", "all"]).default("all").describe("Filtrar por tipo de simbolo (use trigger para Cloud Functions)"),
406
+ area: z.string().optional().describe("Filtrar busca por area especifica (ex: auth, dashboard)"),
365
407
  def: z.boolean().default(false).describe("Mostrar apenas definicoes (onde e declarado)"),
366
408
  refs: z.boolean().default(false).describe("Mostrar apenas referencias (onde e usado)"),
367
409
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
@@ -403,7 +445,11 @@ server.registerTool(
403
445
  {
404
446
  title: "Area Context",
405
447
  description: `Contexto consolidado de toda uma area: tipos, hooks, funcoes, componentes, services, stores.
406
- Uma chamada = entender toda a feature. Muito mais eficiente que chamar context em cada arquivo.`,
448
+ Uma chamada = entender toda a feature. Muito mais eficiente que chamar context em cada arquivo.
449
+
450
+ Parametros:
451
+ - area: Nome da area (ex: auth, dashboard, payments)
452
+ - cwd: Diretorio do projeto a analisar`,
407
453
  inputSchema: {
408
454
  area: z.string().min(1).describe("Nome da area: auth, dashboard, payments, etc"),
409
455
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
@@ -442,9 +488,13 @@ server.registerTool(
442
488
  title: "List Cloud Functions",
443
489
  description: `Lista todas as Cloud Functions Firebase do projeto.
444
490
  Agrupa por tipo de trigger (onCall, onDocumentCreated, onSchedule, etc).
445
- Use para entender a arquitetura serverless antes de modificar triggers.`,
491
+ Use para entender a arquitetura serverless antes de modificar triggers.
492
+
493
+ Parametros:
494
+ - trigger: Filtrar por tipo de trigger (ex: onCall, onDocumentCreated, onSchedule)
495
+ - format: text (legivel) ou json (estruturado)`,
446
496
  inputSchema: {
447
- trigger: z.string().optional().describe("Filtrar por tipo de trigger (ex: onCall, onDocumentCreated)"),
497
+ trigger: z.string().optional().describe("Filtrar por tipo de trigger (ex: onCall, onDocumentCreated, onSchedule)"),
448
498
  format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
449
499
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
450
500
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justmpm/ai-tool",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript. Usa Skott + Knip internamente.",
5
5
  "keywords": [
6
6
  "dependency-analysis",