@justmpm/ai-tool 3.21.0 → 3.22.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.
@@ -20,7 +20,7 @@ import {
20
20
  readConfig,
21
21
  recoveryHint,
22
22
  simplifyType
23
- } from "./chunk-RGRCXEIT.js";
23
+ } from "./chunk-ZNVZNLRQ.js";
24
24
 
25
25
  // src/commands/describe.ts
26
26
  var STOPWORDS = /* @__PURE__ */ new Set([
@@ -641,6 +641,23 @@ function resolveEntryPointFile(packageName, entry, cwd) {
641
641
  `Nao foi possivel resolver o arquivo de tipos para "${packageName}/${entry}". Tente usar "deps api ${packageName}" para ver todos os exports disponiveis.`
642
642
  );
643
643
  }
644
+ function getSubExportPaths(packageName, cwd) {
645
+ try {
646
+ const pkgPath = resolvePackagePath(packageName, cwd);
647
+ const pkgJsonPath = join(pkgPath, "package.json");
648
+ if (!existsSync(pkgJsonPath)) return [];
649
+ const raw = readFileSync(pkgJsonPath, "utf-8");
650
+ const pkg = JSON.parse(raw);
651
+ const exportsField = pkg.exports;
652
+ if (!exportsField || typeof exportsField !== "object" || exportsField === null) {
653
+ return [];
654
+ }
655
+ const exports = exportsField;
656
+ return Object.keys(exports).filter((k) => k.startsWith("./")).map((k) => k.slice(2));
657
+ } catch {
658
+ return [];
659
+ }
660
+ }
644
661
  function resolveEntryPoints(pkgPath, fields) {
645
662
  const raw = [];
646
663
  if (fields.types) {
@@ -1533,6 +1550,80 @@ function sortByRelevance(items, publicApiNames) {
1533
1550
  });
1534
1551
  }
1535
1552
 
1553
+ // src/ts/symbol-aliases.ts
1554
+ var ZUSTAND_ALIASES = {
1555
+ package: "zustand",
1556
+ aliases: {
1557
+ middleware: ["StateCreator", "persist", "combine", "devtools", "subscribeWithSelector", "Immer", "devtools"],
1558
+ store: ["create", "useStore", "createStore"],
1559
+ state: ["State", "PartialState", "SetState", "GetState", "StoreState"],
1560
+ subscribe: ["subscribe", "subscribeWithSelector"],
1561
+ immer: ["Immer", "createWithImmer"],
1562
+ devtools: ["devtools", "redux"],
1563
+ persist: ["persist", "createPersist", "PersistStorage"]
1564
+ }
1565
+ };
1566
+ var MUI_ALIASES = {
1567
+ package: "@mui/material",
1568
+ aliases: {
1569
+ styled: ["styled", "sx", "SxProps", "Theme"],
1570
+ theme: ["Theme", "ThemeOptions", "createTheme", "useTheme", "ThemeProvider"],
1571
+ props: ["Props", "PropsWithRef", "SxProps", "HTMLProps", "ComponentProps"],
1572
+ slot: ["Slot", "Slots", "SlotProps"],
1573
+ component: ["ComponentType", "ElementType", "FC", "ForwardRefExoticComponent"]
1574
+ }
1575
+ };
1576
+ var REACT_ALIASES = {
1577
+ package: "react",
1578
+ aliases: {
1579
+ hook: ["useState", "useEffect", "useContext", "useReducer", "useCallback", "useMemo", "useRef", "useImperativeHandle", "useLayoutEffect", "useDebugValue", "useId", "useTransition", "useDeferredValue", "useSyncExternalStore", "useInsertionEffect", "useFormStatus", "useActionState", "useOptimistic", "use"],
1580
+ component: ["Component", "PureComponent", "FC", "FunctionComponent", "forwardRef", "lazy", "memo", "createContext", "createElement", "cloneElement"],
1581
+ context: ["createContext", "useContext"],
1582
+ element: ["createElement", "cloneElement", "isValidElement"]
1583
+ }
1584
+ };
1585
+ var FIREBASE_ALIASES = {
1586
+ package: "firebase",
1587
+ aliases: {
1588
+ auth: ["getAuth", "signInWithPopup", "signInWithEmailAndPassword", "createUserWithEmailAndPassword", "signOut", "onAuthStateChanged"],
1589
+ firestore: ["getFirestore", "collection", "doc", "getDoc", "getDocs", "setDoc", "addDoc", "updateDoc", "deleteDoc", "onSnapshot", "query", "where", "orderBy"],
1590
+ storage: ["getStorage", "ref", "uploadBytes", "uploadString", "getDownloadURL", "deleteObject"],
1591
+ messaging: ["getMessaging", "getToken", "onMessage"]
1592
+ }
1593
+ };
1594
+ var ALIAS_REGISTRY = [
1595
+ ZUSTAND_ALIASES,
1596
+ MUI_ALIASES,
1597
+ REACT_ALIASES,
1598
+ FIREBASE_ALIASES
1599
+ ];
1600
+ function getAliasesForPackage(packageName) {
1601
+ return ALIAS_REGISTRY.find((a) => a.package === packageName);
1602
+ }
1603
+ function resolveConceptualQuery(query, packageName, extracted) {
1604
+ const aliases = getAliasesForPackage(packageName);
1605
+ if (!aliases) return [];
1606
+ const aliasList = aliases.aliases[query.toLowerCase()];
1607
+ if (!aliasList) return [];
1608
+ const results = [];
1609
+ for (const fn of extracted.functions) {
1610
+ if (aliasList.includes(fn.name)) {
1611
+ results.push({ name: fn.name, kind: "function", file: fn.sourceFile ?? "" });
1612
+ }
1613
+ }
1614
+ for (const t of extracted.types) {
1615
+ if (aliasList.includes(t.name)) {
1616
+ results.push({ name: t.name, kind: t.kind, file: t.sourceFile ?? "" });
1617
+ }
1618
+ }
1619
+ return results;
1620
+ }
1621
+ function isConceptualQuery(query, packageName) {
1622
+ const aliases = getAliasesForPackage(packageName);
1623
+ if (!aliases) return false;
1624
+ return query.toLowerCase() in aliases.aliases;
1625
+ }
1626
+
1536
1627
  // src/commands/deps.ts
1537
1628
  function normalizePath(p) {
1538
1629
  return p.replace(/\\/g, "/");
@@ -1681,9 +1772,16 @@ async function depsApi(packageName, options = {}) {
1681
1772
  });
1682
1773
  filteredConstants = extracted.constants.filter(() => kind === "const");
1683
1774
  const realLimit = options.limit ?? 50;
1684
- filteredFunctions = filteredFunctions.slice(0, realLimit);
1685
- filteredTypes = filteredTypes.slice(0, realLimit);
1686
- filteredConstants = filteredConstants.slice(0, realLimit);
1775
+ const realOffset = options.offset ?? 0;
1776
+ filteredFunctions = filteredFunctions.slice(realOffset, realOffset + realLimit);
1777
+ filteredTypes = filteredTypes.slice(realOffset, realOffset + realLimit);
1778
+ filteredConstants = filteredConstants.slice(realOffset, realOffset + realLimit);
1779
+ } else {
1780
+ const realOffset = options.offset ?? 0;
1781
+ const realLimit = options.limit ?? 50;
1782
+ filteredFunctions = filteredFunctions.slice(realOffset, realOffset + realLimit);
1783
+ filteredTypes = filteredTypes.slice(realOffset, realOffset + realLimit);
1784
+ filteredConstants = filteredConstants.slice(realOffset, realOffset + realLimit);
1687
1785
  }
