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/prompts.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { select, confirm, checkbox, input } from '@inquirer/prompts';
|
|
2
|
+
import { log } from './utils.js';
|
|
3
|
+
import { formatStackSummary } from './recommender.js';
|
|
4
|
+
|
|
5
|
+
const SERVICE_TYPES = [
|
|
6
|
+
{ value: 'web_app', name: '1. Web app (SPA / SSR / static)', description: 'Single page app, server-rendered, or static site' },
|
|
7
|
+
{ value: 'api_service', name: '2. API / backend service', description: 'REST or GraphQL API without a frontend' },
|
|
8
|
+
{ value: 'full_stack', name: '3. Full-stack app (frontend + backend)', description: 'Frontend and backend in one project' },
|
|
9
|
+
{ value: 'mobile', name: '4. Mobile app (cross-platform)', description: 'React Native / Expo' },
|
|
10
|
+
{ value: 'cli_tool', name: '5. CLI tool / utility', description: 'Command-line tool' },
|
|
11
|
+
{ value: 'ai_service', name: '6. AI/ML powered service', description: 'Service with LLM or ML integration' },
|
|
12
|
+
{ value: 'desktop', name: '7. Desktop app', description: 'Tauri or Electron desktop app' },
|
|
13
|
+
{ value: 'extension', name: '8. Browser extension', description: 'Chrome / Firefox extension' },
|
|
14
|
+
{ value: 'microservice', name: '9. Microservice / serverless', description: 'Lambda, Cloud Functions, etc.' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const LANGUAGE_CHOICES = [
|
|
18
|
+
{ value: 'typescript', name: 'TypeScript' },
|
|
19
|
+
{ value: 'python', name: 'Python' },
|
|
20
|
+
{ value: 'go', name: 'Go' },
|
|
21
|
+
{ value: 'rust', name: 'Rust' },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const DEPLOYMENT_CHOICES = [
|
|
25
|
+
{ value: 'docker', name: 'Docker / Docker Compose' },
|
|
26
|
+
{ value: 'vercel', name: 'Vercel' },
|
|
27
|
+
{ value: 'aws', name: 'AWS' },
|
|
28
|
+
{ value: 'gcp', name: 'Google Cloud' },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export async function askNewMode() {
|
|
32
|
+
const mode = await select({
|
|
33
|
+
message: 'How would you like to start?',
|
|
34
|
+
choices: [
|
|
35
|
+
{ value: 'guided', name: '💬 Describe what you want to build (recommended for beginners)' },
|
|
36
|
+
{ value: 'developer', name: '⚡ I know my stack — let me pick (for developers)' },
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
return mode;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function askDescription() {
|
|
43
|
+
const description = await input({
|
|
44
|
+
message: 'Tell me about what you want to build.\n Don\'t worry about technical details — just describe it like you\'re explaining it to a friend.\n\n ',
|
|
45
|
+
});
|
|
46
|
+
return description;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function askAdjustment() {
|
|
50
|
+
const adjustment = await input({
|
|
51
|
+
message: 'What would you like to change?',
|
|
52
|
+
});
|
|
53
|
+
return adjustment;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function askGuidedConfirm() {
|
|
57
|
+
const choice = await select({
|
|
58
|
+
message: 'Sound right?',
|
|
59
|
+
choices: [
|
|
60
|
+
{ value: 'yes', name: 'Yes — create it!' },
|
|
61
|
+
{ value: 'adjust', name: 'No — let me adjust' },
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
return choice;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function askDoctorAction() {
|
|
68
|
+
const action = await select({
|
|
69
|
+
message: 'What would you like to do?',
|
|
70
|
+
choices: [
|
|
71
|
+
{ value: 'guided', name: '🚑 Fix critical issues (guided, one at a time)' },
|
|
72
|
+
{ value: 'report', name: '📋 Generate a full report (save to docs/doctor-report.md)' },
|
|
73
|
+
{ value: 'autofix', name: '⚡ Auto-fix what\'s safe to auto-fix' },
|
|
74
|
+
{ value: 'prompts', name: '📖 Just show me what to do (generates prompts I can run in Claude Code)' },
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
return action;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function askServiceType() {
|
|
81
|
+
const serviceType = await select({
|
|
82
|
+
message: 'What are you building?',
|
|
83
|
+
choices: SERVICE_TYPES,
|
|
84
|
+
});
|
|
85
|
+
return serviceType;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function askRefinements(serviceType) {
|
|
89
|
+
const refinements = {};
|
|
90
|
+
|
|
91
|
+
// Q1: Language preference — adapt choices based on service type
|
|
92
|
+
if (['api_service', 'cli_tool', 'microservice'].includes(serviceType)) {
|
|
93
|
+
refinements.language = await select({
|
|
94
|
+
message: 'Language preference?',
|
|
95
|
+
choices: LANGUAGE_CHOICES,
|
|
96
|
+
});
|
|
97
|
+
} else if (['full_stack', 'ai_service'].includes(serviceType)) {
|
|
98
|
+
refinements.language = await select({
|
|
99
|
+
message: 'Backend language?',
|
|
100
|
+
choices: [
|
|
101
|
+
{ value: 'typescript', name: 'TypeScript (same as frontend)' },
|
|
102
|
+
{ value: 'python', name: 'Python (polyglot: Next.js + Python backend)' },
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
} else if (['web_app'].includes(serviceType)) {
|
|
106
|
+
refinements.language = 'typescript';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Q2: Authentication
|
|
110
|
+
if (!['cli_tool', 'describe'].includes(serviceType)) {
|
|
111
|
+
refinements.auth = await confirm({
|
|
112
|
+
message: 'Need authentication?',
|
|
113
|
+
default: false,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Q3: Additional features (combined into one multi-select)
|
|
118
|
+
const isAI = serviceType === 'ai_service';
|
|
119
|
+
const featureChoices = [];
|
|
120
|
+
if (serviceType !== 'cli_tool') {
|
|
121
|
+
featureChoices.push({ value: 'realtime', name: 'Real-time updates (WebSockets)' });
|
|
122
|
+
featureChoices.push({ value: 'fileUploads', name: 'File uploads' });
|
|
123
|
+
}
|
|
124
|
+
featureChoices.push({ value: 'ai', name: 'AI/LLM integration', checked: isAI });
|
|
125
|
+
|
|
126
|
+
if (featureChoices.length > 0) {
|
|
127
|
+
const selected = await checkbox({
|
|
128
|
+
message: 'Additional features? (space to toggle, enter to continue)',
|
|
129
|
+
choices: featureChoices,
|
|
130
|
+
});
|
|
131
|
+
refinements.ai = selected.includes('ai');
|
|
132
|
+
refinements.fileUploads = selected.includes('fileUploads');
|
|
133
|
+
refinements.realtime = selected.includes('realtime');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Defaults: always include Claude Code, default to docker
|
|
137
|
+
refinements.deployment = 'docker';
|
|
138
|
+
refinements.claudeCode = true;
|
|
139
|
+
|
|
140
|
+
return refinements;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function confirmStack(stackConfig) {
|
|
144
|
+
const summary = formatStackSummary(stackConfig);
|
|
145
|
+
console.log('');
|
|
146
|
+
log.info('Recommended stack:');
|
|
147
|
+
console.log(summary);
|
|
148
|
+
console.log('');
|
|
149
|
+
|
|
150
|
+
const confirmed = await confirm({
|
|
151
|
+
message: 'Proceed with this stack?',
|
|
152
|
+
default: true,
|
|
153
|
+
});
|
|
154
|
+
return confirmed;
|
|
155
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export const SUPPORTED_STACKS = ['nextjs-fullstack', 'fastapi-backend', 'polyglot-fullstack'];
|
|
4
|
+
|
|
5
|
+
export function recommend(serviceType, refinements) {
|
|
6
|
+
const config = {
|
|
7
|
+
projectName: null, // set by caller
|
|
8
|
+
serviceType,
|
|
9
|
+
frontend: null,
|
|
10
|
+
backend: null,
|
|
11
|
+
database: null,
|
|
12
|
+
auth: null,
|
|
13
|
+
testing: { unit: null, e2e: null, backend: null },
|
|
14
|
+
ai: refinements.ai || false,
|
|
15
|
+
realtime: refinements.realtime || false,
|
|
16
|
+
fileUploads: refinements.fileUploads || false,
|
|
17
|
+
deployment: refinements.deployment || 'docker',
|
|
18
|
+
claudeCode: refinements.claudeCode !== false,
|
|
19
|
+
templateModules: [],
|
|
20
|
+
stackId: null,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const lang = refinements.language || 'typescript';
|
|
24
|
+
|
|
25
|
+
switch (serviceType) {
|
|
26
|
+
case 'web_app':
|
|
27
|
+
return buildNextjsFullstack(config, refinements);
|
|
28
|
+
|
|
29
|
+
case 'full_stack':
|
|
30
|
+
if (lang === 'python') {
|
|
31
|
+
return buildPolyglotFullstack(config, refinements);
|
|
32
|
+
}
|
|
33
|
+
return buildNextjsFullstack(config, refinements);
|
|
34
|
+
|
|
35
|
+
case 'api_service':
|
|
36
|
+
if (lang === 'python') {
|
|
37
|
+
return buildFastapiBackend(config, refinements);
|
|
38
|
+
}
|
|
39
|
+
return unsupported(serviceType, lang, 'FastAPI (Python)');
|
|
40
|
+
|
|
41
|
+
case 'ai_service':
|
|
42
|
+
if (lang === 'python') {
|
|
43
|
+
return buildPolyglotFullstack(config, { ...refinements, ai: true });
|
|
44
|
+
}
|
|
45
|
+
return buildPolyglotFullstack(config, { ...refinements, ai: true, language: 'python' });
|
|
46
|
+
|
|
47
|
+
case 'mobile':
|
|
48
|
+
case 'cli_tool':
|
|
49
|
+
case 'desktop':
|
|
50
|
+
case 'extension':
|
|
51
|
+
case 'microservice':
|
|
52
|
+
case 'describe':
|
|
53
|
+
return unsupported(serviceType, lang);
|
|
54
|
+
|
|
55
|
+
default:
|
|
56
|
+
return unsupported(serviceType, lang);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function buildNextjsFullstack(config, refinements) {
|
|
61
|
+
config.stackId = 'nextjs-fullstack';
|
|
62
|
+
config.frontend = { framework: 'nextjs', language: 'typescript', styling: 'tailwind', ui: 'shadcn' };
|
|
63
|
+
config.backend = { framework: 'nextjs', language: 'typescript', orm: 'prisma' };
|
|
64
|
+
config.database = { type: 'postgresql', orm: 'prisma' };
|
|
65
|
+
config.testing = { unit: 'vitest', e2e: 'playwright', backend: null };
|
|
66
|
+
|
|
67
|
+
if (refinements.auth) {
|
|
68
|
+
config.auth = 'nextauth';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
config.templateModules = [
|
|
72
|
+
{ path: 'base', prefix: '' },
|
|
73
|
+
{ path: 'frontend/nextjs', prefix: '' },
|
|
74
|
+
{ path: 'database/prisma-postgres', prefix: '' },
|
|
75
|
+
{ path: 'testing/vitest', prefix: '' },
|
|
76
|
+
{ path: 'testing/playwright', prefix: '' },
|
|
77
|
+
{ path: 'infra/docker-compose', prefix: '' },
|
|
78
|
+
{ path: 'infra/github-actions', prefix: '' },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
if (refinements.auth) {
|
|
82
|
+
config.templateModules.push({ path: 'auth/nextauth', prefix: '' });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildFastapiBackend(config, refinements) {
|
|
89
|
+
config.stackId = 'fastapi-backend';
|
|
90
|
+
config.frontend = null;
|
|
91
|
+
config.backend = { framework: 'fastapi', language: 'python', orm: 'sqlalchemy' };
|
|
92
|
+
config.database = { type: 'postgresql', orm: 'sqlalchemy' };
|
|
93
|
+
config.testing = { unit: null, e2e: null, backend: 'pytest' };
|
|
94
|
+
|
|
95
|
+
if (refinements.auth) {
|
|
96
|
+
config.auth = 'jwt-custom';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
config.templateModules = [
|
|
100
|
+
{ path: 'base', prefix: '' },
|
|
101
|
+
{ path: 'backend/fastapi', prefix: '' },
|
|
102
|
+
{ path: 'database/sqlalchemy-postgres', prefix: '' },
|
|
103
|
+
{ path: 'testing/pytest', prefix: '' },
|
|
104
|
+
{ path: 'infra/docker-compose', prefix: '' },
|
|
105
|
+
{ path: 'infra/github-actions', prefix: '' },
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
if (refinements.auth) {
|
|
109
|
+
config.templateModules.push({ path: 'auth/jwt-custom', prefix: '' });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return config;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildPolyglotFullstack(config, refinements) {
|
|
116
|
+
config.stackId = 'polyglot-fullstack';
|
|
117
|
+
config.frontend = { framework: 'nextjs', language: 'typescript', styling: 'tailwind', ui: 'shadcn' };
|
|
118
|
+
config.backend = { framework: 'fastapi', language: 'python', orm: 'sqlalchemy' };
|
|
119
|
+
config.database = { type: 'postgresql', orm: 'both' };
|
|
120
|
+
config.testing = { unit: 'vitest', e2e: 'playwright', backend: 'pytest' };
|
|
121
|
+
config.ai = refinements.ai || false;
|
|
122
|
+
|
|
123
|
+
if (refinements.auth) {
|
|
124
|
+
config.auth = 'both';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
config.templateModules = [
|
|
128
|
+
{ path: 'base', prefix: '' },
|
|
129
|
+
{ path: 'frontend/nextjs', prefix: 'frontend' },
|
|
130
|
+
{ path: 'backend/fastapi', prefix: '' },
|
|
131
|
+
{ path: 'database/prisma-postgres', prefix: 'frontend' },
|
|
132
|
+
{ path: 'database/sqlalchemy-postgres', prefix: '' },
|
|
133
|
+
{ path: 'testing/vitest', prefix: 'frontend' },
|
|
134
|
+
{ path: 'testing/playwright', prefix: '' },
|
|
135
|
+
{ path: 'testing/pytest', prefix: '' },
|
|
136
|
+
{ path: 'infra/docker-compose', prefix: '' },
|
|
137
|
+
{ path: 'infra/github-actions', prefix: '' },
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
if (refinements.auth) {
|
|
141
|
+
config.templateModules.push({ path: 'auth/nextauth', prefix: 'frontend' });
|
|
142
|
+
config.templateModules.push({ path: 'auth/jwt-custom', prefix: '' });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return config;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function unsupported(serviceType, lang, suggestion) {
|
|
149
|
+
const msg = suggestion
|
|
150
|
+
? `"${serviceType}" with "${lang}" is not yet supported. Try: ${suggestion}`
|
|
151
|
+
: `"${serviceType}" is not yet supported in V1. Supported: web_app, api_service (Python), full_stack, ai_service`;
|
|
152
|
+
return { supported: false, message: msg };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function formatStackSummary(config) {
|
|
156
|
+
if (config.supported === false) {
|
|
157
|
+
return chalk.yellow(config.message);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const lines = [];
|
|
161
|
+
|
|
162
|
+
if (config.frontend) {
|
|
163
|
+
lines.push(` ${chalk.bold('Frontend:')} ${config.frontend.framework} + ${config.frontend.language} + ${config.frontend.styling} + ${config.frontend.ui}`);
|
|
164
|
+
}
|
|
165
|
+
if (config.backend) {
|
|
166
|
+
lines.push(` ${chalk.bold('Backend:')} ${config.backend.framework} + ${config.backend.language} + ${config.backend.orm}`);
|
|
167
|
+
}
|
|
168
|
+
if (config.database) {
|
|
169
|
+
lines.push(` ${chalk.bold('Database:')} ${config.database.type} (${config.database.orm})`);
|
|
170
|
+
}
|
|
171
|
+
if (config.auth) {
|
|
172
|
+
lines.push(` ${chalk.bold('Auth:')} ${config.auth}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const testParts = [config.testing.unit, config.testing.e2e, config.testing.backend].filter(Boolean);
|
|
176
|
+
if (testParts.length) {
|
|
177
|
+
lines.push(` ${chalk.bold('Testing:')} ${testParts.join(' + ')}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
lines.push(` ${chalk.bold('Deploy:')} ${config.deployment}`);
|
|
181
|
+
|
|
182
|
+
if (config.ai) lines.push(` ${chalk.bold('AI:')} enabled`);
|
|
183
|
+
if (config.claudeCode) lines.push(` ${chalk.bold('Claude:')} CLAUDE.md + hooks + skills + agents + commands`);
|
|
184
|
+
|
|
185
|
+
return lines.join('\n');
|
|
186
|
+
}
|