@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.
Files changed (2) hide show
  1. package/bin/vibe +123 -1
  2. 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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Vibe - Claude Code exclusive SPEC-driven AI coding framework",
5
5
  "bin": {
6
6
  "vibe": "./bin/vibe"