claude-autopm 1.17.0 → 1.20.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 (76) hide show
  1. package/README.md +159 -0
  2. package/autopm/.claude/agents/core/mcp-manager.md +1 -1
  3. package/autopm/.claude/commands/pm/context.md +11 -0
  4. package/autopm/.claude/commands/pm/epic-decompose.md +25 -2
  5. package/autopm/.claude/commands/pm/epic-oneshot.md +13 -0
  6. package/autopm/.claude/commands/pm/epic-start.md +19 -0
  7. package/autopm/.claude/commands/pm/epic-sync-modular.md +10 -10
  8. package/autopm/.claude/commands/pm/epic-sync.md +14 -14
  9. package/autopm/.claude/commands/pm/issue-start.md +50 -5
  10. package/autopm/.claude/commands/pm/issue-sync.md +15 -15
  11. package/autopm/.claude/commands/pm/what-next.md +11 -0
  12. package/autopm/.claude/mcp/MCP-REGISTRY.md +1 -1
  13. package/autopm/.claude/scripts/azure/active-work.js +2 -2
  14. package/autopm/.claude/scripts/azure/blocked.js +13 -13
  15. package/autopm/.claude/scripts/azure/daily.js +1 -1
  16. package/autopm/.claude/scripts/azure/dashboard.js +1 -1
  17. package/autopm/.claude/scripts/azure/feature-list.js +2 -2
  18. package/autopm/.claude/scripts/azure/feature-status.js +1 -1
  19. package/autopm/.claude/scripts/azure/next-task.js +1 -1
  20. package/autopm/.claude/scripts/azure/search.js +1 -1
  21. package/autopm/.claude/scripts/azure/setup.js +15 -15
  22. package/autopm/.claude/scripts/azure/sprint-report.js +2 -2
  23. package/autopm/.claude/scripts/azure/sync.js +1 -1
  24. package/autopm/.claude/scripts/azure/us-list.js +1 -1
  25. package/autopm/.claude/scripts/azure/us-status.js +1 -1
  26. package/autopm/.claude/scripts/azure/validate.js +13 -13
  27. package/autopm/.claude/scripts/lib/frontmatter-utils.sh +42 -7
  28. package/autopm/.claude/scripts/lib/logging-utils.sh +20 -16
  29. package/autopm/.claude/scripts/lib/validation-utils.sh +1 -1
  30. package/autopm/.claude/scripts/pm/context.js +338 -0
  31. package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +3 -3
  32. package/autopm/.claude/scripts/pm/lib/README.md +85 -0
  33. package/autopm/.claude/scripts/pm/lib/logger.js +78 -0
  34. package/autopm/.claude/scripts/pm/next.js +25 -1
  35. package/autopm/.claude/scripts/pm/what-next.js +660 -0
  36. package/bin/autopm.js +25 -0
  37. package/bin/commands/team.js +86 -0
  38. package/package.json +1 -1
  39. package/lib/agentExecutor.js.deprecated +0 -101
  40. package/lib/azure/cache.js +0 -80
  41. package/lib/azure/client.js +0 -77
  42. package/lib/azure/formatter.js +0 -177
  43. package/lib/commandHelpers.js +0 -177
  44. package/lib/context/manager.js +0 -290
  45. package/lib/documentation/manager.js +0 -528
  46. package/lib/github/workflow-manager.js +0 -546
  47. package/lib/helpers/azure-batch-api.js +0 -133
  48. package/lib/helpers/azure-cache-manager.js +0 -287
  49. package/lib/helpers/azure-parallel-processor.js +0 -158
  50. package/lib/helpers/azure-work-item-create.js +0 -278
  51. package/lib/helpers/gh-issue-create.js +0 -250
  52. package/lib/helpers/interactive-prompt.js +0 -336
  53. package/lib/helpers/output-manager.js +0 -335
  54. package/lib/helpers/progress-indicator.js +0 -258
  55. package/lib/performance/benchmarker.js +0 -429
  56. package/lib/pm/epic-decomposer.js +0 -273
  57. package/lib/pm/epic-syncer.js +0 -221
  58. package/lib/prdMetadata.js +0 -270
  59. package/lib/providers/azure/index.js +0 -234
  60. package/lib/providers/factory.js +0 -87
  61. package/lib/providers/github/index.js +0 -204
  62. package/lib/providers/interface.js +0 -73
  63. package/lib/python/scaffold-manager.js +0 -576
  64. package/lib/react/scaffold-manager.js +0 -745
  65. package/lib/regression/analyzer.js +0 -578
  66. package/lib/release/manager.js +0 -324
  67. package/lib/tailwind/manager.js +0 -486
  68. package/lib/traefik/manager.js +0 -484
  69. package/lib/utils/colors.js +0 -126
  70. package/lib/utils/config.js +0 -317
  71. package/lib/utils/filesystem.js +0 -316
  72. package/lib/utils/logger.js +0 -135
  73. package/lib/utils/prompts.js +0 -294
  74. package/lib/utils/shell.js +0 -237
  75. package/lib/validators/email-validator.js +0 -337
  76. package/lib/workflow/manager.js +0 -449
