embark-cli 1.1.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.
Files changed (73) hide show
  1. package/.claude/CLAUDE.md +33 -0
  2. package/.claude/settings.local.json +32 -0
  3. package/.github/WORKFLOWS.md +147 -0
  4. package/.github/workflows/ci.yml +49 -0
  5. package/.github/workflows/publish.yml +109 -0
  6. package/.idea/embark-remote-mcp.iml +9 -0
  7. package/.idea/encodings.xml +4 -0
  8. package/.idea/indexLayout.xml +8 -0
  9. package/.idea/vcs.xml +6 -0
  10. package/.mcp.json +14 -0
  11. package/GIT_DISCOVERY.md +231 -0
  12. package/INTEGRATION_TESTING.md +243 -0
  13. package/MULTI_REPOSITORY_SEARCH.md +242 -0
  14. package/README.md +434 -0
  15. package/dist/auth/auth-helper.d.ts +3 -0
  16. package/dist/auth/auth-helper.d.ts.map +1 -0
  17. package/dist/auth/auth-helper.js +171 -0
  18. package/dist/auth/auth-helper.js.map +1 -0
  19. package/dist/auth/index.d.ts +4 -0
  20. package/dist/auth/index.d.ts.map +1 -0
  21. package/dist/auth/index.js +24 -0
  22. package/dist/auth/index.js.map +1 -0
  23. package/dist/auth/jba-login.d.ts +17 -0
  24. package/dist/auth/jba-login.d.ts.map +1 -0
  25. package/dist/auth/jba-login.js +345 -0
  26. package/dist/auth/jba-login.js.map +1 -0
  27. package/dist/auth/types.d.ts +16 -0
  28. package/dist/auth/types.d.ts.map +1 -0
  29. package/dist/auth/types.js +3 -0
  30. package/dist/auth/types.js.map +1 -0
  31. package/dist/config.d.ts +26 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +54 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/embark-client.d.ts +56 -0
  36. package/dist/embark-client.d.ts.map +1 -0
  37. package/dist/embark-client.js +543 -0
  38. package/dist/embark-client.js.map +1 -0
  39. package/dist/git-utils.d.ts +47 -0
  40. package/dist/git-utils.d.ts.map +1 -0
  41. package/dist/git-utils.js +232 -0
  42. package/dist/git-utils.js.map +1 -0
  43. package/dist/handlers.d.ts +80 -0
  44. package/dist/handlers.d.ts.map +1 -0
  45. package/dist/handlers.js +301 -0
  46. package/dist/handlers.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +165 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/logger.d.ts +4 -0
  52. package/dist/logger.d.ts.map +1 -0
  53. package/dist/logger.js +92 -0
  54. package/dist/logger.js.map +1 -0
  55. package/dist/stats-server.d.ts +3 -0
  56. package/dist/stats-server.d.ts.map +1 -0
  57. package/dist/stats-server.js +623 -0
  58. package/dist/stats-server.js.map +1 -0
  59. package/dist/stats.d.ts +118 -0
  60. package/dist/stats.d.ts.map +1 -0
  61. package/dist/stats.js +206 -0
  62. package/dist/stats.js.map +1 -0
  63. package/dist/tools.d.ts +9 -0
  64. package/dist/tools.d.ts.map +1 -0
  65. package/dist/tools.js +62 -0
  66. package/dist/tools.js.map +1 -0
  67. package/package.json +47 -0
  68. package/test-git-discovery.mjs +322 -0
  69. package/test-multi-repo-filters.mjs +151 -0
  70. package/test-multiple-roots.mjs +436 -0
  71. package/test-roots.mjs +306 -0
  72. package/test-snippet-extraction.mjs +136 -0
  73. package/watch-logs.sh +78 -0
