forgedev 1.0.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.
- package/CLAUDE.md +38 -0
- package/LICENSE +21 -0
- package/README.md +246 -0
- package/bin/devforge.js +4 -0
- package/package.json +33 -0
- package/src/claude-configurator.js +260 -0
- package/src/cli.js +119 -0
- package/src/composer.js +214 -0
- package/src/doctor-checks.js +743 -0
- package/src/doctor-prompts.js +295 -0
- package/src/doctor.js +281 -0
- package/src/guided.js +315 -0
- package/src/index.js +148 -0
- package/src/init-mode.js +134 -0
- package/src/prompts.js +155 -0
- package/src/recommender.js +186 -0
- package/src/scanner.js +368 -0
- package/src/uat-generator.js +189 -0
- package/src/utils.js +57 -0
- package/templates/auth/jwt-custom/backend/app/api/auth.py.template +45 -0
- package/templates/auth/jwt-custom/backend/app/api/deps.py.template +16 -0
- package/templates/auth/jwt-custom/backend/app/core/security.py.template +34 -0
- package/templates/auth/nextauth/src/app/api/auth/[...nextauth]/route.ts.template +3 -0
- package/templates/auth/nextauth/src/lib/auth.ts.template +30 -0
- package/templates/auth/nextauth/src/middleware.ts.template +14 -0
- package/templates/backend/fastapi/backend/Dockerfile.template +12 -0
- package/templates/backend/fastapi/backend/app/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/api/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/api/health.py.template +32 -0
- package/templates/backend/fastapi/backend/app/core/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/core/config.py.template +25 -0
- package/templates/backend/fastapi/backend/app/core/errors.py +37 -0
- package/templates/backend/fastapi/backend/app/core/retry.py +32 -0
- package/templates/backend/fastapi/backend/app/main.py.template +58 -0
- package/templates/backend/fastapi/backend/app/models/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/schemas/__init__.py +0 -0
- package/templates/backend/fastapi/backend/pyproject.toml.template +19 -0
- package/templates/backend/fastapi/backend/requirements.txt.template +14 -0
- package/templates/base/.gitignore.template +29 -0
- package/templates/base/README.md.template +25 -0
- package/templates/claude-code/agents/code-quality-reviewer.md +41 -0
- package/templates/claude-code/agents/production-readiness.md +55 -0
- package/templates/claude-code/agents/security-reviewer.md +41 -0
- package/templates/claude-code/agents/spec-validator.md +34 -0
- package/templates/claude-code/agents/uat-validator.md +37 -0
- package/templates/claude-code/claude-md/base.md +33 -0
- package/templates/claude-code/claude-md/fastapi.md +12 -0
- package/templates/claude-code/claude-md/fullstack.md +12 -0
- package/templates/claude-code/claude-md/nextjs.md +11 -0
- package/templates/claude-code/commands/audit-security.md +11 -0
- package/templates/claude-code/commands/audit-spec.md +9 -0
- package/templates/claude-code/commands/audit-wiring.md +17 -0
- package/templates/claude-code/commands/done.md +19 -0
- package/templates/claude-code/commands/generate-prd.md +45 -0
- package/templates/claude-code/commands/generate-uat.md +35 -0
- package/templates/claude-code/commands/help.md +26 -0
- package/templates/claude-code/commands/next.md +20 -0
- package/templates/claude-code/commands/optimize-claude-md.md +31 -0
- package/templates/claude-code/commands/pre-pr.md +19 -0
- package/templates/claude-code/commands/run-uat.md +21 -0
- package/templates/claude-code/commands/status.md +24 -0
- package/templates/claude-code/commands/verify-all.md +11 -0
- package/templates/claude-code/hooks/polyglot.json +36 -0
- package/templates/claude-code/hooks/python.json +36 -0
- package/templates/claude-code/hooks/scripts/autofix-polyglot.sh +16 -0
- package/templates/claude-code/hooks/scripts/autofix-python.sh +14 -0
- package/templates/claude-code/hooks/scripts/autofix-typescript.sh +14 -0
- package/templates/claude-code/hooks/scripts/guard-protected-files.sh +21 -0
- package/templates/claude-code/hooks/typescript.json +36 -0
- package/templates/claude-code/skills/ai-prompts/SKILL.md +43 -0
- package/templates/claude-code/skills/fastapi/SKILL.md +38 -0
- package/templates/claude-code/skills/nextjs/SKILL.md +39 -0
- package/templates/claude-code/skills/playwright/SKILL.md +37 -0
- package/templates/claude-code/skills/security-api/SKILL.md +47 -0
- package/templates/claude-code/skills/security-web/SKILL.md +41 -0
- package/templates/database/prisma-postgres/.env.example +1 -0
- package/templates/database/prisma-postgres/prisma/schema.prisma.template +18 -0
- package/templates/database/sqlalchemy-postgres/.env.example +1 -0
- package/templates/database/sqlalchemy-postgres/backend/alembic/env.py.template +40 -0
- package/templates/database/sqlalchemy-postgres/backend/alembic/versions/.gitkeep +0 -0
- package/templates/database/sqlalchemy-postgres/backend/alembic.ini.template +36 -0
- package/templates/database/sqlalchemy-postgres/backend/app/db/__init__.py +0 -0
- package/templates/database/sqlalchemy-postgres/backend/app/db/base.py +5 -0
- package/templates/database/sqlalchemy-postgres/backend/app/db/session.py.template +48 -0
- package/templates/frontend/nextjs/next.config.ts.template +7 -0
- package/templates/frontend/nextjs/package.json.template +41 -0
- package/templates/frontend/nextjs/postcss.config.mjs +7 -0
- package/templates/frontend/nextjs/src/app/api/health/route.ts.template +10 -0
- package/templates/frontend/nextjs/src/app/globals.css +1 -0
- package/templates/frontend/nextjs/src/app/layout.tsx.template +22 -0
- package/templates/frontend/nextjs/src/app/page.tsx.template +10 -0
- package/templates/frontend/nextjs/src/lib/db.ts.template +40 -0
- package/templates/frontend/nextjs/src/lib/errors.ts +28 -0
- package/templates/frontend/nextjs/src/lib/utils.ts +6 -0
- package/templates/frontend/nextjs/tsconfig.json +23 -0
- package/templates/infra/docker-compose/docker-compose.yml.template +19 -0
- package/templates/testing/playwright/e2e/example.spec.ts.template +15 -0
- package/templates/testing/playwright/playwright.config.ts.template +22 -0
- package/templates/testing/vitest/src/__tests__/example.test.ts.template +12 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { log } from './utils.js';
|
|
5
|
+
|
|
6
|
+
export async function parseCommand(args) {
|
|
7
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
8
|
+
const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
|
9
|
+
console.log(pkg.version);
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
14
|
+
showHelp();
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const command = args[0];
|
|
19
|
+
|
|
20
|
+
if (!command) {
|
|
21
|
+
showUsage();
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (command === 'new') {
|
|
26
|
+
const projectName = args[1];
|
|
27
|
+
if (!projectName) {
|
|
28
|
+
log.error('Usage: devforge new <project-name>');
|
|
29
|
+
log.dim(' Example: devforge new my-app');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const { runNew } = await import('./index.js');
|
|
33
|
+
await runNew(projectName);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (command === 'init') {
|
|
38
|
+
const { runInit } = await import('./init-mode.js');
|
|
39
|
+
await runInit(process.cwd());
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (command === 'doctor') {
|
|
44
|
+
const { runDoctor } = await import('./doctor.js');
|
|
45
|
+
await runDoctor(process.cwd());
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Shorthand: devforge my-app → devforge new my-app
|
|
50
|
+
if (!command.startsWith('-')) {
|
|
51
|
+
const targetDir = path.resolve(process.cwd(), command);
|
|
52
|
+
if (fs.existsSync(targetDir)) {
|
|
53
|
+
console.log('');
|
|
54
|
+
log.warn(`"${command}" already exists. Did you mean:`);
|
|
55
|
+
console.log(` ${chalk.bold('devforge init')} Add dev guardrails to current project`);
|
|
56
|
+
console.log(` ${chalk.bold('devforge doctor')} Diagnose and optimize current project`);
|
|
57
|
+
console.log('');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const { runNew } = await import('./index.js');
|
|
61
|
+
await runNew(command);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
showUsage();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function showUsage() {
|
|
69
|
+
console.log(`
|
|
70
|
+
${chalk.bold.cyan('DevForge')} — AI-first project scaffolding
|
|
71
|
+
|
|
72
|
+
${chalk.bold('Usage:')}
|
|
73
|
+
devforge new <name> Create a new project
|
|
74
|
+
devforge init Add dev guardrails to current project
|
|
75
|
+
devforge doctor Diagnose and optimize current project
|
|
76
|
+
devforge <name> Shorthand for ${chalk.dim('devforge new <name>')}
|
|
77
|
+
|
|
78
|
+
${chalk.bold('Options:')}
|
|
79
|
+
-h, --help Show this help message
|
|
80
|
+
-v, --version Show version number
|
|
81
|
+
|
|
82
|
+
Run ${chalk.cyan('devforge new --help')} for more details.
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function showHelp() {
|
|
87
|
+
console.log(`
|
|
88
|
+
${chalk.bold.cyan('DevForge')} — Universal, AI-first project scaffolding
|
|
89
|
+
|
|
90
|
+
${chalk.bold('Commands:')}
|
|
91
|
+
|
|
92
|
+
${chalk.bold('devforge new <name>')}
|
|
93
|
+
Create a new project. Choose between:
|
|
94
|
+
${chalk.dim('• Guided mode — describe what you want in plain English')}
|
|
95
|
+
${chalk.dim('• Developer mode — pick your stack directly')}
|
|
96
|
+
|
|
97
|
+
${chalk.bold('devforge init')}
|
|
98
|
+
Add Claude Code infrastructure to an existing project.
|
|
99
|
+
Auto-detects your stack and installs hooks, agents, commands,
|
|
100
|
+
skills, and UAT templates. Zero questions asked.
|
|
101
|
+
|
|
102
|
+
${chalk.bold('devforge doctor')}
|
|
103
|
+
Diagnose and optimize an existing project.
|
|
104
|
+
Checks for: oversized CLAUDE.md, unauthenticated endpoints,
|
|
105
|
+
flaky tests, dead code, duplicate code, and more.
|
|
106
|
+
Generates Claude Code prompts to fix each issue.
|
|
107
|
+
|
|
108
|
+
${chalk.bold('Supported stacks (V1):')}
|
|
109
|
+
- Next.js full-stack (TypeScript + Prisma + PostgreSQL)
|
|
110
|
+
- FastAPI backend (Python + SQLAlchemy + PostgreSQL)
|
|
111
|
+
- Polyglot full-stack (Next.js + FastAPI monorepo)
|
|
112
|
+
|
|
113
|
+
${chalk.bold('Examples:')}
|
|
114
|
+
devforge new my-app
|
|
115
|
+
devforge my-saas-platform
|
|
116
|
+
devforge init
|
|
117
|
+
devforge doctor
|
|
118
|
+
`);
|
|
119
|
+
}
|
package/src/composer.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ROOT_DIR, ensureDir, readTemplate, replaceVars, toPascalCase, toSnakeCase, log } from './utils.js';
|
|
4
|
+
|
|
5
|
+
const TEMPLATES_DIR = path.join(ROOT_DIR, 'templates');
|
|
6
|
+
|
|
7
|
+
export async function compose(outputDir, stackConfig) {
|
|
8
|
+
const variables = buildVariables(stackConfig);
|
|
9
|
+
|
|
10
|
+
ensureDir(outputDir);
|
|
11
|
+
|
|
12
|
+
for (const mod of stackConfig.templateModules) {
|
|
13
|
+
const templateDir = path.join(TEMPLATES_DIR, mod.path);
|
|
14
|
+
if (!fs.existsSync(templateDir)) {
|
|
15
|
+
log.warn(`Template module not found: ${mod.path} — skipping`);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
processTemplateDir(templateDir, outputDir, variables, mod.prefix || '');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Inject auth dependencies into package.json if auth is enabled
|
|
22
|
+
injectAuthDependencies(outputDir, stackConfig);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function buildVariables(stackConfig) {
|
|
26
|
+
const vars = {
|
|
27
|
+
PROJECT_NAME: stackConfig.projectName,
|
|
28
|
+
PROJECT_NAME_PASCAL: toPascalCase(stackConfig.projectName),
|
|
29
|
+
PROJECT_NAME_SNAKE: toSnakeCase(stackConfig.projectName),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (stackConfig.frontend) {
|
|
33
|
+
vars.FRONTEND_FRAMEWORK = stackConfig.frontend.framework;
|
|
34
|
+
vars.FRONTEND_LANGUAGE = stackConfig.frontend.language;
|
|
35
|
+
vars.FRONTEND_STYLING = stackConfig.frontend.styling;
|
|
36
|
+
vars.FRONTEND_UI = stackConfig.frontend.ui || '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (stackConfig.backend) {
|
|
40
|
+
vars.BACKEND_FRAMEWORK = stackConfig.backend.framework;
|
|
41
|
+
vars.BACKEND_LANGUAGE = stackConfig.backend.language;
|
|
42
|
+
vars.BACKEND_ORM = stackConfig.backend.orm;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (stackConfig.database) {
|
|
46
|
+
vars.DATABASE_TYPE = stackConfig.database.type;
|
|
47
|
+
vars.DATABASE_ORM = stackConfig.database.orm;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
vars.AUTH_TYPE = stackConfig.auth || 'none';
|
|
51
|
+
vars.DEPLOYMENT = stackConfig.deployment || 'docker';
|
|
52
|
+
|
|
53
|
+
// Commands vary by stack
|
|
54
|
+
if (stackConfig.stackId === 'nextjs-fullstack') {
|
|
55
|
+
vars.LINT_COMMAND = 'npx eslint .';
|
|
56
|
+
vars.TYPE_CHECK_COMMAND = 'npx tsc --noEmit';
|
|
57
|
+
vars.TEST_COMMAND = 'npx vitest run';
|
|
58
|
+
vars.BUILD_COMMAND = 'npm run build';
|
|
59
|
+
vars.DEV_COMMAND = 'npm run dev';
|
|
60
|
+
vars.STACK_DESCRIPTION = 'Next.js full-stack application with TypeScript, Tailwind CSS, Prisma, and PostgreSQL';
|
|
61
|
+
vars.EXTRA_IGNORES = '';
|
|
62
|
+
} else if (stackConfig.stackId === 'fastapi-backend') {
|
|
63
|
+
vars.LINT_COMMAND = 'ruff check .';
|
|
64
|
+
vars.TYPE_CHECK_COMMAND = 'pyright';
|
|
65
|
+
vars.TEST_COMMAND = 'pytest';
|
|
66
|
+
vars.BUILD_COMMAND = 'docker build -t app .';
|
|
67
|
+
vars.DEV_COMMAND = 'uvicorn app.main:app --reload';
|
|
68
|
+
vars.STACK_DESCRIPTION = 'FastAPI backend service with SQLAlchemy, PostgreSQL, and Alembic';
|
|
69
|
+
vars.EXTRA_IGNORES = '\n# Python\n__pycache__/\n*.pyc\nvenv/\n.venv/\n*.egg-info/';
|
|
70
|
+
} else if (stackConfig.stackId === 'polyglot-fullstack') {
|
|
71
|
+
vars.LINT_COMMAND = 'cd frontend && npx eslint . && cd ../backend && ruff check .';
|
|
72
|
+
vars.TYPE_CHECK_COMMAND = 'cd frontend && npx tsc --noEmit';
|
|
73
|
+
vars.TEST_COMMAND = 'cd frontend && npx vitest run && cd ../backend && pytest';
|
|
74
|
+
vars.BUILD_COMMAND = 'docker compose build';
|
|
75
|
+
vars.DEV_COMMAND = 'docker compose up';
|
|
76
|
+
vars.STACK_DESCRIPTION = 'Full-stack application with Next.js frontend and FastAPI backend';
|
|
77
|
+
vars.EXTRA_IGNORES = '\n# Python\n__pycache__/\n*.pyc\nvenv/\n.venv/\n*.egg-info/';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Setup commands for README
|
|
81
|
+
vars.SETUP_COMMANDS = buildSetupCommands(stackConfig);
|
|
82
|
+
vars.AVAILABLE_SCRIPTS = buildAvailableScripts(stackConfig);
|
|
83
|
+
|
|
84
|
+
return vars;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function buildSetupCommands(config) {
|
|
88
|
+
if (config.stackId === 'nextjs-fullstack') {
|
|
89
|
+
return `npm install
|
|
90
|
+
cp .env.example .env
|
|
91
|
+
npx prisma db push
|
|
92
|
+
npm run dev`;
|
|
93
|
+
}
|
|
94
|
+
if (config.stackId === 'fastapi-backend') {
|
|
95
|
+
return `cd backend
|
|
96
|
+
python -m venv venv
|
|
97
|
+
source venv/bin/activate
|
|
98
|
+
pip install -r requirements.txt
|
|
99
|
+
cp .env.example .env
|
|
100
|
+
uvicorn app.main:app --reload`;
|
|
101
|
+
}
|
|
102
|
+
if (config.stackId === 'polyglot-fullstack') {
|
|
103
|
+
return `docker compose up -d postgres
|
|
104
|
+
# Frontend
|
|
105
|
+
cd frontend && npm install && npm run dev
|
|
106
|
+
# Backend
|
|
107
|
+
cd backend && pip install -r requirements.txt && uvicorn app.main:app --reload`;
|
|
108
|
+
}
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildAvailableScripts(config) {
|
|
113
|
+
if (config.stackId === 'nextjs-fullstack') {
|
|
114
|
+
return `- \`npm run dev\` — Start development server
|
|
115
|
+
- \`npm run build\` — Production build
|
|
116
|
+
- \`npm run lint\` — Run ESLint
|
|
117
|
+
- \`npx prisma studio\` — Database GUI
|
|
118
|
+
- \`npx vitest\` — Run unit tests
|
|
119
|
+
- \`npx playwright test\` — Run E2E tests`;
|
|
120
|
+
}
|
|
121
|
+
if (config.stackId === 'fastapi-backend') {
|
|
122
|
+
return `- \`uvicorn app.main:app --reload\` — Start dev server
|
|
123
|
+
- \`pytest\` — Run tests
|
|
124
|
+
- \`ruff check .\` — Run linter
|
|
125
|
+
- \`alembic upgrade head\` — Run migrations`;
|
|
126
|
+
}
|
|
127
|
+
if (config.stackId === 'polyglot-fullstack') {
|
|
128
|
+
return `- \`docker compose up\` — Start all services
|
|
129
|
+
- \`docker compose up -d postgres\` — Start database only
|
|
130
|
+
- Frontend: \`cd frontend && npm run dev\`
|
|
131
|
+
- Backend: \`cd backend && uvicorn app.main:app --reload\``;
|
|
132
|
+
}
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function processTemplateDir(templateDir, outputDir, variables, prefix) {
|
|
137
|
+
const entries = walkDir(templateDir);
|
|
138
|
+
|
|
139
|
+
for (const filePath of entries) {
|
|
140
|
+
const relativePath = path.relative(templateDir, filePath);
|
|
141
|
+
let outputRelative = prefix ? path.join(prefix, relativePath) : relativePath;
|
|
142
|
+
|
|
143
|
+
if (outputRelative.endsWith('.template')) {
|
|
144
|
+
// Process template: replace vars, strip .template extension
|
|
145
|
+
const content = readTemplate(filePath);
|
|
146
|
+
const processed = replaceVars(content, variables);
|
|
147
|
+
const outputPath = path.join(outputDir, outputRelative.replace(/\.template$/, ''));
|
|
148
|
+
ensureDir(path.dirname(outputPath));
|
|
149
|
+
fs.writeFileSync(outputPath, processed, 'utf-8');
|
|
150
|
+
} else if (path.basename(filePath) === '.gitkeep') {
|
|
151
|
+
// Create the directory but don't copy .gitkeep itself
|
|
152
|
+
ensureDir(path.join(outputDir, path.dirname(outputRelative)));
|
|
153
|
+
} else {
|
|
154
|
+
// Binary copy
|
|
155
|
+
const outputPath = path.join(outputDir, outputRelative);
|
|
156
|
+
ensureDir(path.dirname(outputPath));
|
|
157
|
+
fs.copyFileSync(filePath, outputPath);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function walkDir(dir) {
|
|
163
|
+
const results = [];
|
|
164
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
165
|
+
for (const entry of entries) {
|
|
166
|
+
const fullPath = path.join(dir, entry.name);
|
|
167
|
+
if (entry.isDirectory()) {
|
|
168
|
+
results.push(...walkDir(fullPath));
|
|
169
|
+
} else {
|
|
170
|
+
results.push(fullPath);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return results;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function injectAuthDependencies(outputDir, stackConfig) {
|
|
177
|
+
if (!stackConfig.auth || stackConfig.auth === 'none') return;
|
|
178
|
+
|
|
179
|
+
// Next.js projects with nextauth
|
|
180
|
+
if (stackConfig.auth === 'nextauth' || stackConfig.auth === 'both') {
|
|
181
|
+
const pkgPaths = [
|
|
182
|
+
path.join(outputDir, 'package.json'),
|
|
183
|
+
path.join(outputDir, 'frontend', 'package.json'),
|
|
184
|
+
];
|
|
185
|
+
for (const pkgPath of pkgPaths) {
|
|
186
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
187
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
188
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
189
|
+
pkg.dependencies['next-auth'] = '^5.0.0-beta.25';
|
|
190
|
+
pkg.dependencies['@auth/prisma-adapter'] = '^2.7.4';
|
|
191
|
+
pkg.dependencies['bcryptjs'] = '^2.4.3';
|
|
192
|
+
pkg.devDependencies = pkg.devDependencies || {};
|
|
193
|
+
pkg.devDependencies['@types/bcryptjs'] = '^2.4.6';
|
|
194
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// FastAPI projects with jwt-custom
|
|
199
|
+
if (stackConfig.auth === 'jwt-custom' || stackConfig.auth === 'both') {
|
|
200
|
+
const reqPaths = [
|
|
201
|
+
path.join(outputDir, 'requirements.txt'),
|
|
202
|
+
path.join(outputDir, 'backend', 'requirements.txt'),
|
|
203
|
+
];
|
|
204
|
+
for (const reqPath of reqPaths) {
|
|
205
|
+
if (!fs.existsSync(reqPath)) continue;
|
|
206
|
+
const content = fs.readFileSync(reqPath, 'utf-8');
|
|
207
|
+
const authDeps = ['python-jose[cryptography]', 'passlib[bcrypt]', 'python-multipart'];
|
|
208
|
+
const additions = authDeps.filter(dep => !content.includes(dep.split('[')[0]));
|
|
209
|
+
if (additions.length > 0) {
|
|
210
|
+
fs.writeFileSync(reqPath, content.trimEnd() + '\n' + additions.join('\n') + '\n', 'utf-8');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|