@justmpm/ai-tool 3.20.1 → 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-XDFC3IL7.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) {
@@ -930,21 +947,30 @@ function collectRemainingIndexFiles(packagePath, existingFiles, results, maxFile
930
947
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
931
948
  import { dirname, join as join2, resolve as resolve2 } from "path";
932
949
  import { Project } from "ts-morph";
933
- function classifyFunctionKind(name, returnType) {
950
+
951
+ // src/ts/kind-resolvers.ts
952
+ var REACT_COMPONENT_FACTORIES = /* @__PURE__ */ new Set([
953
+ // React API principal
954
+ "memo",
955
+ "forwardRef",
956
+ "lazy",
957
+ "createContext",
958
+ "createElement",
959
+ "cloneElement",
960
+ "createFactory",
961
+ // Hooks que retornam componentes
962
+ "useCallback",
963
+ "useMemo",
964
+ // Suspense e ErrorBoundary
965
+ "Suspense",
966
+ "ErrorBoundary",
967
+ // Factory patterns
968
+ "createComponent",
969
+ "forwardRefLazy"
970
+ ]);
971
+ function isReactComponentFactory(name) {
934
972
  const cleanName = stripModulePrefix(name);
935
- if (cleanName.startsWith("use") && cleanName.length > 3 && cleanName[3] === cleanName[3].toUpperCase()) {
936
- return "hook";
937
- }
938
- if (/^[A-Z]/.test(cleanName)) {
939
- if (returnType) {
940
- const rt = returnType.toLowerCase();
941
- if (rt.includes("jsx.element") || rt.includes("reactelement") || rt.includes("reactnode") || rt.includes("react.fc") || rt.includes("elementtype")) {
942
- return "component";
943
- }
944
- }
945
- return "function";
946
- }
947
- return "function";
973
+ return REACT_COMPONENT_FACTORIES.has(cleanName);
948
974
  }
949
975
  function stripModulePrefix(name) {
950
976
  const dotIdx = name.indexOf(".");
@@ -958,6 +984,144 @@ function stripModulePrefix(name) {
958
984
  }
959
985
  return name;
960
986
  }
987
+ var defaultResolver = {
988
+ canHandle: () => true,
989
+ // Fallback - sempre se aplica
990
+ classifyFunction(name, returnType) {
991
+ const cleanName = stripModulePrefix(name);
992
+ if (cleanName.startsWith("use") && cleanName.length > 3 && cleanName[3] === cleanName[3].toUpperCase()) {
993
+ return "hook";
994
+ }
995
+ if (isReactComponentFactory(name)) {
996
+ return "component";
997
+ }
998
+ if (/^[A-Z]/.test(cleanName)) {
999
+ if (returnType) {
1000
+ const rt = returnType.toLowerCase();
1001
+ if (rt.includes("jsx.element") || rt.includes("reactelement") || rt.includes("reactnode") || rt.includes("react.fc") || rt.includes("elementtype") || rt.includes("react.componenttype") || rt.includes("fc<") || rt.includes(".element")) {
1002
+ return "component";
1003
+ }
1004
+ }
1005
+ return "function";
1006
+ }
1007
+ return "function";
1008
+ },
1009
+ classifyType(_name, kind) {
1010
+ return kind;
1011
+ }
1012
+ };
1013
+ var reactResolver = {
1014
+ canHandle(packageName) {
1015
+ return packageName === "react" || packageName === "@types/react" || packageName === "react-dom" || packageName === "@types/react-dom";
1016
+ },
1017
+ classifyFunction(name, returnType) {
1018
+ const cleanName = stripModulePrefix(name);
1019
+ const reactHooks = /* @__PURE__ */ new Set([
1020
+ // Hooks basicos
1021
+ "useState",
1022
+ "useEffect",
1023
+ "useContext",
1024
+ "useReducer",
1025
+ "useCallback",
1026
+ "useMemo",
1027
+ "useRef",
1028
+ "useImperativeHandle",
1029
+ "useLayoutEffect",
1030
+ "useDebugValue",
1031
+ "useId",
1032
+ // Hooks adicionais
1033
+ "useTransition",
1034
+ "useDeferredValue",
1035
+ "useSyncExternalStore",
1036
+ "useInsertionEffect",
1037
+ "useFormStatus",
1038
+ "useActionState",
1039
+ "useOptimistic",
1040
+ "use"
1041
+ ]);
1042
+ if (reactHooks.has(cleanName)) {
1043
+ return "hook";
1044
+ }
1045
+ if (isReactComponentFactory(name)) {
1046
+ return "component";
1047
+ }
1048
+ if (cleanName.startsWith("use") && cleanName.length > 3 && cleanName[3] === cleanName[3].toUpperCase()) {
1049
+ return "hook";
1050
+ }
1051
+ if (/^[A-Z]/.test(cleanName)) {
1052
+ if (returnType) {
1053
+ const rt = returnType.toLowerCase();
1054
+ if (rt.includes("jsx.element") || rt.includes("reactelement") || rt.includes("reactnode") || rt.includes("react.fc") || rt.includes("elementtype") || rt.includes("react.componenttype") || rt.includes("fc<") || rt.includes(".element")) {
1055
+ return "component";
1056
+ }
1057
+ }
1058
+ return "function";
1059
+ }
1060
+ return "function";
1061
+ },
1062
+ classifyType(name, kind) {
1063
+ const cleanName = stripModulePrefix(name);
1064
+ const reactComponentInterfaces = /* @__PURE__ */ new Set([
1065
+ "FC",
1066
+ "FunctionComponent",
1067
+ "ComponentType",
1068
+ "ElementType",
1069
+ "Element",
1070
+ "ReactElement",
1071
+ "ReactNode",
1072
+ "Component",
1073
+ "PureComponent",
1074
+ "ForwardRefExoticComponent",
1075
+ "ExoticComponent"
1076
+ ]);
1077
+ if (reactComponentInterfaces.has(cleanName)) {
1078
+ return "component";
1079
+ }
1080
+ return kind;
1081
+ }
1082
+ };
1083
+ var resolvers = [
1084
+ reactResolver,
1085
+ defaultResolver
1086
+ // Fallback - deve ser o ultimo
1087
+ ];
1088
+ function getResolver(packageName) {
1089
+ for (const resolver of resolvers) {
1090
+ if (resolver.canHandle(packageName)) {
1091
+ return resolver;
1092
+ }
1093
+ }
1094
+ return defaultResolver;
1095
+ }
1096
+ function classifyFunctionKind(name, returnType, packageName) {
1097
+ const resolver = packageName ? getResolver(packageName) : defaultResolver;
1098
+ return resolver.classifyFunction(name, returnType);
1099
+ }
1100
+ function classifyTypeKind(name, kind, packageName) {
1101
+ const resolver = packageName ? getResolver(packageName) : defaultResolver;
1102
+ return resolver.classifyType(name, kind);
1103
+ }
1104
+ function classifySymbol(symbolKind, name, returnType, packageName) {
1105
+ if (symbolKind === "function") {
1106
+ return classifyFunctionKind(name, returnType, packageName);
1107
+ }
1108
+ if (symbolKind === "type" || symbolKind === "interface" || symbolKind === "enum") {
1109
+ return classifyTypeKind(name, symbolKind, packageName);
1110
+ }
1111
+ return "const";
1112
+ }
1113
+ function isSemanticKindMatch(actualKind, requestedKind) {
1114
+ if (actualKind === requestedKind) return true;
1115
+ if (requestedKind === "type") {
1116
+ return actualKind === "interface" || actualKind === "enum" || actualKind === "const";
1117
+ }
1118
+ if (requestedKind === "function") {
1119
+ return actualKind === "component" || actualKind === "hook";
1120
+ }
1121
+ return false;
1122
+ }
1123
+
1124
+ // src/ts/package-extractor.ts
961
1125
  var MAX_REEXPORT_DEPTH = 3;
962
1126
  var MAX_TOTAL_FILES = 1200;
963
1127
  var TYPE_DECLARATION_EXTENSIONS = [".d.ts", ".d.cts", ".d.mts", ".d.tsx"];
@@ -1284,7 +1448,7 @@ function extractPackageApiImpl(project, typeFiles, packagePath, limit, jsFallbac
1284
1448
  allFunctions.push({
1285
1449
  ...fn,
1286
1450
  sourceFile: relativeSource,
1287
- kind: classifyFunctionKind(fn.name, fn.returnType)
1451
+ kind: classifySymbol("function", fn.name, fn.returnType)
1288
1452
  });
1289
1453
  }
1290
1454
  }
@@ -1386,38 +1550,106 @@ function sortByRelevance(items, publicApiNames) {
1386
1550
  });
1387
1551
  }
