@njeybe/nativ 2.0.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.
- package/CHANGELOG.md +40 -0
- package/LICENSE +21 -0
- package/README.md +334 -0
- package/bin/cli.js +31 -0
- package/dist/commands/bench.d.ts +8 -0
- package/dist/commands/bench.js +52 -0
- package/dist/commands/db.d.ts +22 -0
- package/dist/commands/db.js +415 -0
- package/dist/commands/doctor.d.ts +38 -0
- package/dist/commands/doctor.js +169 -0
- package/dist/commands/hook.d.ts +31 -0
- package/dist/commands/hook.js +105 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +144 -0
- package/dist/commands/mcp.d.ts +5 -0
- package/dist/commands/mcp.js +16 -0
- package/dist/commands/setup.d.ts +17 -0
- package/dist/commands/setup.js +51 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.js +75 -0
- package/dist/commands/task.d.ts +83 -0
- package/dist/commands/task.js +807 -0
- package/dist/commands/test-gen.d.ts +14 -0
- package/dist/commands/test-gen.js +52 -0
- package/dist/commands/triage.d.ts +70 -0
- package/dist/commands/triage.js +343 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +112 -0
- package/dist/commands/validate.d.ts +1 -0
- package/dist/commands/validate.js +89 -0
- package/dist/commands/verify.d.ts +8 -0
- package/dist/commands/verify.js +65 -0
- package/dist/commands/worktree.d.ts +47 -0
- package/dist/commands/worktree.js +396 -0
- package/dist/core/benchmark.d.ts +57 -0
- package/dist/core/benchmark.js +772 -0
- package/dist/core/enforcement.d.ts +80 -0
- package/dist/core/enforcement.js +271 -0
- package/dist/core/legacy-templates.d.ts +24 -0
- package/dist/core/legacy-templates.js +60 -0
- package/dist/core/lock-manager.d.ts +54 -0
- package/dist/core/lock-manager.js +260 -0
- package/dist/core/root-resolver.d.ts +32 -0
- package/dist/core/root-resolver.js +149 -0
- package/dist/core/setup-assets.d.ts +123 -0
- package/dist/core/setup-assets.js +438 -0
- package/dist/core/telemetry.d.ts +156 -0
- package/dist/core/telemetry.js +416 -0
- package/dist/core/test-generator-types.d.ts +227 -0
- package/dist/core/test-generator-types.js +179 -0
- package/dist/core/test-generator.d.ts +34 -0
- package/dist/core/test-generator.js +1690 -0
- package/dist/core/tier1-liaison.d.ts +139 -0
- package/dist/core/tier1-liaison.js +320 -0
- package/dist/core/verifier.d.ts +52 -0
- package/dist/core/verifier.js +249 -0
- package/dist/core/version.d.ts +5 -0
- package/dist/core/version.js +14 -0
- package/dist/core/worktree-merge.d.ts +30 -0
- package/dist/core/worktree-merge.js +98 -0
- package/dist/db/data-engine.d.ts +46 -0
- package/dist/db/data-engine.js +802 -0
- package/dist/db/diff.d.ts +39 -0
- package/dist/db/diff.js +420 -0
- package/dist/db/env-parser.d.ts +86 -0
- package/dist/db/env-parser.js +549 -0
- package/dist/db/introspector.d.ts +21 -0
- package/dist/db/introspector.js +835 -0
- package/dist/db/types.d.ts +217 -0
- package/dist/db/types.js +9 -0
- package/dist/governor/circuit-breaker.d.ts +100 -0
- package/dist/governor/circuit-breaker.js +231 -0
- package/dist/governor/evaluator.d.ts +67 -0
- package/dist/governor/evaluator.js +851 -0
- package/dist/governor/index.d.ts +6 -0
- package/dist/governor/index.js +6 -0
- package/dist/governor/rules/api-rules.d.ts +2 -0
- package/dist/governor/rules/api-rules.js +140 -0
- package/dist/governor/rules/db-rules.d.ts +2 -0
- package/dist/governor/rules/db-rules.js +180 -0
- package/dist/governor/store.d.ts +33 -0
- package/dist/governor/store.js +84 -0
- package/dist/governor/types.d.ts +64 -0
- package/dist/governor/types.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +358 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +309 -0
- package/dist/providers/claude-api.d.ts +5 -0
- package/dist/providers/claude-api.js +75 -0
- package/dist/providers/claude-cli.d.ts +13 -0
- package/dist/providers/claude-cli.js +134 -0
- package/dist/providers/gemini.d.ts +8 -0
- package/dist/providers/gemini.js +78 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/registry.d.ts +38 -0
- package/dist/providers/registry.js +121 -0
- package/dist/providers/types.d.ts +74 -0
- package/dist/providers/types.js +25 -0
- package/dist/runner/agent-supervisor.d.ts +363 -0
- package/dist/runner/agent-supervisor.js +1672 -0
- package/dist/scanner/context-builder.d.ts +2 -0
- package/dist/scanner/context-builder.js +29 -0
- package/dist/scanner/detector.d.ts +2 -0
- package/dist/scanner/detector.js +760 -0
- package/dist/scanner/types.d.ts +80 -0
- package/dist/scanner/types.js +50 -0
- package/dist/server/studio-server.d.ts +39 -0
- package/dist/server/studio-server.js +1931 -0
- package/dist/server/studio-ui.d.ts +15 -0
- package/dist/server/studio-ui.js +5321 -0
- package/package.json +70 -0
- package/plugin/.claude-plugin/plugin.json +18 -0
- package/plugin/.mcp.json +10 -0
- package/plugin/agents/architect.md +28 -0
- package/plugin/agents/verifier.md +28 -0
- package/plugin/agents/worker.md +26 -0
- package/plugin/hooks/hooks.json +28 -0
- package/plugin/skills/nativ/SKILL.md +75 -0
- package/templates/.claudeignore +45 -0
- package/templates/AGENTS.md +67 -0
- package/templates/CLAUDE.md +32 -0
- package/templates/GEMINI.md +66 -0
- package/templates/claude-agents/architect.md +28 -0
- package/templates/claude-agents/verifier.md +28 -0
- package/templates/claude-agents/worker.md +26 -0
- package/templates/dot-ai/api_contracts.json +66 -0
- package/templates/dot-ai/context.md +38 -0
- package/templates/dot-ai/db_schema.json +39 -0
- package/templates/dot-ai/master_plan.json +28 -0
- package/templates/dot-ai/subagents/backend.md +11 -0
- package/templates/dot-ai/subagents/database.md +10 -0
- package/templates/dot-ai/subagents/db-migration.md +11 -0
- package/templates/dot-ai/subagents/devops-agent.md +11 -0
- package/templates/dot-ai/subagents/flutter-developer.md +11 -0
- package/templates/dot-ai/subagents/frontend.md +126 -0
- package/templates/dot-ai/subagents/qa-tester.md +13 -0
- package/templates/dot-ai/subagents/security-auditor.md +11 -0
- package/templates/dot-ai/ui_specs.md +78 -0
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function detectProject(targetDir) {
|
|
4
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
5
|
+
const baseName = path.basename(resolvedTarget);
|
|
6
|
+
let projectName = baseName;
|
|
7
|
+
let projectType = 'Generic Application';
|
|
8
|
+
let projectGoal = 'Awaiting specification from the architect intake interview';
|
|
9
|
+
let runtime = 'Unknown / Greenfield';
|
|
10
|
+
let framework = 'None detected';
|
|
11
|
+
let databaseOrm = 'None detected';
|
|
12
|
+
let styling = 'None detected';
|
|
13
|
+
let testing = 'None detected';
|
|
14
|
+
let packageManager = 'None detected';
|
|
15
|
+
let sourceRoot = '.';
|
|
16
|
+
let architecturePattern = 'Modular / Layered';
|
|
17
|
+
let verificationCommand = 'npm test';
|
|
18
|
+
let repositoryType = 'Standard Monolith';
|
|
19
|
+
let detectedOrmConfig = '';
|
|
20
|
+
const monorepoWorkspaces = [];
|
|
21
|
+
const ecosystemManifests = [];
|
|
22
|
+
const keyDirectories = [];
|
|
23
|
+
// Helper safely reading file content
|
|
24
|
+
const readFileSafe = (filePath) => {
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(filePath)) {
|
|
27
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// ignore
|
|
32
|
+
}
|
|
33
|
+
return '';
|
|
34
|
+
};
|
|
35
|
+
// Helper checking existence
|
|
36
|
+
const fileExists = (relPath) => {
|
|
37
|
+
return fs.existsSync(path.join(resolvedTarget, relPath));
|
|
38
|
+
};
|
|
39
|
+
// -------------------------------------------------------------
|
|
40
|
+
// 1. Structure & Workspace Detection (Monorepo / Multi-service)
|
|
41
|
+
// -------------------------------------------------------------
|
|
42
|
+
const monorepoIndicators = [];
|
|
43
|
+
if (fileExists('pnpm-workspace.yaml')) {
|
|
44
|
+
ecosystemManifests.push('pnpm-workspace.yaml');
|
|
45
|
+
monorepoIndicators.push('pnpm workspaces');
|
|
46
|
+
const pnpmWorkspace = readFileSafe(path.join(resolvedTarget, 'pnpm-workspace.yaml'));
|
|
47
|
+
const packageLines = pnpmWorkspace
|
|
48
|
+
.split('\n')
|
|
49
|
+
.map((l) => l.trim())
|
|
50
|
+
.filter((l) => l.startsWith('-'))
|
|
51
|
+
.map((l) => l.replace(/^-\s*['"]?/, '').replace(/['"]?$/, '').trim());
|
|
52
|
+
if (packageLines.length > 0) {
|
|
53
|
+
monorepoWorkspaces.push(...packageLines);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (fileExists('turbo.json')) {
|
|
57
|
+
ecosystemManifests.push('turbo.json');
|
|
58
|
+
monorepoIndicators.push('Turborepo');
|
|
59
|
+
}
|
|
60
|
+
if (fileExists('nx.json')) {
|
|
61
|
+
ecosystemManifests.push('nx.json');
|
|
62
|
+
monorepoIndicators.push('Nx');
|
|
63
|
+
}
|
|
64
|
+
if (fileExists('lerna.json')) {
|
|
65
|
+
ecosystemManifests.push('lerna.json');
|
|
66
|
+
monorepoIndicators.push('Lerna');
|
|
67
|
+
}
|
|
68
|
+
// Check subdirectories: apps, packages, services, libs, modules
|
|
69
|
+
const candidateWorkspaceDirs = ['apps', 'packages', 'services', 'libs', 'modules'];
|
|
70
|
+
let detectedSubpackagesCount = 0;
|
|
71
|
+
for (const wDir of candidateWorkspaceDirs) {
|
|
72
|
+
const fullWDir = path.join(resolvedTarget, wDir);
|
|
73
|
+
if (fs.existsSync(fullWDir) && fs.statSync(fullWDir).isDirectory()) {
|
|
74
|
+
keyDirectories.push(wDir);
|
|
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
|
+
// -------------------------------------------------------------
|
|
677
|
+
// 4. Resolve Repository Type & Workspaces
|
|
678
|
+
// -------------------------------------------------------------
|
|
679
|
+
if (monorepoIndicators.length > 0 || detectedSubpackagesCount > 0) {
|
|
680
|
+
const toolLabel = monorepoIndicators.length > 0 ? monorepoIndicators.join(' + ') : 'Directory Workspaces';
|
|
681
|
+
repositoryType = `Monorepo (${toolLabel})`;
|
|
682
|
+
if (detectedSubpackagesCount > 0) {
|
|
683
|
+
architecturePattern = 'Monorepo / Multi-Package Modular';
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
else if (ecosystemManifests.length === 0 && independentServices.length > 0) {
|
|
687
|
+
// Polyrepo / Multi-Service with no root manifest
|
|
688
|
+
repositoryType = 'Polyrepo / Multi-Service Workspace';
|
|
689
|
+
projectType = 'Multi-Service Application Suite';
|
|
690
|
+
architecturePattern = 'Polyrepo (Independent Sub-services)';
|
|
691
|
+
const serviceRuntimes = [...new Set(independentServices.map((s) => s.runtime).filter(Boolean))];
|
|
692
|
+
runtime = serviceRuntimes.length > 0 ? serviceRuntimes.join(' + ') : 'Polyglot / Multi-Runtime';
|
|
693
|
+
const serviceSummaries = independentServices.map((s) => `${s.name} (${s.tech})`);
|
|
694
|
+
framework = `Services detected: ${serviceSummaries.join(' | ')}`;
|
|
695
|
+
const serviceOrms = [...new Set(independentServices.map((s) => s.orm).filter(Boolean))];
|
|
696
|
+
if (serviceOrms.length > 0) {
|
|
697
|
+
databaseOrm = serviceOrms.join(' + ');
|
|
698
|
+
}
|
|
699
|
+
const serviceConfigs = independentServices.map((s) => s.ormConfig).filter(Boolean);
|
|
700
|
+
if (serviceConfigs.length > 0 && !detectedOrmConfig) {
|
|
701
|
+
detectedOrmConfig = serviceConfigs.join(', ');
|
|
702
|
+
}
|
|
703
|
+
const allSubManifests = independentServices.flatMap((s) => s.manifests.map((m) => `${s.name}/${m}`));
|
|
704
|
+
ecosystemManifests.push(...allSubManifests);
|
|
705
|
+
verificationCommand = independentServices.map((s) => `cd ${s.name} && test`).join('; ');
|
|
706
|
+
}
|
|
707
|
+
else if (ecosystemManifests.length > 0) {
|
|
708
|
+
repositoryType = 'Standard Monolith';
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
repositoryType = 'Generic Application (Greenfield)';
|
|
712
|
+
}
|
|
713
|
+
// -------------------------------------------------------------
|
|
714
|
+
// 5. Check Key Directories & Source Root
|
|
715
|
+
// -------------------------------------------------------------
|
|
716
|
+
const candidateDirs = [
|
|
717
|
+
'src',
|
|
718
|
+
'app',
|
|
719
|
+
'pages',
|
|
720
|
+
'components',
|
|
721
|
+
'lib',
|
|
722
|
+
'api',
|
|
723
|
+
'controllers',
|
|
724
|
+
'routes',
|
|
725
|
+
'models',
|
|
726
|
+
'tests',
|
|
727
|
+
'test',
|
|
728
|
+
'public',
|
|
729
|
+
'prisma',
|
|
730
|
+
'templates',
|
|
731
|
+
'bin',
|
|
732
|
+
];
|
|
733
|
+
for (const dir of candidateDirs) {
|
|
734
|
+
if (fileExists(dir) && !keyDirectories.includes(dir)) {
|
|
735
|
+
keyDirectories.push(dir);
|
|
736
|
+
if (dir === 'src' || dir === 'app') {
|
|
737
|
+
sourceRoot = `./${dir}`;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
projectName,
|
|
743
|
+
projectType,
|
|
744
|
+
projectGoal,
|
|
745
|
+
runtime,
|
|
746
|
+
framework,
|
|
747
|
+
databaseOrm,
|
|
748
|
+
styling,
|
|
749
|
+
testing,
|
|
750
|
+
packageManager,
|
|
751
|
+
sourceRoot,
|
|
752
|
+
architecturePattern,
|
|
753
|
+
keyDirectories,
|
|
754
|
+
verificationCommand,
|
|
755
|
+
repositoryType,
|
|
756
|
+
monorepoWorkspaces,
|
|
757
|
+
detectedOrmConfig,
|
|
758
|
+
ecosystemManifests,
|
|
759
|
+
};
|
|
760
|
+
}
|