vibe-collab 0.8.6 → 0.8.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.
|
@@ -5,20 +5,23 @@ import chalk from 'chalk';
|
|
|
5
5
|
const SERVER_KEY = 'vibe-orchestrator';
|
|
6
6
|
// Windows에서 npx는 cmd /c 래퍼 없이 stdio MCP 연결이 실패할 수 있음
|
|
7
7
|
const IS_WIN = process.platform === 'win32';
|
|
8
|
-
function getStandardMcpEntry() {
|
|
8
|
+
function getStandardMcpEntry(cwdArg) {
|
|
9
|
+
const extra = cwdArg ? ['--cwd', cwdArg] : [];
|
|
9
10
|
return IS_WIN
|
|
10
|
-
? { command: 'cmd', args: ['/c', 'npx', 'vibe-collab', 'serve'], env: {} }
|
|
11
|
-
: { command: 'npx', args: ['vibe-collab', 'serve'], env: {} };
|
|
11
|
+
? { command: 'cmd', args: ['/c', 'npx', 'vibe-collab', 'serve', ...extra], env: {} }
|
|
12
|
+
: { command: 'npx', args: ['vibe-collab', 'serve', ...extra], env: {} };
|
|
12
13
|
}
|
|
13
|
-
function getVscodeMcpEntry() {
|
|
14
|
+
function getVscodeMcpEntry(cwdArg) {
|
|
15
|
+
const extra = cwdArg ? ['--cwd', cwdArg] : [];
|
|
14
16
|
return IS_WIN
|
|
15
|
-
? { type: 'stdio', command: 'cmd', args: ['/c', 'npx', 'vibe-collab', 'serve'] }
|
|
16
|
-
: { type: 'stdio', command: 'npx', args: ['vibe-collab', 'serve'] };
|
|
17
|
+
? { type: 'stdio', command: 'cmd', args: ['/c', 'npx', 'vibe-collab', 'serve', ...extra] }
|
|
18
|
+
: { type: 'stdio', command: 'npx', args: ['vibe-collab', 'serve', ...extra] };
|
|
17
19
|
}
|
|
18
|
-
function getArrayMcpEntry() {
|
|
20
|
+
function getArrayMcpEntry(cwdArg) {
|
|
21
|
+
const extra = cwdArg ? ['--cwd', cwdArg] : [];
|
|
19
22
|
return IS_WIN
|
|
20
|
-
? { name: SERVER_KEY, command: 'cmd', args: ['/c', 'npx', 'vibe-collab', 'serve'] }
|
|
21
|
-
: { name: SERVER_KEY, command: 'npx', args: ['vibe-collab', 'serve'] };
|
|
23
|
+
? { name: SERVER_KEY, command: 'cmd', args: ['/c', 'npx', 'vibe-collab', 'serve', ...extra] }
|
|
24
|
+
: { name: SERVER_KEY, command: 'npx', args: ['vibe-collab', 'serve', ...extra] };
|
|
22
25
|
}
|
|
23
26
|
function getToolDefs() {
|
|
24
27
|
return {
|
|
@@ -66,12 +69,12 @@ function writeJson(filePath, data) {
|
|
|
66
69
|
function deepEqual(a, b) {
|
|
67
70
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
68
71
|
}
|
|
69
|
-
function checkConfigJson(existing, format) {
|
|
72
|
+
function checkConfigJson(existing, format, cwdArg) {
|
|
70
73
|
if (format === 'vscode') {
|
|
71
74
|
const entry = existing['servers']?.[SERVER_KEY];
|
|
72
75
|
if (!entry)
|
|
73
76
|
return 'missing';
|
|
74
|
-
return deepEqual(entry, getVscodeMcpEntry()) ? 'match' : 'stale';
|
|
77
|
+
return deepEqual(entry, getVscodeMcpEntry(cwdArg)) ? 'match' : 'stale';
|
|
75
78
|
}
|
|
76
79
|
const raw = existing['mcpServers'];
|
|
77
80
|
if (format === 'array') {
|
|
@@ -79,38 +82,38 @@ function checkConfigJson(existing, format) {
|
|
|
79
82
|
const entry = raw.find((s) => s.name === SERVER_KEY);
|
|
80
83
|
if (!entry)
|
|
81
84
|
return 'missing';
|
|
82
|
-
return deepEqual(entry, getArrayMcpEntry()) ? 'match' : 'stale';
|
|
85
|
+
return deepEqual(entry, getArrayMcpEntry(cwdArg)) ? 'match' : 'stale';
|
|
83
86
|
}
|
|
84
87
|
if (raw && typeof raw === 'object') {
|
|
85
88
|
const entry = raw[SERVER_KEY];
|
|
86
89
|
if (!entry)
|
|
87
90
|
return 'missing';
|
|
88
|
-
return deepEqual(entry, getStandardMcpEntry()) ? 'match' : 'stale';
|
|
91
|
+
return deepEqual(entry, getStandardMcpEntry(cwdArg)) ? 'match' : 'stale';
|
|
89
92
|
}
|
|
90
93
|
return 'missing';
|
|
91
94
|
}
|
|
92
95
|
const entry = raw?.[SERVER_KEY];
|
|
93
96
|
if (!entry)
|
|
94
97
|
return 'missing';
|
|
95
|
-
return deepEqual(entry, getStandardMcpEntry()) ? 'match' : 'stale';
|
|
98
|
+
return deepEqual(entry, getStandardMcpEntry(cwdArg)) ? 'match' : 'stale';
|
|
96
99
|
}
|
|
97
|
-
function addEntryJson(existing, format) {
|
|
100
|
+
function addEntryJson(existing, format, cwdArg) {
|
|
98
101
|
if (format === 'vscode') {
|
|
99
102
|
const servers = existing['servers'] ?? {};
|
|
100
|
-
return { ...existing, servers: { ...servers, [SERVER_KEY]: getVscodeMcpEntry() } };
|
|
103
|
+
return { ...existing, servers: { ...servers, [SERVER_KEY]: getVscodeMcpEntry(cwdArg) } };
|
|
101
104
|
}
|
|
102
105
|
if (format === 'array') {
|
|
103
106
|
const raw = existing['mcpServers'];
|
|
104
107
|
// 이미 객체 형식이면 객체 형식 유지 (다른 도구가 먼저 설정한 경우)
|
|
105
108
|
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
|
|
106
|
-
return { ...existing, mcpServers: { ...raw, [SERVER_KEY]: getStandardMcpEntry() } };
|
|
109
|
+
return { ...existing, mcpServers: { ...raw, [SERVER_KEY]: getStandardMcpEntry(cwdArg) } };
|
|
107
110
|
}
|
|
108
111
|
// 배열에서 기존 항목 제거 후 최신 항목 추가
|
|
109
112
|
const arr = (Array.isArray(raw) ? raw.filter((s) => s.name !== SERVER_KEY) : []);
|
|
110
|
-
return { ...existing, mcpServers: [...arr, getArrayMcpEntry()] };
|
|
113
|
+
return { ...existing, mcpServers: [...arr, getArrayMcpEntry(cwdArg)] };
|
|
111
114
|
}
|
|
112
115
|
const mcpServers = existing['mcpServers'] ?? {};
|
|
113
|
-
return { ...existing, mcpServers: { ...mcpServers, [SERVER_KEY]: getStandardMcpEntry() } };
|
|
116
|
+
return { ...existing, mcpServers: { ...mcpServers, [SERVER_KEY]: getStandardMcpEntry(cwdArg) } };
|
|
114
117
|
}
|
|
115
118
|
// ── TOML 헬퍼 ────────────────────────────────────────────────────────────────
|
|
116
119
|
function readFileSafe(filePath) {
|
|
@@ -157,12 +160,14 @@ function configureTool(cwd, def, force) {
|
|
|
157
160
|
return { tool: def.label, configPath, status: 'added' };
|
|
158
161
|
}
|
|
159
162
|
// JSON 기반 형식
|
|
163
|
+
// global 도구는 --cwd를 주입해 MCP 서버가 프로젝트를 찾을 수 있게 함
|
|
164
|
+
const cwdArg = def.scope === 'global' ? cwd : undefined;
|
|
160
165
|
const existing = readJsonSafe(configPath);
|
|
161
|
-
const state = checkConfigJson(existing, def.format);
|
|
166
|
+
const state = checkConfigJson(existing, def.format, cwdArg);
|
|
162
167
|
if (state === 'match') {
|
|
163
168
|
return { tool: def.label, configPath, status: 'already' };
|
|
164
169
|
}
|
|
165
|
-
writeJson(configPath, addEntryJson(existing, def.format));
|
|
170
|
+
writeJson(configPath, addEntryJson(existing, def.format, cwdArg));
|
|
166
171
|
return { tool: def.label, configPath, status: state === 'stale' ? 'updated' : 'added' };
|
|
167
172
|
}
|
|
168
173
|
// ── 메인 커맨드 ──────────────────────────────────────────────────────────────
|
|
@@ -242,9 +247,9 @@ export async function connectCommand(cwd, aiFlag) {
|
|
|
242
247
|
const targetKeys = key
|
|
243
248
|
? (key === 'all' ? Object.keys(toolDefs) : [key])
|
|
244
249
|
: Object.keys(toolDefs);
|
|
245
|
-
printManualGuide(targetKeys);
|
|
250
|
+
printManualGuide(targetKeys, cwd);
|
|
246
251
|
}
|
|
247
|
-
function buildGuideEntries() {
|
|
252
|
+
function buildGuideEntries(projectCwd) {
|
|
248
253
|
return [
|
|
249
254
|
{
|
|
250
255
|
keys: ['claude', 'cursor', 'roocode', 'kiro', 'continue', 'qoder', 'codebuddy', 'droid'],
|
|
@@ -257,10 +262,15 @@ function buildGuideEntries() {
|
|
|
257
262
|
content: JSON.stringify({ servers: { [SERVER_KEY]: getVscodeMcpEntry() } }, null, 2),
|
|
258
263
|
},
|
|
259
264
|
{
|
|
260
|
-
keys: ['trae'
|
|
261
|
-
label: 'Trae (.trae/mcp.json)
|
|
265
|
+
keys: ['trae'],
|
|
266
|
+
label: 'Trae (.trae/mcp.json):',
|
|
262
267
|
content: JSON.stringify({ mcpServers: [getArrayMcpEntry()] }, null, 2),
|
|
263
268
|
},
|
|
269
|
+
{
|
|
270
|
+
keys: ['gemini'],
|
|
271
|
+
label: 'Gemini CLI (~/.gemini/settings.json):',
|
|
272
|
+
content: JSON.stringify({ mcpServers: [getArrayMcpEntry(projectCwd)] }, null, 2),
|
|
273
|
+
},
|
|
264
274
|
{
|
|
265
275
|
keys: ['opencode'],
|
|
266
276
|
label: 'OpenCode (.opencode/config.toml):',
|
|
@@ -274,17 +284,17 @@ function buildGuideEntries() {
|
|
|
274
284
|
{
|
|
275
285
|
keys: ['antigravity'],
|
|
276
286
|
label: 'Antigravity (~/.gemini/antigravity/mcp_config.json):',
|
|
277
|
-
content: JSON.stringify({ mcpServers: { [SERVER_KEY]: getStandardMcpEntry() } }, null, 2),
|
|
287
|
+
content: JSON.stringify({ mcpServers: { [SERVER_KEY]: getStandardMcpEntry(projectCwd) } }, null, 2),
|
|
278
288
|
},
|
|
279
289
|
{
|
|
280
290
|
keys: ['windsurf'],
|
|
281
291
|
label: 'Windsurf (~/.codeium/windsurf/mcp_config.json):',
|
|
282
|
-
content: JSON.stringify({ mcpServers: { [SERVER_KEY]: getStandardMcpEntry() } }, null, 2),
|
|
292
|
+
content: JSON.stringify({ mcpServers: { [SERVER_KEY]: getStandardMcpEntry(projectCwd) } }, null, 2),
|
|
283
293
|
},
|
|
284
294
|
];
|
|
285
295
|
}
|
|
286
|
-
function printManualGuide(targetKeys) {
|
|
287
|
-
const entries = buildGuideEntries();
|
|
296
|
+
function printManualGuide(targetKeys, projectCwd) {
|
|
297
|
+
const entries = buildGuideEntries(projectCwd);
|
|
288
298
|
const matched = entries.filter(e => e.keys.some(k => targetKeys.includes(k)));
|
|
289
299
|
if (matched.length === 0)
|
|
290
300
|
return;
|
|
@@ -2,7 +2,7 @@ import 'dotenv/config';
|
|
|
2
2
|
import { startMCPServer } from '../../mcp/server.js';
|
|
3
3
|
import { detectGitHubToken } from '../../github/auth.js';
|
|
4
4
|
import { getAIProvider } from '../../ai/client.js';
|
|
5
|
-
export async function serveCommand() {
|
|
5
|
+
export async function serveCommand(options = {}) {
|
|
6
6
|
// AI 제공자 확인 (경고만, 종료 안 함)
|
|
7
7
|
const aiProvider = getAIProvider();
|
|
8
8
|
if (!aiProvider) {
|
|
@@ -21,6 +21,6 @@ export async function serveCommand() {
|
|
|
21
21
|
else {
|
|
22
22
|
process.stderr.write('[Vibe Orchestrator] ✓ GitHub 인증 확인됨\n');
|
|
23
23
|
}
|
|
24
|
-
await startMCPServer();
|
|
24
|
+
await startMCPServer(options.cwd);
|
|
25
25
|
}
|
|
26
26
|
//# sourceMappingURL=serve.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -36,8 +36,9 @@ program
|
|
|
36
36
|
program
|
|
37
37
|
.command('serve')
|
|
38
38
|
.description('MCP 서버를 시작합니다 (AI 에이전트 연결용)')
|
|
39
|
-
.
|
|
40
|
-
|
|
39
|
+
.option('--cwd <path>', '프로젝트 디렉토리 경로 (전역 MCP 설정에서 필요)')
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
await serveCommand(options);
|
|
41
42
|
});
|
|
42
43
|
program
|
|
43
44
|
.command('connect')
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function startMCPServer(): Promise<void>;
|
|
1
|
+
export declare function startMCPServer(explicitPath?: string): Promise<void>;
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
1
3
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
5
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -9,8 +11,24 @@ import { requestQA } from './tools/requestQA.js';
|
|
|
9
11
|
import { createPR } from './tools/createPR.js';
|
|
10
12
|
import { requestMergeReview } from './tools/requestMergeReview.js';
|
|
11
13
|
import { executeMerge } from './tools/executeMerge.js';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
// cwd에서 위로 올라가며 .vibe/state.json이 있는 프로젝트 루트를 탐색
|
|
15
|
+
function findProjectRoot(startDir) {
|
|
16
|
+
let dir = startDir;
|
|
17
|
+
for (let i = 0; i < 20; i++) {
|
|
18
|
+
if (fs.existsSync(path.join(dir, '.vibe', 'state.json'))) {
|
|
19
|
+
return dir;
|
|
20
|
+
}
|
|
21
|
+
const parent = path.dirname(dir);
|
|
22
|
+
if (parent === dir)
|
|
23
|
+
break; // 파일시스템 루트 도달
|
|
24
|
+
dir = parent;
|
|
25
|
+
}
|
|
26
|
+
return startDir; // 못 찾으면 원래 cwd
|
|
27
|
+
}
|
|
28
|
+
export async function startMCPServer(explicitPath) {
|
|
29
|
+
const repoPath = explicitPath
|
|
30
|
+
? path.resolve(explicitPath)
|
|
31
|
+
: findProjectRoot(process.cwd());
|
|
14
32
|
const server = new Server({ name: 'vibe-orchestrator', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
15
33
|
// ListTools 핸들러
|
|
16
34
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|