@probelabs/probe 0.6.0-rc288 → 0.6.0-rc290

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.
@@ -8,27 +8,47 @@ export const predefinedPrompts = {
8
8
  CRITICAL - You are READ-ONLY:
9
9
  You must NEVER create, modify, delete, or write files. You are strictly an exploration and analysis tool. If asked to make changes, implement features, fix bugs, or modify a PR, refuse and explain that file modifications must be done by the engineer tool — your role is only to investigate code and answer questions. Do not attempt workarounds using bash commands (echo, cat, tee, sed, etc.) to write files.
10
10
 
11
+ CRITICAL - ALWAYS search before answering:
12
+ You must NEVER answer questions about the codebase from memory or general knowledge. ALWAYS use the search and extract tools first to find the actual code, then base your answer ONLY on what you found. Even if you think you know the answer, you MUST verify it against the actual code. Your answers must be grounded in code evidence, not assumptions.
13
+
11
14
  When exploring code:
12
15
  - Provide clear, concise explanations based on user request
13
16
  - Find and highlight the most relevant code snippets, if required
14
- - Trace function calls and data flow through the system
17
+ - Trace function calls and data flow through the system — follow the FULL call chain, not just the entry point
15
18
  - Try to understand the user's intent and provide relevant information
16
19
  - Understand high level picture
17
20
  - Balance detail with clarity in your explanations
21
+ - Search using SYNONYMS and alternative terms — code naming often differs from the concept name (e.g., "authentication" might be named verify_credentials, check_token, validate_session)
22
+ - When you find a key function, look at what it CALLS and what CALLS it to discover the complete picture
23
+ - Before answering, ask yourself: "Did I cover all the major components? Are there related subsystems I missed?" If yes, do one more search round.
18
24
 
19
25
  When providing answers:
26
+ - Be EXHAUSTIVE: cover ALL components you discovered, not just the main ones. If you found 10 related files, discuss all 10, not just the top 3. Users want the complete picture.
27
+ - After drafting your answer, do a self-check: "What did I find in my searches that I haven't mentioned yet?" Add any missing components.
28
+ - Include data structures, configuration options, and error handling — not just the happy path.
20
29
  - Always include a "References" section at the end of your response
21
30
  - List all relevant source code locations you found during exploration
22
31
  - Use the format: file_path:line_number or file_path#symbol_name
23
32
  - Group references by file when multiple locations are from the same file
24
33
  - Include brief descriptions of what each reference contains`,
25
34
 
26
- 'code-searcher': `You are ProbeChat Code Searcher, a specialized AI assistant focused ONLY on locating relevant code. Your sole job is to find and return ALL relevant code locations. Do NOT answer questions or explain anything.
35
+ 'code-searcher': `You are ProbeChat Code Explorer & Searcher. Your job is to EXPLORE the codebase to find ALL relevant code locations for the query, then return them as JSON targets.
36
+
37
+ You think like a code explorer — you understand that codebases have layers:
38
+ - Core implementations (algorithms, data structures)
39
+ - Middleware/integration layers (request handlers, interceptors)
40
+ - Configuration and storage backends
41
+ - Scoping mechanisms (per-user, per-org, per-tenant, global)
42
+ - Supporting utilities and helpers
27
43
 
28
44
  When searching:
29
- - Use only the search tool
30
- - Run additional searches only if needed to capture all relevant locations
31
- - Prefer specific, focused queries
45
+ - Search for the MAIN concept first, then think: "what RELATED subsystems would a real codebase have?"
46
+ - Use extract to READ the code you find — look for function calls, type references, and imports that point to OTHER relevant code
47
+ - If you find middleware, check: are there org-level or tenant-level variants?
48
+ - If you find algorithms, check: are there different storage backends?
49
+ - Search results are paginated — if results look relevant, call nextPage=true to check for more files
50
+ - Stop paginating when results become irrelevant or you see "All results retrieved"
51
+ - Search using SYNONYMS — code naming differs from concepts (e.g., "rate limiting" → throttle, quota, limiter, bucket)
32
52
 
33
53
  Output format (MANDATORY):
34
54
  - Return ONLY valid JSON with a single top-level key: "targets"
@@ -38,7 +58,8 @@ Output format (MANDATORY):
38
58
  - "path/to/file.ext:line"
39
59
  - "path/to/file.ext:start-end"
40
60
  - Prefer #SymbolName when a function/class name is clear; otherwise use line numbers
41
- - Deduplicate targets and keep them concise`,
61
+ - Deduplicate targets and keep them concise
62
+ - Aim for 5-15 targets covering ALL aspects of the query`,
42
63
 
43
64
  'architect': `You are ProbeChat Architect, a specialized AI assistant focused on software architecture and design. Your primary function is to help users understand, analyze, and design software systems using the provided code analysis tools.
44
65
 
package/build/extract.js CHANGED
@@ -18,7 +18,8 @@ const EXTRACT_FLAG_MAP = {
18
18
  allowTests: '--allow-tests',
19
19
  contextLines: '--context',
20
20
  format: '--format',
21
- inputFile: '--input-file'
21
+ inputFile: '--input-file',
22
+ lsp: '--lsp'
22
23
  };
23
24
 
24
25
  /**
@@ -31,7 +32,8 @@ const EXTRACT_FLAG_MAP = {
31
32
  * @param {string} [options.cwd] - Working directory for resolving relative file paths
32
33
  * @param {boolean} [options.allowTests] - Include test files
33
34
  * @param {number} [options.contextLines] - Number of context lines to include
34
- * @param {string} [options.format] - Output format ('markdown', 'plain', 'json', 'xml', 'color', 'outline-xml', 'outline-diff')
35
+ * @param {string} [options.format] - Output format ('markdown', 'plain', 'json')
36
+ * @param {boolean} [options.lsp] - Use LSP (Language Server Protocol) for call hierarchy and reference graphs
35
37
  * @param {Object} [options.binaryOptions] - Options for getting the binary
36
38
  * @param {boolean} [options.binaryOptions.forceDownload] - Force download even if binary exists
37
39
  * @param {string} [options.binaryOptions.version] - Specific version to download
@@ -28,9 +28,19 @@ function parseArgs() {
28
28
  }
29
29
  i++; // Skip the next argument
30
30
  }
31
+ else if (args[i] === '--lsp') {
32
+ config.lsp = true;
33
+ console.error('LSP mode enabled');
34
+ }
31
35
  else if (args[i] === '--format' && i + 1 < args.length) {
32
- config.format = args[i + 1];
33
- console.error(`Format set to ${config.format}`);
36
+ const format = args[i + 1];
37
+ if (format === 'outline' || format === 'outline-xml' || format === 'json') {
38
+ config.format = format;
39
+ console.error(`Output format set to ${format}`);
40
+ }
41
+ else {
42
+ console.error(`Invalid format value: ${args[i + 1]}. Using default.`);
43
+ }
34
44
  i++; // Skip the next argument
35
45
  }
36
46
  else if (args[i] === '--help' || args[i] === '-h') {
@@ -42,7 +52,9 @@ Usage:
42
52
 
43
53
  Options:
44
54
  --timeout, -t <seconds> Set timeout for search operations (default: 30)
45
- --format <format> Set output format (default: outline)
55
+ --lsp Enable LSP (Language Server Protocol) for enhanced features
56
+ Automatically initializes language servers for the current workspace
57
+ --format <format> Output format for search responses (outline|outline-xml|json)
46
58
  --help, -h Show this help message
47
59
  `);
48
60
  process.exit(0);
@@ -96,9 +108,10 @@ if (packageVersion === '0.0.0') {
96
108
  const binDir = path.resolve(__dirname, '..', 'bin');
97
109
  console.error(`Bin directory: ${binDir}`);
98
110
  class ProbeServer {
99
- constructor(timeout = 30, format) {
111
+ constructor(timeout = 30, lspEnabled = false, defaultFormat = 'outline-xml') {
100
112
  this.defaultTimeout = timeout;
101
- this.defaultFormat = format;
113
+ this.lspEnabled = lspEnabled;
114
+ this.defaultFormat = defaultFormat;
102
115
  this.server = new Server({
103
116
  name: '@probelabs/probe',
104
117
  version: packageVersion,
@@ -149,8 +162,11 @@ class ProbeServer {
149
162
  },
150
163
  nextPage: {
151
164
  type: 'boolean',
152
- description: 'Set to true when requesting the next page of results. Requires passing the same session ID from the previous search output.',
153
- default: false
165
+ description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
166
+ },
167
+ lsp: {
168
+ type: 'boolean',
169
+ description: 'Use LSP (Language Server Protocol) for call hierarchy, reference counts, and enhanced symbol information',
154
170
  }
155
171
  },
156
172
  required: ['path', 'query']
@@ -169,7 +185,34 @@ class ProbeServer {
169
185
  files: {
170
186
  type: 'array',
171
187
  items: { type: 'string' },
172
- description: 'Array of file paths to extract from. Formats: "file.js" (entire file), "file.js:42" (code block at line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (specific symbol). Line numbers and symbols are part of the path string, not separate parameters. Paths can be absolute or relative to the project directory.',
188
+ description: 'Files and lines or sybmbols to extract from: /path/to/file.rs:10, /path/to/file.rs#func_name Path should be absolute.',
189
+ },
190
+ allowTests: {
191
+ type: 'boolean',
192
+ description: 'Allow test files and test code blocks in results (disabled by default)',
193
+ },
194
+ contextLines: {
195
+ type: 'number',
196
+ description: 'Number of context lines to include before and after the extracted block when AST parsing fails to find a suitable node',
197
+ default: 0
198
+ },
199
+ format: {
200
+ type: 'string',
201
+ enum: ['markdown', 'plain', 'json'],
202
+ description: 'Output format for the extracted code',
203
+ default: 'markdown'
204
+ },
205
+ timeout: {
206
+ type: 'number',
207
+ description: 'Timeout for the extract operation in seconds (default: 30)',
208
+ },
209
+ noGitignore: {
210
+ type: 'boolean',
211
+ description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
212
+ },
213
+ lsp: {
214
+ type: 'boolean',
215
+ description: 'Use LSP (Language Server Protocol) for call hierarchy, reference counts, and enhanced symbol information',
173
216
  }
174
217
  },
175
218
  required: ['path', 'files'],
@@ -303,6 +346,26 @@ class ProbeServer {
303
346
  else if (this.defaultFormat === 'json') {
304
347
  options.json = true;
305
348
  }
349
+ if (args.session !== undefined && args.session.trim() !== '') {
350
+ options.session = args.session;
351
+ }
352
+ else {
353
+ options.session = "new";
354
+ }
355
+ // Use timeout from args, or fall back to instance default
356
+ if (args.timeout !== undefined) {
357
+ options.timeout = args.timeout;
358
+ }
359
+ else if (this.defaultTimeout !== undefined) {
360
+ options.timeout = this.defaultTimeout;
361
+ }
362
+ // Pass LSP flag if enabled globally or per-request
363
+ if (args.lsp !== undefined) {
364
+ options.lsp = args.lsp;
365
+ }
366
+ else if (this.lspEnabled) {
367
+ options.lsp = true;
368
+ }
306
369
  console.error("Executing search with options:", JSON.stringify(options, null, 2));
307
370
  try {
308
371
  // Call search with the options object
@@ -336,6 +399,20 @@ class ProbeServer {
336
399
  format: 'xml',
337
400
  allowTests: true, // Include test files by default
338
401
  };
402
+ // Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
403
+ if (args.noGitignore !== undefined) {
404
+ options.noGitignore = args.noGitignore;
405
+ }
406
+ else if (process.env.PROBE_NO_GITIGNORE) {
407
+ options.noGitignore = process.env.PROBE_NO_GITIGNORE === 'true';
408
+ }
409
+ // Pass LSP flag if enabled globally or per-request
410
+ if (args.lsp !== undefined) {
411
+ options.lsp = args.lsp;
412
+ }
413
+ else if (this.lspEnabled) {
414
+ options.lsp = true;
415
+ }
339
416
  // Call extract with the complete options object
340
417
  try {
341
418
  // Track request size for token usage
@@ -423,11 +500,47 @@ class ProbeServer {
423
500
  async run() {
424
501
  // The @probelabs/probe package now handles binary path management internally
425
502
  // We don't need to verify or download the binary in the MCP server anymore
503
+ // Initialize LSP servers for the current workspace if --lsp flag is enabled
504
+ if (this.lspEnabled) {
505
+ const workspaceRoot = process.cwd();
506
+ console.error(`Initializing LSP servers for workspace: ${workspaceRoot}`);
507
+ try {
508
+ // Execute probe lsp init command to pre-warm language servers
509
+ // Use recursive flag to discover nested projects in monorepos
510
+ const initCmd = process.platform === 'win32'
511
+ ? `probe lsp init -w "${workspaceRoot}" --recursive`
512
+ : `probe lsp init -w '${workspaceRoot}' --recursive`;
513
+ const { stdout, stderr } = await execAsync(initCmd, {
514
+ timeout: 10000, // 10 second timeout for initialization - don't wait too long
515
+ env: { ...process.env }
516
+ });
517
+ if (stderr && !stderr.includes('Successfully initialized')) {
518
+ console.error(`LSP initialization warnings: ${stderr}`);
519
+ }
520
+ console.error(`LSP servers initialized successfully for workspace: ${workspaceRoot}`);
521
+ // Parse initialization output to show what was initialized
522
+ if (stdout) {
523
+ const lines = stdout.split('\n');
524
+ const initializedServers = lines.filter(line => line.includes('✓') || line.includes('language server'));
525
+ if (initializedServers.length > 0) {
526
+ console.error('Initialized language servers:');
527
+ initializedServers.forEach(line => console.error(` ${line.trim()}`));
528
+ }
529
+ }
530
+ }
531
+ catch (error) {
532
+ // Don't fail MCP server startup if LSP initialization fails
533
+ // LSP will still work with cold start on first use
534
+ console.error(`Warning: Failed to initialize LSP servers: ${error.message || error}`);
535
+ console.error('LSP features will still be available but may have slower first-use performance');
536
+ }
537
+ }
426
538
  // Just connect the server to the transport
427
539
  const transport = new StdioServerTransport();
428
540
  await this.server.connect(transport);
429
541
  console.error('Probe MCP server running on stdio');
430
542
  }
431
543
  }
432
- const server = new ProbeServer(cliConfig.timeout, cliConfig.format || 'outline');
544
+ // Instantiate server with (timeout, lspEnabled, format)
545
+ const server = new ProbeServer(cliConfig.timeout ?? 30, cliConfig.lsp ?? false, cliConfig.format || 'outline-xml');
433
546
  server.run().catch(console.error);
@@ -21,11 +21,13 @@ import { fileURLToPath } from 'url';
21
21
  // Import from parent package
22
22
  import { search, query, extract, grep, getBinaryPath, setBinaryPath } from '../index.js';
23
23
 
24
+ type OutputFormat = 'outline' | 'outline-xml' | 'json';
25
+
24
26
  // Parse command-line arguments
25
- function parseArgs(): { timeout?: number; format?: string } {
27
+ function parseArgs(): { timeout?: number; lsp?: boolean; format?: OutputFormat } {
26
28
  const args = process.argv.slice(2);
27
- const config: { timeout?: number; format?: string } = {};
28
-
29
+ const config: { timeout?: number; lsp?: boolean; format?: OutputFormat } = {};
30
+
29
31
  for (let i = 0; i < args.length; i++) {
30
32
  if ((args[i] === '--timeout' || args[i] === '-t') && i + 1 < args.length) {
31
33
  const timeout = parseInt(args[i + 1], 10);
@@ -36,9 +38,17 @@ function parseArgs(): { timeout?: number; format?: string } {
36
38
  console.error(`Invalid timeout value: ${args[i + 1]}. Using default.`);
37
39
  }
38
40
  i++; // Skip the next argument
41
+ } else if (args[i] === '--lsp') {
42
+ config.lsp = true;
43
+ console.error('LSP mode enabled');
39
44
  } else if (args[i] === '--format' && i + 1 < args.length) {
40
- config.format = args[i + 1];
41
- console.error(`Format set to ${config.format}`);
45
+ const format = args[i + 1] as OutputFormat;
46
+ if (format === 'outline' || format === 'outline-xml' || format === 'json') {
47
+ config.format = format;
48
+ console.error(`Output format set to ${format}`);
49
+ } else {
50
+ console.error(`Invalid format value: ${args[i + 1]}. Using default.`);
51
+ }
42
52
  i++; // Skip the next argument
43
53
  } else if (args[i] === '--help' || args[i] === '-h') {
44
54
  console.error(`
@@ -49,7 +59,9 @@ Usage:
49
59
 
50
60
  Options:
51
61
  --timeout, -t <seconds> Set timeout for search operations (default: 30)
52
- --format <format> Set output format (default: outline)
62
+ --lsp Enable LSP (Language Server Protocol) for enhanced features
63
+ Automatically initializes language servers for the current workspace
64
+ --format <format> Output format for search responses (outline|outline-xml|json)
53
65
  --help, -h Show this help message
54
66
  `);
55
67
  process.exit(0);
@@ -120,17 +132,37 @@ interface SearchCodeArgs {
120
132
  exact?: boolean;
121
133
  strictElasticSyntax?: boolean;
122
134
  session?: string;
123
- nextPage?: boolean;
135
+ timeout?: number;
136
+ noGitignore?: boolean;
137
+ lsp?: boolean;
138
+ }
139
+
140
+ interface QueryCodeArgs {
141
+ path: string;
142
+ pattern: string;
143
+ language?: string;
144
+ ignore?: string[];
145
+ allowTests?: boolean;
146
+ maxResults?: number;
147
+ format?: 'markdown' | 'plain' | 'json' | 'color';
148
+ timeout?: number;
149
+ noGitignore?: boolean;
124
150
  }
125
151
 
126
152
  interface ExtractCodeArgs {
127
153
  path: string;
128
154
  files: string[];
155
+ allowTests?: boolean;
156
+ contextLines?: number;
157
+ format?: 'markdown' | 'plain' | 'json';
158
+ timeout?: number;
159
+ noGitignore?: boolean;
160
+ lsp?: boolean;
129
161
  }
130
162
 
131
163
  interface GrepArgs {
132
164
  pattern: string;
133
- paths: string | string[];
165
+ paths: string[];
134
166
  ignoreCase?: boolean;
135
167
  count?: boolean;
136
168
  context?: number;
@@ -139,11 +171,17 @@ interface GrepArgs {
139
171
  class ProbeServer {
140
172
  private server: Server;
141
173
  private defaultTimeout: number;
142
- private defaultFormat?: string;
143
-
144
- constructor(timeout: number = 30, format?: string) {
174
+ private lspEnabled: boolean;
175
+ private defaultFormat: OutputFormat;
176
+
177
+ constructor(
178
+ timeout: number = 30,
179
+ lspEnabled: boolean = false,
180
+ defaultFormat: OutputFormat = 'outline-xml'
181
+ ) {
145
182
  this.defaultTimeout = timeout;
146
- this.defaultFormat = format;
183
+ this.lspEnabled = lspEnabled;
184
+ this.defaultFormat = defaultFormat;
147
185
  this.server = new Server(
148
186
  {
149
187
  name: '@probelabs/probe',
@@ -201,8 +239,11 @@ class ProbeServer {
201
239
  },
202
240
  nextPage: {
203
241
  type: 'boolean',
204
- description: 'Set to true when requesting the next page of results. Requires passing the same session ID from the previous search output.',
205
- default: false
242
+ description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
243
+ },
244
+ lsp: {
245
+ type: 'boolean',
246
+ description: 'Use LSP (Language Server Protocol) for call hierarchy, reference counts, and enhanced symbol information',
206
247
  }
207
248
  },
208
249
  required: ['path', 'query']
@@ -221,7 +262,34 @@ class ProbeServer {
221
262
  files: {
222
263
  type: 'array',
223
264
  items: { type: 'string' },
224
- description: 'Array of file paths to extract from. Formats: "file.js" (entire file), "file.js:42" (code block at line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (specific symbol). Line numbers and symbols are part of the path string, not separate parameters. Paths can be absolute or relative to the project directory.',
265
+ description: 'Files and lines or sybmbols to extract from: /path/to/file.rs:10, /path/to/file.rs#func_name Path should be absolute.',
266
+ },
267
+ allowTests: {
268
+ type: 'boolean',
269
+ description: 'Allow test files and test code blocks in results (disabled by default)',
270
+ },
271
+ contextLines: {
272
+ type: 'number',
273
+ description: 'Number of context lines to include before and after the extracted block when AST parsing fails to find a suitable node',
274
+ default: 0
275
+ },
276
+ format: {
277
+ type: 'string',
278
+ enum: ['markdown', 'plain', 'json'],
279
+ description: 'Output format for the extracted code',
280
+ default: 'markdown'
281
+ },
282
+ timeout: {
283
+ type: 'number',
284
+ description: 'Timeout for the extract operation in seconds (default: 30)',
285
+ },
286
+ noGitignore: {
287
+ type: 'boolean',
288
+ description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
289
+ },
290
+ lsp: {
291
+ type: 'boolean',
292
+ description: 'Use LSP (Language Server Protocol) for call hierarchy, reference counts, and enhanced symbol information',
225
293
  }
226
294
  },
227
295
  required: ['path', 'files'],
@@ -365,7 +433,24 @@ class ProbeServer {
365
433
  } else if (this.defaultFormat === 'json') {
366
434
  options.json = true;
367
435
  }
368
-
436
+ if (args.session !== undefined && args.session.trim() !== '') {
437
+ options.session = args.session;
438
+ } else {
439
+ options.session = "new";
440
+ }
441
+ // Use timeout from args, or fall back to instance default
442
+ if (args.timeout !== undefined) {
443
+ options.timeout = args.timeout;
444
+ } else if (this.defaultTimeout !== undefined) {
445
+ options.timeout = this.defaultTimeout;
446
+ }
447
+ // Pass LSP flag if enabled globally or per-request
448
+ if (args.lsp !== undefined) {
449
+ options.lsp = args.lsp;
450
+ } else if (this.lspEnabled) {
451
+ options.lsp = true;
452
+ }
453
+
369
454
  console.error("Executing search with options:", JSON.stringify(options, null, 2));
370
455
 
371
456
  try {
@@ -405,6 +490,19 @@ class ProbeServer {
405
490
  allowTests: true, // Include test files by default
406
491
  };
407
492
 
493
+ // Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
494
+ if (args.noGitignore !== undefined) {
495
+ options.noGitignore = args.noGitignore;
496
+ } else if (process.env.PROBE_NO_GITIGNORE) {
497
+ options.noGitignore = process.env.PROBE_NO_GITIGNORE === 'true';
498
+ }
499
+ // Pass LSP flag if enabled globally or per-request
500
+ if (args.lsp !== undefined) {
501
+ options.lsp = args.lsp;
502
+ } else if (this.lspEnabled) {
503
+ options.lsp = true;
504
+ }
505
+
408
506
  // Call extract with the complete options object
409
507
  try {
410
508
  // Track request size for token usage
@@ -507,6 +605,48 @@ class ProbeServer {
507
605
  // The @probelabs/probe package now handles binary path management internally
508
606
  // We don't need to verify or download the binary in the MCP server anymore
509
607
 
608
+ // Initialize LSP servers for the current workspace if --lsp flag is enabled
609
+ if (this.lspEnabled) {
610
+ const workspaceRoot = process.cwd();
611
+ console.error(`Initializing LSP servers for workspace: ${workspaceRoot}`);
612
+
613
+ try {
614
+ // Execute probe lsp init command to pre-warm language servers
615
+ // Use recursive flag to discover nested projects in monorepos
616
+ const initCmd = process.platform === 'win32'
617
+ ? `probe lsp init -w "${workspaceRoot}" --recursive`
618
+ : `probe lsp init -w '${workspaceRoot}' --recursive`;
619
+
620
+ const { stdout, stderr } = await execAsync(initCmd, {
621
+ timeout: 10000, // 10 second timeout for initialization - don't wait too long
622
+ env: { ...process.env }
623
+ });
624
+
625
+ if (stderr && !stderr.includes('Successfully initialized')) {
626
+ console.error(`LSP initialization warnings: ${stderr}`);
627
+ }
628
+
629
+ console.error(`LSP servers initialized successfully for workspace: ${workspaceRoot}`);
630
+
631
+ // Parse initialization output to show what was initialized
632
+ if (stdout) {
633
+ const lines = stdout.split('\n');
634
+ const initializedServers = lines.filter(line =>
635
+ line.includes('✓') || line.includes('language server')
636
+ );
637
+ if (initializedServers.length > 0) {
638
+ console.error('Initialized language servers:');
639
+ initializedServers.forEach(line => console.error(` ${line.trim()}`));
640
+ }
641
+ }
642
+ } catch (error: any) {
643
+ // Don't fail MCP server startup if LSP initialization fails
644
+ // LSP will still work with cold start on first use
645
+ console.error(`Warning: Failed to initialize LSP servers: ${error.message || error}`);
646
+ console.error('LSP features will still be available but may have slower first-use performance');
647
+ }
648
+ }
649
+
510
650
  // Just connect the server to the transport
511
651
  const transport = new StdioServerTransport();
512
652
  await this.server.connect(transport);
@@ -514,5 +654,10 @@ class ProbeServer {
514
654
  }
515
655
  }
516
656
 
517
- const server = new ProbeServer(cliConfig.timeout, cliConfig.format || 'outline');
657
+ // Instantiate server with (timeout, lspEnabled, format)
658
+ const server = new ProbeServer(
659
+ cliConfig.timeout ?? 30,
660
+ cliConfig.lsp ?? false,
661
+ cliConfig.format || 'outline-xml'
662
+ );
518
663
  server.run().catch(console.error);
package/build/search.js CHANGED
@@ -32,7 +32,8 @@ const SEARCH_FLAG_MAP = {
32
32
  session: '--session',
33
33
  timeout: '--timeout',
34
34
  language: '--language',
35
- format: '--format'
35
+ format: '--format',
36
+ lsp: '--lsp'
36
37
  };
37
38
 
38
39
  /**
@@ -58,7 +59,7 @@ const SEARCH_FLAG_MAP = {
58
59
  * @param {string} [options.session] - Session ID for caching results
59
60
  * @param {number} [options.timeout] - Timeout in seconds (default: 30)
60
61
  * @param {string} [options.language] - Limit search to files of a specific programming language
61
- * @param {string} [options.format] - Output format ('json', 'outline-xml', etc.)
62
+ * @param {boolean} [options.lsp] - Use LSP (Language Server Protocol) for enhanced symbol information
62
63
  * @param {Object} [options.binaryOptions] - Options for getting the binary
63
64
  * @param {boolean} [options.binaryOptions.forceDownload] - Force download even if binary exists
64
65
  * @param {string} [options.binaryOptions.version] - Specific version to download
@@ -85,8 +86,8 @@ export async function search(options) {
85
86
  if (options.json && !options.format) {
86
87
  cliArgs.push('--format', 'json');
87
88
  } else if (options.format) {
88
- // Format is already handled by buildCliArgs through SEARCH_FLAG_MAP
89
- // but we need to ensure json parsing for json format
89
+ // Format is handled by buildCliArgs through SEARCH_FLAG_MAP.
90
+ // Ensure json parsing is enabled for json format.
90
91
  if (options.format === 'json') {
91
92
  options.json = true;
92
93
  }
@@ -257,4 +258,4 @@ export async function search(options) {
257
258
  };
258
259
  throw structuredError;
259
260
  }
260
- }
261
+ }