proagents 1.0.11 → 1.0.13
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/bin/proagents.js +0 -0
- package/lib/commands/ai.js +78 -9
- package/lib/commands/init.js +573 -9
- package/package.json +1 -1
- package/proagents/.cursorrules +20 -60
- package/proagents/.github/copilot-instructions.md +18 -56
- package/proagents/.windsurfrules +18 -61
- package/proagents/ANTIGRAVITY.md +19 -98
- package/proagents/BOLT.md +20 -76
- package/proagents/CHATGPT.md +15 -95
- package/proagents/CLAUDE.md +16 -70
- package/proagents/GEMINI.md +19 -90
- package/proagents/GROQ.md +18 -91
- package/proagents/KIRO.md +15 -88
- package/proagents/LOVABLE.md +17 -97
- package/proagents/REPLIT.md +18 -95
package/lib/commands/init.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync, readdirSync, rmSync } from 'fs';
|
|
2
|
-
import { join, dirname } from 'path';
|
|
2
|
+
import { join, dirname, basename } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
+
import { createInterface } from 'readline';
|
|
4
5
|
import chalk from 'chalk';
|
|
5
6
|
import yaml from 'js-yaml';
|
|
6
7
|
import { selectPlatforms, copyPlatformFiles, savePlatformConfig, loadPlatformConfig } from './ai.js';
|
|
@@ -90,6 +91,559 @@ const FRAMEWORK_FILES = [
|
|
|
90
91
|
'AI_INSTRUCTIONS.md', // Universal instructions kept for reference
|
|
91
92
|
];
|
|
92
93
|
|
|
94
|
+
// Project type definitions for detection
|
|
95
|
+
const PROJECT_TYPES = [
|
|
96
|
+
{
|
|
97
|
+
id: 'nextjs',
|
|
98
|
+
name: 'Next.js (Full-stack)',
|
|
99
|
+
detect: {
|
|
100
|
+
files: ['next.config.js', 'next.config.mjs', 'next.config.ts'],
|
|
101
|
+
deps: ['next']
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 'react',
|
|
106
|
+
name: 'React (Frontend)',
|
|
107
|
+
detect: {
|
|
108
|
+
deps: ['react', 'react-dom'],
|
|
109
|
+
notDeps: ['next', 'react-native']
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'vue',
|
|
114
|
+
name: 'Vue.js (Frontend)',
|
|
115
|
+
detect: {
|
|
116
|
+
files: ['vue.config.js', 'vite.config.ts', 'vite.config.js'],
|
|
117
|
+
deps: ['vue']
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'angular',
|
|
122
|
+
name: 'Angular (Frontend)',
|
|
123
|
+
detect: {
|
|
124
|
+
files: ['angular.json'],
|
|
125
|
+
deps: ['@angular/core']
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'react-native',
|
|
130
|
+
name: 'React Native (Mobile)',
|
|
131
|
+
detect: {
|
|
132
|
+
deps: ['react-native']
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'express',
|
|
137
|
+
name: 'Express.js (Backend)',
|
|
138
|
+
detect: {
|
|
139
|
+
deps: ['express']
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'nestjs',
|
|
144
|
+
name: 'NestJS (Backend)',
|
|
145
|
+
detect: {
|
|
146
|
+
files: ['nest-cli.json'],
|
|
147
|
+
deps: ['@nestjs/core']
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: 'fastify',
|
|
152
|
+
name: 'Fastify (Backend)',
|
|
153
|
+
detect: {
|
|
154
|
+
deps: ['fastify']
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'nodejs',
|
|
159
|
+
name: 'Node.js (Backend)',
|
|
160
|
+
detect: {
|
|
161
|
+
files: ['package.json'],
|
|
162
|
+
notDeps: ['react', 'vue', '@angular/core', 'next']
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'python',
|
|
167
|
+
name: 'Python',
|
|
168
|
+
detect: {
|
|
169
|
+
files: ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile']
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: 'django',
|
|
174
|
+
name: 'Django (Python)',
|
|
175
|
+
detect: {
|
|
176
|
+
files: ['manage.py'],
|
|
177
|
+
patterns: ['django']
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: 'flask',
|
|
182
|
+
name: 'Flask (Python)',
|
|
183
|
+
detect: {
|
|
184
|
+
patterns: ['flask']
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: 'other',
|
|
189
|
+
name: 'Other / Custom',
|
|
190
|
+
detect: {}
|
|
191
|
+
}
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// Tech stack configuration options
|
|
195
|
+
const TECH_STACK_OPTIONS = {
|
|
196
|
+
api_style: {
|
|
197
|
+
label: 'API Style',
|
|
198
|
+
options: [
|
|
199
|
+
{ id: 'rest', name: 'REST' },
|
|
200
|
+
{ id: 'graphql', name: 'GraphQL' },
|
|
201
|
+
{ id: 'grpc', name: 'gRPC' },
|
|
202
|
+
{ id: 'trpc', name: 'tRPC' }
|
|
203
|
+
],
|
|
204
|
+
detect: {
|
|
205
|
+
'graphql': ['@apollo/client', 'graphql', 'apollo-server', 'urql'],
|
|
206
|
+
'grpc': ['@grpc/grpc-js', 'grpc'],
|
|
207
|
+
'trpc': ['@trpc/client', '@trpc/server']
|
|
208
|
+
},
|
|
209
|
+
default: 'rest'
|
|
210
|
+
},
|
|
211
|
+
state_management: {
|
|
212
|
+
label: 'State Management',
|
|
213
|
+
options: [
|
|
214
|
+
{ id: 'zustand', name: 'Zustand' },
|
|
215
|
+
{ id: 'redux', name: 'Redux' },
|
|
216
|
+
{ id: 'jotai', name: 'Jotai' },
|
|
217
|
+
{ id: 'recoil', name: 'Recoil' },
|
|
218
|
+
{ id: 'mobx', name: 'MobX' },
|
|
219
|
+
{ id: 'context', name: 'React Context' },
|
|
220
|
+
{ id: 'none', name: 'None / Other' }
|
|
221
|
+
],
|
|
222
|
+
detect: {
|
|
223
|
+
'zustand': ['zustand'],
|
|
224
|
+
'redux': ['redux', '@reduxjs/toolkit', 'react-redux'],
|
|
225
|
+
'jotai': ['jotai'],
|
|
226
|
+
'recoil': ['recoil'],
|
|
227
|
+
'mobx': ['mobx', 'mobx-react']
|
|
228
|
+
},
|
|
229
|
+
default: 'zustand'
|
|
230
|
+
},
|
|
231
|
+
styling: {
|
|
232
|
+
label: 'Styling',
|
|
233
|
+
options: [
|
|
234
|
+
{ id: 'tailwind', name: 'Tailwind CSS' },
|
|
235
|
+
{ id: 'css-modules', name: 'CSS Modules' },
|
|
236
|
+
{ id: 'styled-components', name: 'Styled Components' },
|
|
237
|
+
{ id: 'emotion', name: 'Emotion' },
|
|
238
|
+
{ id: 'sass', name: 'Sass/SCSS' },
|
|
239
|
+
{ id: 'css', name: 'Plain CSS' }
|
|
240
|
+
],
|
|
241
|
+
detect: {
|
|
242
|
+
'tailwind': ['tailwindcss'],
|
|
243
|
+
'styled-components': ['styled-components'],
|
|
244
|
+
'emotion': ['@emotion/react', '@emotion/styled'],
|
|
245
|
+
'sass': ['sass', 'node-sass']
|
|
246
|
+
},
|
|
247
|
+
default: 'tailwind'
|
|
248
|
+
},
|
|
249
|
+
database: {
|
|
250
|
+
label: 'Database',
|
|
251
|
+
options: [
|
|
252
|
+
{ id: 'postgresql', name: 'PostgreSQL' },
|
|
253
|
+
{ id: 'mysql', name: 'MySQL' },
|
|
254
|
+
{ id: 'mongodb', name: 'MongoDB' },
|
|
255
|
+
{ id: 'sqlite', name: 'SQLite' },
|
|
256
|
+
{ id: 'supabase', name: 'Supabase' },
|
|
257
|
+
{ id: 'firebase', name: 'Firebase' },
|
|
258
|
+
{ id: 'none', name: 'None / Other' }
|
|
259
|
+
],
|
|
260
|
+
detect: {
|
|
261
|
+
'postgresql': ['pg', '@prisma/client', 'postgres'],
|
|
262
|
+
'mysql': ['mysql', 'mysql2'],
|
|
263
|
+
'mongodb': ['mongoose', 'mongodb'],
|
|
264
|
+
'sqlite': ['better-sqlite3', 'sqlite3'],
|
|
265
|
+
'supabase': ['@supabase/supabase-js'],
|
|
266
|
+
'firebase': ['firebase', 'firebase-admin']
|
|
267
|
+
},
|
|
268
|
+
default: 'postgresql'
|
|
269
|
+
},
|
|
270
|
+
orm: {
|
|
271
|
+
label: 'ORM',
|
|
272
|
+
options: [
|
|
273
|
+
{ id: 'prisma', name: 'Prisma' },
|
|
274
|
+
{ id: 'drizzle', name: 'Drizzle' },
|
|
275
|
+
{ id: 'typeorm', name: 'TypeORM' },
|
|
276
|
+
{ id: 'sequelize', name: 'Sequelize' },
|
|
277
|
+
{ id: 'mongoose', name: 'Mongoose' },
|
|
278
|
+
{ id: 'none', name: 'None / Raw SQL' }
|
|
279
|
+
],
|
|
280
|
+
detect: {
|
|
281
|
+
'prisma': ['@prisma/client', 'prisma'],
|
|
282
|
+
'drizzle': ['drizzle-orm'],
|
|
283
|
+
'typeorm': ['typeorm'],
|
|
284
|
+
'sequelize': ['sequelize'],
|
|
285
|
+
'mongoose': ['mongoose']
|
|
286
|
+
},
|
|
287
|
+
default: 'prisma'
|
|
288
|
+
},
|
|
289
|
+
auth_method: {
|
|
290
|
+
label: 'Authentication',
|
|
291
|
+
options: [
|
|
292
|
+
{ id: 'jwt', name: 'JWT' },
|
|
293
|
+
{ id: 'session', name: 'Session-based' },
|
|
294
|
+
{ id: 'oauth', name: 'OAuth' },
|
|
295
|
+
{ id: 'nextauth', name: 'NextAuth.js' },
|
|
296
|
+
{ id: 'clerk', name: 'Clerk' },
|
|
297
|
+
{ id: 'auth0', name: 'Auth0' },
|
|
298
|
+
{ id: 'supabase', name: 'Supabase Auth' },
|
|
299
|
+
{ id: 'none', name: 'None / Custom' }
|
|
300
|
+
],
|
|
301
|
+
detect: {
|
|
302
|
+
'nextauth': ['next-auth'],
|
|
303
|
+
'clerk': ['@clerk/nextjs', '@clerk/clerk-react'],
|
|
304
|
+
'auth0': ['@auth0/auth0-react', 'auth0'],
|
|
305
|
+
'supabase': ['@supabase/auth-helpers-nextjs']
|
|
306
|
+
},
|
|
307
|
+
default: 'jwt'
|
|
308
|
+
},
|
|
309
|
+
test_framework: {
|
|
310
|
+
label: 'Test Framework',
|
|
311
|
+
options: [
|
|
312
|
+
{ id: 'vitest', name: 'Vitest' },
|
|
313
|
+
{ id: 'jest', name: 'Jest' },
|
|
314
|
+
{ id: 'mocha', name: 'Mocha' },
|
|
315
|
+
{ id: 'playwright', name: 'Playwright' },
|
|
316
|
+
{ id: 'cypress', name: 'Cypress' },
|
|
317
|
+
{ id: 'pytest', name: 'Pytest (Python)' },
|
|
318
|
+
{ id: 'none', name: 'None' }
|
|
319
|
+
],
|
|
320
|
+
detect: {
|
|
321
|
+
'vitest': ['vitest'],
|
|
322
|
+
'jest': ['jest'],
|
|
323
|
+
'mocha': ['mocha'],
|
|
324
|
+
'playwright': ['@playwright/test', 'playwright'],
|
|
325
|
+
'cypress': ['cypress'],
|
|
326
|
+
'pytest': ['pytest']
|
|
327
|
+
},
|
|
328
|
+
default: 'vitest'
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Detect tech stack from dependencies
|
|
334
|
+
*/
|
|
335
|
+
function detectTechStack(targetDir) {
|
|
336
|
+
const { allDeps = [] } = getPackageDeps(targetDir);
|
|
337
|
+
const pythonDeps = getPythonDeps(targetDir);
|
|
338
|
+
const allProjectDeps = [...allDeps, ...pythonDeps.map(d => d.toLowerCase())];
|
|
339
|
+
|
|
340
|
+
const detected = {};
|
|
341
|
+
|
|
342
|
+
for (const [key, config] of Object.entries(TECH_STACK_OPTIONS)) {
|
|
343
|
+
detected[key] = null;
|
|
344
|
+
|
|
345
|
+
if (config.detect) {
|
|
346
|
+
for (const [optionId, detectDeps] of Object.entries(config.detect)) {
|
|
347
|
+
const hasMatch = detectDeps.some(dep => allProjectDeps.includes(dep));
|
|
348
|
+
if (hasMatch) {
|
|
349
|
+
detected[key] = optionId;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Use default if not detected
|
|
356
|
+
if (!detected[key]) {
|
|
357
|
+
detected[key] = config.default;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return detected;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Detect project name from package.json or folder name
|
|
366
|
+
*/
|
|
367
|
+
function detectProjectName(targetDir) {
|
|
368
|
+
const packageJsonPath = join(targetDir, 'package.json');
|
|
369
|
+
|
|
370
|
+
if (existsSync(packageJsonPath)) {
|
|
371
|
+
try {
|
|
372
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
373
|
+
if (packageJson.name) {
|
|
374
|
+
return packageJson.name;
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
// Fall through to folder name
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Fallback to folder name
|
|
382
|
+
return basename(targetDir);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Read package.json dependencies
|
|
387
|
+
*/
|
|
388
|
+
function getPackageDeps(targetDir) {
|
|
389
|
+
const packageJsonPath = join(targetDir, 'package.json');
|
|
390
|
+
|
|
391
|
+
if (!existsSync(packageJsonPath)) {
|
|
392
|
+
return { deps: [], devDeps: [] };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
397
|
+
const deps = Object.keys(packageJson.dependencies || {});
|
|
398
|
+
const devDeps = Object.keys(packageJson.devDependencies || {});
|
|
399
|
+
return { deps, devDeps, allDeps: [...deps, ...devDeps] };
|
|
400
|
+
} catch {
|
|
401
|
+
return { deps: [], devDeps: [], allDeps: [] };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Read requirements.txt for Python projects
|
|
407
|
+
*/
|
|
408
|
+
function getPythonDeps(targetDir) {
|
|
409
|
+
const requirementsPath = join(targetDir, 'requirements.txt');
|
|
410
|
+
|
|
411
|
+
if (!existsSync(requirementsPath)) {
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
const content = readFileSync(requirementsPath, 'utf-8');
|
|
417
|
+
return content.split('\n')
|
|
418
|
+
.map(line => line.trim().split('==')[0].split('>=')[0].split('<=')[0])
|
|
419
|
+
.filter(Boolean);
|
|
420
|
+
} catch {
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Detect project type based on files and dependencies
|
|
427
|
+
*/
|
|
428
|
+
function detectProjectType(targetDir) {
|
|
429
|
+
const { allDeps = [] } = getPackageDeps(targetDir);
|
|
430
|
+
const pythonDeps = getPythonDeps(targetDir);
|
|
431
|
+
const allProjectDeps = [...allDeps, ...pythonDeps.map(d => d.toLowerCase())];
|
|
432
|
+
|
|
433
|
+
const detectedTypes = [];
|
|
434
|
+
|
|
435
|
+
for (const projectType of PROJECT_TYPES) {
|
|
436
|
+
if (projectType.id === 'other') continue;
|
|
437
|
+
|
|
438
|
+
const { detect } = projectType;
|
|
439
|
+
let matches = true;
|
|
440
|
+
let score = 0;
|
|
441
|
+
|
|
442
|
+
// Check for required files
|
|
443
|
+
if (detect.files) {
|
|
444
|
+
const hasFile = detect.files.some(file => existsSync(join(targetDir, file)));
|
|
445
|
+
if (hasFile) {
|
|
446
|
+
score += 10;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Check for required dependencies
|
|
451
|
+
if (detect.deps) {
|
|
452
|
+
const hasDep = detect.deps.some(dep => allProjectDeps.includes(dep));
|
|
453
|
+
if (hasDep) {
|
|
454
|
+
score += 5;
|
|
455
|
+
} else if (detect.deps.length > 0 && !detect.files) {
|
|
456
|
+
matches = false;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Check for excluded dependencies
|
|
461
|
+
if (detect.notDeps && matches) {
|
|
462
|
+
const hasExcluded = detect.notDeps.some(dep => allProjectDeps.includes(dep));
|
|
463
|
+
if (hasExcluded) {
|
|
464
|
+
matches = false;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Check for patterns in requirements.txt
|
|
469
|
+
if (detect.patterns && pythonDeps.length > 0) {
|
|
470
|
+
const hasPattern = detect.patterns.some(pattern =>
|
|
471
|
+
pythonDeps.some(dep => dep.toLowerCase().includes(pattern))
|
|
472
|
+
);
|
|
473
|
+
if (hasPattern) {
|
|
474
|
+
score += 5;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (matches && score > 0) {
|
|
479
|
+
detectedTypes.push({ ...projectType, score });
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Sort by score descending
|
|
484
|
+
detectedTypes.sort((a, b) => b.score - a.score);
|
|
485
|
+
|
|
486
|
+
return detectedTypes;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Interactive prompt for project configuration
|
|
491
|
+
*/
|
|
492
|
+
async function promptProjectConfig(targetDir) {
|
|
493
|
+
const rl = createInterface({
|
|
494
|
+
input: process.stdin,
|
|
495
|
+
output: process.stdout
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
|
|
499
|
+
|
|
500
|
+
// Detect project name
|
|
501
|
+
const detectedName = detectProjectName(targetDir);
|
|
502
|
+
|
|
503
|
+
// Detect project types
|
|
504
|
+
const detectedTypes = detectProjectType(targetDir);
|
|
505
|
+
const topDetectedType = detectedTypes.length > 0 ? detectedTypes[0] : null;
|
|
506
|
+
|
|
507
|
+
// Detect tech stack
|
|
508
|
+
const detectedTechStack = detectTechStack(targetDir);
|
|
509
|
+
|
|
510
|
+
console.log(chalk.bold('\nProject Configuration'));
|
|
511
|
+
console.log(chalk.gray('─'.repeat(40) + '\n'));
|
|
512
|
+
|
|
513
|
+
// Project Name
|
|
514
|
+
console.log(chalk.cyan('Project Name'));
|
|
515
|
+
if (detectedName) {
|
|
516
|
+
console.log(chalk.gray(` Detected: ${detectedName}`));
|
|
517
|
+
}
|
|
518
|
+
const nameInput = await question(chalk.yellow(` Enter name (press Enter for "${detectedName}"): `));
|
|
519
|
+
const projectName = nameInput.trim() || detectedName;
|
|
520
|
+
|
|
521
|
+
console.log('');
|
|
522
|
+
|
|
523
|
+
// Project Type
|
|
524
|
+
console.log(chalk.cyan('Project Type'));
|
|
525
|
+
if (topDetectedType) {
|
|
526
|
+
console.log(chalk.green(` Detected: ${topDetectedType.name}`));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
console.log(chalk.gray('\n Available types:'));
|
|
530
|
+
PROJECT_TYPES.forEach((type, index) => {
|
|
531
|
+
const isDetected = topDetectedType && type.id === topDetectedType.id;
|
|
532
|
+
const marker = isDetected ? chalk.green(' ✓ (detected)') : '';
|
|
533
|
+
console.log(chalk.white(` ${index + 1}. ${type.name}`) + marker);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
const defaultTypeIndex = topDetectedType
|
|
537
|
+
? PROJECT_TYPES.findIndex(t => t.id === topDetectedType.id) + 1
|
|
538
|
+
: PROJECT_TYPES.length;
|
|
539
|
+
|
|
540
|
+
const typeInput = await question(chalk.yellow(`\n Enter number (press Enter for ${defaultTypeIndex}): `));
|
|
541
|
+
const typeIndex = parseInt(typeInput.trim()) || defaultTypeIndex;
|
|
542
|
+
const projectType = PROJECT_TYPES[Math.min(Math.max(typeIndex - 1, 0), PROJECT_TYPES.length - 1)];
|
|
543
|
+
|
|
544
|
+
console.log('');
|
|
545
|
+
console.log(chalk.green(`✓ Project: ${projectName} (${projectType.name})`));
|
|
546
|
+
|
|
547
|
+
// Tech Stack Configuration
|
|
548
|
+
console.log(chalk.bold('\nTech Stack Configuration'));
|
|
549
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
550
|
+
console.log(chalk.gray('Press Enter to accept detected/default values\n'));
|
|
551
|
+
|
|
552
|
+
const techStack = {};
|
|
553
|
+
|
|
554
|
+
for (const [key, config] of Object.entries(TECH_STACK_OPTIONS)) {
|
|
555
|
+
const detected = detectedTechStack[key];
|
|
556
|
+
const detectedOption = config.options.find(o => o.id === detected);
|
|
557
|
+
const detectedName = detectedOption ? detectedOption.name : config.default;
|
|
558
|
+
|
|
559
|
+
console.log(chalk.cyan(config.label));
|
|
560
|
+
config.options.forEach((option, index) => {
|
|
561
|
+
const isDetected = option.id === detected;
|
|
562
|
+
const marker = isDetected ? chalk.green(' ✓') : '';
|
|
563
|
+
console.log(chalk.white(` ${index + 1}. ${option.name}`) + marker);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
const defaultIndex = config.options.findIndex(o => o.id === detected) + 1 || 1;
|
|
567
|
+
const input = await question(chalk.yellow(` Select (Enter for ${defaultIndex}): `));
|
|
568
|
+
const selectedIndex = parseInt(input.trim()) || defaultIndex;
|
|
569
|
+
const selectedOption = config.options[Math.min(Math.max(selectedIndex - 1, 0), config.options.length - 1)];
|
|
570
|
+
techStack[key] = selectedOption.id;
|
|
571
|
+
console.log('');
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
rl.close();
|
|
575
|
+
|
|
576
|
+
// Summary
|
|
577
|
+
console.log(chalk.bold('\nConfiguration Summary'));
|
|
578
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
579
|
+
console.log(chalk.white(` Project: ${projectName} (${projectType.name})`));
|
|
580
|
+
for (const [key, value] of Object.entries(techStack)) {
|
|
581
|
+
const config = TECH_STACK_OPTIONS[key];
|
|
582
|
+
const option = config.options.find(o => o.id === value);
|
|
583
|
+
console.log(chalk.white(` ${config.label}: ${option ? option.name : value}`));
|
|
584
|
+
}
|
|
585
|
+
console.log('');
|
|
586
|
+
|
|
587
|
+
return {
|
|
588
|
+
name: projectName,
|
|
589
|
+
type: projectType.id,
|
|
590
|
+
typeName: projectType.name,
|
|
591
|
+
techStack
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Save project config to proagents.config.yaml
|
|
597
|
+
*/
|
|
598
|
+
function saveProjectConfig(projectConfig, configPath) {
|
|
599
|
+
let config = {};
|
|
600
|
+
|
|
601
|
+
if (existsSync(configPath)) {
|
|
602
|
+
try {
|
|
603
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
604
|
+
config = yaml.load(content) || {};
|
|
605
|
+
} catch {
|
|
606
|
+
config = {};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Save project info
|
|
611
|
+
config.project = {
|
|
612
|
+
name: projectConfig.name,
|
|
613
|
+
type: projectConfig.type
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// Save tech stack under automation.decisions
|
|
617
|
+
if (projectConfig.techStack) {
|
|
618
|
+
if (!config.automation) {
|
|
619
|
+
config.automation = {};
|
|
620
|
+
}
|
|
621
|
+
if (!config.automation.decisions) {
|
|
622
|
+
config.automation.decisions = {};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Architecture decisions
|
|
626
|
+
config.automation.decisions.architecture = {
|
|
627
|
+
api_style: projectConfig.techStack.api_style,
|
|
628
|
+
state_management: projectConfig.techStack.state_management,
|
|
629
|
+
styling: projectConfig.techStack.styling,
|
|
630
|
+
database: projectConfig.techStack.database,
|
|
631
|
+
orm: projectConfig.techStack.orm,
|
|
632
|
+
auth_method: projectConfig.techStack.auth_method
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
// Testing decisions
|
|
636
|
+
config.automation.decisions.testing = {
|
|
637
|
+
framework: projectConfig.techStack.test_framework,
|
|
638
|
+
coverage_target: 80,
|
|
639
|
+
location: 'colocated'
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const yamlContent = yaml.dump(config, { indent: 2, lineWidth: 120 });
|
|
644
|
+
writeFileSync(configPath, yamlContent);
|
|
645
|
+
}
|
|
646
|
+
|
|
93
647
|
/**
|
|
94
648
|
* Initialize ProAgents in the current project
|
|
95
649
|
*/
|
|
@@ -223,6 +777,9 @@ No releases yet. Use \`pa:release\` to generate release notes.
|
|
|
223
777
|
console.log(chalk.green('✓ Created RELEASE_NOTES.md'));
|
|
224
778
|
}
|
|
225
779
|
|
|
780
|
+
// Interactive project configuration
|
|
781
|
+
const projectConfig = await promptProjectConfig(targetDir);
|
|
782
|
+
|
|
226
783
|
// Add ProAgents section to README.md (AI tools auto-read this)
|
|
227
784
|
const readmePath = join(targetDir, 'README.md');
|
|
228
785
|
const proagentsSection = `
|
|
@@ -251,25 +808,29 @@ For detailed commands, see \`./proagents/PROAGENTS.md\`
|
|
|
251
808
|
console.log(chalk.green('✓ Added ProAgents commands to README.md'));
|
|
252
809
|
}
|
|
253
810
|
} else {
|
|
254
|
-
writeFileSync(readmePath, proagentsSection + `#
|
|
811
|
+
writeFileSync(readmePath, proagentsSection + `# ${projectConfig.name}\n\nProject description.\n`);
|
|
255
812
|
console.log(chalk.green('✓ Created README.md with ProAgents commands'));
|
|
256
813
|
}
|
|
257
814
|
|
|
258
815
|
// Interactive AI platform selection
|
|
259
816
|
const selectedPlatforms = await selectPlatforms();
|
|
260
817
|
|
|
261
|
-
// Copy AI instruction files for selected platforms
|
|
818
|
+
// Copy AI instruction files for selected platforms (merges with existing files)
|
|
262
819
|
const aiResults = copyPlatformFiles(selectedPlatforms, sourceDir, targetDir);
|
|
263
820
|
|
|
264
821
|
if (aiResults.created.length > 0) {
|
|
265
822
|
console.log(chalk.green(`✓ Created AI files: ${aiResults.created.join(', ')}`));
|
|
266
823
|
}
|
|
267
|
-
if (aiResults.
|
|
268
|
-
console.log(chalk.
|
|
824
|
+
if (aiResults.updated.length > 0) {
|
|
825
|
+
console.log(chalk.green(`✓ Updated AI files: ${aiResults.updated.join(', ')}`));
|
|
826
|
+
}
|
|
827
|
+
if (aiResults.merged.length > 0) {
|
|
828
|
+
console.log(chalk.green(`✓ Merged with existing: ${aiResults.merged.join(', ')}`));
|
|
269
829
|
}
|
|
270
830
|
|
|
271
|
-
// Save
|
|
831
|
+
// Save project and platform config
|
|
272
832
|
const configPath = join(proagentsDir, 'proagents.config.yaml');
|
|
833
|
+
saveProjectConfig(projectConfig, configPath);
|
|
273
834
|
savePlatformConfig(selectedPlatforms, configPath);
|
|
274
835
|
|
|
275
836
|
// Success message
|
|
@@ -383,7 +944,7 @@ async function smartUpdate(sourceDir, targetDir) {
|
|
|
383
944
|
}
|
|
384
945
|
}
|
|
385
946
|
|
|
386
|
-
// Copy AI instruction files for configured platforms (
|
|
947
|
+
// Copy AI instruction files for configured platforms (merges with existing files)
|
|
387
948
|
const projectRoot = join(targetDir, '..');
|
|
388
949
|
const configPath = join(targetDir, 'proagents.config.yaml');
|
|
389
950
|
const selectedPlatforms = loadPlatformConfig(configPath);
|
|
@@ -394,8 +955,11 @@ async function smartUpdate(sourceDir, targetDir) {
|
|
|
394
955
|
if (aiResults.created.length > 0) {
|
|
395
956
|
console.log(chalk.green(`✓ Created new AI files: ${aiResults.created.join(', ')}`));
|
|
396
957
|
}
|
|
397
|
-
if (aiResults.
|
|
398
|
-
console.log(chalk.
|
|
958
|
+
if (aiResults.updated.length > 0) {
|
|
959
|
+
console.log(chalk.green(`✓ Updated AI files: ${aiResults.updated.join(', ')}`));
|
|
960
|
+
}
|
|
961
|
+
if (aiResults.merged.length > 0) {
|
|
962
|
+
console.log(chalk.green(`✓ Merged with existing: ${aiResults.merged.join(', ')}`));
|
|
399
963
|
}
|
|
400
964
|
}
|
|
401
965
|
|