up-cli 0.1.1__py3-none-any.whl → 0.5.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.
- up/__init__.py +1 -1
- up/ai_cli.py +229 -0
- up/cli.py +75 -4
- up/commands/agent.py +521 -0
- up/commands/bisect.py +343 -0
- up/commands/branch.py +350 -0
- up/commands/dashboard.py +248 -0
- up/commands/init.py +195 -6
- up/commands/learn.py +1741 -0
- up/commands/memory.py +545 -0
- up/commands/new.py +108 -10
- up/commands/provenance.py +267 -0
- up/commands/review.py +239 -0
- up/commands/start.py +1124 -0
- up/commands/status.py +360 -0
- up/commands/summarize.py +122 -0
- up/commands/sync.py +317 -0
- up/commands/vibe.py +304 -0
- up/context.py +421 -0
- up/core/__init__.py +69 -0
- up/core/checkpoint.py +479 -0
- up/core/provenance.py +364 -0
- up/core/state.py +678 -0
- up/events.py +512 -0
- up/git/__init__.py +37 -0
- up/git/utils.py +270 -0
- up/git/worktree.py +331 -0
- up/learn/__init__.py +155 -0
- up/learn/analyzer.py +227 -0
- up/learn/plan.py +374 -0
- up/learn/research.py +511 -0
- up/learn/utils.py +117 -0
- up/memory.py +1096 -0
- up/parallel.py +551 -0
- up/summarizer.py +407 -0
- up/templates/__init__.py +70 -2
- up/templates/config/__init__.py +502 -20
- up/templates/docs/SKILL.md +28 -0
- up/templates/docs/__init__.py +341 -0
- up/templates/docs/standards/HEADERS.md +24 -0
- up/templates/docs/standards/STRUCTURE.md +18 -0
- up/templates/docs/standards/TEMPLATES.md +19 -0
- up/templates/learn/__init__.py +567 -14
- up/templates/loop/__init__.py +546 -27
- up/templates/mcp/__init__.py +474 -0
- up/templates/projects/__init__.py +786 -0
- up/ui/__init__.py +14 -0
- up/ui/loop_display.py +650 -0
- up/ui/theme.py +137 -0
- up_cli-0.5.0.dist-info/METADATA +519 -0
- up_cli-0.5.0.dist-info/RECORD +55 -0
- up_cli-0.1.1.dist-info/METADATA +0 -186
- up_cli-0.1.1.dist-info/RECORD +0 -14
- {up_cli-0.1.1.dist-info → up_cli-0.5.0.dist-info}/WHEEL +0 -0
- {up_cli-0.1.1.dist-info → up_cli-0.5.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
"""Project type templates for different tech stacks.
|
|
2
|
+
|
|
3
|
+
Templates available:
|
|
4
|
+
- fastapi: FastAPI backend with SQLAlchemy
|
|
5
|
+
- nextjs: Next.js frontend with TypeScript
|
|
6
|
+
- python-lib: Python library with packaging
|
|
7
|
+
- minimal: Minimal project structure
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from datetime import date
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Template registry
|
|
15
|
+
TEMPLATES = {
|
|
16
|
+
"minimal": "Minimal project structure",
|
|
17
|
+
"fastapi": "FastAPI backend with SQLAlchemy, pytest, and Docker",
|
|
18
|
+
"nextjs": "Next.js 14+ frontend with TypeScript and Tailwind",
|
|
19
|
+
"python-lib": "Python library with pyproject.toml and pytest",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_available_templates() -> dict:
|
|
24
|
+
"""Get dictionary of available templates."""
|
|
25
|
+
return TEMPLATES.copy()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_project_from_template(
|
|
29
|
+
target_dir: Path,
|
|
30
|
+
template: str,
|
|
31
|
+
project_name: str,
|
|
32
|
+
force: bool = False
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Create project from specified template."""
|
|
35
|
+
if template not in TEMPLATES:
|
|
36
|
+
raise ValueError(f"Unknown template: {template}. Available: {list(TEMPLATES.keys())}")
|
|
37
|
+
|
|
38
|
+
if template == "minimal":
|
|
39
|
+
_create_minimal_template(target_dir, project_name, force)
|
|
40
|
+
elif template == "fastapi":
|
|
41
|
+
_create_fastapi_template(target_dir, project_name, force)
|
|
42
|
+
elif template == "nextjs":
|
|
43
|
+
_create_nextjs_template(target_dir, project_name, force)
|
|
44
|
+
elif template == "python-lib":
|
|
45
|
+
_create_python_lib_template(target_dir, project_name, force)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _write_file(path: Path, content: str, force: bool) -> None:
|
|
49
|
+
"""Write file if it doesn't exist or force is True."""
|
|
50
|
+
if path.exists() and not force:
|
|
51
|
+
return
|
|
52
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
path.write_text(content)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# =============================================================================
|
|
57
|
+
# Minimal Template
|
|
58
|
+
# =============================================================================
|
|
59
|
+
|
|
60
|
+
def _create_minimal_template(target_dir: Path, project_name: str, force: bool) -> None:
|
|
61
|
+
"""Create minimal project structure."""
|
|
62
|
+
# Create directories
|
|
63
|
+
(target_dir / "src").mkdir(parents=True, exist_ok=True)
|
|
64
|
+
(target_dir / "tests").mkdir(parents=True, exist_ok=True)
|
|
65
|
+
|
|
66
|
+
# Create README
|
|
67
|
+
readme = f"""# {project_name}
|
|
68
|
+
|
|
69
|
+
> Add project description here
|
|
70
|
+
|
|
71
|
+
## Getting Started
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Install dependencies
|
|
75
|
+
pip install -e .
|
|
76
|
+
|
|
77
|
+
# Run tests
|
|
78
|
+
pytest
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Structure
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
{project_name}/
|
|
85
|
+
├── src/ # Source code
|
|
86
|
+
├── tests/ # Tests
|
|
87
|
+
└── docs/ # Documentation
|
|
88
|
+
```
|
|
89
|
+
"""
|
|
90
|
+
_write_file(target_dir / "README.md", readme, force)
|
|
91
|
+
|
|
92
|
+
# Create .gitignore
|
|
93
|
+
gitignore = """# Python
|
|
94
|
+
__pycache__/
|
|
95
|
+
*.py[cod]
|
|
96
|
+
*$py.class
|
|
97
|
+
*.so
|
|
98
|
+
.Python
|
|
99
|
+
build/
|
|
100
|
+
dist/
|
|
101
|
+
*.egg-info/
|
|
102
|
+
.venv/
|
|
103
|
+
venv/
|
|
104
|
+
|
|
105
|
+
# IDE
|
|
106
|
+
.idea/
|
|
107
|
+
.vscode/
|
|
108
|
+
*.swp
|
|
109
|
+
*.swo
|
|
110
|
+
|
|
111
|
+
# Testing
|
|
112
|
+
.pytest_cache/
|
|
113
|
+
.coverage
|
|
114
|
+
htmlcov/
|
|
115
|
+
|
|
116
|
+
# Up CLI
|
|
117
|
+
.loop_state.json
|
|
118
|
+
.circuit_*.json
|
|
119
|
+
"""
|
|
120
|
+
_write_file(target_dir / ".gitignore", gitignore, force)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# =============================================================================
|
|
124
|
+
# FastAPI Template
|
|
125
|
+
# =============================================================================
|
|
126
|
+
|
|
127
|
+
def _create_fastapi_template(target_dir: Path, project_name: str, force: bool) -> None:
|
|
128
|
+
"""Create FastAPI project template."""
|
|
129
|
+
safe_name = project_name.replace("-", "_").lower()
|
|
130
|
+
|
|
131
|
+
# Create directories
|
|
132
|
+
dirs = [
|
|
133
|
+
f"src/{safe_name}",
|
|
134
|
+
f"src/{safe_name}/api",
|
|
135
|
+
f"src/{safe_name}/models",
|
|
136
|
+
f"src/{safe_name}/services",
|
|
137
|
+
"tests",
|
|
138
|
+
"alembic/versions",
|
|
139
|
+
]
|
|
140
|
+
for d in dirs:
|
|
141
|
+
(target_dir / d).mkdir(parents=True, exist_ok=True)
|
|
142
|
+
|
|
143
|
+
# pyproject.toml
|
|
144
|
+
pyproject = f'''[build-system]
|
|
145
|
+
requires = ["hatchling"]
|
|
146
|
+
build-backend = "hatchling.build"
|
|
147
|
+
|
|
148
|
+
[project]
|
|
149
|
+
name = "{project_name}"
|
|
150
|
+
version = "0.1.0"
|
|
151
|
+
description = "FastAPI application"
|
|
152
|
+
readme = "README.md"
|
|
153
|
+
requires-python = ">=3.10"
|
|
154
|
+
dependencies = [
|
|
155
|
+
"fastapi>=0.100.0",
|
|
156
|
+
"uvicorn[standard]>=0.23.0",
|
|
157
|
+
"sqlalchemy>=2.0.0",
|
|
158
|
+
"alembic>=1.12.0",
|
|
159
|
+
"pydantic>=2.0.0",
|
|
160
|
+
"pydantic-settings>=2.0.0",
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
[project.optional-dependencies]
|
|
164
|
+
dev = [
|
|
165
|
+
"pytest>=7.0",
|
|
166
|
+
"pytest-asyncio>=0.21.0",
|
|
167
|
+
"httpx>=0.24.0",
|
|
168
|
+
"ruff>=0.1.0",
|
|
169
|
+
"mypy>=1.5.0",
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
[tool.hatch.build.targets.wheel]
|
|
173
|
+
packages = ["src/{safe_name}"]
|
|
174
|
+
|
|
175
|
+
[tool.pytest.ini_options]
|
|
176
|
+
asyncio_mode = "auto"
|
|
177
|
+
testpaths = ["tests"]
|
|
178
|
+
|
|
179
|
+
[tool.ruff]
|
|
180
|
+
line-length = 100
|
|
181
|
+
target-version = "py310"
|
|
182
|
+
|
|
183
|
+
[tool.mypy]
|
|
184
|
+
python_version = "3.10"
|
|
185
|
+
strict = true
|
|
186
|
+
'''
|
|
187
|
+
_write_file(target_dir / "pyproject.toml", pyproject, force)
|
|
188
|
+
|
|
189
|
+
# Main app
|
|
190
|
+
main_app = f'''"""FastAPI application."""
|
|
191
|
+
|
|
192
|
+
from fastapi import FastAPI
|
|
193
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
194
|
+
|
|
195
|
+
app = FastAPI(
|
|
196
|
+
title="{project_name}",
|
|
197
|
+
description="API description",
|
|
198
|
+
version="0.1.0",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# CORS middleware
|
|
202
|
+
app.add_middleware(
|
|
203
|
+
CORSMiddleware,
|
|
204
|
+
allow_origins=["*"], # Configure in production
|
|
205
|
+
allow_credentials=True,
|
|
206
|
+
allow_methods=["*"],
|
|
207
|
+
allow_headers=["*"],
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@app.get("/")
|
|
212
|
+
async def root():
|
|
213
|
+
"""Root endpoint."""
|
|
214
|
+
return {{"message": "Welcome to {project_name}"}}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@app.get("/health")
|
|
218
|
+
async def health():
|
|
219
|
+
"""Health check endpoint."""
|
|
220
|
+
return {{"status": "healthy"}}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# Import and include routers
|
|
224
|
+
# from {safe_name}.api import router
|
|
225
|
+
# app.include_router(router)
|
|
226
|
+
'''
|
|
227
|
+
_write_file(target_dir / f"src/{safe_name}/main.py", main_app, force)
|
|
228
|
+
|
|
229
|
+
# __init__.py
|
|
230
|
+
_write_file(target_dir / f"src/{safe_name}/__init__.py", '"""Package."""\n', force)
|
|
231
|
+
_write_file(target_dir / f"src/{safe_name}/api/__init__.py", '"""API module."""\n', force)
|
|
232
|
+
_write_file(target_dir / f"src/{safe_name}/models/__init__.py", '"""Models module."""\n', force)
|
|
233
|
+
_write_file(target_dir / f"src/{safe_name}/services/__init__.py", '"""Services module."""\n', force)
|
|
234
|
+
|
|
235
|
+
# Config
|
|
236
|
+
config = f'''"""Application configuration."""
|
|
237
|
+
|
|
238
|
+
from pydantic_settings import BaseSettings
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class Settings(BaseSettings):
|
|
242
|
+
"""Application settings."""
|
|
243
|
+
|
|
244
|
+
app_name: str = "{project_name}"
|
|
245
|
+
debug: bool = False
|
|
246
|
+
database_url: str = "sqlite:///./app.db"
|
|
247
|
+
|
|
248
|
+
class Config:
|
|
249
|
+
env_file = ".env"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
settings = Settings()
|
|
253
|
+
'''
|
|
254
|
+
_write_file(target_dir / f"src/{safe_name}/config.py", config, force)
|
|
255
|
+
|
|
256
|
+
# Test file
|
|
257
|
+
test_main = f'''"""Test main endpoints."""
|
|
258
|
+
|
|
259
|
+
import pytest
|
|
260
|
+
from httpx import AsyncClient
|
|
261
|
+
from {safe_name}.main import app
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@pytest.fixture
|
|
265
|
+
async def client():
|
|
266
|
+
"""Create test client."""
|
|
267
|
+
async with AsyncClient(app=app, base_url="http://test") as client:
|
|
268
|
+
yield client
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@pytest.mark.asyncio
|
|
272
|
+
async def test_root(client: AsyncClient):
|
|
273
|
+
"""Test root endpoint."""
|
|
274
|
+
response = await client.get("/")
|
|
275
|
+
assert response.status_code == 200
|
|
276
|
+
assert "message" in response.json()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@pytest.mark.asyncio
|
|
280
|
+
async def test_health(client: AsyncClient):
|
|
281
|
+
"""Test health endpoint."""
|
|
282
|
+
response = await client.get("/health")
|
|
283
|
+
assert response.status_code == 200
|
|
284
|
+
assert response.json()["status"] == "healthy"
|
|
285
|
+
'''
|
|
286
|
+
_write_file(target_dir / "tests/test_main.py", test_main, force)
|
|
287
|
+
_write_file(target_dir / "tests/__init__.py", "", force)
|
|
288
|
+
|
|
289
|
+
# Dockerfile
|
|
290
|
+
dockerfile = f'''FROM python:3.11-slim
|
|
291
|
+
|
|
292
|
+
WORKDIR /app
|
|
293
|
+
|
|
294
|
+
# Install dependencies
|
|
295
|
+
COPY pyproject.toml .
|
|
296
|
+
RUN pip install --no-cache-dir -e .
|
|
297
|
+
|
|
298
|
+
# Copy source
|
|
299
|
+
COPY src/ src/
|
|
300
|
+
|
|
301
|
+
# Run
|
|
302
|
+
EXPOSE 8000
|
|
303
|
+
CMD ["uvicorn", "{safe_name}.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
304
|
+
'''
|
|
305
|
+
_write_file(target_dir / "Dockerfile", dockerfile, force)
|
|
306
|
+
|
|
307
|
+
# README
|
|
308
|
+
readme = f'''# {project_name}
|
|
309
|
+
|
|
310
|
+
FastAPI application.
|
|
311
|
+
|
|
312
|
+
## Quick Start
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
# Install
|
|
316
|
+
pip install -e ".[dev]"
|
|
317
|
+
|
|
318
|
+
# Run
|
|
319
|
+
uvicorn {safe_name}.main:app --reload
|
|
320
|
+
|
|
321
|
+
# Test
|
|
322
|
+
pytest
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## API Docs
|
|
326
|
+
|
|
327
|
+
- Swagger UI: http://localhost:8000/docs
|
|
328
|
+
- ReDoc: http://localhost:8000/redoc
|
|
329
|
+
|
|
330
|
+
## Docker
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
docker build -t {project_name} .
|
|
334
|
+
docker run -p 8000:8000 {project_name}
|
|
335
|
+
```
|
|
336
|
+
'''
|
|
337
|
+
_write_file(target_dir / "README.md", readme, force)
|
|
338
|
+
|
|
339
|
+
# .env.example
|
|
340
|
+
env_example = """# Environment variables
|
|
341
|
+
DEBUG=false
|
|
342
|
+
DATABASE_URL=sqlite:///./app.db
|
|
343
|
+
"""
|
|
344
|
+
_write_file(target_dir / ".env.example", env_example, force)
|
|
345
|
+
|
|
346
|
+
# .gitignore
|
|
347
|
+
gitignore = """# Python
|
|
348
|
+
__pycache__/
|
|
349
|
+
*.py[cod]
|
|
350
|
+
*.egg-info/
|
|
351
|
+
.venv/
|
|
352
|
+
venv/
|
|
353
|
+
|
|
354
|
+
# IDE
|
|
355
|
+
.idea/
|
|
356
|
+
.vscode/
|
|
357
|
+
|
|
358
|
+
# Testing
|
|
359
|
+
.pytest_cache/
|
|
360
|
+
.coverage
|
|
361
|
+
|
|
362
|
+
# Environment
|
|
363
|
+
.env
|
|
364
|
+
*.db
|
|
365
|
+
|
|
366
|
+
# Up CLI
|
|
367
|
+
.loop_state.json
|
|
368
|
+
"""
|
|
369
|
+
_write_file(target_dir / ".gitignore", gitignore, force)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# =============================================================================
|
|
373
|
+
# Next.js Template
|
|
374
|
+
# =============================================================================
|
|
375
|
+
|
|
376
|
+
def _create_nextjs_template(target_dir: Path, project_name: str, force: bool) -> None:
|
|
377
|
+
"""Create Next.js project template."""
|
|
378
|
+
# Create directories
|
|
379
|
+
dirs = [
|
|
380
|
+
"src/app",
|
|
381
|
+
"src/components",
|
|
382
|
+
"src/lib",
|
|
383
|
+
"src/types",
|
|
384
|
+
"public",
|
|
385
|
+
]
|
|
386
|
+
for d in dirs:
|
|
387
|
+
(target_dir / d).mkdir(parents=True, exist_ok=True)
|
|
388
|
+
|
|
389
|
+
# package.json
|
|
390
|
+
package_json = f'''{{
|
|
391
|
+
"name": "{project_name}",
|
|
392
|
+
"version": "0.1.0",
|
|
393
|
+
"private": true,
|
|
394
|
+
"scripts": {{
|
|
395
|
+
"dev": "next dev",
|
|
396
|
+
"build": "next build",
|
|
397
|
+
"start": "next start",
|
|
398
|
+
"lint": "next lint",
|
|
399
|
+
"test": "jest"
|
|
400
|
+
}},
|
|
401
|
+
"dependencies": {{
|
|
402
|
+
"next": "14.x",
|
|
403
|
+
"react": "18.x",
|
|
404
|
+
"react-dom": "18.x"
|
|
405
|
+
}},
|
|
406
|
+
"devDependencies": {{
|
|
407
|
+
"@types/node": "20.x",
|
|
408
|
+
"@types/react": "18.x",
|
|
409
|
+
"@types/react-dom": "18.x",
|
|
410
|
+
"typescript": "5.x",
|
|
411
|
+
"tailwindcss": "3.x",
|
|
412
|
+
"postcss": "8.x",
|
|
413
|
+
"autoprefixer": "10.x",
|
|
414
|
+
"eslint": "8.x",
|
|
415
|
+
"eslint-config-next": "14.x",
|
|
416
|
+
"jest": "29.x",
|
|
417
|
+
"@testing-library/react": "14.x",
|
|
418
|
+
"@testing-library/jest-dom": "6.x"
|
|
419
|
+
}}
|
|
420
|
+
}}
|
|
421
|
+
'''
|
|
422
|
+
_write_file(target_dir / "package.json", package_json, force)
|
|
423
|
+
|
|
424
|
+
# tsconfig.json
|
|
425
|
+
tsconfig = '''{
|
|
426
|
+
"compilerOptions": {
|
|
427
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
428
|
+
"allowJs": true,
|
|
429
|
+
"skipLibCheck": true,
|
|
430
|
+
"strict": true,
|
|
431
|
+
"noEmit": true,
|
|
432
|
+
"esModuleInterop": true,
|
|
433
|
+
"module": "esnext",
|
|
434
|
+
"moduleResolution": "bundler",
|
|
435
|
+
"resolveJsonModule": true,
|
|
436
|
+
"isolatedModules": true,
|
|
437
|
+
"jsx": "preserve",
|
|
438
|
+
"incremental": true,
|
|
439
|
+
"plugins": [{ "name": "next" }],
|
|
440
|
+
"paths": {
|
|
441
|
+
"@/*": ["./src/*"]
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
445
|
+
"exclude": ["node_modules"]
|
|
446
|
+
}
|
|
447
|
+
'''
|
|
448
|
+
_write_file(target_dir / "tsconfig.json", tsconfig, force)
|
|
449
|
+
|
|
450
|
+
# tailwind.config.js
|
|
451
|
+
tailwind = '''/** @type {import('tailwindcss').Config} */
|
|
452
|
+
module.exports = {
|
|
453
|
+
content: [
|
|
454
|
+
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
455
|
+
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
456
|
+
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
457
|
+
],
|
|
458
|
+
theme: {
|
|
459
|
+
extend: {},
|
|
460
|
+
},
|
|
461
|
+
plugins: [],
|
|
462
|
+
}
|
|
463
|
+
'''
|
|
464
|
+
_write_file(target_dir / "tailwind.config.js", tailwind, force)
|
|
465
|
+
|
|
466
|
+
# postcss.config.js
|
|
467
|
+
postcss = '''module.exports = {
|
|
468
|
+
plugins: {
|
|
469
|
+
tailwindcss: {},
|
|
470
|
+
autoprefixer: {},
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
'''
|
|
474
|
+
_write_file(target_dir / "postcss.config.js", postcss, force)
|
|
475
|
+
|
|
476
|
+
# next.config.js
|
|
477
|
+
next_config = '''/** @type {import('next').NextConfig} */
|
|
478
|
+
const nextConfig = {
|
|
479
|
+
reactStrictMode: true,
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
module.exports = nextConfig
|
|
483
|
+
'''
|
|
484
|
+
_write_file(target_dir / "next.config.js", next_config, force)
|
|
485
|
+
|
|
486
|
+
# src/app/layout.tsx
|
|
487
|
+
layout = f'''import type {{ Metadata }} from 'next'
|
|
488
|
+
import './globals.css'
|
|
489
|
+
|
|
490
|
+
export const metadata: Metadata = {{
|
|
491
|
+
title: '{project_name}',
|
|
492
|
+
description: 'Generated by up-cli',
|
|
493
|
+
}}
|
|
494
|
+
|
|
495
|
+
export default function RootLayout({{
|
|
496
|
+
children,
|
|
497
|
+
}}: {{
|
|
498
|
+
children: React.ReactNode
|
|
499
|
+
}}) {{
|
|
500
|
+
return (
|
|
501
|
+
<html lang="en">
|
|
502
|
+
<body>{{children}}</body>
|
|
503
|
+
</html>
|
|
504
|
+
)
|
|
505
|
+
}}
|
|
506
|
+
'''
|
|
507
|
+
_write_file(target_dir / "src/app/layout.tsx", layout, force)
|
|
508
|
+
|
|
509
|
+
# src/app/page.tsx
|
|
510
|
+
page = f'''export default function Home() {{
|
|
511
|
+
return (
|
|
512
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
513
|
+
<h1 className="text-4xl font-bold">{project_name}</h1>
|
|
514
|
+
<p className="mt-4 text-gray-600">
|
|
515
|
+
Edit <code className="bg-gray-100 px-2 py-1 rounded">src/app/page.tsx</code> to get started.
|
|
516
|
+
</p>
|
|
517
|
+
</main>
|
|
518
|
+
)
|
|
519
|
+
}}
|
|
520
|
+
'''
|
|
521
|
+
_write_file(target_dir / "src/app/page.tsx", page, force)
|
|
522
|
+
|
|
523
|
+
# src/app/globals.css
|
|
524
|
+
globals_css = '''@tailwind base;
|
|
525
|
+
@tailwind components;
|
|
526
|
+
@tailwind utilities;
|
|
527
|
+
'''
|
|
528
|
+
_write_file(target_dir / "src/app/globals.css", globals_css, force)
|
|
529
|
+
|
|
530
|
+
# README
|
|
531
|
+
readme = f'''# {project_name}
|
|
532
|
+
|
|
533
|
+
Next.js application with TypeScript and Tailwind CSS.
|
|
534
|
+
|
|
535
|
+
## Quick Start
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
npm install
|
|
539
|
+
npm run dev
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
Open [http://localhost:3000](http://localhost:3000).
|
|
543
|
+
|
|
544
|
+
## Scripts
|
|
545
|
+
|
|
546
|
+
- `npm run dev` - Development server
|
|
547
|
+
- `npm run build` - Production build
|
|
548
|
+
- `npm run start` - Start production server
|
|
549
|
+
- `npm run lint` - Run linter
|
|
550
|
+
- `npm run test` - Run tests
|
|
551
|
+
'''
|
|
552
|
+
_write_file(target_dir / "README.md", readme, force)
|
|
553
|
+
|
|
554
|
+
# .gitignore
|
|
555
|
+
gitignore = """# Dependencies
|
|
556
|
+
node_modules/
|
|
557
|
+
.pnp/
|
|
558
|
+
|
|
559
|
+
# Build
|
|
560
|
+
.next/
|
|
561
|
+
out/
|
|
562
|
+
build/
|
|
563
|
+
dist/
|
|
564
|
+
|
|
565
|
+
# Testing
|
|
566
|
+
coverage/
|
|
567
|
+
|
|
568
|
+
# IDE
|
|
569
|
+
.idea/
|
|
570
|
+
.vscode/
|
|
571
|
+
|
|
572
|
+
# Env
|
|
573
|
+
.env*.local
|
|
574
|
+
|
|
575
|
+
# Up CLI
|
|
576
|
+
.loop_state.json
|
|
577
|
+
"""
|
|
578
|
+
_write_file(target_dir / ".gitignore", gitignore, force)
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
# =============================================================================
|
|
582
|
+
# Python Library Template
|
|
583
|
+
# =============================================================================
|
|
584
|
+
|
|
585
|
+
def _create_python_lib_template(target_dir: Path, project_name: str, force: bool) -> None:
|
|
586
|
+
"""Create Python library template."""
|
|
587
|
+
safe_name = project_name.replace("-", "_").lower()
|
|
588
|
+
|
|
589
|
+
# Create directories
|
|
590
|
+
dirs = [
|
|
591
|
+
f"src/{safe_name}",
|
|
592
|
+
"tests",
|
|
593
|
+
]
|
|
594
|
+
for d in dirs:
|
|
595
|
+
(target_dir / d).mkdir(parents=True, exist_ok=True)
|
|
596
|
+
|
|
597
|
+
# pyproject.toml
|
|
598
|
+
pyproject = f'''[build-system]
|
|
599
|
+
requires = ["hatchling"]
|
|
600
|
+
build-backend = "hatchling.build"
|
|
601
|
+
|
|
602
|
+
[project]
|
|
603
|
+
name = "{project_name}"
|
|
604
|
+
version = "0.1.0"
|
|
605
|
+
description = "A Python library"
|
|
606
|
+
readme = "README.md"
|
|
607
|
+
license = "MIT"
|
|
608
|
+
requires-python = ">=3.10"
|
|
609
|
+
authors = [
|
|
610
|
+
{{ name = "Your Name", email = "you@example.com" }}
|
|
611
|
+
]
|
|
612
|
+
classifiers = [
|
|
613
|
+
"Development Status :: 3 - Alpha",
|
|
614
|
+
"Intended Audience :: Developers",
|
|
615
|
+
"License :: OSI Approved :: MIT License",
|
|
616
|
+
"Programming Language :: Python :: 3",
|
|
617
|
+
"Programming Language :: Python :: 3.10",
|
|
618
|
+
"Programming Language :: Python :: 3.11",
|
|
619
|
+
"Programming Language :: Python :: 3.12",
|
|
620
|
+
]
|
|
621
|
+
dependencies = []
|
|
622
|
+
|
|
623
|
+
[project.optional-dependencies]
|
|
624
|
+
dev = [
|
|
625
|
+
"pytest>=7.0",
|
|
626
|
+
"pytest-cov>=4.0",
|
|
627
|
+
"ruff>=0.1.0",
|
|
628
|
+
"mypy>=1.5.0",
|
|
629
|
+
]
|
|
630
|
+
|
|
631
|
+
[project.urls]
|
|
632
|
+
Homepage = "https://github.com/yourusername/{project_name}"
|
|
633
|
+
Documentation = "https://github.com/yourusername/{project_name}#readme"
|
|
634
|
+
Repository = "https://github.com/yourusername/{project_name}"
|
|
635
|
+
|
|
636
|
+
[tool.hatch.build.targets.wheel]
|
|
637
|
+
packages = ["src/{safe_name}"]
|
|
638
|
+
|
|
639
|
+
[tool.pytest.ini_options]
|
|
640
|
+
testpaths = ["tests"]
|
|
641
|
+
addopts = "--cov={safe_name} --cov-report=term-missing"
|
|
642
|
+
|
|
643
|
+
[tool.ruff]
|
|
644
|
+
line-length = 100
|
|
645
|
+
target-version = "py310"
|
|
646
|
+
select = ["E", "F", "I", "N", "W", "UP"]
|
|
647
|
+
|
|
648
|
+
[tool.mypy]
|
|
649
|
+
python_version = "3.10"
|
|
650
|
+
strict = true
|
|
651
|
+
'''
|
|
652
|
+
_write_file(target_dir / "pyproject.toml", pyproject, force)
|
|
653
|
+
|
|
654
|
+
# Main module
|
|
655
|
+
init_py = f'''"""{project_name} - A Python library.
|
|
656
|
+
|
|
657
|
+
Example:
|
|
658
|
+
>>> from {safe_name} import hello
|
|
659
|
+
>>> hello("World")
|
|
660
|
+
'Hello, World!'
|
|
661
|
+
"""
|
|
662
|
+
|
|
663
|
+
__version__ = "0.1.0"
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def hello(name: str) -> str:
|
|
667
|
+
"""Say hello.
|
|
668
|
+
|
|
669
|
+
Args:
|
|
670
|
+
name: Name to greet
|
|
671
|
+
|
|
672
|
+
Returns:
|
|
673
|
+
Greeting message
|
|
674
|
+
"""
|
|
675
|
+
return f"Hello, {{name}}!"
|
|
676
|
+
'''
|
|
677
|
+
_write_file(target_dir / f"src/{safe_name}/__init__.py", init_py, force)
|
|
678
|
+
|
|
679
|
+
# Tests
|
|
680
|
+
test_main = f'''"""Tests for {project_name}."""
|
|
681
|
+
|
|
682
|
+
from {safe_name} import hello, __version__
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def test_version():
|
|
686
|
+
"""Test version is set."""
|
|
687
|
+
assert __version__ == "0.1.0"
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def test_hello():
|
|
691
|
+
"""Test hello function."""
|
|
692
|
+
assert hello("World") == "Hello, World!"
|
|
693
|
+
assert hello("Python") == "Hello, Python!"
|
|
694
|
+
'''
|
|
695
|
+
_write_file(target_dir / "tests/test_main.py", test_main, force)
|
|
696
|
+
_write_file(target_dir / "tests/__init__.py", "", force)
|
|
697
|
+
|
|
698
|
+
# README
|
|
699
|
+
readme = f'''# {project_name}
|
|
700
|
+
|
|
701
|
+
A Python library.
|
|
702
|
+
|
|
703
|
+
## Installation
|
|
704
|
+
|
|
705
|
+
```bash
|
|
706
|
+
pip install {project_name}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
## Usage
|
|
710
|
+
|
|
711
|
+
```python
|
|
712
|
+
from {safe_name} import hello
|
|
713
|
+
|
|
714
|
+
print(hello("World")) # Hello, World!
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
## Development
|
|
718
|
+
|
|
719
|
+
```bash
|
|
720
|
+
# Install with dev dependencies
|
|
721
|
+
pip install -e ".[dev]"
|
|
722
|
+
|
|
723
|
+
# Run tests
|
|
724
|
+
pytest
|
|
725
|
+
|
|
726
|
+
# Lint
|
|
727
|
+
ruff check src/
|
|
728
|
+
|
|
729
|
+
# Type check
|
|
730
|
+
mypy src/
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
## License
|
|
734
|
+
|
|
735
|
+
MIT
|
|
736
|
+
'''
|
|
737
|
+
_write_file(target_dir / "README.md", readme, force)
|
|
738
|
+
|
|
739
|
+
# LICENSE
|
|
740
|
+
license_text = f'''MIT License
|
|
741
|
+
|
|
742
|
+
Copyright (c) {date.today().year} Your Name
|
|
743
|
+
|
|
744
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
745
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
746
|
+
in the Software without restriction, including without limitation the rights
|
|
747
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
748
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
749
|
+
furnished to do so, subject to the following conditions:
|
|
750
|
+
|
|
751
|
+
The above copyright notice and this permission notice shall be included in all
|
|
752
|
+
copies or substantial portions of the Software.
|
|
753
|
+
|
|
754
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
755
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
756
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
757
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
758
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
759
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
760
|
+
SOFTWARE.
|
|
761
|
+
'''
|
|
762
|
+
_write_file(target_dir / "LICENSE", license_text, force)
|
|
763
|
+
|
|
764
|
+
# .gitignore
|
|
765
|
+
gitignore = """# Python
|
|
766
|
+
__pycache__/
|
|
767
|
+
*.py[cod]
|
|
768
|
+
*.egg-info/
|
|
769
|
+
.venv/
|
|
770
|
+
venv/
|
|
771
|
+
dist/
|
|
772
|
+
build/
|
|
773
|
+
|
|
774
|
+
# IDE
|
|
775
|
+
.idea/
|
|
776
|
+
.vscode/
|
|
777
|
+
|
|
778
|
+
# Testing
|
|
779
|
+
.pytest_cache/
|
|
780
|
+
.coverage
|
|
781
|
+
htmlcov/
|
|
782
|
+
|
|
783
|
+
# Up CLI
|
|
784
|
+
.loop_state.json
|
|
785
|
+
"""
|
|
786
|
+
_write_file(target_dir / ".gitignore", gitignore, force)
|