@@ -0,0 +1,436 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test script for multiple roots functionality
5
+ *
6
+ * This script tests whether the MCP server properly handles multiple root directories
7
+ * and allows searching across different repositories.
8
+ */
9
+
10
+ import { spawn } from 'child_process';
11
+ import { fileURLToPath } from 'url';
12
+ import { dirname, join } from 'path';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ // Configuration
18
+ const SERVER_PATH = join(__dirname, 'dist', 'index.js');
19
+
20
+ // Colors for terminal output
21
+ const colors = {
22
+ reset: '\x1b[0m',
23
+ green: '\x1b[32m',
24
+ red: '\x1b[31m',
25
+ yellow: '\x1b[33m',
26
+ blue: '\x1b[34m',
27
+ gray: '\x1b[90m',
28
+ };
29
+
30
+ function log(message, color = colors.reset) {
31
+ console.log(`${color}${message}${colors.reset}`);
32
+ }
33
+
34
+ function logSuccess(message) {
35
+ log(`✓ ${message}`, colors.green);
36
+ }
37
+
38
+ function logError(message) {
39
+ log(`✗ ${message}`, colors.red);
40
+ }
41
+
42
+ function logInfo(message) {
43
+ log(`ℹ ${message}`, colors.blue);
44
+ }
45
+
46
+ function logDebug(message) {
47
+ log(` ${message}`, colors.gray);
48
+ }
49
+
50
+ /**
51
+ * Send a JSON-RPC request to the server
52
+ */
53
+ function sendRequest(server, request) {
54
+ return new Promise((resolve, reject) => {
55
+ const requestStr = JSON.stringify(request) + '\n';
56
+ logDebug(`→ ${requestStr.trim()}`);
57
+
58
+ let responseBuffer = '';
59
+ let resolved = false;
60
+
61
+ const onData = (data) => {
62
+ responseBuffer += data.toString();
63
+
64
+ // Try to parse complete JSON objects
65
+ const lines = responseBuffer.split('\n');
66
+ responseBuffer = lines.pop(); // Keep incomplete line in buffer
67
+
68
+ for (const line of lines) {
69
+ if (line.trim()) {
70
+ try {
71
+ const response = JSON.parse(line);
72
+ logDebug(`← ${line}`);
73
+
74
+ // Check if this is the response to our request
75
+ if (response.id === request.id) {
76
+ resolved = true;
77
+ server.stdout.off('data', onData);
78
+ resolve(response);
79
+ }
80
+ } catch (e) {
81
+ // Not valid JSON, continue accumulating
82
+ }
83
+ }
84
+ }
85
+ };
86
+
87
+ server.stdout.on('data', onData);
88
+
89
+ // Timeout after 30 seconds (longer for search operations)
90
+ setTimeout(() => {
91
+ if (!resolved) {
92
+ server.stdout.off('data', onData);
93
+ reject(new Error('Request timeout'));
94
+ }
95
+ }, 30000);
96
+
97
+ server.stdin.write(requestStr);
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Test initialization with multiple roots
103
+ */
104
+ async function testInitializeWithRoots(server, roots) {
105
+ log('\n--- Testing initialization with multiple roots ---', colors.yellow);
106
+
107
+ const request = {
108
+ jsonrpc: '2.0',
109
+ id: 1,
110
+ method: 'initialize',
111
+ params: {
112
+ protocolVersion: '2024-11-05',
113
+ capabilities: {
114
+ roots: {
115
+ listChanged: true,
116
+ },
117
+ },
118
+ clientInfo: {
119
+ name: 'test-client',
120
+ version: '1.0.0',
121
+ },
122
+ },
123
+ };
124
+
125
+ try {
126
+ const response = await sendRequest(server, request);
127
+
128
+ if (response.error) {
129
+ logError(`Initialization error: ${JSON.stringify(response.error)}`);
130
+ return false;
131
+ }
132
+
133
+ if (!response.result) {
134
+ logError('Initialization response missing result');
135
+ return false;
136
+ }
137
+
138
+ logSuccess('Server initialized successfully');
139
+ return true;
140
+ } catch (error) {
141
+ logError(`Failed to initialize: ${error.message}`);
142
+ return false;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Send roots/list_changed notification
148
+ */
149
+ async function sendRootsNotification(server, roots) {
150
+ log('\n--- Sending roots/list_changed notification ---', colors.yellow);
151
+
152
+ logInfo(`Sending ${roots.length} roots:`);
153
+ roots.forEach((root, i) => {
154
+ logInfo(` ${i + 1}. ${root.uri}`);
155
+ });
156
+
157
+ const notification = {
158
+ jsonrpc: '2.0',
159
+ method: 'notifications/roots/list_changed',
160
+ };
161
+
162
+ const notificationStr = JSON.stringify(notification) + '\n';
163
+ logDebug(`→ ${notificationStr.trim()}`);
164
+
165
+ server.stdin.write(notificationStr);
166
+
167
+ // Wait a bit for processing
168
+ await new Promise(resolve => setTimeout(resolve, 500));
169
+
170
+ logSuccess('Notification sent');
171
+ return true;
172
+ }
173
+
174
+ /**
175
+ * Test tools/list
176
+ */
177
+ async function testListTools(server) {
178
+ log('\n--- Testing tools/list ---', colors.yellow);
179
+
180
+ const request = {
181
+ jsonrpc: '2.0',
182
+ id: 3,
183
+ method: 'tools/list',
184
+ };
185
+
186
+ try {
187
+ const response = await sendRequest(server, request);
188
+
189
+ if (response.error) {
190
+ logError(`Server returned error: ${JSON.stringify(response.error)}`);
191
+ return false;
192
+ }
193
+
194
+ if (!response.result || !response.result.tools) {
195
+ logError('Response missing tools');
196
+ return false;
197
+ }
198
+
199
+ logSuccess(`Found ${response.result.tools.length} tool(s)`);
200
+ response.result.tools.forEach(tool => {
201
+ logInfo(` - ${tool.name}`);
202
+ });
203
+
204
+ return true;
205
+ } catch (error) {
206
+ logError(`Failed to list tools: ${error.message}`);
207
+ return false;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Test semantic_code_search tool call across multiple repositories
213
+ */
214
+ async function testSemanticSearch(server) {
215
+ log('\n--- Testing semantic_code_search with multiple repositories ---', colors.yellow);
216
+
217
+ const request = {
218
+ jsonrpc: '2.0',
219
+ id: 4,
220
+ method: 'tools/call',
221
+ params: {
222
+ name: 'semantic_code_search',
223
+ arguments: {
224
+ text: 'function that handles logging or configuration',
225
+ },
226
+ },
227
+ };
228
+
229
+ try {
230
+ logInfo('Sending search request (this may take a few seconds)...');
231
+ const response = await sendRequest(server, request);
232
+
233
+ if (response.error) {
234
+ logError(`Server returned error: ${JSON.stringify(response.error)}`);
235
+ return false;
236
+ }
237
+
238
+ if (!response.result || !response.result.content) {
239
+ logError('Response missing content');
240
+ return false;
241
+ }
242
+
243
+ const content = response.result.content[0]?.text || '';
244
+
245
+ // Check if the response mentions multiple repositories
246
+ const hasMultipleRepos = content.includes('## Repository:');
247
+ const repoMatches = content.match(/## Repository:/g);
248
+ const repoCount = repoMatches ? repoMatches.length : 0;
249
+
250
+ if (hasMultipleRepos) {
251
+ logSuccess(`Search executed across ${repoCount} repository/repositories`);
252
+
253
+ // Extract repository names from the response
254
+ const repoNames = content.match(/## Repository: ([^\n]+)/g);
255
+ if (repoNames) {
256
+ repoNames.forEach((name, i) => {
257
+ logInfo(` ${i + 1}. ${name.replace('## Repository: ', '')}`);
258
+ });
259
+ }
260
+
261
+ if (repoCount > 1) {
262
+ logSuccess('Multi-repository search confirmed!');
263
+ } else {
264
+ logInfo('Only one repository was searched (this is expected if only one has a Git remote)');
265
+ }
266
+ } else {
267
+ // Single repository response format
268
+ if (content.includes('Found') && content.includes('results')) {
269
+ logSuccess('Search executed successfully (single repository)');
270
+ logInfo('Note: Only one repository was discovered/searched');
271
+ } else {
272
+ logError('Unexpected response format');
273
+ logDebug(content.substring(0, 200));
274
+ return false;
275
+ }
276
+ }
277
+
278
+ return true;
279
+ } catch (error) {
280
+ logError(`Failed to execute search: ${error.message}`);
281
+ return false;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Main test runner
287
+ */
288
+ async function runTests() {
289
+ log('='.repeat(60), colors.blue);
290
+ log('MCP Server Multiple Roots Testing', colors.blue);
291
+ log('='.repeat(60), colors.blue);
292
+
293
+ logInfo(`Server path: ${SERVER_PATH}`);
294
+
295
+ // Define test roots (multiple Git repositories)
296
+ const testRoots = [
297
+ {
298
+ uri: 'file:///Users/potomushto/Projects/temp/embark-mcp',
299
+ name: 'Embark MCP',
300
+ },
301
+ {
302
+ uri: 'file:///tmp/test-repo-1',
303
+ name: 'Test Repo 1',
304
+ },
305
+ {
306
+ uri: 'file:///tmp/test-repo-2',
307
+ name: 'Test Repo 2',
308
+ },
309
+ ];
310
+
311
+ // Spawn the server
312
+ log('\n--- Starting MCP server ---', colors.yellow);
313
+ const server = spawn('node', [SERVER_PATH], {
314
+ env: {
315
+ ...process.env,
316
+ // Don't set REPOSITORY_GIT_REMOTE_URL to test roots discovery
317
+ },
318
+ stdio: ['pipe', 'pipe', 'pipe'],
319
+ });
320
+
321
+ // Set up handler for incoming roots/list requests from the server
322
+ let responseBuffer = '';
323
+ server.stdout.on('data', (data) => {
324
+ responseBuffer += data.toString();
325
+ const lines = responseBuffer.split('\n');
326
+ responseBuffer = lines.pop(); // Keep incomplete line in buffer
327
+
328
+ for (const line of lines) {
329
+ if (line.trim()) {
330
+ try {
331
+ const message = JSON.parse(line);
332
+
333
+ // Handle roots/list request from server
334
+ if (message.method === 'roots/list' && message.id !== undefined) {
335
+ logDebug(`← Server requested roots/list (id: ${message.id})`);
336
+ const response = {
337
+ jsonrpc: '2.0',
338
+ id: message.id,
339
+ result: {
340
+ roots: testRoots
341
+ }
342
+ };
343
+ const responseStr = JSON.stringify(response) + '\n';
344
+ logDebug(`→ Responding with roots: ${responseStr.trim()}`);
345
+ server.stdin.write(responseStr);
346
+ }
347
+ } catch (e) {
348
+ // Not valid JSON, ignore
349
+ }
350
+ }
351
+ }
352
+ });
353
+
354
+ // Log stderr for debugging
355
+ server.stderr.on('data', (data) => {
356
+ logDebug(`[stderr] ${data.toString().trim()}`);
357
+ });
358
+
359
+ server.on('error', (error) => {
360
+ logError(`Failed to start server: ${error.message}`);
361
+ process.exit(1);
362
+ });
363
+
364
+ // Wait a bit for server to start
365
+ await new Promise(resolve => setTimeout(resolve, 500));
366
+
367
+ let allTestsPassed = true;
368
+
369
+ try {
370
+ // Test 1: Initialize with roots capability
371
+ const initSuccess = await testInitializeWithRoots(server, testRoots);
372
+ if (!initSuccess) {
373
+ allTestsPassed = false;
374
+ log('\n❌ Initialization test failed', colors.red);
375
+ }
376
+
377
+ // Test 2: Send roots notification
378
+ if (initSuccess) {
379
+ const rootsSuccess = await sendRootsNotification(server, testRoots);
380
+ if (!rootsSuccess) {
381
+ allTestsPassed = false;
382
+ log('\n❌ Roots notification test failed', colors.red);
383
+ }
384
+ }
385
+
386
+ // Test 3: List tools
387
+ if (initSuccess) {
388
+ const toolsSuccess = await testListTools(server);
389
+ if (!toolsSuccess) {
390
+ allTestsPassed = false;
391
+ log('\n❌ Tools list test failed', colors.red);
392
+ }
393
+ }
394
+
395
+ // Test 4: Execute semantic search to verify multi-repo functionality
396
+ if (initSuccess) {
397
+ const searchSuccess = await testSemanticSearch(server);
398
+ if (!searchSuccess) {
399
+ allTestsPassed = false;
400
+ log('\n❌ Semantic search test failed', colors.red);
401
+ }
402
+ }
403
+
404
+ // Summary
405
+ log('\n' + '='.repeat(60), colors.blue);
406
+ if (allTestsPassed) {
407
+ log('✓ All tests passed!', colors.green);
408
+ log('\nTest results:', colors.yellow);
409
+ log('✓ Server initialized with multiple roots capability', colors.green);
410
+ log('✓ Roots notification sent successfully', colors.green);
411
+ log('✓ Tools listed successfully', colors.green);
412
+ log('✓ Semantic search executed and verified', colors.green);
413
+ log('\nNote: Check logs for details on repository discovery', colors.yellow);
414
+ log('='.repeat(60), colors.blue);
415
+ server.kill();
416
+ process.exit(0);
417
+ } else {
418
+ log('✗ Some tests failed', colors.red);
419
+ log('='.repeat(60), colors.blue);
420
+ server.kill();
421
+ process.exit(1);
422
+ }
423
+ } catch (error) {
424
+ logError(`Test execution failed: ${error.message}`);
425
+ console.error(error);
426
+ server.kill();
427
+ process.exit(1);
428
+ }
429
+ }
430
+
431
+ // Run tests
432
+ runTests().catch((error) => {
433
+ logError(`Fatal error: ${error.message}`);
434
+ console.error(error);
435
+ process.exit(1);
436
+ });