1688
1786
  const availableKindsWhenFilteredEmpty = options.kind && filteredFunctions.length === 0 && filteredTypes.length === 0 ? computeAvailableKinds(extracted, options.kind) : void 0;
1689
1787
  const jsFallback = typeFiles.length > 0 && typeFiles.every(
@@ -1691,6 +1789,7 @@ async function depsApi(packageName, options = {}) {
1691
1789
  );
1692
1790
  const pkgVersion = readPackageJson(packageName, cwd).version;
1693
1791
  const typeFilesMeta = buildTypeFilesMeta(typeFiles, pkgPath);
1792
+ const byKind = options.kind ? computeAvailableKinds(extracted, options.kind) : void 0;
1694
1793
  const result = {
1695
1794
  version: pkgVersion,
1696
1795
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1705,8 +1804,15 @@ async function depsApi(packageName, options = {}) {
1705
1804
  types: filteredTypes.length >= (options.limit ?? 50),
1706
1805
  totalFunctions: filteredFunctions.length,
1707
1806
  totalTypes: filteredTypes.length,
1708
- shown: Math.min(filteredFunctions.length, options.limit ?? 50)
1709
- } : extracted.truncated,
1807
+ shown: Math.min(filteredFunctions.length, options.limit ?? 50),
1808
+ functionsOffset: options.offset,
1809
+ typesOffset: options.offset,
1810
+ byKind
1811
+ } : {
1812
+ ...extracted.truncated,
1813
+ functionsOffset: options.offset,
1814
+ typesOffset: options.offset
1815
+ },
1710
1816
  availableKindsWhenFilteredEmpty,
1711
1817
  typeFilesSummary: typeFilesMeta.summary,
1712
1818
  typeFilesPreview: typeFilesMeta.preview,
@@ -1735,7 +1841,8 @@ async function depsApi(packageName, options = {}) {
1735
1841
  throw new Error(`Erro ao executar deps api: ${message}`);
1736
1842
  }
1737
1843
  }
