popeye-cli 1.7.0 → 1.8.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.
Files changed (132) hide show
  1. package/README.md +102 -5
  2. package/cheatsheet.md +407 -0
  3. package/dist/cli/commands/db.d.ts +10 -0
  4. package/dist/cli/commands/db.d.ts.map +1 -0
  5. package/dist/cli/commands/db.js +240 -0
  6. package/dist/cli/commands/db.js.map +1 -0
  7. package/dist/cli/commands/doctor.d.ts +18 -0
  8. package/dist/cli/commands/doctor.d.ts.map +1 -0
  9. package/dist/cli/commands/doctor.js +255 -0
  10. package/dist/cli/commands/doctor.js.map +1 -0
  11. package/dist/cli/commands/index.d.ts +2 -0
  12. package/dist/cli/commands/index.d.ts.map +1 -1
  13. package/dist/cli/commands/index.js +2 -0
  14. package/dist/cli/commands/index.js.map +1 -1
  15. package/dist/cli/index.d.ts.map +1 -1
  16. package/dist/cli/index.js +3 -1
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/interactive.d.ts.map +1 -1
  19. package/dist/cli/interactive.js +96 -0
  20. package/dist/cli/interactive.js.map +1 -1
  21. package/dist/generators/admin-wizard.d.ts +25 -0
  22. package/dist/generators/admin-wizard.d.ts.map +1 -0
  23. package/dist/generators/admin-wizard.js +123 -0
  24. package/dist/generators/admin-wizard.js.map +1 -0
  25. package/dist/generators/all.d.ts.map +1 -1
  26. package/dist/generators/all.js +10 -3
  27. package/dist/generators/all.js.map +1 -1
  28. package/dist/generators/database.d.ts +58 -0
  29. package/dist/generators/database.d.ts.map +1 -0
  30. package/dist/generators/database.js +229 -0
  31. package/dist/generators/database.js.map +1 -0
  32. package/dist/generators/fullstack.d.ts.map +1 -1
  33. package/dist/generators/fullstack.js +23 -7
  34. package/dist/generators/fullstack.js.map +1 -1
  35. package/dist/generators/index.d.ts +2 -0
  36. package/dist/generators/index.d.ts.map +1 -1
  37. package/dist/generators/index.js +2 -0
  38. package/dist/generators/index.js.map +1 -1
  39. package/dist/generators/templates/admin-wizard-python.d.ts +32 -0
  40. package/dist/generators/templates/admin-wizard-python.d.ts.map +1 -0
  41. package/dist/generators/templates/admin-wizard-python.js +425 -0
  42. package/dist/generators/templates/admin-wizard-python.js.map +1 -0
  43. package/dist/generators/templates/admin-wizard-react.d.ts +48 -0
  44. package/dist/generators/templates/admin-wizard-react.d.ts.map +1 -0
  45. package/dist/generators/templates/admin-wizard-react.js +554 -0
  46. package/dist/generators/templates/admin-wizard-react.js.map +1 -0
  47. package/dist/generators/templates/database-docker.d.ts +23 -0
  48. package/dist/generators/templates/database-docker.d.ts.map +1 -0
  49. package/dist/generators/templates/database-docker.js +221 -0
  50. package/dist/generators/templates/database-docker.js.map +1 -0
  51. package/dist/generators/templates/database-python.d.ts +54 -0
  52. package/dist/generators/templates/database-python.d.ts.map +1 -0
  53. package/dist/generators/templates/database-python.js +723 -0
  54. package/dist/generators/templates/database-python.js.map +1 -0
  55. package/dist/generators/templates/database-typescript.d.ts +34 -0
  56. package/dist/generators/templates/database-typescript.d.ts.map +1 -0
  57. package/dist/generators/templates/database-typescript.js +232 -0
  58. package/dist/generators/templates/database-typescript.js.map +1 -0
  59. package/dist/generators/templates/fullstack.d.ts.map +1 -1
  60. package/dist/generators/templates/fullstack.js +29 -0
  61. package/dist/generators/templates/fullstack.js.map +1 -1
  62. package/dist/generators/templates/index.d.ts +5 -0
  63. package/dist/generators/templates/index.d.ts.map +1 -1
  64. package/dist/generators/templates/index.js +5 -0
  65. package/dist/generators/templates/index.js.map +1 -1
  66. package/dist/state/index.d.ts +10 -0
  67. package/dist/state/index.d.ts.map +1 -1
  68. package/dist/state/index.js +21 -0
  69. package/dist/state/index.js.map +1 -1
  70. package/dist/types/database-runtime.d.ts +86 -0
  71. package/dist/types/database-runtime.d.ts.map +1 -0
  72. package/dist/types/database-runtime.js +61 -0
  73. package/dist/types/database-runtime.js.map +1 -0
  74. package/dist/types/database.d.ts +85 -0
  75. package/dist/types/database.d.ts.map +1 -0
  76. package/dist/types/database.js +71 -0
  77. package/dist/types/database.js.map +1 -0
  78. package/dist/types/index.d.ts +2 -0
  79. package/dist/types/index.d.ts.map +1 -1
  80. package/dist/types/index.js +4 -0
  81. package/dist/types/index.js.map +1 -1
  82. package/dist/types/workflow.d.ts +21 -0
  83. package/dist/types/workflow.d.ts.map +1 -1
  84. package/dist/types/workflow.js +2 -0
  85. package/dist/types/workflow.js.map +1 -1
  86. package/dist/workflow/db-setup-runner.d.ts +63 -0
  87. package/dist/workflow/db-setup-runner.d.ts.map +1 -0
  88. package/dist/workflow/db-setup-runner.js +336 -0
  89. package/dist/workflow/db-setup-runner.js.map +1 -0
  90. package/dist/workflow/db-state-machine.d.ts +30 -0
  91. package/dist/workflow/db-state-machine.d.ts.map +1 -0
  92. package/dist/workflow/db-state-machine.js +51 -0
  93. package/dist/workflow/db-state-machine.js.map +1 -0
  94. package/dist/workflow/index.d.ts +2 -0
  95. package/dist/workflow/index.d.ts.map +1 -1
  96. package/dist/workflow/index.js +2 -0
  97. package/dist/workflow/index.js.map +1 -1
  98. package/package.json +1 -1
  99. package/src/cli/commands/db.ts +281 -0
  100. package/src/cli/commands/doctor.ts +273 -0
  101. package/src/cli/commands/index.ts +2 -0
  102. package/src/cli/index.ts +4 -0
  103. package/src/cli/interactive.ts +102 -0
  104. package/src/generators/admin-wizard.ts +146 -0
  105. package/src/generators/all.ts +10 -3
  106. package/src/generators/database.ts +286 -0
  107. package/src/generators/fullstack.ts +26 -9
  108. package/src/generators/index.ts +12 -0
  109. package/src/generators/templates/admin-wizard-python.ts +431 -0
  110. package/src/generators/templates/admin-wizard-react.ts +560 -0
  111. package/src/generators/templates/database-docker.ts +227 -0
  112. package/src/generators/templates/database-python.ts +734 -0
  113. package/src/generators/templates/database-typescript.ts +238 -0
  114. package/src/generators/templates/fullstack.ts +29 -0
  115. package/src/generators/templates/index.ts +5 -0
  116. package/src/state/index.ts +28 -0
  117. package/src/types/database-runtime.ts +69 -0
  118. package/src/types/database.ts +84 -0
  119. package/src/types/index.ts +29 -0
  120. package/src/types/workflow.ts +5 -0
  121. package/src/workflow/db-setup-runner.ts +391 -0
  122. package/src/workflow/db-state-machine.ts +58 -0
  123. package/src/workflow/index.ts +2 -0
  124. package/tests/generators/admin-wizard-orchestrator.test.ts +64 -0
  125. package/tests/generators/admin-wizard-templates.test.ts +366 -0
  126. package/tests/generators/cross-phase-integration.test.ts +383 -0
  127. package/tests/generators/database.test.ts +456 -0
  128. package/tests/generators/fe-be-db-integration.test.ts +613 -0
  129. package/tests/types/database-runtime.test.ts +158 -0
  130. package/tests/types/database.test.ts +187 -0
  131. package/tests/workflow/db-setup-runner.test.ts +211 -0
  132. package/tests/workflow/db-state-machine.test.ts +117 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Admin Wizard generator orchestration module
