create-jemo-app 0.1.0__tar.gz

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.
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: create-jemo-app
3
+ Version: 0.1.0
4
+ Summary: A CLI tool to scaffold modern web projects with FastAPI, Django, Convex, Next.js, SvelteKit, or TanStack Start.
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: questionary>=2.1.1
8
+ Requires-Dist: rich>=14.3.2
9
+ Requires-Dist: typer>=0.23.0
10
+
11
+ # Jemo Admin CLI
12
+
13
+ A powerful CLI tool to scaffold modern web projects with your favorite stack.
14
+
15
+ ## Supported Stacks
16
+
17
+ **Backend:**
18
+ - **FastAPI** (with Tortoise ORM & Aerich)
19
+ - **Django**
20
+ - **Convex** (Serverless)
21
+
22
+ **Frontend:**
23
+ - **Next.js**
24
+ - **SvelteKit**
25
+ - **TanStack Start**
26
+
27
+ ## Installation
28
+
29
+ You can install `jemo-admin` using `pipx`:
30
+
31
+ ```bash
32
+ pipx install .
33
+ # Or from a git repo:
34
+ # pipx install git+https://github.com/yourusername/create-jemo-app.git
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Create a new project interactively:
40
+
41
+ ```bash
42
+ jemo-admin create
43
+ ```
44
+
45
+ Or specify the project name directly:
46
+
47
+ ```bash
48
+ jemo-admin create my-awesome-app
49
+ ```
50
+
51
+ Follow the interactive prompts to select your backend and frontend framework.
52
+
53
+ ## Requirements
54
+
55
+ - **Python 3.12+**
56
+ - **Bun** (for frontend package management)
57
+ - **UV** (for Python package management)
@@ -0,0 +1,47 @@
1
+ # Jemo Admin CLI
2
+
3
+ A powerful CLI tool to scaffold modern web projects with your favorite stack.
4
+
5
+ ## Supported Stacks
6
+
7
+ **Backend:**
8
+ - **FastAPI** (with Tortoise ORM & Aerich)
9
+ - **Django**
10
+ - **Convex** (Serverless)
11
+
12
+ **Frontend:**
13
+ - **Next.js**
14
+ - **SvelteKit**
15
+ - **TanStack Start**
16
+
17
+ ## Installation
18
+
19
+ You can install `jemo-admin` using `pipx`:
20
+
21
+ ```bash
22
+ pipx install .
23
+ # Or from a git repo:
24
+ # pipx install git+https://github.com/yourusername/create-jemo-app.git
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ Create a new project interactively:
30
+
31
+ ```bash
32
+ jemo-admin create
33
+ ```
34
+
35
+ Or specify the project name directly:
36
+
37
+ ```bash
38
+ jemo-admin create my-awesome-app
39
+ ```
40
+
41
+ Follow the interactive prompts to select your backend and frontend framework.
42
+
43
+ ## Requirements
44
+
45
+ - **Python 3.12+**
46
+ - **Bun** (for frontend package management)
47
+ - **UV** (for Python package management)
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "create-jemo-app"
3
+ version = "0.1.0"
4
+ description = "A CLI tool to scaffold modern web projects with FastAPI, Django, Convex, Next.js, SvelteKit, or TanStack Start."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "questionary>=2.1.1",
9
+ "rich>=14.3.2",
10
+ "typer>=0.23.0",
11
+ ]
12
+
13
+ [project.scripts]
14
+ jemo-admin = "jemo_admin.main:app"
15
+
16
+ [tool.uv.workspace]
17
+ members = [
18
+ "test/test-app/backend",
19
+ "test/my-jemo-app/backend",
20
+ "test/reproduce_error/backend",
21
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: create-jemo-app
3
+ Version: 0.1.0
4
+ Summary: A CLI tool to scaffold modern web projects with FastAPI, Django, Convex, Next.js, SvelteKit, or TanStack Start.
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: questionary>=2.1.1
8
+ Requires-Dist: rich>=14.3.2
9
+ Requires-Dist: typer>=0.23.0
10
+
11
+ # Jemo Admin CLI
12
+
13
+ A powerful CLI tool to scaffold modern web projects with your favorite stack.
14
+
15
+ ## Supported Stacks
16
+
17
+ **Backend:**
18
+ - **FastAPI** (with Tortoise ORM & Aerich)
19
+ - **Django**
20
+ - **Convex** (Serverless)
21
+
22
+ **Frontend:**
23
+ - **Next.js**
24
+ - **SvelteKit**
25
+ - **TanStack Start**
26
+
27
+ ## Installation
28
+
29
+ You can install `jemo-admin` using `pipx`:
30
+
31
+ ```bash
32
+ pipx install .
33
+ # Or from a git repo:
34
+ # pipx install git+https://github.com/yourusername/create-jemo-app.git
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Create a new project interactively:
40
+
41
+ ```bash
42
+ jemo-admin create
43
+ ```
44
+
45
+ Or specify the project name directly:
46
+
47
+ ```bash
48
+ jemo-admin create my-awesome-app
49
+ ```
50
+
51
+ Follow the interactive prompts to select your backend and frontend framework.
52
+
53
+ ## Requirements
54
+
55
+ - **Python 3.12+**
56
+ - **Bun** (for frontend package management)
57
+ - **UV** (for Python package management)
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/create_jemo_app.egg-info/PKG-INFO
4
+ src/create_jemo_app.egg-info/SOURCES.txt
5
+ src/create_jemo_app.egg-info/dependency_links.txt
6
+ src/create_jemo_app.egg-info/entry_points.txt
7
+ src/create_jemo_app.egg-info/requires.txt
8
+ src/create_jemo_app.egg-info/top_level.txt
9
+ src/jemo_admin/main.py
10
+ src/jemo_admin/generators/backend.py
11
+ src/jemo_admin/generators/frontend.py
12
+ src/jemo_admin/generators/vcs.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ jemo-admin = jemo_admin.main:app
@@ -0,0 +1,3 @@
1
+ questionary>=2.1.1
2
+ rich>=14.3.2
3
+ typer>=0.23.0
@@ -0,0 +1,163 @@
1
+ from pathlib import Path
2
+ import subprocess
3
+ import shutil
4
+ from rich.console import Console
5
+
6
+ console = Console()
7
+
8
+
9
+ def run_command(command, cwd=None, shell=False):
10
+ """Run a shell command."""
11
+ try:
12
+ # Capture output to show on error
13
+ subprocess.run(command, cwd=cwd, shell=shell, check=True, capture_output=False)
14
+ except subprocess.CalledProcessError as e:
15
+ console.print(f"[red]Error executing command: {command}[/red]")
16
+ console.print(f"[red]Exit Code: {e.returncode}[/red]")
17
+ if e.stdout:
18
+ console.print(f"[red]Stdout: {e.stdout.decode()}[/red]")
19
+ if e.stderr:
20
+ console.print(f"[red]Stderr: {e.stderr.decode()}[/red]")
21
+ raise e
22
+
23
+
24
+ def create_fastapi(project_dir: Path, separate_folders: bool = True):
25
+ """Scaffold a FastAPI project with Tortoise ORM and Aerich."""
26
+
27
+ backend_dir = project_dir / "backend"
28
+ if separate_folders:
29
+ backend_dir.mkdir(parents=True, exist_ok=True)
30
+ else:
31
+ backend_dir = project_dir # If not separate folders, use project dir directly (uncommon for backend+frontend combo)
32
+
33
+ console.print(
34
+ f"[bold cyan]Initializing FastAPI backend in {backend_dir}...[/bold cyan]"
35
+ )
36
+
37
+ # Initialize UV project
38
+ # Use --no-workspace to ensure we create a standalone project
39
+ run_command(["uv", "init", "--app", "--no-workspace"], cwd=backend_dir)
40
+
41
+ # Add dependencies
42
+ console.print(
43
+ "[bold]Adding dependencies: fastapi, uvicorn, tortoise-orm, aerich...[/bold]"
44
+ )
45
+ run_command(
46
+ ["uv", "add", "fastapi", "uvicorn", "tortoise-orm", "aerich"], cwd=backend_dir
47
+ )
48
+
49
+ # Create main.py
50
+ main_py_content = """
51
+ from fastapi import FastAPI
52
+ from tortoise import Tortoise
53
+ from tortoise.contrib.fastapi import register_tortoise
54
+
55
+ app = FastAPI(title="Jemo Admin API")
56
+
57
+ @app.get("/")
58
+ async def read_root():
59
+ return {"message": "Welcome to Jemo Admin FastAPI Backend with Tortoise ORM!"}
60
+
61
+ # Database Configuration
62
+ TORTOISE_ORM = {
63
+ "connections": {"default": "sqlite://db.sqlite3"},
64
+ "apps": {
65
+ "models": {
66
+ "models": ["aerich.models", "models"], # Add your models here
67
+ "default_connection": "default",
68
+ },
69
+ },
70
+ }
71
+
72
+ register_tortoise(
73
+ app,
74
+ config=TORTOISE_ORM,
75
+ generate_schemas=True,
76
+ add_exception_handlers=True,
77
+ )
78
+ """
79
+ (backend_dir / "main.py").write_text(main_py_content)
80
+
81
+ # Create models.py
82
+ models_py_content = """
83
+ from tortoise import fields, models
84
+
85
+ class User(models.Model):
86
+ id = fields.IntField(pk=True)
87
+ username = fields.CharField(max_length=50, unique=True)
88
+ email = fields.CharField(max_length=255, unique=True)
89
+ created_at = fields.DatetimeField(auto_now_add=True)
90
+
91
+ def __str__(self):
92
+ return self.username
93
+ """
94
+ (backend_dir / "models.py").write_text(models_py_content)
95
+
96
+ # Create tortoise_conf.py for Aerich
97
+ tortoise_conf_content = """
98
+ TORTOISE_ORM = {
99
+ "connections": {"default": "sqlite://db.sqlite3"},
100
+ "apps": {
101
+ "models": {
102
+ "models": ["aerich.models", "models"],
103
+ "default_connection": "default",
104
+ },
105
+ },
106
+ }
107
+ """
108
+ (backend_dir / "tortoise_conf.py").write_text(tortoise_conf_content)
109
+
110
+ console.print("[green]FastAPI backend setup complete![/green]")
111
+
112
+
113
+ def create_django(project_dir: Path, separate_folders: bool = True):
114
+ """Scaffold a Django project."""
115
+
116
+ backend_dir = project_dir / "backend"
117
+ if separate_folders:
118
+ backend_dir.mkdir(parents=True, exist_ok=True)
119
+ else:
120
+ backend_dir = project_dir
121
+
122
+ console.print(
123
+ f"[bold cyan]Initializing Django backend in {backend_dir}...[/bold cyan]"
124
+ )
125
+
126
+ # Initialize UV project
127
+ # Use --no-workspace to ensure we create a standalone project
128
+ run_command(["uv", "init", "--app", "--no-workspace"], cwd=backend_dir)
129
+
130
+ # Add Django
131
+ console.print("[bold]Adding dependencies: django...[/bold]")
132
+ run_command(["uv", "add", "django"], cwd=backend_dir)
133
+
134
+ # Create Django Project
135
+ # We use 'config' as the project name usually, or 'core'. Let's use 'config'.
136
+ console.print("[bold]Running django-admin startproject...[/bold]")
137
+ run_command(
138
+ ["uv", "run", "django-admin", "startproject", "config", "."], cwd=backend_dir
139
+ )
140
+
141
+ console.print("[green]Django backend setup complete![/green]")
142
+
143
+
144
+ def create_convex_standalone(project_dir: Path):
145
+ """Initialize Convex in a standalone directory (rarely used without frontend)."""
146
+ console.print(f"[bold cyan]Initializing Convex in {project_dir}...[/bold cyan]")
147
+
148
+ project_dir.mkdir(parents=True, exist_ok=True)
149
+
150
+ # Check if npm/bun init is needed first
151
+ if not (project_dir / "package.json").exists():
152
+ run_command(["bun", "init", "-y"], cwd=project_dir)
153
+
154
+ run_command(["bun", "add", "convex"], cwd=project_dir)
155
+ run_command(
156
+ ["npx", "convex", "dev"], cwd=project_dir
157
+ ) # This is interactive usually!
158
+ # Wait, 'npx convex dev' is interactive and blocking. We probably shouldn't run it here.
159
+ # We should just install it and tell user to run it.
160
+
161
+ console.print(
162
+ "[green]Convex setup complete! Run 'npx convex dev' to start.[/green]"
163
+ )
@@ -0,0 +1,186 @@
1
+ from pathlib import Path
2
+ import subprocess
3
+ import shutil
4
+ from rich.console import Console
5
+
6
+ console = Console()
7
+
8
+
9
+ def run_command(command, cwd=None, capture_output=False):
10
+ """Run a shell command."""
11
+ try:
12
+ result = subprocess.run(
13
+ command, cwd=cwd, check=True, capture_output=capture_output, text=True
14
+ )
15
+ return result
16
+ except subprocess.CalledProcessError as e:
17
+ console.print(f"[red]Error executing command: {' '.join(command)}[/red]")
18
+ if e.stderr:
19
+ console.print(f"[red]{e.stderr}[/red]")
20
+ raise e
21
+
22
+
23
+ def add_dependency(package_manager, package, cwd, dev=False):
24
+ """Add a dependency."""
25
+ cmd = [package_manager, "add", "-d" if dev else "", package]
26
+ cmd = [c for c in cmd if c]
27
+ subprocess.run(cmd, cwd=cwd, check=True)
28
+
29
+
30
+ def install_tailwind(project_dir: Path, framework: str):
31
+ """Install and configure Tailwind CSS."""
32
+ console.print(f"[bold]Setting up Tailwind CSS for {framework}...[/bold]")
33
+
34
+ if framework == "nextjs":
35
+ # Next.js with --yes already includes Tailwind by default
36
+ console.print(
37
+ "[green]Tailwind CSS already included with Next.js defaults[/green]"
38
+ )
39
+ return
40
+
41
+ # For other frameworks, install Tailwind v4 (zero config)
42
+ try:
43
+ # Install tailwindcss v4
44
+ run_command(
45
+ ["bun", "add", "-d", "tailwindcss"],
46
+ cwd=project_dir,
47
+ )
48
+
49
+ console.print("[green]Tailwind CSS v4 installed (zero config mode)[/green]")
50
+ console.print("[yellow]Note: Import Tailwind in your CSS file:[/yellow]")
51
+ console.print(' @import "tailwindcss";')
52
+ except Exception as e:
53
+ console.print(
54
+ f"[yellow]Warning: Could not auto-configure Tailwind. You may need to set it up manually.[/yellow]"
55
+ )
56
+
57
+
58
+ def create_nextjs(
59
+ project_dir: Path, subfolder: str = "frontend", use_convex: bool = False
60
+ ):
61
+ """Scaffold a Next.js project with Tailwind CSS."""
62
+ target_dir = project_dir / subfolder if subfolder != "." else project_dir
63
+ console.print(f"[bold cyan]Initializing Next.js in {target_dir}...[/bold cyan]")
64
+
65
+ if not project_dir.exists():
66
+ project_dir.mkdir(parents=True)
67
+
68
+ # Use --yes for non-interactive mode with defaults (includes Tailwind)
69
+ cmd = ["bun", "create", "next-app", subfolder if subfolder != "." else ".", "--yes"]
70
+ try:
71
+ run_command(cmd, cwd=project_dir)
72
+ except Exception:
73
+ console.print("[red]Failed to create Next.js project[/red]")
74
+ return
75
+
76
+ if not target_dir.exists():
77
+ console.print(f"[red]Error: Directory {target_dir} was not created.[/red]")
78
+ return
79
+
80
+ # Tailwind is already included with --yes flag (default Next.js setup includes it)
81
+ console.print("[green]Next.js with Tailwind CSS created successfully![/green]")
82
+
83
+ if use_convex:
84
+ console.print("[bold]Adding Convex to Next.js...[/bold]")
85
+ add_dependency("bun", "convex", cwd=target_dir)
86
+ console.print(
87
+ "[yellow]Note: Run 'npx convex dev' in the frontend directory to configure Convex.[/yellow]"
88
+ )
89
+
90
+
91
+ def create_sveltekit(
92
+ project_dir: Path, subfolder: str = "frontend", use_convex: bool = False
93
+ ):
94
+ """Scaffold a SvelteKit project with Tailwind CSS."""
95
+ target_dir = project_dir / subfolder if subfolder != "." else project_dir
96
+ console.print(f"[bold cyan]Initializing SvelteKit in {target_dir}...[/bold cyan]")
97
+
98
+ if not project_dir.exists():
99
+ project_dir.mkdir(parents=True)
100
+
101
+ # Use npx sv create with flags for non-interactive mode
102
+ folder_arg = subfolder if subfolder != "." else "."
103
+ cmd = [
104
+ "npx",
105
+ "sv",
106
+ "create",
107
+ folder_arg,
108
+ "--template",
109
+ "demo", # Use demo template (includes more features)
110
+ "--types",
111
+ "ts", # TypeScript
112
+ "--no-add-ons", # Don't prompt for add-ons
113
+ "--install",
114
+ "bun", # Use bun to install
115
+ ]
116
+
117
+ try:
118
+ run_command(cmd, cwd=project_dir)
119
+ except Exception:
120
+ console.print("[red]Failed to create SvelteKit project[/red]")
121
+ return
122
+
123
+ if not target_dir.exists():
124
+ console.print(
125
+ f"[red]Error: Directory {target_dir} was not created. Skipping subsequent steps.[/red]"
126
+ )
127
+ return
128
+
129
+ console.print("[bold]Installing dependencies...[/bold]")
130
+ run_command(["bun", "install"], cwd=target_dir)
131
+
132
+ # Add Tailwind CSS
133
+ install_tailwind(target_dir, "sveltekit")
134
+
135
+ if use_convex:
136
+ console.print("[bold]Adding Convex to SvelteKit...[/bold]")
137
+ add_dependency("bun", "convex", cwd=target_dir)
138
+ console.print(
139
+ "[yellow]Note: Run 'npx convex dev' in the frontend directory to configure Convex.[/yellow]"
140
+ )
141
+
142
+
143
+ def create_tanstack(
144
+ project_dir: Path, subfolder: str = "frontend", use_convex: bool = False
145
+ ):
146
+ """Scaffold a TanStack Start project with Tailwind CSS."""
147
+ target_dir = project_dir / subfolder if subfolder != "." else project_dir
148
+ console.print(
149
+ f"[bold cyan]Initializing TanStack Start in {target_dir}...[/bold cyan]"
150
+ )
151
+
152
+ if not project_dir.exists():
153
+ project_dir.mkdir(parents=True)
154
+
155
+ # Use create subcommand (required for @tanstack/start v1)
156
+ folder_arg = subfolder if subfolder != "." else "."
157
+ cmd = [
158
+ "bun",
159
+ "create",
160
+ "@tanstack/start",
161
+ "create", # Subcommand required
162
+ folder_arg,
163
+ "--package-manager",
164
+ "bun",
165
+ "--no-git", # We'll handle git separately
166
+ ]
167
+
168
+ try:
169
+ run_command(cmd, cwd=project_dir)
170
+ except Exception:
171
+ console.print("[red]Failed to create TanStack Start project[/red]")
172
+ return
173
+
174
+ if not target_dir.exists():
175
+ console.print(f"[red]Error: Directory {target_dir} was not created.[/red]")
176
+ return
177
+
178
+ # Add Tailwind CSS
179
+ install_tailwind(target_dir, "tanstack")
180
+
181
+ if use_convex:
182
+ console.print("[bold]Adding Convex to TanStack Start...[/bold]")
183
+ add_dependency("bun", "convex", cwd=target_dir)
184
+ console.print(
185
+ "[yellow]Note: Run 'npx convex dev' in the frontend directory to configure Convex.[/yellow]"
186
+ )
@@ -0,0 +1,42 @@
1
+ from pathlib import Path
2
+ import subprocess
3
+ from rich.console import Console
4
+
5
+ console = Console()
6
+
7
+
8
+ def run_command(command, cwd=None):
9
+ """Run a shell command."""
10
+ try:
11
+ subprocess.run(command, cwd=cwd, check=True, stdout=subprocess.DEVNULL)
12
+ except subprocess.CalledProcessError as e:
13
+ console.print(f"[red]Error executing command: {' '.join(command)}[/red]")
14
+ # Don't raise, just warn for VCS
15
+ console.print(
16
+ f"[yellow]VCS initialization failed. You may need to install the tool or init manually.[/yellow]"
17
+ )
18
+
19
+
20
+ def init_git(project_dir: Path):
21
+ """Initialize a Git repository."""
22
+ console.print(
23
+ f"[bold cyan]Initializing Git repository in {project_dir}...[/bold cyan]"
24
+ )
25
+ run_command(["git", "init"], cwd=project_dir)
26
+ # create .gitignore if it doesn't exist?
27
+ # Usually frameworks create their own .gitignores.
28
+ # We might want to stage files.
29
+ # run_command(["git", "add", "."], cwd=project_dir)
30
+ # console.print("[green]Git initialized.[/green]")
31
+
32
+
33
+ def init_jj(project_dir: Path):
34
+ """Initialize a Jujutsu repository (git-backed)."""
35
+ console.print(
36
+ f"[bold cyan]Initializing Jujutsu (jj) repository in {project_dir}...[/bold cyan]"
37
+ )
38
+
39
+ # 'jj git init' creates a repo backed by git, which is most common for interop.
40
+ run_command(["jj", "git", "init"], cwd=project_dir)
41
+
42
+ console.print("[green]Jujutsu initialized.[/green]")
@@ -0,0 +1,194 @@
1
+ import typer
2
+ import questionary
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+ from pathlib import Path
6
+ import subprocess
7
+ import shutil
8
+ import sys
9
+ import os
10
+
11
+ app = typer.Typer(
12
+ help="CLI tool to scaffold modern web projects.", add_completion=False
13
+ )
14
+ console = Console()
15
+
16
+
17
+ @app.callback()
18
+ def main():
19
+ """
20
+ Jemo Admin CLI to scaffold modern web projects.
21
+ """
22
+ pass
23
+
24
+
25
+ @app.command()
26
+ def info():
27
+ """
28
+ Show information about the CLI.
29
+ """
30
+ console.print(Panel.fit("Jemo Admin CLI v0.1.0", border_style="cyan"))
31
+ console.print("Powered by [bold]Typer[/bold] and [bold]Rich[/bold].")
32
+
33
+
34
+ @app.command()
35
+ def create(
36
+ project_name: str = typer.Argument(
37
+ None, help="The name of the project directory (optional)."
38
+ ),
39
+ ):
40
+ """
41
+ Create a new project with your choice of backend and frontend.
42
+ """
43
+ console.print(
44
+ Panel.fit(
45
+ "Welcome to [bold cyan]jemo-admin[/bold cyan]! Let's build something great.",
46
+ border_style="cyan",
47
+ )
48
+ )
49
+
50
+ # 1. Project Name / Directory
51
+ if not project_name:
52
+ project_name = questionary.text(
53
+ "What is your project name?", default="my-jemo-app"
54
+ ).ask()
55
+
56
+ if not project_name:
57
+ console.print("[red]Project name is required![/red]")
58
+ raise typer.Exit(code=1)
59
+
60
+ # Resolve project directory
61
+ project_dir = Path.cwd() / project_name
62
+
63
+ if project_dir.exists() and any(project_dir.iterdir()):
64
+ overwrite = questionary.confirm(
65
+ f"Directory '{project_name}' is not empty. Do you want to continue (files may be overwritten)?"
66
+ ).ask()
67
+ if not overwrite:
68
+ console.print("[yellow]Aborted.[/yellow]")
69
+ raise typer.Exit(code=0)
70
+
71
+ # 2. Select Backend
72
+ backend_choice = questionary.select(
73
+ "Select your backend framework:",
74
+ choices=[
75
+ "FastAPI (Tortoise ORM + Aerich)",
76
+ "Django",
77
+ "Convex (Serverless)",
78
+ "None (Frontend Only)",
79
+ ],
80
+ ).ask()
81
+
82
+ # 3. Select Frontend
83
+ frontend_choice = questionary.select(
84
+ "Select your frontend framework:",
85
+ choices=["Next.js", "SvelteKit", "TanStack Start", "None (Backend Only)"],
86
+ ).ask()
87
+
88
+ # 4. Select Version Control
89
+ vcs_choice = questionary.select(
90
+ "Select your Version Control System:",
91
+ choices=["Git", "Jujutsu (jj)", "None"],
92
+ ).ask()
93
+
94
+ if not backend_choice or not frontend_choice or not vcs_choice:
95
+ # Handle cancellation (Ctrl+C)
96
+ console.print("[yellow]Operation cancelled.[/yellow]")
97
+ raise typer.Exit(code=0)
98
+
99
+ if "None" in backend_choice and "None" in frontend_choice:
100
+ console.print("[red]You must select at least a backend or a frontend![/red]")
101
+ raise typer.Exit(code=1)
102
+
103
+ # Confirm Plan
104
+ console.print(f"\n[bold green]Plan:[/bold green]")
105
+ console.print(f" Project: [cyan]{project_name}[/cyan]")
106
+ console.print(f" Backend: [cyan]{backend_choice}[/cyan]")
107
+ console.print(f" Frontend: [cyan]{frontend_choice}[/cyan]")
108
+ console.print(f" VCS: [cyan]{vcs_choice}[/cyan]")
109
+
110
+ if not questionary.confirm("Does this look correct?").ask():
111
+ console.print("[yellow]Aborted.[/yellow]")
112
+ raise typer.Exit(code=0)
113
+
114
+ # Create Project Directory
115
+ if not project_dir.exists():
116
+ project_dir.mkdir(parents=True)
117
+
118
+ console.print("\n[bold]Scaffolding project...[/bold]")
119
+
120
+ # Import generators here to avoid circular imports or issues before structure is ready
121
+ from jemo_admin.generators import backend as backend_gen
122
+ from jemo_admin.generators import frontend as frontend_gen
123
+ from jemo_admin.generators import vcs as vcs_gen
124
+
125
+ # Determine types
126
+ is_convex = "Convex" in backend_choice
127
+ is_fastapi = "FastAPI" in backend_choice
128
+ is_django = "Django" in backend_choice
129
+
130
+ # Backend Generation
131
+ if is_fastapi:
132
+ backend_gen.create_fastapi(project_dir)
133
+ elif is_django:
134
+ backend_gen.create_django(project_dir)
135
+ elif is_convex and "None" in frontend_choice:
136
+ # Standalone Convex (rare but possible)
137
+ backend_gen.create_convex_standalone(project_dir)
138
+
139
+ # Frontend Generation
140
+ frontend_subfolder = "frontend" if (is_fastapi or is_django) else "."
141
+
142
+ if "Next.js" in frontend_choice:
143
+ frontend_gen.create_nextjs(
144
+ project_dir, subfolder=frontend_subfolder, use_convex=is_convex
145
+ )
146
+ elif "SvelteKit" in frontend_choice:
147
+ frontend_gen.create_sveltekit(
148
+ project_dir, subfolder=frontend_subfolder, use_convex=is_convex
149
+ )
150
+ elif "TanStack" in frontend_choice:
151
+ frontend_gen.create_tanstack(
152
+ project_dir, subfolder=frontend_subfolder, use_convex=is_convex
153
+ )
154
+
155
+ # VCS Initialization
156
+ if "Git" in vcs_choice:
157
+ vcs_gen.init_git(project_dir)
158
+ elif "Jujutsu" in vcs_choice:
159
+ vcs_gen.init_jj(project_dir)
160
+
161
+ console.print(f"\n[bold green]Successfully created {project_name}![/bold green]")
162
+ console.print("\n[bold]To get started:[/bold]")
163
+ console.print(f" cd {project_name}")
164
+
165
+ if is_fastapi:
166
+ console.print("\n [bold cyan]Backend (FastAPI):[/bold cyan]")
167
+ console.print(" cd backend")
168
+ console.print(" uv run aerich init -t tortoise_conf.TORTOISE_ORM")
169
+ console.print(" uv run aerich init-db")
170
+ console.print(" uv run uvicorn main:app --reload")
171
+ elif is_django:
172
+ console.print("\n [bold cyan]Backend (Django):[/bold cyan]")
173
+ console.print(" cd backend")
174
+ console.print(" uv run python manage.py migrate")
175
+ console.print(" uv run python manage.py runserver")
176
+
177
+ if "None" not in frontend_choice:
178
+ console.print("\n [bold cyan]Frontend:[/bold cyan]")
179
+ if is_fastapi or is_django:
180
+ console.print(" cd ../frontend")
181
+ # If single folder (Convex/None backend), we are already in root (or just cd into it initially)
182
+
183
+ console.print(" bun install")
184
+ if is_convex:
185
+ console.print(" npx convex dev")
186
+ console.print(" bun dev")
187
+
188
+
189
+ if __name__ == "__main__":
190
+ try:
191
+ app()
192
+ except KeyboardInterrupt:
193
+ console.print("\n[yellow]Operation cancelled by user.[/yellow]")
194
+ sys.exit(0)