@su-record/vibe 1.0.2 → 1.0.3
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 +123 -1
- package/package.json +1 -1
package/bin/vibe
CHANGED
|
@@ -78,6 +78,118 @@ function copyDirRecursive(sourceDir, targetDir) {
|
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// 기술 스택 감지 (루트 + 1레벨 하위 폴더)
|
|
82
|
+
function detectTechStacks(projectRoot) {
|
|
83
|
+
const stacks = [];
|
|
84
|
+
|
|
85
|
+
const detectInDir = (dir, prefix = '') => {
|
|
86
|
+
const detected = [];
|
|
87
|
+
|
|
88
|
+
// Node.js / TypeScript
|
|
89
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) {
|
|
90
|
+
try {
|
|
91
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
|
|
92
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
93
|
+
|
|
94
|
+
if (deps['next']) detected.push({ type: 'typescript-nextjs', path: prefix });
|
|
95
|
+
else if (deps['react-native']) detected.push({ type: 'typescript-react-native', path: prefix });
|
|
96
|
+
else if (deps['react']) detected.push({ type: 'typescript-react', path: prefix });
|
|
97
|
+
else if (deps['vue']) detected.push({ type: 'typescript-vue', path: prefix });
|
|
98
|
+
else if (deps['express'] || deps['fastify'] || deps['koa']) detected.push({ type: 'typescript-node', path: prefix });
|
|
99
|
+
else if (pkg.name) detected.push({ type: 'typescript-node', path: prefix });
|
|
100
|
+
} catch (e) {}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Python
|
|
104
|
+
if (fs.existsSync(path.join(dir, 'pyproject.toml'))) {
|
|
105
|
+
try {
|
|
106
|
+
const content = fs.readFileSync(path.join(dir, 'pyproject.toml'), 'utf-8');
|
|
107
|
+
if (content.includes('fastapi')) detected.push({ type: 'python-fastapi', path: prefix });
|
|
108
|
+
else if (content.includes('django')) detected.push({ type: 'python-django', path: prefix });
|
|
109
|
+
else detected.push({ type: 'python', path: prefix });
|
|
110
|
+
} catch (e) {}
|
|
111
|
+
} else if (fs.existsSync(path.join(dir, 'requirements.txt'))) {
|
|
112
|
+
try {
|
|
113
|
+
const content = fs.readFileSync(path.join(dir, 'requirements.txt'), 'utf-8');
|
|
114
|
+
if (content.includes('fastapi')) detected.push({ type: 'python-fastapi', path: prefix });
|
|
115
|
+
else if (content.includes('django')) detected.push({ type: 'python-django', path: prefix });
|
|
116
|
+
else detected.push({ type: 'python', path: prefix });
|
|
117
|
+
} catch (e) {}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Flutter / Dart
|
|
121
|
+
if (fs.existsSync(path.join(dir, 'pubspec.yaml'))) {
|
|
122
|
+
detected.push({ type: 'dart-flutter', path: prefix });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Go
|
|
126
|
+
if (fs.existsSync(path.join(dir, 'go.mod'))) {
|
|
127
|
+
detected.push({ type: 'go', path: prefix });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Rust
|
|
131
|
+
if (fs.existsSync(path.join(dir, 'Cargo.toml'))) {
|
|
132
|
+
detected.push({ type: 'rust', path: prefix });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Java / Kotlin
|
|
136
|
+
if (fs.existsSync(path.join(dir, 'build.gradle')) || fs.existsSync(path.join(dir, 'build.gradle.kts'))) {
|
|
137
|
+
try {
|
|
138
|
+
const gradleFile = fs.existsSync(path.join(dir, 'build.gradle.kts'))
|
|
139
|
+
? path.join(dir, 'build.gradle.kts')
|
|
140
|
+
: path.join(dir, 'build.gradle');
|
|
141
|
+
const content = fs.readFileSync(gradleFile, 'utf-8');
|
|
142
|
+
if (content.includes('com.android')) detected.push({ type: 'kotlin-android', path: prefix });
|
|
143
|
+
else if (content.includes('kotlin')) detected.push({ type: 'kotlin', path: prefix });
|
|
144
|
+
else if (content.includes('spring')) detected.push({ type: 'java-spring', path: prefix });
|
|
145
|
+
else detected.push({ type: 'java', path: prefix });
|
|
146
|
+
} catch (e) {}
|
|
147
|
+
} else if (fs.existsSync(path.join(dir, 'pom.xml'))) {
|
|
148
|
+
try {
|
|
149
|
+
const content = fs.readFileSync(path.join(dir, 'pom.xml'), 'utf-8');
|
|
150
|
+
if (content.includes('spring')) detected.push({ type: 'java-spring', path: prefix });
|
|
151
|
+
else detected.push({ type: 'java', path: prefix });
|
|
152
|
+
} catch (e) {}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Swift / iOS
|
|
156
|
+
if (fs.existsSync(path.join(dir, 'Package.swift')) ||
|
|
157
|
+
fs.readdirSync(dir).some(f => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace'))) {
|
|
158
|
+
detected.push({ type: 'swift-ios', path: prefix });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return detected;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// 루트 디렉토리 검사
|
|
165
|
+
stacks.push(...detectInDir(projectRoot));
|
|
166
|
+
|
|
167
|
+
// 1레벨 하위 폴더 검사 (일반적인 모노레포 구조)
|
|
168
|
+
const subDirs = ['backend', 'frontend', 'server', 'client', 'api', 'web', 'mobile', 'app', 'packages', 'apps'];
|
|
169
|
+
for (const subDir of subDirs) {
|
|
170
|
+
const subPath = path.join(projectRoot, subDir);
|
|
171
|
+
if (fs.existsSync(subPath) && fs.statSync(subPath).isDirectory()) {
|
|
172
|
+
stacks.push(...detectInDir(subPath, subDir));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// packages/* 또는 apps/* 내부 검사 (monorepo)
|
|
177
|
+
for (const monoDir of ['packages', 'apps']) {
|
|
178
|
+
const monoPath = path.join(projectRoot, monoDir);
|
|
179
|
+
if (fs.existsSync(monoPath) && fs.statSync(monoPath).isDirectory()) {
|
|
180
|
+
const subPackages = fs.readdirSync(monoPath).filter(f => {
|
|
181
|
+
const fullPath = path.join(monoPath, f);
|
|
182
|
+
return fs.statSync(fullPath).isDirectory() && !f.startsWith('.');
|
|
183
|
+
});
|
|
184
|
+
for (const pkg of subPackages) {
|
|
185
|
+
stacks.push(...detectInDir(path.join(monoPath, pkg), `${monoDir}/${pkg}`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return stacks;
|
|
191
|
+
}
|
|
192
|
+
|
|
81
193
|
// 협업자 자동 설치 설정
|
|
82
194
|
function setupCollaboratorAutoInstall(projectRoot) {
|
|
83
195
|
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
@@ -247,9 +359,19 @@ async function init(projectName) {
|
|
|
247
359
|
fs.copyFileSync(templatePath, constitutionPath);
|
|
248
360
|
}
|
|
249
361
|
|
|
362
|
+
// 기술 스택 감지
|
|
363
|
+
const detectedStacks = detectTechStacks(projectRoot);
|
|
364
|
+
if (detectedStacks.length > 0) {
|
|
365
|
+
log(` 🔍 감지된 기술 스택:\n`);
|
|
366
|
+
detectedStacks.forEach(s => {
|
|
367
|
+
log(` - ${s.type}${s.path ? ` (${s.path}/)` : ''}\n`);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
250
371
|
const config = {
|
|
251
372
|
language: 'ko',
|
|
252
|
-
quality: { strict: true, autoVerify: true }
|
|
373
|
+
quality: { strict: true, autoVerify: true },
|
|
374
|
+
stacks: detectedStacks
|
|
253
375
|
};
|
|
254
376
|
fs.writeFileSync(path.join(vibeDir, 'config.json'), JSON.stringify(config, null, 2));
|
|
255
377
|
|