3
+ * Creates the admin wizard backend + frontend layers for fullstack projects
4
+ */
5
+
6
+ import { promises as fs } from 'node:fs';
7
+ import path from 'node:path';
8
+ import {
9
+ generateAdminAuthMiddleware,
10
+ generateMiddlewareInit,
11
+ generateAdminDbRoutes,
12
+ } from './templates/admin-wizard-python.js';
13
+ import {
14
+ generateUseAdminApiHook,
15
+ generateDbStatusBanner,
16
+ generateConnectionForm,
17
+ generateMigrationProgress,
18
+ generateDbSetupStepper,
19
+ generateAdminIndex,
20
+ } from './templates/admin-wizard-react.js';
21
+
22
+ /** Python dependencies required by the admin wizard */
23
+ export const ADMIN_WIZARD_PYTHON_DEPS = ['python-multipart>=0.0.7'];
24
+
25
+ /**
26
+ * Create a directory if it doesn't exist
27
+ */
28
+ async function ensureDir(dirPath: string): Promise<void> {
29
+ await fs.mkdir(dirPath, { recursive: true });
30
+ }
31
+
32
+ /**
33
+ * Write a file with content
34
+ */
35
+ async function writeFile(filePath: string, content: string): Promise<void> {
36
+ await fs.writeFile(filePath, content, 'utf-8');
37
+ }
38
+
39
+ /**
40
+ * Generate the complete admin wizard layer (backend middleware/routes + frontend components)
41
+ *
42
+ * Creates middleware/, routes/admin_db.py in the backend, and admin/ components
43
+ * in the frontend. Also augments requirements.txt with admin dependencies.
44
+ *
45
+ * @param projectDir - Root project directory (contains apps/)
46
+ * @param packageName - Python package name (snake_case)
47
+ * @returns List of absolute file paths created
48
+ */
49
+ export async function generateAdminWizardLayer(
50
+ projectDir: string,
51
+ packageName: string
52
+ ): Promise<string[]> {
53
+ const backendDir = path.join(projectDir, 'apps', 'backend');
54
+ const srcPkgDir = path.join(backendDir, 'src', packageName);
55
+ const frontendDir = path.join(projectDir, 'apps', 'frontend');
56
+ const filesCreated: string[] = [];
57
+
58
+ // Ensure directories
59
+ await ensureDir(path.join(srcPkgDir, 'middleware'));
60
+ await ensureDir(path.join(frontendDir, 'src', 'admin'));
61
+
62
+ // Define all files to generate
63
+ const files: Array<{ path: string; content: string }> = [
64
+ // Backend: middleware
65
+ {
66
+ path: path.join(srcPkgDir, 'middleware', '__init__.py'),
67
+ content: generateMiddlewareInit(),
68
+ },
69
+ {
70
+ path: path.join(srcPkgDir, 'middleware', 'admin_auth.py'),
71
+ content: generateAdminAuthMiddleware(),
72
+ },
73
+ // Backend: admin routes
74
+ {
75
+ path: path.join(srcPkgDir, 'routes', 'admin_db.py'),
76
+ content: generateAdminDbRoutes(packageName),
77
+ },
78
+ // Frontend: admin components
79
+ {
80
+ path: path.join(frontendDir, 'src', 'admin', 'useAdminApi.ts'),
81
+ content: generateUseAdminApiHook(),
82
+ },
83
+ {
84
+ path: path.join(frontendDir, 'src', 'admin', 'DbStatusBanner.tsx'),
85
+ content: generateDbStatusBanner(),
86
+ },
87
+ {
88
+ path: path.join(frontendDir, 'src', 'admin', 'ConnectionForm.tsx'),
89
+ content: generateConnectionForm(),
90
+ },
91
+ {
92
+ path: path.join(frontendDir, 'src', 'admin', 'MigrationProgress.tsx'),
93
+ content: generateMigrationProgress(),
94
+ },
95
+ {
96
+ path: path.join(frontendDir, 'src', 'admin', 'DbSetupStepper.tsx'),
97
+ content: generateDbSetupStepper(),
98
+ },
99
+ {
100
+ path: path.join(frontendDir, 'src', 'admin', 'index.ts'),
101
+ content: generateAdminIndex(),
102
+ },
103
+ ];
104
+
105
+ // Write all files
106
+ for (const file of files) {
107
+ await writeFile(file.path, file.content);
108
+ filesCreated.push(file.path);
109
+ }
110
+
111
+ // Augment requirements.txt with admin deps
112
+ const reqPath = path.join(backendDir, 'requirements.txt');
113
+ try {
114
+ const existingReqs = await fs.readFile(reqPath, 'utf-8');
115
+ if (!existingReqs.includes('# Admin')) {
116
+ const adminSection = `\n# Admin\n${ADMIN_WIZARD_PYTHON_DEPS.join('\n')}\n`;
117
+ await writeFile(reqPath, existingReqs.trimEnd() + adminSection);
118
+ }
119
+ } catch {
120
+ // requirements.txt doesn't exist yet - will be created by the main generator
121
+ }
122
+
123
+ return filesCreated;
124
+ }
125
+
126
+ /**
127
+ * Get the list of relative file paths generated by the admin wizard layer
128
+ *
129
+ * @param packageName - Python package name (snake_case)
130
+ * @returns List of relative file paths
131
+ */
132
+ export function getAdminWizardFiles(packageName: string): string[] {
133
+ return [
134
+ // Backend
135
+ `apps/backend/src/${packageName}/middleware/__init__.py`,
136
+ `apps/backend/src/${packageName}/middleware/admin_auth.py`,
137
+ `apps/backend/src/${packageName}/routes/admin_db.py`,
138
+ // Frontend
139
+ 'apps/frontend/src/admin/useAdminApi.ts',
140
+ 'apps/frontend/src/admin/DbStatusBanner.tsx',
141
+ 'apps/frontend/src/admin/ConnectionForm.tsx',
142
+ 'apps/frontend/src/admin/MigrationProgress.tsx',
143
+ 'apps/frontend/src/admin/DbSetupStepper.tsx',
144
+ 'apps/frontend/src/admin/index.ts',
145
+ ];
146
+ }
@@ -16,6 +16,9 @@ import {
16
16
  generateUiPackage as generateUiPackageImpl,
17
17
  } from './shared-packages.js';
