@justmpm/ai-tool 0.5.4 → 0.6.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.
@@ -858,9 +858,237 @@ function formatAreaDetailText(result, options = {}) {
858
858
  }
859
859
  return out;
860
860
  }
861
+ function formatFindText(result) {
862
+ let out = "";
863
+ out += `
864
+ `;
865
+ out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
866
+ `;
867
+ out += `\u2551 \u{1F50D} FIND \u2551
868
+ `;
869
+ out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
870
+
871
+ `;
872
+ out += `\u{1F50D} "${result.query}"`;
873
+ if (result.filters.type) {
874
+ out += ` [type: ${result.filters.type}]`;
875
+ }
876
+ if (result.filters.area) {
877
+ out += ` [area: ${result.filters.area}]`;
878
+ }
879
+ out += `
880
+
881
+ `;
882
+ if (!result.definition && result.references.length === 0) {
883
+ out += `\u274C Nenhum resultado encontrado para "${result.query}"
884
+
885
+ `;
886
+ out += `\u{1F4A1} Dicas:
887
+ `;
888
+ out += ` \u2022 Verifique a ortografia
889
+ `;
890
+ out += ` \u2022 Tente buscar parte do nome
891
+ `;
892
+ out += ` \u2022 Remova filtros de tipo ou \xE1rea
893
+ `;
894
+ return out;
895
+ }
896
+ out += `\u{1F4CA} ${result.summary.definitions} defini\xE7\xE3o, ${result.summary.references} refer\xEAncias em ${result.summary.files} arquivos
897
+
898
+ `;
899
+ out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
900
+
901
+ `;
902
+ if (result.definition) {
903
+ out += `\u{1F4CD} DEFINI\xC7\xC3O
904
+
905
+ `;
906
+ const def = result.definition;
907
+ const icon = categoryIcons[def.category];
908
+ out += ` ${icon} ${def.file}:${def.line}
909
+ `;
910
+ out += ` ${def.code}
911
+ `;
912
+ out += `
913
+ `;
914
+ }
915
+ if (result.references.length > 0) {
916
+ const imports = result.references.filter((r) => r.matchType === "import");
917
+ const usages = result.references.filter((r) => r.matchType === "usage");
918
+ if (imports.length > 0) {
919
+ out += `\u{1F4E5} IMPORTS (${imports.length})
920
+
921
+ `;
922
+ for (const ref of imports.slice(0, 10)) {
923
+ const icon = categoryIcons[ref.category];
924
+ out += ` ${icon} ${ref.file}:${ref.line}
925
+ `;
926
+ out += ` ${ref.code}
927
+ `;
928
+ }
929
+ if (imports.length > 10) {
930
+ out += ` ... e mais ${imports.length - 10}
931
+ `;
932
+ }
933
+ out += `
934
+ `;
935
+ }
936
+ if (usages.length > 0) {
937
+ out += `\u26A1 USOS (${usages.length})
938
+
939
+ `;
940
+ for (const ref of usages.slice(0, 15)) {
941
+ const icon = categoryIcons[ref.category];
942
+ out += ` ${icon} ${ref.file}:${ref.line}
943
+ `;
944
+ out += ` ${ref.code}
945
+ `;
946
+ }
947
+ if (usages.length > 15) {
948
+ out += ` ... e mais ${usages.length - 15}
949
+ `;
950
+ }
951
+ out += `
952
+ `;
953
+ }
954
+ }
955
+ return out;
956
+ }
957
+ function formatAreaContextText(result) {
958
+ let out = "";
959
+ out += `
960
+ `;
961
+ out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
962
+ `;
963
+ out += `\u2551 \u{1F4E6} AREA CONTEXT \u2551
964
+ `;
965
+ out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
966
+
967
+ `;
968
+ out += `\u{1F4E6} ${result.area.name} - Contexto Consolidado (${result.area.fileCount} arquivos)
969
+ `;
970
+ if (result.area.description) {
971
+ out += ` ${result.area.description}
972
+ `;
973
+ }
974
+ out += `
975
+ `;
976
+ out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
977
+
978
+ `;
979
+ if (result.types.length > 0) {
980
+ out += `\u{1F3F7}\uFE0F TYPES (${result.types.length})
981
+
982
+ `;
983
+ for (const t of result.types) {
984
+ const filePart = t.file.split("/").pop() || t.file;
985
+ out += ` ${t.name}`.padEnd(45) + `[${filePart}:${t.line}]
986
+ `;
987
+ out += ` ${t.definition}
988
+
989
+ `;
990
+ }
991
+ }
992
+ if (result.hooks.length > 0) {
993
+ out += `\u{1FA9D} HOOKS (${result.hooks.length})
994
+
995
+ `;
996
+ for (const h of result.hooks) {
997
+ const filePart = h.file.split("/").pop() || h.file;
998
+ const params = h.params.length > 0 ? h.params.join(", ") : "";
999
+ out += ` ${h.name}(${params})`.padEnd(45) + `[${filePart}:${h.line}]
1000
+ `;
1001
+ out += ` \u2192 ${h.returns}
1002
+
1003
+ `;
1004
+ }
1005
+ }
1006
+ if (result.functions.length > 0) {
1007
+ out += `\u26A1 FUNCTIONS (${result.functions.length})
1008
+
1009
+ `;
1010
+ for (const f of result.functions) {
1011
+ const filePart = f.file.split("/").pop() || f.file;
1012
+ const params = f.params.length > 0 ? f.params.join(", ") : "";
1013
+ out += ` ${f.name}(${params})`.padEnd(45) + `[${filePart}:${f.line}]
1014
+ `;
1015
+ out += ` \u2192 ${f.returns}
1016
+
1017
+ `;
1018
+ }
1019
+ }
1020
+ if (result.components.length > 0) {
1021
+ out += `\u{1F9E9} COMPONENTS (${result.components.length})
1022
+
1023
+ `;
1024
+ for (const c of result.components) {
1025
+ const filePart = c.file.split("/").pop() || c.file;
1026
+ out += ` ${c.name}`.padEnd(45) + `[${filePart}:${c.line}]
1027
+ `;
1028
+ if (c.props) {
1029
+ out += ` props: ${c.props}
1030
+ `;
1031
+ }
1032
+ out += `
1033
+ `;
1034
+ }
1035
+ }
1036
+ if (result.services.length > 0) {
1037
+ out += `\u{1F527} SERVICES (${result.services.length})
1038
+
1039
+ `;
1040
+ for (const s of result.services) {
1041
+ const filePart = s.file.split("/").pop() || s.file;
1042
+ const params = s.params.length > 0 ? s.params.join(", ") : "";
1043
+ out += ` ${s.name}(${params})`.padEnd(45) + `[${filePart}:${s.line}]
1044
+ `;
1045
+ out += ` \u2192 ${s.returns}
1046
+
1047
+ `;
1048
+ }
1049
+ }
1050
+ if (result.stores.length > 0) {
1051
+ out += `\u{1F5C3}\uFE0F STORES (${result.stores.length})
1052
+
1053
+ `;
1054
+ for (const st of result.stores) {
1055
+ const filePart = st.file.split("/").pop() || st.file;
1056
+ out += ` ${st.name}`.padEnd(45) + `[${filePart}:${st.line}]
1057
+ `;
1058
+ out += ` state: ${st.state}
1059
+
1060
+ `;
1061
+ }
1062
+ }
1063
+ out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1064
+
1065
+ `;
1066
+ out += `\u{1F4CA} RESUMO
1067
+ `;
1068
+ out += ` Types: ${result.types.length}
1069
+ `;
1070
+ out += ` Hooks: ${result.hooks.length}
1071
+ `;
1072
+ out += ` Functions: ${result.functions.length}
1073
+ `;
1074
+ out += ` Components: ${result.components.length}
1075
+ `;
1076
+ out += ` Services: ${result.services.length}
1077
+ `;
1078
+ out += ` Stores: ${result.stores.length}
1079
+ `;
1080
+ return out;
1081
+ }
861
1082
 
862
1083
  // src/cache/index.ts
863
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, statSync, readdirSync } from "fs";
1084
+ import {
1085
+ existsSync as existsSync2,
1086
+ mkdirSync,
1087
+ readFileSync as readFileSync2,
1088
+ writeFileSync,
1089
+ statSync,
1090
+ readdirSync
1091
+ } from "fs";
864
1092
  import { join as join2, extname } from "path";
865
1093
 
866
1094
  // src/utils/firebase.ts
@@ -957,6 +1185,7 @@ var META_FILE = "meta.json";
957
1185
  var GRAPH_FILE = "graph.json";
958
1186
  var MAP_FILE = "map.json";
959
1187
  var DEAD_FILE = "dead.json";
1188
+ var SYMBOLS_FILE = "symbols.json";
960
1189
  function calculateFilesHash(cwd) {
961
1190
  const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
962
1191
  const timestamps = [];
@@ -985,6 +1214,14 @@ function calculateFilesHash(cwd) {
985
1214
  }
986
1215
  }
987
1216
  scanDir(cwd);
1217
+ try {
1218
+ const configPath = join2(cwd, CACHE_DIR, "areas.config.json");
1219
+ if (existsSync2(configPath)) {
1220
+ const stat = statSync(configPath);
1221
+ timestamps.push(stat.mtimeMs);
1222
+ }
1223
+ } catch {
1224
+ }
988
1225
  const sum = timestamps.reduce((a, b) => a + b, 0);
989
1226
  return `${timestamps.length}-${Math.floor(sum)}`;
990
1227
  }
@@ -1071,6 +1308,16 @@ function invalidateCache(cwd) {
1071
1308
  }
1072
1309
  }
1073
1310
  }
1311
+ function cacheSymbolsIndex(cwd, index) {
1312
+ writeCache(cwd, SYMBOLS_FILE, index);
1313
+ updateCacheMeta(cwd);
1314
+ }
1315
+ function getCachedSymbolsIndex(cwd) {
1316
+ if (!isCacheValid(cwd)) {
1317
+ return null;
1318
+ }
1319
+ return readCache(cwd, SYMBOLS_FILE);
1320
+ }
1074
1321
 
1075
1322
  // src/areas/config.ts
1076
1323
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
@@ -2564,8 +2811,8 @@ function formatNotFound2(target, allFiles) {
2564
2811
  }
2565
2812
 
2566
2813
  // src/commands/context.ts
2567
- import { existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
2568
- import { join as join4, resolve, basename, extname as extname2 } from "path";
2814
+ import { existsSync as existsSync4, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
2815
+ import { join as join5, resolve as resolve2, basename, extname as extname3 } from "path";
2569
2816
 
2570
2817
  // src/ts/extractor.ts
2571
2818
  import { Project, SyntaxKind } from "ts-morph";
@@ -2771,6 +3018,307 @@ function extractExports(sourceFile) {
2771
3018
  return [...new Set(exports)];
2772
3019
  }
2773
3020
 
3021
+ // src/ts/indexer.ts
3022
+ import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
3023
+ import { join as join4, extname as extname2, resolve } from "path";
3024
+ import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
3025
+ var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3026
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([
3027
+ "node_modules",
3028
+ "dist",
3029
+ "build",
3030
+ ".git",
3031
+ ".next",
3032
+ ".cache",
3033
+ "coverage",
3034
+ ".turbo",
3035
+ ".vercel",
3036
+ ".analyze"
3037
+ ]);
3038
+ function indexProject(cwd) {
3039
+ const allFiles = getAllCodeFiles(cwd);
3040
+ const project = createProject2(cwd);
3041
+ for (const file of allFiles) {
3042
+ try {
3043
+ project.addSourceFileAtPath(resolve(cwd, file));
3044
+ } catch {
3045
+ }
3046
+ }
3047
+ const files = {};
3048
+ const symbolsByName = {};
3049
+ let symbolCount = 0;
3050
+ for (const sourceFile of project.getSourceFiles()) {
3051
+ let filePath = sourceFile.getFilePath().replace(/\\/g, "/");
3052
+ const cwdNormalized = cwd.replace(/\\/g, "/");
3053
+ if (filePath.startsWith(cwdNormalized + "/")) {
3054
+ filePath = filePath.slice(cwdNormalized.length + 1);
3055
+ } else if (filePath.startsWith(cwdNormalized)) {
3056
+ filePath = filePath.slice(cwdNormalized.length);
3057
+ }
3058
+ if (filePath.includes("node_modules")) continue;
3059
+ const category = detectCategory(filePath);
3060
+ const symbols = [];
3061
+ const imports = [];
3062
+ const exports = [];
3063
+ for (const importDecl of sourceFile.getImportDeclarations()) {
3064
+ const specifiers = [];
3065
+ const defaultImport = importDecl.getDefaultImport();
3066
+ if (defaultImport) {
3067
+ specifiers.push(defaultImport.getText());
3068
+ }
3069
+ for (const namedImport of importDecl.getNamedImports()) {
3070
+ specifiers.push(namedImport.getName());
3071
+ }
3072
+ const namespaceImport = importDecl.getNamespaceImport();
3073
+ if (namespaceImport) {
3074
+ specifiers.push(`* as ${namespaceImport.getText()}`);
3075
+ }
3076
+ imports.push({
3077
+ source: importDecl.getModuleSpecifierValue(),
3078
+ specifiers,
3079
+ isTypeOnly: importDecl.isTypeOnly()
3080
+ });
3081
+ }
3082
+ for (const func of sourceFile.getFunctions()) {
3083
+ const name = func.getName();
3084
+ if (!name) continue;
3085
+ const isExported = func.isExported();
3086
+ const params = func.getParameters().map((p) => p.getName());
3087
+ const returnType = simplifyType2(func.getReturnType().getText());
3088
+ const kind = inferSymbolKind(name, "function");
3089
+ const symbol = {
3090
+ name,
3091
+ file: filePath,
3092
+ line: func.getStartLineNumber(),
3093
+ kind,
3094
+ signature: `${isExported ? "export " : ""}${func.isAsync() ? "async " : ""}function ${name}(${params.join(", ")})`,
3095
+ isExported,
3096
+ params,
3097
+ returnType
3098
+ };
3099
+ symbols.push(symbol);
3100
+ if (!symbolsByName[name]) {
3101
+ symbolsByName[name] = [];
3102
+ }
3103
+ symbolsByName[name].push(symbol);
3104
+ if (isExported) {
3105
+ exports.push(name);
3106
+ }
3107
+ }
3108
+ for (const varStatement of sourceFile.getVariableStatements()) {
3109
+ const isExported = varStatement.isExported();
3110
+ for (const varDecl of varStatement.getDeclarations()) {
3111
+ const name = varDecl.getName();
3112
+ const init = varDecl.getInitializer();
3113
+ if (!init) continue;
3114
+ const initKind = init.getKind();
3115
+ if (initKind === SyntaxKind2.ArrowFunction || initKind === SyntaxKind2.FunctionExpression) {
3116
+ const funcLike = init.asKind(SyntaxKind2.ArrowFunction) || init.asKind(SyntaxKind2.FunctionExpression);
3117
+ if (!funcLike) continue;
3118
+ const params = funcLike.getParameters().map((p) => p.getName());
3119
+ const returnType = simplifyType2(funcLike.getReturnType().getText());
3120
+ const kind = inferSymbolKind(name, "function");
3121
+ const symbol = {
3122
+ name,
3123
+ file: filePath,
3124
+ line: varDecl.getStartLineNumber(),
3125
+ kind,
3126
+ signature: `${isExported ? "export " : ""}const ${name} = (${params.join(", ")}) => ...`,
3127
+ isExported,
3128
+ params,
3129
+ returnType
3130
+ };
3131
+ symbols.push(symbol);
3132
+ if (!symbolsByName[name]) {
3133
+ symbolsByName[name] = [];
3134
+ }
3135
+ symbolsByName[name].push(symbol);
3136
+ if (isExported) {
3137
+ exports.push(name);
3138
+ }
3139
+ } else {
3140
+ const declKind = varStatement.getDeclarationKind();
3141
+ if (declKind.toString() !== "const") continue;
3142
+ const symbol = {
3143
+ name,
3144
+ file: filePath,
3145
+ line: varDecl.getStartLineNumber(),
3146
+ kind: "const",
3147
+ signature: `${isExported ? "export " : ""}const ${name} = ${truncateCode(init.getText(), 40)}`,
3148
+ isExported
3149
+ };
3150
+ symbols.push(symbol);
3151
+ if (!symbolsByName[name]) {
3152
+ symbolsByName[name] = [];
3153
+ }
3154
+ symbolsByName[name].push(symbol);
3155
+ if (isExported) {
3156
+ exports.push(name);
3157
+ }
3158
+ }
3159
+ }
3160
+ }
3161
+ for (const iface of sourceFile.getInterfaces()) {
3162
+ const name = iface.getName();
3163
+ const isExported = iface.isExported();
3164
+ const symbol = {
3165
+ name,
3166
+ file: filePath,
3167
+ line: iface.getStartLineNumber(),
3168
+ kind: "interface",
3169
+ signature: `${isExported ? "export " : ""}interface ${name}`,
3170
+ isExported,
3171
+ definition: formatInterfaceDefinition2(iface)
3172
+ };
3173
+ symbols.push(symbol);
3174
+ if (!symbolsByName[name]) {
3175
+ symbolsByName[name] = [];
3176
+ }
3177
+ symbolsByName[name].push(symbol);
3178
+ if (isExported) {
3179
+ exports.push(name);
3180
+ }
3181
+ }
3182
+ for (const typeAlias of sourceFile.getTypeAliases()) {
3183
+ const name = typeAlias.getName();
3184
+ const isExported = typeAlias.isExported();
3185
+ const symbol = {
3186
+ name,
3187
+ file: filePath,
3188
+ line: typeAlias.getStartLineNumber(),
3189
+ kind: "type",
3190
+ signature: `${isExported ? "export " : ""}type ${name}`,
3191
+ isExported,
3192
+ definition: simplifyType2(typeAlias.getType().getText())
3193
+ };
3194
+ symbols.push(symbol);
3195
+ if (!symbolsByName[name]) {
3196
+ symbolsByName[name] = [];
3197
+ }
3198
+ symbolsByName[name].push(symbol);
3199
+ if (isExported) {
3200
+ exports.push(name);
3201
+ }
3202
+ }
3203
+ for (const enumDecl of sourceFile.getEnums()) {
3204
+ const name = enumDecl.getName();
3205
+ const isExported = enumDecl.isExported();
3206
+ const symbol = {
3207
+ name,
3208
+ file: filePath,
3209
+ line: enumDecl.getStartLineNumber(),
3210
+ kind: "enum",
3211
+ signature: `${isExported ? "export " : ""}enum ${name}`,
3212
+ isExported,
3213
+ definition: enumDecl.getMembers().map((m) => m.getName()).join(" | ")
3214
+ };
3215
+ symbols.push(symbol);
3216
+ if (!symbolsByName[name]) {
3217
+ symbolsByName[name] = [];
3218
+ }
3219
+ symbolsByName[name].push(symbol);
3220
+ if (isExported) {
3221
+ exports.push(name);
3222
+ }
3223
+ }
3224
+ symbolCount += symbols.length;
3225
+ files[filePath] = {
3226
+ path: filePath,
3227
+ category,
3228
+ symbols,
3229
+ imports,
3230
+ exports
3231
+ };
3232
+ }
3233
+ return {
3234
+ version: "1.0.0",
3235
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3236
+ files,
3237
+ symbolsByName,
3238
+ fileCount: Object.keys(files).length,
3239
+ symbolCount
3240
+ };
3241
+ }
3242
+ function createProject2(cwd) {
3243
+ try {
3244
+ return new Project2({
3245
+ tsConfigFilePath: `${cwd}/tsconfig.json`,
3246
+ skipAddingFilesFromTsConfig: true
3247
+ });
3248
+ } catch {
3249
+ return new Project2({
3250
+ skipAddingFilesFromTsConfig: true,
3251
+ compilerOptions: {
3252
+ allowJs: true,
3253
+ checkJs: false
3254
+ }
3255
+ });
3256
+ }
3257
+ }
3258
+ function getAllCodeFiles(dir, files = [], baseDir = dir) {
3259
+ try {
3260
+ const entries = readdirSync2(dir);
3261
+ for (const entry of entries) {
3262
+ const fullPath = join4(dir, entry);
3263
+ if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) {
3264
+ continue;
3265
+ }
3266
+ try {
3267
+ const stat = statSync2(fullPath);
3268
+ if (stat.isDirectory()) {
3269
+ getAllCodeFiles(fullPath, files, baseDir);
3270
+ } else {
3271
+ const ext = extname2(entry).toLowerCase();
3272
+ if (CODE_EXTENSIONS2.has(ext)) {
3273
+ const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
3274
+ files.push(relativePath);
3275
+ }
3276
+ }
3277
+ } catch {
3278
+ }
3279
+ }
3280
+ } catch {
3281
+ }
3282
+ return files;
3283
+ }
3284
+ function inferSymbolKind(name, context2) {
3285
+ if (name.startsWith("use") && name.length > 3 && name[3] === name[3].toUpperCase()) {
3286
+ return "hook";
3287
+ }
3288
+ if (context2 === "function" && name[0] === name[0].toUpperCase() && !name.includes("_")) {
3289
+ return "component";
3290
+ }
3291
+ return context2 === "function" ? "function" : "const";
3292
+ }
3293
+ function simplifyType2(typeText) {
3294
+ let simplified = typeText.replace(/import\([^)]+\)\./g, "");
3295
+ if (simplified.length > 80) {
3296
+ simplified = simplified.slice(0, 77) + "...";
3297
+ }
3298
+ return simplified;
3299
+ }
3300
+ function truncateCode(code, maxLen) {
3301
+ const oneLine = code.replace(/\s+/g, " ").trim();
3302
+ if (oneLine.length <= maxLen) return oneLine;
3303
+ return oneLine.slice(0, maxLen - 3) + "...";
3304
+ }
3305
+ function formatInterfaceDefinition2(iface) {
3306
+ const parts = [];
3307
+ const extendsClauses = iface.getExtends();
3308
+ if (extendsClauses.length > 0) {
3309
+ parts.push(`extends ${extendsClauses.map((e) => e.getText()).join(", ")}`);
3310
+ }
3311
+ const props = iface.getProperties();
3312
+ for (const prop of props.slice(0, 10)) {
3313
+ const propType = simplifyType2(prop.getType().getText());
3314
+ parts.push(`${prop.getName()}: ${propType}`);
3315
+ }
3316
+ if (props.length > 10) {
3317
+ parts.push(`... +${props.length - 10} props`);
3318
+ }
3319
+ return parts.length > 0 ? `{ ${parts.join("; ")} }` : "{}";
3320
+ }
3321
+
2774
3322
  // src/commands/context.ts
2775
3323
  async function context(target, options = {}) {
2776
3324
  const cwd = options.cwd || process.cwd();
@@ -2784,7 +3332,7 @@ async function context(target, options = {}) {
2784
3332
  return formatNotFound3(target, cwd);
2785
3333
  }
2786
3334
  const project = createProject(cwd);
2787
- const absolutePath = resolve(cwd, targetPath);
3335
+ const absolutePath = resolve2(cwd, targetPath);
2788
3336
  const sourceFile = addSourceFile(project, absolutePath);
2789
3337
  const imports = extractImports(sourceFile);
2790
3338
  const functions = extractFunctions(sourceFile);
@@ -2809,14 +3357,14 @@ async function context(target, options = {}) {
2809
3357
  throw new Error(`Erro ao executar context: ${message}`);
2810
3358
  }
2811
3359
  }
2812
- var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3360
+ var CODE_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
2813
3361
  function findTargetFile3(target, cwd) {
2814
3362
  const normalizedTarget = target.replace(/\\/g, "/");
2815
- const directPath = resolve(cwd, normalizedTarget);
3363
+ const directPath = resolve2(cwd, normalizedTarget);
2816
3364
  if (existsSync4(directPath) && isCodeFile2(directPath)) {
2817
3365
  return normalizedTarget;
2818
3366
  }
2819
- for (const ext of CODE_EXTENSIONS2) {
3367
+ for (const ext of CODE_EXTENSIONS3) {
2820
3368
  const withExt = directPath + ext;
2821
3369
  if (existsSync4(withExt)) {
2822
3370
  return normalizedTarget + ext;
@@ -2824,7 +3372,7 @@ function findTargetFile3(target, cwd) {
2824
3372
  }
2825
3373
  const targetName = basename(normalizedTarget).toLowerCase();
2826
3374
  const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
2827
- const allFiles = getAllCodeFiles(cwd);
3375
+ const allFiles = getAllCodeFiles2(cwd);
2828
3376
  const matches = [];
2829
3377
  for (const file of allFiles) {
2830
3378
  const fileName = basename(file).toLowerCase();
@@ -2841,21 +3389,21 @@ function findTargetFile3(target, cwd) {
2841
3389
  return null;
2842
3390
  }
2843
3391
  function isCodeFile2(filePath) {
2844
- const ext = extname2(filePath);
2845
- return CODE_EXTENSIONS2.has(ext);
3392
+ const ext = extname3(filePath);
3393
+ return CODE_EXTENSIONS3.has(ext);
2846
3394
  }
2847
- function getAllCodeFiles(dir, files = [], baseDir = dir) {
3395
+ function getAllCodeFiles2(dir, files = [], baseDir = dir) {
2848
3396
  try {
2849
- const entries = readdirSync2(dir);
3397
+ const entries = readdirSync3(dir);
2850
3398
  for (const entry of entries) {
2851
- const fullPath = join4(dir, entry);
3399
+ const fullPath = join5(dir, entry);
2852
3400
  if (shouldIgnore(entry)) {
2853
3401
  continue;
2854
3402
  }
2855
3403
  try {
2856
- const stat = statSync2(fullPath);
3404
+ const stat = statSync3(fullPath);
2857
3405
  if (stat.isDirectory()) {
2858
- getAllCodeFiles(fullPath, files, baseDir);
3406
+ getAllCodeFiles2(fullPath, files, baseDir);
2859
3407
  } else if (isCodeFile2(entry)) {
2860
3408
  const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
2861
3409
  files.push(relativePath);
@@ -2882,15 +3430,174 @@ function shouldIgnore(name) {
2882
3430
  return ignoredDirs.includes(name) || name.startsWith(".");
2883
3431
  }
2884
3432
  function formatNotFound3(target, cwd) {
2885
- const allFiles = getAllCodeFiles(cwd);
3433
+ const allFiles = getAllCodeFiles2(cwd);
2886
3434
  return formatFileNotFound({ target, allFiles, command: "context" });
2887
3435
  }
3436
+ async function areaContext(areaName, options = {}) {
3437
+ const cwd = options.cwd || process.cwd();
3438
+ const format = options.format || "text";
3439
+ const useCache = options.cache !== false;
3440
+ if (!areaName) {
3441
+ throw new Error("Nome da \xE1rea \xE9 obrigat\xF3rio. Exemplo: ai-tool context --area=auth");
3442
+ }
3443
+ try {
3444
+ const config = readConfig(cwd);
3445
+ let index;
3446
+ if (useCache && isCacheValid(cwd)) {
3447
+ const cached = getCachedSymbolsIndex(cwd);
3448
+ if (cached && cached.files) {
3449
+ index = cached;
3450
+ } else {
3451
+ index = indexProject(cwd);
3452
+ cacheSymbolsIndex(cwd, index);
3453
+ updateCacheMeta(cwd);
3454
+ }
3455
+ } else {
3456
+ index = indexProject(cwd);
3457
+ if (useCache) {
3458
+ cacheSymbolsIndex(cwd, index);
3459
+ updateCacheMeta(cwd);
3460
+ }
3461
+ }
3462
+ const areaLower = areaName.toLowerCase();
3463
+ const areaFiles = [];
3464
+ for (const filePath of Object.keys(index.files)) {
3465
+ if (isFileIgnored(filePath, config)) continue;
3466
+ const fileAreas = detectFileAreas(filePath, config);
3467
+ const belongsToArea = fileAreas.some(
3468
+ (a) => a.toLowerCase() === areaLower || a.toLowerCase().includes(areaLower)
3469
+ );
3470
+ if (belongsToArea) {
3471
+ areaFiles.push(filePath);
3472
+ }
3473
+ }
3474
+ if (areaFiles.length === 0) {
3475
+ return format === "json" ? JSON.stringify({ error: `\xC1rea n\xE3o encontrada: "${areaName}"` }) : `\u274C \xC1rea n\xE3o encontrada: "${areaName}"
3476
+
3477
+ \u{1F4A1} Use 'ai-tool areas' para listar \xE1reas dispon\xEDveis`;
3478
+ }
3479
+ const types = [];
3480
+ const hooks = [];
3481
+ const functions = [];
3482
+ const components = [];
3483
+ const services = [];
3484
+ const stores = [];
3485
+ for (const filePath of areaFiles) {
3486
+ const fileData = index.files[filePath];
3487
+ if (!fileData) continue;
3488
+ const category = fileData.category;
3489
+ for (const symbol of fileData.symbols) {
3490
+ if (!symbol.isExported) continue;
3491
+ switch (symbol.kind) {
3492
+ case "type":
3493
+ case "interface":
3494
+ case "enum":
3495
+ types.push({
3496
+ name: symbol.name,
3497
+ file: filePath,
3498
+ line: symbol.line,
3499
+ definition: symbol.definition || symbol.signature
3500
+ });
3501
+ break;
3502
+ case "hook":
3503
+ hooks.push({
3504
+ name: symbol.name,
3505
+ file: filePath,
3506
+ line: symbol.line,
3507
+ params: symbol.params || [],
3508
+ returns: symbol.returnType || "unknown"
3509
+ });
3510
+ break;
3511
+ case "component":
3512
+ components.push({
3513
+ name: symbol.name,
3514
+ file: filePath,
3515
+ line: symbol.line,
3516
+ props: symbol.params?.join(", ")
3517
+ });
3518
+ break;
3519
+ case "function":
3520
+ if (category === "service" || filePath.includes("service")) {
3521
+ services.push({
3522
+ name: symbol.name,
3523
+ file: filePath,
3524
+ line: symbol.line,
3525
+ params: symbol.params || [],
3526
+ returns: symbol.returnType || "unknown"
3527
+ });
3528
+ } else {
3529
+ functions.push({
3530
+ name: symbol.name,
3531
+ file: filePath,
3532
+ line: symbol.line,
3533
+ params: symbol.params || [],
3534
+ returns: symbol.returnType || "unknown"
3535
+ });
3536
+ }
3537
+ break;
3538
+ case "const":
3539
+ if ((category === "store" || filePath.includes("store")) && symbol.name.startsWith("use")) {
3540
+ stores.push({
3541
+ name: symbol.name,
3542
+ file: filePath,
3543
+ line: symbol.line,
3544
+ state: symbol.definition || "unknown"
3545
+ });
3546
+ }
3547
+ break;
3548
+ }
3549
+ }
3550
+ }
3551
+ const realAreaId = findRealAreaIdFromIndex(areaName, areaFiles, config);
3552
+ const result = {
3553
+ version: "1.0.0",
3554
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3555
+ area: {
3556
+ id: realAreaId || areaName,
3557
+ name: getAreaName(realAreaId || areaName, config),
3558
+ description: getAreaDescription(realAreaId || areaName, config),
3559
+ fileCount: areaFiles.length
3560
+ },
3561
+ types,
3562
+ hooks,
3563
+ functions,
3564
+ components,
3565
+ services,
3566
+ stores
3567
+ };
3568
+ if (format === "json") {
3569
+ return JSON.stringify(result, null, 2);
3570
+ }
3571
+ return formatAreaContextText(result);
3572
+ } catch (error) {
3573
+ const message = error instanceof Error ? error.message : String(error);
3574
+ throw new Error(`Erro ao executar context --area: ${message}`);
3575
+ }
3576
+ }
3577
+ function findRealAreaIdFromIndex(target, areaFiles, config) {
3578
+ const targetLower = target.toLowerCase();
3579
+ for (const areaId of Object.keys(config.areas)) {
3580
+ if (areaId.toLowerCase() === targetLower || areaId.toLowerCase().includes(targetLower)) {
3581
+ return areaId;
3582
+ }
3583
+ }
3584
+ const detectedAreas = /* @__PURE__ */ new Set();
3585
+ for (const filePath of areaFiles) {
3586
+ const areas2 = detectFileAreas(filePath, config);
3587
+ for (const areaId of areas2) {
3588
+ if (areaId.toLowerCase() === targetLower || areaId.toLowerCase().includes(targetLower)) {
3589
+ detectedAreas.add(areaId);
3590
+ }
3591
+ }
3592
+ }
3593
+ return detectedAreas.size > 0 ? [...detectedAreas][0] : null;
3594
+ }
2888
3595
 
2889
3596
  // src/commands/areas.ts
2890
- import { readdirSync as readdirSync3, statSync as statSync3 } from "fs";
2891
- import { join as join5, extname as extname3 } from "path";
2892
- var CODE_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
2893
- var IGNORED_DIRS = /* @__PURE__ */ new Set([
3597
+ import { readdirSync as readdirSync4, statSync as statSync4 } from "fs";
3598
+ import { join as join6, extname as extname4 } from "path";
3599
+ var CODE_EXTENSIONS4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3600
+ var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
2894
3601
  "node_modules",
2895
3602
  "dist",
2896
3603
  "build",
@@ -2907,7 +3614,7 @@ async function areas(options = {}) {
2907
3614
  const format = options.format || "text";
2908
3615
  try {
2909
3616
  const config = readConfig(cwd);
2910
- const allFiles = getAllCodeFiles2(cwd);
3617
+ const allFiles = getAllCodeFiles3(cwd);
2911
3618
  const filteredFiles = allFiles.filter((filePath) => !isFileIgnored(filePath, config));
2912
3619
  const areaMap = /* @__PURE__ */ new Map();
2913
3620
  const unmapped = [];
@@ -2972,21 +3679,21 @@ async function areas(options = {}) {
2972
3679
  throw new Error(`Erro ao executar areas: ${message}`);
2973
3680
  }
2974
3681
  }
2975
- function getAllCodeFiles2(dir, files = [], baseDir = dir) {
3682
+ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
2976
3683
  try {
2977
- const entries = readdirSync3(dir);
3684
+ const entries = readdirSync4(dir);
2978
3685
  for (const entry of entries) {
2979
- const fullPath = join5(dir, entry);
2980
- if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) {
3686
+ const fullPath = join6(dir, entry);
3687
+ if (IGNORED_DIRS2.has(entry) || entry.startsWith(".")) {
2981
3688
  continue;
2982
3689
  }
2983
3690
  try {
2984
- const stat = statSync3(fullPath);
3691
+ const stat = statSync4(fullPath);
2985
3692
  if (stat.isDirectory()) {
2986
- getAllCodeFiles2(fullPath, files, baseDir);
3693
+ getAllCodeFiles3(fullPath, files, baseDir);
2987
3694
  } else {
2988
- const ext = extname3(entry).toLowerCase();
2989
- if (CODE_EXTENSIONS3.has(ext)) {
3695
+ const ext = extname4(entry).toLowerCase();
3696
+ if (CODE_EXTENSIONS4.has(ext)) {
2990
3697
  const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
2991
3698
  files.push(relativePath);
2992
3699
  }
@@ -3000,10 +3707,10 @@ function getAllCodeFiles2(dir, files = [], baseDir = dir) {
3000
3707
  }
3001
3708
 
3002
3709
  // src/commands/area.ts
3003
- import { readdirSync as readdirSync4, statSync as statSync4 } from "fs";
3004
- import { join as join6, extname as extname4 } from "path";
3005
- var CODE_EXTENSIONS4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3006
- var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
3710
+ import { readdirSync as readdirSync5, statSync as statSync5 } from "fs";
3711
+ import { join as join7, extname as extname5 } from "path";
3712
+ var CODE_EXTENSIONS5 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3713
+ var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
3007
3714
  "node_modules",
3008
3715
  "dist",
3009
3716
  "build",
@@ -3025,7 +3732,7 @@ async function area(target, options = {}) {
3025
3732
  }
3026
3733
  try {
3027
3734
  const config = readConfig(cwd);
3028
- const allFiles = getAllCodeFiles3(cwd);
3735
+ const allFiles = getAllCodeFiles4(cwd);
3029
3736
  const filteredFiles = allFiles.filter((filePath) => !isFileIgnored(filePath, config));
3030
3737
  const areaFiles = [];
3031
3738
  const targetLower = target.toLowerCase();
@@ -3122,21 +3829,21 @@ function getAvailableAreas(allFiles, config) {
3122
3829
  function formatAreaNotFound2(target, availableAreas) {
3123
3830
  return formatAreaNotFound({ target, availableAreas });
3124
3831
  }
3125
- function getAllCodeFiles3(dir, files = [], baseDir = dir) {
3832
+ function getAllCodeFiles4(dir, files = [], baseDir = dir) {
3126
3833
  try {
3127
- const entries = readdirSync4(dir);
3834
+ const entries = readdirSync5(dir);
3128
3835
  for (const entry of entries) {
3129
- const fullPath = join6(dir, entry);
3130
- if (IGNORED_DIRS2.has(entry) || entry.startsWith(".")) {
3836
+ const fullPath = join7(dir, entry);
3837
+ if (IGNORED_DIRS3.has(entry) || entry.startsWith(".")) {
3131
3838
  continue;
3132
3839
  }
3133
3840
  try {
3134
- const stat = statSync4(fullPath);
3841
+ const stat = statSync5(fullPath);
3135
3842
  if (stat.isDirectory()) {
3136
- getAllCodeFiles3(fullPath, files, baseDir);
3843
+ getAllCodeFiles4(fullPath, files, baseDir);
3137
3844
  } else {
3138
- const ext = extname4(entry).toLowerCase();
3139
- if (CODE_EXTENSIONS4.has(ext)) {
3845
+ const ext = extname5(entry).toLowerCase();
3846
+ if (CODE_EXTENSIONS5.has(ext)) {
3140
3847
  const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
3141
3848
  files.push(relativePath);
3142
3849
  }
@@ -3150,10 +3857,10 @@ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
3150
3857
  }
3151
3858
 
3152
3859
  // src/commands/areas-init.ts
3153
- import { readdirSync as readdirSync5, statSync as statSync5 } from "fs";
3154
- import { join as join7, extname as extname5 } from "path";
3155
- var CODE_EXTENSIONS5 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3156
- var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
3860
+ import { readdirSync as readdirSync6, statSync as statSync6 } from "fs";
3861
+ import { join as join8, extname as extname6 } from "path";
3862
+ var CODE_EXTENSIONS6 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3863
+ var IGNORED_DIRS4 = /* @__PURE__ */ new Set([
3157
3864
  "node_modules",
3158
3865
  "dist",
3159
3866
  "build",
@@ -3179,7 +3886,7 @@ Use --force para sobrescrever:
3179
3886
  Ou edite manualmente o arquivo existente.
3180
3887
  `.trim();
3181
3888
  }
