@su-record/vibe 1.0.5 → 1.0.7
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 +238 -16
- package/package.json +1 -1
- package/templates/constitution-template.md +0 -9
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,11 +525,57 @@ async function init(projectName) {
|
|
|
416
525
|
);
|
|
417
526
|
}
|
|
418
527
|
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
+
}
|
|
424
579
|
|
|
425
580
|
fs.writeFileSync(constitutionPath, constitution);
|
|
426
581
|
}
|
|
@@ -428,7 +583,8 @@ async function init(projectName) {
|
|
|
428
583
|
const config = {
|
|
429
584
|
language: 'ko',
|
|
430
585
|
quality: { strict: true, autoVerify: true },
|
|
431
|
-
stacks: detectedStacks
|
|
586
|
+
stacks: detectedStacks,
|
|
587
|
+
details: stackDetails
|
|
432
588
|
};
|
|
433
589
|
fs.writeFileSync(path.join(vibeDir, 'config.json'), JSON.stringify(config, null, 2));
|
|
434
590
|
|
|
@@ -704,19 +860,85 @@ async function update() {
|
|
|
704
860
|
copyDirContents(sourceDir, commandsDir);
|
|
705
861
|
log(' ✅ 슬래시 커맨드 업데이트 완료 (7개)\n');
|
|
706
862
|
|
|
707
|
-
// 기술 스택 감지 (update에서도)
|
|
708
|
-
const detectedStacks = detectTechStacks(projectRoot);
|
|
863
|
+
// 기술 스택 감지 (update에서도 실제 의존성 기반)
|
|
864
|
+
const { stacks: detectedStacks, details: stackDetails } = detectTechStacks(projectRoot);
|
|
709
865
|
|
|
710
|
-
// config.json 업데이트 (stacks 정보 추가)
|
|
866
|
+
// config.json 업데이트 (stacks + details 정보 추가)
|
|
711
867
|
const configPath = path.join(vibeDir, 'config.json');
|
|
712
868
|
if (fs.existsSync(configPath)) {
|
|
713
869
|
try {
|
|
714
870
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
715
871
|
config.stacks = detectedStacks;
|
|
872
|
+
config.details = stackDetails;
|
|
716
873
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
717
874
|
} catch (e) {}
|
|
718
875
|
}
|
|
719
876
|
|
|
877
|
+
// constitution.md 업데이트 (실제 감지된 스택으로)
|
|
878
|
+
const templatePath = path.join(__dirname, '../templates/constitution-template.md');
|
|
879
|
+
const constitutionPath = path.join(vibeDir, 'constitution.md');
|
|
880
|
+
if (fs.existsSync(templatePath)) {
|
|
881
|
+
let constitution = fs.readFileSync(templatePath, 'utf-8');
|
|
882
|
+
|
|
883
|
+
const backendStack = detectedStacks.find(s =>
|
|
884
|
+
s.type.includes('python') || s.type.includes('node') ||
|
|
885
|
+
s.type.includes('go') || s.type.includes('java') || s.type.includes('rust')
|
|
886
|
+
);
|
|
887
|
+
const frontendStack = detectedStacks.find(s =>
|
|
888
|
+
s.type.includes('react') || s.type.includes('vue') ||
|
|
889
|
+
s.type.includes('flutter') || s.type.includes('swift') || s.type.includes('android')
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
const stackNames = {
|
|
893
|
+
'python-fastapi': { lang: 'Python 3.11+', framework: 'FastAPI' },
|
|
894
|
+
'python-django': { lang: 'Python 3.11+', framework: 'Django' },
|
|
895
|
+
'python': { lang: 'Python 3.11+', framework: '-' },
|
|
896
|
+
'typescript-node': { lang: 'TypeScript/Node.js', framework: 'Express/Fastify' },
|
|
897
|
+
'typescript-nextjs': { lang: 'TypeScript', framework: 'Next.js' },
|
|
898
|
+
'typescript-react': { lang: 'TypeScript', framework: 'React' },
|
|
899
|
+
'typescript-vue': { lang: 'TypeScript', framework: 'Vue.js' },
|
|
900
|
+
'typescript-react-native': { lang: 'TypeScript', framework: 'React Native' },
|
|
901
|
+
'dart-flutter': { lang: 'Dart', framework: 'Flutter' },
|
|
902
|
+
'go': { lang: 'Go', framework: '-' },
|
|
903
|
+
'rust': { lang: 'Rust', framework: '-' },
|
|
904
|
+
'java-spring': { lang: 'Java 17+', framework: 'Spring Boot' },
|
|
905
|
+
'kotlin-android': { lang: 'Kotlin', framework: 'Android' },
|
|
906
|
+
'swift-ios': { lang: 'Swift', framework: 'iOS/SwiftUI' }
|
|
907
|
+
};
|
|
908
|
+
|
|
909
|
+
if (backendStack && stackNames[backendStack.type]) {
|
|
910
|
+
const info = stackNames[backendStack.type];
|
|
911
|
+
constitution = constitution.replace('- Language: {Python 3.11+ / Node.js / etc.}', `- Language: ${info.lang}`);
|
|
912
|
+
constitution = constitution.replace('- Framework: {FastAPI / Express / etc.}', `- Framework: ${info.framework}`);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (frontendStack && stackNames[frontendStack.type]) {
|
|
916
|
+
const info = stackNames[frontendStack.type];
|
|
917
|
+
constitution = constitution.replace('- Framework: {Flutter / React / etc.}', `- Framework: ${info.framework}`);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// 실제 감지된 값으로 업데이트
|
|
921
|
+
constitution = constitution.replace(
|
|
922
|
+
'- Database: {PostgreSQL / MongoDB / etc.}',
|
|
923
|
+
stackDetails.databases.length > 0 ? `- Database: ${stackDetails.databases.join(', ')}` : '- Database: (프로젝트에 맞게 설정)'
|
|
924
|
+
);
|
|
925
|
+
constitution = constitution.replace(
|
|
926
|
+
'- State Management: {Provider / Redux / etc.}',
|
|
927
|
+
stackDetails.stateManagement.length > 0 ? `- State Management: ${stackDetails.stateManagement.join(', ')}` : '- State Management: (프로젝트에 맞게 설정)'
|
|
928
|
+
);
|
|
929
|
+
constitution = constitution.replace(
|
|
930
|
+
'- Hosting: {Cloud Run / Vercel / etc.}',
|
|
931
|
+
stackDetails.hosting.length > 0 ? `- Hosting: ${stackDetails.hosting.join(', ')}` : '- Hosting: (프로젝트에 맞게 설정)'
|
|
932
|
+
);
|
|
933
|
+
constitution = constitution.replace(
|
|
934
|
+
'- CI/CD: {GitHub Actions / etc.}',
|
|
935
|
+
stackDetails.cicd.length > 0 ? `- CI/CD: ${stackDetails.cicd.join(', ')}` : '- CI/CD: (프로젝트에 맞게 설정)'
|
|
936
|
+
);
|
|
937
|
+
|
|
938
|
+
fs.writeFileSync(constitutionPath, constitution);
|
|
939
|
+
log(' ✅ constitution.md 업데이트 완료\n');
|
|
940
|
+
}
|
|
941
|
+
|
|
720
942
|
// .vibe/rules/ 업데이트 (감지된 스택에 해당하는 언어 규칙만)
|
|
721
943
|
const rulesSource = path.join(__dirname, '../.vibe/rules');
|
|
722
944
|
const rulesTarget = path.join(vibeDir, 'rules');
|
package/package.json
CHANGED
|
@@ -159,7 +159,6 @@ chore: 빌드, 설정 변경
|
|
|
159
159
|
|
|
160
160
|
### 인증
|
|
161
161
|
- JWT 기반 인증
|
|
162
|
-
- 토큰 유효 기간: {기간}
|
|
163
162
|
- Refresh 토큰 사용
|
|
164
163
|
|
|
165
164
|
### 권한
|
|
@@ -183,11 +182,3 @@ chore: 빌드, 설정 변경
|
|
|
183
182
|
- Uptime: 99.9%
|
|
184
183
|
- RTO: 1시간
|
|
185
184
|
- RPO: 15분
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## 10. 변경 이력
|
|
190
|
-
|
|
191
|
-
| 날짜 | 변경 내용 | 작성자 |
|
|
192
|
-
|------|-----------|--------|
|
|
193
|
-
| {날짜} | 문서 생성 | {이름} |
|