@justmpm/ai-tool 0.3.2 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,8 @@ import skott from "skott";
5
5
  function detectCategory(filePath) {
6
6
  const normalized = filePath.replace(/\\/g, "/").toLowerCase();
7
7
  const fileName = normalized.split("/").pop() || "";
8
- const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
9
- if (fileName.includes(".test.") || fileName.includes(".spec.") || normalized.includes("/__tests__/") || normalized.includes("/test/")) {
8
+ const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs|vue|svelte|astro)$/, "");
9
+ if (fileName.includes(".test.") || fileName.includes(".spec.") || normalized.includes("/__tests__/") || normalized.includes("/test/") || normalized.includes("/tests/")) {
10
10
  return "test";
11
11
  }
12
12
  if (isConfigFile(fileName)) {
@@ -20,23 +20,55 @@ function detectCategory(filePath) {
20
20
  )) {
21
21
  return "page";
22
22
  }
23
- if (normalized.includes("/api/")) return "route";
24
- if (normalized.includes("/pages/")) return "page";
23
+ if (normalized.includes("/pages/") && !normalized.includes("/pages/api/")) {
24
+ return "page";
25
+ }
26
+ if (normalized.includes("/pages/") || normalized.includes("/views/") || normalized.includes("/screens/")) {
27
+ if (!normalized.includes("/components/")) {
28
+ return "page";
29
+ }
30
+ }
31
+ if (fileNameNoExt.endsWith("page") || fileNameNoExt.endsWith("view") || fileNameNoExt.endsWith("screen")) {
32
+ return "page";
33
+ }
34
+ if (normalized.includes("/routes/") && normalized.includes("/app/")) {
35
+ return "page";
36
+ }
37
+ if (normalized.includes("/layouts/")) {
38
+ return "layout";
39
+ }
40
+ if (fileNameNoExt === "+page" || fileNameNoExt === "+page.server") return "page";
41
+ if (fileNameNoExt === "+layout" || fileNameNoExt === "+layout.server") return "layout";
42
+ if (fileNameNoExt === "+server") return "route";
43
+ if (fileNameNoExt === "+error") return "page";
44
+ if (fileName.endsWith(".astro") && normalized.includes("/pages/")) {
45
+ return "page";
46
+ }
47
+ if (fileName.endsWith(".astro") && normalized.includes("/layouts/")) {
48
+ return "layout";
49
+ }
50
+ if (normalized.includes("/api/") || normalized.includes("/server/") || fileNameNoExt.endsWith(".server") || fileNameNoExt === "+server") {
51
+ return "route";
52
+ }
25
53
  if (fileNameNoExt.startsWith("use") && fileNameNoExt.length > 3) return "hook";
26
54
  if (normalized.includes("/hooks/")) return "hook";
