@njeybe/nativ 2.4.1 → 2.5.1
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/CHANGELOG.md +26 -0
- package/README.md +26 -0
- package/dist/commands/db/db-formatter.d.ts +5 -0
- package/dist/commands/db/db-formatter.js +116 -0
- package/dist/commands/db/db-introspect.d.ts +11 -0
- package/dist/commands/db/db-introspect.js +84 -0
- package/dist/commands/db.js +4 -206
- package/dist/commands/profile.d.ts +3 -0
- package/dist/commands/profile.js +39 -0
- package/dist/commands/task/task-add.d.ts +11 -0
- package/dist/commands/task/task-add.js +169 -0
- package/dist/commands/task/task-common.d.ts +57 -0
- package/dist/commands/task/task-common.js +45 -0
- package/dist/commands/task/task-escalate.d.ts +7 -0
- package/dist/commands/task/task-escalate.js +119 -0
- package/dist/commands/task/task-lifecycle.d.ts +5 -0
- package/dist/commands/task/task-lifecycle.js +252 -0
- package/dist/commands/task/task-query.d.ts +10 -0
- package/dist/commands/task/task-query.js +269 -0
- package/dist/commands/task.d.ts +9 -87
- package/dist/commands/task.js +8 -850
- package/dist/commands/triage/triage-store.d.ts +56 -0
- package/dist/commands/triage/triage-store.js +108 -0
- package/dist/commands/triage/triage-ui.d.ts +14 -0
- package/dist/commands/triage/triage-ui.js +104 -0
- package/dist/commands/triage.d.ts +3 -52
- package/dist/commands/triage.js +8 -216
- package/dist/core/enforcement.d.ts +1 -0
- package/dist/core/enforcement.js +19 -0
- package/dist/core/human-gate.d.ts +2 -1
- package/dist/core/human-gate.js +9 -2
- package/dist/core/profiles.d.ts +14 -0
- package/dist/core/profiles.js +82 -0
- package/dist/core/setup/config-merger.d.ts +59 -0
- package/dist/core/setup/config-merger.js +107 -0
- package/dist/core/setup/session-context.d.ts +4 -0
- package/dist/core/setup/session-context.js +65 -0
- package/dist/core/setup/setup-planner.d.ts +14 -0
- package/dist/core/setup/setup-planner.js +180 -0
- package/dist/core/setup/template-planner.d.ts +8 -0
- package/dist/core/setup/template-planner.js +32 -0
- package/dist/core/setup/types.d.ts +65 -0
- package/dist/core/setup/types.js +55 -0
- package/dist/core/setup-assets.d.ts +8 -119
- package/dist/core/setup-assets.js +7 -437
- package/dist/core/tier1-liaison.d.ts +2 -0
- package/dist/core/tier1-liaison.js +64 -3
- package/dist/core/verifier/executor.d.ts +5 -0
- package/dist/core/verifier/executor.js +62 -0
- package/dist/core/verifier/phases.d.ts +5 -0
- package/dist/core/verifier/phases.js +71 -0
- package/dist/core/verifier/task-verifier.d.ts +37 -0
- package/dist/core/verifier/task-verifier.js +233 -0
- package/dist/core/verifier/types.d.ts +51 -0
- package/dist/core/verifier/types.js +1 -0
- package/dist/core/verifier.d.ts +5 -93
- package/dist/core/verifier.js +5 -359
- package/dist/db/introspector.js +59 -59
- package/dist/governor/evaluator.d.ts +4 -64
- package/dist/governor/evaluator.js +21 -686
- package/dist/governor/patch-applier.d.ts +8 -0
- package/dist/governor/patch-applier.js +121 -0
- package/dist/governor/self-healing.d.ts +24 -0
- package/dist/governor/self-healing.js +351 -0
- package/dist/governor/test-integrity.d.ts +41 -0
- package/dist/governor/test-integrity.js +237 -0
- package/dist/index.js +8 -0
- package/dist/mcp/capture.d.ts +4 -0
- package/dist/mcp/capture.js +33 -0
- package/dist/mcp/resources.d.ts +39 -0
- package/dist/mcp/resources.js +44 -0
- package/dist/mcp/server.d.ts +11 -0
- package/dist/mcp/server.js +14 -89
- package/dist/mcp/tools.d.ts +1 -0
- package/dist/mcp/tools.js +1 -0
- package/dist/runner/agent-supervisor.d.ts +8 -302
- package/dist/runner/agent-supervisor.js +41 -1030
- package/dist/runner/cli-config.d.ts +84 -0
- package/dist/runner/cli-config.js +215 -0
- package/dist/runner/native-driver.d.ts +18 -0
- package/dist/runner/native-driver.js +208 -0
- package/dist/runner/native-engine.d.ts +27 -0
- package/dist/runner/native-engine.js +184 -0
- package/dist/runner/native-tools.d.ts +31 -0
- package/dist/runner/native-tools.js +241 -0
- package/dist/runner/process-utils.d.ts +12 -0
- package/dist/runner/process-utils.js +96 -0
- package/dist/runner/run-lifecycle.d.ts +6 -0
- package/dist/runner/run-lifecycle.js +42 -0
- package/dist/runner/types.d.ts +215 -0
- package/dist/runner/types.js +41 -0
- package/dist/scanner/detector.js +42 -663
- package/dist/scanner/detectors/ecosystem-detector.d.ts +16 -0
- package/dist/scanner/detectors/ecosystem-detector.js +354 -0
- package/dist/scanner/detectors/orm-detector.d.ts +5 -0
- package/dist/scanner/detectors/orm-detector.js +59 -0
- package/dist/scanner/detectors/workspace-detector.d.ts +19 -0
- package/dist/scanner/detectors/workspace-detector.js +266 -0
- package/dist/server/cli-runner.d.ts +15 -0
- package/dist/server/cli-runner.js +43 -0
- package/dist/server/event-hub.d.ts +42 -0
- package/dist/server/event-hub.js +169 -0
- package/dist/server/http-utils.d.ts +15 -0
- package/dist/server/http-utils.js +56 -0
- package/dist/server/plan-utils.d.ts +9 -0
- package/dist/server/plan-utils.js +31 -0
- package/dist/server/routes/db-routes.d.ts +57 -0
- package/dist/server/routes/db-routes.js +484 -0
- package/dist/server/routes/task-routes.d.ts +266 -0
- package/dist/server/routes/task-routes.js +285 -0
- package/dist/server/routes/telemetry-routes.d.ts +128 -0
- package/dist/server/routes/telemetry-routes.js +179 -0
- package/dist/server/routes/triage-routes.d.ts +81 -0
- package/dist/server/routes/triage-routes.js +244 -0
- package/dist/server/routes/worktree-routes.d.ts +31 -0
- package/dist/server/routes/worktree-routes.js +144 -0
- package/dist/server/studio-server.d.ts +4 -11
- package/dist/server/studio-server.js +34 -1578
- package/dist/server/studio-ui/shell/pipeline-state.js +2 -0
- package/dist/server/studio-ui/shell/triage.js +10 -0
- package/package.json +2 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/agents/architect.md +4 -2
- package/plugin/agents/verifier.md +1 -1
- package/plugin/agents/worker.md +7 -4
- package/plugin/skills/nativ/SKILL.md +1 -0
- package/templates/.claudeignore +62 -45
- package/templates/AGENTS.md +1 -0
- package/templates/CLAUDE.md +13 -0
- package/templates/claude-agents/architect.md +4 -2
- package/templates/claude-agents/verifier.md +1 -1
- package/templates/claude-agents/worker.md +7 -4
- package/templates/dot-ai/api_contracts.json +66 -66
- package/templates/dot-ai/context.md +38 -38
- package/templates/dot-ai/db_schema.json +39 -39
- package/templates/dot-ai/subagents/architect.md +54 -0
- package/dist/commands/session.d.ts +0 -22
- package/dist/commands/session.js +0 -106
- package/dist/core/session-memory.d.ts +0 -42
- package/dist/core/session-memory.js +0 -121
package/dist/scanner/detector.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { detectWorkspacesAndServices } from './detectors/workspace-detector.js';
|
|
4
|
+
import { detectEcosystems } from './detectors/ecosystem-detector.js';
|
|
5
|
+
import { detectOrm } from './detectors/orm-detector.js';
|
|
3
6
|
export function detectProject(targetDir) {
|
|
4
7
|
const resolvedTarget = path.resolve(targetDir);
|
|
5
8
|
const baseName = path.basename(resolvedTarget);
|
|
@@ -16,11 +19,6 @@ export function detectProject(targetDir) {
|
|
|
16
19
|
let architecturePattern = 'Modular / Layered';
|
|
17
20
|
let verificationCommand = 'npm test';
|
|
18
21
|
let repositoryType = 'Standard Monolith';
|
|
19
|
-
let detectedOrmConfig = '';
|
|
20
|
-
const monorepoWorkspaces = [];
|
|
21
|
-
const ecosystemManifests = [];
|
|
22
|
-
const keyDirectories = [];
|
|
23
|
-
// Helper safely reading file content
|
|
24
22
|
const readFileSafe = (filePath) => {
|
|
25
23
|
try {
|
|
26
24
|
if (fs.existsSync(filePath)) {
|
|
@@ -32,650 +30,46 @@ export function detectProject(targetDir) {
|
|
|
32
30
|
}
|
|
33
31
|
return '';
|
|
34
32
|
};
|
|
35
|
-
// Helper checking existence
|
|
36
33
|
const fileExists = (relPath) => {
|
|
37
34
|
return fs.existsSync(path.join(resolvedTarget, relPath));
|
|
38
35
|
};
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!monorepoWorkspaces.includes(`${wDir}/*`)) {
|
|
76
|
-
monorepoWorkspaces.push(`${wDir}/*`);
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
const children = fs.readdirSync(fullWDir);
|
|
80
|
-
for (const child of children) {
|
|
81
|
-
const childPath = path.join(fullWDir, child);
|
|
82
|
-
if (fs.statSync(childPath).isDirectory()) {
|
|
83
|
-
if (fs.existsSync(path.join(childPath, 'package.json')) ||
|
|
84
|
-
fs.existsSync(path.join(childPath, 'go.mod')) ||
|
|
85
|
-
fs.existsSync(path.join(childPath, 'pom.xml')) ||
|
|
86
|
-
fs.existsSync(path.join(childPath, 'composer.json'))) {
|
|
87
|
-
detectedSubpackagesCount++;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
// ignore read error
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const independentServices = [];
|
|
98
|
-
try {
|
|
99
|
-
const rootItems = fs.readdirSync(resolvedTarget);
|
|
100
|
-
for (const item of rootItems) {
|
|
101
|
-
if (item.startsWith('.') ||
|
|
102
|
-
item === 'node_modules' ||
|
|
103
|
-
item === 'vendor' ||
|
|
104
|
-
item === 'dist' ||
|
|
105
|
-
item === 'build' ||
|
|
106
|
-
item === 'templates' ||
|
|
107
|
-
item === 'bin' ||
|
|
108
|
-
item === 'public') {
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
const itemPath = path.join(resolvedTarget, item);
|
|
112
|
-
if (fs.existsSync(itemPath) && fs.statSync(itemPath).isDirectory()) {
|
|
113
|
-
const subPathItem = (filename) => fs.existsSync(path.join(itemPath, filename));
|
|
114
|
-
const subManifests = [];
|
|
115
|
-
if (subPathItem('package.json'))
|
|
116
|
-
subManifests.push('package.json');
|
|
117
|
-
if (subPathItem('composer.json'))
|
|
118
|
-
subManifests.push('composer.json');
|
|
119
|
-
if (subPathItem('go.mod'))
|
|
120
|
-
subManifests.push('go.mod');
|
|
121
|
-
if (subPathItem('Cargo.toml'))
|
|
122
|
-
subManifests.push('Cargo.toml');
|
|
123
|
-
if (subPathItem('pom.xml'))
|
|
124
|
-
subManifests.push('pom.xml');
|
|
125
|
-
if (subPathItem('build.gradle'))
|
|
126
|
-
subManifests.push('build.gradle');
|
|
127
|
-
if (subPathItem('build.gradle.kts'))
|
|
128
|
-
subManifests.push('build.gradle.kts');
|
|
129
|
-
if (subPathItem('pyproject.toml'))
|
|
130
|
-
subManifests.push('pyproject.toml');
|
|
131
|
-
if (subPathItem('requirements.txt'))
|
|
132
|
-
subManifests.push('requirements.txt');
|
|
133
|
-
if (subPathItem('pubspec.yaml'))
|
|
134
|
-
subManifests.push('pubspec.yaml');
|
|
135
|
-
if (subPathItem('Podfile'))
|
|
136
|
-
subManifests.push('Podfile');
|
|
137
|
-
if (subPathItem('AndroidManifest.xml'))
|
|
138
|
-
subManifests.push('AndroidManifest.xml');
|
|
139
|
-
if (subManifests.length > 0) {
|
|
140
|
-
if (!keyDirectories.includes(item)) {
|
|
141
|
-
keyDirectories.push(item);
|
|
142
|
-
}
|
|
143
|
-
let subTech = 'Service';
|
|
144
|
-
let subRuntime = '';
|
|
145
|
-
let subFramework = '';
|
|
146
|
-
let subOrm = '';
|
|
147
|
-
let subOrmConfig = '';
|
|
148
|
-
// Check mobile & ecosystem profiles
|
|
149
|
-
if (subPathItem('pubspec.yaml')) {
|
|
150
|
-
subTech = 'Flutter Mobile App';
|
|
151
|
-
subRuntime = 'Dart / Flutter';
|
|
152
|
-
subFramework = 'Flutter';
|
|
153
|
-
}
|
|
154
|
-
else if (subPathItem('Podfile') || subPathItem('Info.plist') || fs.existsSync(path.join(itemPath, 'ios'))) {
|
|
155
|
-
subTech = 'Native iOS App (Swift/Obj-C)';
|
|
156
|
-
subRuntime = 'Swift / Objective-C';
|
|
157
|
-
subFramework = 'iOS Native';
|
|
158
|
-
}
|
|
159
|
-
else if (subPathItem('AndroidManifest.xml') || (subPathItem('build.gradle') && !subPathItem('package.json'))) {
|
|
160
|
-
subTech = 'Native Android App (Kotlin/Java)';
|
|
161
|
-
subRuntime = 'Kotlin / Java (Android)';
|
|
162
|
-
subFramework = 'Android Native';
|
|
163
|
-
}
|
|
164
|
-
else if (subPathItem('composer.json')) {
|
|
165
|
-
subRuntime = 'PHP';
|
|
166
|
-
subTech = 'PHP Backend';
|
|
167
|
-
try {
|
|
168
|
-
const subComposer = JSON.parse(readFileSafe(path.join(itemPath, 'composer.json')));
|
|
169
|
-
const allReq = { ...subComposer.require, ...subComposer['require-dev'] };
|
|
170
|
-
if (allReq['laravel/framework']) {
|
|
171
|
-
subFramework = 'Laravel';
|
|
172
|
-
subTech = 'Laravel Backend (PHP)';
|
|
173
|
-
subOrm = 'Eloquent ORM';
|
|
174
|
-
}
|
|
175
|
-
else if (allReq['symfony/framework-bundle']) {
|
|
176
|
-
subFramework = 'Symfony';
|
|
177
|
-
subTech = 'Symfony Backend (PHP)';
|
|
178
|
-
subOrm = 'Doctrine ORM';
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
catch { }
|
|
182
|
-
if (fs.existsSync(path.join(itemPath, 'database/migrations'))) {
|
|
183
|
-
subOrmConfig = `${item}/database/migrations`;
|
|
184
|
-
if (!subOrm)
|
|
185
|
-
subOrm = 'SQL Migrations';
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
else if (subPathItem('pyproject.toml') || subPathItem('requirements.txt')) {
|
|
189
|
-
subRuntime = 'Python';
|
|
190
|
-
subTech = 'Python Backend';
|
|
191
|
-
const pyStr = readFileSafe(path.join(itemPath, 'requirements.txt')) + readFileSafe(path.join(itemPath, 'pyproject.toml'));
|
|
192
|
-
if (pyStr.includes('fastapi')) {
|
|
193
|
-
subFramework = 'FastAPI';
|
|
194
|
-
subTech = 'FastAPI Backend (Python)';
|
|
195
|
-
}
|
|
196
|
-
else if (pyStr.includes('django')) {
|
|
197
|
-
subFramework = 'Django';
|
|
198
|
-
subTech = 'Django Backend (Python)';
|
|
199
|
-
subOrm = 'Django ORM';
|
|
200
|
-
}
|
|
201
|
-
else if (pyStr.includes('flask')) {
|
|
202
|
-
subFramework = 'Flask';
|
|
203
|
-
subTech = 'Flask Backend (Python)';
|
|
204
|
-
}
|
|
205
|
-
if (pyStr.includes('sqlalchemy') || fs.existsSync(path.join(itemPath, 'alembic.ini'))) {
|
|
206
|
-
subOrm = 'SQLAlchemy';
|
|
207
|
-
if (fs.existsSync(path.join(itemPath, 'alembic.ini'))) {
|
|
208
|
-
subOrmConfig = `${item}/alembic.ini`;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
else if (subPathItem('go.mod')) {
|
|
213
|
-
subRuntime = 'Go';
|
|
214
|
-
subTech = 'Go Backend';
|
|
215
|
-
const goStr = readFileSafe(path.join(itemPath, 'go.mod'));
|
|
216
|
-
if (goStr.includes('gin-gonic/gin')) {
|
|
217
|
-
subFramework = 'Gin';
|
|
218
|
-
subTech = 'Gin Backend (Go)';
|
|
219
|
-
}
|
|
220
|
-
else if (goStr.includes('gofiber/fiber')) {
|
|
221
|
-
subFramework = 'Fiber';
|
|
222
|
-
subTech = 'Fiber Backend (Go)';
|
|
223
|
-
}
|
|
224
|
-
if (goStr.includes('gorm.io/gorm'))
|
|
225
|
-
subOrm = 'GORM';
|
|
226
|
-
}
|
|
227
|
-
else if (subPathItem('package.json')) {
|
|
228
|
-
subRuntime = 'Node.js';
|
|
229
|
-
subTech = 'Node.js Service';
|
|
230
|
-
try {
|
|
231
|
-
const subPkg = JSON.parse(readFileSafe(path.join(itemPath, 'package.json')));
|
|
232
|
-
const subDeps = { ...subPkg.dependencies, ...subPkg.devDependencies };
|
|
233
|
-
if (subDeps.typescript || fs.existsSync(path.join(itemPath, 'tsconfig.json'))) {
|
|
234
|
-
subRuntime = 'Node.js (TypeScript)';
|
|
235
|
-
}
|
|
236
|
-
if (subDeps['react-native'] || subDeps['expo']) {
|
|
237
|
-
subTech = 'React Native / Expo Mobile App';
|
|
238
|
-
subFramework = subDeps.expo ? 'Expo Mobile App' : 'React Native';
|
|
239
|
-
}
|
|
240
|
-
else if (subDeps.next) {
|
|
241
|
-
subTech = 'Next.js Web App';
|
|
242
|
-
subFramework = 'Next.js';
|
|
243
|
-
}
|
|
244
|
-
else if (subDeps.nuxt) {
|
|
245
|
-
subTech = 'Nuxt Web App';
|
|
246
|
-
subFramework = 'Nuxt';
|
|
247
|
-
}
|
|
248
|
-
else if (subDeps.vite || subDeps.react || subDeps.vue) {
|
|
249
|
-
subTech = 'React / Web Frontend';
|
|
250
|
-
subFramework = subDeps.vite ? 'Vite Frontend' : (subDeps.react ? 'React SPA' : 'Vue SPA');
|
|
251
|
-
}
|
|
252
|
-
else if (subDeps.express || subDeps['@nestjs/core'] || subDeps.fastify) {
|
|
253
|
-
subTech = 'Node.js Backend';
|
|
254
|
-
subFramework = subDeps['@nestjs/core'] ? 'NestJS' : (subDeps.fastify ? 'Fastify' : 'Express.js');
|
|
255
|
-
}
|
|
256
|
-
if (subDeps.prisma || fs.existsSync(path.join(itemPath, 'prisma/schema.prisma'))) {
|
|
257
|
-
subOrm = 'Prisma ORM';
|
|
258
|
-
subOrmConfig = `${item}/prisma/schema.prisma`;
|
|
259
|
-
}
|
|
260
|
-
else if (subDeps['drizzle-orm'] || fs.existsSync(path.join(itemPath, 'drizzle.config.ts'))) {
|
|
261
|
-
subOrm = 'Drizzle ORM';
|
|
262
|
-
subOrmConfig = `${item}/drizzle.config.ts`;
|
|
263
|
-
}
|
|
264
|
-
else if (subDeps.typeorm) {
|
|
265
|
-
subOrm = 'TypeORM';
|
|
266
|
-
}
|
|
267
|
-
else if (subDeps.mongoose) {
|
|
268
|
-
subOrm = 'Mongoose';
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
catch {
|
|
272
|
-
subTech = 'Node.js / Web App';
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
independentServices.push({
|
|
276
|
-
name: item,
|
|
277
|
-
dirPath: itemPath,
|
|
278
|
-
manifests: subManifests,
|
|
279
|
-
tech: subTech,
|
|
280
|
-
runtime: subRuntime,
|
|
281
|
-
framework: subFramework,
|
|
282
|
-
orm: subOrm,
|
|
283
|
-
ormConfig: subOrmConfig,
|
|
284
|
-
});
|
|
285
|
-
if (!monorepoWorkspaces.includes(`${item}/*`) && !candidateWorkspaceDirs.includes(item)) {
|
|
286
|
-
monorepoWorkspaces.push(`${item}/*`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
// ignore read error
|
|
294
|
-
}
|
|
295
|
-
// -------------------------------------------------------------
|
|
296
|
-
// 2. Language & Framework Profiling Across Ecosystems
|
|
297
|
-
// -------------------------------------------------------------
|
|
298
|
-
// --- Ecosystem A: Node.js / JavaScript / TypeScript ---
|
|
299
|
-
const pkgPath = path.join(resolvedTarget, 'package.json');
|
|
300
|
-
if (fileExists('package.json')) {
|
|
301
|
-
ecosystemManifests.push('package.json');
|
|
302
|
-
try {
|
|
303
|
-
const pkg = JSON.parse(readFileSafe(pkgPath));
|
|
304
|
-
if (pkg.name)
|
|
305
|
-
projectName = pkg.name;
|
|
306
|
-
if (pkg.description)
|
|
307
|
-
projectGoal = pkg.description;
|
|
308
|
-
// Check package.json workspaces
|
|
309
|
-
if (pkg.workspaces) {
|
|
310
|
-
if (Array.isArray(pkg.workspaces)) {
|
|
311
|
-
monorepoWorkspaces.push(...pkg.workspaces);
|
|
312
|
-
}
|
|
313
|
-
else if (Array.isArray(pkg.workspaces.packages)) {
|
|
314
|
-
monorepoWorkspaces.push(...pkg.workspaces.packages);
|
|
315
|
-
}
|
|
316
|
-
if (!monorepoIndicators.includes('npm/yarn workspaces')) {
|
|
317
|
-
monorepoIndicators.push('npm/yarn workspaces');
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
321
|
-
// Language & Runtime
|
|
322
|
-
if (deps.typescript || fileExists('tsconfig.json')) {
|
|
323
|
-
runtime = 'Node.js (TypeScript)';
|
|
324
|
-
if (fileExists('tsconfig.json'))
|
|
325
|
-
ecosystemManifests.push('tsconfig.json');
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
|
-
runtime = 'Node.js (JavaScript)';
|
|
329
|
-
}
|
|
330
|
-
// Frameworks
|
|
331
|
-
if (deps['react-native'] || deps.expo) {
|
|
332
|
-
framework = deps.expo ? 'Expo Mobile App' : 'React Native';
|
|
333
|
-
}
|
|
334
|
-
else if (deps.next)
|
|
335
|
-
framework = `Next.js (${deps.next})`;
|
|
336
|
-
else if (deps.nuxt)
|
|
337
|
-
framework = `Nuxt (${deps.nuxt})`;
|
|
338
|
-
else if (deps.remix || deps['@remix-run/react'])
|
|
339
|
-
framework = 'Remix';
|
|
340
|
-
else if (deps.astro)
|
|
341
|
-
framework = 'Astro';
|
|
342
|
-
else if (deps['@sveltejs/kit'] || deps.svelte)
|
|
343
|
-
framework = 'Svelte / SvelteKit';
|
|
344
|
-
else if (deps.vite)
|
|
345
|
-
framework = 'Vite / React/Vue';
|
|
346
|
-
else if (deps['@nestjs/core'])
|
|
347
|
-
framework = 'NestJS';
|
|
348
|
-
else if (deps.hono)
|
|
349
|
-
framework = 'Hono';
|
|
350
|
-
else if (deps.fastify)
|
|
351
|
-
framework = 'Fastify';
|
|
352
|
-
else if (deps.express)
|
|
353
|
-
framework = 'Express.js';
|
|
354
|
-
else if (deps['@angular/core'])
|
|
355
|
-
framework = 'Angular';
|
|
356
|
-
else if (deps.react)
|
|
357
|
-
framework = 'React SPA';
|
|
358
|
-
else if (deps.vue)
|
|
359
|
-
framework = 'Vue.js SPA';
|
|
360
|
-
// Database & ORMs in deps
|
|
361
|
-
if (deps.prisma || deps['@prisma/client'])
|
|
362
|
-
databaseOrm = 'Prisma ORM';
|
|
363
|
-
else if (deps['drizzle-orm'])
|
|
364
|
-
databaseOrm = 'Drizzle ORM';
|
|
365
|
-
else if (deps.typeorm)
|
|
366
|
-
databaseOrm = 'TypeORM';
|
|
367
|
-
else if (deps.mongoose)
|
|
368
|
-
databaseOrm = 'Mongoose / MongoDB';
|
|
369
|
-
else if (deps.sequelize)
|
|
370
|
-
databaseOrm = 'Sequelize';
|
|
371
|
-
else if (deps.knex)
|
|
372
|
-
databaseOrm = 'Knex';
|
|
373
|
-
else if (deps['@supabase/supabase-js'])
|
|
374
|
-
databaseOrm = 'Supabase Client';
|
|
375
|
-
else if (deps.pg)
|
|
376
|
-
databaseOrm = 'pg (node-postgres)';
|
|
377
|
-
else if (deps.mysql2)
|
|
378
|
-
databaseOrm = 'mysql2';
|
|
379
|
-
else if (deps['better-sqlite3'] || deps.sqlite3)
|
|
380
|
-
databaseOrm = 'SQLite';
|
|
381
|
-
// Styling
|
|
382
|
-
if (deps.tailwindcss)
|
|
383
|
-
styling = 'Tailwind CSS';
|
|
384
|
-
else if (deps['styled-components'])
|
|
385
|
-
styling = 'Styled Components';
|
|
386
|
-
else if (deps['@emotion/react'])
|
|
387
|
-
styling = 'Emotion';
|
|
388
|
-
else if (deps.sass)
|
|
389
|
-
styling = 'SASS / SCSS';
|
|
390
|
-
// Testing
|
|
391
|
-
if (deps.vitest) {
|
|
392
|
-
testing = 'Vitest';
|
|
393
|
-
verificationCommand = 'npm run test';
|
|
394
|
-
}
|
|
395
|
-
else if (deps.jest) {
|
|
396
|
-
testing = 'Jest';
|
|
397
|
-
verificationCommand = 'npm test';
|
|
398
|
-
}
|
|
399
|
-
else if (deps.playwright || deps['@playwright/test']) {
|
|
400
|
-
testing = 'Playwright';
|
|
401
|
-
verificationCommand = 'npx playwright test';
|
|
402
|
-
}
|
|
403
|
-
// Package Manager
|
|
404
|
-
if (fileExists('pnpm-lock.yaml')) {
|
|
405
|
-
packageManager = 'pnpm';
|
|
406
|
-
ecosystemManifests.push('pnpm-lock.yaml');
|
|
407
|
-
}
|
|
408
|
-
else if (fileExists('yarn.lock')) {
|
|
409
|
-
packageManager = 'yarn';
|
|
410
|
-
ecosystemManifests.push('yarn.lock');
|
|
411
|
-
}
|
|
412
|
-
else if (fileExists('bun.lockb') || fileExists('bun.lock')) {
|
|
413
|
-
packageManager = 'bun';
|
|
414
|
-
ecosystemManifests.push(fileExists('bun.lockb') ? 'bun.lockb' : 'bun.lock');
|
|
415
|
-
}
|
|
416
|
-
else if (fileExists('package-lock.json')) {
|
|
417
|
-
packageManager = 'npm';
|
|
418
|
-
ecosystemManifests.push('package-lock.json');
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
packageManager = 'npm';
|
|
422
|
-
}
|
|
423
|
-
// Verification command adjustment
|
|
424
|
-
if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified" && exit 1') {
|
|
425
|
-
verificationCommand = `${packageManager === 'npm' ? 'npm test' : `${packageManager} test`}`;
|
|
426
|
-
}
|
|
427
|
-
else if (pkg.scripts?.build) {
|
|
428
|
-
verificationCommand = `${packageManager === 'npm' ? 'npm run build' : `${packageManager} build`}`;
|
|
429
|
-
}
|
|
430
|
-
projectType = framework !== 'None detected' ? `Web Application (${framework})` : 'Node.js Application';
|
|
431
|
-
}
|
|
432
|
-
catch {
|
|
433
|
-
// ignore parse failure
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
// --- Ecosystem B: PHP (composer.json) ---
|
|
437
|
-
if (fileExists('composer.json')) {
|
|
438
|
-
ecosystemManifests.push('composer.json');
|
|
439
|
-
if (runtime === 'Unknown / Greenfield') {
|
|
440
|
-
runtime = 'PHP';
|
|
441
|
-
packageManager = 'composer';
|
|
442
|
-
projectType = 'PHP Application';
|
|
443
|
-
verificationCommand = 'composer test';
|
|
444
|
-
}
|
|
445
|
-
const composerRaw = readFileSafe(path.join(resolvedTarget, 'composer.json'));
|
|
446
|
-
try {
|
|
447
|
-
const composerPkg = JSON.parse(composerRaw);
|
|
448
|
-
if (composerPkg.name && projectName === baseName) {
|
|
449
|
-
projectName = composerPkg.name;
|
|
450
|
-
}
|
|
451
|
-
if (composerPkg.description && projectGoal.includes('Awaiting specification')) {
|
|
452
|
-
projectGoal = composerPkg.description;
|
|
453
|
-
}
|
|
454
|
-
const allPhpDeps = { ...composerPkg.require, ...composerPkg['require-dev'] };
|
|
455
|
-
if (allPhpDeps['laravel/framework']) {
|
|
456
|
-
framework = 'Laravel';
|
|
457
|
-
projectType = 'Laravel Web Application';
|
|
458
|
-
databaseOrm = 'Eloquent ORM';
|
|
459
|
-
verificationCommand = 'php artisan test';
|
|
460
|
-
}
|
|
461
|
-
else if (allPhpDeps['symfony/framework-bundle'] || allPhpDeps['symfony/symfony']) {
|
|
462
|
-
framework = 'Symfony';
|
|
463
|
-
projectType = 'Symfony Application';
|
|
464
|
-
databaseOrm = 'Doctrine ORM';
|
|
465
|
-
verificationCommand = 'bin/phpunit';
|
|
466
|
-
}
|
|
467
|
-
else if (allPhpDeps['slim/slim']) {
|
|
468
|
-
framework = 'Slim Framework';
|
|
469
|
-
}
|
|
470
|
-
else if (allPhpDeps['roots/bedrock'] || composerRaw.includes('wordpress')) {
|
|
471
|
-
framework = 'WordPress';
|
|
472
|
-
}
|
|
473
|
-
if (allPhpDeps['pestphp/pest']) {
|
|
474
|
-
testing = 'Pest';
|
|
475
|
-
}
|
|
476
|
-
else if (allPhpDeps['phpunit/phpunit']) {
|
|
477
|
-
testing = 'PHPUnit';
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
catch {
|
|
481
|
-
if (composerRaw.includes('laravel/framework')) {
|
|
482
|
-
framework = 'Laravel';
|
|
483
|
-
databaseOrm = 'Eloquent ORM';
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
// --- Ecosystem C: Python (pyproject.toml, requirements.txt, poetry.lock, Pipfile) ---
|
|
488
|
-
const hasPyproject = fileExists('pyproject.toml');
|
|
489
|
-
const hasRequirements = fileExists('requirements.txt');
|
|
490
|
-
const hasPipfile = fileExists('Pipfile');
|
|
491
|
-
if (hasPyproject || hasRequirements || hasPipfile) {
|
|
492
|
-
if (hasPyproject)
|
|
493
|
-
ecosystemManifests.push('pyproject.toml');
|
|
494
|
-
if (hasRequirements)
|
|
495
|
-
ecosystemManifests.push('requirements.txt');
|
|
496
|
-
if (hasPipfile)
|
|
497
|
-
ecosystemManifests.push('Pipfile');
|
|
498
|
-
if (fileExists('poetry.lock'))
|
|
499
|
-
ecosystemManifests.push('poetry.lock');
|
|
500
|
-
if (fileExists('uv.lock'))
|
|
501
|
-
ecosystemManifests.push('uv.lock');
|
|
502
|
-
if (runtime === 'Unknown / Greenfield') {
|
|
503
|
-
runtime = 'Python 3.x';
|
|
504
|
-
projectType = 'Python Service / Application';
|
|
505
|
-
packageManager = fileExists('poetry.lock')
|
|
506
|
-
? 'Poetry'
|
|
507
|
-
: fileExists('uv.lock')
|
|
508
|
-
? 'uv'
|
|
509
|
-
: fileExists('Pipfile')
|
|
510
|
-
? 'Pipenv'
|
|
511
|
-
: 'pip';
|
|
512
|
-
verificationCommand = 'pytest';
|
|
513
|
-
}
|
|
514
|
-
let pyContent = '';
|
|
515
|
-
if (hasRequirements)
|
|
516
|
-
pyContent += readFileSafe(path.join(resolvedTarget, 'requirements.txt'));
|
|
517
|
-
if (hasPyproject)
|
|
518
|
-
pyContent += readFileSafe(path.join(resolvedTarget, 'pyproject.toml'));
|
|
519
|
-
if (pyContent.includes('fastapi'))
|
|
520
|
-
framework = 'FastAPI';
|
|
521
|
-
else if (pyContent.includes('django')) {
|
|
522
|
-
framework = 'Django';
|
|
523
|
-
databaseOrm = 'Django ORM';
|
|
524
|
-
verificationCommand = 'python manage.py test';
|
|
525
|
-
}
|
|
526
|
-
else if (pyContent.includes('flask'))
|
|
527
|
-
framework = 'Flask';
|
|
528
|
-
else if (pyContent.includes('litestar'))
|
|
529
|
-
framework = 'Litestar';
|
|
530
|
-
if (pyContent.includes('sqlalchemy'))
|
|
531
|
-
databaseOrm = 'SQLAlchemy';
|
|
532
|
-
else if (pyContent.includes('tortoise-orm'))
|
|
533
|
-
databaseOrm = 'Tortoise ORM';
|
|
534
|
-
else if (pyContent.includes('sqlmodel'))
|
|
535
|
-
databaseOrm = 'SQLModel';
|
|
536
|
-
if (pyContent.includes('pytest'))
|
|
537
|
-
testing = 'pytest';
|
|
538
|
-
}
|
|
539
|
-
// --- Ecosystem D: Go (go.mod) ---
|
|
540
|
-
if (fileExists('go.mod')) {
|
|
541
|
-
ecosystemManifests.push('go.mod');
|
|
542
|
-
if (runtime === 'Unknown / Greenfield') {
|
|
543
|
-
runtime = 'Go';
|
|
544
|
-
projectType = 'Go Service';
|
|
545
|
-
packageManager = 'go modules';
|
|
546
|
-
verificationCommand = 'go test ./...';
|
|
547
|
-
}
|
|
548
|
-
const modContent = readFileSafe(path.join(resolvedTarget, 'go.mod'));
|
|
549
|
-
const moduleMatch = modContent.match(/module\s+([^\s\n]+)/);
|
|
550
|
-
if (moduleMatch && projectName === baseName) {
|
|
551
|
-
projectName = path.basename(moduleMatch[1]);
|
|
552
|
-
}
|
|
553
|
-
if (modContent.includes('gin-gonic/gin'))
|
|
554
|
-
framework = 'Gin';
|
|
555
|
-
else if (modContent.includes('gofiber/fiber'))
|
|
556
|
-
framework = 'Fiber';
|
|
557
|
-
else if (modContent.includes('labstack/echo'))
|
|
558
|
-
framework = 'Echo';
|
|
559
|
-
else if (modContent.includes('go-chi/chi'))
|
|
560
|
-
framework = 'Chi';
|
|
561
|
-
if (modContent.includes('gorm.io/gorm'))
|
|
562
|
-
databaseOrm = 'GORM';
|
|
563
|
-
else if (modContent.includes('entgo.io/ent'))
|
|
564
|
-
databaseOrm = 'Ent';
|
|
565
|
-
else if (modContent.includes('jmoiron/sqlx'))
|
|
566
|
-
databaseOrm = 'sqlx';
|
|
567
|
-
}
|
|
568
|
-
// --- Ecosystem E: Rust (Cargo.toml) ---
|
|
569
|
-
if (fileExists('Cargo.toml')) {
|
|
570
|
-
ecosystemManifests.push('Cargo.toml');
|
|
571
|
-
if (runtime === 'Unknown / Greenfield') {
|
|
572
|
-
runtime = 'Rust';
|
|
573
|
-
projectType = 'Rust Application';
|
|
574
|
-
packageManager = 'cargo';
|
|
575
|
-
verificationCommand = 'cargo test';
|
|
576
|
-
}
|
|
577
|
-
const cargoContent = readFileSafe(path.join(resolvedTarget, 'Cargo.toml'));
|
|
578
|
-
if (cargoContent.includes('actix-web'))
|
|
579
|
-
framework = 'Actix Web';
|
|
580
|
-
else if (cargoContent.includes('axum'))
|
|
581
|
-
framework = 'Axum';
|
|
582
|
-
else if (cargoContent.includes('rocket'))
|
|
583
|
-
framework = 'Rocket';
|
|
584
|
-
else if (cargoContent.includes('tauri'))
|
|
585
|
-
framework = 'Tauri';
|
|
586
|
-
if (cargoContent.includes('diesel'))
|
|
587
|
-
databaseOrm = 'Diesel';
|
|
588
|
-
else if (cargoContent.includes('sqlx'))
|
|
589
|
-
databaseOrm = 'SQLx';
|
|
590
|
-
else if (cargoContent.includes('sea-orm'))
|
|
591
|
-
databaseOrm = 'SeaORM';
|
|
592
|
-
}
|
|
593
|
-
// --- Ecosystem F: Java / JVM (pom.xml, build.gradle) ---
|
|
594
|
-
if (fileExists('pom.xml') || fileExists('build.gradle') || fileExists('build.gradle.kts')) {
|
|
595
|
-
if (fileExists('pom.xml'))
|
|
596
|
-
ecosystemManifests.push('pom.xml');
|
|
597
|
-
if (fileExists('build.gradle'))
|
|
598
|
-
ecosystemManifests.push('build.gradle');
|
|
599
|
-
if (fileExists('build.gradle.kts'))
|
|
600
|
-
ecosystemManifests.push('build.gradle.kts');
|
|
601
|
-
if (runtime === 'Unknown / Greenfield') {
|
|
602
|
-
runtime = 'Java / JVM';
|
|
603
|
-
projectType = 'Java Service / Application';
|
|
604
|
-
packageManager = fileExists('pom.xml') ? 'Maven' : 'Gradle';
|
|
605
|
-
verificationCommand = fileExists('pom.xml') ? 'mvn test' : './gradlew test';
|
|
606
|
-
}
|
|
607
|
-
const jvmContent = readFileSafe(path.join(resolvedTarget, fileExists('pom.xml') ? 'pom.xml' : 'build.gradle'));
|
|
608
|
-
if (jvmContent.includes('spring-boot'))
|
|
609
|
-
framework = 'Spring Boot';
|
|
610
|
-
else if (jvmContent.includes('quarkus'))
|
|
611
|
-
framework = 'Quarkus';
|
|
612
|
-
else if (jvmContent.includes('micronaut'))
|
|
613
|
-
framework = 'Micronaut';
|
|
614
|
-
if (jvmContent.includes('hibernate') || jvmContent.includes('spring-boot-starter-data-jpa')) {
|
|
615
|
-
databaseOrm = 'Hibernate / JPA';
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
// --- Ecosystem G: Mobile / Flutter (pubspec.yaml) ---
|
|
619
|
-
if (fileExists('pubspec.yaml')) {
|
|
620
|
-
ecosystemManifests.push('pubspec.yaml');
|
|
621
|
-
if (runtime === 'Unknown / Greenfield') {
|
|
622
|
-
runtime = 'Dart / Flutter';
|
|
623
|
-
framework = 'Flutter Mobile App';
|
|
624
|
-
projectType = 'Mobile Application (Flutter)';
|
|
625
|
-
packageManager = 'flutter pub';
|
|
626
|
-
verificationCommand = 'flutter test';
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
// -------------------------------------------------------------
|
|
630
|
-
// 3. Database & ORM Layer Detection (Config files & Migrations)
|
|
631
|
-
// -------------------------------------------------------------
|
|
632
|
-
if (fileExists('prisma/schema.prisma')) {
|
|
633
|
-
detectedOrmConfig = 'prisma/schema.prisma';
|
|
634
|
-
databaseOrm = 'Prisma ORM';
|
|
635
|
-
}
|
|
636
|
-
else if (fileExists('schema.prisma')) {
|
|
637
|
-
detectedOrmConfig = 'schema.prisma';
|
|
638
|
-
databaseOrm = 'Prisma ORM';
|
|
639
|
-
}
|
|
640
|
-
if (fileExists('drizzle.config.ts')) {
|
|
641
|
-
detectedOrmConfig = 'drizzle.config.ts';
|
|
642
|
-
databaseOrm = 'Drizzle ORM';
|
|
643
|
-
}
|
|
644
|
-
else if (fileExists('drizzle.config.js')) {
|
|
645
|
-
detectedOrmConfig = 'drizzle.config.js';
|
|
646
|
-
databaseOrm = 'Drizzle ORM';
|
|
647
|
-
}
|
|
648
|
-
else if (fileExists('drizzle.config.json')) {
|
|
649
|
-
detectedOrmConfig = 'drizzle.config.json';
|
|
650
|
-
databaseOrm = 'Drizzle ORM';
|
|
651
|
-
}
|
|
652
|
-
if (fileExists('knexfile.js')) {
|
|
653
|
-
detectedOrmConfig = 'knexfile.js';
|
|
654
|
-
databaseOrm = 'Knex';
|
|
655
|
-
}
|
|
656
|
-
else if (fileExists('knexfile.ts')) {
|
|
657
|
-
detectedOrmConfig = 'knexfile.ts';
|
|
658
|
-
databaseOrm = 'Knex';
|
|
659
|
-
}
|
|
660
|
-
if (fileExists('alembic.ini')) {
|
|
661
|
-
detectedOrmConfig = 'alembic.ini';
|
|
662
|
-
databaseOrm = databaseOrm === 'None detected' ? 'SQLAlchemy (Alembic)' : `${databaseOrm} (Alembic)`;
|
|
663
|
-
}
|
|
664
|
-
if (fileExists('database/migrations')) {
|
|
665
|
-
const isDir = fs.statSync(path.join(resolvedTarget, 'database/migrations')).isDirectory();
|
|
666
|
-
if (isDir) {
|
|
667
|
-
detectedOrmConfig = 'database/migrations';
|
|
668
|
-
if (databaseOrm === 'None detected')
|
|
669
|
-
databaseOrm = 'SQL Migrations';
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
if (fileExists('ormconfig.json')) {
|
|
673
|
-
detectedOrmConfig = 'ormconfig.json';
|
|
674
|
-
databaseOrm = 'TypeORM';
|
|
675
|
-
}
|
|
676
|
-
// -------------------------------------------------------------
|
|
36
|
+
// 1. Structure & Workspace Detection
|
|
37
|
+
const wsResult = detectWorkspacesAndServices(resolvedTarget, fileExists, readFileSafe);
|
|
38
|
+
const monorepoIndicators = [...wsResult.monorepoIndicators];
|
|
39
|
+
const monorepoWorkspaces = [...wsResult.monorepoWorkspaces];
|
|
40
|
+
const keyDirectories = [...wsResult.keyDirectories];
|
|
41
|
+
const ecosystemManifests = [...wsResult.ecosystemManifests];
|
|
42
|
+
const { detectedSubpackagesCount, independentServices } = wsResult;
|
|
43
|
+
// 2. Language & Framework Profiling
|
|
44
|
+
const ecoResult = detectEcosystems(resolvedTarget, baseName, fileExists, readFileSafe);
|
|
45
|
+
if (ecoResult.projectName)
|
|
46
|
+
projectName = ecoResult.projectName;
|
|
47
|
+
if (ecoResult.projectGoal)
|
|
48
|
+
projectGoal = ecoResult.projectGoal;
|
|
49
|
+
if (ecoResult.projectType)
|
|
50
|
+
projectType = ecoResult.projectType;
|
|
51
|
+
if (ecoResult.runtime !== 'Unknown / Greenfield')
|
|
52
|
+
runtime = ecoResult.runtime;
|
|
53
|
+
if (ecoResult.framework !== 'None detected')
|
|
54
|
+
framework = ecoResult.framework;
|
|
55
|
+
if (ecoResult.databaseOrm !== 'None detected')
|
|
56
|
+
databaseOrm = ecoResult.databaseOrm;
|
|
57
|
+
if (ecoResult.styling !== 'None detected')
|
|
58
|
+
styling = ecoResult.styling;
|
|
59
|
+
if (ecoResult.testing !== 'None detected')
|
|
60
|
+
testing = ecoResult.testing;
|
|
61
|
+
if (ecoResult.packageManager !== 'None detected')
|
|
62
|
+
packageManager = ecoResult.packageManager;
|
|
63
|
+
if (ecoResult.verificationCommand)
|
|
64
|
+
verificationCommand = ecoResult.verificationCommand;
|
|
65
|
+
ecosystemManifests.push(...ecoResult.ecosystemManifests.filter((m) => !ecosystemManifests.includes(m)));
|
|
66
|
+
monorepoWorkspaces.push(...ecoResult.monorepoWorkspaces.filter((w) => !monorepoWorkspaces.includes(w)));
|
|
67
|
+
monorepoIndicators.push(...ecoResult.monorepoIndicators.filter((i) => !monorepoIndicators.includes(i)));
|
|
68
|
+
// 3. Database & ORM Config Detection
|
|
69
|
+
const ormResult = detectOrm(resolvedTarget, fileExists, databaseOrm);
|
|
70
|
+
let detectedOrmConfig = ormResult.detectedOrmConfig;
|
|
71
|
+
databaseOrm = ormResult.databaseOrm;
|
|
677
72
|
// 4. Resolve Repository Type & Workspaces
|
|
678
|
-
// -------------------------------------------------------------
|
|
679
73
|
if (monorepoIndicators.length > 0 || detectedSubpackagesCount > 0) {
|
|
680
74
|
const toolLabel = monorepoIndicators.length > 0 ? monorepoIndicators.join(' + ') : 'Directory Workspaces';
|
|
681
75
|
repositoryType = `Monorepo (${toolLabel})`;
|
|
@@ -684,7 +78,6 @@ export function detectProject(targetDir) {
|
|
|
684
78
|
}
|
|
685
79
|
}
|
|
686
80
|
else if (ecosystemManifests.length === 0 && independentServices.length > 0) {
|
|
687
|
-
// Polyrepo / Multi-Service with no root manifest
|
|
688
81
|
repositoryType = 'Polyrepo / Multi-Service Workspace';
|
|
689
82
|
projectType = 'Multi-Service Application Suite';
|
|
690
83
|
architecturePattern = 'Polyrepo (Independent Sub-services)';
|
|
@@ -710,25 +103,11 @@ export function detectProject(targetDir) {
|
|
|
710
103
|
else {
|
|
711
104
|
repositoryType = 'Generic Application (Greenfield)';
|
|
712
105
|
}
|
|
713
|
-
// -------------------------------------------------------------
|
|
714
106
|
// 5. Check Key Directories & Source Root
|
|
715
|
-
// -------------------------------------------------------------
|
|
716
107
|
const candidateDirs = [
|
|
717
|
-
'src',
|
|
718
|
-
'
|
|
719
|
-
'
|
|
720
|
-
'components',
|
|
721
|
-
'lib',
|
|
722
|
-
'api',
|
|
723
|
-
'controllers',
|
|
724
|
-
'routes',
|
|
725
|
-
'models',
|
|
726
|
-
'tests',
|
|
727
|
-
'test',
|
|
728
|
-
'public',
|
|
729
|
-
'prisma',
|
|
730
|
-
'templates',
|
|
731
|
-
'bin',
|
|
108
|
+
'src', 'app', 'pages', 'components', 'lib', 'api',
|
|
109
|
+
'controllers', 'routes', 'models', 'tests', 'test',
|
|
110
|
+
'public', 'prisma', 'templates', 'bin',
|
|
732
111
|
];
|
|
733
112
|
for (const dir of candidateDirs) {
|
|
734
113
|
if (fileExists(dir) && !keyDirectories.includes(dir)) {
|