@su-record/vibe 1.0.5 → 1.0.6
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/bin/vibe +244 -11
- package/package.json +1 -1
package/bin/vibe
CHANGED
|
@@ -78,9 +78,10 @@ function copyDirRecursive(sourceDir, targetDir) {
|
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// 기술 스택 감지 (루트 + 1레벨 하위 폴더)
|
|
81
|
+
// 기술 스택 감지 (루트 + 1레벨 하위 폴더) - 실제 의존성 기반
|
|
82
82
|
function detectTechStacks(projectRoot) {
|
|
83
83
|
const stacks = [];
|
|
84
|
+
const details = { databases: [], stateManagement: [], hosting: [], cicd: [] };
|
|
84
85
|
|
|
85
86
|
const detectInDir = (dir, prefix = '') => {
|
|
86
87
|
const detected = [];
|
|
@@ -91,12 +92,35 @@ function detectTechStacks(projectRoot) {
|
|
|
91
92
|
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
|
|
92
93
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
93
94
|
|
|
95
|
+
// 프레임워크 감지
|
|
94
96
|
if (deps['next']) detected.push({ type: 'typescript-nextjs', path: prefix });
|
|
95
97
|
else if (deps['react-native']) detected.push({ type: 'typescript-react-native', path: prefix });
|
|
96
98
|
else if (deps['react']) detected.push({ type: 'typescript-react', path: prefix });
|
|
97
99
|
else if (deps['vue']) detected.push({ type: 'typescript-vue', path: prefix });
|
|
98
100
|
else if (deps['express'] || deps['fastify'] || deps['koa']) detected.push({ type: 'typescript-node', path: prefix });
|
|
99
101
|
else if (pkg.name) detected.push({ type: 'typescript-node', path: prefix });
|
|
102
|
+
|
|
103
|
+
// DB 감지
|
|
104
|
+
if (deps['pg'] || deps['postgres'] || deps['@prisma/client']) details.databases.push('PostgreSQL');
|
|
105
|
+
if (deps['mysql'] || deps['mysql2']) details.databases.push('MySQL');
|
|
106
|
+
if (deps['mongodb'] || deps['mongoose']) details.databases.push('MongoDB');
|
|
107
|
+
if (deps['redis'] || deps['ioredis']) details.databases.push('Redis');
|
|
108
|
+
if (deps['sqlite3'] || deps['better-sqlite3']) details.databases.push('SQLite');
|
|
109
|
+
if (deps['typeorm']) details.databases.push('TypeORM');
|
|
110
|
+
if (deps['prisma'] || deps['@prisma/client']) details.databases.push('Prisma');
|
|
111
|
+
if (deps['drizzle-orm']) details.databases.push('Drizzle');
|
|
112
|
+
if (deps['sequelize']) details.databases.push('Sequelize');
|
|
113
|
+
|
|
114
|
+
// 상태관리 감지
|
|
115
|
+
if (deps['redux'] || deps['@reduxjs/toolkit']) details.stateManagement.push('Redux');
|
|
116
|
+
if (deps['zustand']) details.stateManagement.push('Zustand');
|
|
117
|
+
if (deps['jotai']) details.stateManagement.push('Jotai');
|
|
118
|
+
if (deps['recoil']) details.stateManagement.push('Recoil');
|
|
119
|
+
if (deps['mobx']) details.stateManagement.push('MobX');
|
|
120
|
+
if (deps['@tanstack/react-query'] || deps['react-query']) details.stateManagement.push('React Query');
|
|
121
|
+
if (deps['swr']) details.stateManagement.push('SWR');
|
|
122
|
+
if (deps['pinia']) details.stateManagement.push('Pinia');
|
|
123
|
+
if (deps['vuex']) details.stateManagement.push('Vuex');
|
|
100
124
|
} catch (e) {}
|
|
101
125
|
}
|
|
102
126
|
|
|
@@ -107,6 +131,12 @@ function detectTechStacks(projectRoot) {
|
|
|
107
131
|
if (content.includes('fastapi')) detected.push({ type: 'python-fastapi', path: prefix });
|
|
108
132
|
else if (content.includes('django')) detected.push({ type: 'python-django', path: prefix });
|
|
109
133
|
else detected.push({ type: 'python', path: prefix });
|
|
134
|
+
|
|
135
|
+
// Python DB
|
|
136
|
+
if (content.includes('psycopg') || content.includes('asyncpg')) details.databases.push('PostgreSQL');
|
|
137
|
+
if (content.includes('pymongo')) details.databases.push('MongoDB');
|
|
138
|
+
if (content.includes('sqlalchemy')) details.databases.push('SQLAlchemy');
|
|
139
|
+
if (content.includes('prisma')) details.databases.push('Prisma');
|
|
110
140
|
} catch (e) {}
|
|
111
141
|
} else if (fs.existsSync(path.join(dir, 'requirements.txt'))) {
|
|
112
142
|
try {
|
|
@@ -114,22 +144,44 @@ function detectTechStacks(projectRoot) {
|
|
|
114
144
|
if (content.includes('fastapi')) detected.push({ type: 'python-fastapi', path: prefix });
|
|
115
145
|
else if (content.includes('django')) detected.push({ type: 'python-django', path: prefix });
|
|
116
146
|
else detected.push({ type: 'python', path: prefix });
|
|
147
|
+
|
|
148
|
+
if (content.includes('psycopg') || content.includes('asyncpg')) details.databases.push('PostgreSQL');
|
|
149
|
+
if (content.includes('pymongo')) details.databases.push('MongoDB');
|
|
150
|
+
if (content.includes('sqlalchemy')) details.databases.push('SQLAlchemy');
|
|
117
151
|
} catch (e) {}
|
|
118
152
|
}
|
|
119
153
|
|
|
120
154
|
// Flutter / Dart
|
|
121
155
|
if (fs.existsSync(path.join(dir, 'pubspec.yaml'))) {
|
|
122
156
|
detected.push({ type: 'dart-flutter', path: prefix });
|
|
157
|
+
try {
|
|
158
|
+
const content = fs.readFileSync(path.join(dir, 'pubspec.yaml'), 'utf-8');
|
|
159
|
+
if (content.includes('flutter_riverpod') || content.includes('riverpod')) details.stateManagement.push('Riverpod');
|
|
160
|
+
else if (content.includes('provider')) details.stateManagement.push('Provider');
|
|
161
|
+
if (content.includes('bloc')) details.stateManagement.push('BLoC');
|
|
162
|
+
if (content.includes('getx') || content.includes('get:')) details.stateManagement.push('GetX');
|
|
163
|
+
} catch (e) {}
|
|
123
164
|
}
|
|
124
165
|
|
|
125
166
|
// Go
|
|
126
167
|
if (fs.existsSync(path.join(dir, 'go.mod'))) {
|
|
127
168
|
detected.push({ type: 'go', path: prefix });
|
|
169
|
+
try {
|
|
170
|
+
const content = fs.readFileSync(path.join(dir, 'go.mod'), 'utf-8');
|
|
171
|
+
if (content.includes('pgx') || content.includes('pq')) details.databases.push('PostgreSQL');
|
|
172
|
+
if (content.includes('go-redis')) details.databases.push('Redis');
|
|
173
|
+
if (content.includes('mongo-driver')) details.databases.push('MongoDB');
|
|
174
|
+
} catch (e) {}
|
|
128
175
|
}
|
|
129
176
|
|
|
130
177
|
// Rust
|
|
131
178
|
if (fs.existsSync(path.join(dir, 'Cargo.toml'))) {
|
|
132
179
|
detected.push({ type: 'rust', path: prefix });
|
|
180
|
+
try {
|
|
181
|
+
const content = fs.readFileSync(path.join(dir, 'Cargo.toml'), 'utf-8');
|
|
182
|
+
if (content.includes('sqlx') || content.includes('diesel')) details.databases.push('PostgreSQL');
|
|
183
|
+
if (content.includes('mongodb')) details.databases.push('MongoDB');
|
|
184
|
+
} catch (e) {}
|
|
133
185
|
}
|
|
134
186
|
|
|
135
187
|
// Java / Kotlin
|
|
@@ -143,12 +195,19 @@ function detectTechStacks(projectRoot) {
|
|
|
143
195
|
else if (content.includes('kotlin')) detected.push({ type: 'kotlin', path: prefix });
|
|
144
196
|
else if (content.includes('spring')) detected.push({ type: 'java-spring', path: prefix });
|
|
145
197
|
else detected.push({ type: 'java', path: prefix });
|
|
198
|
+
|
|
199
|
+
if (content.includes('postgresql')) details.databases.push('PostgreSQL');
|
|
200
|
+
if (content.includes('mysql')) details.databases.push('MySQL');
|
|
201
|
+
if (content.includes('jpa') || content.includes('hibernate')) details.databases.push('JPA/Hibernate');
|
|
146
202
|
} catch (e) {}
|
|
147
203
|
} else if (fs.existsSync(path.join(dir, 'pom.xml'))) {
|
|
148
204
|
try {
|
|
149
205
|
const content = fs.readFileSync(path.join(dir, 'pom.xml'), 'utf-8');
|
|
150
206
|
if (content.includes('spring')) detected.push({ type: 'java-spring', path: prefix });
|
|
151
207
|
else detected.push({ type: 'java', path: prefix });
|
|
208
|
+
|
|
209
|
+
if (content.includes('postgresql')) details.databases.push('PostgreSQL');
|
|
210
|
+
if (content.includes('mysql')) details.databases.push('MySQL');
|
|
152
211
|
} catch (e) {}
|
|
153
212
|
}
|
|
154
213
|
|
|
@@ -161,6 +220,43 @@ function detectTechStacks(projectRoot) {
|
|
|
161
220
|
return detected;
|
|
162
221
|
};
|
|
163
222
|
|
|
223
|
+
// CI/CD 감지
|
|
224
|
+
if (fs.existsSync(path.join(projectRoot, '.github', 'workflows'))) {
|
|
225
|
+
details.cicd.push('GitHub Actions');
|
|
226
|
+
}
|
|
227
|
+
if (fs.existsSync(path.join(projectRoot, '.gitlab-ci.yml'))) {
|
|
228
|
+
details.cicd.push('GitLab CI');
|
|
229
|
+
}
|
|
230
|
+
if (fs.existsSync(path.join(projectRoot, 'Jenkinsfile'))) {
|
|
231
|
+
details.cicd.push('Jenkins');
|
|
232
|
+
}
|
|
233
|
+
if (fs.existsSync(path.join(projectRoot, '.circleci'))) {
|
|
234
|
+
details.cicd.push('CircleCI');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Hosting 감지 (설정 파일 기반)
|
|
238
|
+
if (fs.existsSync(path.join(projectRoot, 'vercel.json')) ||
|
|
239
|
+
fs.existsSync(path.join(projectRoot, '.vercel'))) {
|
|
240
|
+
details.hosting.push('Vercel');
|
|
241
|
+
}
|
|
242
|
+
if (fs.existsSync(path.join(projectRoot, 'netlify.toml'))) {
|
|
243
|
+
details.hosting.push('Netlify');
|
|
244
|
+
}
|
|
245
|
+
if (fs.existsSync(path.join(projectRoot, 'app.yaml')) ||
|
|
246
|
+
fs.existsSync(path.join(projectRoot, 'cloudbuild.yaml'))) {
|
|
247
|
+
details.hosting.push('Google Cloud');
|
|
248
|
+
}
|
|
249
|
+
if (fs.existsSync(path.join(projectRoot, 'Dockerfile')) ||
|
|
250
|
+
fs.existsSync(path.join(projectRoot, 'docker-compose.yml'))) {
|
|
251
|
+
details.hosting.push('Docker');
|
|
252
|
+
}
|
|
253
|
+
if (fs.existsSync(path.join(projectRoot, 'fly.toml'))) {
|
|
254
|
+
details.hosting.push('Fly.io');
|
|
255
|
+
}
|
|
256
|
+
if (fs.existsSync(path.join(projectRoot, 'railway.json'))) {
|
|
257
|
+
details.hosting.push('Railway');
|
|
258
|
+
}
|
|
259
|
+
|
|
164
260
|
// 루트 디렉토리 검사
|
|
165
261
|
stacks.push(...detectInDir(projectRoot));
|
|
166
262
|
|
|
@@ -187,7 +283,13 @@ function detectTechStacks(projectRoot) {
|
|
|
187
283
|
}
|
|
188
284
|
}
|
|
189
285
|
|
|
190
|
-
|
|
286
|
+
// 중복 제거
|
|
287
|
+
details.databases = [...new Set(details.databases)];
|
|
288
|
+
details.stateManagement = [...new Set(details.stateManagement)];
|
|
289
|
+
details.hosting = [...new Set(details.hosting)];
|
|
290
|
+
details.cicd = [...new Set(details.cicd)];
|
|
291
|
+
|
|
292
|
+
return { stacks, details };
|
|
191
293
|
}
|
|
192
294
|
|
|
193
295
|
// 협업자 자동 설치 설정
|
|
@@ -352,16 +454,22 @@ async function init(projectName) {
|
|
|
352
454
|
copyDirContents(sourceDir, commandsDir);
|
|
353
455
|
log(' ✅ 슬래시 커맨드 설치 완료 (7개)\n');
|
|
354
456
|
|
|
355
|
-
// 기술 스택 감지
|
|
356
|
-
const detectedStacks = detectTechStacks(projectRoot);
|
|
457
|
+
// 기술 스택 감지 (실제 의존성 기반)
|
|
458
|
+
const { stacks: detectedStacks, details: stackDetails } = detectTechStacks(projectRoot);
|
|
357
459
|
if (detectedStacks.length > 0) {
|
|
358
460
|
log(` 🔍 감지된 기술 스택:\n`);
|
|
359
461
|
detectedStacks.forEach(s => {
|
|
360
462
|
log(` - ${s.type}${s.path ? ` (${s.path}/)` : ''}\n`);
|
|
361
463
|
});
|
|
464
|
+
if (stackDetails.databases.length > 0) {
|
|
465
|
+
log(` - DB: ${stackDetails.databases.join(', ')}\n`);
|
|
466
|
+
}
|
|
467
|
+
if (stackDetails.stateManagement.length > 0) {
|
|
468
|
+
log(` - State: ${stackDetails.stateManagement.join(', ')}\n`);
|
|
469
|
+
}
|
|
362
470
|
}
|
|
363
471
|
|
|
364
|
-
// constitution.md 생성 (감지된 스택으로 플레이스홀더 업데이트)
|
|
472
|
+
// constitution.md 생성 (실제 감지된 스택으로 플레이스홀더 업데이트)
|
|
365
473
|
const templatePath = path.join(__dirname, '../templates/constitution-template.md');
|
|
366
474
|
const constitutionPath = path.join(vibeDir, 'constitution.md');
|
|
367
475
|
if (fs.existsSync(templatePath)) {
|
|
@@ -377,7 +485,7 @@ async function init(projectName) {
|
|
|
377
485
|
s.type.includes('flutter') || s.type.includes('swift') || s.type.includes('android')
|
|
378
486
|
);
|
|
379
487
|
|
|
380
|
-
// 스택 이름 매핑
|
|
488
|
+
// 스택 이름 매핑 (기본값)
|
|
381
489
|
const stackNames = {
|
|
382
490
|
'python-fastapi': { lang: 'Python 3.11+', framework: 'FastAPI' },
|
|
383
491
|
'python-django': { lang: 'Python 3.11+', framework: 'Django' },
|
|
@@ -395,7 +503,7 @@ async function init(projectName) {
|
|
|
395
503
|
'swift-ios': { lang: 'Swift', framework: 'iOS/SwiftUI' }
|
|
396
504
|
};
|
|
397
505
|
|
|
398
|
-
// 플레이스홀더 업데이트
|
|
506
|
+
// 플레이스홀더 업데이트 - Backend
|
|
399
507
|
if (backendStack && stackNames[backendStack.type]) {
|
|
400
508
|
const info = stackNames[backendStack.type];
|
|
401
509
|
constitution = constitution.replace(
|
|
@@ -408,6 +516,7 @@ async function init(projectName) {
|
|
|
408
516
|
);
|
|
409
517
|
}
|
|
410
518
|
|
|
519
|
+
// 플레이스홀더 업데이트 - Frontend
|
|
411
520
|
if (frontendStack && stackNames[frontendStack.type]) {
|
|
412
521
|
const info = stackNames[frontendStack.type];
|
|
413
522
|
constitution = constitution.replace(
|
|
@@ -416,6 +525,58 @@ async function init(projectName) {
|
|
|
416
525
|
);
|
|
417
526
|
}
|
|
418
527
|
|
|
528
|
+
// 실제 감지된 DB로 플레이스홀더 업데이트
|
|
529
|
+
if (stackDetails.databases.length > 0) {
|
|
530
|
+
constitution = constitution.replace(
|
|
531
|
+
'- Database: {PostgreSQL / MongoDB / etc.}',
|
|
532
|
+
`- Database: ${stackDetails.databases.join(', ')}`
|
|
533
|
+
);
|
|
534
|
+
} else {
|
|
535
|
+
constitution = constitution.replace(
|
|
536
|
+
'- Database: {PostgreSQL / MongoDB / etc.}',
|
|
537
|
+
'- Database: (프로젝트에 맞게 설정)'
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// 실제 감지된 상태관리로 플레이스홀더 업데이트
|
|
542
|
+
if (stackDetails.stateManagement.length > 0) {
|
|
543
|
+
constitution = constitution.replace(
|
|
544
|
+
'- State Management: {Provider / Redux / etc.}',
|
|
545
|
+
`- State Management: ${stackDetails.stateManagement.join(', ')}`
|
|
546
|
+
);
|
|
547
|
+
} else {
|
|
548
|
+
constitution = constitution.replace(
|
|
549
|
+
'- State Management: {Provider / Redux / etc.}',
|
|
550
|
+
'- State Management: (프로젝트에 맞게 설정)'
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 실제 감지된 Hosting으로 플레이스홀더 업데이트
|
|
555
|
+
if (stackDetails.hosting.length > 0) {
|
|
556
|
+
constitution = constitution.replace(
|
|
557
|
+
'- Hosting: {Cloud Run / Vercel / etc.}',
|
|
558
|
+
`- Hosting: ${stackDetails.hosting.join(', ')}`
|
|
559
|
+
);
|
|
560
|
+
} else {
|
|
561
|
+
constitution = constitution.replace(
|
|
562
|
+
'- Hosting: {Cloud Run / Vercel / etc.}',
|
|
563
|
+
'- Hosting: (프로젝트에 맞게 설정)'
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 실제 감지된 CI/CD로 플레이스홀더 업데이트
|
|
568
|
+
if (stackDetails.cicd.length > 0) {
|
|
569
|
+
constitution = constitution.replace(
|
|
570
|
+
'- CI/CD: {GitHub Actions / etc.}',
|
|
571
|
+
`- CI/CD: ${stackDetails.cicd.join(', ')}`
|
|
572
|
+
);
|
|
573
|
+
} else {
|
|
574
|
+
constitution = constitution.replace(
|
|
575
|
+
'- CI/CD: {GitHub Actions / etc.}',
|
|
576
|
+
'- CI/CD: (프로젝트에 맞게 설정)'
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
419
580
|
// 날짜 업데이트
|
|
420
581
|
const today = new Date().toISOString().split('T')[0];
|
|
421
582
|
constitution = constitution.replace('{날짜}', today);
|
|
@@ -428,7 +589,8 @@ async function init(projectName) {
|
|
|
428
589
|
const config = {
|
|
429
590
|
language: 'ko',
|
|
430
591
|
quality: { strict: true, autoVerify: true },
|
|
431
|
-
stacks: detectedStacks
|
|
592
|
+
stacks: detectedStacks,
|
|
593
|
+
details: stackDetails
|
|
432
594
|
};
|
|
433
595
|
fs.writeFileSync(path.join(vibeDir, 'config.json'), JSON.stringify(config, null, 2));
|
|
434
596
|
|
|
@@ -704,19 +866,90 @@ async function update() {
|
|
|
704
866
|
copyDirContents(sourceDir, commandsDir);
|
|
705
867
|
log(' ✅ 슬래시 커맨드 업데이트 완료 (7개)\n');
|
|
706
868
|
|
|
707
|
-
// 기술 스택 감지 (update에서도)
|
|
708
|
-
const detectedStacks = detectTechStacks(projectRoot);
|
|
869
|
+
// 기술 스택 감지 (update에서도 실제 의존성 기반)
|
|
870
|
+
const { stacks: detectedStacks, details: stackDetails } = detectTechStacks(projectRoot);
|
|
709
871
|
|
|
710
|
-
// config.json 업데이트 (stacks 정보 추가)
|
|
872
|
+
// config.json 업데이트 (stacks + details 정보 추가)
|
|
711
873
|
const configPath = path.join(vibeDir, 'config.json');
|
|
712
874
|
if (fs.existsSync(configPath)) {
|
|
713
875
|
try {
|
|
714
876
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
715
877
|
config.stacks = detectedStacks;
|
|
878
|
+
config.details = stackDetails;
|
|
716
879
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
717
880
|
} catch (e) {}
|
|
718
881
|
}
|
|
719
882
|
|
|
883
|
+
// constitution.md 업데이트 (실제 감지된 스택으로)
|
|
884
|
+
const templatePath = path.join(__dirname, '../templates/constitution-template.md');
|
|
885
|
+
const constitutionPath = path.join(vibeDir, 'constitution.md');
|
|
886
|
+
if (fs.existsSync(templatePath)) {
|
|
887
|
+
let constitution = fs.readFileSync(templatePath, 'utf-8');
|
|
888
|
+
|
|
889
|
+
const backendStack = detectedStacks.find(s =>
|
|
890
|
+
s.type.includes('python') || s.type.includes('node') ||
|
|
891
|
+
s.type.includes('go') || s.type.includes('java') || s.type.includes('rust')
|
|
892
|
+
);
|
|
893
|
+
const frontendStack = detectedStacks.find(s =>
|
|
894
|
+
s.type.includes('react') || s.type.includes('vue') ||
|
|
895
|
+
s.type.includes('flutter') || s.type.includes('swift') || s.type.includes('android')
|
|
896
|
+
);
|
|
897
|
+
|
|
898
|
+
const stackNames = {
|
|
899
|
+
'python-fastapi': { lang: 'Python 3.11+', framework: 'FastAPI' },
|
|
900
|
+
'python-django': { lang: 'Python 3.11+', framework: 'Django' },
|
|
901
|
+
'python': { lang: 'Python 3.11+', framework: '-' },
|
|
902
|
+
'typescript-node': { lang: 'TypeScript/Node.js', framework: 'Express/Fastify' },
|
|
903
|
+
'typescript-nextjs': { lang: 'TypeScript', framework: 'Next.js' },
|
|
904
|
+
'typescript-react': { lang: 'TypeScript', framework: 'React' },
|
|
905
|
+
'typescript-vue': { lang: 'TypeScript', framework: 'Vue.js' },
|
|
906
|
+
'typescript-react-native': { lang: 'TypeScript', framework: 'React Native' },
|
|
907
|
+
'dart-flutter': { lang: 'Dart', framework: 'Flutter' },
|
|
908
|
+
'go': { lang: 'Go', framework: '-' },
|
|
909
|
+
'rust': { lang: 'Rust', framework: '-' },
|
|
910
|
+
'java-spring': { lang: 'Java 17+', framework: 'Spring Boot' },
|
|
911
|
+
'kotlin-android': { lang: 'Kotlin', framework: 'Android' },
|
|
912
|
+
'swift-ios': { lang: 'Swift', framework: 'iOS/SwiftUI' }
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
if (backendStack && stackNames[backendStack.type]) {
|
|
916
|
+
const info = stackNames[backendStack.type];
|
|
917
|
+
constitution = constitution.replace('- Language: {Python 3.11+ / Node.js / etc.}', `- Language: ${info.lang}`);
|
|
918
|
+
constitution = constitution.replace('- Framework: {FastAPI / Express / etc.}', `- Framework: ${info.framework}`);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (frontendStack && stackNames[frontendStack.type]) {
|
|
922
|
+
const info = stackNames[frontendStack.type];
|
|
923
|
+
constitution = constitution.replace('- Framework: {Flutter / React / etc.}', `- Framework: ${info.framework}`);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// 실제 감지된 값으로 업데이트
|
|
927
|
+
constitution = constitution.replace(
|
|
928
|
+
'- Database: {PostgreSQL / MongoDB / etc.}',
|
|
929
|
+
stackDetails.databases.length > 0 ? `- Database: ${stackDetails.databases.join(', ')}` : '- Database: (프로젝트에 맞게 설정)'
|
|
930
|
+
);
|
|
931
|
+
constitution = constitution.replace(
|
|
932
|
+
'- State Management: {Provider / Redux / etc.}',
|
|
933
|
+
stackDetails.stateManagement.length > 0 ? `- State Management: ${stackDetails.stateManagement.join(', ')}` : '- State Management: (프로젝트에 맞게 설정)'
|
|
934
|
+
);
|
|
935
|
+
constitution = constitution.replace(
|
|
936
|
+
'- Hosting: {Cloud Run / Vercel / etc.}',
|
|
937
|
+
stackDetails.hosting.length > 0 ? `- Hosting: ${stackDetails.hosting.join(', ')}` : '- Hosting: (프로젝트에 맞게 설정)'
|
|
938
|
+
);
|
|
939
|
+
constitution = constitution.replace(
|
|
940
|
+
'- CI/CD: {GitHub Actions / etc.}',
|
|
941
|
+
stackDetails.cicd.length > 0 ? `- CI/CD: ${stackDetails.cicd.join(', ')}` : '- CI/CD: (프로젝트에 맞게 설정)'
|
|
942
|
+
);
|
|
943
|
+
|
|
944
|
+
const today = new Date().toISOString().split('T')[0];
|
|
945
|
+
constitution = constitution.replace('{날짜}', today);
|
|
946
|
+
constitution = constitution.replace('{이름}', 'vibe update');
|
|
947
|
+
constitution = constitution.replace('토큰 유효 기간: {기간}', '토큰 유효 기간: 1시간');
|
|
948
|
+
|
|
949
|
+
fs.writeFileSync(constitutionPath, constitution);
|
|
950
|
+
log(' ✅ constitution.md 업데이트 완료\n');
|
|
951
|
+
}
|
|
952
|
+
|
|
720
953
|
// .vibe/rules/ 업데이트 (감지된 스택에 해당하는 언어 규칙만)
|
|
721
954
|
const rulesSource = path.join(__dirname, '../.vibe/rules');
|
|
722
955
|
const rulesTarget = path.join(vibeDir, 'rules');
|