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.
- package/.claude/CLAUDE.md +33 -0
- package/.claude/settings.local.json +32 -0
- package/.github/WORKFLOWS.md +147 -0
- package/.github/workflows/ci.yml +49 -0
- package/.github/workflows/publish.yml +109 -0
- package/.idea/embark-remote-mcp.iml +9 -0
- package/.idea/encodings.xml +4 -0
- package/.idea/indexLayout.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/.mcp.json +14 -0
- package/GIT_DISCOVERY.md +231 -0
- package/INTEGRATION_TESTING.md +243 -0
- package/MULTI_REPOSITORY_SEARCH.md +242 -0
- package/README.md +434 -0
- package/dist/auth/auth-helper.d.ts +3 -0
- package/dist/auth/auth-helper.d.ts.map +1 -0
- package/dist/auth/auth-helper.js +171 -0
- package/dist/auth/auth-helper.js.map +1 -0
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +24 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jba-login.d.ts +17 -0
- package/dist/auth/jba-login.d.ts.map +1 -0
- package/dist/auth/jba-login.js +345 -0
- package/dist/auth/jba-login.js.map +1 -0
- package/dist/auth/types.d.ts +16 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +3 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +54 -0
- package/dist/config.js.map +1 -0
- package/dist/embark-client.d.ts +56 -0
- package/dist/embark-client.d.ts.map +1 -0
- package/dist/embark-client.js +543 -0
- package/dist/embark-client.js.map +1 -0
- package/dist/git-utils.d.ts +47 -0
- package/dist/git-utils.d.ts.map +1 -0
- package/dist/git-utils.js +232 -0
- package/dist/git-utils.js.map +1 -0
- package/dist/handlers.d.ts +80 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +301 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +92 -0
- package/dist/logger.js.map +1 -0
- package/dist/stats-server.d.ts +3 -0
- package/dist/stats-server.d.ts.map +1 -0
- package/dist/stats-server.js +623 -0
- package/dist/stats-server.js.map +1 -0
- package/dist/stats.d.ts +118 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +206 -0
- package/dist/stats.js.map +1 -0
- package/dist/tools.d.ts +9 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +62 -0
- package/dist/tools.js.map +1 -0
- package/package.json +47 -0
- package/test-git-discovery.mjs +322 -0
- package/test-multi-repo-filters.mjs +151 -0
- package/test-multiple-roots.mjs +436 -0
- package/test-roots.mjs +306 -0
- package/test-snippet-extraction.mjs +136 -0
- package/watch-logs.sh +78 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test script for Git repository discovery from roots
|
|
5
|
+
*
|
|
6
|
+
* This script tests that the server can:
|
|
7
|
+
* 1. Accept roots from a client (simulated)
|
|
8
|
+
* 2. Discover Git remote URLs from those directories
|
|
9
|
+
* 3. Use the discovered URLs for searches
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawn } from 'child_process';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { dirname, join } from 'path';
|
|
15
|
+
import { execSync } from 'child_process';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
|
|
20
|
+
// Configuration
|
|
21
|
+
const SERVER_PATH = join(__dirname, 'dist', 'index.js');
|
|
22
|
+
const TEST_ROOT = process.cwd(); // Use current directory as test root
|
|
23
|
+
|
|
24
|
+
// Colors for terminal output
|
|
25
|
+
const colors = {
|
|
26
|
+
reset: '\x1b[0m',
|
|
27
|
+
green: '\x1b[32m',
|
|
28
|
+
red: '\x1b[31m',
|
|
29
|
+
yellow: '\x1b[33m',
|
|
30
|
+
blue: '\x1b[34m',
|
|
31
|
+
gray: '\x1b[90m',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function log(message, color = colors.reset) {
|
|
35
|
+
console.log(`${color}${message}${colors.reset}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function logSuccess(message) {
|
|
39
|
+
log(`✓ ${message}`, colors.green);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function logError(message) {
|
|
43
|
+
log(`✗ ${message}`, colors.red);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function logInfo(message) {
|
|
47
|
+
log(`ℹ ${message}`, colors.blue);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function logDebug(message) {
|
|
51
|
+
log(` ${message}`, colors.gray);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get Git remote URL from current directory
|
|
56
|
+
*/
|
|
57
|
+
function getLocalGitRemote() {
|
|
58
|
+
try {
|
|
59
|
+
const result = execSync('git remote get-url origin', {
|
|
60
|
+
cwd: TEST_ROOT,
|
|
61
|
+
encoding: 'utf-8',
|
|
62
|
+
});
|
|
63
|
+
return result.trim();
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Send a JSON-RPC request to the server
|
|
71
|
+
*/
|
|
72
|
+
function sendRequest(server, request) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const requestStr = JSON.stringify(request) + '\n';
|
|
75
|
+
logDebug(`→ ${requestStr.trim()}`);
|
|
76
|
+
|
|
77
|
+
let responseBuffer = '';
|
|
78
|
+
let resolved = false;
|
|
79
|
+
|
|
80
|
+
const onData = (data) => {
|
|
81
|
+
responseBuffer += data.toString();
|
|
82
|
+
|
|
83
|
+
// Try to parse complete JSON objects
|
|
84
|
+
const lines = responseBuffer.split('\n');
|
|
85
|
+
responseBuffer = lines.pop(); // Keep incomplete line in buffer
|
|
86
|
+
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
if (line.trim()) {
|
|
89
|
+
try {
|
|
90
|
+
const response = JSON.parse(line);
|
|
91
|
+
logDebug(`← ${line}`);
|
|
92
|
+
|
|
93
|
+
// Check if this is the response to our request
|
|
94
|
+
if (response.id === request.id) {
|
|
95
|
+
resolved = true;
|
|
96
|
+
server.stdout.off('data', onData);
|
|
97
|
+
resolve(response);
|
|
98
|
+
} else if (response.method === 'roots/list') {
|
|
99
|
+
// Server is requesting roots from us
|
|
100
|
+
logInfo('Server requested roots from client');
|
|
101
|
+
const rootsResponse = {
|
|
102
|
+
jsonrpc: '2.0',
|
|
103
|
+
id: response.id,
|
|
104
|
+
result: {
|
|
105
|
+
roots: [
|
|
106
|
+
{
|
|
107
|
+
uri: `file://${TEST_ROOT}`,
|
|
108
|
+
name: 'Test Project'
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const responseStr = JSON.stringify(rootsResponse) + '\n';
|
|
114
|
+
logDebug(`→ ${responseStr.trim()}`);
|
|
115
|
+
server.stdin.write(responseStr);
|
|
116
|
+
}
|
|
117
|
+
} catch (e) {
|
|
118
|
+
// Not valid JSON, continue accumulating
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
server.stdout.on('data', onData);
|
|
125
|
+
|
|
126
|
+
// Timeout after 10 seconds
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
if (!resolved) {
|
|
129
|
+
server.stdout.off('data', onData);
|
|
130
|
+
reject(new Error('Request timeout'));
|
|
131
|
+
}
|
|
132
|
+
}, 10000);
|
|
133
|
+
|
|
134
|
+
server.stdin.write(requestStr);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Test server initialization
|
|
140
|
+
*/
|
|
141
|
+
async function testInitialize(server) {
|
|
142
|
+
log('\n--- Testing initialization ---', colors.yellow);
|
|
143
|
+
|
|
144
|
+
const request = {
|
|
145
|
+
jsonrpc: '2.0',
|
|
146
|
+
id: 1,
|
|
147
|
+
method: 'initialize',
|
|
148
|
+
params: {
|
|
149
|
+
protocolVersion: '2024-11-05',
|
|
150
|
+
capabilities: {
|
|
151
|
+
roots: {
|
|
152
|
+
listChanged: true,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
clientInfo: {
|
|
156
|
+
name: 'test-client',
|
|
157
|
+
version: '1.0.0',
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const response = await sendRequest(server, request);
|
|
164
|
+
|
|
165
|
+
if (response.error) {
|
|
166
|
+
logError(`Initialization error: ${JSON.stringify(response.error)}`);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!response.result) {
|
|
171
|
+
logError('Initialization response missing result');
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
logSuccess('Server initialized successfully');
|
|
176
|
+
return true;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
logError(`Failed to initialize: ${error.message}`);
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Test that Git discovery works by making a search
|
|
185
|
+
*/
|
|
186
|
+
async function testGitDiscovery(server, expectedGitUrl) {
|
|
187
|
+
log('\n--- Testing Git repository discovery ---', colors.yellow);
|
|
188
|
+
|
|
189
|
+
// Wait a bit for the server to process roots
|
|
190
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
191
|
+
|
|
192
|
+
// Make a search request without specifying repository
|
|
193
|
+
const request = {
|
|
194
|
+
jsonrpc: '2.0',
|
|
195
|
+
id: 3,
|
|
196
|
+
method: 'tools/call',
|
|
197
|
+
params: {
|
|
198
|
+
name: 'semantic_code_search',
|
|
199
|
+
arguments: {
|
|
200
|
+
text: 'test search query',
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const response = await sendRequest(server, request);
|
|
207
|
+
|
|
208
|
+
if (response.error) {
|
|
209
|
+
logError(`Search error: ${JSON.stringify(response.error)}`);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const content = response.result?.content?.[0]?.text || '';
|
|
214
|
+
|
|
215
|
+
// Check if the response indicates the discovered repository was used
|
|
216
|
+
if (content.includes(expectedGitUrl) || !content.includes('No repository URL configured')) {
|
|
217
|
+
logSuccess('Git repository was discovered and used for search');
|
|
218
|
+
logInfo(`Repository: ${expectedGitUrl}`);
|
|
219
|
+
return true;
|
|
220
|
+
} else {
|
|
221
|
+
logError('Search did not use discovered Git repository');
|
|
222
|
+
logDebug(`Response: ${content}`);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
logError(`Failed to test Git discovery: ${error.message}`);
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Main test runner
|
|
233
|
+
*/
|
|
234
|
+
async function runTests() {
|
|
235
|
+
log('='.repeat(60), colors.blue);
|
|
236
|
+
log('MCP Server Git Discovery Testing', colors.blue);
|
|
237
|
+
log('='.repeat(60), colors.blue);
|
|
238
|
+
|
|
239
|
+
logInfo(`Server path: ${SERVER_PATH}`);
|
|
240
|
+
logInfo(`Test root: ${TEST_ROOT}`);
|
|
241
|
+
|
|
242
|
+
// Check if current directory is a Git repository
|
|
243
|
+
const gitRemote = getLocalGitRemote();
|
|
244
|
+
if (!gitRemote) {
|
|
245
|
+
logError('Current directory is not a Git repository or has no remote');
|
|
246
|
+
log('Please run this test from a Git repository with a configured remote', colors.yellow);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
logInfo(`Expected Git remote: ${gitRemote}`);
|
|
251
|
+
|
|
252
|
+
// Spawn the server (without REPOSITORY_GIT_REMOTE_URL env var)
|
|
253
|
+
log('\n--- Starting MCP server ---', colors.yellow);
|
|
254
|
+
const server = spawn('node', [SERVER_PATH], {
|
|
255
|
+
env: {
|
|
256
|
+
...process.env,
|
|
257
|
+
ENABLE_LOCAL_LOGS: 'true',
|
|
258
|
+
// Intentionally NOT setting REPOSITORY_GIT_REMOTE_URL
|
|
259
|
+
},
|
|
260
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Log stderr for debugging
|
|
264
|
+
server.stderr.on('data', (data) => {
|
|
265
|
+
logDebug(`[stderr] ${data.toString().trim()}`);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
server.on('error', (error) => {
|
|
269
|
+
logError(`Failed to start server: ${error.message}`);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Wait a bit for server to start
|
|
274
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
275
|
+
|
|
276
|
+
let allTestsPassed = true;
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
// Test 1: Initialize
|
|
280
|
+
const initSuccess = await testInitialize(server);
|
|
281
|
+
if (!initSuccess) {
|
|
282
|
+
allTestsPassed = false;
|
|
283
|
+
log('\n❌ Initialization test failed', colors.red);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Test 2: Git discovery
|
|
287
|
+
if (initSuccess) {
|
|
288
|
+
const discoverySuccess = await testGitDiscovery(server, gitRemote);
|
|
289
|
+
if (!discoverySuccess) {
|
|
290
|
+
allTestsPassed = false;
|
|
291
|
+
log('\n❌ Git discovery test failed', colors.red);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Summary
|
|
296
|
+
log('\n' + '='.repeat(60), colors.blue);
|
|
297
|
+
if (allTestsPassed) {
|
|
298
|
+
log('✓ All tests passed!', colors.green);
|
|
299
|
+
log('✓ Server successfully discovered Git repository from roots', colors.green);
|
|
300
|
+
log('='.repeat(60), colors.blue);
|
|
301
|
+
server.kill();
|
|
302
|
+
process.exit(0);
|
|
303
|
+
} else {
|
|
304
|
+
log('✗ Some tests failed', colors.red);
|
|
305
|
+
log('='.repeat(60), colors.blue);
|
|
306
|
+
server.kill();
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
} catch (error) {
|
|
310
|
+
logError(`Test execution failed: ${error.message}`);
|
|
311
|
+
console.error(error);
|
|
312
|
+
server.kill();
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Run tests
|
|
318
|
+
runTests().catch((error) => {
|
|
319
|
+
logError(`Fatal error: ${error.message}`);
|
|
320
|
+
console.error(error);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test script for multi-repository filtering with environment variables
|
|
5
|
+
*
|
|
6
|
+
* This script tests the INCLUDE_REPOSITORY_URLS and EXCLUDE_REPOSITORY_URLS
|
|
7
|
+
* environment variable functionality.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { dirname, join } from 'path';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Colors for terminal output
|
|
17
|
+
const colors = {
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
green: '\x1b[32m',
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
yellow: '\x1b[33m',
|
|
22
|
+
blue: '\x1b[34m',
|
|
23
|
+
gray: '\x1b[90m',
|
|
24
|
+
cyan: '\x1b[36m',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function log(message, color = colors.reset) {
|
|
28
|
+
console.log(`${color}${message}${colors.reset}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function logSuccess(message) {
|
|
32
|
+
log(`✓ ${message}`, colors.green);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function logInfo(message) {
|
|
36
|
+
log(`ℹ ${message}`, colors.blue);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function logSection(message) {
|
|
40
|
+
log(`\n${'='.repeat(60)}`, colors.cyan);
|
|
41
|
+
log(message, colors.cyan);
|
|
42
|
+
log('='.repeat(60), colors.cyan);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Main test runner
|
|
47
|
+
*/
|
|
48
|
+
async function runTests() {
|
|
49
|
+
log('='.repeat(60), colors.blue);
|
|
50
|
+
log('Multi-Repository Filter Testing', colors.blue);
|
|
51
|
+
log('='.repeat(60), colors.blue);
|
|
52
|
+
|
|
53
|
+
logInfo('This test demonstrates the multi-repository search functionality');
|
|
54
|
+
logInfo('with INCLUDE_REPOSITORY_URLS and EXCLUDE_REPOSITORY_URLS environment variables\n');
|
|
55
|
+
|
|
56
|
+
// Test scenarios
|
|
57
|
+
const scenarios = [
|
|
58
|
+
{
|
|
59
|
+
name: 'No filters (search all discovered repositories)',
|
|
60
|
+
env: {},
|
|
61
|
+
description: 'When no filter env vars are set, all discovered repositories are searched',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'Include additions (add specific repositories to search)',
|
|
65
|
+
env: {
|
|
66
|
+
INCLUDE_REPOSITORY_URLS: 'https://github.com/owner/repo1.git,https://github.com/owner/repo2.git'
|
|
67
|
+
},
|
|
68
|
+
description: 'Discovered repositories plus the ones in INCLUDE_REPOSITORY_URLS are searched',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Exclude filter (skip specified repositories)',
|
|
72
|
+
env: {
|
|
73
|
+
EXCLUDE_REPOSITORY_URLS: 'https://github.com/owner/large-repo.git'
|
|
74
|
+
},
|
|
75
|
+
description: 'All repositories except those in EXCLUDE_REPOSITORY_URLS are searched',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Both filters (include then exclude)',
|
|
79
|
+
env: {
|
|
80
|
+
INCLUDE_REPOSITORY_URLS: 'https://github.com/owner/repo1.git,https://github.com/owner/repo2.git,https://github.com/owner/repo3.git',
|
|
81
|
+
EXCLUDE_REPOSITORY_URLS: 'https://github.com/owner/repo3.git'
|
|
82
|
+
},
|
|
83
|
+
description: 'Repositories from INCLUDE_REPOSITORY_URLS are added, then anything in EXCLUDE_REPOSITORY_URLS is removed',
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
scenarios.forEach((scenario, index) => {
|
|
88
|
+
logSection(`Scenario ${index + 1}: ${scenario.name}`);
|
|
89
|
+
logInfo(scenario.description);
|
|
90
|
+
|
|
91
|
+
if (Object.keys(scenario.env).length === 0) {
|
|
92
|
+
log('\n No environment variables set', colors.gray);
|
|
93
|
+
} else {
|
|
94
|
+
log('\n Environment variables:', colors.gray);
|
|
95
|
+
Object.entries(scenario.env).forEach(([key, value]) => {
|
|
96
|
+
log(` ${key}=${value}`, colors.gray);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
log('\n To test this scenario, run:', colors.yellow);
|
|
101
|
+
const envVars = Object.entries(scenario.env)
|
|
102
|
+
.map(([key, value]) => `${key}="${value}"`)
|
|
103
|
+
.join(' ');
|
|
104
|
+
const command = envVars
|
|
105
|
+
? ` ${envVars} node dist/index.js`
|
|
106
|
+
: ` node dist/index.js`;
|
|
107
|
+
log(command, colors.yellow);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
logSection('Implementation Details');
|
|
111
|
+
|
|
112
|
+
logInfo('How it works:');
|
|
113
|
+
log(' 1. Server discovers all Git repositories from workspace roots', colors.gray);
|
|
114
|
+
log(' 2. Before each search, repositories are adjusted based on env vars:', colors.gray);
|
|
115
|
+
log(' - If INCLUDE_REPOSITORY_URLS is set: those repos are added to the list', colors.gray);
|
|
116
|
+
log(' - If EXCLUDE_REPOSITORY_URLS is set: those repos are removed', colors.gray);
|
|
117
|
+
log(' 3. If multiple repos remain after filtering: multi-repo search', colors.gray);
|
|
118
|
+
log(' 4. If only one repo remains: single-repo search (cleaner output)', colors.gray);
|
|
119
|
+
log(' 5. If no repos remain: error message', colors.gray);
|
|
120
|
+
|
|
121
|
+
logSection('Configuration Format');
|
|
122
|
+
|
|
123
|
+
log(' INCLUDE_REPOSITORY_URLS: Comma-separated list of Git URLs to include', colors.gray);
|
|
124
|
+
log(' Example: export INCLUDE_REPOSITORY_URLS="https://github.com/owner/repo1.git,https://github.com/owner/repo2.git"', colors.gray);
|
|
125
|
+
|
|
126
|
+
log('\n EXCLUDE_REPOSITORY_URLS: Comma-separated list of Git URLs to exclude', colors.gray);
|
|
127
|
+
log(' Example: export EXCLUDE_REPOSITORY_URLS="https://github.com/owner/large-repo.git"', colors.gray);
|
|
128
|
+
|
|
129
|
+
logSection('Backward Compatibility');
|
|
130
|
+
|
|
131
|
+
log(' ✓ Existing single-repository setups continue to work', colors.green);
|
|
132
|
+
log(' ✓ REPOSITORY_GIT_REMOTE_URL env var still supported as fallback', colors.green);
|
|
133
|
+
log(' ✓ No breaking changes to existing tool parameters', colors.green);
|
|
134
|
+
|
|
135
|
+
logSection('Summary');
|
|
136
|
+
|
|
137
|
+
logSuccess('Multi-repository search is now implemented!');
|
|
138
|
+
log(' • By default: searches all discovered repositories', colors.gray);
|
|
139
|
+
log(' • With INCLUDE_REPOSITORY_URLS: searches discovered repos plus specified URLs', colors.gray);
|
|
140
|
+
log(' • With EXCLUDE_REPOSITORY_URLS: searches all except specified repos', colors.gray);
|
|
141
|
+
log(' • Results are aggregated with clear repository labels', colors.gray);
|
|
142
|
+
log(' • Parallel search execution for better performance', colors.gray);
|
|
143
|
+
|
|
144
|
+
log('\n');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Run tests
|
|
148
|
+
runTests().catch((error) => {
|
|
149
|
+
console.error('Fatal error:', error);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
});
|