spawnio 1.0.5__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.
- spawn/__init__.py +6 -0
- spawn/cli/__init__.py +0 -0
- spawn/cli/app.py +218 -0
- spawn/cli/noninteractive.py +204 -0
- spawn/cli/prompts.py +267 -0
- spawn/core/__init__.py +0 -0
- spawn/core/exceptions.py +7 -0
- spawn/core/models.py +21 -0
- spawn/core/registry.py +152 -0
- spawn/generators/__init__.py +0 -0
- spawn/generators/custom_structure.py +508 -0
- spawn/generators/project_generator.py +115 -0
- spawn/github/__init__.py +0 -0
- spawn/github/exceptions.py +5 -0
- spawn/github/publisher.py +69 -0
- spawn/github/validators.py +23 -0
- spawn/templates/__init__.py +0 -0
- spawn/templates/agent/__init__.py +182 -0
- spawn/templates/agent/content.py +374 -0
- spawn/templates/automation/__init__.py +100 -0
- spawn/templates/automation/content.py +181 -0
- spawn/templates/backend_api/__init__.py +243 -0
- spawn/templates/backend_api/content.py +509 -0
- spawn/templates/base.py +58 -0
- spawn/templates/chatbot/__init__.py +243 -0
- spawn/templates/chatbot/content.py +668 -0
- spawn/templates/cli_application/__init__.py +211 -0
- spawn/templates/cli_application/content.py +512 -0
- spawn/templates/data_project/__init__.py +125 -0
- spawn/templates/data_project/analysis/__init__.py +74 -0
- spawn/templates/data_project/analysis/content.py +190 -0
- spawn/templates/data_project/dashboard/__init__.py +61 -0
- spawn/templates/data_project/dashboard/content.py +135 -0
- spawn/templates/data_project/etl/__init__.py +66 -0
- spawn/templates/data_project/etl/content.py +239 -0
- spawn/templates/data_project/ml/__init__.py +62 -0
- spawn/templates/data_project/ml/content.py +239 -0
- spawn/templates/mcp_server/__init__.py +83 -0
- spawn/templates/mcp_server/content.py +161 -0
- spawn/templates/rag/__init__.py +124 -0
- spawn/templates/rag/content.py +463 -0
- spawn/templates/shared_content.py +89 -0
- spawn/utils/__init__.py +0 -0
- spawn/utils/banner.py +41 -0
- spawn/utils/console.py +3 -0
- spawn/utils/doctor.py +594 -0
- spawn/utils/git.py +132 -0
- spawn/utils/success.py +47 -0
- spawn/utils/theme.py +25 -0
- spawn/utils/uv.py +61 -0
- spawn/utils/validators.py +15 -0
- spawnio-1.0.5.dist-info/METADATA +798 -0
- spawnio-1.0.5.dist-info/RECORD +56 -0
- spawnio-1.0.5.dist-info/WHEEL +4 -0
- spawnio-1.0.5.dist-info/entry_points.txt +2 -0
- spawnio-1.0.5.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
MAIN_CONTENT = """\
|
|
2
|
+
from fastapi import FastAPI
|
|
3
|
+
|
|
4
|
+
from app.core.config import settings
|
|
5
|
+
from app.api.routes.health import router as health_router
|
|
6
|
+
|
|
7
|
+
app = FastAPI(title=settings.APP_NAME)
|
|
8
|
+
|
|
9
|
+
app.include_router(health_router)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
HEALTH_ROUTER_CONTENT = """\
|
|
13
|
+
from fastapi import APIRouter
|
|
14
|
+
|
|
15
|
+
router = APIRouter()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@router.get("/")
|
|
19
|
+
def health_check():
|
|
20
|
+
return {{"status": "running"}}
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
CONFIG_CONTENT = """\
|
|
24
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Settings(BaseSettings):
|
|
28
|
+
model_config = SettingsConfigDict(env_file=".env")
|
|
29
|
+
|
|
30
|
+
APP_NAME: str = "{project_name}"
|
|
31
|
+
DEBUG: bool = False
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
settings = Settings()
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
TEST_HEALTH_CONTENT = """\
|
|
38
|
+
from fastapi.testclient import TestClient
|
|
39
|
+
|
|
40
|
+
from app.main import app
|
|
41
|
+
|
|
42
|
+
client = TestClient(app)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_health_check():
|
|
46
|
+
response = client.get("/")
|
|
47
|
+
assert response.status_code == 200
|
|
48
|
+
assert response.json() == {{"status": "running"}}
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
ENV_EXAMPLE_CONTENT = """\
|
|
52
|
+
APP_NAME={project_name}
|
|
53
|
+
DEBUG=True
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
BACKEND_API_README_CONTENT = """\
|
|
57
|
+
# {project_name}
|
|
58
|
+
|
|
59
|
+
Generated with [Spawn](https://github.com/your-org/spawn).
|
|
60
|
+
|
|
61
|
+
## Getting Started
|
|
62
|
+
|
|
63
|
+
Install dependencies and start the development server:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv run uvicorn app.main:app --reload
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Running Tests
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv run pytest
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Endpoints
|
|
76
|
+
|
|
77
|
+
| Method | Path | Description |
|
|
78
|
+
|--------|------|--------------|
|
|
79
|
+
| GET | / | Health check |
|
|
80
|
+
|
|
81
|
+
## Project Structure
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
{project_name}/
|
|
85
|
+
├── app/
|
|
86
|
+
│ ├── api/
|
|
87
|
+
│ │ └── routes/
|
|
88
|
+
│ │ └── health.py # Health check route
|
|
89
|
+
│ ├── core/
|
|
90
|
+
│ │ └── config.py # App settings (pydantic-settings)
|
|
91
|
+
│ ├── models/ # SQLAlchemy / domain models
|
|
92
|
+
│ ├── schemas/ # Pydantic request/response schemas
|
|
93
|
+
│ ├── services/ # Business logic
|
|
94
|
+
│ └── main.py # FastAPI app entry point
|
|
95
|
+
├── tests/
|
|
96
|
+
│ └── test_health.py
|
|
97
|
+
├── .env.example
|
|
98
|
+
└── README.md
|
|
99
|
+
```
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
BACKEND_API_AGENTS_MD_CONTENT = """\
|
|
103
|
+
# {project_name}
|
|
104
|
+
|
|
105
|
+
Backend API built with FastAPI.
|
|
106
|
+
|
|
107
|
+
## Run Command
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
uv run uvicorn app.main:app --reload
|
|
111
|
+
```
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
INIT_CONTENT = ""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
# Flask
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
FLASK_APP_INIT_CONTENT = """\
|
|
122
|
+
from flask import Flask
|
|
123
|
+
from app.config import Config
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def create_app() -> Flask:
|
|
127
|
+
app = Flask(__name__)
|
|
128
|
+
app.config.from_object(Config)
|
|
129
|
+
|
|
130
|
+
from app.routes.health import health_bp
|
|
131
|
+
app.register_blueprint(health_bp)
|
|
132
|
+
|
|
133
|
+
return app
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
FLASK_CONFIG_CONTENT = """\
|
|
137
|
+
import os
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class Config:
|
|
141
|
+
APP_NAME: str = os.getenv("APP_NAME", "{project_name}")
|
|
142
|
+
DEBUG: bool = os.getenv("DEBUG", "True").lower() == "true"
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
FLASK_HEALTH_CONTENT = """\
|
|
146
|
+
from flask import Blueprint, jsonify
|
|
147
|
+
|
|
148
|
+
health_bp = Blueprint("health", __name__)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@health_bp.route("/")
|
|
152
|
+
def health_check():
|
|
153
|
+
return jsonify({{"status": "running"}})
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
FLASK_RUN_CONTENT = """\
|
|
157
|
+
from app import create_app
|
|
158
|
+
|
|
159
|
+
app = create_app()
|
|
160
|
+
|
|
161
|
+
if __name__ == "__main__":
|
|
162
|
+
app.run(debug=app.config.get("DEBUG", True))
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
FLASK_TEST_HEALTH_CONTENT = """\
|
|
166
|
+
import pytest
|
|
167
|
+
from app import create_app
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@pytest.fixture
|
|
171
|
+
def client():
|
|
172
|
+
app = create_app()
|
|
173
|
+
app.config["TESTING"] = True
|
|
174
|
+
with app.test_client() as client:
|
|
175
|
+
yield client
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_health_check(client):
|
|
179
|
+
response = client.get("/")
|
|
180
|
+
assert response.status_code == 200
|
|
181
|
+
assert response.get_json() == {{"status": "running"}}
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
FLASK_ENV_EXAMPLE_CONTENT = """\
|
|
185
|
+
APP_NAME={project_name}
|
|
186
|
+
DEBUG=True
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
FLASK_README_CONTENT = """\
|
|
190
|
+
# {project_name}
|
|
191
|
+
|
|
192
|
+
Generated with [Spawn](https://github.com/Abhiix0/spawn).
|
|
193
|
+
|
|
194
|
+
## Getting Started
|
|
195
|
+
|
|
196
|
+
Install dependencies and start the development server:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
uv run python run.py
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Running Tests
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
uv run pytest
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Endpoints
|
|
209
|
+
|
|
210
|
+
| Method | Path | Description |
|
|
211
|
+
|--------|------|--------------|
|
|
212
|
+
| GET | / | Health check |
|
|
213
|
+
|
|
214
|
+
## Project Structure
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
{project_name}/
|
|
218
|
+
├── app/
|
|
219
|
+
│ ├── routes/
|
|
220
|
+
│ │ └── health.py # Health check blueprint
|
|
221
|
+
│ ├── models/ # Domain models
|
|
222
|
+
│ ├── services/ # Business logic
|
|
223
|
+
│ ├── config.py # App configuration
|
|
224
|
+
│ └── __init__.py # App factory
|
|
225
|
+
├── run.py # Entry point
|
|
226
|
+
├── tests/
|
|
227
|
+
│ └── test_health.py
|
|
228
|
+
├── .env.example
|
|
229
|
+
└── README.md
|
|
230
|
+
```
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
FLASK_AGENTS_MD_CONTENT = """\
|
|
234
|
+
# {project_name}
|
|
235
|
+
|
|
236
|
+
Backend API built with Flask.
|
|
237
|
+
|
|
238
|
+
## Run Command
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
uv run python run.py
|
|
242
|
+
```
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
# ---------------------------------------------------------------------------
|
|
246
|
+
# Django
|
|
247
|
+
# ---------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
DJANGO_MANAGE_CONTENT = """\
|
|
250
|
+
#!/usr/bin/env python
|
|
251
|
+
import os
|
|
252
|
+
import sys
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def main():
|
|
256
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
257
|
+
try:
|
|
258
|
+
from django.core.management import execute_from_command_line
|
|
259
|
+
except ImportError as exc:
|
|
260
|
+
raise ImportError(
|
|
261
|
+
"Couldn't import Django. Are you sure it's installed?"
|
|
262
|
+
) from exc
|
|
263
|
+
execute_from_command_line(sys.argv)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
if __name__ == "__main__":
|
|
267
|
+
main()
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
DJANGO_SETTINGS_CONTENT = """\
|
|
271
|
+
from pathlib import Path
|
|
272
|
+
|
|
273
|
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
274
|
+
|
|
275
|
+
SECRET_KEY = "django-insecure-change-me-in-production"
|
|
276
|
+
|
|
277
|
+
DEBUG = True
|
|
278
|
+
|
|
279
|
+
ALLOWED_HOSTS = ["*"]
|
|
280
|
+
|
|
281
|
+
INSTALLED_APPS = [
|
|
282
|
+
"django.contrib.contenttypes",
|
|
283
|
+
"django.contrib.staticfiles",
|
|
284
|
+
"apps.health",
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
MIDDLEWARE = [
|
|
288
|
+
"django.middleware.common.CommonMiddleware",
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
ROOT_URLCONF = "config.urls"
|
|
292
|
+
|
|
293
|
+
WSGI_APPLICATION = "config.wsgi.application"
|
|
294
|
+
|
|
295
|
+
DATABASES = {{}}
|
|
296
|
+
|
|
297
|
+
STATIC_URL = "/static/"
|
|
298
|
+
|
|
299
|
+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
DJANGO_URLS_CONTENT = """\
|
|
303
|
+
from django.urls import path, include
|
|
304
|
+
|
|
305
|
+
urlpatterns = [
|
|
306
|
+
path("", include("apps.health.urls")),
|
|
307
|
+
]
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
DJANGO_ASGI_CONTENT = """\
|
|
311
|
+
import os
|
|
312
|
+
from django.core.asgi import get_asgi_application
|
|
313
|
+
|
|
314
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
315
|
+
|
|
316
|
+
application = get_asgi_application()
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
DJANGO_WSGI_CONTENT = """\
|
|
320
|
+
import os
|
|
321
|
+
from django.core.wsgi import get_wsgi_application
|
|
322
|
+
|
|
323
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
324
|
+
|
|
325
|
+
application = get_wsgi_application()
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
DJANGO_HEALTH_VIEWS_CONTENT = """\
|
|
329
|
+
from django.http import JsonResponse
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def health_check(request):
|
|
333
|
+
return JsonResponse({{"status": "running"}})
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
DJANGO_HEALTH_URLS_CONTENT = """\
|
|
337
|
+
from django.urls import path
|
|
338
|
+
from apps.health.views import health_check
|
|
339
|
+
|
|
340
|
+
urlpatterns = [
|
|
341
|
+
path("", health_check, name="health_check"),
|
|
342
|
+
]
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
DJANGO_HEALTH_TESTS_CONTENT = """\
|
|
346
|
+
from django.test import TestCase, Client
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class HealthCheckTestCase(TestCase):
|
|
350
|
+
def setUp(self):
|
|
351
|
+
self.client = Client()
|
|
352
|
+
|
|
353
|
+
def test_health_check(self):
|
|
354
|
+
response = self.client.get("/")
|
|
355
|
+
self.assertEqual(response.status_code, 200)
|
|
356
|
+
self.assertJSONEqual(response.content, {{"status": "running"}})
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
DJANGO_README_CONTENT = """\
|
|
360
|
+
# {project_name}
|
|
361
|
+
|
|
362
|
+
Generated with [Spawn](https://github.com/Abhiix0/spawn).
|
|
363
|
+
|
|
364
|
+
## Getting Started
|
|
365
|
+
|
|
366
|
+
Install dependencies and start the development server:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
uv run python manage.py runserver
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Running Tests
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
uv run python manage.py test
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Endpoints
|
|
379
|
+
|
|
380
|
+
| Method | Path | Description |
|
|
381
|
+
|--------|------|--------------|
|
|
382
|
+
| GET | / | Health check |
|
|
383
|
+
|
|
384
|
+
## Project Structure
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
{project_name}/
|
|
388
|
+
├── manage.py
|
|
389
|
+
├── config/
|
|
390
|
+
│ ├── settings.py
|
|
391
|
+
│ ├── urls.py
|
|
392
|
+
│ ├── asgi.py
|
|
393
|
+
│ └── wsgi.py
|
|
394
|
+
├── apps/
|
|
395
|
+
│ └── health/
|
|
396
|
+
│ ├── views.py
|
|
397
|
+
│ ├── urls.py
|
|
398
|
+
│ └── tests.py
|
|
399
|
+
└── README.md
|
|
400
|
+
```
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
DJANGO_AGENTS_MD_CONTENT = """\
|
|
404
|
+
# {project_name}
|
|
405
|
+
|
|
406
|
+
Backend API built with Django.
|
|
407
|
+
|
|
408
|
+
## Run Command
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
uv run python manage.py runserver
|
|
412
|
+
```
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
# ---------------------------------------------------------------------------
|
|
416
|
+
# Docker
|
|
417
|
+
# ---------------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
DOCKERFILE_FASTAPI_CONTENT = """\
|
|
420
|
+
FROM python:3.12-slim
|
|
421
|
+
|
|
422
|
+
WORKDIR /app
|
|
423
|
+
|
|
424
|
+
COPY pyproject.toml .
|
|
425
|
+
RUN pip install uv && uv sync
|
|
426
|
+
|
|
427
|
+
COPY . .
|
|
428
|
+
|
|
429
|
+
EXPOSE 8000
|
|
430
|
+
|
|
431
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
432
|
+
"""
|
|
433
|
+
|
|
434
|
+
DOCKERFILE_FLASK_CONTENT = """\
|
|
435
|
+
FROM python:3.12-slim
|
|
436
|
+
|
|
437
|
+
WORKDIR /app
|
|
438
|
+
|
|
439
|
+
COPY pyproject.toml .
|
|
440
|
+
RUN pip install uv && uv sync
|
|
441
|
+
|
|
442
|
+
COPY . .
|
|
443
|
+
|
|
444
|
+
EXPOSE 5000
|
|
445
|
+
|
|
446
|
+
CMD ["python", "run.py"]
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
DOCKERFILE_DJANGO_CONTENT = """\
|
|
450
|
+
FROM python:3.12-slim
|
|
451
|
+
|
|
452
|
+
WORKDIR /app
|
|
453
|
+
|
|
454
|
+
COPY pyproject.toml .
|
|
455
|
+
RUN pip install uv && uv sync
|
|
456
|
+
|
|
457
|
+
COPY . .
|
|
458
|
+
|
|
459
|
+
EXPOSE 8000
|
|
460
|
+
|
|
461
|
+
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
DOCKERIGNORE_CONTENT = """\
|
|
465
|
+
.venv
|
|
466
|
+
__pycache__
|
|
467
|
+
*.pyc
|
|
468
|
+
*.pyo
|
|
469
|
+
.env
|
|
470
|
+
.git
|
|
471
|
+
.gitignore
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
# ---------------------------------------------------------------------------
|
|
475
|
+
# GitHub Actions
|
|
476
|
+
# ---------------------------------------------------------------------------
|
|
477
|
+
|
|
478
|
+
GITHUB_ACTIONS_CI_BASE = """\
|
|
479
|
+
name: CI
|
|
480
|
+
|
|
481
|
+
on:
|
|
482
|
+
push:
|
|
483
|
+
branches: [main]
|
|
484
|
+
pull_request:
|
|
485
|
+
branches: [main]
|
|
486
|
+
|
|
487
|
+
jobs:
|
|
488
|
+
ci:
|
|
489
|
+
runs-on: ubuntu-latest
|
|
490
|
+
|
|
491
|
+
steps:
|
|
492
|
+
- uses: actions/checkout@v4
|
|
493
|
+
|
|
494
|
+
- name: Install uv
|
|
495
|
+
uses: astral-sh/setup-uv@v4
|
|
496
|
+
|
|
497
|
+
- name: Install dependencies
|
|
498
|
+
run: uv sync
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
GITHUB_ACTIONS_CI_RUFF_STEP = """\
|
|
502
|
+
- name: Run Ruff
|
|
503
|
+
run: uv run ruff check .
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
GITHUB_ACTIONS_CI_PYTEST_STEP = """\
|
|
507
|
+
- name: Run Pytest
|
|
508
|
+
run: uv run pytest
|
|
509
|
+
"""
|
spawn/templates/base.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class BaseTemplate:
|
|
7
|
+
name: str
|
|
8
|
+
folders: list[str]
|
|
9
|
+
starter_files: list[tuple[str, str]] = field(default_factory=list)
|
|
10
|
+
next_steps: list[str] = field(default_factory=list)
|
|
11
|
+
# Each tuple: (relative_path_string, content_template_string)
|
|
12
|
+
|
|
13
|
+
def get_dependencies(self) -> list[str]:
|
|
14
|
+
"""Return packages to install after uv init. Subclasses override."""
|
|
15
|
+
return []
|
|
16
|
+
|
|
17
|
+
def post_install(self, project_path: Path) -> None:
|
|
18
|
+
"""Run post-install actions after packages are installed. Subclasses override."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def get_readme_content(self, context: dict) -> str | None:
|
|
22
|
+
"""
|
|
23
|
+
Return custom README content for this template, or None to use the
|
|
24
|
+
shared README. Subclasses override this to provide template-specific
|
|
25
|
+
README content.
|
|
26
|
+
"""
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
def get_agents_md_content(self, context: dict) -> str | None:
|
|
30
|
+
"""
|
|
31
|
+
Return custom AGENTS.md content for this template, or None to use
|
|
32
|
+
the shared default. Subclasses override this to provide
|
|
33
|
+
template-specific agent context — framework, provider, or
|
|
34
|
+
environment-variable details an agent working on this project
|
|
35
|
+
would need.
|
|
36
|
+
"""
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def generate(self, project_path: Path, context: dict) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Create the template's folder structure and starter files.
|
|
42
|
+
|
|
43
|
+
project_path: the already-created root project directory (Path object)
|
|
44
|
+
context: dict with at minimum {"project_name": str}
|
|
45
|
+
|
|
46
|
+
Subclasses may override this in future phases.
|
|
47
|
+
The default implementation creates folders and writes starter_files.
|
|
48
|
+
"""
|
|
49
|
+
for folder in self.folders:
|
|
50
|
+
(project_path / folder).mkdir(parents=True, exist_ok=True)
|
|
51
|
+
|
|
52
|
+
for relative_path, content_template in self.starter_files:
|
|
53
|
+
file_path = project_path / relative_path
|
|
54
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
file_path.write_text(
|
|
56
|
+
content_template.format_map(context),
|
|
57
|
+
encoding="utf-8",
|
|
58
|
+
)
|