@probelabs/probe 0.6.0-rc56

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/src/query.js ADDED
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Query functionality for the probe package
3
+ * @module query
4
+ */
5
+
6
+ import { exec } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import { getBinaryPath, buildCliArgs, escapeString } from './utils.js';
9
+
10
+ const execAsync = promisify(exec);
11
+
12
+ /**
13
+ * Flag mapping for query options
14
+ * Maps option keys to command-line flags
15
+ */
16
+ const QUERY_FLAG_MAP = {
17
+ language: '--language',
18
+ ignore: '--ignore',
19
+ allowTests: '--allow-tests',
20
+ maxResults: '--max-results',
21
+ format: '--format'
22
+ };
23
+
24
+ /**
25
+ * Query code in a specified directory using tree-sitter patterns
26
+ *
27
+ * @param {Object} options - Query options
28
+ * @param {string} options.path - Path to search in
29
+ * @param {string} options.pattern - The ast-grep pattern to search for
30
+ * @param {string} [options.language] - Programming language to search in
31
+ * @param {string[]} [options.ignore] - Patterns to ignore
32
+ * @param {boolean} [options.allowTests] - Include test files
33
+ * @param {number} [options.maxResults] - Maximum number of results
34
+ * @param {string} [options.format] - Output format ('markdown', 'plain', 'json', 'color')
35
+ * @param {Object} [options.binaryOptions] - Options for getting the binary
36
+ * @param {boolean} [options.binaryOptions.forceDownload] - Force download even if binary exists
37
+ * @param {string} [options.binaryOptions.version] - Specific version to download
38
+ * @param {boolean} [options.json] - Return results as parsed JSON instead of string
39
+ * @returns {Promise<string|Object>} - Query results as string or parsed JSON
40
+ * @throws {Error} If the query fails
41
+ */
42
+ export async function query(options) {
43
+ if (!options || !options.path) {
44
+ throw new Error('Path is required');
45
+ }
46
+
47
+ if (!options.pattern) {
48
+ throw new Error('Pattern is required');
49
+ }
50
+
51
+ // Get the binary path
52
+ const binaryPath = await getBinaryPath(options.binaryOptions || {});
53
+
54
+ // Build CLI arguments from options
55
+ const cliArgs = buildCliArgs(options, QUERY_FLAG_MAP);
56
+
57
+ // If json option is true, override format to json
58
+ if (options.json && !options.format) {
59
+ cliArgs.push('--format', 'json');
60
+ }
61
+
62
+ // Add pattern and path as positional arguments
63
+ cliArgs.push(escapeString(options.pattern), escapeString(options.path));
64
+
65
+ // Create a single log record with all query parameters
66
+ let logMessage = `Query: pattern="${options.pattern}" path="${options.path}"`;
67
+ if (options.language) logMessage += ` language=${options.language}`;
68
+ if (options.maxResults) logMessage += ` maxResults=${options.maxResults}`;
69
+ if (options.allowTests) logMessage += " allowTests=true";
70
+ console.error(logMessage);
71
+
72
+ // Execute command
73
+ const command = `${binaryPath} query ${cliArgs.join(' ')}`;
74
+
75
+ try {
76
+ const { stdout, stderr } = await execAsync(command);
77
+
78
+ if (stderr) {
79
+ console.error(`stderr: ${stderr}`);
80
+ }
81
+
82
+ // Count results
83
+ let resultCount = 0;
84
+
85
+ // Try to count results from stdout
86
+ const lines = stdout.split('\n');
87
+ for (const line of lines) {
88
+ if (line.startsWith('```') && !line.includes('```language')) {
89
+ resultCount++;
90
+ }
91
+ }
92
+
93
+ // Log the results count
94
+ console.error(`Query results: ${resultCount} matches`);
95
+
96
+ // Parse JSON if requested or if format is json
97
+ if (options.json || options.format === 'json') {
98
+ try {
99
+ return JSON.parse(stdout);
100
+ } catch (error) {
101
+ console.error('Error parsing JSON output:', error);
102
+ return stdout; // Fall back to string output
103
+ }
104
+ }
105
+
106
+ return stdout;
107
+ } catch (error) {
108
+ // Enhance error message with command details
109
+ const errorMessage = `Error executing query command: ${error.message}\nCommand: ${command}`;
110
+ throw new Error(errorMessage);
111
+ }
112
+ }
package/src/search.js ADDED
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Search functionality for the probe package
3
+ * @module search
4
+ */
5
+
6
+ import { exec } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import { getBinaryPath, buildCliArgs, escapeString } from './utils.js';
9
+
10
+ const execAsync = promisify(exec);
11
+
12
+ /**
13
+ * Flag mapping for search options
14
+ * Maps option keys to command-line flags
15
+ */
16
+ const SEARCH_FLAG_MAP = {
17
+ filesOnly: '--files-only',
18
+ ignore: '--ignore',
19
+ excludeFilenames: '--exclude-filenames',
20
+ reranker: '--reranker',
21
+ frequencySearch: '--frequency',
22
+ exact: '--exact',
23
+ maxResults: '--max-results',
24
+ maxBytes: '--max-bytes',
25
+ maxTokens: '--max-tokens',
26
+ allowTests: '--allow-tests',
27
+ noMerge: '--no-merge',
28
+ mergeThreshold: '--merge-threshold',
29
+ session: '--session',
30
+ timeout: '--timeout',
31
+ language: '--language'
32
+ };
33
+
34
+ /**
35
+ * Search code in a specified directory
36
+ *
37
+ * @param {Object} options - Search options
38
+ * @param {string} options.path - Path to search in
39
+ * @param {string|string[]} options.query - Search query or queries
40
+ * @param {boolean} [options.filesOnly] - Only output file paths
41
+ * @param {string[]} [options.ignore] - Patterns to ignore
42
+ * @param {boolean} [options.excludeFilenames] - Exclude filenames from search
43
+ * @param {string} [options.reranker] - Reranking method ('hybrid', 'hybrid2', 'bm25', 'tfidf')
44
+ * @param {boolean} [options.frequencySearch] - Use frequency-based search
45
+ * @param {boolean} [options.exact] - Perform exact search without tokenization (case-insensitive)
46
+ * @param {number} [options.maxResults] - Maximum number of results
47
+ * @param {number} [options.maxBytes] - Maximum bytes to return
48
+ * @param {number} [options.maxTokens] - Maximum tokens to return
49
+ * @param {boolean} [options.allowTests] - Include test files
50
+ * @param {boolean} [options.noMerge] - Don't merge adjacent blocks
51
+ * @param {number} [options.mergeThreshold] - Merge threshold
52
+ * @param {string} [options.session] - Session ID for caching results
53
+ * @param {number} [options.timeout] - Timeout in seconds (default: 30)
54
+ * @param {string} [options.language] - Limit search to files of a specific programming language
55
+ * @param {Object} [options.binaryOptions] - Options for getting the binary
56
+ * @param {boolean} [options.binaryOptions.forceDownload] - Force download even if binary exists
57
+ * @param {string} [options.binaryOptions.version] - Specific version to download
58
+ * @param {boolean} [options.json] - Return results as parsed JSON instead of string
59
+ * @returns {Promise<string|Object>} - Search results as string or parsed JSON
60
+ * @throws {Error} If the search fails
61
+ */
62
+ export async function search(options) {
63
+ if (!options || !options.path) {
64
+ throw new Error('Path is required');
65
+ }
66
+
67
+ if (!options.query) {
68
+ throw new Error('Query is required');
69
+ }
70
+
71
+ // Get the binary path
72
+ const binaryPath = await getBinaryPath(options.binaryOptions || {});
73
+
74
+ // Build CLI arguments from options
75
+ const cliArgs = buildCliArgs(options, SEARCH_FLAG_MAP);
76
+
77
+ // Add JSON format if requested
78
+ if (options.json) {
79
+ cliArgs.push('--format', 'json');
80
+ }
81
+
82
+ // Set default maxTokens if not provided
83
+ if (!options.maxTokens) {
84
+ options.maxTokens = 10000;
85
+ cliArgs.push('--max-tokens', '10000');
86
+ }
87
+
88
+ // Set default timeout if not provided
89
+ if (!options.timeout) {
90
+ options.timeout = 30;
91
+ cliArgs.push('--timeout', '30');
92
+ }
93
+
94
+ // Ensure language is properly passed if provided
95
+ if (options.language) {
96
+ // Ensure language flag is in cliArgs
97
+ if (!cliArgs.includes('--language')) {
98
+ cliArgs.push('--language', options.language);
99
+ }
100
+ }
101
+
102
+ // Ensure exact search is properly passed if enabled
103
+ if (options.exact) {
104
+ // Ensure exact flag is in cliArgs
105
+ if (!cliArgs.includes('--exact')) {
106
+ cliArgs.push('--exact');
107
+ }
108
+ }
109
+
110
+ // Add session ID from environment variable if not provided in options
111
+ if (!options.session && process.env.PROBE_SESSION_ID) {
112
+ options.session = process.env.PROBE_SESSION_ID;
113
+ }
114
+
115
+ // Add query and path as positional arguments
116
+ const queries = Array.isArray(options.query) ? options.query : [options.query];
117
+
118
+ // Create a single log record with all search parameters (commented out for less verbose output)
119
+ let logMessage = `\nSearch: query="${queries[0]}" path="${options.path}"`;
120
+ if (options.maxResults) logMessage += ` maxResults=${options.maxResults}`;
121
+ logMessage += ` maxTokens=${options.maxTokens}`;
122
+ logMessage += ` timeout=${options.timeout}`;
123
+ if (options.allowTests) logMessage += " allowTests=true";
124
+ if (options.language) logMessage += ` language=${options.language}`;
125
+ if (options.exact) logMessage += " exact=true";
126
+ if (options.session) logMessage += ` session=${options.session}`;
127
+ console.error(logMessage);
128
+ // Create positional arguments array separate from flags
129
+ const positionalArgs = [];
130
+
131
+ if (queries.length > 0) {
132
+ // Escape the query to handle special characters
133
+ positionalArgs.push(escapeString(queries[0]));
134
+ }
135
+
136
+ // Escape the path to handle spaces and special characters
137
+ positionalArgs.push(escapeString(options.path));
138
+ // Don't add the path to cliArgs, it should only be a positional argument
139
+
140
+ // Execute command with flags first, then positional arguments
141
+ const command = `${binaryPath} search ${cliArgs.join(' ')} ${positionalArgs.join(' ')}`;
142
+
143
+ // Debug logs to see the actual command with quotes and the path
144
+ // console.error(`Executing command: ${command}`);
145
+ // console.error(`Path being used: "${options.path}"`);
146
+ // console.error(`Escaped path: ${escapeString(options.path)}`);
147
+ // console.error(`Command flags: ${cliArgs.join(' ')}`);
148
+ // console.error(`Positional arguments: ${positionalArgs.join(' ')}`);
149
+
150
+ try {
151
+ // Log before executing
152
+ // console.error(`About to execute command: ${command}`);
153
+
154
+ // Execute the command with options to preserve quotes and apply timeout
155
+ const { stdout, stderr } = await execAsync(command, {
156
+ shell: true,
157
+ timeout: options.timeout * 1000 // Convert seconds to milliseconds
158
+ });
159
+
160
+ // Log after executing
161
+ // console.error(`Command executed successfully`);
162
+
163
+ if (stderr && process.env.DEBUG) {
164
+ console.error(`stderr: ${stderr}`);
165
+ }
166
+
167
+ // Count results, tokens, and bytes
168
+ let resultCount = 0;
169
+ let tokenCount = 0;
170
+ let bytesCount = 0;
171
+
172
+ // Try to count results from stdout
173
+ const lines = stdout.split('\n');
174
+ for (const line of lines) {
175
+ if (line.startsWith('```') && !line.includes('```language')) {
176
+ resultCount++;
177
+ }
178
+ }
179
+
180
+ // Look for the specific "Total bytes returned: X" line in the output
181
+ const totalBytesMatch = stdout.match(/Total bytes returned:\s*(\d+)/i);
182
+ if (totalBytesMatch && totalBytesMatch[1]) {
183
+ bytesCount = parseInt(totalBytesMatch[1], 10);
184
+ }
185
+
186
+ // Look for the specific "Total tokens returned: X" line in the output
187
+ const totalTokensMatch = stdout.match(/Total tokens returned:\s*(\d+)/i);
188
+ if (totalTokensMatch && totalTokensMatch[1]) {
189
+ tokenCount = parseInt(totalTokensMatch[1], 10);
190
+ } else {
191
+ // Try other patterns if the specific format isn't found
192
+ const tokenMatch = stdout.match(/Tokens:?\s*(\d+)/i) ||
193
+ stdout.match(/(\d+)\s*tokens/i) ||
194
+ stdout.match(/token count:?\s*(\d+)/i);
195
+
196
+ if (tokenMatch && tokenMatch[1]) {
197
+ tokenCount = parseInt(tokenMatch[1], 10);
198
+ } else {
199
+ // If we still can't find the token count, use the default maxTokens value
200
+ // This is a fallback, but the command should be returning the actual count
201
+ tokenCount = options.maxTokens;
202
+ }
203
+ }
204
+
205
+ // Log the results count, token count, and bytes count (commented out for less verbose output)
206
+ let resultsMessage = `\nSearch results: ${resultCount} matches, ${tokenCount} tokens`;
207
+ if (bytesCount > 0) {
208
+ resultsMessage += `, ${bytesCount} bytes`;
209
+ }
210
+ console.error(resultsMessage);
211
+
212
+ // Parse JSON if requested
213
+ if (options.json) {
214
+ try {
215
+ return JSON.parse(stdout);
216
+ } catch (error) {
217
+ console.error('Error parsing JSON output:', error);
218
+ return stdout; // Fall back to string output
219
+ }
220
+ }
221
+
222
+ return stdout;
223
+ } catch (error) {
224
+ // Check if the error is a timeout
225
+ if (error.code === 'ETIMEDOUT' || error.killed) {
226
+ const timeoutMessage = `Search operation timed out after ${options.timeout} seconds.\nCommand: ${command}`;
227
+ console.error(timeoutMessage);
228
+ throw new Error(timeoutMessage);
229
+ }
230
+
231
+ // Enhance error message with command details
232
+ const errorMessage = `Error executing search command: ${error.message}\nCommand: ${command}`;
233
+ throw new Error(errorMessage);
234
+ }
235
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Common schemas and definitions for AI tools
3
+ * @module tools/common
4
+ */
5
+
6
+ import { z } from 'zod';
7
+
8
+ // Common schemas for tool parameters (used for internal execution after XML parsing)
9
+ export const searchSchema = z.object({
10
+ query: z.string().describe('Search query with Elasticsearch syntax. Use + for important terms.'),
11
+ path: z.string().optional().default('.').describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
12
+ allow_tests: z.boolean().optional().default(false).describe('Allow test files in search results'),
13
+ exact: z.boolean().optional().default(false).describe('Perform exact search without tokenization (case-insensitive)'),
14
+ maxResults: z.number().optional().describe('Maximum number of results to return'),
15
+ maxTokens: z.number().optional().default(10000).describe('Maximum number of tokens to return'),
16
+ language: z.string().optional().describe('Limit search to files of a specific programming language')
17
+ });
18
+
19
+ export const querySchema = z.object({
20
+ pattern: z.string().describe('AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc.'),
21
+ path: z.string().optional().default('.').describe('Path to search in'),
22
+ language: z.string().optional().default('rust').describe('Programming language to use for parsing'),
23
+ allow_tests: z.boolean().optional().default(false).describe('Allow test files in search results')
24
+ });
25
+
26
+ export const extractSchema = z.object({
27
+ file_path: z.string().optional().describe('Path to the file to extract from. Can include line numbers or symbol names'),
28
+ input_content: z.string().optional().describe('Text content to extract file paths from'),
29
+ line: z.number().optional().describe('Start line number to extract a specific code block'),
30
+ end_line: z.number().optional().describe('End line number for extracting a range of lines'),
31
+ allow_tests: z.boolean().optional().default(false).describe('Allow test files and test code blocks'),
32
+ context_lines: z.number().optional().default(10).describe('Number of context lines to include'),
33
+ format: z.string().optional().default('plain').describe('Output format (plain, markdown, json, color)')
34
+ });
35
+
36
+ // Schema for the new attempt_completion tool
37
+ export const attemptCompletionSchema = z.object({
38
+ result: z.string().describe('The final result of the task. Formulate this result in a way that is final and does not require further input from the user. Do not end your result with questions or offers for further assistance.'),
39
+ command: z.string().optional().describe('A CLI command to execute to show a live demo of the result to the user (e.g., `open index.html`). Do not use commands like `echo` or `cat` that merely print text.')
40
+ });
41
+
42
+
43
+ // Tool descriptions for the system prompt (using XML format)
44
+
45
+ export const searchToolDefinition = `
46
+ ## search
47
+ Description: Search code in the repository using Elasticsearch query syntax (except field based queries, e.g. "filename:..." NOT supported).
48
+
49
+ You need to focus on main keywords when constructing the query, and always use elastic search syntax like OR AND and brackets to group keywords.
50
+ Parameters:
51
+ - query: (required) Search query with Elasticsearch syntax. You can use + for important terms, and - for negation.
52
+ - path: (required) Path to search in. All dependencies located in /dep folder, under language sub folders, like this: "/dep/go/github.com/owner/repo", "/dep/js/package_name", or "/dep/rust/cargo_name" etc. YOU SHOULD ALWAYS provide FULL PATH when searching dependencies, including depency name.
53
+ - allow_tests: (optional, default: false) Allow test files in search results (true/false).
54
+ - exact: (optional, default: false) Perform exact pricise search. Use it when you already know function or struct name, or some other code block, and want exact match.
55
+ - maxResults: (optional) Maximum number of results to return (number).
56
+ - maxTokens: (optional, default: 10000) Maximum number of tokens to return (number).
57
+ - language: (optional) Limit search to files of a specific programming language (e.g., 'rust', 'js', 'python', 'go' etc.).
58
+
59
+
60
+ Usage Example:
61
+
62
+ <examples>
63
+
64
+ User: How to calculate the total amount in the payments module?
65
+ <search>
66
+ <query>calculate AND payment</query>
67
+ <path>src/utils</path>
68
+ <allow_tests>false</allow_tests>
69
+ </search>
70
+
71
+ User: How do the user authentication and authorization work?
72
+ <search>
73
+ <query>+user and (authentification OR authroization OR authz)</query>
74
+ <path>.</path>
75
+ <allow_tests>true</allow_tests>
76
+ <language>go</language>
77
+ </search>
78
+
79
+ User: Find all react imports in the project.
80
+ <search>
81
+ <query>import { react }</query>
82
+ <path>.</path>
83
+ <exact>true</exact>
84
+ <language>js</language>
85
+ </search>
86
+
87
+
88
+ User: Find how decompoud library works?
89
+ <search>
90
+ <query>import { react }</query>
91
+ <path>/dep/rust/decompound</path>
92
+ <language>rust</language>
93
+ </search>
94
+
95
+ </examples>
96
+ `;
97
+
98
+ export const queryToolDefinition = `
99
+ ## query
100
+ Description: Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.
101
+ Parameters:
102
+ - pattern: (required) AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc.
103
+ - path: (optional, default: '.') Path to search in.
104
+ - language: (optional, default: 'rust') Programming language to use for parsing.
105
+ - allow_tests: (optional, default: false) Allow test files in search results (true/false).
106
+ Usage Example:
107
+
108
+ <examples>
109
+
110
+ <query>
111
+ <pattern>function $FUNC($$$PARAMS) { $$$BODY }</pattern>
112
+ <path>src/parser</path>
113
+ <language>js</language>
114
+ </query>
115
+
116
+ </examples>
117
+ `;
118
+
119
+ export const extractToolDefinition = `
120
+ ## extract
121
+ Description: Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. It can be used to read full files as well.
122
+ Full file extraction should be the LAST RESORT! Always prefer search.
123
+
124
+ Parameters:
125
+ - file_path: (required) Path to the file to extract from. Can include line numbers or symbol names (e.g., 'src/main.rs:10-20', 'src/utils.js#myFunction').
126
+ - line: (optional) Start line number to extract a specific code block. Use with end_line for ranges.
127
+ - end_line: (optional) End line number for extracting a range of lines.
128
+ - allow_tests: (optional, default: false) Allow test files and test code blocks (true/false).
129
+ Usage Example:
130
+
131
+ <examples>
132
+
133
+ User: How RankManager works
134
+ <extract>
135
+ <file_path>src/search/ranking.rs#RankManager</file_path>
136
+ </extract>
137
+
138
+ User: Lets read the whole file
139
+ <extract>
140
+ <file_path>src/search/ranking.rs</file_path>
141
+ </extract>
142
+
143
+ User: Read the first 10 lines of the file
144
+ <extract>
145
+ <file_path>src/search/ranking.rs</file_path>
146
+ <line>1</line>
147
+ <end_line>10</end_line>
148
+ </extract>
149
+
150
+ User: Read file inside the dependency
151
+ <extract>
152
+ <file_path>/dep/go/github.com/gorilla/mux/router.go</file_path>
153
+ </extract>
154
+
155
+
156
+ </examples>
157
+ `;
158
+
159
+ export const attemptCompletionToolDefinition = `
160
+ ## attempt_completion
161
+ Description: Use this tool ONLY when the task is fully complete and you have received confirmation of success for all previous tool uses. Presents the final result to the user.
162
+ Parameters:
163
+ - result: (required) The final result of the task. Formulate this result concisely and definitively. Do not end with questions or offers for further assistance. Ensure that answer fully addresses the user's request, and a clear and detailed maneer.
164
+ - command: (optional) A CLI command to demonstrate the result (e.g., 'open index.html'). Avoid simple print commands like 'echo'.
165
+ Usage Example:
166
+ <attempt_completion>
167
+ <result>I have refactored the search module according to the requirements and verified the tests pass.</result>
168
+ <command>cargo test --lib</command>
169
+ </attempt_completion>
170
+ `;
171
+
172
+ export const searchDescription = 'Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.';
173
+ export const queryDescription = 'Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.';
174
+ export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.';
175
+
176
+ // Simple XML parser helper
177
+ export function parseXmlToolCall(xmlString) {
178
+ const toolMatch = xmlString.match(/<([a-zA-Z0-9_]+)>([\s\S]*?)<\/\1>/);
179
+ if (!toolMatch) {
180
+ return null;
181
+ }
182
+
183
+ const toolName = toolMatch[1];
184
+ const innerContent = toolMatch[2];
185
+ const params = {};
186
+
187
+ const paramRegex = /<([a-zA-Z0-9_]+)>([\s\S]*?)<\/\1>/g;
188
+ let paramMatch;
189
+ while ((paramMatch = paramRegex.exec(innerContent)) !== null) {
190
+ const paramName = paramMatch[1];
191
+ let paramValue = paramMatch[2].trim();
192
+
193
+ // Basic type inference (can be improved)
194
+ if (paramValue.toLowerCase() === 'true') {
195
+ paramValue = true;
196
+ } else if (paramValue.toLowerCase() === 'false') {
197
+ paramValue = false;
198
+ } else if (!isNaN(paramValue) && paramValue.trim() !== '') {
199
+ // Check if it's potentially a number (handle integers and floats)
200
+ const num = Number(paramValue);
201
+ if (Number.isFinite(num)) { // Use Number.isFinite to avoid Infinity/NaN
202
+ paramValue = num;
203
+ }
204
+ // Keep as string if not a valid finite number
205
+ }
206
+
207
+ params[paramName] = paramValue;
208
+ }
209
+
210
+ // Special handling for attempt_completion where result might contain nested XML/code
211
+ if (toolName === 'attempt_completion') {
212
+ const resultMatch = innerContent.match(/<result>([\s\S]*?)<\/result>/);
213
+ if (resultMatch) {
214
+ params['result'] = resultMatch[1].trim(); // Keep result content as is
215
+ }
216
+ const commandMatch = innerContent.match(/<command>([\s\S]*?)<\/command>/);
217
+ if (commandMatch) {
218
+ params['command'] = commandMatch[1].trim();
219
+ }
220
+ }
221
+
222
+
223
+ return { toolName, params };
224
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Main tools module
3
+ * @module tools
4
+ */
5
+
6
+ // Export Vercel AI SDK tool generators
7
+ export { searchTool, queryTool, extractTool } from './vercel.js';
8
+
9
+ // Export LangChain tools
10
+ export { createSearchTool, createQueryTool, createExtractTool } from './langchain.js';
11
+
12
+ // Export common schemas
13
+ export {
14
+ searchSchema,
15
+ querySchema,
16
+ extractSchema,
17
+ attemptCompletionSchema,
18
+ attemptCompletionToolDefinition
19
+ } from './common.js';
20
+
21
+ // Export system message
22
+ export { DEFAULT_SYSTEM_MESSAGE } from './system-message.js';
23
+
24
+ // For backward compatibility, create and export pre-configured tools
25
+ import { searchTool as searchToolGenerator, queryTool as queryToolGenerator, extractTool as extractToolGenerator } from './vercel.js';
26
+ import { DEFAULT_SYSTEM_MESSAGE } from './system-message.js';
27
+
28
+ // Create default tool instances (for backward compatibility)
29
+ const tools = {
30
+ searchTool: searchToolGenerator(),
31
+ queryTool: queryToolGenerator(),
32
+ extractTool: extractToolGenerator(),
33
+ DEFAULT_SYSTEM_MESSAGE
34
+ };
35
+
36
+ export { tools };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Tools for LangChain
3
+ * @module tools/langchain
4
+ */
5
+
6
+ import { search } from '../search.js';
7
+ import { query } from '../query.js';
8
+ import { extract } from '../extract.js';
9
+ import { searchSchema, querySchema, extractSchema, searchDescription, queryDescription, extractDescription } from './common.js';
10
+
11
+ // LangChain tool for searching code
12
+ export function createSearchTool() {
13
+ return {
14
+ name: 'search',
15
+ description: searchDescription,
16
+ schema: searchSchema,
17
+ func: async ({ query: searchQuery, path, allow_tests, exact, maxResults, maxTokens = 10000, language }) => {
18
+ try {
19
+ const results = await search({
20
+ query: searchQuery,
21
+ path,
22
+ allow_tests,
23
+ exact,
24
+ json: false,
25
+ maxResults,
26
+ maxTokens,
27
+ language
28
+ });
29
+
30
+ return results;
31
+ } catch (error) {
32
+ console.error('Error executing search command:', error);
33
+ return `Error executing search command: ${error.message}`;
34
+ }
35
+ }
36
+ };
37
+ }
38
+
39
+ // LangChain tool for querying code
40
+ export function createQueryTool() {
41
+ return {
42
+ name: 'query',
43
+ description: queryDescription,
44
+ schema: querySchema,
45
+ func: async ({ pattern, path, language, allow_tests }) => {
46
+ try {
47
+ const results = await query({
48
+ pattern,
49
+ path,
50
+ language,
51
+ allow_tests,
52
+ json: false
53
+ });
54
+
55
+ return results;
56
+ } catch (error) {
57
+ console.error('Error executing query command:', error);
58
+ return `Error executing query command: ${error.message}`;
59
+ }
60
+ }
61
+ };
62
+ }
63
+
64
+ // LangChain tool for extracting code
65
+ export function createExtractTool() {
66
+ return {
67
+ name: 'extract',
68
+ description: extractDescription,
69
+ schema: extractSchema,
70
+ func: async ({ file_path, line, end_line, allow_tests, context_lines, format }) => {
71
+ try {
72
+ const files = [file_path];
73
+
74
+ const results = await extract({
75
+ files,
76
+ allowTests: allow_tests,
77
+ contextLines: context_lines,
78
+ format
79
+ });
80
+
81
+ return results;
82
+ } catch (error) {
83
+ console.error('Error executing extract command:', error);
84
+ return `Error executing extract command: ${error.message}`;
85
+ }
86
+ }
87
+ };
88
+ }