1388
1552
 
1389
- // src/commands/deps.ts
1390
- function stripModulePrefix2(name) {
1391
- const dotIdx = name.indexOf(".");
1392
- if (dotIdx === -1) return name;
1393
- const prefix = name.slice(0, dotIdx);
1394
- if (prefix.startsWith('"') || prefix.startsWith("'")) {
1395
- return name.slice(dotIdx + 1);
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"]
1396
1564
  }
1397
- if (/^[A-Za-z]+$/.test(prefix) && /^[A-Z]/.test(prefix)) {
1398
- return name.slice(dotIdx + 1);
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"]
1399
1574
  }
1400
- return name;
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);
1401
1602
  }
1402
- function classifyFunctionKind2(name, returnType) {
1403
- const cleanName = stripModulePrefix2(name);
1404
- if (cleanName.startsWith("use") && cleanName.length > 3 && cleanName[3] === cleanName[3].toUpperCase()) {
1405
- return "hook";
1406
- }
1407
- if (/^[A-Z]/.test(cleanName)) {
1408
- if (returnType) {
1409
- const rt = returnType.toLowerCase();
1410
- if (rt.includes("jsx.element") || rt.includes("reactelement") || rt.includes("reactnode") || rt.includes("react.fc") || rt.includes("elementtype")) {
1411
- return "component";
1412
- }
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 ?? "" });
1413
1612
  }