3182
- const allFiles = getAllCodeFiles4(cwd);
3889
+ const allFiles = getAllCodeFiles5(cwd);
3183
3890
  const currentConfig = readConfig(cwd);
3184
3891
  const areaCounts = /* @__PURE__ */ new Map();
3185
3892
  for (const filePath of allFiles) {
@@ -3278,21 +3985,21 @@ function inferPatternsFromFiles(files) {
3278
3985
  }
3279
3986
  return [...patterns].sort();
3280
3987
  }
3281
- function getAllCodeFiles4(dir, files = [], baseDir = dir) {
3988
+ function getAllCodeFiles5(dir, files = [], baseDir = dir) {
3282
3989
  try {
3283
- const entries = readdirSync5(dir);
3990
+ const entries = readdirSync6(dir);
3284
3991
  for (const entry of entries) {
3285
- const fullPath = join7(dir, entry);
3286
- if (IGNORED_DIRS3.has(entry) || entry.startsWith(".")) {
3992
+ const fullPath = join8(dir, entry);
3993
+ if (IGNORED_DIRS4.has(entry) || entry.startsWith(".")) {
3287
3994
  continue;
3288
3995
  }
3289
3996
  try {
3290
- const stat = statSync5(fullPath);
3997
+ const stat = statSync6(fullPath);
3291
3998
  if (stat.isDirectory()) {
3292
- getAllCodeFiles4(fullPath, files, baseDir);
3999
+ getAllCodeFiles5(fullPath, files, baseDir);
3293
4000
  } else {
3294
- const ext = extname5(entry).toLowerCase();
3295
- if (CODE_EXTENSIONS5.has(ext)) {
4001
+ const ext = extname6(entry).toLowerCase();
4002
+ if (CODE_EXTENSIONS6.has(ext)) {
3296
4003
  const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
3297
4004
  files.push(relativePath);
3298
4005
  }
@@ -3316,6 +4023,7 @@ export {
3316
4023
  categoryIcons,
3317
4024
  isEntryPoint,
3318
4025
  isCodeFile,
4026
+ formatFindText,
3319
4027
  isFirebaseProject,
3320
4028
  hasFirebaseFunctions,
3321
4029
  isExportedCloudFunction,
@@ -3323,7 +4031,10 @@ export {
3323
4031
  clearFirebaseCache,
3324
4032
  getCacheDir,
3325
4033
  isCacheValid,
4034
+ updateCacheMeta,
3326
4035
  invalidateCache,
4036
+ cacheSymbolsIndex,
4037
+ getCachedSymbolsIndex,
3327
4038
  configExists,
3328
4039
  readConfig,
3329
4040
  writeConfig,
@@ -3335,6 +4046,7 @@ export {
3335
4046
  KEYWORD_PATTERNS,
3336
4047
  AREA_NAMES,
3337
4048
  AREA_DESCRIPTIONS,
4049
+ isFileIgnored,
3338
4050
  detectFileAreas,
3339
4051
  getAreaName,
3340
4052
  getAreaDescription,
@@ -3353,7 +4065,9 @@ export {
3353
4065
  formatInvalidCommand,
3354
4066
  impact,
3355
4067
  suggest,
4068
+ indexProject,
3356
4069
  context,
4070
+ areaContext,
3357
4071
  areas,
3358
4072
  area,
3359
4073
  areasInit,