1738
- var MAX_SEARCH_RESULTS = 15;
1844
+ var DEFAULT_SEARCH_LIMIT = 15;
1845
+ var MAX_SEARCH_LIMIT = 200;
1739
1846
  var AUXILIARY_TYPE_SUFFIXES = [
1740
1847
  "Props",
1741
1848
  "PropsWithRef",
@@ -1953,19 +2060,99 @@ async function depsSearch(packageName, query, options = {}) {
1953
2060
  }
1954
2061
  }
1955
2062
  scored.sort((a, b) => b._score - a._score);
2063
+ const effectiveOffset = options.offset ?? 0;
2064
+ const effectiveLimit = Math.min(options.limit ?? DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT);
2065
+ const kindCountMap = /* @__PURE__ */ new Map();
2066
+ for (const m of scored) {
2067
+ const key = m.kind;
2068
+ const existing = kindCountMap.get(key);
2069
+ if (existing) {
2070
+ existing.total++;
2071
+ } else {
2072
+ kindCountMap.set(key, { total: 1, shown: 0 });
2073
+ }
2074
+ }
2075
+ const fileCountMap = /* @__PURE__ */ new Map();
2076
+ for (const m of scored) {
2077
+ fileCountMap.set(m.file, (fileCountMap.get(m.file) ?? 0) + 1);
2078
+ }
1956
2079
  const filtered = options.kind ? scored.filter((m) => isSemanticKindMatch(m.kind, options.kind)) : scored;
1957
- const matches = filtered.slice(0, MAX_SEARCH_RESULTS).map(({ _score: _, ...match }) => match);
2080
+ const pageMatches = filtered.slice(effectiveOffset, effectiveOffset + effectiveLimit);
2081
+ for (const m of pageMatches) {
2082
+ const k = kindCountMap.get(m.kind);
2083
+ if (k) k.shown++;
2084
+ }
2085
+ const byKind = [];
2086
+ for (const [kind, counts] of kindCountMap.entries()) {
2087
+ byKind.push({ kind, total: counts.total, shown: counts.shown });
2088
+ }
2089
+ byKind.sort((a, b) => b.total - a.total);
2090
+ const byFile = Array.from(fileCountMap.entries()).map(([file, count]) => ({ file, count })).sort((a, b) => b.count - a.count).slice(0, 10);
1958
2091
  const availableKindsWhenEmpty = options.kind && filtered.length === 0 && scored.length > 0 ? [...new Set(scored.map((m) => m.kind))] : void 0;
