popeye-cli 1.4.3 → 1.4.5
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.
|
@@ -29,7 +29,7 @@ import { runComprehensiveVerification, autoFixIssues, resolveProjectPaths } from
|
|
|
29
29
|
import { setupUI } from './ui-setup.js';
|
|
30
30
|
import { designUI, saveUISpecification, loadUISpecification } from './ui-designer.js';
|
|
31
31
|
import { iterateUntilConsensus, runOptimizedConsensusProcess, type ConsensusProcessResult } from './consensus.js';
|
|
32
|
-
import { isWorkspace } from '../types/project.js';
|
|
32
|
+
import { isWorkspace, type OutputLanguage } from '../types/project.js';
|
|
33
33
|
import { getProjectStructureSummary } from './project-structure.js';
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -167,6 +167,7 @@ async function generateProjectReadme(
|
|
|
167
167
|
): Promise<{ success: boolean; path?: string; error?: string }> {
|
|
168
168
|
try {
|
|
169
169
|
const readmePath = path.join(projectDir, 'README.md');
|
|
170
|
+
const workspace = isWorkspace(state.language);
|
|
170
171
|
|
|
171
172
|
// Extract features from completed milestones
|
|
172
173
|
const features = state.milestones
|
|
@@ -177,121 +178,562 @@ async function generateProjectReadme(
|
|
|
177
178
|
tasks: m.tasks.filter(t => t.status === 'complete').map(t => t.name),
|
|
178
179
|
}));
|
|
179
180
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
181
|
+
// Load workspace.json for workspace projects
|
|
182
|
+
let wsConfig: Record<string, unknown> | null = null;
|
|
183
|
+
if (workspace) {
|
|
184
|
+
try {
|
|
185
|
+
const wsContent = await fs.readFile(path.join(projectDir, '.popeye', 'workspace.json'), 'utf-8');
|
|
186
|
+
wsConfig = JSON.parse(wsContent);
|
|
187
|
+
} catch {
|
|
188
|
+
// workspace.json not available, use defaults
|
|
189
|
+
}
|
|
190
|
+
}
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
const description = state.specification
|
|
193
|
+
? extractDescriptionFromSpec(state.specification)
|
|
194
|
+
: 'A project generated by Popeye CLI.';
|
|
189
195
|
|
|
190
|
-
|
|
196
|
+
const sections: string[] = [];
|
|
191
197
|
|
|
192
|
-
|
|
198
|
+
// Title and description
|
|
199
|
+
sections.push(`# ${state.name}\n\n${description}`);
|
|
193
200
|
|
|
194
|
-
|
|
201
|
+
// Features
|
|
202
|
+
const featuresContent = features.map(f => `### ${f.name}
|
|
195
203
|
${f.description || ''}
|
|
196
|
-
${f.tasks.length > 0 ? f.tasks.map(t => `- ${t}`).join('\n') : ''}`).join('\n\n')
|
|
197
|
-
|
|
198
|
-
## Prerequisites
|
|
204
|
+
${f.tasks.length > 0 ? f.tasks.map(t => `- ${t}`).join('\n') : ''}`).join('\n\n');
|
|
205
|
+
sections.push(`## Features\n\n${featuresContent}`);
|
|
199
206
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
- pip (Python package manager)`}
|
|
207
|
+
// Prerequisites
|
|
208
|
+
sections.push(`## Prerequisites\n\n${generatePrerequisites(state.language)}`);
|
|
203
209
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
\`\`\`bash
|
|
207
|
-
# Clone the repository (if applicable)
|
|
208
|
-
cd ${state.name}
|
|
209
|
-
|
|
210
|
-
# Install dependencies
|
|
211
|
-
${installCmd}
|
|
212
|
-
\`\`\`
|
|
210
|
+
// Installation
|
|
211
|
+
sections.push(`## Installation\n\n${generateInstallation(state.name, state.language)}`);
|
|
213
212
|
|
|
214
|
-
|
|
213
|
+
// Environment Setup
|
|
214
|
+
sections.push(`## Environment Setup
|
|
215
215
|
|
|
216
216
|
1. Copy the example environment file:
|
|
217
217
|
\`\`\`bash
|
|
218
218
|
cp .env.example .env
|
|
219
219
|
\`\`\`
|
|
220
220
|
|
|
221
|
-
2. Edit \`.env\` and fill in the required values
|
|
221
|
+
2. Edit \`.env\` and fill in the required values.`);
|
|
222
222
|
|
|
223
|
-
|
|
223
|
+
// Running the Application
|
|
224
|
+
sections.push(`## Running the Application\n\n${generateRunSection(state.language, wsConfig)}`);
|
|
224
225
|
|
|
225
|
-
|
|
226
|
+
// Project Structure
|
|
227
|
+
sections.push(`## Project Structure\n\n${generateStructureSection(state.name, state.language)}`);
|
|
228
|
+
|
|
229
|
+
// Deployment - per-app for workspace projects
|
|
230
|
+
if (workspace) {
|
|
231
|
+
sections.push(`## Deployment\n\n${generateDeploymentSection(state.name, state.language, wsConfig)}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Development footer
|
|
235
|
+
sections.push(`## Development
|
|
236
|
+
|
|
237
|
+
This project was generated using [Popeye CLI](https://github.com/your-org/popeye-cli), an autonomous code generation tool.
|
|
238
|
+
|
|
239
|
+
### Development Plan
|
|
240
|
+
|
|
241
|
+
See [docs/PLAN.md](docs/PLAN.md) for the complete development plan used to build this project.
|
|
242
|
+
|
|
243
|
+
### Workflow Log
|
|
244
|
+
|
|
245
|
+
See [docs/WORKFLOW_LOG.md](docs/WORKFLOW_LOG.md) for detailed execution logs.
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT`);
|
|
250
|
+
|
|
251
|
+
const readmeContent = sections.join('\n\n') + '\n';
|
|
252
|
+
await fs.writeFile(readmePath, readmeContent, 'utf-8');
|
|
253
|
+
|
|
254
|
+
return { success: true, path: readmePath };
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
error: error instanceof Error ? error.message : 'Failed to generate README',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Generate prerequisites section based on language
|
|
265
|
+
*/
|
|
266
|
+
function generatePrerequisites(language: OutputLanguage): string {
|
|
267
|
+
if (language === 'python') {
|
|
268
|
+
return `- Python 3.9 or higher
|
|
269
|
+
- pip (Python package manager)`;
|
|
270
|
+
}
|
|
271
|
+
if (language === 'typescript' || language === 'website') {
|
|
272
|
+
return `- Node.js 18.0 or higher
|
|
273
|
+
- npm 8.0 or higher`;
|
|
274
|
+
}
|
|
275
|
+
// Workspace projects (fullstack, all) need both
|
|
276
|
+
return `- Node.js 18.0 or higher
|
|
277
|
+
- npm 8.0 or higher
|
|
278
|
+
- Python 3.9 or higher
|
|
279
|
+
- pip (Python package manager)
|
|
280
|
+
- Docker and Docker Compose (recommended for local development)`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Generate installation section based on language
|
|
285
|
+
*/
|
|
286
|
+
function generateInstallation(name: string, language: OutputLanguage): string {
|
|
287
|
+
const lines = [`\`\`\`bash`, `cd ${name}`, ''];
|
|
288
|
+
|
|
289
|
+
if (isWorkspace(language)) {
|
|
290
|
+
lines.push('# Install all workspace dependencies');
|
|
291
|
+
lines.push('npm install');
|
|
292
|
+
lines.push('');
|
|
293
|
+
lines.push('# Install backend Python dependencies');
|
|
294
|
+
lines.push('cd apps/backend && pip install -r requirements.txt && cd ../..');
|
|
295
|
+
if (language === 'all') {
|
|
296
|
+
lines.push('');
|
|
297
|
+
lines.push('# Install website dependencies (if not covered by workspace)');
|
|
298
|
+
lines.push('cd apps/website && npm install && cd ../..');
|
|
299
|
+
}
|
|
300
|
+
} else if (language === 'python') {
|
|
301
|
+
lines.push('pip install -r requirements.txt');
|
|
302
|
+
} else {
|
|
303
|
+
lines.push('npm install');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
lines.push('```');
|
|
307
|
+
return lines.join('\n');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Generate run commands section based on language
|
|
312
|
+
*/
|
|
313
|
+
function generateRunSection(language: OutputLanguage, wsConfig: Record<string, unknown> | null): string {
|
|
314
|
+
if (language === 'python') {
|
|
315
|
+
return `### Development Mode
|
|
226
316
|
|
|
227
317
|
\`\`\`bash
|
|
228
|
-
|
|
318
|
+
python src/main.py
|
|
229
319
|
\`\`\`
|
|
230
320
|
|
|
231
321
|
### Running Tests
|
|
232
322
|
|
|
233
323
|
\`\`\`bash
|
|
234
|
-
|
|
324
|
+
pytest tests/ -v
|
|
235
325
|
\`\`\`
|
|
236
326
|
|
|
237
327
|
### Build for Production
|
|
238
328
|
|
|
239
329
|
\`\`\`bash
|
|
240
|
-
|
|
330
|
+
python -m py_compile src/**/*.py
|
|
331
|
+
\`\`\``;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (language === 'typescript') {
|
|
335
|
+
return `### Development Mode
|
|
336
|
+
|
|
337
|
+
\`\`\`bash
|
|
338
|
+
npm run dev
|
|
339
|
+
\`\`\`
|
|
340
|
+
|
|
341
|
+
### Running Tests
|
|
342
|
+
|
|
343
|
+
\`\`\`bash
|
|
344
|
+
npm test
|
|
345
|
+
\`\`\`
|
|
346
|
+
|
|
347
|
+
### Build for Production
|
|
348
|
+
|
|
349
|
+
\`\`\`bash
|
|
350
|
+
npm run build
|
|
241
351
|
\`\`\`
|
|
242
352
|
|
|
243
|
-
|
|
353
|
+
### Start Production Server
|
|
244
354
|
|
|
245
355
|
\`\`\`bash
|
|
246
356
|
npm start
|
|
357
|
+
\`\`\``;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (language === 'website') {
|
|
361
|
+
return `### Development Mode
|
|
362
|
+
|
|
363
|
+
\`\`\`bash
|
|
364
|
+
npm run dev
|
|
365
|
+
\`\`\`
|
|
366
|
+
|
|
367
|
+
### Running Tests
|
|
368
|
+
|
|
369
|
+
\`\`\`bash
|
|
370
|
+
npm test
|
|
371
|
+
\`\`\`
|
|
372
|
+
|
|
373
|
+
### Build for Production
|
|
374
|
+
|
|
375
|
+
\`\`\`bash
|
|
376
|
+
npm run build
|
|
377
|
+
\`\`\`
|
|
378
|
+
|
|
379
|
+
### Preview Production Build
|
|
380
|
+
|
|
381
|
+
\`\`\`bash
|
|
382
|
+
npm run start
|
|
383
|
+
\`\`\``;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Workspace projects (fullstack, all)
|
|
387
|
+
const apps = (wsConfig as { apps?: Record<string, { path?: string; commands?: Record<string, string> }> })?.apps;
|
|
388
|
+
|
|
389
|
+
const lines: string[] = [];
|
|
390
|
+
|
|
391
|
+
lines.push(`### Run Everything (Docker Compose)
|
|
392
|
+
|
|
393
|
+
\`\`\`bash
|
|
394
|
+
docker-compose up
|
|
247
395
|
\`\`\`
|
|
248
|
-
` : ''}
|
|
249
|
-
## Project Structure
|
|
250
396
|
|
|
397
|
+
### Run Individual Apps`);
|
|
398
|
+
|
|
399
|
+
// Frontend
|
|
400
|
+
const fePath = apps?.frontend?.path || 'apps/frontend';
|
|
401
|
+
lines.push(`
|
|
402
|
+
#### Frontend (React/Vite)
|
|
403
|
+
|
|
404
|
+
\`\`\`bash
|
|
405
|
+
cd ${fePath}
|
|
406
|
+
npm run dev # Development server
|
|
407
|
+
npm run build # Production build
|
|
408
|
+
npm test # Run tests
|
|
409
|
+
\`\`\``);
|
|
410
|
+
|
|
411
|
+
// Backend
|
|
412
|
+
const bePath = apps?.backend?.path || 'apps/backend';
|
|
413
|
+
lines.push(`
|
|
414
|
+
#### Backend (Python/FastAPI)
|
|
415
|
+
|
|
416
|
+
\`\`\`bash
|
|
417
|
+
cd ${bePath}
|
|
418
|
+
uvicorn src.backend.main:app --reload --port 8000 # Development server
|
|
419
|
+
pip install -e . # Build/install
|
|
420
|
+
pytest tests/ -v # Run tests
|
|
421
|
+
\`\`\``);
|
|
422
|
+
|
|
423
|
+
// Website (for 'all' projects)
|
|
424
|
+
if (language === 'all') {
|
|
425
|
+
const webPath = apps?.website?.path || 'apps/website';
|
|
426
|
+
lines.push(`
|
|
427
|
+
#### Website (Next.js)
|
|
428
|
+
|
|
429
|
+
\`\`\`bash
|
|
430
|
+
cd ${webPath}
|
|
431
|
+
npm run dev # Development server (port 3001)
|
|
432
|
+
npm run build # Production build
|
|
433
|
+
npm test # Run tests
|
|
434
|
+
\`\`\``);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// All-at-once commands
|
|
438
|
+
lines.push(`
|
|
439
|
+
### Run All Tests
|
|
440
|
+
|
|
441
|
+
\`\`\`bash
|
|
442
|
+
npm run test:all
|
|
251
443
|
\`\`\`
|
|
252
|
-
|
|
444
|
+
|
|
445
|
+
### Build All
|
|
446
|
+
|
|
447
|
+
\`\`\`bash
|
|
448
|
+
npm run build:all
|
|
449
|
+
\`\`\``);
|
|
450
|
+
|
|
451
|
+
return lines.join('\n');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Generate project structure section based on language
|
|
456
|
+
*/
|
|
457
|
+
function generateStructureSection(name: string, language: OutputLanguage): string {
|
|
458
|
+
if (language === 'python') {
|
|
459
|
+
return `\`\`\`
|
|
460
|
+
${name}/
|
|
461
|
+
├── src/ # Source code
|
|
462
|
+
│ ├── __init__.py
|
|
463
|
+
│ └── main.py # Main entry point
|
|
464
|
+
├── tests/ # Test files
|
|
465
|
+
├── docs/ # Documentation
|
|
466
|
+
│ ├── PLAN.md # Development plan
|
|
467
|
+
│ └── WORKFLOW_LOG.md # Execution log
|
|
468
|
+
├── requirements.txt # Python dependencies
|
|
469
|
+
├── pyproject.toml # Project configuration
|
|
470
|
+
├── .env.example # Environment template
|
|
471
|
+
├── .gitignore
|
|
472
|
+
├── Dockerfile
|
|
473
|
+
└── README.md
|
|
474
|
+
\`\`\``;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (language === 'typescript') {
|
|
478
|
+
return `\`\`\`
|
|
479
|
+
${name}/
|
|
253
480
|
├── src/ # Source code
|
|
254
|
-
|
|
255
|
-
│ └── main.py # Main entry point`}
|
|
481
|
+
│ └── index.ts # Main entry point
|
|
256
482
|
├── tests/ # Test files
|
|
257
483
|
├── docs/ # Documentation
|
|
258
484
|
│ ├── PLAN.md # Development plan
|
|
259
485
|
│ └── WORKFLOW_LOG.md # Execution log
|
|
260
|
-
|
|
261
|
-
├── tsconfig.json # TypeScript configuration
|
|
262
|
-
├── pyproject.toml # Project configuration`}
|
|
486
|
+
├── package.json # Dependencies and scripts
|
|
487
|
+
├── tsconfig.json # TypeScript configuration
|
|
263
488
|
├── .env.example # Environment template
|
|
264
489
|
├── .gitignore
|
|
265
490
|
├── Dockerfile
|
|
266
491
|
└── README.md
|
|
492
|
+
\`\`\``;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (language === 'website') {
|
|
496
|
+
return `\`\`\`
|
|
497
|
+
${name}/
|
|
498
|
+
├── app/ # Next.js app directory
|
|
499
|
+
│ ├── layout.tsx # Root layout
|
|
500
|
+
│ └── page.tsx # Home page
|
|
501
|
+
├── components/ # React components
|
|
502
|
+
├── public/ # Static assets
|
|
503
|
+
├── docs/ # Documentation
|
|
504
|
+
│ ├── PLAN.md # Development plan
|
|
505
|
+
│ └── WORKFLOW_LOG.md # Execution log
|
|
506
|
+
├── package.json # Dependencies and scripts
|
|
507
|
+
├── next.config.js # Next.js configuration
|
|
508
|
+
├── tsconfig.json # TypeScript configuration
|
|
509
|
+
├── .env.example # Environment template
|
|
510
|
+
├── .gitignore
|
|
511
|
+
├── Dockerfile
|
|
512
|
+
└── README.md
|
|
513
|
+
\`\`\``;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Workspace (fullstack / all)
|
|
517
|
+
const websiteTree = language === 'all' ? `│ ├── website/ # Next.js marketing/landing site
|
|
518
|
+
│ │ ├── app/
|
|
519
|
+
│ │ ├── components/
|
|
520
|
+
│ │ ├── package.json
|
|
521
|
+
│ │ └── Dockerfile
|
|
522
|
+
` : '';
|
|
523
|
+
|
|
524
|
+
const packagesTree = language === 'all' ? `├── packages/ # Shared packages
|
|
525
|
+
│ ├── design-tokens/ # Shared design tokens
|
|
526
|
+
│ ├── ui/ # Shared UI components
|
|
527
|
+
│ └── contracts/ # API contracts (OpenAPI)
|
|
528
|
+
` : '';
|
|
529
|
+
|
|
530
|
+
return `\`\`\`
|
|
531
|
+
${name}/
|
|
532
|
+
├── apps/
|
|
533
|
+
│ ├── frontend/ # React/Vite frontend
|
|
534
|
+
│ │ ├── src/
|
|
535
|
+
│ │ ├── package.json
|
|
536
|
+
│ │ └── Dockerfile
|
|
537
|
+
│ ├── backend/ # Python/FastAPI backend
|
|
538
|
+
│ │ ├── src/
|
|
539
|
+
│ │ ├── tests/
|
|
540
|
+
│ │ ├── requirements.txt
|
|
541
|
+
│ │ └── Dockerfile
|
|
542
|
+
${websiteTree}${packagesTree}├── docs/ # Documentation
|
|
543
|
+
│ ├── PLAN.md # Development plan
|
|
544
|
+
│ └── WORKFLOW_LOG.md # Execution log
|
|
545
|
+
├── .popeye/ # Popeye configuration
|
|
546
|
+
│ └── workspace.json # Workspace configuration
|
|
547
|
+
├── docker-compose.yml # Local development stack
|
|
548
|
+
├── package.json # Root workspace config
|
|
549
|
+
├── .env.example # Environment template
|
|
550
|
+
├── .gitignore
|
|
551
|
+
└── README.md
|
|
552
|
+
\`\`\``;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Generate deployment section for workspace projects
|
|
557
|
+
*/
|
|
558
|
+
function generateDeploymentSection(
|
|
559
|
+
name: string,
|
|
560
|
+
language: OutputLanguage,
|
|
561
|
+
wsConfig: Record<string, unknown> | null
|
|
562
|
+
): string {
|
|
563
|
+
const apps = (wsConfig as { apps?: Record<string, { docker?: { imageName?: string; context?: string; dockerfile?: string } }> })?.apps;
|
|
564
|
+
const feImage = apps?.frontend?.docker?.imageName || `${name}-frontend`;
|
|
565
|
+
const beImage = apps?.backend?.docker?.imageName || `${name}-backend`;
|
|
566
|
+
|
|
567
|
+
const lines: string[] = [];
|
|
568
|
+
|
|
569
|
+
lines.push(`Each app can be built and deployed independently as a Docker container.
|
|
570
|
+
|
|
571
|
+
### Build Docker Images
|
|
572
|
+
|
|
573
|
+
\`\`\`bash
|
|
574
|
+
# Frontend
|
|
575
|
+
docker build -t ${feImage} -f apps/frontend/Dockerfile apps/frontend
|
|
576
|
+
|
|
577
|
+
# Backend
|
|
578
|
+
docker build -t ${beImage} -f apps/backend/Dockerfile apps/backend`);
|
|
579
|
+
|
|
580
|
+
if (language === 'all') {
|
|
581
|
+
const webImage = apps?.website?.docker?.imageName || `${name}-website`;
|
|
582
|
+
lines.push(`
|
|
583
|
+
# Website
|
|
584
|
+
docker build -t ${webImage} -f apps/website/Dockerfile apps/website`);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
lines.push('```');
|
|
588
|
+
|
|
589
|
+
lines.push(`
|
|
590
|
+
### Deploy with Docker Compose
|
|
591
|
+
|
|
592
|
+
\`\`\`bash
|
|
593
|
+
# Start all services
|
|
594
|
+
docker-compose up -d
|
|
595
|
+
|
|
596
|
+
# View logs
|
|
597
|
+
docker-compose logs -f
|
|
598
|
+
|
|
599
|
+
# Stop all services
|
|
600
|
+
docker-compose down
|
|
601
|
+
\`\`\``);
|
|
602
|
+
|
|
603
|
+
lines.push(`
|
|
604
|
+
### Individual App Deployment
|
|
605
|
+
|
|
606
|
+
#### Frontend (Static Site)
|
|
607
|
+
The frontend builds to static files that can be served by any CDN or static hosting:
|
|
608
|
+
\`\`\`bash
|
|
609
|
+
cd apps/frontend
|
|
610
|
+
npm run build
|
|
611
|
+
# Deploy the dist/ folder to your hosting provider (Vercel, Netlify, S3, etc.)
|
|
267
612
|
\`\`\`
|
|
268
613
|
|
|
269
|
-
|
|
614
|
+
#### Backend (API Server)
|
|
615
|
+
The backend runs as a Python ASGI server:
|
|
616
|
+
\`\`\`bash
|
|
617
|
+
cd apps/backend
|
|
618
|
+
pip install -r requirements.txt
|
|
619
|
+
uvicorn src.backend.main:app --host 0.0.0.0 --port 8000
|
|
620
|
+
\`\`\``);
|
|
621
|
+
|
|
622
|
+
if (language === 'all') {
|
|
623
|
+
lines.push(`
|
|
624
|
+
#### Website (Next.js SSR/SSG)
|
|
625
|
+
The website can be deployed as a Node.js server or exported as static files:
|
|
626
|
+
\`\`\`bash
|
|
627
|
+
cd apps/website
|
|
628
|
+
npm run build
|
|
629
|
+
npm start # Run as Node.js server
|
|
630
|
+
# Or deploy to Vercel/Netlify for automatic SSR support
|
|
631
|
+
\`\`\``);
|
|
632
|
+
}
|
|
270
633
|
|
|
271
|
-
|
|
634
|
+
return lines.join('\n');
|
|
635
|
+
}
|
|
272
636
|
|
|
273
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Result of README validation
|
|
639
|
+
*/
|
|
640
|
+
interface ReadmeValidationResult {
|
|
641
|
+
valid: boolean;
|
|
642
|
+
missingCritical: string[];
|
|
643
|
+
missingRecommended: string[];
|
|
644
|
+
}
|
|
274
645
|
|
|
275
|
-
|
|
646
|
+
/**
|
|
647
|
+
* Validate that the generated README contains required sections for the project type.
|
|
648
|
+
*
|
|
649
|
+
* Critical sections block completion; recommended sections produce warnings.
|
|
650
|
+
*
|
|
651
|
+
* @param projectDir - Project root directory
|
|
652
|
+
* @param language - Project language type
|
|
653
|
+
* @returns Validation result with lists of missing sections
|
|
654
|
+
*/
|
|
655
|
+
async function validateReadme(
|
|
656
|
+
projectDir: string,
|
|
657
|
+
language: OutputLanguage
|
|
658
|
+
): Promise<ReadmeValidationResult> {
|
|
659
|
+
const missingCritical: string[] = [];
|
|
660
|
+
const missingRecommended: string[] = [];
|
|
276
661
|
|
|
277
|
-
|
|
662
|
+
let content: string;
|
|
663
|
+
try {
|
|
664
|
+
content = await fs.readFile(path.join(projectDir, 'README.md'), 'utf-8');
|
|
665
|
+
} catch {
|
|
666
|
+
return { valid: false, missingCritical: ['README.md file not found'], missingRecommended: [] };
|
|
667
|
+
}
|
|
278
668
|
|
|
279
|
-
|
|
669
|
+
const contentLower = content.toLowerCase();
|
|
280
670
|
|
|
281
|
-
|
|
671
|
+
// Sections required for ALL project types
|
|
672
|
+
const universalRequired: Array<{ label: string; patterns: string[] }> = [
|
|
673
|
+
{ label: 'Installation', patterns: ['## installation'] },
|
|
674
|
+
{ label: 'Running the Application', patterns: ['## running', 'development mode'] },
|
|
675
|
+
{ label: 'Project Structure', patterns: ['## project structure'] },
|
|
676
|
+
{ label: 'Environment Setup', patterns: ['## environment', '.env'] },
|
|
677
|
+
];
|
|
282
678
|
|
|
283
|
-
|
|
284
|
-
|
|
679
|
+
for (const section of universalRequired) {
|
|
680
|
+
if (!section.patterns.some(p => contentLower.includes(p))) {
|
|
681
|
+
missingCritical.push(section.label);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
285
684
|
|
|
286
|
-
|
|
685
|
+
// Check for build/test commands
|
|
686
|
+
if (!contentLower.includes('npm run build') && !contentLower.includes('pip install') && !contentLower.includes('py_compile')) {
|
|
687
|
+
missingCritical.push('Build command');
|
|
688
|
+
}
|
|
689
|
+
if (!contentLower.includes('npm test') && !contentLower.includes('pytest')) {
|
|
690
|
+
missingRecommended.push('Test command');
|
|
691
|
+
}
|
|
287
692
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
693
|
+
// Workspace-specific checks (fullstack, all)
|
|
694
|
+
if (isWorkspace(language)) {
|
|
695
|
+
// Must have per-app sections
|
|
696
|
+
if (!contentLower.includes('frontend') || !contentLower.includes('backend')) {
|
|
697
|
+
missingCritical.push('Per-app instructions (frontend/backend)');
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Must have deployment section
|
|
701
|
+
if (!contentLower.includes('## deployment') && !contentLower.includes('### deploy')) {
|
|
702
|
+
missingCritical.push('Deployment section');
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Must mention docker
|
|
706
|
+
if (!contentLower.includes('docker')) {
|
|
707
|
+
missingCritical.push('Docker instructions');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// 'all' projects must mention website
|
|
711
|
+
if (language === 'all') {
|
|
712
|
+
if (!contentLower.includes('website') && !contentLower.includes('next.js')) {
|
|
713
|
+
missingCritical.push('Website app instructions');
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Website-specific
|
|
719
|
+
if (language === 'website') {
|
|
720
|
+
if (!contentLower.includes('next.js') && !contentLower.includes('next')) {
|
|
721
|
+
missingRecommended.push('Next.js reference');
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Python-specific
|
|
726
|
+
if (language === 'python') {
|
|
727
|
+
if (!contentLower.includes('pip install') && !contentLower.includes('requirements.txt')) {
|
|
728
|
+
missingCritical.push('Python dependency installation');
|
|
729
|
+
}
|
|
294
730
|
}
|
|
731
|
+
|
|
732
|
+
return {
|
|
733
|
+
valid: missingCritical.length === 0,
|
|
734
|
+
missingCritical,
|
|
735
|
+
missingRecommended,
|
|
736
|
+
};
|
|
295
737
|
}
|
|
296
738
|
|
|
297
739
|
/**
|
|
@@ -1283,6 +1725,39 @@ ${buildResult.structuralIssue ? buildErrors.slice(0, 1500) : buildErrors.slice(0
|
|
|
1283
1725
|
});
|
|
1284
1726
|
}
|
|
1285
1727
|
|
|
1728
|
+
// ============================================
|
|
1729
|
+
// VALIDATE README COMPLETENESS
|
|
1730
|
+
// ============================================
|
|
1731
|
+
onProgress?.('readme', 'Validating README completeness...');
|
|
1732
|
+
let readmeValidation = await validateReadme(projectDir, state.language);
|
|
1733
|
+
|
|
1734
|
+
if (!readmeValidation.valid) {
|
|
1735
|
+
onProgress?.('readme-warning', `README missing critical sections: ${readmeValidation.missingCritical.join(', ')}`);
|
|
1736
|
+
|
|
1737
|
+
// Re-generate and retry once
|
|
1738
|
+
onProgress?.('readme', 'Re-generating README to include missing sections...');
|
|
1739
|
+
await generateProjectReadme(projectDir, state);
|
|
1740
|
+
readmeValidation = await validateReadme(projectDir, state.language);
|
|
1741
|
+
|
|
1742
|
+
if (!readmeValidation.valid) {
|
|
1743
|
+
onProgress?.('readme-warning', `README still missing after re-generation: ${readmeValidation.missingCritical.join(', ')}`);
|
|
1744
|
+
await logger.warn('completion', 'readme_incomplete', 'README is missing critical sections', {
|
|
1745
|
+
missing: readmeValidation.missingCritical,
|
|
1746
|
+
});
|
|
1747
|
+
} else {
|
|
1748
|
+
onProgress?.('readme', 'README validated successfully after re-generation');
|
|
1749
|
+
}
|
|
1750
|
+
} else {
|
|
1751
|
+
onProgress?.('readme', 'README validated successfully');
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
// Log recommended (non-blocking) warnings
|
|
1755
|
+
if (readmeValidation.missingRecommended.length > 0) {
|
|
1756
|
+
for (const rec of readmeValidation.missingRecommended) {
|
|
1757
|
+
onProgress?.('readme-info', `README recommendation: add ${rec}`);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1286
1761
|
// All milestones complete
|
|
1287
1762
|
state = await completeProject(projectDir);
|
|
1288
1763
|
|