1414
- return "function";
1415
1613
  }
1416
- return "function";
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;
1417
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
+
1627
+ // src/commands/deps.ts
1418
1628
  function normalizePath(p) {
1419
1629
  return p.replace(/\\/g, "/");
1420
1630
  }
1631
+ function computeAvailableKinds(extracted, requestedKind) {
1632
+ const kindCounts = /* @__PURE__ */ new Map();
1633
+ for (const fn of extracted.functions) {
1634
+ const kind = classifySymbol("function", fn.name, fn.returnType);
1635
+ if (isSemanticKindMatch(kind, requestedKind)) {
1636
+ kindCounts.set(kind, (kindCounts.get(kind) ?? 0) + 1);
1637
+ }
1638
+ }
1639
+ for (const t of extracted.types) {
1640
+ const kind = classifySymbol("type", t.name);
1641
+ if (isSemanticKindMatch(kind, requestedKind)) {
1642
+ kindCounts.set(kind, (kindCounts.get(kind) ?? 0) + 1);
1643
+ }
1644
+ }
1645
+ for (const c of extracted.constants) {
1646
+ const kind = classifySymbol("const", c.name);
1647
+ if (isSemanticKindMatch(kind, requestedKind)) {
1648
+ kindCounts.set(kind, (kindCounts.get(kind) ?? 0) + 1);
1649
+ }
1650
+ }
1651
+ return Array.from(kindCounts.entries()).map(([kind, count]) => ({ kind, count })).sort((a, b) => b.count - a.count);
1652
+ }
1421
1653
  function relativeTypeFile(typeFiles, normalizedPkgPath, packageName, sourceFile) {
1422
1654
  if (sourceFile) {
1423
1655
  return `${packageName}/${sourceFile}`;
@@ -1427,16 +1659,6 @@ function relativeTypeFile(typeFiles, normalizedPkgPath, packageName, sourceFile)
1427
1659
  }
1428
1660
  return normalizePath(typeFiles[0]).replace(normalizedPkgPath, packageName);
1429
1661
  }