18
18
  import type { BrandColorOptions } from './shared-packages.js';
19
+ import { generateAllDockerComposeWithDb } from './templates/database-docker.js';
20
+ import { getAdminWizardFiles } from './admin-wizard.js';
21
+ import { getDatabaseFiles } from './database.js';
19
22
 
20
23
  /**
21
24
  * Options for all project generation
@@ -479,14 +482,14 @@ export async function generateAllProject(
479
482
  path: path.join(projectDir, '.popeye', 'workspace.json'),
480
483
  content: generateAllWorkspaceJson(projectName),
481
484
  },
482
- // Docker compose (override to include website)
485
+ // Docker compose (override to include website + postgres)
483
486
  {
484
487
  path: path.join(projectDir, 'docker-compose.yml'),
485
- content: generateAllDockerCompose(projectName),
488
+ content: generateAllDockerComposeWithDb(projectName),
486
489
  },
487
490
  {
488
491
  path: path.join(projectDir, 'infra', 'docker', 'docker-compose.yml'),
489
- content: generateAllDockerCompose(projectName),
492
+ content: generateAllDockerComposeWithDb(projectName),
490
493
  },
491
494
  // README
492
495
  {
@@ -562,6 +565,10 @@ export function getAllProjectFiles(projectName: string): string[] {
562
565
  'packages/ui/src/button.tsx',
563
566
  'packages/ui/src/card.tsx',
564
567
  'packages/contracts/.gitkeep',
568
+ // Database layer
569
+ ...getDatabaseFiles(packageName, 'sqlalchemy'),
570
+ // Admin wizard layer
571
+ ...getAdminWizardFiles(packageName),
565
572
  ];
566
573
  }
567
574
 
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Database generator orchestration module
3
+ * Creates complete database layers for fullstack and TS backend projects
4
+ */
5
+
6
+ import { promises as fs } from 'node:fs';
7
+ import path from 'node:path';
8
+ import type { BackendOrm } from '../types/database.js';
9
+ import {
10
+ generateDbConnection,
11
+ generateDbModels,
12
+ generateDbInit,
13
+ generateDbSettings,
14
+ generateAlembicIni,
15
+ generateAlembicEnvPy,
16
+ generateAlembicScriptMako,
17
+ generateInitialMigration,
18
+ generateDbVectorHelpers,
19
+ generateDbStartupHook,
20
+ generateDbHealthRoute,
21
+ generateDbConftest,
22
+ } from './templates/database-python.js';
23
+ import {
24
+ generatePrismaSchema,
25
+ generatePrismaClient,
26
+ generatePrismaSeed,
27
+ generatePrismaDbHealth,
28
+ generatePrismaVectorHelpers,
29
+ generatePrismaDbInit,
30
+ } from './templates/database-typescript.js';
31
+
32
+ /** Python database dependencies */
33
+ export const DB_PYTHON_DEPS = [
34
+ 'sqlalchemy[asyncio]>=2.0.0',
35
+ 'asyncpg>=0.29.0',
36
+ 'alembic>=1.13.0',
37
+ 'pgvector>=0.2.5',
38
+ ];
39
+
40
+ /**
41
+ * Options for database layer generation
42
+ */
43
+ interface DatabaseLayerOptions {
44
+ includeVector?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Create a directory if it doesn't exist
49
+ */
50
+ async function ensureDir(dirPath: string): Promise<void> {
51
+ await fs.mkdir(dirPath, { recursive: true });
52
+ }
53
+
54
+ /**
55
+ * Write a file with content
56
+ */
57
+ async function writeFile(filePath: string, content: string): Promise<void> {
58
+ await fs.writeFile(filePath, content, 'utf-8');
59
+ }
60
+
61
+ /**
62
+ * Generate the complete Python database layer for a backend project
63
+ *
64
+ * Creates database/, migrations/, and supporting files under apps/backend/
65
+ *
66
+ * @param projectDir - Root project directory (contains apps/)
67
+ * @param projectName - Project name (used for naming)
68
+ * @param packageName - Python package name (snake_case)
69
+ * @param options - Generation options
70
+ * @returns List of absolute file paths created
71
+ */
72
+ export async function generatePythonDatabaseLayer(
73
+ projectDir: string,
74
+ _projectName: string,
75
+ packageName: string,
76
+ options: DatabaseLayerOptions = {}
77
+ ): Promise<string[]> {
78
+ const includeVector = options.includeVector !== false;
79
+ const backendDir = path.join(projectDir, 'apps', 'backend');
80
+ const srcPkgDir = path.join(backendDir, 'src', packageName);
81
+ const filesCreated: string[] = [];
82
+
83
+ // Ensure directories exist
84
+ await ensureDir(path.join(srcPkgDir, 'database'));
85
+ await ensureDir(path.join(srcPkgDir, 'routes'));
86
+ await ensureDir(path.join(backendDir, 'migrations', 'versions'));
87
+ await ensureDir(path.join(backendDir, 'tests'));
88
+
89
+ // Define all files to generate
90
+ const files: Array<{ path: string; content: string }> = [
91
+ // Database package
92
+ {
93
+ path: path.join(srcPkgDir, 'database', '__init__.py'),
94
+ content: generateDbInit(packageName),
95
+ },
96
+ {
97
+ path: path.join(srcPkgDir, 'database', 'connection.py'),
98
+ content: generateDbConnection(packageName),
99
+ },
100
+ {
101
+ path: path.join(srcPkgDir, 'database', 'models.py'),
102
+ content: generateDbModels(packageName),
103
+ },
104
+ {
105
+ path: path.join(srcPkgDir, 'database', 'settings.py'),
106
+ content: generateDbSettings(packageName),
107
+ },
108
+ // Alembic migrations
109
+ {
110
+ path: path.join(backendDir, 'alembic.ini'),
111
+ content: generateAlembicIni(packageName),
112
+ },
113
+ {
114
+ path: path.join(backendDir, 'migrations', 'env.py'),
115
+ content: generateAlembicEnvPy(packageName),
116
+ },
117
+ {
118
+ path: path.join(backendDir, 'migrations', 'script.py.mako'),
119
+ content: generateAlembicScriptMako(),
120
+ },
121
+ {
122
+ path: path.join(backendDir, 'migrations', 'versions', '001_initial.py'),
123
+ content: generateInitialMigration(packageName),
124
+ },
125
+ // Startup hook
126
+ {
127
+ path: path.join(srcPkgDir, 'startup.py'),
128
+ content: generateDbStartupHook(packageName),
129
+ },
130
+ // Health route
131
+ {
132
+ path: path.join(srcPkgDir, 'routes', 'health_db.py'),
133
+ content: generateDbHealthRoute(packageName),
134
+ },
135
+ // Test fixtures
136
+ {
137
+ path: path.join(backendDir, 'tests', 'conftest_db.py'),
138
+ content: generateDbConftest(packageName),
139
+ },
140
+ ];
141
+
142
+ // Add vector helpers if requested
143
+ if (includeVector) {
144
+ files.push({
145
+ path: path.join(srcPkgDir, 'database', 'vector.py'),
146
+ content: generateDbVectorHelpers(packageName),
147
+ });
148
+ }
149
+
150
+ // Write all files
151
+ for (const file of files) {
152
+ await writeFile(file.path, file.content);
153
+ filesCreated.push(file.path);
154
+ }
155
+
156
+ // Augment requirements.txt with DB deps
157
+ const reqPath = path.join(backendDir, 'requirements.txt');
158
+ try {
159
+ const existingReqs = await fs.readFile(reqPath, 'utf-8');
160
+ const augmented = augmentRequirements(existingReqs, DB_PYTHON_DEPS);
161
+ await writeFile(reqPath, augmented);
162
+ } catch {
163
+ // requirements.txt doesn't exist yet - will be created by the main generator
164
+ }
165
+
166
+ return filesCreated;
167
+ }
168
+
169
+ /**
170
+ * Generate the complete TypeScript database layer for a project
171
+ *
172
+ * Creates prisma/ and src/db/ directories with Prisma-based DB files
173
+ * NOTE: Not wired into any generator in Phase 1
174
+ *
175
+ * @param projectDir - Project directory (where prisma/ and src/ live)
176
+ * @param projectName - Project name
177
+ * @param options - Generation options
178
+ * @returns List of absolute file paths created
179
+ */
180
+ export async function generateTypeScriptDatabaseLayer(
181
+ projectDir: string,
182
+ projectName: string,
183
+ _options: DatabaseLayerOptions = {}
184
+ ): Promise<string[]> {
185
+ const filesCreated: string[] = [];
186
+
187
+ // Ensure directories
188
+ await ensureDir(path.join(projectDir, 'prisma'));
189
+ await ensureDir(path.join(projectDir, 'src', 'db'));
190
+
191
+ const files: Array<{ path: string; content: string }> = [
192
+ {
193
+ path: path.join(projectDir, 'prisma', 'schema.prisma'),
194
+ content: generatePrismaSchema(projectName),
195
+ },
196
+ {
197
+ path: path.join(projectDir, 'prisma', 'seed.ts'),
198
+ content: generatePrismaSeed(),
199
+ },
200
+ {
201
+ path: path.join(projectDir, 'src', 'db', 'client.ts'),
202
+ content: generatePrismaClient(projectName),
203
+ },
204
+ {
205
+ path: path.join(projectDir, 'src', 'db', 'health.ts'),
206
+ content: generatePrismaDbHealth(),
207
+ },
208
+ {
209
+ path: path.join(projectDir, 'src', 'db', 'vector.ts'),
210
+ content: generatePrismaVectorHelpers(),
211
+ },
212
+ {
213
+ path: path.join(projectDir, 'src', 'db', 'index.ts'),
214
+ content: generatePrismaDbInit(),
215
+ },
216
+ ];
217
+
218
+ for (const file of files) {
219
+ await writeFile(file.path, file.content);
220
+ filesCreated.push(file.path);
221
+ }
222
+
223
+ return filesCreated;
224
+ }
225
+
226
+ /**
227
+ * Append database dependencies to an existing requirements.txt
228
+ *
229
+ * Idempotent: checks for existing "# Database" section to avoid duplicates.
230
+ * Handles empty input gracefully.
231
+ *
232
+ * @param baseContent - Existing requirements.txt content
233
+ * @param dbDeps - Database dependency strings to add
234
+ * @returns Augmented requirements.txt content
235
+ */
236
+ export function augmentRequirements(baseContent: string, dbDeps: string[]): string {
237
+ // Check for existing Database section
238
+ if (baseContent.includes('# Database')) {
239
+ return baseContent;
240
+ }
241
+
242
+ // Ensure trailing newline before adding section
243
+ const base = baseContent.trimEnd();
244
+ const depsBlock = dbDeps.join('\n');
245
+
246
+ return `${base}\n\n# Database\n${depsBlock}\n`;
247
+ }
248
+
249
+ /**
250
+ * Get the list of relative file paths generated for a database layer
251
+ *
252
+ * @param packageName - Python package name (for sqlalchemy) or project name (for prisma)
253
+ * @param orm - ORM type
254
+ * @returns List of relative file paths
255
+ */
256
+ export function getDatabaseFiles(packageName: string, orm: BackendOrm): string[] {
257
+ if (orm === 'sqlalchemy') {
258
+ return [
259
+ `apps/backend/src/${packageName}/database/__init__.py`,
260
+ `apps/backend/src/${packageName}/database/connection.py`,
261
+ `apps/backend/src/${packageName}/database/models.py`,
262
+ `apps/backend/src/${packageName}/database/settings.py`,
263
+ `apps/backend/src/${packageName}/database/vector.py`,
264
+ `apps/backend/src/${packageName}/routes/health_db.py`,
265
+ `apps/backend/src/${packageName}/startup.py`,
266
+ 'apps/backend/alembic.ini',
267
+ 'apps/backend/migrations/env.py',
268
+ 'apps/backend/migrations/script.py.mako',
269
+ 'apps/backend/migrations/versions/001_initial.py',
270
+ 'apps/backend/tests/conftest_db.py',
271
+ ];
272
+ }
273
+
274
+ if (orm === 'prisma') {
275
+ return [
276
+ 'prisma/schema.prisma',
277
+ 'prisma/seed.ts',
278
+ 'src/db/client.ts',
279
+ 'src/db/health.ts',
280
+ 'src/db/vector.ts',
281
+ 'src/db/index.ts',
282
+ ];
283
+ }
284
+
285
+ return [];
286
+ }
@@ -9,7 +9,6 @@ import type { ProjectSpec } from '../types/project.js';
9
9
  import type { GenerationResult } from './python.js';
10
10
  import {
11
11
  generateWorkspaceJson,
12
- generateRootDockerCompose,
13
12
  generateRootReadme,
14
13
  generateRootGitignore,
15
14
  generateFrontendReadme,
@@ -19,7 +18,6 @@ import {
19
18
  generateTailwindConfig,
20
19
  generatePostcssConfig,
21
20
  generateMainCss,
22
- generateAppTsx,
23
21
  generateMainTsx,
24
22
  generateIndexHtml,
25
23
  generateFrontendPackageJson,
@@ -30,10 +28,15 @@ import {
30
28
  generateFrontendTest,
31
29
  generateVitestSetup,
32
30
  generateFrontendVitestConfig,
33
- generateFastAPIMain,
34
31
  generateBackendDockerfile,
35
32
  generateFastAPIRequirements,
36
33
  } from './templates/fullstack.js';
34
+ import { generateAppTsxWithAdmin } from './templates/admin-wizard-react.js';
35
+ import { generateFastAPIMainWithAdmin } from './templates/admin-wizard-python.js';
36
+ import { generateDockerComposeWithDb, generateDbEnvExample } from './templates/database-docker.js';
37
+ import { generatePythonDatabaseLayer } from './database.js';
38
+ import { getDatabaseFiles } from './database.js';
39
+ import { generateAdminWizardLayer, getAdminWizardFiles } from './admin-wizard.js';
37
40
 
38
41
  /**
39
42
  * Options for fullstack project generation
@@ -112,11 +115,11 @@ export async function generateFullstackProject(
112
115
  // Docker
113
116
  {
114
117
  path: path.join(projectDir, 'infra', 'docker', 'docker-compose.yml'),
115
- content: generateRootDockerCompose(projectName),
118
+ content: generateDockerComposeWithDb(projectName),
116
119
  },
117
120
  {
118
121
  path: path.join(projectDir, 'docker-compose.yml'),
119
- content: generateRootDockerCompose(projectName),
122
+ content: generateDockerComposeWithDb(projectName),
120
123
  },
121
124
  // Documentation
122
125
  {
@@ -192,7 +195,7 @@ export async function generateFullstackProject(
192
195
  },
193
196
  {
194
197
  path: path.join(frontendDir, 'src', 'App.tsx'),
195
- content: generateAppTsx(projectName),
198
+ content: generateAppTsxWithAdmin(projectName),
196
199
  },
197
200
  {
198
201
  path: path.join(frontendDir, 'src', 'index.css'),
@@ -228,7 +231,7 @@ export async function generateFullstackProject(
228
231
  // Environment
229
232
  {
230
233
  path: path.join(frontendDir, '.env.example'),
231
- content: 'VITE_API_URL=http://localhost:8000\n',
234
+ content: 'VITE_API_URL=http://localhost:8000\nVITE_ADMIN_TOKEN=change-me-to-a-random-string\n',
232
235
  },
233
236
  {
234
237
  path: path.join(frontendDir, '.gitignore'),
@@ -265,7 +268,7 @@ export async function generateFullstackProject(
265
268
  },
266
269
  {
267
270
  path: path.join(backendDir, 'src', packageName, 'main.py'),
268
- content: generateFastAPIMain(projectName),
271
+ content: generateFastAPIMainWithAdmin(projectName, packageName),
269
272
  },
270
273
  // Test files
271
274
  {
@@ -293,7 +296,7 @@ export async function generateFullstackProject(
293
296
  // Environment
294
297
  {
295
298
  path: path.join(backendDir, '.env.example'),
296
- content: 'DEBUG=true\nDATABASE_URL=sqlite:///./data/app.db\n',
299
+ content: generateDbEnvExample(projectName),
297
300
  },
298
301
  {
299
302
  path: path.join(backendDir, '.gitignore'),
@@ -312,6 +315,16 @@ export async function generateFullstackProject(
312
315
  filesCreated.push(file.path);
313
316
  }
314
317
 
318
+ // Generate database layer (SQLAlchemy + Alembic + pgvector)
319
+ const dbFiles = await generatePythonDatabaseLayer(
320
+ projectDir, projectName, packageName, { includeVector: true }
321
+ );
322
+ filesCreated.push(...dbFiles);
323
+
324
+ // Generate admin wizard layer (middleware + routes + React components)
325
+ const adminFiles = await generateAdminWizardLayer(projectDir, packageName);
326
+ filesCreated.push(...adminFiles);
327
+
315
328
  return {
316
329
  success: true,
317
330
  projectDir,
@@ -520,6 +533,10 @@ export function getFullstackProjectFiles(projectName: string): string[] {
520
533
  'apps/backend/.env.example',
521
534
  'apps/backend/.gitignore',
522
535
  'apps/backend/Makefile',
536
+ // Database layer
537
+ ...getDatabaseFiles(packageName, 'sqlalchemy'),
538
+ // Admin wizard layer
539
+ ...getAdminWizardFiles(packageName),
523
540
  ];
524
541
  }
525
542
 
@@ -69,6 +69,18 @@ export {
69
69
  getAllProjectFiles,
70
70
  type AllGeneratorOptions,
71
71
  } from './all.js';
72
+ export {
73
+ generatePythonDatabaseLayer,
74
+ generateTypeScriptDatabaseLayer,
75
+ augmentRequirements,
76
+ getDatabaseFiles,
77
+ DB_PYTHON_DEPS,
78
+ } from './database.js';
79
+ export {
80
+ generateAdminWizardLayer,
81
+ getAdminWizardFiles,
82
+ ADMIN_WIZARD_PYTHON_DEPS,
83
+ } from './admin-wizard.js';
72
84
  export * from './templates/index.js';
73
85
 
74
86
  /**