@codexa/cli 8.6.0 → 8.6.9
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.
- package/commands/architect.ts +760 -760
- package/commands/check.ts +131 -131
- package/commands/clear.ts +170 -170
- package/commands/decide.ts +249 -249
- package/commands/discover.ts +1071 -1071
- package/commands/knowledge.ts +361 -361
- package/commands/patterns.ts +621 -621
- package/commands/plan.ts +376 -376
- package/commands/product.ts +626 -626
- package/commands/research.ts +754 -754
- package/commands/review.ts +463 -463
- package/commands/standards.ts +200 -200
- package/commands/task.ts +623 -623
- package/commands/utils.ts +1021 -1021
- package/db/connection.ts +32 -32
- package/db/schema.ts +719 -719
- package/detectors/README.md +109 -109
- package/detectors/dotnet.ts +357 -357
- package/detectors/flutter.ts +350 -350
- package/detectors/go.ts +324 -324
- package/detectors/index.ts +387 -387
- package/detectors/jvm.ts +433 -433
- package/detectors/loader.ts +128 -128
- package/detectors/node.ts +493 -493
- package/detectors/python.ts +423 -423
- package/detectors/rust.ts +348 -348
- package/gates/standards-validator.ts +204 -204
- package/gates/validator.ts +441 -441
- package/package.json +44 -43
- package/protocol/process-return.ts +450 -450
- package/protocol/subagent-protocol.ts +401 -401
- package/workflow.ts +783 -782
package/detectors/flutter.ts
CHANGED
|
@@ -1,351 +1,351 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Flutter/Dart Ecosystem Detector
|
|
3
|
-
*
|
|
4
|
-
* Detects Flutter and Dart projects including:
|
|
5
|
-
* - Flutter (Mobile, Web, Desktop)
|
|
6
|
-
* - Dart CLI/Server applications
|
|
7
|
-
* - State management: Riverpod, Bloc, Provider, GetX
|
|
8
|
-
* - Testing: flutter_test, mockito
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { join } from "path";
|
|
12
|
-
import {
|
|
13
|
-
registerDetector,
|
|
14
|
-
Detector,
|
|
15
|
-
DetectorResult,
|
|
16
|
-
DetectedTechnology,
|
|
17
|
-
fileExists,
|
|
18
|
-
dirExists,
|
|
19
|
-
findFiles,
|
|
20
|
-
readText,
|
|
21
|
-
parseYaml,
|
|
22
|
-
} from "./index";
|
|
23
|
-
|
|
24
|
-
interface PubspecYaml {
|
|
25
|
-
name?: string;
|
|
26
|
-
version?: string;
|
|
27
|
-
environment?: {
|
|
28
|
-
sdk?: string;
|
|
29
|
-
flutter?: string;
|
|
30
|
-
};
|
|
31
|
-
dependencies?: Record<string, any>;
|
|
32
|
-
dev_dependencies?: Record<string, any>;
|
|
33
|
-
flutter?: {
|
|
34
|
-
uses_material_design?: boolean;
|
|
35
|
-
assets?: string[];
|
|
36
|
-
fonts?: any[];
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const flutterDetector: Detector = {
|
|
41
|
-
name: "flutter",
|
|
42
|
-
ecosystem: "flutter",
|
|
43
|
-
priority: 82,
|
|
44
|
-
markers: [
|
|
45
|
-
{ type: "file", pattern: "pubspec.yaml", weight: 1.0 },
|
|
46
|
-
{ type: "file", pattern: "pubspec.lock", weight: 0.9 },
|
|
47
|
-
{ type: "file", pattern: ".flutter-plugins", weight: 0.8 },
|
|
48
|
-
{ type: "file", pattern: ".flutter-plugins-dependencies", weight: 0.8 },
|
|
49
|
-
{ type: "directory", pattern: "android", weight: 0.6 },
|
|
50
|
-
{ type: "directory", pattern: "ios", weight: 0.6 },
|
|
51
|
-
{ type: "directory", pattern: "lib", weight: 0.5 },
|
|
52
|
-
{ type: "glob", pattern: "**/*.dart", weight: 0.7 },
|
|
53
|
-
],
|
|
54
|
-
|
|
55
|
-
async detect(cwd: string): Promise<DetectorResult | null> {
|
|
56
|
-
const technologies: DetectedTechnology[] = [];
|
|
57
|
-
const structure: Record<string, string> = {};
|
|
58
|
-
const configFiles: string[] = [];
|
|
59
|
-
|
|
60
|
-
// Check for pubspec.yaml
|
|
61
|
-
const pubspecPath = join(cwd, "pubspec.yaml");
|
|
62
|
-
if (!fileExists(pubspecPath)) {
|
|
63
|
-
// Check for Dart files without pubspec
|
|
64
|
-
const dartFiles = findFiles(cwd, "**/*.dart");
|
|
65
|
-
if (dartFiles.length === 0) return null;
|
|
66
|
-
|
|
67
|
-
technologies.push({
|
|
68
|
-
name: "Dart",
|
|
69
|
-
confidence: 0.6,
|
|
70
|
-
source: "*.dart files (no pubspec)",
|
|
71
|
-
category: "runtime",
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
return { ecosystem: "flutter", technologies, structure, configFiles };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
configFiles.push("pubspec.yaml");
|
|
78
|
-
if (fileExists(join(cwd, "pubspec.lock"))) configFiles.push("pubspec.lock");
|
|
79
|
-
|
|
80
|
-
const pubspecContent = readText(pubspecPath);
|
|
81
|
-
if (!pubspecContent) return null;
|
|
82
|
-
|
|
83
|
-
const pubspec = parseYaml(pubspecContent) as PubspecYaml;
|
|
84
|
-
|
|
85
|
-
// Collect all dependencies
|
|
86
|
-
const allDeps = new Map<string, any>();
|
|
87
|
-
const addDeps = (deps: Record<string, any> | undefined) => {
|
|
88
|
-
if (!deps) return;
|
|
89
|
-
for (const [name, value] of Object.entries(deps)) {
|
|
90
|
-
allDeps.set(name, value);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
addDeps(pubspec.dependencies);
|
|
95
|
-
addDeps(pubspec.dev_dependencies);
|
|
96
|
-
|
|
97
|
-
// Detect if Flutter or pure Dart
|
|
98
|
-
const isFlutter = allDeps.has("flutter") || pubspec.flutter !== undefined;
|
|
99
|
-
|
|
100
|
-
if (isFlutter) {
|
|
101
|
-
technologies.push({
|
|
102
|
-
name: "Flutter",
|
|
103
|
-
version: pubspec.environment?.flutter,
|
|
104
|
-
confidence: 1.0,
|
|
105
|
-
source: "pubspec.yaml",
|
|
106
|
-
category: "frontend",
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Detect platforms
|
|
110
|
-
const platforms: string[] = [];
|
|
111
|
-
if (dirExists(join(cwd, "android"))) platforms.push("Android");
|
|
112
|
-
if (dirExists(join(cwd, "ios"))) platforms.push("iOS");
|
|
113
|
-
if (dirExists(join(cwd, "web"))) platforms.push("Web");
|
|
114
|
-
if (dirExists(join(cwd, "macos"))) platforms.push("macOS");
|
|
115
|
-
if (dirExists(join(cwd, "windows"))) platforms.push("Windows");
|
|
116
|
-
if (dirExists(join(cwd, "linux"))) platforms.push("Linux");
|
|
117
|
-
|
|
118
|
-
if (platforms.length > 0) {
|
|
119
|
-
technologies.push({
|
|
120
|
-
name: `Flutter (${platforms.join(", ")})`,
|
|
121
|
-
confidence: 0.95,
|
|
122
|
-
source: "Platform directories",
|
|
123
|
-
category: "frontend",
|
|
124
|
-
metadata: { platforms },
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Add Dart runtime
|
|
130
|
-
technologies.push({
|
|
131
|
-
name: "Dart",
|
|
132
|
-
version: pubspec.environment?.sdk,
|
|
133
|
-
confidence: 1.0,
|
|
134
|
-
source: "pubspec.yaml",
|
|
135
|
-
category: "runtime",
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// State Management Detection
|
|
139
|
-
const stateManagement = [
|
|
140
|
-
{ deps: ["flutter_riverpod", "riverpod", "hooks_riverpod"], name: "Riverpod", confidence: 1.0 },
|
|
141
|
-
{ deps: ["flutter_bloc", "bloc"], name: "Bloc", confidence: 1.0 },
|
|
142
|
-
{ deps: ["provider"], name: "Provider", confidence: 1.0 },
|
|
143
|
-
{ deps: ["get", "get_it"], name: "GetX", confidence: 1.0 },
|
|
144
|
-
{ deps: ["mobx", "flutter_mobx"], name: "MobX", confidence: 1.0 },
|
|
145
|
-
{ deps: ["redux", "flutter_redux"], name: "Redux", confidence: 1.0 },
|
|
146
|
-
{ deps: ["stacked"], name: "Stacked", confidence: 1.0 },
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
for (const sm of stateManagement) {
|
|
150
|
-
const found = sm.deps.find(d => allDeps.has(d));
|
|
151
|
-
if (found) {
|
|
152
|
-
technologies.push({
|
|
153
|
-
name: sm.name,
|
|
154
|
-
confidence: sm.confidence,
|
|
155
|
-
source: `Dependency: ${found}`,
|
|
156
|
-
category: "frontend",
|
|
157
|
-
metadata: { type: "state-management" },
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Backend/API Libraries
|
|
163
|
-
const apiLibs = [
|
|
164
|
-
{ deps: ["dio"], name: "Dio", confidence: 1.0 },
|
|
165
|
-
{ deps: ["http"], name: "HTTP", confidence: 0.9 },
|
|
166
|
-
{ deps: ["chopper"], name: "Chopper", confidence: 1.0 },
|
|
167
|
-
{ deps: ["retrofit"], name: "Retrofit", confidence: 1.0 },
|
|
168
|
-
{ deps: ["graphql_flutter", "graphql"], name: "GraphQL", confidence: 1.0 },
|
|
169
|
-
];
|
|
170
|
-
|
|
171
|
-
for (const lib of apiLibs) {
|
|
172
|
-
const found = lib.deps.find(d => allDeps.has(d));
|
|
173
|
-
if (found) {
|
|
174
|
-
technologies.push({
|
|
175
|
-
name: lib.name,
|
|
176
|
-
confidence: lib.confidence,
|
|
177
|
-
source: `Dependency: ${found}`,
|
|
178
|
-
category: "backend",
|
|
179
|
-
metadata: { type: "http-client" },
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Database/Storage Detection
|
|
185
|
-
const databases = [
|
|
186
|
-
{ deps: ["sqflite"], name: "SQFlite (SQLite)", confidence: 1.0 },
|
|
187
|
-
{ deps: ["hive", "hive_flutter"], name: "Hive", confidence: 1.0 },
|
|
188
|
-
{ deps: ["isar", "isar_flutter_libs"], name: "Isar", confidence: 1.0 },
|
|
189
|
-
{ deps: ["moor", "drift"], name: "Drift (Moor)", confidence: 1.0 },
|
|
190
|
-
{ deps: ["objectbox"], name: "ObjectBox", confidence: 1.0 },
|
|
191
|
-
{ deps: ["shared_preferences"], name: "SharedPreferences", confidence: 0.9 },
|
|
192
|
-
{ deps: ["firebase_core", "cloud_firestore"], name: "Firebase/Firestore", confidence: 1.0 },
|
|
193
|
-
{ deps: ["supabase_flutter", "supabase"], name: "Supabase", confidence: 1.0 },
|
|
194
|
-
{ deps: ["appwrite"], name: "Appwrite", confidence: 1.0 },
|
|
195
|
-
];
|
|
196
|
-
|
|
197
|
-
const detectedDbs = new Set<string>();
|
|
198
|
-
for (const db of databases) {
|
|
199
|
-
const found = db.deps.find(d => allDeps.has(d));
|
|
200
|
-
if (found && !detectedDbs.has(db.name)) {
|
|
201
|
-
technologies.push({
|
|
202
|
-
name: db.name,
|
|
203
|
-
confidence: db.confidence,
|
|
204
|
-
source: `Dependency: ${found}`,
|
|
205
|
-
category: "database",
|
|
206
|
-
});
|
|
207
|
-
detectedDbs.add(db.name);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Auth Detection
|
|
212
|
-
const authProviders = [
|
|
213
|
-
{ deps: ["firebase_auth"], name: "Firebase Auth", confidence: 1.0 },
|
|
214
|
-
{ deps: ["supabase_auth_ui"], name: "Supabase Auth", confidence: 1.0 },
|
|
215
|
-
{ deps: ["google_sign_in"], name: "Google Sign-In", confidence: 0.9 },
|
|
216
|
-
{ deps: ["sign_in_with_apple"], name: "Sign in with Apple", confidence: 0.9 },
|
|
217
|
-
{ deps: ["flutter_facebook_auth"], name: "Facebook Auth", confidence: 0.9 },
|
|
218
|
-
{ deps: ["flutter_appauth"], name: "AppAuth", confidence: 1.0 },
|
|
219
|
-
];
|
|
220
|
-
|
|
221
|
-
for (const auth of authProviders) {
|
|
222
|
-
const found = auth.deps.find(d => allDeps.has(d));
|
|
223
|
-
if (found) {
|
|
224
|
-
technologies.push({
|
|
225
|
-
name: auth.name,
|
|
226
|
-
confidence: auth.confidence,
|
|
227
|
-
source: `Dependency: ${found}`,
|
|
228
|
-
category: "auth",
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Testing Detection
|
|
234
|
-
const testingTools = [
|
|
235
|
-
{ deps: ["flutter_test"], name: "Flutter Test", confidence: 1.0 },
|
|
236
|
-
{ deps: ["test"], name: "Dart Test", confidence: 0.95 },
|
|
237
|
-
{ deps: ["mockito"], name: "Mockito", confidence: 0.95 },
|
|
238
|
-
{ deps: ["mocktail"], name: "Mocktail", confidence: 0.95 },
|
|
239
|
-
{ deps: ["bloc_test"], name: "Bloc Test", confidence: 0.95 },
|
|
240
|
-
{ deps: ["golden_toolkit"], name: "Golden Toolkit", confidence: 0.9 },
|
|
241
|
-
{ deps: ["integration_test"], name: "Integration Test", confidence: 0.95 },
|
|
242
|
-
];
|
|
243
|
-
|
|
244
|
-
for (const test of testingTools) {
|
|
245
|
-
const found = test.deps.find(d => allDeps.has(d));
|
|
246
|
-
if (found) {
|
|
247
|
-
technologies.push({
|
|
248
|
-
name: test.name,
|
|
249
|
-
confidence: test.confidence,
|
|
250
|
-
source: `Dependency: ${found}`,
|
|
251
|
-
category: "testing",
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Navigation/Routing
|
|
257
|
-
const navigation = [
|
|
258
|
-
{ deps: ["go_router"], name: "GoRouter", confidence: 1.0 },
|
|
259
|
-
{ deps: ["auto_route"], name: "AutoRoute", confidence: 1.0 },
|
|
260
|
-
{ deps: ["beamer"], name: "Beamer", confidence: 1.0 },
|
|
261
|
-
{ deps: ["routemaster"], name: "Routemaster", confidence: 1.0 },
|
|
262
|
-
];
|
|
263
|
-
|
|
264
|
-
for (const nav of navigation) {
|
|
265
|
-
const found = nav.deps.find(d => allDeps.has(d));
|
|
266
|
-
if (found) {
|
|
267
|
-
technologies.push({
|
|
268
|
-
name: nav.name,
|
|
269
|
-
confidence: nav.confidence,
|
|
270
|
-
source: `Dependency: ${found}`,
|
|
271
|
-
category: "frontend",
|
|
272
|
-
metadata: { type: "routing" },
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Code Generation
|
|
278
|
-
const codeGen = [
|
|
279
|
-
{ deps: ["freezed", "freezed_annotation"], name: "Freezed", confidence: 1.0 },
|
|
280
|
-
{ deps: ["json_serializable"], name: "JSON Serializable", confidence: 0.95 },
|
|
281
|
-
{ deps: ["build_runner"], name: "Build Runner", confidence: 0.9 },
|
|
282
|
-
{ deps: ["injectable"], name: "Injectable", confidence: 1.0 },
|
|
283
|
-
];
|
|
284
|
-
|
|
285
|
-
for (const gen of codeGen) {
|
|
286
|
-
const found = gen.deps.find(d => allDeps.has(d));
|
|
287
|
-
if (found) {
|
|
288
|
-
technologies.push({
|
|
289
|
-
name: gen.name,
|
|
290
|
-
confidence: gen.confidence,
|
|
291
|
-
source: `Dependency: ${found}`,
|
|
292
|
-
category: "build",
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Structure detection
|
|
298
|
-
const structurePaths = [
|
|
299
|
-
{ key: "lib", paths: ["lib"] },
|
|
300
|
-
{ key: "screens", paths: ["lib/screens", "lib/pages", "lib/views", "lib/presentation/screens"] },
|
|
301
|
-
{ key: "widgets", paths: ["lib/widgets", "lib/components", "lib/presentation/widgets"] },
|
|
302
|
-
{ key: "models", paths: ["lib/models", "lib/domain/models", "lib/data/models"] },
|
|
303
|
-
{ key: "services", paths: ["lib/services", "lib/data/services"] },
|
|
304
|
-
{ key: "providers", paths: ["lib/providers", "lib/riverpod", "lib/blocs"] },
|
|
305
|
-
{ key: "repositories", paths: ["lib/repositories", "lib/data/repositories"] },
|
|
306
|
-
{ key: "utils", paths: ["lib/utils", "lib/helpers", "lib/core/utils"] },
|
|
307
|
-
{ key: "config", paths: ["lib/config", "lib/core/config"] },
|
|
308
|
-
{ key: "test", paths: ["test"] },
|
|
309
|
-
{ key: "integration_test", paths: ["integration_test"] },
|
|
310
|
-
];
|
|
311
|
-
|
|
312
|
-
for (const { key, paths } of structurePaths) {
|
|
313
|
-
for (const p of paths) {
|
|
314
|
-
if (dirExists(join(cwd, p))) {
|
|
315
|
-
structure[key] = p;
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Config files
|
|
322
|
-
const configPatterns = [
|
|
323
|
-
"analysis_options.yaml",
|
|
324
|
-
".dart_tool/package_config.json",
|
|
325
|
-
"build.yaml",
|
|
326
|
-
"melos.yaml",
|
|
327
|
-
"firebase.json",
|
|
328
|
-
".firebaserc",
|
|
329
|
-
"Podfile",
|
|
330
|
-
"android/build.gradle",
|
|
331
|
-
];
|
|
332
|
-
|
|
333
|
-
for (const pattern of configPatterns) {
|
|
334
|
-
if (fileExists(join(cwd, pattern))) {
|
|
335
|
-
configFiles.push(pattern);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return {
|
|
340
|
-
ecosystem: "flutter",
|
|
341
|
-
technologies,
|
|
342
|
-
structure,
|
|
343
|
-
configFiles: [...new Set(configFiles)],
|
|
344
|
-
};
|
|
345
|
-
},
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
// Register the detector
|
|
349
|
-
registerDetector(flutterDetector);
|
|
350
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Flutter/Dart Ecosystem Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects Flutter and Dart projects including:
|
|
5
|
+
* - Flutter (Mobile, Web, Desktop)
|
|
6
|
+
* - Dart CLI/Server applications
|
|
7
|
+
* - State management: Riverpod, Bloc, Provider, GetX
|
|
8
|
+
* - Testing: flutter_test, mockito
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import {
|
|
13
|
+
registerDetector,
|
|
14
|
+
Detector,
|
|
15
|
+
DetectorResult,
|
|
16
|
+
DetectedTechnology,
|
|
17
|
+
fileExists,
|
|
18
|
+
dirExists,
|
|
19
|
+
findFiles,
|
|
20
|
+
readText,
|
|
21
|
+
parseYaml,
|
|
22
|
+
} from "./index";
|
|
23
|
+
|
|
24
|
+
interface PubspecYaml {
|
|
25
|
+
name?: string;
|
|
26
|
+
version?: string;
|
|
27
|
+
environment?: {
|
|
28
|
+
sdk?: string;
|
|
29
|
+
flutter?: string;
|
|
30
|
+
};
|
|
31
|
+
dependencies?: Record<string, any>;
|
|
32
|
+
dev_dependencies?: Record<string, any>;
|
|
33
|
+
flutter?: {
|
|
34
|
+
uses_material_design?: boolean;
|
|
35
|
+
assets?: string[];
|
|
36
|
+
fonts?: any[];
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const flutterDetector: Detector = {
|
|
41
|
+
name: "flutter",
|
|
42
|
+
ecosystem: "flutter",
|
|
43
|
+
priority: 82,
|
|
44
|
+
markers: [
|
|
45
|
+
{ type: "file", pattern: "pubspec.yaml", weight: 1.0 },
|
|
46
|
+
{ type: "file", pattern: "pubspec.lock", weight: 0.9 },
|
|
47
|
+
{ type: "file", pattern: ".flutter-plugins", weight: 0.8 },
|
|
48
|
+
{ type: "file", pattern: ".flutter-plugins-dependencies", weight: 0.8 },
|
|
49
|
+
{ type: "directory", pattern: "android", weight: 0.6 },
|
|
50
|
+
{ type: "directory", pattern: "ios", weight: 0.6 },
|
|
51
|
+
{ type: "directory", pattern: "lib", weight: 0.5 },
|
|
52
|
+
{ type: "glob", pattern: "**/*.dart", weight: 0.7 },
|
|
53
|
+
],
|
|
54
|
+
|
|
55
|
+
async detect(cwd: string): Promise<DetectorResult | null> {
|
|
56
|
+
const technologies: DetectedTechnology[] = [];
|
|
57
|
+
const structure: Record<string, string> = {};
|
|
58
|
+
const configFiles: string[] = [];
|
|
59
|
+
|
|
60
|
+
// Check for pubspec.yaml
|
|
61
|
+
const pubspecPath = join(cwd, "pubspec.yaml");
|
|
62
|
+
if (!fileExists(pubspecPath)) {
|
|
63
|
+
// Check for Dart files without pubspec
|
|
64
|
+
const dartFiles = findFiles(cwd, "**/*.dart");
|
|
65
|
+
if (dartFiles.length === 0) return null;
|
|
66
|
+
|
|
67
|
+
technologies.push({
|
|
68
|
+
name: "Dart",
|
|
69
|
+
confidence: 0.6,
|
|
70
|
+
source: "*.dart files (no pubspec)",
|
|
71
|
+
category: "runtime",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return { ecosystem: "flutter", technologies, structure, configFiles };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
configFiles.push("pubspec.yaml");
|
|
78
|
+
if (fileExists(join(cwd, "pubspec.lock"))) configFiles.push("pubspec.lock");
|
|
79
|
+
|
|
80
|
+
const pubspecContent = readText(pubspecPath);
|
|
81
|
+
if (!pubspecContent) return null;
|
|
82
|
+
|
|
83
|
+
const pubspec = parseYaml(pubspecContent) as PubspecYaml;
|
|
84
|
+
|
|
85
|
+
// Collect all dependencies
|
|
86
|
+
const allDeps = new Map<string, any>();
|
|
87
|
+
const addDeps = (deps: Record<string, any> | undefined) => {
|
|
88
|
+
if (!deps) return;
|
|
89
|
+
for (const [name, value] of Object.entries(deps)) {
|
|
90
|
+
allDeps.set(name, value);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
addDeps(pubspec.dependencies);
|
|
95
|
+
addDeps(pubspec.dev_dependencies);
|
|
96
|
+
|
|
97
|
+
// Detect if Flutter or pure Dart
|
|
98
|
+
const isFlutter = allDeps.has("flutter") || pubspec.flutter !== undefined;
|
|
99
|
+
|
|
100
|
+
if (isFlutter) {
|
|
101
|
+
technologies.push({
|
|
102
|
+
name: "Flutter",
|
|
103
|
+
version: pubspec.environment?.flutter,
|
|
104
|
+
confidence: 1.0,
|
|
105
|
+
source: "pubspec.yaml",
|
|
106
|
+
category: "frontend",
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Detect platforms
|
|
110
|
+
const platforms: string[] = [];
|
|
111
|
+
if (dirExists(join(cwd, "android"))) platforms.push("Android");
|
|
112
|
+
if (dirExists(join(cwd, "ios"))) platforms.push("iOS");
|
|
113
|
+
if (dirExists(join(cwd, "web"))) platforms.push("Web");
|
|
114
|
+
if (dirExists(join(cwd, "macos"))) platforms.push("macOS");
|
|
115
|
+
if (dirExists(join(cwd, "windows"))) platforms.push("Windows");
|
|
116
|
+
if (dirExists(join(cwd, "linux"))) platforms.push("Linux");
|
|
117
|
+
|
|
118
|
+
if (platforms.length > 0) {
|
|
119
|
+
technologies.push({
|
|
120
|
+
name: `Flutter (${platforms.join(", ")})`,
|
|
121
|
+
confidence: 0.95,
|
|
122
|
+
source: "Platform directories",
|
|
123
|
+
category: "frontend",
|
|
124
|
+
metadata: { platforms },
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Add Dart runtime
|
|
130
|
+
technologies.push({
|
|
131
|
+
name: "Dart",
|
|
132
|
+
version: pubspec.environment?.sdk,
|
|
133
|
+
confidence: 1.0,
|
|
134
|
+
source: "pubspec.yaml",
|
|
135
|
+
category: "runtime",
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// State Management Detection
|
|
139
|
+
const stateManagement = [
|
|
140
|
+
{ deps: ["flutter_riverpod", "riverpod", "hooks_riverpod"], name: "Riverpod", confidence: 1.0 },
|
|
141
|
+
{ deps: ["flutter_bloc", "bloc"], name: "Bloc", confidence: 1.0 },
|
|
142
|
+
{ deps: ["provider"], name: "Provider", confidence: 1.0 },
|
|
143
|
+
{ deps: ["get", "get_it"], name: "GetX", confidence: 1.0 },
|
|
144
|
+
{ deps: ["mobx", "flutter_mobx"], name: "MobX", confidence: 1.0 },
|
|
145
|
+
{ deps: ["redux", "flutter_redux"], name: "Redux", confidence: 1.0 },
|
|
146
|
+
{ deps: ["stacked"], name: "Stacked", confidence: 1.0 },
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
for (const sm of stateManagement) {
|
|
150
|
+
const found = sm.deps.find(d => allDeps.has(d));
|
|
151
|
+
if (found) {
|
|
152
|
+
technologies.push({
|
|
153
|
+
name: sm.name,
|
|
154
|
+
confidence: sm.confidence,
|
|
155
|
+
source: `Dependency: ${found}`,
|
|
156
|
+
category: "frontend",
|
|
157
|
+
metadata: { type: "state-management" },
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Backend/API Libraries
|
|
163
|
+
const apiLibs = [
|
|
164
|
+
{ deps: ["dio"], name: "Dio", confidence: 1.0 },
|
|
165
|
+
{ deps: ["http"], name: "HTTP", confidence: 0.9 },
|
|
166
|
+
{ deps: ["chopper"], name: "Chopper", confidence: 1.0 },
|
|
167
|
+
{ deps: ["retrofit"], name: "Retrofit", confidence: 1.0 },
|
|
168
|
+
{ deps: ["graphql_flutter", "graphql"], name: "GraphQL", confidence: 1.0 },
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
for (const lib of apiLibs) {
|
|
172
|
+
const found = lib.deps.find(d => allDeps.has(d));
|
|
173
|
+
if (found) {
|
|
174
|
+
technologies.push({
|
|
175
|
+
name: lib.name,
|
|
176
|
+
confidence: lib.confidence,
|
|
177
|
+
source: `Dependency: ${found}`,
|
|
178
|
+
category: "backend",
|
|
179
|
+
metadata: { type: "http-client" },
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Database/Storage Detection
|
|
185
|
+
const databases = [
|
|
186
|
+
{ deps: ["sqflite"], name: "SQFlite (SQLite)", confidence: 1.0 },
|
|
187
|
+
{ deps: ["hive", "hive_flutter"], name: "Hive", confidence: 1.0 },
|
|
188
|
+
{ deps: ["isar", "isar_flutter_libs"], name: "Isar", confidence: 1.0 },
|
|
189
|
+
{ deps: ["moor", "drift"], name: "Drift (Moor)", confidence: 1.0 },
|
|
190
|
+
{ deps: ["objectbox"], name: "ObjectBox", confidence: 1.0 },
|
|
191
|
+
{ deps: ["shared_preferences"], name: "SharedPreferences", confidence: 0.9 },
|
|
192
|
+
{ deps: ["firebase_core", "cloud_firestore"], name: "Firebase/Firestore", confidence: 1.0 },
|
|
193
|
+
{ deps: ["supabase_flutter", "supabase"], name: "Supabase", confidence: 1.0 },
|
|
194
|
+
{ deps: ["appwrite"], name: "Appwrite", confidence: 1.0 },
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const detectedDbs = new Set<string>();
|
|
198
|
+
for (const db of databases) {
|
|
199
|
+
const found = db.deps.find(d => allDeps.has(d));
|
|
200
|
+
if (found && !detectedDbs.has(db.name)) {
|
|
201
|
+
technologies.push({
|
|
202
|
+
name: db.name,
|
|
203
|
+
confidence: db.confidence,
|
|
204
|
+
source: `Dependency: ${found}`,
|
|
205
|
+
category: "database",
|
|
206
|
+
});
|
|
207
|
+
detectedDbs.add(db.name);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Auth Detection
|
|
212
|
+
const authProviders = [
|
|
213
|
+
{ deps: ["firebase_auth"], name: "Firebase Auth", confidence: 1.0 },
|
|
214
|
+
{ deps: ["supabase_auth_ui"], name: "Supabase Auth", confidence: 1.0 },
|
|
215
|
+
{ deps: ["google_sign_in"], name: "Google Sign-In", confidence: 0.9 },
|
|
216
|
+
{ deps: ["sign_in_with_apple"], name: "Sign in with Apple", confidence: 0.9 },
|
|
217
|
+
{ deps: ["flutter_facebook_auth"], name: "Facebook Auth", confidence: 0.9 },
|
|
218
|
+
{ deps: ["flutter_appauth"], name: "AppAuth", confidence: 1.0 },
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
for (const auth of authProviders) {
|
|
222
|
+
const found = auth.deps.find(d => allDeps.has(d));
|
|
223
|
+
if (found) {
|
|
224
|
+
technologies.push({
|
|
225
|
+
name: auth.name,
|
|
226
|
+
confidence: auth.confidence,
|
|
227
|
+
source: `Dependency: ${found}`,
|
|
228
|
+
category: "auth",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Testing Detection
|
|
234
|
+
const testingTools = [
|
|
235
|
+
{ deps: ["flutter_test"], name: "Flutter Test", confidence: 1.0 },
|
|
236
|
+
{ deps: ["test"], name: "Dart Test", confidence: 0.95 },
|
|
237
|
+
{ deps: ["mockito"], name: "Mockito", confidence: 0.95 },
|
|
238
|
+
{ deps: ["mocktail"], name: "Mocktail", confidence: 0.95 },
|
|
239
|
+
{ deps: ["bloc_test"], name: "Bloc Test", confidence: 0.95 },
|
|
240
|
+
{ deps: ["golden_toolkit"], name: "Golden Toolkit", confidence: 0.9 },
|
|
241
|
+
{ deps: ["integration_test"], name: "Integration Test", confidence: 0.95 },
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
for (const test of testingTools) {
|
|
245
|
+
const found = test.deps.find(d => allDeps.has(d));
|
|
246
|
+
if (found) {
|
|
247
|
+
technologies.push({
|
|
248
|
+
name: test.name,
|
|
249
|
+
confidence: test.confidence,
|
|
250
|
+
source: `Dependency: ${found}`,
|
|
251
|
+
category: "testing",
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Navigation/Routing
|
|
257
|
+
const navigation = [
|
|
258
|
+
{ deps: ["go_router"], name: "GoRouter", confidence: 1.0 },
|
|
259
|
+
{ deps: ["auto_route"], name: "AutoRoute", confidence: 1.0 },
|
|
260
|
+
{ deps: ["beamer"], name: "Beamer", confidence: 1.0 },
|
|
261
|
+
{ deps: ["routemaster"], name: "Routemaster", confidence: 1.0 },
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
for (const nav of navigation) {
|
|
265
|
+
const found = nav.deps.find(d => allDeps.has(d));
|
|
266
|
+
if (found) {
|
|
267
|
+
technologies.push({
|
|
268
|
+
name: nav.name,
|
|
269
|
+
confidence: nav.confidence,
|
|
270
|
+
source: `Dependency: ${found}`,
|
|
271
|
+
category: "frontend",
|
|
272
|
+
metadata: { type: "routing" },
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Code Generation
|
|
278
|
+
const codeGen = [
|
|
279
|
+
{ deps: ["freezed", "freezed_annotation"], name: "Freezed", confidence: 1.0 },
|
|
280
|
+
{ deps: ["json_serializable"], name: "JSON Serializable", confidence: 0.95 },
|
|
281
|
+
{ deps: ["build_runner"], name: "Build Runner", confidence: 0.9 },
|
|
282
|
+
{ deps: ["injectable"], name: "Injectable", confidence: 1.0 },
|
|
283
|
+
];
|
|
284
|
+
|
|
285
|
+
for (const gen of codeGen) {
|
|
286
|
+
const found = gen.deps.find(d => allDeps.has(d));
|
|
287
|
+
if (found) {
|
|
288
|
+
technologies.push({
|
|
289
|
+
name: gen.name,
|
|
290
|
+
confidence: gen.confidence,
|
|
291
|
+
source: `Dependency: ${found}`,
|
|
292
|
+
category: "build",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Structure detection
|
|
298
|
+
const structurePaths = [
|
|
299
|
+
{ key: "lib", paths: ["lib"] },
|
|
300
|
+
{ key: "screens", paths: ["lib/screens", "lib/pages", "lib/views", "lib/presentation/screens"] },
|
|
301
|
+
{ key: "widgets", paths: ["lib/widgets", "lib/components", "lib/presentation/widgets"] },
|
|
302
|
+
{ key: "models", paths: ["lib/models", "lib/domain/models", "lib/data/models"] },
|
|
303
|
+
{ key: "services", paths: ["lib/services", "lib/data/services"] },
|
|
304
|
+
{ key: "providers", paths: ["lib/providers", "lib/riverpod", "lib/blocs"] },
|
|
305
|
+
{ key: "repositories", paths: ["lib/repositories", "lib/data/repositories"] },
|
|
306
|
+
{ key: "utils", paths: ["lib/utils", "lib/helpers", "lib/core/utils"] },
|
|
307
|
+
{ key: "config", paths: ["lib/config", "lib/core/config"] },
|
|
308
|
+
{ key: "test", paths: ["test"] },
|
|
309
|
+
{ key: "integration_test", paths: ["integration_test"] },
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
for (const { key, paths } of structurePaths) {
|
|
313
|
+
for (const p of paths) {
|
|
314
|
+
if (dirExists(join(cwd, p))) {
|
|
315
|
+
structure[key] = p;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Config files
|
|
322
|
+
const configPatterns = [
|
|
323
|
+
"analysis_options.yaml",
|
|
324
|
+
".dart_tool/package_config.json",
|
|
325
|
+
"build.yaml",
|
|
326
|
+
"melos.yaml",
|
|
327
|
+
"firebase.json",
|
|
328
|
+
".firebaserc",
|
|
329
|
+
"Podfile",
|
|
330
|
+
"android/build.gradle",
|
|
331
|
+
];
|
|
332
|
+
|
|
333
|
+
for (const pattern of configPatterns) {
|
|
334
|
+
if (fileExists(join(cwd, pattern))) {
|
|
335
|
+
configFiles.push(pattern);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
ecosystem: "flutter",
|
|
341
|
+
technologies,
|
|
342
|
+
structure,
|
|
343
|
+
configFiles: [...new Set(configFiles)],
|
|
344
|
+
};
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Register the detector
|
|
349
|
+
registerDetector(flutterDetector);
|
|
350
|
+
|
|
351
351
|
export default flutterDetector;
|