2092
+ const allMatches = pageMatches.map(({ _score: _, ...match }) => match);
2093
+ if (allMatches.length > 0 && allMatches.length < 3 && !useWildcard) {
2094
+ const isConcept = isConceptualQuery(query, packageName);
2095
+ if (!isConcept) {
2096
+ const aliasResults = resolveConceptualQuery(query, packageName, {
2097
+ functions: extracted.functions,
2098
+ types: extracted.types
2099
+ });
2100
+ for (const r of aliasResults) {
2101
+ if (!allMatches.some((m) => m.name === r.name)) {
2102
+ const fn = extracted.functions.find((f) => f.name === r.name);
2103
+ const t = extracted.types.find((t2) => t2.name === r.name);
2104
+ const signature = fn ? `${fn.name}(${fn.params.map((p) => `${p.name}: ${p.type}`).join(", ")}): ${fn.returnType}` : t?.definition ?? r.name;
2105
+ allMatches.push({ name: r.name, kind: r.kind, file: r.file, signature, fromConcept: query });
2106
+ }
2107
+ }
2108
+ }
2109
+ }
2110
+ if (allMatches.length > 0 && allMatches.length < 3) {
2111
+ const subPaths = getSubExportPaths(packageName, cwd);
2112
+ for (const subPath of subPaths) {
2113
+ try {
2114
+ const subEntryFile = resolveEntryPointFile(packageName, subPath, cwd);
2115
+ const subExtracted = extractPackageApi([subEntryFile], pkgPath, { limit: 200 });
2116
+ const subMatchFn = useWildcard ? (name) => matchWithWildcard(name, query) || matchWithWildcard(stripModulePrefix(name), query) : (name) => {
2117
+ const n = name.toLowerCase();
2118
+ const s = stripModulePrefix(name).toLowerCase();
2119
+ return n === queryLower || s === queryLower;
2120
+ };
2121
+ for (const fn of subExtracted.functions) {
2122
+ if (subMatchFn(fn.name) && !allMatches.some((m) => m.name === fn.name)) {
2123
+ const kind = classifySymbol("function", fn.name, fn.returnType, packageName);
2124
+ const params = fn.params.map((p) => `${p.name}: ${p.type}`).join(", ");
2125
+ const signature = `${fn.name}(${params}): ${fn.returnType}`;
2126
+ const file = relativeTypeFile([subEntryFile], normalizePath(subEntryFile), `${packageName}/${subPath}`, fn.sourceFile);
2127
+ allMatches.push({ name: fn.name, kind, file, signature });
2128
+ }
2129
+ }
2130
+ for (const t of subExtracted.types) {
2131
+ if (subMatchFn(t.name) && !allMatches.some((m) => m.name === t.name)) {
2132
+ const kind = classifySymbol("type", t.name, void 0, packageName) === "hook" ? "hook" : t.kind;
2133
+ const file = relativeTypeFile([subEntryFile], normalizePath(subEntryFile), `${packageName}/${subPath}`, t.sourceFile);
2134
+ allMatches.push({ name: t.name, kind, file, signature: t.definition });
2135
+ }
2136
+ }
2137
+ } catch {
2138
+ }
2139
+ if (allMatches.length >= 10) break;
2140
+ }
2141
+ }
1959
2142
  const pkgVersion = readPackageJson(packageName, cwd).version;
