@codexa/cli 8.5.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.
@@ -0,0 +1,110 @@
1
+ # Universal Stack Detection System
2
+
3
+ Sistema de detecção de stack agnóstico e modular para o Codexa Workflow.
4
+
5
+ ## Ecossistemas Suportados
6
+
7
+ | Ecossistema | Marcadores Principais | Tecnologias Detectadas |
8
+ |-------------|----------------------|------------------------|
9
+ | **.NET** | `*.csproj`, `*.sln`, `*.fsproj` | ASP.NET Core, Blazor, EF Core, xUnit, NUnit |
10
+ | **Node.js** | `package.json`, `*.lock` | Next.js, React, Vue, Express, Prisma, Drizzle |
11
+ | **Python** | `pyproject.toml`, `requirements.txt` | Django, FastAPI, Flask, SQLAlchemy, pytest |
12
+ | **Go** | `go.mod`, `go.sum` | Gin, Echo, Fiber, GORM, Ent |
13
+ | **Rust** | `Cargo.toml`, `Cargo.lock` | Actix, Axum, Diesel, SeaORM, Tokio |
14
+ | **JVM** | `pom.xml`, `build.gradle` | Spring Boot, Quarkus, Hibernate, JUnit |
15
+ | **Flutter** | `pubspec.yaml` | Flutter, Riverpod, Bloc, Hive |
16
+
17
+ ## Arquitetura
18
+
19
+ ```
20
+ detectors/
21
+ ├── index.ts # Core: tipos, registro, utilities
22
+ ├── loader.ts # Carrega todos os detectores
23
+ ├── dotnet.ts # .NET/C#/F#
24
+ ├── node.ts # Node.js/Bun/Deno
25
+ ├── python.ts # Python
26
+ ├── go.ts # Go
27
+ ├── rust.ts # Rust
28
+ ├── jvm.ts # Java/Kotlin/Scala
29
+ └── flutter.ts # Flutter/Dart
30
+ ```
31
+
32
+ ## Uso
33
+
34
+ ### Detecção Completa
35
+
36
+ ```typescript
37
+ import { detectUniversal } from "./detectors/loader";
38
+
39
+ const result = await detectUniversal();
40
+
41
+ console.log(result.primary); // { language, runtime, framework }
42
+ console.log(result.ecosystems); // ["dotnet", "node", ...]
43
+ console.log(result.stack); // { frontend, backend, database, ... }
44
+ console.log(result.allTechnologies); // Full details with confidence
45
+ ```
46
+
47
+ ### Compatibilidade Legacy
48
+
49
+ ```typescript
50
+ import { detectStackLegacy } from "./detectors/loader";
51
+
52
+ const { stack, structure } = await detectStackLegacy();
53
+ // Returns same format as old detectStack()/detectStructure()
54
+ ```
55
+
56
+ ## Adicionando Novo Detector
57
+
58
+ ```typescript
59
+ // detectors/myecosystem.ts
60
+ import { registerDetector, Detector } from "./index";
61
+
62
+ const myDetector: Detector = {
63
+ name: "myecosystem",
64
+ ecosystem: "myecosystem",
65
+ priority: 50,
66
+ markers: [
67
+ { type: "file", pattern: "myconfig.json", weight: 1.0 },
68
+ ],
69
+
70
+ async detect(cwd: string) {
71
+ // Detection logic
72
+ return {
73
+ ecosystem: "myecosystem",
74
+ technologies: [...],
75
+ structure: {...},
76
+ configFiles: [...],
77
+ };
78
+ },
79
+ };
80
+
81
+ registerDetector(myDetector);
82
+ ```
83
+
84
+ Depois adicione o import em `loader.ts`:
85
+
86
+ ```typescript
87
+ import "./myecosystem";
88
+ ```
89
+
90
+ ## Categorias de Tecnologia
91
+
92
+ - `runtime` - Linguagem/runtime (Node.js, .NET, Python)
93
+ - `frontend` - Frameworks frontend (Next.js, React, Flutter)
94
+ - `backend` - Frameworks backend (Express, ASP.NET, Django)
95
+ - `database` - Bancos de dados (PostgreSQL, MongoDB, Redis)
96
+ - `orm` - ORMs (Prisma, EF Core, SQLAlchemy)
97
+ - `styling` - CSS/Styling (Tailwind, Styled Components)
98
+ - `auth` - Autenticação (NextAuth, Firebase Auth)
99
+ - `testing` - Testes (Jest, pytest, xUnit)
100
+ - `build` - Build tools (Webpack, Gradle, Poetry)
101
+ - `devops` - DevOps (Docker, Prometheus)
102
+
103
+ ## Confiança (Confidence)
104
+
105
+ Cada tecnologia detectada tem um score de confiança (0-1):
106
+
107
+ - **1.0**: Arquivo/configuração específica encontrada
108
+ - **0.9**: Dependência explícita no manifest
109
+ - **0.7-0.8**: Arquivos de código encontrados
110
+ - **0.5-0.6**: Diretórios/estrutura inferida
@@ -0,0 +1,358 @@
1
+ /**
2
+ * .NET Ecosystem Detector
3
+ *
4
+ * Detects C#, F#, VB.NET projects including:
5
+ * - ASP.NET Core (MVC, Web API, Razor, Blazor)
6
+ * - .NET MAUI
7
+ * - Entity Framework
8
+ * - Dapper
9
+ * - xUnit, NUnit, MSTest
10
+ */
11
+
12
+ import { join } from "path";
13
+ import {
14
+ registerDetector,
15
+ Detector,
16
+ DetectorResult,
17
+ DetectedTechnology,
18
+ fileExists,
19
+ dirExists,
20
+ findFiles,
21
+ readText,
22
+ } from "./index";
23
+
24
+ interface CsprojInfo {
25
+ path: string;
26
+ sdk?: string;
27
+ targetFramework?: string;
28
+ packages: { name: string; version: string }[];
29
+ projectReferences: string[];
30
+ }
31
+
32
+ function parseCsproj(content: string): Partial<CsprojInfo> {
33
+ const result: Partial<CsprojInfo> = { packages: [], projectReferences: [] };
34
+
35
+ // Extract SDK
36
+ const sdkMatch = content.match(/<Project\s+Sdk="([^"]+)"/i);
37
+ if (sdkMatch) result.sdk = sdkMatch[1];
38
+
39
+ // Extract TargetFramework(s)
40
+ const tfMatch = content.match(/<TargetFramework>([^<]+)<\/TargetFramework>/i);
41
+ if (tfMatch) result.targetFramework = tfMatch[1];
42
+
43
+ const tfsMatch = content.match(/<TargetFrameworks>([^<]+)<\/TargetFrameworks>/i);
44
+ if (tfsMatch) result.targetFramework = tfsMatch[1].split(";")[0];
45
+
46
+ // Extract PackageReferences
47
+ const packageRegex = /<PackageReference\s+Include="([^"]+)"(?:\s+Version="([^"]+)")?/gi;
48
+ let match;
49
+ while ((match = packageRegex.exec(content)) !== null) {
50
+ result.packages!.push({ name: match[1], version: match[2] || "unknown" });
51
+ }
52
+
53
+ // Extract ProjectReferences
54
+ const projRefRegex = /<ProjectReference\s+Include="([^"]+)"/gi;
55
+ while ((match = projRefRegex.exec(content)) !== null) {
56
+ result.projectReferences!.push(match[1]);
57
+ }
58
+
59
+ return result;
60
+ }
61
+
62
+ function parseSln(content: string): string[] {
63
+ const projects: string[] = [];
64
+ const projectRegex = /Project\([^)]+\)\s*=\s*"[^"]+",\s*"([^"]+\.csproj)"/gi;
65
+ let match;
66
+ while ((match = projectRegex.exec(content)) !== null) {
67
+ projects.push(match[1].replace(/\\/g, "/"));
68
+ }
69
+ return projects;
70
+ }
71
+
72
+ const dotnetDetector: Detector = {
73
+ name: "dotnet",
74
+ ecosystem: "dotnet",
75
+ priority: 100,
76
+ markers: [
77
+ { type: "glob", pattern: "*.sln", weight: 1.0 },
78
+ { type: "glob", pattern: "*.csproj", weight: 1.0 },
79
+ { type: "glob", pattern: "**/*.csproj", weight: 0.9 },
80
+ { type: "glob", pattern: "*.fsproj", weight: 1.0 },
81
+ { type: "glob", pattern: "*.vbproj", weight: 1.0 },
82
+ { type: "file", pattern: "global.json", weight: 0.8 },
83
+ { type: "file", pattern: "nuget.config", weight: 0.6 },
84
+ { type: "file", pattern: "Directory.Build.props", weight: 0.7 },
85
+ { type: "directory", pattern: "obj", weight: 0.3 },
86
+ { type: "directory", pattern: "bin", weight: 0.3 },
87
+ ],
88
+
89
+ async detect(cwd: string): Promise<DetectorResult | null> {
90
+ const technologies: DetectedTechnology[] = [];
91
+ const structure: Record<string, string> = {};
92
+ const configFiles: string[] = [];
93
+
94
+ // Find all project files
95
+ const slnFiles = findFiles(cwd, "*.sln");
96
+ const csprojFiles = findFiles(cwd, "**/*.csproj");
97
+ const fsprojFiles = findFiles(cwd, "**/*.fsproj");
98
+
99
+ if (csprojFiles.length === 0 && fsprojFiles.length === 0 && slnFiles.length === 0) {
100
+ return null;
101
+ }
102
+
103
+ // Parse solution file if exists
104
+ if (slnFiles.length > 0) {
105
+ configFiles.push(slnFiles[0]);
106
+ }
107
+
108
+ // Track detected packages across all projects
109
+ const allPackages = new Map<string, string>();
110
+ const projectInfos: CsprojInfo[] = [];
111
+
112
+ // Parse each csproj
113
+ for (const csproj of csprojFiles) {
114
+ const content = readText(join(cwd, csproj));
115
+ if (!content) continue;
116
+
117
+ configFiles.push(csproj);
118
+ const info = parseCsproj(content);
119
+ info.path = csproj;
120
+ projectInfos.push(info as CsprojInfo);
121
+
122
+ for (const pkg of info.packages || []) {
123
+ allPackages.set(pkg.name.toLowerCase(), pkg.version);
124
+ }
125
+ }
126
+
127
+ // Determine primary SDK/framework type
128
+ const sdks = projectInfos.map(p => p.sdk).filter(Boolean);
129
+ const frameworks = projectInfos.map(p => p.targetFramework).filter(Boolean);
130
+
131
+ // Runtime detection
132
+ if (frameworks.length > 0) {
133
+ const framework = frameworks[0]!;
134
+ let runtimeName = ".NET";
135
+ let version = framework;
136
+
137
+ if (framework.startsWith("net8")) {
138
+ runtimeName = ".NET 8";
139
+ version = "8.0";
140
+ } else if (framework.startsWith("net7")) {
141
+ runtimeName = ".NET 7";
142
+ version = "7.0";
143
+ } else if (framework.startsWith("net6")) {
144
+ runtimeName = ".NET 6";
145
+ version = "6.0";
146
+ } else if (framework.startsWith("net5")) {
147
+ runtimeName = ".NET 5";
148
+ version = "5.0";
149
+ } else if (framework.startsWith("netcoreapp")) {
150
+ runtimeName = ".NET Core";
151
+ version = framework.replace("netcoreapp", "");
152
+ } else if (framework.startsWith("netstandard")) {
153
+ runtimeName = ".NET Standard";
154
+ version = framework.replace("netstandard", "");
155
+ } else if (framework.startsWith("net4")) {
156
+ runtimeName = ".NET Framework";
157
+ version = framework.replace("net", "");
158
+ }
159
+
160
+ technologies.push({
161
+ name: runtimeName,
162
+ version,
163
+ confidence: 1.0,
164
+ source: projectInfos[0]?.path || "csproj",
165
+ category: "runtime",
166
+ metadata: { targetFramework: framework },
167
+ });
168
+ }
169
+
170
+ // Backend framework detection
171
+ const backendFrameworks = [
172
+ { packages: ["microsoft.aspnetcore.app", "microsoft.aspnetcore"], name: "ASP.NET Core", category: "backend" as const },
173
+ { packages: ["microsoft.aspnetcore.mvc"], name: "ASP.NET Core MVC", category: "backend" as const },
174
+ { packages: ["microsoft.aspnetcore.components"], name: "Blazor", category: "frontend" as const },
175
+ { packages: ["microsoft.maui"], name: ".NET MAUI", category: "frontend" as const },
176
+ { packages: ["avalonia", "avalonia.desktop"], name: "Avalonia", category: "frontend" as const },
177
+ { packages: ["uno.ui"], name: "Uno Platform", category: "frontend" as const },
178
+ ];
179
+
180
+ // Check SDK for implicit framework
181
+ for (const sdk of sdks) {
182
+ if (sdk?.includes("Microsoft.NET.Sdk.Web")) {
183
+ technologies.push({
184
+ name: "ASP.NET Core",
185
+ confidence: 1.0,
186
+ source: "SDK",
187
+ category: "backend",
188
+ });
189
+ } else if (sdk?.includes("Microsoft.NET.Sdk.BlazorWebAssembly")) {
190
+ technologies.push({
191
+ name: "Blazor WebAssembly",
192
+ confidence: 1.0,
193
+ source: "SDK",
194
+ category: "frontend",
195
+ });
196
+ } else if (sdk?.includes("Microsoft.NET.Sdk.Razor")) {
197
+ technologies.push({
198
+ name: "Razor",
199
+ confidence: 0.9,
200
+ source: "SDK",
201
+ category: "frontend",
202
+ });
203
+ }
204
+ }
205
+
206
+ for (const fw of backendFrameworks) {
207
+ const found = fw.packages.find(p => allPackages.has(p));
208
+ if (found) {
209
+ technologies.push({
210
+ name: fw.name,
211
+ version: allPackages.get(found),
212
+ confidence: 0.95,
213
+ source: `Package: ${found}`,
214
+ category: fw.category,
215
+ });
216
+ }
217
+ }
218
+
219
+ // ORM detection
220
+ const ormMap = [
221
+ { packages: ["microsoft.entityframeworkcore", "entityframework"], name: "Entity Framework", category: "orm" as const },
222
+ { packages: ["dapper"], name: "Dapper", category: "orm" as const },
223
+ { packages: ["npgsql.entityframeworkcore.postgresql"], name: "EF Core + PostgreSQL", category: "orm" as const },
224
+ { packages: ["pomelo.entityframeworkcore.mysql"], name: "EF Core + MySQL", category: "orm" as const },
225
+ { packages: ["microsoft.entityframeworkcore.sqlserver"], name: "EF Core + SQL Server", category: "orm" as const },
226
+ { packages: ["microsoft.entityframeworkcore.sqlite"], name: "EF Core + SQLite", category: "orm" as const },
227
+ { packages: ["nhibernate"], name: "NHibernate", category: "orm" as const },
228
+ ];
229
+
230
+ for (const orm of ormMap) {
231
+ const found = orm.packages.find(p => allPackages.has(p));
232
+ if (found) {
233
+ technologies.push({
234
+ name: orm.name,
235
+ version: allPackages.get(found),
236
+ confidence: 0.95,
237
+ source: `Package: ${found}`,
238
+ category: orm.category,
239
+ });
240
+ }
241
+ }
242
+
243
+ // Database detection
244
+ const dbMap = [
245
+ { packages: ["npgsql"], name: "PostgreSQL", category: "database" as const },
246
+ { packages: ["mysql.data", "mysqlconnector"], name: "MySQL", category: "database" as const },
247
+ { packages: ["microsoft.data.sqlclient", "system.data.sqlclient"], name: "SQL Server", category: "database" as const },
248
+ { packages: ["microsoft.data.sqlite"], name: "SQLite", category: "database" as const },
249
+ { packages: ["mongodb.driver"], name: "MongoDB", category: "database" as const },
250
+ { packages: ["stackexchange.redis"], name: "Redis", category: "database" as const },
251
+ ];
252
+
253
+ for (const db of dbMap) {
254
+ const found = db.packages.find(p => allPackages.has(p));
255
+ if (found) {
256
+ technologies.push({
257
+ name: db.name,
258
+ version: allPackages.get(found),
259
+ confidence: 0.9,
260
+ source: `Package: ${found}`,
261
+ category: db.category,
262
+ });
263
+ }
264
+ }
265
+
266
+ // Testing framework detection
267
+ const testMap = [
268
+ { packages: ["xunit", "xunit.runner.visualstudio"], name: "xUnit", category: "testing" as const },
269
+ { packages: ["nunit", "nunit3testadapter"], name: "NUnit", category: "testing" as const },
270
+ { packages: ["mstest.testframework", "microsoft.net.test.sdk"], name: "MSTest", category: "testing" as const },
271
+ { packages: ["moq"], name: "Moq", category: "testing" as const },
272
+ { packages: ["nsubstitute"], name: "NSubstitute", category: "testing" as const },
273
+ { packages: ["fluentassertions"], name: "FluentAssertions", category: "testing" as const },
274
+ { packages: ["specflow"], name: "SpecFlow", category: "testing" as const },
275
+ ];
276
+
277
+ for (const test of testMap) {
278
+ const found = test.packages.find(p => allPackages.has(p));
279
+ if (found) {
280
+ technologies.push({
281
+ name: test.name,
282
+ version: allPackages.get(found),
283
+ confidence: 0.9,
284
+ source: `Package: ${found}`,
285
+ category: test.category,
286
+ });
287
+ }
288
+ }
289
+
290
+ // Auth detection
291
+ const authMap = [
292
+ { packages: ["microsoft.aspnetcore.authentication.jwtbearer"], name: "JWT Bearer", category: "auth" as const },
293
+ { packages: ["microsoft.aspnetcore.identity"], name: "ASP.NET Identity", category: "auth" as const },
294
+ { packages: ["identityserver4"], name: "IdentityServer4", category: "auth" as const },
295
+ { packages: ["duende.identityserver"], name: "Duende IdentityServer", category: "auth" as const },
296
+ { packages: ["microsoft.identity.web"], name: "Microsoft Identity Web", category: "auth" as const },
297
+ ];
298
+
299
+ for (const auth of authMap) {
300
+ const found = auth.packages.find(p => allPackages.has(p));
301
+ if (found) {
302
+ technologies.push({
303
+ name: auth.name,
304
+ version: allPackages.get(found),
305
+ confidence: 0.9,
306
+ source: `Package: ${found}`,
307
+ category: auth.category,
308
+ });
309
+ }
310
+ }
311
+
312
+ // Structure detection
313
+ const structurePaths = [
314
+ { key: "controllers", paths: ["Controllers", "Api/Controllers", "src/Controllers"] },
315
+ { key: "services", paths: ["Services", "src/Services", "Application/Services"] },
316
+ { key: "models", paths: ["Models", "Domain/Models", "Entities", "Domain/Entities"] },
317
+ { key: "data", paths: ["Data", "Infrastructure/Data", "Persistence"] },
318
+ { key: "api", paths: ["Api", "WebApi", "src/Api"] },
319
+ { key: "tests", paths: ["Tests", "test", "tests", "*.Tests"] },
320
+ { key: "migrations", paths: ["Migrations", "Data/Migrations"] },
321
+ ];
322
+
323
+ for (const { key, paths } of structurePaths) {
324
+ for (const p of paths) {
325
+ if (p.includes("*")) {
326
+ const matches = findFiles(cwd, p);
327
+ if (matches.length > 0) {
328
+ structure[key] = matches[0];
329
+ break;
330
+ }
331
+ } else if (dirExists(join(cwd, p))) {
332
+ structure[key] = p;
333
+ break;
334
+ }
335
+ }
336
+ }
337
+
338
+ // Check for config files
339
+ const dotnetConfigs = ["appsettings.json", "appsettings.Development.json", "launchSettings.json", "web.config"];
340
+ for (const cfg of dotnetConfigs) {
341
+ if (fileExists(join(cwd, cfg)) || findFiles(cwd, `**/${cfg}`).length > 0) {
342
+ configFiles.push(cfg);
343
+ }
344
+ }
345
+
346
+ return {
347
+ ecosystem: "dotnet",
348
+ technologies,
349
+ structure,
350
+ configFiles: [...new Set(configFiles)],
351
+ };
352
+ },
353
+ };
354
+
355
+ // Register the detector
356
+ registerDetector(dotnetDetector);
357
+
358
+ export default dotnetDetector;