1430
- function isKindMatch(actualKind, requestedKind) {
1431
- if (actualKind === requestedKind) return true;
1432
- if (requestedKind === "type") {
1433
- return actualKind === "interface" || actualKind === "enum" || actualKind === "const";
1434
- }
1435
- if (requestedKind === "function") {
1436
- return actualKind === "component" || actualKind === "hook";
1437
- }
1438
- return false;
1439
- }
1440
1662
  var TYPE_FILES_COMPACT_THRESHOLD = 10;
1441
1663
  var TYPE_FILES_PREVIEW_COUNT = 5;
1442
1664
  function buildTypeFilesMeta(typeFiles, pkgPath) {
@@ -1543,22 +1765,31 @@ async function depsApi(packageName, options = {}) {
1543
1765
  let filteredConstants = extracted.constants;
1544
1766
  if (options.kind) {
1545
1767
  const kind = options.kind;
1546
- filteredFunctions = extracted.functions.filter((fn) => classifyFunctionKind2(fn.name, fn.returnType) === kind);
1768
+ filteredFunctions = extracted.functions.filter((fn) => classifySymbol("function", fn.name, fn.returnType) === kind);
1547
1769
  filteredTypes = extracted.types.filter((t) => {
1548
- const resolvedKind = classifyFunctionKind2(t.name) === "hook" ? "hook" : t.kind;
1770
+ const resolvedKind = classifySymbol("type", t.name) === "hook" ? "hook" : t.kind;
1549
1771
  return resolvedKind === kind;
1550
1772
  });
1551
1773
  filteredConstants = extracted.constants.filter(() => kind === "const");
1552
1774
  const realLimit = options.limit ?? 50;
1553
- filteredFunctions = filteredFunctions.slice(0, realLimit);
1554
- filteredTypes = filteredTypes.slice(0, realLimit);
1555
- 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);
1556
1785
  }
1786
+ const availableKindsWhenFilteredEmpty = options.kind && filteredFunctions.length === 0 && filteredTypes.length === 0 ? computeAvailableKinds(extracted, options.kind) : void 0;
1557
1787
  const jsFallback = typeFiles.length > 0 && typeFiles.every(
1558
1788
  (f) => f.endsWith(".js") || f.endsWith(".mjs") || f.endsWith(".cjs")
1559
1789
  );
1560
1790
  const pkgVersion = readPackageJson(packageName, cwd).version;
1561
1791
  const typeFilesMeta = buildTypeFilesMeta(typeFiles, pkgPath);
1792
+ const byKind = options.kind ? computeAvailableKinds(extracted, options.kind) : void 0;
1562
1793
  const result = {
1563
1794
  version: pkgVersion,
1564
1795
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1573,8 +1804,16 @@ async function depsApi(packageName, options = {}) {
1573
1804
  types: filteredTypes.length >= (options.limit ?? 50),
1574
1805
  totalFunctions: filteredFunctions.length,
1575
1806
  totalTypes: filteredTypes.length,
1576
- shown: Math.min(filteredFunctions.length, options.limit ?? 50)
1577
- } : 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
+ },
1816
+ availableKindsWhenFilteredEmpty,
1578
1817
  typeFilesSummary: typeFilesMeta.summary,
1579
1818
  typeFilesPreview: typeFilesMeta.preview,
1580
1819
  jsFallback: jsFallback || void 0,
@@ -1602,7 +1841,8 @@ async function depsApi(packageName, options = {}) {
1602
1841
  throw new Error(`Erro ao executar deps api: ${message}`);
1603
1842
  }
1604
1843
  }