27
- if (normalized.includes("/types/") || fileName.endsWith(".d.ts") || fileNameNoExt === "types" || fileNameNoExt === "interfaces") {
55
+ if (normalized.includes("/composables/")) return "hook";
56
+ if (normalized.includes("/stores/") && fileName.endsWith(".ts")) return "hook";
57
+ if (normalized.includes("/types/") || normalized.includes("/interfaces/") || fileName.endsWith(".d.ts") || fileNameNoExt === "types" || fileNameNoExt === "interfaces") {
28
58
  return "type";
29
59
  }
30
- if (normalized.includes("/services/") || fileNameNoExt.endsWith("service")) {
60
+ if (normalized.includes("/services/") || normalized.includes("/api-client/") || fileNameNoExt.endsWith("service") || fileNameNoExt.endsWith("api")) {
31
61
  return "service";
32
62
  }
33
- if (normalized.includes("/store/") || normalized.includes("/stores/") || normalized.includes("/context/") || normalized.includes("/contexts/") || normalized.includes("/providers/")) {
63
+ if (normalized.includes("/store/") || normalized.includes("/stores/") || normalized.includes("/context/") || normalized.includes("/contexts/") || normalized.includes("/providers/") || normalized.includes("/state/") || // Zustand, Redux, Pinia, etc
64
+ fileNameNoExt.endsWith("store") || fileNameNoExt.endsWith("slice") || fileNameNoExt.endsWith("reducer")) {
34
65
  return "store";
35
66
  }
36
- if (normalized.includes("/utils/") || normalized.includes("/lib/") || normalized.includes("/helpers/") || normalized.includes("/common/")) {
67
+ if (normalized.includes("/utils/") || normalized.includes("/lib/") || normalized.includes("/helpers/") || normalized.includes("/common/") || normalized.includes("/shared/") || fileNameNoExt.endsWith("utils") || fileNameNoExt.endsWith("helpers")) {
37
68
  return "util";
38
69
  }
39
- if (normalized.includes("/components/") || normalized.includes("/ui/") || normalized.includes("/features/")) {
70
+ if (normalized.includes("/components/") || normalized.includes("/ui/") || normalized.includes("/features/") || normalized.includes("/modules/") || // Vue/Svelte single file components
71
+ fileName.endsWith(".vue") || fileName.endsWith(".svelte")) {
40
72
  return "component";
41
73
  }
42
74
  return "other";
@@ -597,6 +629,177 @@ function formatContextText(result) {
597
629
  `;
598
630
  return out;
599
631
  }
632
+ function formatAreasText(result) {
633
+ let out = "";
634
+ out += `
635
+ `;
636
+ 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
637
+ `;
638
+ out += `\u2551 \u{1F4E6} PROJECT AREAS \u2551
639
+ `;
640
+ 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
641
+
642
+ `;
643
+ out += `\u{1F4CA} RESUMO
644
+ `;
645
+ out += ` \xC1reas: ${result.summary.totalAreas}
646
+ `;
647
+ out += ` Arquivos: ${result.summary.totalFiles}
648
+ `;
649
+ if (result.summary.unmappedCount > 0) {
650
+ out += ` \u26A0\uFE0F Sem \xE1rea: ${result.summary.unmappedCount}
651
+ `;
652
+ }
653
+ out += `
654
+ `;
655
+ 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
656
+
657
+ `;
658
+ out += `\u{1F4E6} \xC1REAS DETECTADAS
659
+
660
+ `;
661
+ for (const area2 of result.areas) {
662
+ const autoTag = area2.isAutoDetected ? " (auto)" : "";
663
+ out += ` ${area2.name.padEnd(25)} ${String(area2.fileCount).padStart(4)} arquivos${autoTag}
664
+ `;
665
+ const catSummary = Object.entries(area2.categories).sort((a, b) => b[1] - a[1]).slice(0, 4).map(([cat, count]) => `${categoryIcons[cat]}${count}`).join(" ");
666
+ if (catSummary) {
667
+ out += ` ${catSummary}
668
+ `;
669
+ }
670
+ if (area2.description) {
671
+ out += ` ${area2.description}
672
+ `;
673
+ }
674
+ out += `
675
+ `;
676
+ }
677
+ if (result.unmapped.length > 0) {
678
+ 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
679
+
680
+ `;
681
+ out += `\u26A0\uFE0F ARQUIVOS SEM \xC1REA (${result.unmapped.length})
682
+
683
+ `;
684
+ for (const file of result.unmapped.slice(0, 10)) {
685
+ const icon = categoryIcons[file.category];
686
+ out += ` ${icon} ${file.path}
687
+ `;
688
+ }
689
+ if (result.unmapped.length > 10) {
690
+ out += ` ... e mais ${result.unmapped.length - 10}
691
+ `;
692
+ }
693
+ out += `
694
+ \u{1F4A1} Adicione padr\xF5es em .analyze/areas.config.json
695
+ `;
696
+ out += ` ou execute 'ai-tool areas init' para gerar configura\xE7\xE3o
697
+ `;
698
+ }
699
+ out += `
700
+ \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
701
+
702
+ `;
703
+ out += `\u{1F4A1} Use 'ai-tool area <nome>' para ver detalhes de uma \xE1rea
704
+ `;
705
+ out += ` Exemplo: ai-tool area meus-pets
706
+ `;
707
+ return out;
708
+ }
709
+ function formatAreaDetailText(result, options = {}) {
710
+ const { full = false, filterType } = options;
711
+ const { area: area2, byCategory } = result;
712
+ let out = "";
713
+ out += `
714
+ `;
715
+ 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
716
+ `;
717
+ out += `\u2551 \u{1F4E6} AREA DETAIL \u2551
718
+ `;
719
+ 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
720
+
721
+ `;
722
+ out += `\u{1F4E6} ${area2.name}
723
+ `;
724
+ if (area2.description) {
725
+ out += ` ${area2.description}
726
+ `;
727
+ }
728
+ out += `
729
+ `;
730
+ out += `\u{1F4CA} Resumo: ${area2.fileCount} arquivos
731
+ `;
732
+ const catOrder = [
733
+ "page",
734
+ "layout",
735
+ "route",
736
+ "component",
737
+ "hook",
738
+ "service",
739
+ "store",
740
+ "util",
741
+ "type",
742
+ "config",
743
+ "test",
744
+ "other"
745
+ ];
746
+ const catParts = [];
747
+ for (const cat of catOrder) {
748
+ const count = area2.categories[cat];
749
+ if (count) {
750
+ catParts.push(`${categoryIcons[cat]} ${cat}: ${count}`);
751
+ }
752
+ }
753
+ out += catParts.join(" ");
754
+ out += `
755
+
756
+ `;
757
+ 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
758
+
759
+ `;
760
+ if (filterType) {
761
+ const files = byCategory[filterType] || [];
762
+ out += `${categoryIcons[filterType]} ${filterType.toUpperCase()} (${files.length})
763
+
764
+ `;
765
+ for (const file of files) {
766
+ const desc = file.description ? ` ${file.description}` : "";
767
+ out += ` ${file.path}${desc}
768
+ `;
769
+ }
770
+ return out;
771
+ }
772
+ const maxFilesPerCategory = full ? 100 : 8;
773
+ for (const cat of catOrder) {
774
+ const files = byCategory[cat];
775
+ if (!files || files.length === 0) continue;
776
+ out += `${categoryIcons[cat]} ${cat.toUpperCase()} (${files.length})
777
+ `;
778
+ const filesToShow = files.slice(0, maxFilesPerCategory);
779
+ const remaining = files.length - filesToShow.length;
780
+ for (const file of filesToShow) {
781
+ const maxPathLen = 50;
782
+ let displayPath = file.path;
783
+ if (displayPath.length > maxPathLen) {
784
+ displayPath = "..." + displayPath.slice(-maxPathLen + 3);
785
+ }
786
+ const desc = file.description || "";
787
+ out += ` ${displayPath.padEnd(maxPathLen + 2)} ${desc}
788
+ `;
789
+ }
790
+ if (remaining > 0) {
791
+ out += ` ... e mais ${remaining}
792
+ `;
793
+ }
794
+ out += `
795
+ `;
796
+ }
797
+ if (!full && area2.fileCount > 20) {
798
+ out += `\u{1F4A1} Use --full para ver todos os arquivos
799
+ `;
800
+ }
801
+ return out;
802
+ }
600
803
 
601
804
  // src/cache/index.ts
602
805
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, statSync, readdirSync } from "fs";
@@ -1943,8 +2146,1058 @@ function levenshteinDistance3(a, b) {
1943
2146
  return matrix[b.length][a.length];
1944
2147
  }
1945
2148
 
2149
+ // src/commands/areas.ts
2150
+ import { readdirSync as readdirSync3, statSync as statSync3 } from "fs";
2151
+ import { join as join5, extname as extname3 } from "path";
2152
+
2153
+ // src/areas/config.ts
2154
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
2155
+ import { join as join4 } from "path";
2156
+ var CONFIG_FILE = "areas.config.json";
2157
+ var DEFAULT_CONFIG = {
2158
+ $schema: "./areas.schema.json",
2159
+ version: "1.0.0",
2160
+ areas: {},
2161
+ descriptions: {},
2162
+ settings: {
2163
+ autoDetect: true,
2164
+ inferDescriptions: true,
2165
+ groupByCategory: true
2166
+ }
2167
+ };
2168
+ function getConfigPath(cwd) {
2169
+ return join4(cwd, CACHE_DIR, CONFIG_FILE);
2170
+ }
2171
+ function configExists(cwd) {
2172
+ return existsSync4(getConfigPath(cwd));
2173
+ }
2174
+ function readConfig(cwd) {
2175
+ const configPath = getConfigPath(cwd);
2176
+ if (!existsSync4(configPath)) {
2177
+ return DEFAULT_CONFIG;
2178
+ }
2179
+ try {
2180
+ const content = readFileSync3(configPath, "utf-8");
2181
+ const config = JSON.parse(content);
2182
+ return {
2183
+ ...DEFAULT_CONFIG,
2184
+ ...config,
2185
+ settings: {
2186
+ ...DEFAULT_CONFIG.settings,
2187
+ ...config.settings
2188
+ }
2189
+ };
2190
+ } catch {
2191
+ return DEFAULT_CONFIG;
2192
+ }
2193
+ }
2194
+ function writeConfig(cwd, config) {
2195
+ const cacheDir = join4(cwd, CACHE_DIR);
2196
+ if (!existsSync4(cacheDir)) {
2197
+ mkdirSync2(cacheDir, { recursive: true });
2198
+ }
2199
+ const configPath = getConfigPath(cwd);
2200
+ writeFileSync2(configPath, JSON.stringify(config, null, 2), "utf-8");
2201
+ }
2202
+ function setArea(cwd, id, area2) {
2203
+ const config = readConfig(cwd);
2204
+ config.areas[id] = area2;
2205
+ writeConfig(cwd, config);
2206
+ }
2207
+ function removeArea(cwd, id) {
2208
+ const config = readConfig(cwd);
2209
+ delete config.areas[id];
2210
+ writeConfig(cwd, config);
2211
+ }
2212
+ function setFileDescription(cwd, filePath, description) {
2213
+ const config = readConfig(cwd);
2214
+ if (!config.descriptions) {
2215
+ config.descriptions = {};
2216
+ }
2217
+ config.descriptions[filePath] = description;
2218
+ writeConfig(cwd, config);
2219
+ }
2220
+ function getFileDescription(cwd, filePath) {
2221
+ const config = readConfig(cwd);
2222
+ return config.descriptions?.[filePath];
2223
+ }
2224
+
2225
+ // src/areas/detector.ts
2226
+ import { minimatch } from "minimatch";
2227
+
2228
+ // src/areas/patterns.ts
2229
+ var FOLDER_PATTERNS = [
2230
+ // ============================================================================
2231
+ // NEXT.JS APP ROUTER - Rotas específicas (alta prioridade)
2232
+ // ============================================================================
2233
+ { pattern: /app\/.*\/meus-pets\//, area: "meus-pets", priority: 100 },
2234
+ { pattern: /app\/.*\/pets\//, area: "meus-pets", priority: 100 },
2235
+ { pattern: /app\/.*\/consultas\//, area: "consultas-ia", priority: 100 },
2236
+ { pattern: /app\/.*\/dashboard\//, area: "dashboard", priority: 100 },
2237
+ { pattern: /app\/.*\/admin\//, area: "admin", priority: 100 },
2238
+ { pattern: /app\/.*\/assinatura\//, area: "stripe", priority: 100 },
2239
+ { pattern: /app\/.*\/guias\//, area: "training", priority: 100 },
2240
+ { pattern: /app\/.*\/treino\//, area: "training", priority: 100 },
2241
+ { pattern: /app\/.*\/login\//, area: "auth", priority: 100 },
2242
+ { pattern: /app\/.*\/cadastro\//, area: "auth", priority: 100 },
2243
+ { pattern: /app\/.*\/auth\//, area: "auth", priority: 100 },
2244
+ { pattern: /app\/.*\/perfil\//, area: "profile", priority: 100 },
2245
+ { pattern: /app\/.*\/configuracoes\//, area: "settings", priority: 100 },
2246
+ { pattern: /app\/.*\/settings\//, area: "settings", priority: 100 },
2247
+ { pattern: /app\/.*\/onboarding/, area: "onboarding", priority: 100 },
2248
+ { pattern: /app\/.*\/beta\//, area: "beta", priority: 100 },
2249
+ { pattern: /app\/.*\/precos\//, area: "pricing", priority: 100 },
2250
+ { pattern: /app\/.*\/pricing\//, area: "pricing", priority: 100 },
2251
+ { pattern: /app\/.*\/checkout\//, area: "checkout", priority: 100 },
2252
+ { pattern: /app\/.*\/cart\//, area: "cart", priority: 100 },
2253
+ { pattern: /app\/.*\/shop\//, area: "shop", priority: 100 },
2254
+ { pattern: /app\/.*\/products\//, area: "products", priority: 100 },
2255
+ { pattern: /app\/.*\/orders\//, area: "orders", priority: 100 },
2256
+ { pattern: /app\/.*\/blog\//, area: "blog", priority: 100 },
2257
+ { pattern: /app\/.*\/docs\//, area: "docs", priority: 100 },
2258
+ { pattern: /app\/.*\/legal\//, area: "legal", priority: 100 },
2259
+ { pattern: /app\/.*\/privacy\//, area: "legal", priority: 100 },
2260
+ { pattern: /app\/.*\/terms\//, area: "legal", priority: 100 },
2261
+ { pattern: /app\/.*\/about\//, area: "about", priority: 100 },
2262
+ { pattern: /app\/.*\/contact\//, area: "contact", priority: 100 },
2263
+ { pattern: /app\/.*\/faq\//, area: "faq", priority: 100 },
2264
+ { pattern: /app\/.*\/help\//, area: "help", priority: 100 },
2265
+ { pattern: /app\/.*\/support\//, area: "support", priority: 100 },
2266
+ { pattern: /app\/.*\/feedback\//, area: "feedback", priority: 100 },
2267
+ // ============================================================================
2268
+ // VITE / CRA - src/pages/ ou src/views/ (alta prioridade)
2269
+ // ============================================================================
2270
+ { pattern: /src\/pages\/[Dd]ashboard/, area: "dashboard", priority: 100 },
2271
+ { pattern: /src\/pages\/[Aa]dmin/, area: "admin", priority: 100 },
2272
+ { pattern: /src\/pages\/[Aa]uth/, area: "auth", priority: 100 },
2273
+ { pattern: /src\/pages\/[Ll]ogin/, area: "auth", priority: 100 },
2274
+ { pattern: /src\/pages\/[Rr]egister/, area: "auth", priority: 100 },
2275
+ { pattern: /src\/pages\/[Ss]ignup/, area: "auth", priority: 100 },
2276
+ { pattern: /src\/pages\/[Pp]rofile/, area: "profile", priority: 100 },
2277
+ { pattern: /src\/pages\/[Ss]ettings/, area: "settings", priority: 100 },
2278
+ { pattern: /src\/pages\/[Pp]ricing/, area: "pricing", priority: 100 },
2279
+ { pattern: /src\/pages\/[Cc]heckout/, area: "checkout", priority: 100 },
2280
+ { pattern: /src\/pages\/[Cc]art/, area: "cart", priority: 100 },
2281
+ { pattern: /src\/pages\/[Ss]hop/, area: "shop", priority: 100 },
2282
+ { pattern: /src\/pages\/[Pp]roducts?/, area: "products", priority: 100 },
2283
+ { pattern: /src\/pages\/[Oo]rders?/, area: "orders", priority: 100 },
2284
+ { pattern: /src\/pages\/[Bb]log/, area: "blog", priority: 100 },
2285
+ { pattern: /src\/views\/[Dd]ashboard/, area: "dashboard", priority: 100 },
2286
+ { pattern: /src\/views\/[Aa]dmin/, area: "admin", priority: 100 },
2287
+ { pattern: /src\/views\/[Aa]uth/, area: "auth", priority: 100 },
2288
+ // ============================================================================
2289
+ // REMIX - app/routes/
2290
+ // ============================================================================
2291
+ { pattern: /app\/routes\/dashboard/, area: "dashboard", priority: 100 },
2292
+ { pattern: /app\/routes\/admin/, area: "admin", priority: 100 },
2293
+ { pattern: /app\/routes\/auth/, area: "auth", priority: 100 },
2294
+ { pattern: /app\/routes\/login/, area: "auth", priority: 100 },
2295
+ { pattern: /app\/routes\/_auth/, area: "auth", priority: 100 },
2296
+ // ============================================================================
2297
+ // NUXT - pages/ (sem src/)
2298
+ // ============================================================================
2299
+ { pattern: /^pages\/dashboard/, area: "dashboard", priority: 100 },
2300
+ { pattern: /^pages\/admin/, area: "admin", priority: 100 },
2301
+ { pattern: /^pages\/auth/, area: "auth", priority: 100 },
2302
+ // ============================================================================
2303
+ // SVELTEKIT - src/routes/
2304
+ // ============================================================================
2305
+ { pattern: /src\/routes\/dashboard/, area: "dashboard", priority: 100 },
2306
+ { pattern: /src\/routes\/admin/, area: "admin", priority: 100 },
2307
+ { pattern: /src\/routes\/auth/, area: "auth", priority: 100 },
2308
+ { pattern: /src\/routes\/\(auth\)/, area: "auth", priority: 100 },
2309
+ { pattern: /src\/routes\/\(app\)/, area: "app", priority: 90 },
2310
+ // ============================================================================
2311
+ // ASTRO - src/pages/
2312
+ // ============================================================================
2313
+ { pattern: /src\/pages\/blog/, area: "blog", priority: 100 },
2314
+ { pattern: /src\/pages\/docs/, area: "docs", priority: 100 },
2315
+ // ============================================================================
2316
+ // COMPONENTS - Por subpasta (alta prioridade)
2317
+ // Funciona em qualquer framework (com ou sem src/)
2318
+ // ============================================================================
2319
+ { pattern: /components\/pets\//, area: "meus-pets", priority: 90 },
2320
+ { pattern: /components\/pet\//, area: "meus-pets", priority: 90 },
2321
+ { pattern: /components\/consultation\//, area: "consultas-ia", priority: 90 },
2322
+ { pattern: /components\/chat\//, area: "chat", priority: 90 },
2323
+ { pattern: /components\/training\//, area: "training", priority: 90 },
2324
+ { pattern: /components\/health\//, area: "health-tracking", priority: 90 },
2325
+ { pattern: /components\/auth\//, area: "auth", priority: 90 },
2326
+ { pattern: /components\/admin\//, area: "admin", priority: 90 },
2327
+ { pattern: /components\/landing\//, area: "landing", priority: 90 },
2328
+ { pattern: /components\/marketing\//, area: "landing", priority: 90 },
2329
+ { pattern: /components\/dashboard\//, area: "dashboard", priority: 90 },
2330
+ { pattern: /components\/subscription\//, area: "stripe", priority: 90 },
2331
+ { pattern: /components\/stripe\//, area: "stripe", priority: 90 },
2332
+ { pattern: /components\/payment\//, area: "payments", priority: 90 },
2333
+ { pattern: /components\/checkout\//, area: "checkout", priority: 90 },
2334
+ { pattern: /components\/cart\//, area: "cart", priority: 90 },
2335
+ { pattern: /components\/notification\//, area: "notifications", priority: 90 },
2336
+ { pattern: /components\/pdf\//, area: "pdf", priority: 90 },
2337
+ { pattern: /components\/seo\//, area: "seo", priority: 90 },
2338
+ { pattern: /components\/blog\//, area: "blog", priority: 90 },
2339
+ { pattern: /components\/docs\//, area: "docs", priority: 90 },
2340
+ { pattern: /components\/legal\//, area: "legal", priority: 90 },
2341
+ { pattern: /components\/feedback\//, area: "feedback", priority: 90 },
2342
+ { pattern: /components\/beta\//, area: "beta", priority: 90 },
2343
+ { pattern: /components\/onboarding\//, area: "onboarding", priority: 90 },
2344
+ { pattern: /components\/settings\//, area: "settings", priority: 90 },
2345
+ { pattern: /components\/profile\//, area: "profile", priority: 90 },
2346
+ { pattern: /components\/user\//, area: "user", priority: 90 },
2347
+ { pattern: /components\/products?\//, area: "products", priority: 90 },
2348
+ { pattern: /components\/orders?\//, area: "orders", priority: 90 },
2349
+ { pattern: /components\/shop\//, area: "shop", priority: 90 },
2350
+ { pattern: /components\/forms?\//, area: "forms", priority: 85 },
2351
+ { pattern: /components\/tables?\//, area: "tables", priority: 85 },
2352
+ { pattern: /components\/modals?\//, area: "modals", priority: 85 },
2353
+ { pattern: /components\/dialogs?\//, area: "modals", priority: 85 },
2354
+ // Componentes compartilhados (baixa prioridade)
2355
+ { pattern: /components\/ui\//, area: "shared-ui", priority: 30 },
2356
+ { pattern: /components\/common\//, area: "shared-ui", priority: 30 },
2357
+ { pattern: /components\/shared\//, area: "shared-ui", priority: 30 },
2358
+ { pattern: /components\/base\//, area: "shared-ui", priority: 30 },
2359
+ { pattern: /components\/core\//, area: "shared-ui", priority: 30 },
2360
+ { pattern: /components\/primitives\//, area: "shared-ui", priority: 30 },
2361
+ { pattern: /components\/providers\//, area: "core", priority: 40 },
2362
+ { pattern: /components\/layout\//, area: "core", priority: 40 },
2363
+ { pattern: /components\/layouts\//, area: "core", priority: 40 },
2364
+ // ============================================================================
2365
+ // FEATURES (padrão comum em projetos maiores)
2366
+ // ============================================================================
2367
+ { pattern: /features\/auth\//, area: "auth", priority: 95 },
2368
+ { pattern: /features\/dashboard\//, area: "dashboard", priority: 95 },
2369
+ { pattern: /features\/admin\//, area: "admin", priority: 95 },
2370
+ { pattern: /features\/checkout\//, area: "checkout", priority: 95 },
2371
+ { pattern: /features\/cart\//, area: "cart", priority: 95 },
2372
+ { pattern: /features\/products?\//, area: "products", priority: 95 },
2373
+ { pattern: /features\/orders?\//, area: "orders", priority: 95 },
2374
+ { pattern: /features\/user\//, area: "user", priority: 95 },
2375
+ { pattern: /features\/settings\//, area: "settings", priority: 95 },
2376
+ { pattern: /features\/notifications?\//, area: "notifications", priority: 95 },
2377
+ { pattern: /features\/blog\//, area: "blog", priority: 95 },
2378
+ // ============================================================================
2379
+ // MODULES (outro padrão comum)
2380
+ // ============================================================================
2381
+ { pattern: /modules\/auth\//, area: "auth", priority: 95 },
2382
+ { pattern: /modules\/dashboard\//, area: "dashboard", priority: 95 },
2383
+ { pattern: /modules\/admin\//, area: "admin", priority: 95 },
2384
+ { pattern: /modules\/checkout\//, area: "checkout", priority: 95 },
2385
+ { pattern: /modules\/products?\//, area: "products", priority: 95 },
2386
+ // ============================================================================
2387
+ // LIB - Módulos específicos
2388
+ // ============================================================================
2389
+ { pattern: /lib\/firebase\/ai\//, area: "firebase-ai", priority: 85 },
2390
+ { pattern: /lib\/firebase\/aiExtraction\//, area: "firebase-ai", priority: 85 },
2391
+ { pattern: /lib\/firebase\/firestore\//, area: "firebase-firestore", priority: 85 },
2392
+ { pattern: /lib\/firebase\/prompts\//, area: "firebase-ai", priority: 85 },
2393
+ { pattern: /lib\/firebase\/analytics\//, area: "analytics", priority: 85 },
2394
+ { pattern: /lib\/firebase\//, area: "firebase-core", priority: 80 },
2395
+ { pattern: /lib\/stripe\//, area: "stripe", priority: 80 },
2396
+ { pattern: /lib\/i18n\//, area: "i18n", priority: 80 },
2397
+ // ============================================================================
2398
+ // HOOKS - Por nome
2399
+ // ============================================================================
2400
+ { pattern: /hooks\/.*[Pp]et/, area: "meus-pets", priority: 70 },
2401
+ { pattern: /hooks\/.*[Aa]uth/, area: "auth", priority: 70 },
2402
+ { pattern: /hooks\/.*[Ss]ubscription/, area: "stripe", priority: 70 },
2403
+ { pattern: /hooks\/.*[Nn]otification/, area: "notifications", priority: 70 },
2404
+ { pattern: /hooks\/.*[Hh]ealth/, area: "health-tracking", priority: 70 },
2405
+ { pattern: /hooks\/.*[Tt]raining/, area: "training", priority: 70 },
2406
+ // ============================================================================
2407
+ // STORE - Por nome
2408
+ // ============================================================================
2409
+ { pattern: /store\/.*[Pp]et/, area: "meus-pets", priority: 70 },
2410
+ { pattern: /store\/.*[Aa]uth/, area: "auth", priority: 70 },
2411
+ { pattern: /store\/.*[Uu]ser/, area: "auth", priority: 70 },
2412
+ // ============================================================================
2413
+ // SCHEMAS - Por nome
2414
+ // ============================================================================
2415
+ { pattern: /schemas\/.*[Pp]et/, area: "meus-pets", priority: 70 },
2416
+ { pattern: /schemas\/.*[Aa]uth/, area: "auth", priority: 70 },
2417
+ // ============================================================================
2418
+ // TYPES - Por nome
2419
+ // ============================================================================
2420
+ { pattern: /types\/.*[Pp]et/, area: "meus-pets", priority: 70 },
2421
+ { pattern: /types\/.*[Aa]uth/, area: "auth", priority: 70 },
2422
+ { pattern: /types\/.*[Ss]tripe/, area: "stripe", priority: 70 },
2423
+ { pattern: /types\/.*[Ss]ubscription/, area: "stripe", priority: 70 },
2424
+ // ============================================================================
2425
+ // CLOUD FUNCTIONS
2426
+ // ============================================================================
2427
+ { pattern: /functions\/src\//, area: "cloud-functions", priority: 80 },
2428
+ // ============================================================================
2429
+ // OUTROS
2430
+ // ============================================================================
2431
+ { pattern: /messages\//, area: "i18n", priority: 60 },
2432
+ { pattern: /i18n\//, area: "i18n", priority: 60 },
2433
+ { pattern: /public\//, area: "assets", priority: 50 },
2434
+ { pattern: /scripts\//, area: "scripts", priority: 50 }
2435
+ ];
2436
+ var KEYWORD_PATTERNS = [
2437
+ // Pets
2438
+ { keyword: /[Pp]et/, area: "meus-pets", priority: 60 },
2439
+ { keyword: /[Vv]accin/, area: "meus-pets", priority: 60 },
2440
+ { keyword: /[Dd]eworm/, area: "meus-pets", priority: 60 },
2441
+ { keyword: /[Mm]edication/, area: "meus-pets", priority: 60 },
2442
+ { keyword: /[Ss]urgery/, area: "meus-pets", priority: 60 },
2443
+ { keyword: /[Vv]eterinary/, area: "meus-pets", priority: 60 },
2444
+ // Consultas IA
2445
+ { keyword: /[Cc]onsultation/, area: "consultas-ia", priority: 60 },
2446
+ { keyword: /[Cc]hat/, area: "consultas-ia", priority: 50 },
2447
+ { keyword: /[Gg]emini/, area: "firebase-ai", priority: 60 },
2448
+ // Health
2449
+ { keyword: /[Hh]ealth[Tt]racking/, area: "health-tracking", priority: 65 },
2450
+ { keyword: /[Hh]ome[Cc]are/, area: "health-tracking", priority: 65 },
2451
+ // Training
2452
+ { keyword: /[Tt]raining/, area: "training", priority: 60 },
2453
+ { keyword: /[Gg]uide/, area: "training", priority: 55 },
2454
+ { keyword: /[Aa]destramento/, area: "training", priority: 60 },
2455
+ // Auth
2456
+ { keyword: /[Aa]uth/, area: "auth", priority: 60 },
2457
+ { keyword: /[Ll]ogin/, area: "auth", priority: 60 },
2458
+ { keyword: /[Rr]egister/, area: "auth", priority: 60 },
2459
+ { keyword: /[Ss]ignup/, area: "auth", priority: 60 },
2460
+ { keyword: /[Ss]ignin/, area: "auth", priority: 60 },
2461
+ // Stripe
2462
+ { keyword: /[Ss]tripe/, area: "stripe", priority: 65 },
2463
+ { keyword: /[Ss]ubscription/, area: "stripe", priority: 60 },
2464
+ { keyword: /[Pp]ayment/, area: "stripe", priority: 60 },
2465
+ { keyword: /[Cc]heckout/, area: "stripe", priority: 60 },
2466
+ { keyword: /[Pp]rice/, area: "pricing", priority: 55 },
2467
+ { keyword: /[Pp]ricing/, area: "pricing", priority: 60 },
2468
+ // Notifications
2469
+ { keyword: /[Nn]otification/, area: "notifications", priority: 60 },
2470
+ { keyword: /[Ff][Cc][Mm]/, area: "notifications", priority: 65 },
2471
+ { keyword: /[Pp]ush/, area: "notifications", priority: 55 },
2472
+ // i18n
2473
+ { keyword: /[Ii]18n/, area: "i18n", priority: 60 },
2474
+ { keyword: /[Ll]ocale/, area: "i18n", priority: 55 },
2475
+ { keyword: /[Tt]ranslat/, area: "i18n", priority: 55 },
2476
+ // SEO
2477
+ { keyword: /[Ss][Ee][Oo]/, area: "seo", priority: 60 },
2478
+ { keyword: /[Ss]itemap/, area: "seo", priority: 60 },
2479
+ { keyword: /[Mm]eta/, area: "seo", priority: 50 },
2480
+ // Analytics
2481
+ { keyword: /[Aa]nalytics/, area: "analytics", priority: 60 },
2482
+ { keyword: /[Uu]tm/, area: "analytics", priority: 55 },
2483
+ { keyword: /[Tt]racking/, area: "analytics", priority: 50 },
2484
+ // Admin
2485
+ { keyword: /[Aa]dmin/, area: "admin", priority: 60 },
2486
+ // PWA
2487
+ { keyword: /[Pp][Ww][Aa]/, area: "pwa", priority: 60 },
2488
+ { keyword: /[Ss]ervice[Ww]orker/, area: "pwa", priority: 60 },
2489
+ { keyword: /[Mm]anifest/, area: "pwa", priority: 55 },
2490
+ { keyword: /[Oo]ffline/, area: "pwa", priority: 55 },
2491
+ // PDF
2492
+ { keyword: /[Pp]df/, area: "pdf", priority: 60 },
2493
+ { keyword: /[Rr]eport/, area: "pdf", priority: 50 }
2494
+ ];
2495
+ var AREA_NAMES = {
2496
+ // Áreas específicas de domínio
2497
+ "meus-pets": "Meus Pets",
2498
+ "consultas-ia": "Consultas IA",
2499
+ "health-tracking": "Health Tracking",
2500
+ training: "Adestramento",
2501
+ // Autenticação e usuário
2502
+ auth: "Autentica\xE7\xE3o",
2503
+ user: "Usu\xE1rio",
2504
+ profile: "Perfil",
2505
+ settings: "Configura\xE7\xF5es",
2506
+ onboarding: "Onboarding",
2507
+ // E-commerce
2508
+ stripe: "Pagamentos (Stripe)",
2509
+ payments: "Pagamentos",
2510
+ checkout: "Checkout",
2511
+ cart: "Carrinho",
2512
+ shop: "Loja",
2513
+ products: "Produtos",
2514
+ orders: "Pedidos",
2515
+ pricing: "Pre\xE7os",
2516
+ // Comunicação
2517
+ notifications: "Notifica\xE7\xF5es",
2518
+ chat: "Chat",
2519
+ feedback: "Feedback",
2520
+ support: "Suporte",
2521
+ help: "Ajuda",
2522
+ faq: "FAQ",
2523
+ contact: "Contato",
2524
+ // Firebase
2525
+ "firebase-core": "Firebase Core",
2526
+ "firebase-ai": "Firebase AI",
2527
+ "firebase-firestore": "Firestore",
2528
+ // Conteúdo
2529
+ blog: "Blog",
2530
+ docs: "Documenta\xE7\xE3o",
2531
+ legal: "P\xE1ginas Legais",
2532
+ about: "Sobre",
2533
+ // Marketing e SEO
2534
+ landing: "Landing Pages",
2535
+ seo: "SEO",
2536
+ analytics: "Analytics",
2537
+ beta: "Programa Beta",
2538
+ // Admin e Dashboard
2539
+ admin: "Admin",
2540
+ dashboard: "Dashboard",
2541
+ // Técnico
2542
+ i18n: "Internacionaliza\xE7\xE3o",
2543
+ pwa: "PWA",
2544
+ pdf: "PDF",
2545
+ core: "Core",
2546
+ "shared-ui": "UI Compartilhado",
2547
+ "cloud-functions": "Cloud Functions",
2548
+ assets: "Assets",
2549
+ scripts: "Scripts",
2550
+ // UI patterns
2551
+ forms: "Formul\xE1rios",
2552
+ tables: "Tabelas",
2553
+ modals: "Modais",
2554
+ // Genéricos
2555
+ app: "Aplica\xE7\xE3o"
2556
+ };
2557
+ var AREA_DESCRIPTIONS = {
2558
+ // Áreas específicas de domínio
2559
+ "meus-pets": "Gerenciamento completo de pets do usu\xE1rio",
2560
+ "consultas-ia": "Chat com IA para consultas veterin\xE1rias",
2561
+ "health-tracking": "Acompanhamento de sa\xFAde e sintomas",
2562
+ training: "Sistema de adestramento com guias e progresso",
2563
+ // Autenticação e usuário
2564
+ auth: "Autentica\xE7\xE3o e gerenciamento de sess\xE3o",
2565
+ user: "Gerenciamento de dados do usu\xE1rio",
2566
+ profile: "Perfil do usu\xE1rio",
2567
+ settings: "Configura\xE7\xF5es do usu\xE1rio",
2568
+ onboarding: "Fluxo de onboarding de novos usu\xE1rios",
2569
+ // E-commerce
2570
+ stripe: "Integra\xE7\xE3o Stripe para pagamentos e assinaturas",
2571
+ payments: "Sistema de pagamentos",
2572
+ checkout: "Fluxo de checkout e finaliza\xE7\xE3o de compra",
2573
+ cart: "Carrinho de compras",
2574
+ shop: "Loja e cat\xE1logo de produtos",
2575
+ products: "Gerenciamento de produtos",
2576
+ orders: "Gerenciamento de pedidos",
2577
+ pricing: "P\xE1gina de pre\xE7os e planos",
2578
+ // Comunicação
2579
+ notifications: "Sistema de notifica\xE7\xF5es push",
2580
+ chat: "Sistema de chat e mensagens",
2581
+ feedback: "Coleta de feedback dos usu\xE1rios",
2582
+ support: "Suporte ao cliente",
2583
+ help: "Central de ajuda",
2584
+ faq: "Perguntas frequentes",
2585
+ contact: "P\xE1gina de contato",
2586
+ // Firebase
2587
+ "firebase-core": "Configura\xE7\xE3o e servi\xE7os Firebase client-side",
2588
+ "firebase-ai": "Integra\xE7\xE3o com Firebase AI (Gemini)",
2589
+ "firebase-firestore": "Opera\xE7\xF5es CRUD no Firestore",
2590
+ // Conteúdo
2591
+ blog: "Blog e artigos",
2592
+ docs: "Documenta\xE7\xE3o e guias",
2593
+ legal: "Termos de uso, privacidade e pol\xEDticas",
2594
+ about: "P\xE1gina sobre a empresa",
2595
+ // Marketing e SEO
2596
+ landing: "Landing pages e marketing",
2597
+ seo: "SEO, meta tags e sitemaps",
2598
+ analytics: "Analytics e tracking de eventos",
2599
+ beta: "Programa de beta testers",
2600
+ // Admin e Dashboard
2601
+ admin: "Painel administrativo",
2602
+ dashboard: "Dashboard do usu\xE1rio",
2603
+ // Técnico
2604
+ i18n: "Internacionaliza\xE7\xE3o e tradu\xE7\xF5es",
2605
+ pwa: "Progressive Web App (offline, install)",
2606
+ pdf: "Gera\xE7\xE3o de relat\xF3rios PDF",
2607
+ core: "Providers e layout principal",
2608
+ "shared-ui": "Componentes de UI compartilhados",
2609
+ "cloud-functions": "Cloud Functions (serverless)",
2610
+ assets: "Assets p\xFAblicos (imagens, fontes)",
2611
+ scripts: "Scripts de automa\xE7\xE3o",
2612
+ // UI patterns
2613
+ forms: "Componentes de formul\xE1rio reutiliz\xE1veis",
2614
+ tables: "Componentes de tabela reutiliz\xE1veis",
2615
+ modals: "Modais e dialogs reutiliz\xE1veis",
2616
+ // Genéricos
2617
+ app: "\xC1rea principal da aplica\xE7\xE3o"
2618
+ };
2619
+
2620
+ // src/areas/detector.ts
2621
+ function detectFileAreas(filePath, config) {
2622
+ const normalizedPath = filePath.replace(/\\/g, "/");
2623
+ const matches = [];
2624
+ for (const [areaId, areaConfig] of Object.entries(config.areas)) {
2625
+ if (matchesAreaConfig(normalizedPath, areaConfig)) {
2626
+ matches.push({ area: areaId, priority: 200, source: "config" });
2627
+ }
2628
+ }
2629
+ for (const { pattern, area: area2, priority } of FOLDER_PATTERNS) {
2630
+ if (pattern.test(normalizedPath)) {
2631
+ if (!matches.some((m) => m.area === area2 && m.source === "config")) {
2632
+ matches.push({ area: area2, priority, source: "folder" });
2633
+ }
2634
+ }
2635
+ }
2636
+ const fileName = normalizedPath.split("/").pop() || "";
2637
+ for (const { keyword, area: area2, priority } of KEYWORD_PATTERNS) {
2638
+ if (keyword.test(fileName) || keyword.test(normalizedPath)) {
2639
+ if (!matches.some((m) => m.area === area2)) {
2640
+ matches.push({ area: area2, priority, source: "keyword" });
2641
+ }
2642
+ }
2643
+ }
2644
+ const sorted = matches.sort((a, b) => b.priority - a.priority);
2645
+ const unique = [...new Set(sorted.map((m) => m.area))];
2646
+ return unique;
2647
+ }
2648
+ function matchesAreaConfig(filePath, config) {
2649
+ if (config.exclude) {
2650
+ for (const pattern of config.exclude) {
2651
+ if (minimatch(filePath, pattern, { dot: true })) {
2652
+ return false;
2653
+ }
2654
+ }
2655
+ }
2656
+ for (const pattern of config.patterns) {
2657
+ if (minimatch(filePath, pattern, { dot: true })) {
2658
+ return true;
2659
+ }
2660
+ }
2661
+ if (config.keywords) {
2662
+ const lowerPath = filePath.toLowerCase();
2663
+ for (const keyword of config.keywords) {
2664
+ if (lowerPath.includes(keyword.toLowerCase())) {
2665
+ return true;
2666
+ }
2667
+ }
2668
+ }
2669
+ return false;
2670
+ }
2671
+ function getAreaName(areaId, config) {
2672
+ if (config.areas[areaId]?.name) {
2673
+ return config.areas[areaId].name;
2674
+ }
2675
+ if (AREA_NAMES[areaId]) {
2676
+ return AREA_NAMES[areaId];
2677
+ }
2678
+ return areaId.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
2679
+ }
2680
+ function getAreaDescription(areaId, config) {
2681
+ if (config.areas[areaId]?.description) {
2682
+ return config.areas[areaId].description;
2683
+ }
2684
+ return AREA_DESCRIPTIONS[areaId];
2685
+ }
2686
+ function inferFileDescription(filePath, category) {
2687
+ const fileName = filePath.split("/").pop() || "";
2688
+ const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
2689
+ const patterns = [
2690
+ // Pages
2691
+ { pattern: /^page$/, description: () => "P\xE1gina principal" },
2692
+ { pattern: /^layout$/, description: () => "Layout" },
2693
+ { pattern: /^loading$/, description: () => "Estado de loading" },
2694
+ { pattern: /^error$/, description: () => "P\xE1gina de erro" },
2695
+ { pattern: /^not-found$/, description: () => "P\xE1gina 404" },
2696
+ // Components com sufixo
2697
+ { pattern: /(.+)PageClient$/, description: (m) => `Client component da p\xE1gina ${m[1]}` },
2698
+ { pattern: /(.+)Dialog$/, description: (m) => `Dialog de ${m[1].toLowerCase()}` },
2699
+ { pattern: /(.+)Modal$/, description: (m) => `Modal de ${m[1].toLowerCase()}` },
2700
+ { pattern: /(.+)Form$/, description: (m) => `Formul\xE1rio de ${m[1].toLowerCase()}` },
2701
+ { pattern: /(.+)Card$/, description: (m) => `Card de ${m[1].toLowerCase()}` },
2702
+ { pattern: /(.+)List$/, description: (m) => `Lista de ${m[1].toLowerCase()}` },
2703
+ { pattern: /(.+)Table$/, description: (m) => `Tabela de ${m[1].toLowerCase()}` },
2704
+ { pattern: /(.+)Manager$/, description: (m) => `Gerenciador de ${m[1].toLowerCase()}` },
2705
+ { pattern: /(.+)Provider$/, description: (m) => `Provider de ${m[1].toLowerCase()}` },
2706
+ { pattern: /(.+)Context$/, description: (m) => `Context de ${m[1].toLowerCase()}` },
2707
+ { pattern: /(.+)Step$/, description: (m) => `Step: ${m[1]}` },
2708
+ { pattern: /(.+)Tab$/, description: (m) => `Aba: ${m[1]}` },
2709
+ { pattern: /(.+)Section$/, description: (m) => `Se\xE7\xE3o: ${m[1]}` },
2710
+ { pattern: /(.+)Header$/, description: (m) => `Header de ${m[1].toLowerCase()}` },
2711
+ { pattern: /(.+)Footer$/, description: (m) => `Footer de ${m[1].toLowerCase()}` },
2712
+ { pattern: /(.+)Skeleton$/, description: (m) => `Skeleton de ${m[1].toLowerCase()}` },
2713
+ { pattern: /(.+)Chip$/, description: (m) => `Chip de ${m[1].toLowerCase()}` },
2714
+ { pattern: /(.+)Badge$/, description: (m) => `Badge de ${m[1].toLowerCase()}` },
2715
+ { pattern: /(.+)Button$/, description: (m) => `Bot\xE3o ${m[1].toLowerCase()}` },
2716
+ { pattern: /(.+)Icon$/, description: (m) => `\xCDcone ${m[1].toLowerCase()}` },
2717
+ // Hooks
2718
+ { pattern: /^use(.+)$/, description: (m) => `Hook de ${m[1].toLowerCase()}` },
2719
+ // Types/Schemas
2720
+ { pattern: /(.+)\.types$/, description: (m) => `Tipos de ${m[1].toLowerCase()}` },
2721
+ { pattern: /(.+)Schemas?$/, description: (m) => `Schema de ${m[1].toLowerCase()}` },
2722
+ // Utils
2723
+ { pattern: /(.+)Helpers?$/, description: (m) => `Helpers de ${m[1].toLowerCase()}` },
2724
+ { pattern: /(.+)Utils?$/, description: (m) => `Utilit\xE1rios de ${m[1].toLowerCase()}` },
2725
+ { pattern: /(.+)Formatters?$/, description: (m) => `Formatador de ${m[1].toLowerCase()}` },
2726
+ { pattern: /(.+)Validators?$/, description: (m) => `Validador de ${m[1].toLowerCase()}` },
2727
+ { pattern: /(.+)Mappers?$/, description: (m) => `Mapper de ${m[1].toLowerCase()}` },
2728
+ { pattern: /(.+)Converters?$/, description: (m) => `Conversor de ${m[1].toLowerCase()}` },
2729
+ // Services
2730
+ { pattern: /(.+)Service$/, description: (m) => `Servi\xE7o de ${m[1].toLowerCase()}` },
2731
+ // Index
2732
+ { pattern: /^index$/, description: () => "Export principal" }
2733
+ ];
2734
+ for (const { pattern, description } of patterns) {
2735
+ const match = fileNameNoExt.match(pattern);
2736
+ if (match) {
2737
+ return description(match);
2738
+ }
2739
+ }
2740
+ const categoryDescriptions = {
2741
+ page: "P\xE1gina",
2742
+ layout: "Layout",
2743
+ route: "API Route",
2744
+ component: "Componente",
2745
+ hook: "Hook",
2746
+ service: "Servi\xE7o",
2747
+ store: "Store",
2748
+ util: "Utilit\xE1rio",
2749
+ type: "Tipos",
2750
+ config: "Configura\xE7\xE3o",
2751
+ test: "Teste"
2752
+ };
2753
+ return categoryDescriptions[category];
2754
+ }
2755
+
2756
+ // src/commands/areas.ts
2757
+ var CODE_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
2758
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([
2759
+ "node_modules",
2760
+ "dist",
2761
+ "build",
2762
+ ".git",
2763
+ ".next",
2764
+ ".cache",
2765
+ "coverage",
2766
+ ".turbo",
2767
+ ".vercel",
2768
+ ".analyze"
2769
+ ]);
2770
+ async function areas(options = {}) {
2771
+ const cwd = options.cwd || process.cwd();
2772
+ const format = options.format || "text";
2773
+ try {
2774
+ const config = readConfig(cwd);
2775
+ const allFiles = getAllCodeFiles2(cwd);
2776
+ const areaMap = /* @__PURE__ */ new Map();
2777
+ const unmapped = [];
2778
+ for (const filePath of allFiles) {
2779
+ const category = detectCategory(filePath);
2780
+ const areas2 = detectFileAreas(filePath, config);
2781
+ let description = getFileDescription(cwd, filePath);
2782
+ if (!description && config.settings?.inferDescriptions !== false) {
2783
+ description = inferFileDescription(filePath, category);
2784
+ }
2785
+ const areaFile = {
2786
+ path: filePath,
2787
+ category,
2788
+ description
2789
+ };
2790
+ if (areas2.length === 0) {
2791
+ unmapped.push(areaFile);
2792
+ } else {
2793
+ for (const areaId of areas2) {
2794
+ if (!areaMap.has(areaId)) {
2795
+ areaMap.set(areaId, []);
2796
+ }
2797
+ areaMap.get(areaId).push(areaFile);
2798
+ }
2799
+ }
2800
+ }
2801
+ const detectedAreas = [];
2802
+ for (const [areaId, files] of areaMap) {
2803
+ const categories = {};
2804
+ for (const file of files) {
2805
+ categories[file.category] = (categories[file.category] || 0) + 1;
2806
+ }
2807
+ detectedAreas.push({
2808
+ id: areaId,
2809
+ name: getAreaName(areaId, config),
2810
+ description: getAreaDescription(areaId, config),
2811
+ files,
2812
+ fileCount: files.length,
2813
+ categories,
2814
+ isAutoDetected: !config.areas[areaId]
2815
+ });
2816
+ }
2817
+ detectedAreas.sort((a, b) => b.fileCount - a.fileCount);
2818
+ const result = {
2819
+ version: "1.0.0",
2820
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2821
+ cwd,
2822
+ areas: detectedAreas,
2823
+ unmapped,
2824
+ summary: {
2825
+ totalAreas: detectedAreas.length,
2826
+ totalFiles: allFiles.length,
2827
+ unmappedCount: unmapped.length
2828
+ }
2829
+ };
2830
+ if (format === "json") {
2831
+ return JSON.stringify(result, null, 2);
2832
+ }
2833
+ return formatAreasText(result);
2834
+ } catch (error) {
2835
+ const message = error instanceof Error ? error.message : String(error);
2836
+ throw new Error(`Erro ao executar areas: ${message}`);
2837
+ }
2838
+ }
2839
+ function getAllCodeFiles2(dir, files = [], baseDir = dir) {
2840
+ try {
2841
+ const entries = readdirSync3(dir);
2842
+ for (const entry of entries) {
2843
+ const fullPath = join5(dir, entry);
2844
+ if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) {
2845
+ continue;
2846
+ }
2847
+ try {
2848
+ const stat = statSync3(fullPath);
2849
+ if (stat.isDirectory()) {
2850
+ getAllCodeFiles2(fullPath, files, baseDir);
2851
+ } else {
2852
+ const ext = extname3(entry).toLowerCase();
2853
+ if (CODE_EXTENSIONS3.has(ext)) {
2854
+ const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
2855
+ files.push(relativePath);
2856
+ }
2857
+ }
2858
+ } catch {
2859
+ }
2860
+ }
2861
+ } catch {
2862
+ }
2863
+ return files;
2864
+ }
2865
+
2866
+ // src/commands/area.ts
2867
+ import { readdirSync as readdirSync4, statSync as statSync4 } from "fs";
2868
+ import { join as join6, extname as extname4 } from "path";
2869
+ var CODE_EXTENSIONS4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
2870
+ var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
2871
+ "node_modules",
2872
+ "dist",
2873
+ "build",
2874
+ ".git",
2875
+ ".next",
2876
+ ".cache",
2877
+ "coverage",
2878
+ ".turbo",
2879
+ ".vercel",
2880
+ ".analyze"
2881
+ ]);
2882
+ async function area(target, options = {}) {
2883
+ const cwd = options.cwd || process.cwd();
2884
+ const format = options.format || "text";
2885
+ const filterType = options.type;
2886
+ const full = options.full ?? false;
2887
+ if (!target) {
2888
+ throw new Error("Nome da \xE1rea \xE9 obrigat\xF3rio. Exemplo: ai-tool area meus-pets");
2889
+ }
2890
+ try {
2891
+ const config = readConfig(cwd);
2892
+ const allFiles = getAllCodeFiles3(cwd);
2893
+ const areaFiles = [];
2894
+ const targetLower = target.toLowerCase();
2895
+ for (const filePath of allFiles) {
2896
+ const fileAreas = detectFileAreas(filePath, config);
2897
+ const belongsToArea = fileAreas.some(
2898
+ (a) => a.toLowerCase() === targetLower || a.toLowerCase().includes(targetLower)
2899
+ );
2900
+ if (belongsToArea) {
2901
+ const category = detectCategory(filePath);
2902
+ if (filterType && category !== filterType) {
2903
+ continue;
2904
+ }
2905
+ let description = getFileDescription(cwd, filePath);
2906
+ if (!description && config.settings?.inferDescriptions !== false) {
2907
+ description = inferFileDescription(filePath, category);
2908
+ }
2909
+ areaFiles.push({
2910
+ path: filePath,
2911
+ category,
2912
+ description
2913
+ });
2914
+ }
2915
+ }
2916
+ if (areaFiles.length === 0) {
2917
+ const availableAreas = getAvailableAreas(allFiles, config);
2918
+ return formatAreaNotFound(target, availableAreas);
2919
+ }
2920
+ const byCategory = {};
2921
+ const categories = {};
2922
+ for (const file of areaFiles) {
2923
+ if (!byCategory[file.category]) {
2924
+ byCategory[file.category] = [];
2925
+ }
2926
+ byCategory[file.category].push(file);
2927
+ categories[file.category] = (categories[file.category] || 0) + 1;
2928
+ }
2929
+ for (const cat of Object.keys(byCategory)) {
2930
+ byCategory[cat].sort((a, b) => a.path.localeCompare(b.path));
2931
+ }
2932
+ const realAreaId = findRealAreaId(target, allFiles, config);
2933
+ const detectedArea = {
2934
+ id: realAreaId || target,
2935
+ name: getAreaName(realAreaId || target, config),
2936
+ description: getAreaDescription(realAreaId || target, config),
2937
+ files: areaFiles,
2938
+ fileCount: areaFiles.length,
2939
+ categories,
2940
+ isAutoDetected: !config.areas[realAreaId || target]
2941
+ };
2942
+ const result = {
2943
+ version: "1.0.0",
2944
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2945
+ area: detectedArea,
2946
+ byCategory
2947
+ };
2948
+ if (format === "json") {
2949
+ return JSON.stringify(result, null, 2);
2950
+ }
2951
+ return formatAreaDetailText(result, { full, filterType });
2952
+ } catch (error) {
2953
+ const message = error instanceof Error ? error.message : String(error);
2954
+ throw new Error(`Erro ao executar area: ${message}`);
2955
+ }
2956
+ }
2957
+ function findRealAreaId(target, allFiles, config) {
2958
+ const targetLower = target.toLowerCase();
2959
+ for (const areaId of Object.keys(config.areas)) {
2960
+ if (areaId.toLowerCase() === targetLower || areaId.toLowerCase().includes(targetLower)) {
2961
+ return areaId;
2962
+ }
2963
+ }
2964
+ const detectedAreas = /* @__PURE__ */ new Set();
2965
+ for (const filePath of allFiles) {
2966
+ const areas2 = detectFileAreas(filePath, config);
2967
+ for (const areaId of areas2) {
2968
+ if (areaId.toLowerCase() === targetLower || areaId.toLowerCase().includes(targetLower)) {
2969
+ detectedAreas.add(areaId);
2970
+ }
2971
+ }
2972
+ }
2973
+ return detectedAreas.size > 0 ? [...detectedAreas][0] : null;
2974
+ }
2975
+ function getAvailableAreas(allFiles, config) {
2976
+ const areaCounts = /* @__PURE__ */ new Map();
2977
+ for (const filePath of allFiles) {
2978
+ const areas2 = detectFileAreas(filePath, config);
2979
+ for (const areaId of areas2) {
2980
+ areaCounts.set(areaId, (areaCounts.get(areaId) || 0) + 1);
2981
+ }
2982
+ }
2983
+ return [...areaCounts.entries()].map(([id, count]) => ({ id, count })).sort((a, b) => b.count - a.count);
2984
+ }
2985
+ function formatAreaNotFound(target, availableAreas) {
2986
+ let out = `
2987
+ \u274C \xC1rea n\xE3o encontrada: "${target}"
2988
+
2989
+ `;
2990
+ if (availableAreas.length > 0) {
2991
+ out += `\u{1F4E6} \xC1reas dispon\xEDveis:
2992
+
2993
+ `;
2994
+ for (const { id, count } of availableAreas.slice(0, 15)) {
2995
+ out += ` ${id.padEnd(25)} ${count} arquivos
2996
+ `;
2997
+ }
2998
+ if (availableAreas.length > 15) {
2999
+ out += ` ... e mais ${availableAreas.length - 15}
3000
+ `;
3001
+ }
3002
+ out += `
3003
+ `;
3004
+ }
3005
+ out += `\u{1F4A1} Dicas:
3006
+ `;
3007
+ out += ` - Use o ID exato da \xE1rea (ex: ai-tool area meus-pets)
3008
+ `;
3009
+ out += ` - Use 'ai-tool areas' para listar todas as \xE1reas
3010
+ `;
3011
+ return out;
3012
+ }
3013
+ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
3014
+ try {
3015
+ const entries = readdirSync4(dir);
3016
+ for (const entry of entries) {
3017
+ const fullPath = join6(dir, entry);
3018
+ if (IGNORED_DIRS2.has(entry) || entry.startsWith(".")) {
3019
+ continue;
3020
+ }
3021
+ try {
3022
+ const stat = statSync4(fullPath);
3023
+ if (stat.isDirectory()) {
3024
+ getAllCodeFiles3(fullPath, files, baseDir);
3025
+ } else {
3026
+ const ext = extname4(entry).toLowerCase();
3027
+ if (CODE_EXTENSIONS4.has(ext)) {
3028
+ const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
3029
+ files.push(relativePath);
3030
+ }
3031
+ }
3032
+ } catch {
3033
+ }
3034
+ }
3035
+ } catch {
3036
+ }
3037
+ return files;
3038
+ }
3039
+
3040
+ // src/commands/areas-init.ts
3041
+ import { readdirSync as readdirSync5, statSync as statSync5 } from "fs";
3042
+ import { join as join7, extname as extname5 } from "path";
3043
+ var CODE_EXTENSIONS5 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
3044
+ var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
3045
+ "node_modules",
3046
+ "dist",
3047
+ "build",
3048
+ ".git",
3049
+ ".next",
3050
+ ".cache",
3051
+ "coverage",
3052
+ ".turbo",
3053
+ ".vercel",
3054
+ ".analyze"
3055
+ ]);
3056
+ async function areasInit(options = {}) {
3057
+ const cwd = options.cwd || process.cwd();
3058
+ const force = options.force ?? false;
3059
+ try {
3060
+ if (configExists(cwd) && !force) {
3061
+ return `
3062
+ \u26A0\uFE0F Arquivo de configura\xE7\xE3o j\xE1 existe: .analyze/areas.config.json
3063
+
3064
+ Use --force para sobrescrever:
3065
+ ai-tool areas init --force
3066
+
3067
+ Ou edite manualmente o arquivo existente.
3068
+ `.trim();
3069
+ }
3070
+ const allFiles = getAllCodeFiles4(cwd);
3071
+ const currentConfig = readConfig(cwd);
3072
+ const areaCounts = /* @__PURE__ */ new Map();
3073
+ for (const filePath of allFiles) {
3074
+ const areas2 = detectFileAreas(filePath, currentConfig);
3075
+ for (const areaId of areas2) {
3076
+ if (!areaCounts.has(areaId)) {
3077
+ areaCounts.set(areaId, /* @__PURE__ */ new Set());
3078
+ }
3079
+ areaCounts.get(areaId).add(filePath);
3080
+ }
3081
+ }
3082
+ const generatedAreas = {};
3083
+ for (const [areaId, files] of areaCounts) {
3084
+ const patterns = inferPatternsFromFiles([...files]);
3085
+ generatedAreas[areaId] = {
3086
+ name: getAreaName(areaId, currentConfig),
3087
+ description: getAreaDescription(areaId, currentConfig),
3088
+ patterns
3089
+ };
3090
+ }
3091
+ const newConfig = {
3092
+ $schema: "./areas.schema.json",
3093
+ version: "1.0.0",
3094
+ areas: generatedAreas,
3095
+ descriptions: {},
3096
+ settings: {
3097
+ autoDetect: true,
3098
+ inferDescriptions: true,
3099
+ groupByCategory: true
3100
+ }
3101
+ };
3102
+ writeConfig(cwd, newConfig);
3103
+ const sortedAreas = [...areaCounts.entries()].sort((a, b) => b[1].size - a[1].size);
3104
+ let out = `
3105
+ \u2705 Arquivo criado: .analyze/areas.config.json
3106
+
3107
+ \u{1F4E6} \xC1reas detectadas: ${sortedAreas.length}
3108
+
3109
+ `;
3110
+ for (const [areaId, files] of sortedAreas.slice(0, 15)) {
3111
+ const name = getAreaName(areaId, newConfig);
3112
+ out += ` ${name.padEnd(25)} ${files.size} arquivos
3113
+ `;
3114
+ }
3115
+ if (sortedAreas.length > 15) {
3116
+ out += ` ... e mais ${sortedAreas.length - 15}
3117
+ `;
3118
+ }
3119
+ const unmappedCount = allFiles.filter(
3120
+ (f) => detectFileAreas(f, currentConfig).length === 0
3121
+ ).length;
3122
+ if (unmappedCount > 0) {
3123
+ out += `
3124
+ \u26A0\uFE0F ${unmappedCount} arquivos sem \xE1rea definida
3125
+ Use 'ai-tool areas' para ver detalhes
3126
+ `;
3127
+ }
3128
+ out += `
3129
+ \u{1F4A1} Edite o arquivo para:
3130
+ - Renomear \xE1reas (campo "name")
3131
+ - Adicionar descri\xE7\xF5es (campo "description")
3132
+ - Ajustar padr\xF5es (campo "patterns")
3133
+ - Adicionar/remover \xE1reas
3134
+ - Definir descri\xE7\xF5es espec\xEDficas de arquivos (campo "descriptions")
3135
+ `;
3136
+ return out.trim();
3137
+ } catch (error) {
3138
+ const message = error instanceof Error ? error.message : String(error);
3139
+ throw new Error(`Erro ao executar areas init: ${message}`);
3140
+ }
3141
+ }
3142
+ function inferPatternsFromFiles(files) {
3143
+ const patterns = /* @__PURE__ */ new Set();
3144
+ const folderGroups = /* @__PURE__ */ new Map();
3145
+ for (const file of files) {
3146
+ const parts = file.split("/");
3147
+ if (parts.length > 1) {
3148
+ const folder = parts.slice(0, Math.min(3, parts.length - 1)).join("/");
3149
+ if (!folderGroups.has(folder)) {
3150
+ folderGroups.set(folder, []);
3151
+ }
3152
+ folderGroups.get(folder).push(file);
3153
+ }
3154
+ }
3155
+ for (const [folder, folderFiles] of folderGroups) {
3156
+ if (folderFiles.length >= 2) {
3157
+ patterns.add(`${folder}/**`);
3158
+ } else {
3159
+ patterns.add(folderFiles[0]);
3160
+ }
3161
+ }
3162
+ for (const file of files) {
3163
+ if (!file.includes("/")) {
3164
+ patterns.add(file);
3165
+ }
3166
+ }
3167
+ return [...patterns].sort();
3168
+ }
3169
+ function getAllCodeFiles4(dir, files = [], baseDir = dir) {
3170
+ try {
3171
+ const entries = readdirSync5(dir);
3172
+ for (const entry of entries) {
3173
+ const fullPath = join7(dir, entry);
3174
+ if (IGNORED_DIRS3.has(entry) || entry.startsWith(".")) {
3175
+ continue;
3176
+ }
3177
+ try {
3178
+ const stat = statSync5(fullPath);
3179
+ if (stat.isDirectory()) {
3180
+ getAllCodeFiles4(fullPath, files, baseDir);
3181
+ } else {
3182
+ const ext = extname5(entry).toLowerCase();
3183
+ if (CODE_EXTENSIONS5.has(ext)) {
3184
+ const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
3185
+ files.push(relativePath);
3186
+ }
3187
+ }
3188
+ } catch {
3189
+ }
3190
+ }
3191
+ } catch {
3192
+ }
3193
+ return files;
3194
+ }
3195
+
1946
3196
  // src/index.ts
1947
- var VERSION = "0.3.2";
3197
+ import { createRequire } from "module";
3198
+ var require2 = createRequire(import.meta.url);
3199
+ var pkg = require2("../package.json");
3200
+ var VERSION = pkg.version;
1948
3201
 
1949
3202
  export {
1950
3203
  detectCategory,
@@ -1965,5 +3218,23 @@ export {
1965
3218
  impact,
1966
3219
  suggest,
1967
3220
  context,
3221
+ configExists,
3222
+ readConfig,
3223
+ writeConfig,
3224
+ setArea,
3225
+ removeArea,
3226
+ setFileDescription,
3227
+ getFileDescription,
3228
+ FOLDER_PATTERNS,
3229
+ KEYWORD_PATTERNS,
3230
+ AREA_NAMES,
3231
+ AREA_DESCRIPTIONS,
3232
+ detectFileAreas,
3233
+ getAreaName,
3234
+ getAreaDescription,
3235
+ inferFileDescription,
3236
+ areas,
3237
+ area,
3238
+ areasInit,
1968
3239
  VERSION
1969
3240
  };