vibe-collab 0.8.1 → 0.8.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/README.md CHANGED
@@ -78,7 +78,7 @@ vibe start
78
78
  | `vibe auth status` | 로그인 상태 및 만료일 확인 |
79
79
  | `vibe init` | 프로젝트 초기화 (CHARTER, state.json 생성 + MCP 자동 감지) |
80
80
  | `vibe connect` | 프로젝트 내 AI 도구 자동 감지 후 MCP 설정 추가 |
81
- | `vibe connect --ai <tool>` | 특정 AI 도구에 MCP 설정 강제 추가 |
81
+ | `vibe connect --ai <tool>` | 특정 AI 도구에 MCP 설정 추가 (해당 도구의 수동 설정 가이드만 표시) |
82
82
  | `vibe start` | 작업 시작 또는 이어서 진행 |
83
83
  | `vibe status` | 팀 현황 출력 |
84
84
  | `vibe serve` | MCP 서버 시작 (AI 도구에서 자동 실행됨) |
@@ -86,25 +86,32 @@ vibe start
86
86
 
87
87
  ### `vibe connect --ai` 지원 도구
88
88
 
89
- ```bash
90
- vibe connect --ai claude # Claude Code → .claude/settings.json
91
- vibe connect --ai cursor # Cursor → .cursor/mcp.json
92
- vibe connect --ai vscode # VS Code → .vscode/mcp.json
93
- vibe connect --ai copilot # Copilot → .vscode/mcp.json
94
- vibe connect --ai windsurf # Windsurf → .windsurf/mcp.json
95
- vibe connect --ai gemini # Gemini CLI → .gemini/settings.json
96
- vibe connect --ai kiro # Kiro → .kiro/mcp.json
97
- vibe connect --ai antigravity # Antigravity → .antigravity/mcp.json
98
- vibe connect --ai codex # Codex CLI → .codex/mcp.json
99
- vibe connect --ai qoder # Qoder → .qoder/mcp.json
100
- vibe connect --ai roocode # Roo Code → .roo/mcp.json
101
- vibe connect --ai trae # Trae → .trae/mcp.json
102
- vibe connect --ai opencode # OpenCode .opencode/mcp.json
103
- vibe connect --ai continue # Continue → .continue/mcp.json
104
- vibe connect --ai codebuddy # CodeBuddy → .codebuddy/mcp.json
105
- vibe connect --ai droid # Droid → .droid/mcp.json
106
- vibe connect --ai all # 전체 설정
107
- ```
89
+ 각 도구별로 올바른 설정 파일 경로와 형식을 자동으로 적용합니다.
90
+ `--ai <tool>` 지정 해당 도구의 수동 설정 가이드만 출력됩니다.
91
+
92
+ | 명령어 | 도구 | 설정 파일 |
93
+ |--------|------|-----------|
94
+ | `vibe connect --ai claude` | Claude Code | `.claude/settings.json` |
95
+ | `vibe connect --ai cursor` | Cursor | `.cursor/mcp.json` |
96
+ | `vibe connect --ai vscode` | VS Code | `.vscode/mcp.json` |
97
+ | `vibe connect --ai copilot` | Copilot | `.vscode/mcp.json` |
98
+ | `vibe connect --ai kiro` | Kiro | `.kiro/settings/mcp.json` |
99
+ | `vibe connect --ai roocode` | Roo Code | `.roo/mcp.json` |
100
+ | `vibe connect --ai trae` | Trae | `.trae/mcp.json` (배열 형식) |
101
+ | `vibe connect --ai continue` | Continue | `.continue/mcpServers/mcp.json` |
102
+ | `vibe connect --ai opencode` | OpenCode | `.opencode/config.toml` |
103
+ | `vibe connect --ai codex` | Codex CLI | `.codex/config.toml` |
104
+ | `vibe connect --ai qoder` | Qoder | `.qoder/mcp.json` |
105
+ | `vibe connect --ai codebuddy` | CodeBuddy | `.codebuddy/mcp.json` |
106
+ | `vibe connect --ai droid` | Droid | `.droid/mcp.json` |
107
+ | `vibe connect --ai windsurf` | Windsurf | `~/.codeium/windsurf/mcp_config.json` (전역) |
108
+ | `vibe connect --ai gemini` | Gemini CLI | `~/.gemini/settings.json` (전역) |
109
+ | `vibe connect --ai antigravity` | Antigravity | `antigravity mcp add` 자동 실행 |
110
+ | `vibe connect --ai all` | 위 전체 설정 | — |
111
+
112
+ > **Antigravity**: 파일 기반 MCP 설정을 지원하지 않으므로 `antigravity mcp add vibe-orchestrator -- npx vibe-collab serve` 명령어를 자동으로 실행합니다.
113
+ >
114
+ > **Windsurf / Gemini CLI**: 전역 설정 파일에 기록됩니다 (프로젝트 디렉토리 불필요). 기존 설정 파일의 형식(객체/배열)이 다를 경우 자동으로 호환 처리합니다.
108
115
 