@@ -1,576 +0,0 @@
1
- /**
2
- * Python Scaffold Manager
3
- * Centralized Python project scaffolding functionality
4
- */
5
-
6
- const fs = require('fs').promises;
7
- const path = require('path');
8
-
9
- /**
10
- * Configuration
11
- */
12
- const CONFIG = {
13
- defaults: {
14
- port: 8000,
15
- database: 'postgres',
16
- environment: 'development'
17
- }
18
- };
19
-
20
- /**
21
- * Template definitions
22
- */
23
- const TEMPLATES = {
24
- fastapi: {
25
- main: `from fastapi import FastAPI
26
- from fastapi.middleware.cors import CORSMiddleware
27
-
28
- app = FastAPI(title="API", version="1.0.0")
29
-
30
- # Configure CORS
31
- app.add_middleware(
32
- CORSMiddleware,
33
- allow_origins=["*"],
34
- allow_credentials=True,
35
- allow_methods=["*"],
36
- allow_headers=["*"],
37
- )
38
-
39
- @app.get("/")
40
- def read_root():
41
- return {"message": "Welcome to FastAPI"}
42
-
43
- @app.get("/health")
44
- def health_check():
45
- return {"status": "healthy"}
46
- `,
47
- requirements: `fastapi==0.104.1
48
- uvicorn[standard]==0.24.0
49
- pydantic==2.4.2
50
- python-multipart==0.0.6
51
- python-jose[cryptography]==3.3.0
52
- passlib[bcrypt]==1.7.4
53
- sqlalchemy==2.0.23
54
- alembic==1.12.1
55
- psycopg2-binary==2.9.9
56
- redis==5.0.1
57
- celery==5.3.4
58
- pytest==7.4.3
59
- pytest-asyncio==0.21.1
60
- httpx==0.25.1
61
- `
62
- },
63
- flask: {
64
- app: `from flask import Flask, jsonify
65
- from flask_cors import CORS
66
-
67
- app = Flask(__name__)
68
- CORS(app)
69
-
70
- @app.route('/')
71
- def index():
72
- return jsonify({"message": "Welcome to Flask"})
73
-
74
- @app.route('/health')
75
- def health():
76
- return jsonify({"status": "healthy"})
77
-
78
- if __name__ == '__main__':
79
- app.run(debug=True, host='0.0.0.0', port=5000)
80
- `,
81
- requirements: `flask==3.0.0
82
- flask-cors==4.0.0
83
- flask-sqlalchemy==3.1.1
84
- flask-migrate==4.0.5
85
- flask-jwt-extended==4.5.3
86
- python-dotenv==1.0.0
87
- gunicorn==21.2.0
88
- psycopg2-binary==2.9.9
89
- redis==5.0.1
90
- celery==5.3.4
91
- pytest==7.4.3
92
- pytest-flask==1.3.0
93
- `
94
- }
95
- };
96
-
97
- class PythonScaffoldManager {
98
- constructor(projectRoot = process.cwd()) {
99
- this.projectRoot = projectRoot;
100
- }
101
-
102
- /**
103
- * Creates API project
104
- */
105
- async createAPIProject(framework = 'fastapi', projectName = 'api') {
106
- if (framework === 'fastapi') {
107
- return await this.createFastAPIProject(projectName);
108
- } else if (framework === 'flask') {
109
- return await this.createFlaskProject();
110
- } else {
111
- throw new Error(`Unknown framework: ${framework}`);
112
- }
113
- }
114
-
115
- /**
116
- * Creates FastAPI project
117
- */
118
- async createFastAPIProject(projectName) {
119
- const projectDir = path.join(this.projectRoot, projectName);
120
-
121
- // Create directory structure
122
- await fs.mkdir(projectDir, { recursive: true });
123
- await fs.mkdir(path.join(projectDir, 'app'), { recursive: true });
124
- await fs.mkdir(path.join(projectDir, 'app', 'api'), { recursive: true });
125
- await fs.mkdir(path.join(projectDir, 'app', 'core'), { recursive: true });
126
- await fs.mkdir(path.join(projectDir, 'app', 'models'), { recursive: true });
127
-
128
- // Create main.py
129
- await fs.writeFile(path.join(projectDir, 'main.py'), TEMPLATES.fastapi.main);
130
-
131
- // Create __init__.py files
132
- await fs.writeFile(path.join(projectDir, 'app', '__init__.py'), '');
133
- await fs.writeFile(path.join(projectDir, 'app', 'api', '__init__.py'), '');
134
- await fs.writeFile(path.join(projectDir, 'app', 'core', '__init__.py'), '');
135
- await fs.writeFile(path.join(projectDir, 'app', 'models', '__init__.py'), '');
136
-
137
- // Create requirements.txt
138
- await fs.writeFile(
139
- path.join(this.projectRoot, 'requirements.txt'),
140
- TEMPLATES.fastapi.requirements
141
- );
142
-
143
- return {
144
- projectName,
145
- framework: 'fastapi',
146
- mainFile: `${projectName}/main.py`,
147
- structure: ['app/api', 'app/core', 'app/models']
148
- };
149
- }
150
-
151
- /**
152
- * Creates Flask project
153
- */
154
- async createFlaskProject() {
155
- // Create directory structure
156
- await fs.mkdir(path.join(this.projectRoot, 'templates'), { recursive: true });
157
- await fs.mkdir(path.join(this.projectRoot, 'static'), { recursive: true });
158
- await fs.mkdir(path.join(this.projectRoot, 'static', 'css'), { recursive: true });
159
- await fs.mkdir(path.join(this.projectRoot, 'static', 'js'), { recursive: true });
160
-
161
- // Create app.py
162
- await fs.writeFile(
163
- path.join(this.projectRoot, 'app.py'),
164
- TEMPLATES.flask.app
165
- );
166
-
167
- // Create requirements.txt
168
- await fs.writeFile(
169
- path.join(this.projectRoot, 'requirements.txt'),
170
- TEMPLATES.flask.requirements
171
- );
172
-
173
- return {
174
- framework: 'flask',
175
- mainFile: 'app.py',
176
- directories: ['templates/', 'static/']
177
- };
178
- }
179
-
180
- /**
181
- * Creates database models
182
- */
183
- async createModels(database = 'postgres') {
184
- await fs.mkdir(path.join(this.projectRoot, 'models'), { recursive: true });
185
-
186
- // Base model
187
- const baseModel = `from sqlalchemy import Column, Integer, DateTime, func
188
- from sqlalchemy.ext.declarative import declarative_base
189
-
190
- Base = declarative_base()
191
-
192
- class BaseModel(Base):
193
- __abstract__ = True
194
-
195
- id = Column(Integer, primary_key=True, index=True)
196
- created_at = Column(DateTime(timezone=True), server_default=func.now())
197
- updated_at = Column(DateTime(timezone=True), onupdate=func.now())
198
- `;
199
-
200
- // User model
201
- const userModel = `from sqlalchemy import Column, String, Boolean
202
- from .base import BaseModel
203
-
204
- class User(BaseModel):
205
- __tablename__ = "users"
206
-
207
- email = Column(String, unique=True, index=True, nullable=False)
208
- username = Column(String, unique=True, index=True, nullable=False)
209
- hashed_password = Column(String, nullable=False)
210
- is_active = Column(Boolean, default=True)
211
- is_superuser = Column(Boolean, default=False)
212
- `;
213
-
214
- // Database configuration
215
- const dbConfig = `from sqlalchemy import create_engine
216
- from sqlalchemy.orm import sessionmaker
217
-
218
- DATABASE_URL = "${database}://user:password@localhost/dbname"
219
-
220
- engine = create_engine(DATABASE_URL)
221
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
222
-
223
- def get_db():
224
- db = SessionLocal()
225
- try:
226
- yield db
227
- finally:
228
- db.close()
229
- `;
230
-
231
- await fs.writeFile(path.join(this.projectRoot, 'models', '__init__.py'), '');
232
- await fs.writeFile(path.join(this.projectRoot, 'models', 'base.py'), baseModel);
233
- await fs.writeFile(path.join(this.projectRoot, 'models', 'user.py'), userModel);
234
- await fs.writeFile(path.join(this.projectRoot, 'models', 'database.py'), dbConfig);
235
-
236
- return {
237
- database,
238
- models: ['base.py', 'user.py'],
239
- config: 'database.py'
240
- };
241
- }
242
-
243
- /**
244
- * Creates API routes
245
- */
246
- async createRoutes(resource = 'items') {
247
- await fs.mkdir(path.join(this.projectRoot, 'routes'), { recursive: true });
248
-
249
- const routeTemplate = `from fastapi import APIRouter, Depends, HTTPException
250
- from sqlalchemy.orm import Session
251
- from typing import List
252
-
253
- router = APIRouter(prefix="/${resource}", tags=["${resource}"])
254
-
255
- @router.get("/", response_model=List[dict])
256
- def get_${resource}():
257
- """Get all ${resource}"""
258
- return [{"id": 1, "name": "Example"}]
259
-
260
- @router.get("/{id}", response_model=dict)
261
- def get_${resource.slice(0, -1)}(id: int):
262
- """Get a specific ${resource.slice(0, -1)}"""
263
- return {"id": id, "name": "Example"}
264
-
265
- @router.post("/", response_model=dict)
266
- def create_${resource.slice(0, -1)}(data: dict):
267
- """Create a new ${resource.slice(0, -1)}"""
268
- return {"id": 1, **data}
269
-
270
- @router.put("/{id}", response_model=dict)
271
- def update_${resource.slice(0, -1)}(id: int, data: dict):
272
- """Update a ${resource.slice(0, -1)}"""
273
- return {"id": id, **data}
274
-
275
- @router.delete("/{id}")
276
- def delete_${resource.slice(0, -1)}(id: int):
277
- """Delete a ${resource.slice(0, -1)}"""
278
- return {"message": "Deleted successfully"}
279
- `;
280
-
281
- await fs.writeFile(path.join(this.projectRoot, 'routes', '__init__.py'), '');
282
- await fs.writeFile(
283
- path.join(this.projectRoot, 'routes', `${resource}.py`),
284
- routeTemplate
285
- );
286
-
287
- return {
288
- resource,
289
- file: `routes/${resource}.py`,
290
- endpoints: ['GET', 'POST', 'PUT', 'DELETE']
291
- };
292
- }
293
-
294
- /**
295
- * Adds authentication
296
- */
297
- async addAuthentication(authType = 'jwt') {
298
- await fs.mkdir(path.join(this.projectRoot, 'auth'), { recursive: true });
299
-
300
- const authInit = `from .auth import *
301
- from .jwt import *
302
- `;
303
-
304
- const jwtAuth = `from datetime import datetime, timedelta
305
- from typing import Optional
306
- from jose import JWTError, jwt
307
- from passlib.context import CryptContext
308
-
309
- SECRET_KEY = "your-secret-key-here"
310
- ALGORITHM = "HS256"
311
- ACCESS_TOKEN_EXPIRE_MINUTES = 30
312
-
313
- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
314
-
315
- def verify_password(plain_password, hashed_password):
316
- return pwd_context.verify(plain_password, hashed_password)
317
-
318
- def get_password_hash(password):
319
- return pwd_context.hash(password)
320
-
321
- def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
322
- to_encode = data.copy()
323
- if expires_delta:
324
- expire = datetime.utcnow() + expires_delta
325
- else:
326
- expire = datetime.utcnow() + timedelta(minutes=15)
327
- to_encode.update({"exp": expire})
328
- encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
329
- return encoded_jwt
330
-
331
- def decode_token(token: str):
332
- try:
333
- payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
334
- return payload
335
- except JWTError:
336
- return None
337
- `;
338
-
339
- await fs.writeFile(path.join(this.projectRoot, 'auth', '__init__.py'), authInit);
340
- await fs.writeFile(path.join(this.projectRoot, 'auth', 'jwt.py'), jwtAuth);
341
-
342
- return {
343
- type: authType,
344
- module: 'auth/',
345
- functions: ['verify_password', 'create_access_token', 'decode_token']
346
- };
347
- }
348
-
349
- /**
350
- * Generates Docker configuration
351
- */
352
- async generateDockerConfig(port = CONFIG.defaults.port) {
353
- const dockerfile = `FROM python:3.11-slim
354
-
355
- WORKDIR /app
356
-
357
- # Install system dependencies
358
- RUN apt-get update && apt-get install -y \\
359
- gcc \\
360
- && rm -rf /var/lib/apt/lists/*
361
-
362
- # Copy requirements first for better caching
363
- COPY requirements.txt .
364
- RUN pip install --no-cache-dir -r requirements.txt
365
-
366
- # Copy application code
367
- COPY . .
368
-
369
- # Expose port
370
- EXPOSE ${port}
371
-
372
- # Run the application
373
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "${port}"]
374
- `;
375
-
376
- const dockerCompose = `version: '3.8'
377
-
378
- services:
379
- api:
380
- build: .
381
- ports:
382
- - "${port}:${port}"
383
- environment:
384
- - DATABASE_URL=postgresql://user:password@db:5432/dbname
385
- - REDIS_URL=redis://redis:6379
386
- depends_on:
387
- - db
388
- - redis
389
- volumes:
390
- - .:/app
391
- command: uvicorn main:app --reload --host 0.0.0.0 --port ${port}
392
-
393
- db:
394
- image: postgres:15
395
- environment:
396
- - POSTGRES_USER=user
397
- - POSTGRES_PASSWORD=password
398
- - POSTGRES_DB=dbname
399
- volumes:
400
- - postgres_data:/var/lib/postgresql/data
401
- ports:
402
- - "5432:5432"
403
-
404
- redis:
405
- image: redis:7
406
- ports:
407
- - "6379:6379"
408
-
409
- volumes:
410
- postgres_data:
411
- `;
412
-
413
- await fs.writeFile(path.join(this.projectRoot, 'Dockerfile'), dockerfile);
414
- await fs.writeFile(path.join(this.projectRoot, 'docker-compose.yml'), dockerCompose);
415
-
416
- return {
417
- dockerfile: 'Dockerfile',
418
- compose: 'docker-compose.yml',
419
- port,
420
- services: ['api', 'db', 'redis']
421
- };
422
- }
423
-
424
- /**
425
- * Creates environment configuration
426
- */
427
- async createEnvironmentConfig(env = CONFIG.defaults.environment) {
428
- const envConfig = `# Environment
429
- ENV=${env}
430
- DEBUG=${env === 'development' ? 'true' : 'false'}
431
-
432
- # Application
433
- APP_NAME=Python API
434
- APP_VERSION=1.0.0
435
- SECRET_KEY=your-secret-key-here-change-in-production
436
-
437
- # Database
438
- DATABASE_URL=postgresql://user:password@localhost:5432/dbname
439
- DATABASE_POOL_SIZE=5
440
- DATABASE_MAX_OVERFLOW=10
441
-
442
- # Redis
443
- REDIS_URL=redis://localhost:6379/0
444
-
445
- # JWT
446
- JWT_SECRET_KEY=your-jwt-secret-key
447
- JWT_ALGORITHM=HS256
448
- JWT_EXPIRATION_MINUTES=30
449
-
450
- # CORS
451
- CORS_ORIGINS=http://localhost:3000,http://localhost:8080
452
-
453
- # Logging
454
- LOG_LEVEL=${env === 'development' ? 'DEBUG' : 'INFO'}
455
- `;
456
-
457
- const configLoader = `import os
458
- from dotenv import load_dotenv
459
-
460
- # Load environment variables
461
- load_dotenv(f".env.{os.getenv('ENV', 'development')}")
462
-
463
- class Settings:
464
- # Environment
465
- ENV: str = os.getenv("ENV", "development")
466
- DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true"
467
-
468
- # Application
469
- APP_NAME: str = os.getenv("APP_NAME", "API")
470
- APP_VERSION: str = os.getenv("APP_VERSION", "1.0.0")
471
- SECRET_KEY: str = os.getenv("SECRET_KEY", "")
472
-
473
- # Database
474
- DATABASE_URL: str = os.getenv("DATABASE_URL", "")
475
-
476
- # Redis
477
- REDIS_URL: str = os.getenv("REDIS_URL", "")
478
-
479
- # JWT
480
- JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY", "")
481
- JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM", "HS256")
482
- JWT_EXPIRATION_MINUTES: int = int(os.getenv("JWT_EXPIRATION_MINUTES", "30"))
483
-
484
- settings = Settings()
485
- `;
486
-
487
- await fs.writeFile(path.join(this.projectRoot, `.env.${env}`), envConfig);
488
- await fs.writeFile(path.join(this.projectRoot, 'config.py'), configLoader);
489
-
490
- return {
491
- environment: env,
492
- configFile: `.env.${env}`,
493
- loader: 'config.py'
494
- };
495
- }
496
-
497
- /**
498
- * Sets up testing framework
499
- */
500
- async setupTesting(framework = 'pytest') {
501
- await fs.mkdir(path.join(this.projectRoot, 'tests'), { recursive: true });
502
- await fs.mkdir(path.join(this.projectRoot, 'tests', 'unit'), { recursive: true });
503
- await fs.mkdir(path.join(this.projectRoot, 'tests', 'integration'), { recursive: true });
504
-
505
- // Create pytest.ini
506
- const pytestConfig = `[pytest]
507
- testpaths = tests
508
- python_files = test_*.py
509
- python_classes = Test*
510
- python_functions = test_*
511
- addopts = -v --tb=short --strict-markers --cov=app --cov-report=term-missing
512
- markers =
513
- unit: Unit tests
514
- integration: Integration tests
515
- slow: Slow tests
516
- `;
517
-
518
- // Create conftest.py
519
- const conftest = `import pytest
520
- from fastapi.testclient import TestClient
521
- from sqlalchemy import create_engine
522
- from sqlalchemy.orm import sessionmaker
523
-
524
- @pytest.fixture
525
- def client():
526
- from main import app
527
- return TestClient(app)
528
-
529
- @pytest.fixture
530
- def db_session():
531
- # Create test database session
532
- engine = create_engine("sqlite:///:memory:")
533
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
534
- session = SessionLocal()
535
- yield session
536
- session.close()
537
- `;
538
-
539
- // Sample test
540
- const sampleTest = `import pytest
541
-
542
- def test_health_check(client):
543
- response = client.get("/health")
544
- assert response.status_code == 200
545
- assert response.json() == {"status": "healthy"}
546
-
547
- def test_root(client):
548
- response = client.get("/")
549
- assert response.status_code == 200
550
- assert "message" in response.json()
551
-
552
- @pytest.mark.unit
553
- def test_example_unit():
554
- assert 1 + 1 == 2
555
-
556
- @pytest.mark.integration
557
- def test_example_integration(db_session):
558
- # Test with database
559
- assert db_session is not None
560
- `;
561
-
562
- await fs.writeFile(path.join(this.projectRoot, 'pytest.ini'), pytestConfig);
563
- await fs.writeFile(path.join(this.projectRoot, 'tests', '__init__.py'), '');
564
- await fs.writeFile(path.join(this.projectRoot, 'tests', 'conftest.py'), conftest);
565
- await fs.writeFile(path.join(this.projectRoot, 'tests', 'test_main.py'), sampleTest);
566
-
567
- return {
568
- framework,
569
- config: 'pytest.ini',
570
- testsDir: 'tests/',
571
- fixtures: 'conftest.py'
572
- };
573
- }
574
- }
575
-
576
- module.exports = PythonScaffoldManager;