stup 0.1.0__py3-none-any.whl

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.
app/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """stup — Scaffold production-ready project structures in seconds."""
2
+
3
+ __version__ = "0.1.0"
app/cli.py ADDED
@@ -0,0 +1,173 @@
1
+ """stup CLI — Main entry point."""
2
+
3
+ import typer
4
+ from rich.console import Console
5
+
6
+ from app import __version__
7
+ from app.commands import (
8
+ activate,
9
+ add,
10
+ api,
11
+ cli_cmd,
12
+ django_cmd,
13
+ docs,
14
+ lang_agent,
15
+ mean,
16
+ mern,
17
+ ml,
18
+ next_django,
19
+ notebook,
20
+ openai_agent,
21
+ react_fastapi,
22
+ saas,
23
+ scraper,
24
+ test_cmd,
25
+ uv,
26
+ )
27
+
28
+ console = Console()
29
+
30
+ app = typer.Typer(
31
+ name="stup",
32
+ help="Scaffold production-ready project structures in seconds.",
33
+ add_completion=False,
34
+ rich_markup_mode="rich",
35
+ no_args_is_help=True,
36
+ )
37
+
38
+
39
+ def _version_callback(value: bool) -> None:
40
+ if value:
41
+ console.print(f"[bold cyan]stup[/bold cyan] v{__version__}")
42
+ raise typer.Exit()
43
+
44
+
45
+ @app.callback()
46
+ def main(
47
+ version: bool = typer.Option(
48
+ False, "--version", "-v", help="Show version and exit.", callback=_version_callback, is_eager=True
49
+ ),
50
+ ) -> None:
51
+ """stup — Install once, scaffold forever."""
52
+
53
+
54
+ # ── Foundation commands ──────────────────────────────────────────────
55
+
56
+ @app.command()
57
+ def uv_cmd() -> None:
58
+ """[bold cyan]Foundation[/bold cyan] · Bootstrap a complete uv Python project."""
59
+ uv.run_command()
60
+
61
+
62
+ # Register as "uv" not "uv-cmd"
63
+ uv_cmd.__name__ = "uv" # type: ignore[attr-defined]
64
+ uv_cmd = app.registered_commands[-1]
65
+ uv_cmd.name = "uv"
66
+
67
+
68
+ @app.command(name="activate")
69
+ def activate_cmd() -> None:
70
+ """[bold cyan]Foundation[/bold cyan] · Smart cross-platform venv activator."""
71
+ activate.run_command()
72
+
73
+
74
+ @app.command(name="add")
75
+ def add_cmd(packages: list[str]) -> None:
76
+ """[bold cyan]Foundation[/bold cyan] · Add packages and sync requirements.txt."""
77
+ add.run_command(packages)
78
+
79
+
80
+ # ── Template commands ────────────────────────────────────────────────
81
+
82
+ @app.command(name="react-fastapi")
83
+ def react_fastapi_cmd() -> None:
84
+ """[bold magenta]Full-stack[/bold magenta] · React + FastAPI with Docker Compose & CORS."""
85
+ react_fastapi.run_command()
86
+
87
+
88
+ @app.command(name="notebook")
89
+ def notebook_cmd() -> None:
90
+ """[bold green]Data Science[/bold green] · Jupyter notebooks with uv-managed deps."""
91
+ notebook.run_command()
92
+
93
+
94
+ @app.command(name="mern")
95
+ def mern_cmd() -> None:
96
+ """[bold magenta]Full-stack[/bold magenta] · MongoDB + Express + React + Node."""
97
+ mern.run_command()
98
+
99
+
100
+ @app.command(name="mean")
101
+ def mean_cmd() -> None:
102
+ """[bold magenta]Full-stack[/bold magenta] · MongoDB + Express + Angular + Node."""
103
+ mean.run_command()
104
+
105
+
106
+ @app.command(name="django")
107
+ def django_cmd_fn() -> None:
108
+ """[bold blue]Backend[/bold blue] · Django + DRF + Celery + Redis + PostgreSQL."""
109
+ django_cmd.run_command()
110
+
111
+
112
+ @app.command(name="next-django")
113
+ def next_django_cmd() -> None:
114
+ """[bold magenta]Full-stack[/bold magenta] · Next.js 14 + Django REST + JWT auth."""
115
+ next_django.run_command()
116
+
117
+
118
+ @app.command(name="ml")
119
+ def ml_cmd() -> None:
120
+ """[bold green]Data Science[/bold green] · ML project with experiment tracking & MLflow."""
121
+ ml.run_command()
122
+
123
+
124
+ @app.command(name="lang-agent")
125
+ def lang_agent_cmd() -> None:
126
+ """[bold yellow]AI[/bold yellow] · LangGraph agent with tool stubs & Ollama config."""
127
+ lang_agent.run_command()
128
+
129
+
130
+ @app.command(name="openai-agent")
131
+ def openai_agent_cmd() -> None:
132
+ """[bold yellow]AI[/bold yellow] · Modern OpenAI agent with function calling."""
133
+ openai_agent.run_command()
134
+
135
+
136
+ @app.command(name="scraper")
137
+ def scraper_cmd() -> None:
138
+ """[bold yellow]Automation[/bold yellow] · Playwright + BeautifulSoup scraping pipeline."""
139
+ scraper.run_command()
140
+
141
+
142
+ @app.command(name="cli")
143
+ def cli_cmd_fn() -> None:
144
+ """[bold blue]Package[/bold blue] · PyPI-ready CLI package with Typer + Rich."""
145
+ cli_cmd.run_command()
146
+
147
+
148
+ @app.command(name="api")
149
+ def api_cmd() -> None:
150
+ """[bold blue]Backend[/bold blue] · Minimal FastAPI microservice with auth middleware."""
151
+ api.run_command()
152
+
153
+
154
+ @app.command(name="saas")
155
+ def saas_cmd() -> None:
156
+ """[bold magenta]Full-stack[/bold magenta] · Full SaaS boilerplate with Stripe & Docker."""
157
+ saas.run_command()
158
+
159
+
160
+ @app.command(name="docs")
161
+ def docs_cmd() -> None:
162
+ """[bold dim]Addon[/bold dim] · Add MkDocs + Material theme to any project."""
163
+ docs.run_command()
164
+
165
+
166
+ @app.command(name="test")
167
+ def test_cmd_fn() -> None:
168
+ """[bold dim]Addon[/bold dim] · Scaffold a full pytest suite with coverage."""
169
+ test_cmd.run_command()
170
+
171
+
172
+ if __name__ == "__main__":
173
+ app()
@@ -0,0 +1 @@
1
+ """stup.commands — All CLI command implementations."""
@@ -0,0 +1,33 @@
1
+ """stup activate — Smart cross-platform venv activator."""
2
+
3
+ from app.utils import (
4
+ console,
5
+ ensure_venv_exists,
6
+ get_activate_command,
7
+ is_windows,
8
+ print_banner,
9
+ )
10
+
11
+
12
+ def run_command() -> None:
13
+ """Print the correct venv activation command for the current platform."""
14
+ print_banner("activate", "Smart cross-platform venv activator")
15
+
16
+ ensure_venv_exists()
17
+
18
+ cmd = get_activate_command()
19
+
20
+ console.print()
21
+ if is_windows():
22
+ console.print(" [yellow]→[/yellow] Run this in PowerShell:")
23
+ console.print(f" [bold white]{cmd}[/bold white]")
24
+ console.print()
25
+ console.print(" [dim]Or in CMD:[/dim]")
26
+ console.print(r" [dim].venv\Scripts\activate.bat[/dim]")
27
+ else:
28
+ console.print(" [yellow]→[/yellow] Run this in your terminal:")
29
+ console.print(f" [bold white]{cmd}[/bold white]")
30
+ console.print()
31
+ console.print(" [dim]Or use eval:[/dim]")
32
+ console.print(" [dim]eval $(stup activate)[/dim]")
33
+ console.print()
app/commands/add.py ADDED
@@ -0,0 +1,35 @@
1
+ """stup add — Thin wrapper over uv add that maintains requirements.txt."""
2
+
3
+ import typer
4
+ from app.utils import (
5
+ check_uv,
6
+ ensure_venv_exists,
7
+ print_banner,
8
+ print_done,
9
+ run,
10
+ )
11
+
12
+ def run_command(packages: list[str]) -> None:
13
+ """Add packages using uv and update requirements.txt."""
14
+ print_banner("add", f"Adding packages: {', '.join(packages)}")
15
+
16
+ check_uv()
17
+ ensure_venv_exists()
18
+
19
+ # Add packages
20
+ pkg_str = " ".join(packages)
21
+ run(f"uv add {pkg_str}")
22
+
23
+ # Update requirements.txt for compatibility (clean format)
24
+ run("uv export --format requirements-txt --no-hashes --no-emit-project --output-file requirements.txt --quiet")
25
+
26
+ # Post-process to remove all 'via' comments for a super clean look
27
+ req_file = "requirements.txt"
28
+ with open(req_file, "r") as f:
29
+ lines = f.readlines()
30
+ with open(req_file, "w") as f:
31
+ for line in lines:
32
+ if not line.strip().startswith("#"):
33
+ f.write(line)
34
+
35
+ print_done()
app/commands/api.py ADDED
@@ -0,0 +1,178 @@
1
+ """stup api — Minimal FastAPI microservice scaffold."""
2
+
3
+ from app.utils import (
4
+ check_uv,
5
+ create_dirs,
6
+ ensure_venv_exists,
7
+ print_banner,
8
+ print_done,
9
+ run,
10
+ write_file,
11
+ )
12
+
13
+ # ── Templates ────────────────────────────────────────────────────────
14
+
15
+ MAIN_PY = '''\
16
+ """FastAPI microservice entry point."""
17
+
18
+ from fastapi import FastAPI
19
+ from fastapi.middleware.cors import CORSMiddleware
20
+ from routers import health, v1
21
+ from middleware.auth import AuthMiddleware
22
+
23
+ app = FastAPI(
24
+ title="My API",
25
+ version="0.1.0",
26
+ docs_url="/docs",
27
+ redoc_url="/redoc",
28
+ )
29
+
30
+ # CORS
31
+ app.add_middleware(
32
+ CORSMiddleware,
33
+ allow_origins=["*"],
34
+ allow_credentials=True,
35
+ allow_methods=["*"],
36
+ allow_headers=["*"],
37
+ )
38
+
39
+ # Custom auth middleware (optional — uncomment to enable)
40
+ # app.add_middleware(AuthMiddleware)
41
+
42
+ # Routers
43
+ app.include_router(health.router, prefix="/health", tags=["Health"])
44
+ app.include_router(v1.router, prefix="/api/v1", tags=["v1"])
45
+
46
+
47
+ @app.get("/")
48
+ async def root():
49
+ return {"message": "Welcome to the API. Visit /docs for documentation."}
50
+ '''
51
+
52
+ HEALTH_ROUTER = '''\
53
+ """Health check router."""
54
+
55
+ from fastapi import APIRouter
56
+
57
+ router = APIRouter()
58
+
59
+
60
+ @router.get("/")
61
+ async def health_check():
62
+ return {"status": "ok"}
63
+
64
+
65
+ @router.get("/ready")
66
+ async def readiness():
67
+ return {"status": "ready"}
68
+ '''
69
+
70
+ V1_ROUTER = '''\
71
+ """API v1 router."""
72
+
73
+ from fastapi import APIRouter
74
+ from pydantic import BaseModel
75
+
76
+ router = APIRouter()
77
+
78
+
79
+ class Item(BaseModel):
80
+ name: str
81
+ description: str = ""
82
+ price: float
83
+
84
+
85
+ # In-memory store (replace with database)
86
+ items: list[Item] = []
87
+
88
+
89
+ @router.get("/items")
90
+ async def list_items():
91
+ return {"items": items}
92
+
93
+
94
+ @router.post("/items")
95
+ async def create_item(item: Item):
96
+ items.append(item)
97
+ return {"message": "Item created", "item": item}
98
+
99
+
100
+ @router.get("/items/{item_id}")
101
+ async def get_item(item_id: int):
102
+ if 0 <= item_id < len(items):
103
+ return {"item": items[item_id]}
104
+ return {"error": "Item not found"}
105
+ '''
106
+
107
+ MODELS_INIT = '''\
108
+ """Pydantic models / schemas."""
109
+
110
+ from pydantic import BaseModel
111
+
112
+
113
+ class HealthResponse(BaseModel):
114
+ status: str
115
+
116
+
117
+ class ErrorResponse(BaseModel):
118
+ detail: str
119
+ '''
120
+
121
+ AUTH_MIDDLEWARE = '''\
122
+ """Authentication middleware stub."""
123
+
124
+ from starlette.middleware.base import BaseHTTPMiddleware
125
+ from starlette.requests import Request
126
+ from starlette.responses import JSONResponse
127
+
128
+
129
+ class AuthMiddleware(BaseHTTPMiddleware):
130
+ """Simple API key authentication middleware.
131
+
132
+ TODO: Replace with your preferred auth strategy (JWT, OAuth, etc.)
133
+ """
134
+
135
+ SKIP_PATHS = {"/", "/docs", "/redoc", "/openapi.json", "/health", "/health/ready"}
136
+
137
+ async def dispatch(self, request: Request, call_next):
138
+ if request.url.path in self.SKIP_PATHS:
139
+ return await call_next(request)
140
+
141
+ api_key = request.headers.get("X-API-Key")
142
+ if not api_key:
143
+ return JSONResponse(
144
+ status_code=401,
145
+ content={"detail": "Missing X-API-Key header"},
146
+ )
147
+
148
+ # TODO: Validate api_key against your store
149
+ # if not is_valid_key(api_key):
150
+ # return JSONResponse(status_code=403, content={"detail": "Invalid API key"})
151
+
152
+ return await call_next(request)
153
+ '''
154
+
155
+
156
+ def run_command() -> None:
157
+ """Scaffold a minimal FastAPI microservice."""
158
+ print_banner("api", "Minimal FastAPI microservice with auth middleware")
159
+
160
+ check_uv()
161
+ ensure_venv_exists()
162
+
163
+ # Install dependencies
164
+ run("uv add fastapi uvicorn pydantic")
165
+
166
+ # Create directory structure
167
+ create_dirs("routers/v1", "routers/health", "models", "middleware")
168
+
169
+ # Create files
170
+ write_file("main.py", MAIN_PY)
171
+ write_file("routers/__init__.py", "")
172
+ write_file("routers/health/__init__.py", HEALTH_ROUTER)
173
+ write_file("routers/v1/__init__.py", V1_ROUTER)
174
+ write_file("models/__init__.py", MODELS_INIT)
175
+ write_file("middleware/__init__.py", "")
176
+ write_file("middleware/auth.py", AUTH_MIDDLEWARE)
177
+
178
+ print_done()
@@ -0,0 +1,137 @@
1
+ """stup cli — PyPI-ready CLI package with Typer + Rich."""
2
+
3
+ import os
4
+
5
+ from app.utils import (
6
+ check_uv,
7
+ create_dirs,
8
+ ensure_venv_exists,
9
+ print_banner,
10
+ print_done,
11
+ run,
12
+ write_file,
13
+ )
14
+
15
+ # ── Templates ────────────────────────────────────────────────────────
16
+
17
+
18
+ def _get_templates(project_name: str) -> dict[str, str]:
19
+ """Return template files with project name substituted."""
20
+ return {
21
+ "init": f'''\
22
+ """{project_name} — A CLI tool built with Typer + Rich."""
23
+
24
+ __version__ = "0.1.0"
25
+ ''',
26
+ "main": f'''\
27
+ """Entry point for {project_name} CLI."""
28
+
29
+ import typer
30
+ from rich.console import Console
31
+ from {project_name} import __version__
32
+ from {project_name}.commands import hello
33
+
34
+ console = Console()
35
+
36
+ app = typer.Typer(
37
+ name="{project_name}",
38
+ help="🚀 {project_name} CLI",
39
+ add_completion=False,
40
+ rich_markup_mode="rich",
41
+ no_args_is_help=True,
42
+ )
43
+
44
+
45
+ def _version_callback(value: bool) -> None:
46
+ if value:
47
+ console.print(f"[bold cyan]{project_name}[/bold cyan] v{{__version__}}")
48
+ raise typer.Exit()
49
+
50
+
51
+ @app.callback()
52
+ def main(
53
+ version: bool = typer.Option(
54
+ False, "--version", "-v", help="Show version.", callback=_version_callback, is_eager=True
55
+ ),
56
+ ) -> None:
57
+ pass
58
+
59
+
60
+ @app.command()
61
+ def greet(name: str = typer.Argument("World", help="Name to greet.")) -> None:
62
+ """Say hello to someone."""
63
+ hello.greet(name)
64
+
65
+
66
+ if __name__ == "__main__":
67
+ app()
68
+ ''',
69
+ "hello": '''\
70
+ """Hello command implementation."""
71
+
72
+ from rich.console import Console
73
+
74
+ console = Console()
75
+
76
+
77
+ def greet(name: str) -> None:
78
+ """Print a greeting."""
79
+ console.print(f"[bold green]Hello, {name}![/bold green] 👋")
80
+ ''',
81
+ "commands_init": '''\
82
+ """CLI commands package."""
83
+ ''',
84
+ }
85
+
86
+
87
+ def run_command() -> None:
88
+ """Scaffold a PyPI-ready CLI package."""
89
+ print_banner("cli", "PyPI-ready CLI package with Typer + Rich")
90
+
91
+ check_uv()
92
+ ensure_venv_exists()
93
+
94
+ # Use current directory name as project name
95
+ project_name = os.path.basename(os.getcwd()).replace("-", "_").replace(" ", "_").lower()
96
+
97
+ # Install CLI dependencies
98
+ run("uv add typer rich")
99
+
100
+ templates = _get_templates(project_name)
101
+
102
+ # Create package structure
103
+ create_dirs(f"src/{project_name}/commands")
104
+
105
+ write_file(f"src/{project_name}/__init__.py", templates["init"])
106
+ write_file(f"src/{project_name}/__main__.py", templates["main"])
107
+ write_file(f"src/{project_name}/commands/__init__.py", templates["commands_init"])
108
+ write_file(f"src/{project_name}/commands/hello.py", templates["hello"])
109
+
110
+ # Create README
111
+ readme = f"""\
112
+ # {project_name}
113
+
114
+ A CLI tool built with [Typer](https://typer.tiangolo.com/) + [Rich](https://rich.readthedocs.io/).
115
+
116
+ ## Installation
117
+
118
+ ```bash
119
+ pip install {project_name}
120
+ ```
121
+
122
+ ## Usage
123
+
124
+ ```bash
125
+ {project_name} --help
126
+ {project_name} greet "World"
127
+ ```
128
+
129
+ ## Development
130
+
131
+ ```bash
132
+ uv pip install -e .
133
+ ```
134
+ """
135
+ write_file("README.md", readme)
136
+
137
+ print_done()