109
116
  ---
110
117
 
@@ -1,6 +1,9 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import os from 'os';
4
+ import { spawnSync } from 'child_process';
3
5
  import chalk from 'chalk';
6
+ const SERVER_KEY = 'vibe-orchestrator';
4
7
  const STANDARD_MCP_ENTRY = {
5
8
  command: 'npx',
6
9
  args: ['vibe-collab', 'serve'],
@@ -11,34 +14,74 @@ const VSCODE_MCP_ENTRY = {
11
14
  command: 'npx',
12
15
  args: ['vibe-collab', 'serve'],
13
16
  };
14
- function getToolDefs(cwd) {
17
+ // Trae / Gemini CLI 공통 배열 엔트리
18
+ const ARRAY_MCP_ENTRY = {
19
+ name: SERVER_KEY,
20
+ command: 'npx',
21
+ args: ['vibe-collab', 'serve'],
22
+ };
23
+ function getToolDefs() {
15
24
  return {
16
- claude: { label: 'Claude Code', configDir: '.claude', configFile: 'settings.json', format: 'standard' },
17
- cursor: { label: 'Cursor', configDir: '.cursor', configFile: 'mcp.json', format: 'standard' },
18
- vscode: { label: 'VS Code', configDir: '.vscode', configFile: 'mcp.json', format: 'vscode' },
19
- copilot: { label: 'Copilot', configDir: '.vscode', configFile: 'mcp.json', format: 'vscode' },
20
- windsurf: { label: 'Windsurf', configDir: '.windsurf', configFile: 'mcp.json', format: 'standard' },
21
- gemini: { label: 'Gemini CLI', configDir: '.gemini', configFile: 'settings.json', format: 'standard' },
22
- kiro: { label: 'Kiro', configDir: '.kiro', configFile: 'mcp.json', format: 'standard' },
23
- antigravity: { label: 'Antigravity', configDir: '.antigravity', configFile: 'mcp.json', format: 'standard' },
24
- codex: { label: 'Codex CLI', configDir: '.codex', configFile: 'mcp.json', format: 'standard' },
25
- qoder: { label: 'Qoder', configDir: '.qoder', configFile: 'mcp.json', format: 'standard' },
26
- roocode: { label: 'Roo Code', configDir: '.roo', configFile: 'mcp.json', format: 'standard' },
27
- trae: { label: 'Trae', configDir: '.trae', configFile: 'mcp.json', format: 'standard' },
28
- opencode: { label: 'OpenCode', configDir: '.opencode', configFile: 'mcp.json', format: 'standard' },
29
- continue: { label: 'Continue', configDir: '.continue', configFile: 'mcp.json', format: 'standard' },
30
- codebuddy: { label: 'CodeBuddy', configDir: '.codebuddy', configFile: 'mcp.json', format: 'standard' },
31
- droid: { label: 'Droid', configDir: '.droid', configFile: 'mcp.json', format: 'standard' },
25
+ // ── 프로젝트 로컬 (JSON) ───────────────────────────────────────────────────
26
+ claude: { label: 'Claude Code', configDir: '.claude', configFile: 'settings.json', format: 'standard', scope: 'project' },
27
+ cursor: { label: 'Cursor', configDir: '.cursor', configFile: 'mcp.json', format: 'standard', scope: 'project' },
28
+ vscode: { label: 'VS Code', configDir: '.vscode', configFile: 'mcp.json', format: 'vscode', scope: 'project' },
29
+ copilot: { label: 'Copilot', configDir: '.vscode', configFile: 'mcp.json', format: 'vscode', scope: 'project' },
30
+ kiro: { label: 'Kiro', configDir: '.kiro/settings', configFile: 'mcp.json', format: 'standard', scope: 'project' },
31
+ roocode: { label: 'Roo Code', configDir: '.roo', configFile: 'mcp.json', format: 'standard', scope: 'project' },
32
+ trae: { label: 'Trae', configDir: '.trae', configFile: 'mcp.json', format: 'array', scope: 'project' },
33
+ continue: { label: 'Continue', configDir: '.continue/mcpServers', configFile: 'mcp.json', format: 'standard', scope: 'project' },
34
+ qoder: { label: 'Qoder', configDir: '.qoder', configFile: 'mcp.json', format: 'standard', scope: 'project' },
35
+ codebuddy: { label: 'CodeBuddy', configDir: '.codebuddy', configFile: 'mcp.json', format: 'standard', scope: 'project' },
36
+ droid: { label: 'Droid', configDir: '.droid', configFile: 'mcp.json', format: 'standard', scope: 'project' },
37
+ // ── 프로젝트 로컬 (TOML) ──────────────────────────────────────────────────
38
+ opencode: { label: 'OpenCode', configDir: '.opencode', configFile: 'config.toml', format: 'toml-opencode', scope: 'project' },
39
+ codex: { label: 'Codex CLI', configDir: '.codex', configFile: 'config.toml', format: 'toml-codex', scope: 'project' },
40
+ // ── 전역 (홈 디렉토리) ────────────────────────────────────────────────────
41
+ windsurf: { label: 'Windsurf', configDir: '.codeium/windsurf', configFile: 'mcp_config.json', format: 'standard', scope: 'global' },
42
+ gemini: { label: 'Gemini CLI', configDir: '.gemini', configFile: 'settings.json', format: 'array', scope: 'global' },
43
+ // ── 수동 설정만 지원 ──────────────────────────────────────────────────────
44
+ antigravity: { label: 'Antigravity', configDir: '', configFile: '', format: 'cli', scope: 'manual' },
32
45
  };
33
- void cwd;
34
46
  }
35
- function hasDir(dir) {
36
- return fs.existsSync(dir);
47
+ function resolveDir(cwd, def) {
48
+ if (def.scope === 'manual')
49
+ return ''; // 수동 설정 도구는 디렉토리 불필요
50
+ const base = def.scope === 'global' ? os.homedir() : cwd;
51
+ return path.join(base, def.configDir);
52
+ }
53
+ function isCommandAvailable(cmd) {
54
+ const check = spawnSync(process.platform === 'win32' ? 'where' : 'which', [cmd], { stdio: 'pipe' });
55
+ return check.status === 0;
37
56
  }
57
+ function configureViaCli(def) {
58
+ const binary = def.configDir; // configDir에 바이너리명 저장
59
+ const result = spawnSync(binary, ['mcp', 'add', SERVER_KEY, '--', 'npx', 'vibe-collab', 'serve'], {
60
+ encoding: 'utf-8',
61
+ shell: true,
62
+ stdio: 'pipe',
63
+ });
64
+ const output = ((result.stdout ?? '') + (result.stderr ?? '')).toLowerCase();
65
+ if (result.status === 0) {
66
+ // 이미 존재한다는 메시지가 포함된 경우 (도구마다 다를 수 있음)
67
+ if (output.includes('already') || output.includes('exist') || output.includes('duplicate')) {
68
+ return { tool: def.label, configPath: '', status: 'already' };
69
+ }
70
+ return { tool: def.label, configPath: '', status: 'added' };
71
+ }
72
+ // 종료 코드 비정상
73
+ if (output.includes('already') || output.includes('exist') || output.includes('duplicate')) {
74
+ return { tool: def.label, configPath: '', status: 'already' };
75
+ }
76
+ if (result.error?.message?.includes('ENOENT') || output.includes('not found')) {
77
+ return { tool: def.label, configPath: '', status: 'skipped' }; // 미설치
78
+ }
79
+ return { tool: def.label, configPath: '', status: 'error', errorMsg: (result.stderr ?? '').trim() };
80
+ }
81
+ // ── JSON 헬퍼 ────────────────────────────────────────────────────────────────
38
82
  function readJsonSafe(filePath) {
39
83
  try {
40
- const raw = fs.readFileSync(filePath, 'utf-8');
41
- return JSON.parse(raw);
84
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
42
85
  }
43
86
  catch {
44
87
  return {};
@@ -46,72 +89,119 @@ function readJsonSafe(filePath) {
46
89
  }
47
90
  function writeJson(filePath, data) {
48
91
  const dir = path.dirname(filePath);
49
- if (!fs.existsSync(dir)) {
92
+ if (!fs.existsSync(dir))
50
93
  fs.mkdirSync(dir, { recursive: true });
51
- }
52
94
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
53
95
  }
54
- function isAlreadyConfigured(existing, serverKey) {
55
- const mcpServers = existing['mcpServers'];
56
- const servers = existing['servers'];
57
- return !!(mcpServers?.[serverKey] || servers?.[serverKey]);
96
+ function isConfiguredJson(existing, format) {
97
+ if (format === 'vscode') {
98
+ return !!(existing['servers']?.[SERVER_KEY]);
99
+ }
100
+ const raw = existing['mcpServers'];
101
+ if (format === 'array') {
102
+ // mcpServers가 배열이면 name으로 검색, 객체이면 키로 검색
103
+ if (Array.isArray(raw))
104
+ return raw.some(s => s.name === SERVER_KEY);
105
+ if (raw && typeof raw === 'object')
106
+ return !!raw[SERVER_KEY];
107
+ return false;
108
+ }
109
+ return !!(raw?.[SERVER_KEY]);
58
110
  }
59
- function addStandardEntry(existing, serverKey) {
111
+ function addEntryJson(existing, format) {
112
+ if (format === 'vscode') {
113
+ const servers = existing['servers'] ?? {};
114
+ return { ...existing, servers: { ...servers, [SERVER_KEY]: VSCODE_MCP_ENTRY } };
115
+ }
116
+ if (format === 'array') {
117
+ const raw = existing['mcpServers'];
118
+ // 이미 객체 형식이면 객체 형식 유지 (다른 도구가 먼저 설정한 경우)
119
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
120
+ return { ...existing, mcpServers: { ...raw, [SERVER_KEY]: STANDARD_MCP_ENTRY } };
121
+ }
122
+ const arr = raw ?? [];
123
+ return { ...existing, mcpServers: [...arr, ARRAY_MCP_ENTRY] };
124
+ }
60
125
  const mcpServers = existing['mcpServers'] ?? {};
61
- return {
62
- ...existing,
63
- mcpServers: {
64
- ...mcpServers,
65
- [serverKey]: STANDARD_MCP_ENTRY,
66
- },
67
- };
126
+ return { ...existing, mcpServers: { ...mcpServers, [SERVER_KEY]: STANDARD_MCP_ENTRY } };
68
127
  }
69
- function addVSCodeEntry(existing, serverKey) {
70
- const servers = existing['servers'] ?? {};
71
- return {
72
- ...existing,
73
- servers: {
74
- ...servers,
75
- [serverKey]: VSCODE_MCP_ENTRY,
76
- },
77
- };
128
+ // ── TOML 헬퍼 ────────────────────────────────────────────────────────────────
129
+ function readFileSafe(filePath) {
130
+ try {
131
+ return fs.readFileSync(filePath, 'utf-8');
132
+ }
133
+ catch {
134
+ return '';
135
+ }
78
136
  }
137
+ function appendToFile(filePath, content) {
138
+ const dir = path.dirname(filePath);
139
+ if (!fs.existsSync(dir))
140
+ fs.mkdirSync(dir, { recursive: true });
141
+ fs.appendFileSync(filePath, content, 'utf-8');
142
+ }
143
+ // ── 도구 설정 ────────────────────────────────────────────────────────────────
79
144
  function configureTool(cwd, def, force) {
80
- const dir = path.join(cwd, def.configDir);
81
- if (!force && !hasDir(dir)) {
145
+ // 수동 설정만 지원하는 도구 (Antigravity ) — 자동 설정 불가, 가이드만 표시
146
+ if (def.scope === 'manual') {
147
+ return { tool: def.label, configPath: '', status: 'manual' };
148
+ }
149
+ // 자동 감지 모드: 디렉토리 존재 여부만 확인 (--ai 지정 시 force=true → 무조건 설정)
150
+ if (!force && !fs.existsSync(resolveDir(cwd, def))) {
82
151
  return { tool: def.label, configPath: '', status: 'skipped' };
83
152
  }
153
+ const dir = resolveDir(cwd, def);
84
154
  const configPath = path.join(dir, def.configFile);
155
+ // OpenCode: [mcp.vibe-orchestrator] 섹션 형식
156
+ if (def.format === 'toml-opencode') {
157
+ const content = readFileSafe(configPath);
158
+ if (content.includes(`[mcp.${SERVER_KEY}]`)) {
159
+ return { tool: def.label, configPath, status: 'already' };
160
+ }
161
+ const entry = `\n[mcp.${SERVER_KEY}]\ntype = "stdio"\ncommand = "npx"\nargs = ["vibe-collab", "serve"]\n`;
162
+ appendToFile(configPath, entry);
163
+ return { tool: def.label, configPath, status: 'added' };
164
+ }
165
+ // Codex CLI: [[mcp]] 배열 항목 형식
166
+ if (def.format === 'toml-codex') {
167
+ const content = readFileSafe(configPath);
168
+ const alreadyExists = content.split('[[mcp]]').some(b => b.includes(`name = "${SERVER_KEY}"`));
169
+ if (alreadyExists) {
170
+ return { tool: def.label, configPath, status: 'already' };
171
+ }
172
+ const entry = `\n[[mcp]]\nname = "${SERVER_KEY}"\ncommand = "npx"\nargs = ["vibe-collab", "serve"]\n`;
173
+ appendToFile(configPath, entry);
174
+ return { tool: def.label, configPath, status: 'added' };
175
+ }
176
+ // JSON 기반 형식
85
177
  const existing = readJsonSafe(configPath);
86
- if (isAlreadyConfigured(existing, 'vibe-orchestrator')) {
178
+ if (isConfiguredJson(existing, def.format)) {
87
179
  return { tool: def.label, configPath, status: 'already' };
88
180
  }
89
- const updated = def.format === 'vscode'
90
- ? addVSCodeEntry(existing, 'vibe-orchestrator')
91
- : addStandardEntry(existing, 'vibe-orchestrator');
92
- writeJson(configPath, updated);
181
+ writeJson(configPath, addEntryJson(existing, def.format));
93
182
  return { tool: def.label, configPath, status: 'added' };
94
183
  }
184
+ // ── 메인 커맨드 ──────────────────────────────────────────────────────────────
95
185
  export async function connectCommand(cwd, aiFlag) {
96
- const toolDefs = getToolDefs(cwd);
186
+ const toolDefs = getToolDefs();
187
+ const key = aiFlag?.toLowerCase();
97
188
  let targets;
98
- if (aiFlag) {
99
- const key = aiFlag.toLowerCase();
189
+ if (key) {
100
190
  if (key === 'all') {
101
191
  targets = Object.values(toolDefs).map(def => ({ def, force: true }));
192
+ console.log(chalk.cyan('\n🔌 전체 AI 도구 MCP 설정 중...\n'));
102
193
  }
103
194
  else if (toolDefs[key]) {
104
195
  targets = [{ def: toolDefs[key], force: true }];
196
+ console.log(chalk.cyan(`\n🔌 ${toolDefs[key].label} MCP 설정 중...\n`));
105
197
  }
106
198
  else {
107
199
  console.log(chalk.red(`알 수 없는 AI 도구: ${aiFlag}`));
108
200
  console.log(chalk.gray(`지원 목록: ${Object.keys(toolDefs).join(', ')}, all`));
109
201
  return;
110
202
  }
111
- console.log(chalk.cyan(`\n🔌 ${aiFlag === 'all' ? '전체' : toolDefs[aiFlag.toLowerCase()]?.label} MCP 설정 중...\n`));
112
203
  }
113
204
  else {
114
- // 자동 감지: 프로젝트 디렉토리가 있는 것만
115
205
  targets = Object.values(toolDefs).map(def => ({ def, force: false }));
116
206
  console.log(chalk.cyan('\n🔌 AI 도구 자동 감지 중...\n'));
117
207
  }
@@ -119,24 +209,35 @@ export async function connectCommand(cwd, aiFlag) {
119
209
  let addedCount = 0;
120
210
  let alreadyCount = 0;
121
211
  let skippedCount = 0;
212
+ let manualCount = 0;
122
213
  for (const result of results) {
123
214
  if (result.status === 'added') {
124
215
  console.log(chalk.green(` ✅ ${result.tool} — 설정 완료`));
125
- console.log(chalk.gray(` ${result.configPath}`));
216
+ if (result.configPath)
217
+ console.log(chalk.gray(` ${result.configPath}`));
126
218
  addedCount++;
127
219
  }
128
220
  else if (result.status === 'already') {
129
221
  console.log(chalk.yellow(` ☑️ ${result.tool} — 이미 설정됨`));
130
222
  alreadyCount++;
131
223
  }
224
+ else if (result.status === 'error') {
225
+ console.log(chalk.red(` ❌ ${result.tool} — 설정 실패`));
226
+ if (result.errorMsg)
227
+ console.log(chalk.gray(` ${result.errorMsg}`));
228
+ }
229
+ else if (result.status === 'manual') {
230
+ console.log(chalk.cyan(` 📋 ${result.tool} — 수동 설정 필요 (아래 가이드 참고)`));
231
+ manualCount++;
232
+ }
132
233
  else {
133
234
  console.log(chalk.gray(` ⬜ ${result.tool} — 미감지`));
134
235
  skippedCount++;
135
236
  }
136
237
  }
137
238
  console.log('');
138
- if (addedCount === 0 && alreadyCount === 0) {
139
- console.log(chalk.yellow('감지된 AI 도구가 없습니다.\nCursor, VS Code, Claude Code 중 하나를 설치하거나\n해당 도구의 프로젝트 디렉터리(.cursor, .vscode, .claude)를 먼저 만들어주세요.'));
239
+ if (addedCount === 0 && alreadyCount === 0 && manualCount === 0) {
240
+ console.log(chalk.yellow('감지된 AI 도구가 없습니다.\n--ai 플래그로 수동 설정하거나 해당 도구를 먼저 설치하세요.'));
140
241
  }
141
242
  else {
142
243
  const parts = [];
@@ -144,21 +245,73 @@ export async function connectCommand(cwd, aiFlag) {
144
245
  parts.push(`${addedCount}개 도구 설정 완료`);
145
246
  if (alreadyCount > 0)
146
247
  parts.push(`${alreadyCount}개 이미 설정됨`);
248
+ if (manualCount > 0)
249
+ parts.push(`${manualCount}개 수동 설정 필요`);
147
250
  if (skippedCount > 0)
148
251
  parts.push(`${skippedCount}개 미감지`);
149
252
  console.log(chalk.green(`✨ ${parts.join(', ')}`));
150
- console.log(chalk.gray(' AI 도구를 재시작하면 vibe-orchestrator MCP가 활성화됩니다.\n'));
253
+ if (addedCount > 0 || alreadyCount > 0) {
254
+ console.log(chalk.gray(' AI 도구를 재시작하면 vibe-orchestrator MCP가 활성화됩니다.\n'));
255
+ }
256
+ else {
257
+ console.log('');
258
+ }
259
+ }
260
+ // ── 수동 설정 가이드 ───────────────────────────────────────────────────────
261
+ const targetKeys = key
262
+ ? (key === 'all' ? Object.keys(toolDefs) : [key])
263
+ : Object.keys(toolDefs);
264
+ printManualGuide(targetKeys);
265
+ }
266
+ function buildGuideEntries() {
267
+ return [
268
+ {
269
+ keys: ['claude', 'cursor', 'roocode', 'kiro', 'continue', 'qoder', 'codebuddy', 'droid'],
270
+ label: 'Claude Code / Cursor / Roo Code / Kiro / Continue / Qoder / CodeBuddy / Droid:',
271
+ content: JSON.stringify({ mcpServers: { [SERVER_KEY]: STANDARD_MCP_ENTRY } }, null, 2),
272
+ },
273
+ {
274
+ keys: ['vscode', 'copilot'],
275
+ label: 'VS Code / Copilot (.vscode/mcp.json):',
276
+ content: JSON.stringify({ servers: { [SERVER_KEY]: VSCODE_MCP_ENTRY } }, null, 2),
277
+ },
278
+ {
279
+ keys: ['trae', 'gemini'],
280
+ label: 'Trae (.trae/mcp.json) / Gemini CLI (~/.gemini/settings.json):',
281
+ content: JSON.stringify({ mcpServers: [ARRAY_MCP_ENTRY] }, null, 2),
282
+ },
283
+ {
284
+ keys: ['opencode'],
285
+ label: 'OpenCode (.opencode/config.toml):',
286
+ content: `[mcp.${SERVER_KEY}]\ntype = "stdio"\ncommand = "npx"\nargs = ["vibe-collab", "serve"]`,
287
+ },
288
+ {
289
+ keys: ['codex'],
290
+ label: 'Codex CLI (.codex/config.toml):',
291
+ content: `[[mcp]]\nname = "${SERVER_KEY}"\ncommand = "npx"\nargs = ["vibe-collab", "serve"]`,
292
+ },
293
+ {
294
+ keys: ['antigravity'],
295
+ label: 'Antigravity (파일 기반 MCP 미지원 — CLI 명령어 사용):',
296
+ content: `antigravity mcp add ${SERVER_KEY} -- npx vibe-collab serve`,
297
+ },
298
+ {
299
+ keys: ['windsurf'],
300
+ label: 'Windsurf (~/.codeium/windsurf/mcp_config.json):',
301
+ content: JSON.stringify({ mcpServers: { [SERVER_KEY]: STANDARD_MCP_ENTRY } }, null, 2),
302
+ },
303
+ ];
304
+ }
305
+ function printManualGuide(targetKeys) {
306
+ const entries = buildGuideEntries();
307
+ const matched = entries.filter(e => e.keys.some(k => targetKeys.includes(k)));
308
+ if (matched.length === 0)
309
+ return;
310
+ console.log(chalk.cyan('─── 수동 설정 가이드 ───\n'));
311
+ for (const entry of matched) {
312
+ console.log(chalk.white(entry.label));
313
+ console.log(chalk.gray(entry.content));
314
+ console.log('');
151
315
  }
152
- console.log(chalk.cyan('─── 수동 설정 (다른 MCP 지원 도구에 직접 추가) ───\n'));
153
- console.log(chalk.white('Claude Code / Cursor / 기타:'));
154
- console.log(chalk.gray(JSON.stringify({
155
- mcpServers: { 'vibe-orchestrator': STANDARD_MCP_ENTRY },
156
- }, null, 2)));
157
- console.log('');
158
- console.log(chalk.white('VS Code:'));
159
- console.log(chalk.gray(JSON.stringify({
160
- servers: { 'vibe-orchestrator': VSCODE_MCP_ENTRY },
161
- }, null, 2)));
162
- console.log('');
163
316
  }
164
317
  //# sourceMappingURL=connect.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-collab",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "누가 어떤 AI를 써도, 항상 한 팀처럼 작동하는 바이브 코딩 협업 도구",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,4 +39,4 @@
39
39
  "tsx": "^4.19.0",
40
40
  "typescript": "^5.7.0"
41
41
  }
42
- }
42
+ }