@vibescope/mcp-server 0.4.4 → 0.4.6
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/CHANGELOG.md +84 -84
- package/README.md +194 -194
- package/dist/api-client/bodies-of-work.d.ts +125 -0
- package/dist/api-client/bodies-of-work.js +78 -0
- package/dist/api-client/chat.d.ts +26 -0
- package/dist/api-client/chat.js +20 -0
- package/dist/api-client/connectors.d.ts +104 -0
- package/dist/api-client/connectors.js +46 -0
- package/dist/api-client/deployment.d.ts +190 -0
- package/dist/api-client/deployment.js +113 -0
- package/dist/api-client/file-checkouts.d.ts +71 -0
- package/dist/api-client/file-checkouts.js +43 -0
- package/dist/api-client/git-issues.d.ts +55 -0
- package/dist/api-client/git-issues.js +34 -0
- package/dist/api-client/index.d.ts +619 -1
- package/dist/api-client/index.js +148 -0
- package/dist/api-client/organizations.d.ts +101 -0
- package/dist/api-client/organizations.js +86 -0
- package/dist/api-client/progress.d.ts +61 -0
- package/dist/api-client/progress.js +34 -0
- package/dist/api-client/project.d.ts +1 -0
- package/dist/api-client/requests.d.ts +28 -0
- package/dist/api-client/requests.js +28 -0
- package/dist/api-client/sprints.d.ts +153 -0
- package/dist/api-client/sprints.js +82 -0
- package/dist/api-client/subtasks.d.ts +37 -0
- package/dist/api-client/subtasks.js +23 -0
- package/dist/api-client.d.ts +23 -0
- package/dist/api-client.js +15 -0
- package/dist/cli-init.js +21 -21
- package/dist/cli.js +26 -26
- package/dist/handlers/blockers.js +4 -0
- package/dist/handlers/chat.d.ts +23 -0
- package/dist/handlers/chat.js +84 -0
- package/dist/handlers/deployment.d.ts +3 -0
- package/dist/handlers/deployment.js +23 -0
- package/dist/handlers/discovery.js +13 -0
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/project.js +4 -2
- package/dist/handlers/session.js +7 -0
- package/dist/handlers/tasks.js +7 -0
- package/dist/handlers/tool-docs.js +1204 -1131
- package/dist/index.js +73 -73
- package/dist/templates/agent-guidelines.d.ts +1 -1
- package/dist/templates/agent-guidelines.js +205 -187
- package/dist/templates/help-content.js +1621 -1621
- package/dist/tools/bodies-of-work.js +6 -6
- package/dist/tools/chat.d.ts +1 -0
- package/dist/tools/chat.js +24 -0
- package/dist/tools/cloud-agents.js +22 -22
- package/dist/tools/deployment.js +13 -0
- package/dist/tools/features.d.ts +13 -0
- package/dist/tools/features.js +151 -0
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.js +4 -1
- package/dist/tools/milestones.js +2 -2
- package/dist/tools/project.js +4 -0
- package/dist/tools/requests.js +1 -1
- package/dist/tools/session.js +11 -11
- package/dist/tools/sprints.js +9 -9
- package/dist/tools/tasks.js +35 -35
- package/dist/tools/worktrees.js +14 -14
- package/dist/tools.d.ts +2 -0
- package/dist/tools.js +3602 -0
- package/dist/utils.js +11 -11
- package/docs/TOOLS.md +2663 -2545
- package/package.json +53 -53
- package/scripts/generate-docs.ts +212 -212
- package/scripts/version-bump.ts +203 -203
- package/src/api-client/blockers.ts +86 -86
- package/src/api-client/bodies-of-work.ts +194 -0
- package/src/api-client/chat.ts +50 -0
- package/src/api-client/connectors.ts +152 -0
- package/src/api-client/cost.ts +185 -185
- package/src/api-client/decisions.ts +87 -87
- package/src/api-client/deployment.ts +313 -0
- package/src/api-client/discovery.ts +81 -81
- package/src/api-client/fallback.ts +52 -52
- package/src/api-client/file-checkouts.ts +115 -0
- package/src/api-client/findings.ts +100 -100
- package/src/api-client/git-issues.ts +88 -0
- package/src/api-client/ideas.ts +112 -112
- package/src/api-client/index.ts +592 -426
- package/src/api-client/milestones.ts +83 -83
- package/src/api-client/organizations.ts +185 -0
- package/src/api-client/progress.ts +94 -0
- package/src/api-client/project.ts +180 -179
- package/src/api-client/requests.ts +54 -0
- package/src/api-client/session.ts +220 -220
- package/src/api-client/sprints.ts +227 -0
- package/src/api-client/subtasks.ts +57 -0
- package/src/api-client/tasks.ts +450 -450
- package/src/api-client/types.ts +32 -32
- package/src/api-client/validation.ts +60 -60
- package/src/api-client/worktrees.ts +53 -53
- package/src/api-client.test.ts +847 -850
- package/src/api-client.ts +2707 -2672
- package/src/cli-init.ts +557 -557
- package/src/cli.test.ts +284 -284
- package/src/cli.ts +204 -204
- package/src/handlers/__test-setup__.ts +240 -236
- package/src/handlers/__test-utils__.ts +89 -89
- package/src/handlers/blockers.test.ts +468 -468
- package/src/handlers/blockers.ts +172 -163
- package/src/handlers/bodies-of-work.test.ts +704 -704
- package/src/handlers/bodies-of-work.ts +526 -526
- package/src/handlers/chat.test.ts +185 -0
- package/src/handlers/chat.ts +101 -0
- package/src/handlers/cloud-agents.test.ts +438 -438
- package/src/handlers/cloud-agents.ts +156 -156
- package/src/handlers/connectors.test.ts +834 -834
- package/src/handlers/connectors.ts +229 -229
- package/src/handlers/cost.test.ts +462 -462
- package/src/handlers/cost.ts +285 -285
- package/src/handlers/decisions.test.ts +382 -382
- package/src/handlers/decisions.ts +153 -153
- package/src/handlers/deployment.test.ts +551 -551
- package/src/handlers/deployment.ts +570 -541
- package/src/handlers/discovery.test.ts +206 -206
- package/src/handlers/discovery.ts +427 -414
- package/src/handlers/fallback.test.ts +537 -537
- package/src/handlers/fallback.ts +194 -194
- package/src/handlers/file-checkouts.test.ts +750 -750
- package/src/handlers/file-checkouts.ts +185 -185
- package/src/handlers/findings.test.ts +633 -633
- package/src/handlers/findings.ts +239 -239
- package/src/handlers/git-issues.test.ts +631 -631
- package/src/handlers/git-issues.ts +136 -136
- package/src/handlers/ideas.test.ts +644 -644
- package/src/handlers/ideas.ts +207 -207
- package/src/handlers/index.ts +93 -90
- package/src/handlers/milestones.test.ts +475 -475
- package/src/handlers/milestones.ts +180 -180
- package/src/handlers/organizations.test.ts +826 -826
- package/src/handlers/organizations.ts +315 -315
- package/src/handlers/progress.test.ts +269 -269
- package/src/handlers/progress.ts +77 -77
- package/src/handlers/project.test.ts +546 -546
- package/src/handlers/project.ts +242 -239
- package/src/handlers/requests.test.ts +303 -303
- package/src/handlers/requests.ts +99 -99
- package/src/handlers/roles.test.ts +305 -305
- package/src/handlers/roles.ts +219 -219
- package/src/handlers/session.test.ts +998 -998
- package/src/handlers/session.ts +1105 -1093
- package/src/handlers/sprints.test.ts +732 -732
- package/src/handlers/sprints.ts +537 -537
- package/src/handlers/tasks.test.ts +931 -931
- package/src/handlers/tasks.ts +1133 -1121
- package/src/handlers/tool-categories.test.ts +66 -66
- package/src/handlers/tool-docs.test.ts +511 -511
- package/src/handlers/tool-docs.ts +1571 -1491
- package/src/handlers/types.test.ts +259 -259
- package/src/handlers/types.ts +176 -176
- package/src/handlers/validation.test.ts +582 -582
- package/src/handlers/validation.ts +164 -164
- package/src/handlers/version.ts +63 -63
- package/src/index.test.ts +674 -674
- package/src/index.ts +807 -807
- package/src/setup.test.ts +233 -233
- package/src/setup.ts +404 -404
- package/src/templates/agent-guidelines.ts +233 -215
- package/src/templates/help-content.ts +1751 -1751
- package/src/token-tracking.test.ts +463 -463
- package/src/token-tracking.ts +167 -167
- package/src/tools/blockers.ts +122 -122
- package/src/tools/bodies-of-work.ts +283 -283
- package/src/tools/chat.ts +72 -46
- package/src/tools/cloud-agents.ts +101 -101
- package/src/tools/connectors.ts +191 -191
- package/src/tools/cost.ts +111 -111
- package/src/tools/decisions.ts +111 -111
- package/src/tools/deployment.ts +455 -442
- package/src/tools/discovery.ts +76 -76
- package/src/tools/fallback.ts +111 -111
- package/src/tools/features.ts +154 -0
- package/src/tools/file-checkouts.ts +145 -145
- package/src/tools/findings.ts +101 -101
- package/src/tools/git-issues.ts +130 -130
- package/src/tools/ideas.ts +162 -162
- package/src/tools/index.ts +141 -137
- package/src/tools/milestones.ts +118 -118
- package/src/tools/organizations.ts +224 -224
- package/src/tools/progress.ts +73 -73
- package/src/tools/project.ts +206 -202
- package/src/tools/requests.ts +68 -68
- package/src/tools/roles.ts +112 -112
- package/src/tools/session.ts +181 -181
- package/src/tools/sprints.ts +298 -298
- package/src/tools/tasks.ts +550 -550
- package/src/tools/tools.test.ts +222 -222
- package/src/tools/types.ts +9 -9
- package/src/tools/validation.ts +75 -75
- package/src/tools/version.ts +34 -34
- package/src/tools/worktrees.ts +66 -66
- package/src/tools.test.ts +416 -416
- package/src/utils.test.ts +1014 -1014
- package/src/utils.ts +586 -586
- package/src/validators.test.ts +223 -223
- package/src/validators.ts +249 -249
- package/src/version.ts +109 -109
- package/tsconfig.json +16 -16
- package/vitest.config.ts +14 -14
package/src/setup.test.ts
CHANGED
|
@@ -1,233 +1,233 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
// Mock node:fs before importing setup module
|
|
4
|
-
vi.mock('node:fs', () => ({
|
|
5
|
-
existsSync: vi.fn(),
|
|
6
|
-
readFileSync: vi.fn(),
|
|
7
|
-
writeFileSync: vi.fn(),
|
|
8
|
-
mkdirSync: vi.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
// Mock node:os
|
|
12
|
-
vi.mock('node:os', () => ({
|
|
13
|
-
homedir: vi.fn(() => '/home/testuser'),
|
|
14
|
-
platform: vi.fn(() => 'darwin'),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
import { existsSync } from 'node:fs';
|
|
18
|
-
import { homedir, platform } from 'node:os';
|
|
19
|
-
import { detectIdes, generateMcpConfig, type IdeConfig } from './setup.js';
|
|
20
|
-
|
|
21
|
-
describe('Setup module', () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
vi.clearAllMocks();
|
|
24
|
-
vi.mocked(homedir).mockReturnValue('/home/testuser');
|
|
25
|
-
vi.mocked(platform).mockReturnValue('darwin');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('detectIdes', () => {
|
|
29
|
-
it('should always include Claude Code as detected', () => {
|
|
30
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
31
|
-
const ides = detectIdes();
|
|
32
|
-
const claudeCode = ides.find(ide => ide.name === 'claude-code');
|
|
33
|
-
|
|
34
|
-
expect(claudeCode).toBeDefined();
|
|
35
|
-
expect(claudeCode?.detected).toBe(true);
|
|
36
|
-
expect(claudeCode?.configPath).toBe('.mcp.json');
|
|
37
|
-
expect(claudeCode?.configFormat).toBe('mcp-json');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should always include Gemini CLI as detected', () => {
|
|
41
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
42
|
-
const ides = detectIdes();
|
|
43
|
-
const gemini = ides.find(ide => ide.name === 'gemini');
|
|
44
|
-
|
|
45
|
-
expect(gemini).toBeDefined();
|
|
46
|
-
expect(gemini?.detected).toBe(true);
|
|
47
|
-
expect(gemini?.configFormat).toBe('settings-json');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should detect Claude Desktop when directory exists', () => {
|
|
51
|
-
vi.mocked(existsSync).mockImplementation((path: unknown) => {
|
|
52
|
-
if (typeof path === 'string' && path.includes('Claude')) {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const ides = detectIdes();
|
|
59
|
-
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
60
|
-
|
|
61
|
-
expect(claudeDesktop).toBeDefined();
|
|
62
|
-
expect(claudeDesktop?.detected).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should not detect Claude Desktop when directory does not exist', () => {
|
|
66
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
67
|
-
|
|
68
|
-
const ides = detectIdes();
|
|
69
|
-
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
70
|
-
|
|
71
|
-
expect(claudeDesktop).toBeDefined();
|
|
72
|
-
expect(claudeDesktop?.detected).toBe(false);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should detect Cursor when directory exists', () => {
|
|
76
|
-
vi.mocked(existsSync).mockImplementation((path: unknown) => {
|
|
77
|
-
if (typeof path === 'string' && path.includes('Cursor')) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
return false;
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const ides = detectIdes();
|
|
84
|
-
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
85
|
-
|
|
86
|
-
expect(cursor).toBeDefined();
|
|
87
|
-
expect(cursor?.detected).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should return all four IDE configs', () => {
|
|
91
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
92
|
-
const ides = detectIdes();
|
|
93
|
-
|
|
94
|
-
expect(ides).toHaveLength(4);
|
|
95
|
-
expect(ides.map(ide => ide.name)).toEqual([
|
|
96
|
-
'claude-code',
|
|
97
|
-
'claude-desktop',
|
|
98
|
-
'cursor',
|
|
99
|
-
'gemini',
|
|
100
|
-
]);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should use correct config paths on macOS', () => {
|
|
104
|
-
vi.mocked(platform).mockReturnValue('darwin');
|
|
105
|
-
vi.mocked(homedir).mockReturnValue('/Users/testuser');
|
|
106
|
-
vi.mocked(existsSync).mockReturnValue(true);
|
|
107
|
-
|
|
108
|
-
const ides = detectIdes();
|
|
109
|
-
|
|
110
|
-
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
111
|
-
// Note: path.join uses OS separator, so we check for path components
|
|
112
|
-
expect(claudeDesktop?.configPath).toMatch(/Library.*Application Support.*Claude/);
|
|
113
|
-
|
|
114
|
-
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
115
|
-
expect(cursor?.configPath).toMatch(/Library.*Application Support.*Cursor/);
|
|
116
|
-
|
|
117
|
-
const gemini = ides.find(ide => ide.name === 'gemini');
|
|
118
|
-
expect(gemini?.configPath).toMatch(/Users.*testuser.*\.gemini.*settings\.json/);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should use correct config paths on Windows', () => {
|
|
122
|
-
vi.mocked(platform).mockReturnValue('win32');
|
|
123
|
-
vi.mocked(homedir).mockReturnValue('C:\\Users\\testuser');
|
|
124
|
-
vi.mocked(existsSync).mockReturnValue(true);
|
|
125
|
-
|
|
126
|
-
const ides = detectIdes();
|
|
127
|
-
|
|
128
|
-
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
129
|
-
// Note: path.join uses OS separator, so we check for path components
|
|
130
|
-
expect(claudeDesktop?.configPath).toMatch(/AppData.*Roaming.*Claude/);
|
|
131
|
-
|
|
132
|
-
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
133
|
-
expect(cursor?.configPath).toMatch(/AppData.*Roaming.*Cursor/);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('should use correct config paths on Linux', () => {
|
|
137
|
-
vi.mocked(platform).mockReturnValue('linux');
|
|
138
|
-
vi.mocked(homedir).mockReturnValue('/home/testuser');
|
|
139
|
-
vi.mocked(existsSync).mockReturnValue(true);
|
|
140
|
-
|
|
141
|
-
const ides = detectIdes();
|
|
142
|
-
|
|
143
|
-
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
144
|
-
// Note: path.join uses OS separator, so we check for path components
|
|
145
|
-
expect(claudeDesktop?.configPath).toMatch(/\.config.*Claude/);
|
|
146
|
-
|
|
147
|
-
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
148
|
-
expect(cursor?.configPath).toMatch(/\.config.*Cursor/);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
describe('IdeConfig types', () => {
|
|
153
|
-
it('should have correct config format for MCP-based IDEs', () => {
|
|
154
|
-
vi.mocked(existsSync).mockReturnValue(true);
|
|
155
|
-
const ides = detectIdes();
|
|
156
|
-
|
|
157
|
-
const claudeCode = ides.find(ide => ide.name === 'claude-code');
|
|
158
|
-
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
159
|
-
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
160
|
-
|
|
161
|
-
expect(claudeCode?.configFormat).toBe('mcp-json');
|
|
162
|
-
expect(claudeDesktop?.configFormat).toBe('mcp-json');
|
|
163
|
-
expect(cursor?.configFormat).toBe('mcp-json');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should have correct config format for Gemini CLI', () => {
|
|
167
|
-
vi.mocked(existsSync).mockReturnValue(true);
|
|
168
|
-
const ides = detectIdes();
|
|
169
|
-
|
|
170
|
-
const gemini = ides.find(ide => ide.name === 'gemini');
|
|
171
|
-
expect(gemini?.configFormat).toBe('settings-json');
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe('generateMcpConfig', () => {
|
|
176
|
-
it('should generate correct config structure for standard MCP IDEs', () => {
|
|
177
|
-
const claudeCodeIde: IdeConfig = {
|
|
178
|
-
name: 'claude-code',
|
|
179
|
-
displayName: 'Claude Code (CLI)',
|
|
180
|
-
configPath: '.mcp.json',
|
|
181
|
-
detected: true,
|
|
182
|
-
configFormat: 'mcp-json',
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const config = generateMcpConfig('test-api-key', claudeCodeIde);
|
|
186
|
-
const mcpServers = config.mcpServers as Record<string, unknown>;
|
|
187
|
-
const vibescope = mcpServers.vibescope as Record<string, unknown>;
|
|
188
|
-
|
|
189
|
-
expect(vibescope.command).toBe('npx');
|
|
190
|
-
expect(vibescope.args).toContain('@vibescope/mcp-server@latest');
|
|
191
|
-
expect((vibescope.env as Record<string, string>).VIBESCOPE_API_KEY).toBe('test-api-key');
|
|
192
|
-
// Standard MCP config should NOT have timeout/trust
|
|
193
|
-
expect(vibescope.timeout).toBeUndefined();
|
|
194
|
-
expect(vibescope.trust).toBeUndefined();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should include timeout and trust for Gemini CLI config', () => {
|
|
198
|
-
const geminiIde: IdeConfig = {
|
|
199
|
-
name: 'gemini',
|
|
200
|
-
displayName: 'Gemini CLI',
|
|
201
|
-
configPath: '~/.gemini/settings.json',
|
|
202
|
-
detected: true,
|
|
203
|
-
configFormat: 'settings-json',
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const config = generateMcpConfig('test-api-key', geminiIde);
|
|
207
|
-
const mcpServers = config.mcpServers as Record<string, unknown>;
|
|
208
|
-
const vibescope = mcpServers.vibescope as Record<string, unknown>;
|
|
209
|
-
|
|
210
|
-
expect(vibescope.command).toBe('npx');
|
|
211
|
-
expect(vibescope.args).toContain('@vibescope/mcp-server@latest');
|
|
212
|
-
expect((vibescope.env as Record<string, string>).VIBESCOPE_API_KEY).toBe('test-api-key');
|
|
213
|
-
expect(vibescope.timeout).toBe(30000);
|
|
214
|
-
expect(vibescope.trust).toBe(true);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should use correct npx args format', () => {
|
|
218
|
-
const ide: IdeConfig = {
|
|
219
|
-
name: 'claude-code',
|
|
220
|
-
displayName: 'Claude Code (CLI)',
|
|
221
|
-
configPath: '.mcp.json',
|
|
222
|
-
detected: true,
|
|
223
|
-
configFormat: 'mcp-json',
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const config = generateMcpConfig('my-key', ide);
|
|
227
|
-
const mcpServers = config.mcpServers as Record<string, unknown>;
|
|
228
|
-
const vibescope = mcpServers.vibescope as Record<string, unknown>;
|
|
229
|
-
|
|
230
|
-
expect(vibescope.args).toEqual(['-y', '-p', '@vibescope/mcp-server@latest', 'vibescope-mcp']);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock node:fs before importing setup module
|
|
4
|
+
vi.mock('node:fs', () => ({
|
|
5
|
+
existsSync: vi.fn(),
|
|
6
|
+
readFileSync: vi.fn(),
|
|
7
|
+
writeFileSync: vi.fn(),
|
|
8
|
+
mkdirSync: vi.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
// Mock node:os
|
|
12
|
+
vi.mock('node:os', () => ({
|
|
13
|
+
homedir: vi.fn(() => '/home/testuser'),
|
|
14
|
+
platform: vi.fn(() => 'darwin'),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
import { existsSync } from 'node:fs';
|
|
18
|
+
import { homedir, platform } from 'node:os';
|
|
19
|
+
import { detectIdes, generateMcpConfig, type IdeConfig } from './setup.js';
|
|
20
|
+
|
|
21
|
+
describe('Setup module', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
vi.clearAllMocks();
|
|
24
|
+
vi.mocked(homedir).mockReturnValue('/home/testuser');
|
|
25
|
+
vi.mocked(platform).mockReturnValue('darwin');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('detectIdes', () => {
|
|
29
|
+
it('should always include Claude Code as detected', () => {
|
|
30
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
31
|
+
const ides = detectIdes();
|
|
32
|
+
const claudeCode = ides.find(ide => ide.name === 'claude-code');
|
|
33
|
+
|
|
34
|
+
expect(claudeCode).toBeDefined();
|
|
35
|
+
expect(claudeCode?.detected).toBe(true);
|
|
36
|
+
expect(claudeCode?.configPath).toBe('.mcp.json');
|
|
37
|
+
expect(claudeCode?.configFormat).toBe('mcp-json');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should always include Gemini CLI as detected', () => {
|
|
41
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
42
|
+
const ides = detectIdes();
|
|
43
|
+
const gemini = ides.find(ide => ide.name === 'gemini');
|
|
44
|
+
|
|
45
|
+
expect(gemini).toBeDefined();
|
|
46
|
+
expect(gemini?.detected).toBe(true);
|
|
47
|
+
expect(gemini?.configFormat).toBe('settings-json');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should detect Claude Desktop when directory exists', () => {
|
|
51
|
+
vi.mocked(existsSync).mockImplementation((path: unknown) => {
|
|
52
|
+
if (typeof path === 'string' && path.includes('Claude')) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const ides = detectIdes();
|
|
59
|
+
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
60
|
+
|
|
61
|
+
expect(claudeDesktop).toBeDefined();
|
|
62
|
+
expect(claudeDesktop?.detected).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should not detect Claude Desktop when directory does not exist', () => {
|
|
66
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
67
|
+
|
|
68
|
+
const ides = detectIdes();
|
|
69
|
+
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
70
|
+
|
|
71
|
+
expect(claudeDesktop).toBeDefined();
|
|
72
|
+
expect(claudeDesktop?.detected).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should detect Cursor when directory exists', () => {
|
|
76
|
+
vi.mocked(existsSync).mockImplementation((path: unknown) => {
|
|
77
|
+
if (typeof path === 'string' && path.includes('Cursor')) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const ides = detectIdes();
|
|
84
|
+
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
85
|
+
|
|
86
|
+
expect(cursor).toBeDefined();
|
|
87
|
+
expect(cursor?.detected).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should return all four IDE configs', () => {
|
|
91
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
92
|
+
const ides = detectIdes();
|
|
93
|
+
|
|
94
|
+
expect(ides).toHaveLength(4);
|
|
95
|
+
expect(ides.map(ide => ide.name)).toEqual([
|
|
96
|
+
'claude-code',
|
|
97
|
+
'claude-desktop',
|
|
98
|
+
'cursor',
|
|
99
|
+
'gemini',
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should use correct config paths on macOS', () => {
|
|
104
|
+
vi.mocked(platform).mockReturnValue('darwin');
|
|
105
|
+
vi.mocked(homedir).mockReturnValue('/Users/testuser');
|
|
106
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
107
|
+
|
|
108
|
+
const ides = detectIdes();
|
|
109
|
+
|
|
110
|
+
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
111
|
+
// Note: path.join uses OS separator, so we check for path components
|
|
112
|
+
expect(claudeDesktop?.configPath).toMatch(/Library.*Application Support.*Claude/);
|
|
113
|
+
|
|
114
|
+
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
115
|
+
expect(cursor?.configPath).toMatch(/Library.*Application Support.*Cursor/);
|
|
116
|
+
|
|
117
|
+
const gemini = ides.find(ide => ide.name === 'gemini');
|
|
118
|
+
expect(gemini?.configPath).toMatch(/Users.*testuser.*\.gemini.*settings\.json/);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should use correct config paths on Windows', () => {
|
|
122
|
+
vi.mocked(platform).mockReturnValue('win32');
|
|
123
|
+
vi.mocked(homedir).mockReturnValue('C:\\Users\\testuser');
|
|
124
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
125
|
+
|
|
126
|
+
const ides = detectIdes();
|
|
127
|
+
|
|
128
|
+
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
129
|
+
// Note: path.join uses OS separator, so we check for path components
|
|
130
|
+
expect(claudeDesktop?.configPath).toMatch(/AppData.*Roaming.*Claude/);
|
|
131
|
+
|
|
132
|
+
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
133
|
+
expect(cursor?.configPath).toMatch(/AppData.*Roaming.*Cursor/);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should use correct config paths on Linux', () => {
|
|
137
|
+
vi.mocked(platform).mockReturnValue('linux');
|
|
138
|
+
vi.mocked(homedir).mockReturnValue('/home/testuser');
|
|
139
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
140
|
+
|
|
141
|
+
const ides = detectIdes();
|
|
142
|
+
|
|
143
|
+
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
144
|
+
// Note: path.join uses OS separator, so we check for path components
|
|
145
|
+
expect(claudeDesktop?.configPath).toMatch(/\.config.*Claude/);
|
|
146
|
+
|
|
147
|
+
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
148
|
+
expect(cursor?.configPath).toMatch(/\.config.*Cursor/);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('IdeConfig types', () => {
|
|
153
|
+
it('should have correct config format for MCP-based IDEs', () => {
|
|
154
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
155
|
+
const ides = detectIdes();
|
|
156
|
+
|
|
157
|
+
const claudeCode = ides.find(ide => ide.name === 'claude-code');
|
|
158
|
+
const claudeDesktop = ides.find(ide => ide.name === 'claude-desktop');
|
|
159
|
+
const cursor = ides.find(ide => ide.name === 'cursor');
|
|
160
|
+
|
|
161
|
+
expect(claudeCode?.configFormat).toBe('mcp-json');
|
|
162
|
+
expect(claudeDesktop?.configFormat).toBe('mcp-json');
|
|
163
|
+
expect(cursor?.configFormat).toBe('mcp-json');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should have correct config format for Gemini CLI', () => {
|
|
167
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
168
|
+
const ides = detectIdes();
|
|
169
|
+
|
|
170
|
+
const gemini = ides.find(ide => ide.name === 'gemini');
|
|
171
|
+
expect(gemini?.configFormat).toBe('settings-json');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('generateMcpConfig', () => {
|
|
176
|
+
it('should generate correct config structure for standard MCP IDEs', () => {
|
|
177
|
+
const claudeCodeIde: IdeConfig = {
|
|
178
|
+
name: 'claude-code',
|
|
179
|
+
displayName: 'Claude Code (CLI)',
|
|
180
|
+
configPath: '.mcp.json',
|
|
181
|
+
detected: true,
|
|
182
|
+
configFormat: 'mcp-json',
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const config = generateMcpConfig('test-api-key', claudeCodeIde);
|
|
186
|
+
const mcpServers = config.mcpServers as Record<string, unknown>;
|
|
187
|
+
const vibescope = mcpServers.vibescope as Record<string, unknown>;
|
|
188
|
+
|
|
189
|
+
expect(vibescope.command).toBe('npx');
|
|
190
|
+
expect(vibescope.args).toContain('@vibescope/mcp-server@latest');
|
|
191
|
+
expect((vibescope.env as Record<string, string>).VIBESCOPE_API_KEY).toBe('test-api-key');
|
|
192
|
+
// Standard MCP config should NOT have timeout/trust
|
|
193
|
+
expect(vibescope.timeout).toBeUndefined();
|
|
194
|
+
expect(vibescope.trust).toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should include timeout and trust for Gemini CLI config', () => {
|
|
198
|
+
const geminiIde: IdeConfig = {
|
|
199
|
+
name: 'gemini',
|
|
200
|
+
displayName: 'Gemini CLI',
|
|
201
|
+
configPath: '~/.gemini/settings.json',
|
|
202
|
+
detected: true,
|
|
203
|
+
configFormat: 'settings-json',
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const config = generateMcpConfig('test-api-key', geminiIde);
|
|
207
|
+
const mcpServers = config.mcpServers as Record<string, unknown>;
|
|
208
|
+
const vibescope = mcpServers.vibescope as Record<string, unknown>;
|
|
209
|
+
|
|
210
|
+
expect(vibescope.command).toBe('npx');
|
|
211
|
+
expect(vibescope.args).toContain('@vibescope/mcp-server@latest');
|
|
212
|
+
expect((vibescope.env as Record<string, string>).VIBESCOPE_API_KEY).toBe('test-api-key');
|
|
213
|
+
expect(vibescope.timeout).toBe(30000);
|
|
214
|
+
expect(vibescope.trust).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should use correct npx args format', () => {
|
|
218
|
+
const ide: IdeConfig = {
|
|
219
|
+
name: 'claude-code',
|
|
220
|
+
displayName: 'Claude Code (CLI)',
|
|
221
|
+
configPath: '.mcp.json',
|
|
222
|
+
detected: true,
|
|
223
|
+
configFormat: 'mcp-json',
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const config = generateMcpConfig('my-key', ide);
|
|
227
|
+
const mcpServers = config.mcpServers as Record<string, unknown>;
|
|
228
|
+
const vibescope = mcpServers.vibescope as Record<string, unknown>;
|
|
229
|
+
|
|
230
|
+
expect(vibescope.args).toEqual(['-y', '-p', '@vibescope/mcp-server@latest', 'vibescope-mcp']);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|