1605
- var MAX_SEARCH_RESULTS = 15;
1844
+ var DEFAULT_SEARCH_LIMIT = 15;
1845
+ var MAX_SEARCH_LIMIT = 200;
1606
1846
  var AUXILIARY_TYPE_SUFFIXES = [
1607
1847
  "Props",
1608
1848
  "PropsWithRef",
@@ -1739,20 +1979,20 @@ async function depsSearch(packageName, query, options = {}) {
1739
1979
  const filesSet = /* @__PURE__ */ new Set();
1740
1980
  const normalizedPkgPath = normalizePath(pkgPath);
1741
1981
  const useWildcard = hasWildcard(query);
1742
- const matchFn = useWildcard ? (name) => matchWithWildcard(name, query) || matchWithWildcard(stripModulePrefix2(name), query) : (name) => {
1982
+ const matchFn = useWildcard ? (name) => matchWithWildcard(name, query) || matchWithWildcard(stripModulePrefix(name), query) : (name) => {
1743
1983
  const n = name.toLowerCase();
1744
- const s = stripModulePrefix2(name).toLowerCase();
1984
+ const s = stripModulePrefix(name).toLowerCase();
1745
1985
  return n === queryLower || s === queryLower;
1746
1986
  };
1747
1987
  const scoreFn = useWildcard ? (name, kind, isExported) => calculateWildcardMatchScore(name, query, kind, isExported) : (name, kind, isExported) => calculateMatchScore(name, queryLower, kind, isExported);
1748
1988
  const fuzzyMatchFn = (name) => {
1749
1989
  const n = name.toLowerCase();
1750
- const s = stripModulePrefix2(name).toLowerCase();
1990
+ const s = stripModulePrefix(name).toLowerCase();
1751
1991
  return (n.includes(queryLower) || s.includes(queryLower)) && n !== queryLower && s !== queryLower;
1752
1992
  };
1753
1993
  for (const fn of extracted.functions) {
1754
1994
  if (matchFn(fn.name)) {
1755
- const kind = classifyFunctionKind2(fn.name, fn.returnType);
1995
+ const kind = classifySymbol("function", fn.name, fn.returnType, packageName);
1756
1996
  const score = scoreFn(fn.name, kind, fn.isExported);
1757
1997
  const params = fn.params.map((p) => `${p.name}: ${p.type}`).join(", ");
1758
1998
  const signature = `${fn.name}(${params}): ${fn.returnType}`;
@@ -1763,7 +2003,7 @@ async function depsSearch(packageName, query, options = {}) {
1763
2003
  }
1764
2004
  for (const t of extracted.types) {
1765
2005
  if (matchFn(t.name)) {
1766
- const kind = classifyFunctionKind2(t.name) === "hook" ? "hook" : t.kind;
2006
+ const kind = classifySymbol("type", t.name, void 0, packageName) === "hook" ? "hook" : t.kind;
1767
2007
  const score = scoreFn(t.name, kind, t.isExported);
1768
2008
  const file = relativeTypeFile(typeFiles, normalizedPkgPath, packageName, t.sourceFile);
1769
2009
  scored.push({ name: t.name, kind, file, signature: t.definition, _score: score });
@@ -1783,7 +2023,7 @@ async function depsSearch(packageName, query, options = {}) {
1783
2023
  const matchedNames = new Set(scored.map((m) => m.name));
1784
2024
  for (const fn of extracted.functions) {
1785
2025
  if (matchedNames.has(fn.name)) continue;
1786
- const kind = classifyFunctionKind2(fn.name, fn.returnType);
2026
+ const kind = classifySymbol("function", fn.name, fn.returnType, packageName);
1787
2027
  if (exactKinds.has(kind)) continue;
1788
2028
  if (!fuzzyMatchFn(fn.name)) continue;
1789
2029
  const score = scoreFn(fn.name, kind, fn.isExported);
@@ -1797,7 +2037,7 @@ async function depsSearch(packageName, query, options = {}) {
1797
2037
  }
1798
2038
  for (const t of extracted.types) {
1799
2039
  if (matchedNames.has(t.name)) continue;
1800
- const kind = classifyFunctionKind2(t.name) === "hook" ? "hook" : t.kind;
2040
+ const kind = classifySymbol("type", t.name, void 0, packageName) === "hook" ? "hook" : t.kind;
1801
2041
  if (exactKinds.has(kind)) continue;
1802
2042
  if (!fuzzyMatchFn(t.name)) continue;
1803
2043
  const score = scoreFn(t.name, kind, t.isExported);
@@ -1820,19 +2060,101 @@ async function depsSearch(packageName, query, options = {}) {
1820
2060
  }
1821
2061
  }
1822
2062
  scored.sort((a, b) => b._score - a._score);
1823
- const filtered = options.kind ? scored.filter((m) => isKindMatch(m.kind, options.kind)) : scored;
1824
- const matches = filtered.slice(0, MAX_SEARCH_RESULTS).map(({ _score: _, ...match }) => match);
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
+ }
2079
+ const filtered = options.kind ? scored.filter((m) => isSemanticKindMatch(m.kind, options.kind)) : scored;
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);
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
+ }
1825
2142
  const pkgVersion = readPackageJson(packageName, cwd).version;
1826
2143
  const result = {
1827
2144
  version: pkgVersion,
1828
2145
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1829
2146
  package: packageName,
1830
2147
  query,
1831
- matches,
2148
+ matches: allMatches,
1832
2149
  summary: {
1833
2150
  total: scored.length,
1834
- 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
1835
2156
  },
2157
+ availableKindsWhenEmpty,
1836
2158
  jsFallback: jsFallback || void 0,
1837
2159
  typesSource: resolved.source,
1838
2160
  typesPackage: resolved.typesPackage
@@ -2337,6 +2337,16 @@ function formatDepsApiText(result, ctx = "cli") {
2337
2337
  `;
2338
2338
  }
2339
2339
  out += `Use ${hint("deps_search", ctx, { "<pacote>": result.package, "<termo>": "*Symbol*" })} para buscar simbolos especificos
2340
+ `;
2341
+ }
2342
+ if (result.availableKindsWhenFilteredEmpty && result.availableKindsWhenFilteredEmpty.length > 0) {
2343
+ const kinds = result.availableKindsWhenFilteredEmpty.map((k) => `${k.kind} (${k.count})`).join(", ");
2344
+ out += `
2345
+ Filtro --kind nao encontrou simbolos, mas ha outros tipos disponiveis:
2346
+ `;
2347
+ out += ` ${kinds}
2348
+ `;
2349
+ out += ` Use ${hint("deps_api", ctx, { "<pacote>": result.package })} sem filtro --kind para ver tudo
2340
2350
  `;
2341
2351
  }
2342
2352
  out += nextSteps("deps_api", ctx);
@@ -2364,6 +2374,16 @@ function formatDepsSearchText(result, ctx = "cli") {
2364
2374
  if (result.matches.length === 0) {
2365
2375
  out += `Nenhum simbolo encontrado para "${result.query}".
2366
2376
  `;
2377
+ if (result.availableKindsWhenEmpty && result.availableKindsWhenEmpty.length > 0) {
2378
+ const kinds = result.availableKindsWhenEmpty.map((k) => `"${k}"`).join(", ");
2379
+ out += `
2380
+ Simbolos encontrados, mas como tipo diferente:
2381
+ `;
2382
+ out += ` Kinds disponiveis: ${kinds}
2383
+ `;
2384
+ out += ` Tente: ${hint("deps_search", ctx, { "<pacote>": result.package, "<termo>": result.query })} sem filtro --kind
2385
+ `;
2386
+ }
2367
2387
  if (result.typesSource === void 0 && !result.jsFallback) {
2368
2388
  out += `
2369
2389
  Pacote "${result.package}" nao possui TypeScript declarations.
@@ -2384,7 +2404,11 @@ function formatDepsSearchText(result, ctx = "cli") {
2384
2404
  }
2385
2405
  for (let i = 0; i < result.matches.length; i++) {
2386
2406
  const match = result.matches[i];
2387
- 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 += `
2388
2412
  `;
2389
2413
  out += ` ${match.file}
2390
2414
  `;
@@ -2394,12 +2418,36 @@ function formatDepsSearchText(result, ctx = "cli") {
2394
2418
  out += `
2395
2419
  `;
2396
2420
  }
2397
- 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;
2398
2443
  const total = result.summary.total;
2444
+ const hidden = result.summary.hidden;
2399
2445
  const pluralTotal = total !== 1 ? "s" : "";
2400
2446
  const pluralFiles = result.summary.files !== 1 ? "s" : "";
2401
- if (shown < total) {
2402
- 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}
2403
2451
  `;
2404
2452
  } else {
2405
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-IC74XLRN.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-XDFC3IL7.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-V3N7JTNN.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-XDFC3IL7.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-IC74XLRN.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-XDFC3IL7.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.20.1",
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",