aerocoding 0.1.27 → 0.1.29

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/dist/index.js CHANGED
@@ -1115,8 +1115,12 @@ var projectConfigSchema = z.object({
1115
1115
  $schema: z.string().optional().default("https://aerocoding.dev/schema.json"),
1116
1116
  /** Project UUID from aerocoding.dev */
1117
1117
  projectId: z.string().uuid(),
1118
- /** Template ID for architecture generation */
1118
+ /** Template ID for architecture generation (legacy, prefer backendTemplateId/frontendTemplateId) */
1119
1119
  templateId: z.string().min(1),
1120
+ /** Backend template ID */
1121
+ backendTemplateId: z.string().min(1).optional(),
1122
+ /** Frontend template ID */
1123
+ frontendTemplateId: z.string().min(1).optional(),
1120
1124
  /** Template version at project creation */
1121
1125
  templateVersion: z.string().optional(),
1122
1126
  /** Root namespace/package name */
@@ -1149,6 +1153,8 @@ function createProjectConfig(options) {
1149
1153
  $schema: "https://aerocoding.dev/schema.json",
1150
1154
  projectId: options.projectId,
1151
1155
  templateId: options.templateId,
1156
+ backendTemplateId: options.backendTemplateId,
1157
+ frontendTemplateId: options.frontendTemplateId,
1152
1158
  templateVersion: options.templateVersion,
1153
1159
  namespace: options.namespace,
1154
1160
  organizationId: options.organizationId,
@@ -1316,32 +1322,65 @@ async function createCommand(projectName, options) {
1316
1322
  }
1317
1323
  } catch {
1318
1324
  }
1319
- let templateId = options.template;
1320
- if (!templateId) {
1325
+ let backendTemplateId = options.template;
1326
+ const hasBackend = !!project.backendFramework;
1327
+ if (hasBackend && !backendTemplateId) {
1321
1328
  const templateSpinner = p.spinner();
1322
- templateSpinner.start("Loading templates...");
1329
+ templateSpinner.start("Loading backend templates...");
1323
1330
  const templateResult = await apiClient.getTemplates({
1324
1331
  category: "backend",
1325
1332
  language: project.backendFramework || void 0
1326
1333
  });
1327
- templateSpinner.stop("Templates loaded");
1334
+ templateSpinner.stop("Backend templates loaded");
1328
1335
  if (templateResult.templates.length === 0) {
1329
- p.cancel("No templates available for this project's framework.");
1330
- process.exit(1);
1336
+ p.log.warn("No backend templates available for this project's framework.");
1337
+ } else {
1338
+ const selectedTemplate = await p.select({
1339
+ message: "Select backend architecture template",
1340
+ options: templateResult.templates.map((tmpl) => ({
1341
+ value: tmpl.id,
1342
+ label: tmpl.name,
1343
+ hint: tmpl.description || `${tmpl.tier} tier`
1344
+ }))
1345
+ });
1346
+ if (p.isCancel(selectedTemplate)) {
1347
+ p.cancel("Operation cancelled.");
1348
+ process.exit(0);
1349
+ }
1350
+ backendTemplateId = selectedTemplate;
1331
1351
  }
1332
- const selectedTemplate = await p.select({
1333
- message: "Select architecture template",
1334
- options: templateResult.templates.map((tmpl) => ({
1335
- value: tmpl.id,
1336
- label: tmpl.name,
1337
- hint: tmpl.description || `${tmpl.tier} tier`
1338
- }))
1352
+ }
1353
+ let frontendTemplateId;
1354
+ const hasFrontend = !!project.frontendFramework;
1355
+ if (hasFrontend) {
1356
+ const frontendSpinner = p.spinner();
1357
+ frontendSpinner.start("Loading frontend templates...");
1358
+ const frontendResult = await apiClient.getTemplates({
1359
+ category: "frontend",
1360
+ framework: project.frontendFramework || void 0
1339
1361
  });
1340
- if (p.isCancel(selectedTemplate)) {
1341
- p.cancel("Operation cancelled.");
1342
- process.exit(0);
1362
+ frontendSpinner.stop("Frontend templates loaded");
1363
+ if (frontendResult.templates.length === 0) {
1364
+ p.log.warn("No frontend templates available for this project's framework.");
1365
+ } else {
1366
+ const selectedFrontend = await p.select({
1367
+ message: "Select frontend architecture template",
1368
+ options: frontendResult.templates.map((tmpl) => ({
1369
+ value: tmpl.id,
1370
+ label: tmpl.name,
1371
+ hint: tmpl.description || `${tmpl.tier} tier`
1372
+ }))
1373
+ });
1374
+ if (p.isCancel(selectedFrontend)) {
1375
+ p.cancel("Operation cancelled.");
1376
+ process.exit(0);
1377
+ }
1378
+ frontendTemplateId = selectedFrontend;
1343
1379
  }
1344
- templateId = selectedTemplate;
1380
+ }
1381
+ if (!backendTemplateId && !frontendTemplateId) {
1382
+ p.cancel("No templates selected. At least one backend or frontend template is required.");
1383
+ process.exit(1);
1345
1384
  }
1346
1385
  const diagrams = project.schema?.diagrams || [];
1347
1386
  const hasMultipleDiagrams = diagrams.length > 1;
@@ -1378,55 +1417,101 @@ async function createCommand(projectName, options) {
1378
1417
  p.cancel("Operation cancelled.");
1379
1418
  process.exit(0);
1380
1419
  }
1420
+ const archStyleChoice = await p.select({
1421
+ message: "Architecture style",
1422
+ options: [
1423
+ {
1424
+ value: "bounded-contexts",
1425
+ label: "Bounded Contexts",
1426
+ hint: "DDD-style: separate folders per module (recommended)"
1427
+ },
1428
+ {
1429
+ value: "flat",
1430
+ label: "Flat Structure",
1431
+ hint: "All layers in single structure (simpler)"
1432
+ }
1433
+ ],
1434
+ initialValue: "bounded-contexts"
1435
+ });
1436
+ if (p.isCancel(archStyleChoice)) {
1437
+ p.cancel("Operation cancelled.");
1438
+ process.exit(0);
1439
+ }
1440
+ const useContexts = archStyleChoice === "bounded-contexts";
1381
1441
  const estimateSpinner = p.spinner();
1382
1442
  estimateSpinner.start("Calculating credit cost...");
1383
- let estimatedCredits = 0;
1384
1443
  let creditsRemaining = 0;
1385
- let estimatedFiles = [];
1386
- let estimatedEntities = 0;
1444
+ let backendEstimatedCredits = 0;
1445
+ let backendEstimatedFiles = [];
1446
+ let backendEstimatedEntities = 0;
1447
+ let frontendEstimatedCredits = 0;
1448
+ let frontendEstimatedFiles = [];
1449
+ let frontendEstimatedEntities = 0;
1387
1450
  const estimateDiagramIds = selectedDiagramIds.length < diagrams.length ? selectedDiagramIds : void 0;
1451
+ const featureFlags = {
1452
+ includeDtos: true,
1453
+ includeUseCases: true,
1454
+ includeMappers: true,
1455
+ includeControllers: true,
1456
+ includeEfConfig: true,
1457
+ includeValidation: true,
1458
+ includeDtoValidation: true,
1459
+ includeUnitTests: true,
1460
+ includeIntegrationTests: true,
1461
+ includeStarterFiles: true,
1462
+ includeReactions: true,
1463
+ includeOutbox: true,
1464
+ includeInbox: true
1465
+ };
1388
1466
  try {
1389
- const estimate = await apiClient.estimateCreditCost({
1390
- projectId,
1391
- templateId,
1392
- options: {
1393
- featureFlags: {
1394
- includeDtos: true,
1395
- includeUseCases: true,
1396
- includeMappers: true,
1397
- includeControllers: true,
1398
- includeEfConfig: true,
1399
- includeValidation: true,
1400
- includeDtoValidation: true,
1401
- includeUnitTests: true,
1402
- includeIntegrationTests: true,
1403
- includeStarterFiles: true,
1404
- includeReactions: true,
1405
- includeOutbox: true,
1406
- includeInbox: true
1407
- },
1408
- useContexts: true,
1409
- diagramIds: estimateDiagramIds
1410
- }
1411
- });
1412
- estimatedCredits = estimate.estimatedCredits;
1413
- estimatedFiles = estimate.files || [];
1414
- estimatedEntities = estimate.entities || 0;
1467
+ if (backendTemplateId) {
1468
+ const backendEstimate = await apiClient.estimateCreditCost({
1469
+ projectId,
1470
+ templateId: backendTemplateId,
1471
+ options: {
1472
+ featureFlags,
1473
+ useContexts,
1474
+ diagramIds: estimateDiagramIds
1475
+ }
1476
+ });
1477
+ backendEstimatedCredits = backendEstimate.estimatedCredits;
1478
+ backendEstimatedFiles = backendEstimate.files || [];
1479
+ backendEstimatedEntities = backendEstimate.entities || 0;
1480
+ }
1481
+ if (frontendTemplateId) {
1482
+ const frontendEstimate = await apiClient.estimateCreditCost({
1483
+ projectId,
1484
+ templateId: frontendTemplateId,
1485
+ options: {
1486
+ featureFlags,
1487
+ useContexts,
1488
+ diagramIds: estimateDiagramIds
1489
+ }
1490
+ });
1491
+ frontendEstimatedCredits = frontendEstimate.estimatedCredits;
1492
+ frontendEstimatedFiles = frontendEstimate.files || [];
1493
+ frontendEstimatedEntities = frontendEstimate.entities || 0;
1494
+ }
1415
1495
  const creditUsage = await apiClient.getCreditUsage(organizationId);
1416
1496
  creditsRemaining = creditUsage.remaining;
1417
1497
  estimateSpinner.stop("Credit estimate calculated");
1418
1498
  } catch {
1419
1499
  estimateSpinner.stop("Could not estimate credits (will be calculated on generation)");
1420
1500
  }
1421
- const hasEnoughCredits = estimatedCredits === 0 || creditsRemaining >= estimatedCredits;
1501
+ const totalEstimatedCredits = backendEstimatedCredits + frontendEstimatedCredits;
1502
+ const totalEstimatedFiles = backendEstimatedFiles.length + frontendEstimatedFiles.length;
1503
+ const totalEstimatedEntities = backendEstimatedEntities + frontendEstimatedEntities;
1504
+ const hasEnoughCredits = totalEstimatedCredits === 0 || creditsRemaining >= totalEstimatedCredits;
1422
1505
  console.log("");
1423
1506
  console.log(chalk7.bold(" Generation Summary"));
1424
1507
  console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1425
1508
  console.log(chalk7.gray(" Project:"), chalk7.white(project.name));
1426
- console.log(chalk7.gray(" Template:"), chalk7.cyan(templateId));
1427
1509
  console.log(chalk7.gray(" Namespace:"), chalk7.cyan(namespace));
1428
1510
  console.log(chalk7.gray(" Directory:"), chalk7.cyan(`${safeName}/`));
1429
- console.log(chalk7.gray(" Architecture:"), chalk7.cyan("Bounded Contexts"));
1511
+ console.log(
1512
+ chalk7.gray(" Architecture:"),
1513
+ useContexts ? chalk7.cyan("Bounded Contexts") : chalk7.yellow("Flat Structure")
1514
+ );
1430
1515
  if (hasMultipleDiagrams) {
1431
1516
  const selectedCount = selectedDiagramIds.length;
1432
1517
  const totalCount = diagrams.length;
@@ -1437,14 +1522,23 @@ async function createCommand(projectName, options) {
1437
1522
  console.log(chalk7.gray(" Bounded Contexts:"), chalk7.cyan(`${selectedCount}/${totalCount} (${selectedNames})`));
1438
1523
  }
1439
1524
  }
1440
- if (estimatedFiles.length > 0) {
1525
+ console.log("");
1526
+ console.log(chalk7.bold(" Templates"));
1527
+ console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1528
+ if (backendTemplateId) {
1529
+ console.log(chalk7.gray(" Backend:"), chalk7.cyan(backendTemplateId));
1530
+ }
1531
+ if (frontendTemplateId) {
1532
+ console.log(chalk7.gray(" Frontend:"), chalk7.cyan(frontendTemplateId));
1533
+ }
1534
+ if (backendEstimatedFiles.length > 0) {
1441
1535
  console.log("");
1442
- console.log(chalk7.bold(" Files to Generate"));
1536
+ console.log(chalk7.bold(` Backend Files (${backendTemplateId})`));
1443
1537
  console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1444
- const categories = categorizeFilePaths(estimatedFiles);
1445
- const maxNameLength = Math.max(...categories.map((c) => c.name.length));
1446
- for (const category of categories) {
1447
- const padding = " ".repeat(maxNameLength - category.name.length + 2);
1538
+ const backendCategories = categorizeFilePaths(backendEstimatedFiles);
1539
+ const backendMaxNameLength = Math.max(...backendCategories.map((c) => c.name.length), 8);
1540
+ for (const category of backendCategories) {
1541
+ const padding = " ".repeat(backendMaxNameLength - category.name.length + 2);
1448
1542
  const countStr = category.count.toString().padStart(3, " ");
1449
1543
  console.log(
1450
1544
  chalk7.gray(` ${category.name}${padding}`),
@@ -1452,21 +1546,65 @@ async function createCommand(projectName, options) {
1452
1546
  );
1453
1547
  }
1454
1548
  console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1455
- const totalPadding = " ".repeat(maxNameLength - 5 + 2);
1456
- const totalStr = estimatedFiles.length.toString().padStart(3, " ");
1549
+ const subtotalPadding = " ".repeat(backendMaxNameLength - 8 + 2);
1550
+ const subtotalStr = backendEstimatedFiles.length.toString().padStart(3, " ");
1457
1551
  console.log(
1458
- chalk7.white(` Total${totalPadding}`),
1459
- chalk7.bold.cyan(`${totalStr} files`)
1552
+ chalk7.white(` Subtotal${subtotalPadding}`),
1553
+ chalk7.bold.cyan(`${subtotalStr} files`)
1460
1554
  );
1461
- console.log(chalk7.gray(" Entities:"), chalk7.cyan(estimatedEntities));
1555
+ }
1556
+ if (frontendEstimatedFiles.length > 0) {
1557
+ console.log("");
1558
+ console.log(chalk7.bold(` Frontend Files (${frontendTemplateId})`));
1559
+ console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1560
+ const frontendCategories = categorizeFilePaths(frontendEstimatedFiles);
1561
+ const frontendMaxNameLength = Math.max(...frontendCategories.map((c) => c.name.length), 8);
1562
+ for (const category of frontendCategories) {
1563
+ const padding = " ".repeat(frontendMaxNameLength - category.name.length + 2);
1564
+ const countStr = category.count.toString().padStart(3, " ");
1565
+ console.log(
1566
+ chalk7.gray(` ${category.name}${padding}`),
1567
+ chalk7.cyan(`${countStr} files`)
1568
+ );
1569
+ }
1570
+ console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1571
+ const subtotalPadding = " ".repeat(frontendMaxNameLength - 8 + 2);
1572
+ const subtotalStr = frontendEstimatedFiles.length.toString().padStart(3, " ");
1573
+ console.log(
1574
+ chalk7.white(` Subtotal${subtotalPadding}`),
1575
+ chalk7.bold.cyan(`${subtotalStr} files`)
1576
+ );
1577
+ }
1578
+ if (backendEstimatedFiles.length > 0 && frontendEstimatedFiles.length > 0) {
1579
+ console.log("");
1580
+ console.log(chalk7.gray(" \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1581
+ console.log(
1582
+ chalk7.bold.white(" TOTAL "),
1583
+ chalk7.bold.green(`${totalEstimatedFiles.toString().padStart(3, " ")} files`)
1584
+ );
1585
+ console.log(chalk7.gray(" Entities:"), chalk7.cyan(totalEstimatedEntities));
1586
+ } else if (totalEstimatedFiles > 0) {
1587
+ console.log(chalk7.gray(" Entities:"), chalk7.cyan(totalEstimatedEntities));
1462
1588
  }
1463
1589
  console.log("");
1464
1590
  console.log(chalk7.bold(" Credits"));
1465
1591
  console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1466
- if (estimatedCredits > 0) {
1592
+ if (backendEstimatedCredits > 0) {
1593
+ console.log(
1594
+ chalk7.gray(" Backend:"),
1595
+ chalk7.yellow(`${backendEstimatedCredits} credits`)
1596
+ );
1597
+ }
1598
+ if (frontendEstimatedCredits > 0) {
1599
+ console.log(
1600
+ chalk7.gray(" Frontend:"),
1601
+ chalk7.yellow(`${frontendEstimatedCredits} credits`)
1602
+ );
1603
+ }
1604
+ if (totalEstimatedCredits > 0) {
1467
1605
  console.log(
1468
- chalk7.white(" Cost:"),
1469
- hasEnoughCredits ? chalk7.bold.yellow(`${estimatedCredits} credits`) : chalk7.bold.red(`${estimatedCredits} credits`)
1606
+ chalk7.white(" Total Cost:"),
1607
+ hasEnoughCredits ? chalk7.bold.yellow(`${totalEstimatedCredits} credits`) : chalk7.bold.red(`${totalEstimatedCredits} credits`)
1470
1608
  );
1471
1609
  }
1472
1610
  console.log(
@@ -1476,12 +1614,12 @@ async function createCommand(projectName, options) {
1476
1614
  console.log("");
1477
1615
  if (!hasEnoughCredits) {
1478
1616
  console.log(chalk7.red(" \u26A0 Not enough credits for this generation."));
1479
- console.log(chalk7.gray(` Need ${estimatedCredits - creditsRemaining} more credits.
1617
+ console.log(chalk7.gray(` Need ${totalEstimatedCredits - creditsRemaining} more credits.
1480
1618
  `));
1481
1619
  process.exit(1);
1482
1620
  }
1483
1621
  const proceed = await p.confirm({
1484
- message: estimatedCredits > 0 ? `Proceed with generation? (~${estimatedCredits} credits)` : "Proceed with generation?",
1622
+ message: totalEstimatedCredits > 0 ? `Proceed with generation? (~${totalEstimatedCredits} credits)` : "Proceed with generation?",
1485
1623
  initialValue: true
1486
1624
  });
1487
1625
  if (p.isCancel(proceed) || !proceed) {
@@ -1492,47 +1630,68 @@ async function createCommand(projectName, options) {
1492
1630
  dirSpinner.start(`Creating ${safeName}/...`);
1493
1631
  await mkdir(projectDir, { recursive: true });
1494
1632
  dirSpinner.stop(`Created ${safeName}/`);
1495
- const genSpinner = ora2({ text: "Generating architecture...", color: "cyan" }).start();
1496
- const result = await apiClient.generateCode({
1497
- projectId,
1498
- templateId,
1499
- options: {
1500
- includeValidations: true,
1501
- includeComments: true,
1502
- featureFlags: {
1503
- includeDtos: true,
1504
- includeUseCases: true,
1505
- includeMappers: true,
1506
- includeControllers: true,
1507
- includeEfConfig: true,
1508
- includeValidation: true,
1509
- includeDtoValidation: true,
1510
- includeUnitTests: true,
1511
- includeIntegrationTests: true,
1512
- includeStarterFiles: true,
1513
- includeReactions: true,
1514
- includeOutbox: true,
1515
- includeInbox: true
1516
- },
1517
- useContexts: true,
1518
- diagramIds: estimateDiagramIds
1519
- // Filter by selected bounded contexts
1633
+ const allGeneratedFiles = [];
1634
+ let totalCreditsUsed = 0;
1635
+ let totalCreditsRemaining = creditsRemaining;
1636
+ if (backendTemplateId) {
1637
+ const backendSpinner = ora2({ text: "Generating backend architecture...", color: "cyan" }).start();
1638
+ const backendResult = await apiClient.generateCode({
1639
+ projectId,
1640
+ templateId: backendTemplateId,
1641
+ options: {
1642
+ includeValidations: true,
1643
+ includeComments: true,
1644
+ featureFlags,
1645
+ useContexts,
1646
+ diagramIds: estimateDiagramIds
1647
+ }
1648
+ });
1649
+ const backendFiles = (backendResult.files || []).map((file) => ({
1650
+ ...file,
1651
+ path: `backend/${file.path}`
1652
+ }));
1653
+ allGeneratedFiles.push(...backendFiles);
1654
+ if (backendResult.creditsUsed !== void 0) {
1655
+ totalCreditsUsed += backendResult.creditsUsed;
1656
+ totalCreditsRemaining = backendResult.creditsRemaining ?? totalCreditsRemaining;
1520
1657
  }
1521
- });
1522
- genSpinner.succeed(chalk7.green(`Generated ${result.files?.length || 0} files`));
1658
+ backendSpinner.succeed(chalk7.green(`Backend: ${backendFiles.length} files generated`));
1659
+ }
1660
+ if (frontendTemplateId) {
1661
+ const frontendSpinner = ora2({ text: "Generating frontend architecture...", color: "cyan" }).start();
1662
+ const frontendResult = await apiClient.generateCode({
1663
+ projectId,
1664
+ templateId: frontendTemplateId,
1665
+ options: {
1666
+ includeValidations: true,
1667
+ includeComments: true,
1668
+ featureFlags,
1669
+ useContexts,
1670
+ diagramIds: estimateDiagramIds
1671
+ }
1672
+ });
1673
+ const frontendFiles = (frontendResult.files || []).map((file) => ({
1674
+ ...file,
1675
+ path: `frontend/${file.path}`
1676
+ }));
1677
+ allGeneratedFiles.push(...frontendFiles);
1678
+ if (frontendResult.creditsUsed !== void 0) {
1679
+ totalCreditsUsed += frontendResult.creditsUsed;
1680
+ totalCreditsRemaining = frontendResult.creditsRemaining ?? totalCreditsRemaining;
1681
+ }
1682
+ frontendSpinner.succeed(chalk7.green(`Frontend: ${frontendFiles.length} files generated`));
1683
+ }
1523
1684
  const writeSpinner = p.spinner();
1524
1685
  writeSpinner.start("Writing files...");
1525
- const organizedFiles = result.files.map((file) => ({
1526
- ...file,
1527
- path: `backend/${file.path}`
1528
- }));
1529
- await writeGeneratedFiles(organizedFiles, projectDir, false);
1530
- writeSpinner.stop(`Wrote ${organizedFiles.length} files`);
1686
+ await writeGeneratedFiles(allGeneratedFiles, projectDir, false);
1687
+ writeSpinner.stop(`Wrote ${allGeneratedFiles.length} files`);
1531
1688
  const configSpinner = p.spinner();
1532
1689
  configSpinner.start("Creating config files...");
1533
1690
  const projectConfig = createProjectConfig({
1534
1691
  projectId,
1535
- templateId,
1692
+ templateId: backendTemplateId || frontendTemplateId || "",
1693
+ backendTemplateId,
1694
+ frontendTemplateId,
1536
1695
  templateVersion: "1.0.0",
1537
1696
  // TODO: get from template
1538
1697
  namespace,
@@ -1541,7 +1700,7 @@ async function createCommand(projectName, options) {
1541
1700
  });
1542
1701
  await saveProjectConfig(projectConfig, projectDir);
1543
1702
  let manifest = createEmptyManifest("1.0.0");
1544
- for (const file of organizedFiles) {
1703
+ for (const file of allGeneratedFiles) {
1545
1704
  const hash = hashString(file.content);
1546
1705
  manifest = setManifestFile(manifest, file.path, {
1547
1706
  hash,
@@ -1566,13 +1725,21 @@ async function createCommand(projectName, options) {
1566
1725
  console.log(chalk7.bold(" Project Created Successfully!"));
1567
1726
  console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1568
1727
  console.log(chalk7.gray(" Directory:"), chalk7.cyan(safeName + "/"));
1569
- console.log(chalk7.gray(" Files:"), chalk7.cyan(organizedFiles.length));
1728
+ const backendFileCount = allGeneratedFiles.filter((f) => f.path.startsWith("backend/")).length;
1729
+ const frontendFileCount = allGeneratedFiles.filter((f) => f.path.startsWith("frontend/")).length;
1730
+ if (backendFileCount > 0 && frontendFileCount > 0) {
1731
+ console.log(chalk7.gray(" Backend:"), chalk7.cyan(`${backendFileCount} files`));
1732
+ console.log(chalk7.gray(" Frontend:"), chalk7.cyan(`${frontendFileCount} files`));
1733
+ console.log(chalk7.gray(" Total:"), chalk7.bold.cyan(`${allGeneratedFiles.length} files`));
1734
+ } else {
1735
+ console.log(chalk7.gray(" Files:"), chalk7.cyan(allGeneratedFiles.length));
1736
+ }
1570
1737
  console.log(chalk7.gray(" Config:"), chalk7.cyan(PROJECT_CONFIG_FILENAME));
1571
1738
  console.log(chalk7.gray(" Manifest:"), chalk7.cyan(MANIFEST_FILENAME));
1572
- if (result.creditsUsed !== void 0) {
1739
+ if (totalCreditsUsed > 0) {
1573
1740
  console.log("");
1574
- console.log(chalk7.gray(" Credits used:"), chalk7.yellow(result.creditsUsed));
1575
- console.log(chalk7.gray(" Credits remaining:"), chalk7.green(result.creditsRemaining));
1741
+ console.log(chalk7.gray(" Credits used:"), chalk7.yellow(totalCreditsUsed));
1742
+ console.log(chalk7.gray(" Credits remaining:"), chalk7.green(totalCreditsRemaining));
1576
1743
  }
1577
1744
  p.outro(
1578
1745
  chalk7.green("Project ready!") + "\n\n" + chalk7.gray(" Next steps:\n") + chalk7.cyan(` cd ${safeName}
@@ -2043,7 +2210,7 @@ async function findConflictFiles(dir) {
2043
2210
  // src/index.ts
2044
2211
  import "dotenv/config";
2045
2212
  var program = new Command();
2046
- program.name("aerocoding").description("AeroCoding CLI - Generate production-ready code from UML diagrams").version("0.1.27");
2213
+ program.name("aerocoding").description("AeroCoding CLI - Generate production-ready code from UML diagrams").version("0.1.28");
2047
2214
  program.command("login").description("Authenticate with AeroCoding").action(loginCommand);
2048
2215
  program.command("logout").description("Logout and clear stored credentials").action(logoutCommand);
2049
2216
  program.command("whoami").description("Show current authenticated user").action(whoamiCommand);