1960
2143
  const result = {
1961
2144
  version: pkgVersion,
1962
2145
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1963
2146
  package: packageName,
1964
2147
  query,
1965
- matches,
2148
+ matches: allMatches,
1966
2149
  summary: {
1967
2150
  total: scored.length,
1968
- files: filesSet.size
2151
+ files: filesSet.size,
2152
+ shown: allMatches.length,
2153
+ hidden: Math.max(0, filtered.length - effectiveOffset - effectiveLimit),
2154
+ byKind: byKind.length > 1 ? byKind : void 0,
2155
+ byFile: byFile.length > 1 ? byFile : void 0
1969
2156
  },
1970
2157
  availableKindsWhenEmpty,
1971
2158
  jsFallback: jsFallback || void 0,
@@ -2404,7 +2404,11 @@ function formatDepsSearchText(result, ctx = "cli") {
2404
2404
  }
2405
2405
  for (let i = 0; i < result.matches.length; i++) {
2406
2406
  const match = result.matches[i];
2407
- out += `${i + 1}. ${match.name} (${match.kind})
2407
+ out += `${i + 1}. ${match.name} (${match.kind})`;
2408
+ if (match.fromConcept) {
2409
+ out += ` [conceito: "${match.fromConcept}"]`;
2410
+ }
2411
+ out += `
2408
2412
  `;
2409
2413
  out += ` ${match.file}
2410
2414
  `;
@@ -2414,12 +2418,36 @@ function formatDepsSearchText(result, ctx = "cli") {
2414
2418
  out += `
2415
2419
  `;
2416
2420
  }
2417
- const shown = result.matches.length;
2421
+ if (result.summary.byKind && result.summary.byKind.length > 1) {
2422
+ out += `POR KIND:
2423
+ `;
2424
+ for (const k of result.summary.byKind) {
2425
+ if (k.total > 0) {
2426
+ const truncInfo = k.shown < k.total ? ` (${k.shown}/${k.total})` : "";
2427
+ out += ` ${k.kind}: ${k.total}${truncInfo}
2428
+ `;
2429
+ }
2430
+ }
2431
+ }
2432
+ if (result.summary.byFile && result.summary.byFile.length > 1) {
2433
+ out += `POR ARQUIVO:
2434
+ `;
2435
+ for (const f of result.summary.byFile) {
2436
+ if (f.count > 1) {
2437
+ out += ` ${f.file}: ${f.count} matches
2438
+ `;
2439
+ }
2440
+ }
2441
+ }
2442
+ const shown = result.summary.shown;
2418
2443
  const total = result.summary.total;
2444
+ const hidden = result.summary.hidden;
2419
2445
  const pluralTotal = total !== 1 ? "s" : "";
2420
2446
  const pluralFiles = result.summary.files !== 1 ? "s" : "";
2421
- if (shown < total) {
2422
- out += `${shown} de ${total} resultado${pluralTotal} em ${result.summary.files} arquivo${pluralFiles}
2447
+ if (hidden > 0) {
2448
+ out += `${shown} de ${total} resultado${pluralTotal} (${hidden} hidden)
2449
+ `;
2450
+ out += ` em ${result.summary.files} arquivo${pluralFiles}
2423
2451
  `;
2424
2452
  } else {
2425
2453
  out += `${total} resultado${pluralTotal} em ${result.summary.files} arquivo${pluralFiles}
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  depsInfo,
5
5
  depsSearch,
6
6
  describe
7
- } from "./chunk-KOMBNMGC.js";
7
+ } from "./chunk-CFJB2JDZ.js";
8
8
  import {
9
9
  VERSION,
10
10
  area,
@@ -20,7 +20,7 @@ import {
20
20
  impact,
21
21
  map,
22
22
  suggest
23
- } from "./chunk-RGRCXEIT.js";
23
+ } from "./chunk-ZNVZNLRQ.js";
24
24
 
25
25
  // src/cli.ts
26
26
  import { resolve } from "path";
@@ -131,7 +131,7 @@ async function main() {
131
131
  }
132
132
  }
133
133
  if (flags.mcp) {
134
- const { startMcpServer } = await import("./server-66R2Y3FA.js");
134
+ const { startMcpServer } = await import("./server-QI5WOPV5.js");
135
135
  await startMcpServer();
136
136
  return;
137
137
  }
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  setFileDescription,
48
48
  suggest,
49
49
  writeConfig
50
- } from "./chunk-RGRCXEIT.js";
50
+ } from "./chunk-ZNVZNLRQ.js";
51
51
  export {
52
52
  VERSION,
53
53
  area,
@@ -3,7 +3,7 @@ import {
3
3
  depsInfo,
4
4
  depsSearch,
5
5
  describe
6
- } from "./chunk-KOMBNMGC.js";
6
+ } from "./chunk-CFJB2JDZ.js";
7
7
  import {
8
8
  VERSION,
9
9
  area,
@@ -19,7 +19,7 @@ import {
19
19
  map,
20
20
  recoveryHint,
21
21
  suggest
22
- } from "./chunk-RGRCXEIT.js";
22
+ } from "./chunk-ZNVZNLRQ.js";
23
23
 
24
24
  // src/mcp/server.ts
25
25
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -691,7 +691,8 @@ Dica: Se a API estiver truncada, use deps_search com wildcards: "Button" (exato)
691
691
  Dica: Para libs muito grandes (MUI, React), use --limit para controlar o output.`,
692
692
  inputSchema: {
693
693
  package: z.string().min(1).describe("Nome do pacote"),
694
- limit: z.number().int().min(1).max(200).optional().describe("Limite de simbolos por tipo (default: 50)"),
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)"),
695
696
  entry: z.string().optional().describe("Sub-export especifico (ex: 'auth' para firebase/firestore)"),
696
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)"),
697
698
  format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
@@ -711,6 +712,7 @@ Dica: Para libs muito grandes (MUI, React), use --limit para controlar o output.
711
712
  format: params.format,
712
713
  cwd: params.cwd,
713
714
  limit: params.limit,
715
+ offset: params.offset,
714
716
  entry: params.entry,
715
717
  kind: params.kind,
716
718
  ctx: "mcp"
@@ -747,6 +749,8 @@ Workflow: deps_info (overview) -> deps_api (API completa) -> deps_search (simbol
747
749
  package: z.string().min(1).describe("Nome do pacote"),
748
750
  query: z.string().min(1).describe("Termo de busca (nome do simbolo)"),
749
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)"),
750
754
  format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
751
755
  cwd: z.string().optional().describe("Diretorio do projeto a analisar")
752
756
  },
@@ -764,6 +768,8 @@ Workflow: deps_info (overview) -> deps_api (API completa) -> deps_search (simbol
764
768
  format: params.format,
765
769
  cwd: params.cwd,
766
770
  kind: params.kind,
771
+ offset: params.offset,
772
+ limit: params.limit,
767
773
  ctx: "mcp"
768
774
  });
769
775
  return { content: [{ type: "text", text: result }] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justmpm/ai-tool",
3
- "version": "3.21.0",
3
+ "version": "3.22.0",
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",