@probelabs/probe 0.6.0-rc148 → 0.6.0-rc150
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/build/agent/ProbeAgent.js +3 -5
- package/build/agent/index.js +6951 -302
- package/build/agent/schemaUtils.js +211 -3
- package/build/extract.js +1 -1
- package/build/mcp/index.js +3 -3
- package/build/mcp/index.ts +3 -3
- package/build/tools/common.js +58 -49
- package/cjs/agent/ProbeAgent.cjs +7246 -597
- package/cjs/index.cjs +7246 -597
- package/package.json +4 -3
- package/src/agent/ProbeAgent.js +3 -5
- package/src/agent/schemaUtils.js +211 -3
- package/src/extract.js +1 -1
- package/src/mcp/index.ts +3 -3
- package/src/tools/common.js +58 -49
- package/bin/binaries/probe-v0.6.0-rc148-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc148-aarch64-unknown-linux-gnu.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc148-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc148-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc148-x86_64-unknown-linux-gnu.tar.gz +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@probelabs/probe",
|
|
3
|
-
"version": "0.6.0-
|
|
3
|
+
"version": "0.6.0-rc150",
|
|
4
4
|
"description": "Node.js wrapper for the probe code search tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -75,15 +75,16 @@
|
|
|
75
75
|
"@ai-sdk/google": "^2.0.14",
|
|
76
76
|
"@ai-sdk/openai": "^2.0.10",
|
|
77
77
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
78
|
-
"@probelabs/maid": "^0.0.
|
|
78
|
+
"@probelabs/maid": "^0.0.19",
|
|
79
|
+
"adm-zip": "^0.5.16",
|
|
79
80
|
"ai": "^5.0.0",
|
|
81
|
+
"ajv": "^8.17.1",
|
|
80
82
|
"axios": "^1.8.3",
|
|
81
83
|
"dotenv": "^16.4.7",
|
|
82
84
|
"fs-extra": "^11.1.1",
|
|
83
85
|
"glob": "^10.3.10",
|
|
84
86
|
"gpt-tokenizer": "^3.0.1",
|
|
85
87
|
"tar": "^6.2.0",
|
|
86
|
-
"adm-zip": "^0.5.16",
|
|
87
88
|
"zod": "^3.24.2"
|
|
88
89
|
},
|
|
89
90
|
"devDependencies": {
|
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -920,9 +920,7 @@ Examples:
|
|
|
920
920
|
</search>
|
|
921
921
|
|
|
922
922
|
<extract>
|
|
923
|
-
<
|
|
924
|
-
<start_line>15</start_line>
|
|
925
|
-
<end_line>25</end_line>
|
|
923
|
+
<targets>src/config.js:15-25</targets>
|
|
926
924
|
</extract>
|
|
927
925
|
|
|
928
926
|
<attempt_completion>
|
|
@@ -1833,7 +1831,7 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
1833
1831
|
});
|
|
1834
1832
|
}
|
|
1835
1833
|
|
|
1836
|
-
let validation = validateJsonResponse(finalResult, { debug: this.debug });
|
|
1834
|
+
let validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
|
|
1837
1835
|
let retryCount = 0;
|
|
1838
1836
|
const maxRetries = 3;
|
|
1839
1837
|
|
|
@@ -1884,7 +1882,7 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
1884
1882
|
);
|
|
1885
1883
|
|
|
1886
1884
|
// Validate the corrected response
|
|
1887
|
-
currentValidation = validateJsonResponse(currentResult, { debug: this.debug });
|
|
1885
|
+
currentValidation = validateJsonResponse(currentResult, { debug: this.debug, schema: options.schema });
|
|
1888
1886
|
retryCount++;
|
|
1889
1887
|
|
|
1890
1888
|
if (this.debug) {
|
package/src/agent/schemaUtils.js
CHANGED
|
@@ -5,6 +5,65 @@
|
|
|
5
5
|
|
|
6
6
|
import { createMessagePreview } from '../tools/common.js';
|
|
7
7
|
import { validate, fixText, extractMermaidBlocks } from '@probelabs/maid';
|
|
8
|
+
import Ajv from 'ajv';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Recursively apply additionalProperties: false to all object schemas
|
|
12
|
+
* This ensures strict validation at all nesting levels
|
|
13
|
+
* @param {Object} schema - JSON schema object
|
|
14
|
+
* @returns {Object} - Modified schema with additionalProperties enforced
|
|
15
|
+
*/
|
|
16
|
+
function enforceNoAdditionalProperties(schema) {
|
|
17
|
+
if (!schema || typeof schema !== 'object') {
|
|
18
|
+
return schema;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Create a deep clone to avoid modifying the original
|
|
22
|
+
const cloned = JSON.parse(JSON.stringify(schema));
|
|
23
|
+
|
|
24
|
+
function applyRecursively(obj) {
|
|
25
|
+
if (!obj || typeof obj !== 'object') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// If this is an object type schema and doesn't have additionalProperties set
|
|
30
|
+
if (obj.type === 'object' && obj.additionalProperties === undefined) {
|
|
31
|
+
obj.additionalProperties = false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Recursively process properties
|
|
35
|
+
if (obj.properties && typeof obj.properties === 'object') {
|
|
36
|
+
Object.values(obj.properties).forEach(applyRecursively);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Process items for arrays
|
|
40
|
+
if (obj.items) {
|
|
41
|
+
if (Array.isArray(obj.items)) {
|
|
42
|
+
obj.items.forEach(applyRecursively);
|
|
43
|
+
} else {
|
|
44
|
+
applyRecursively(obj.items);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Process nested schemas in oneOf, anyOf, allOf
|
|
49
|
+
['oneOf', 'anyOf', 'allOf'].forEach(key => {
|
|
50
|
+
if (Array.isArray(obj[key])) {
|
|
51
|
+
obj[key].forEach(applyRecursively);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Process definitions/defs
|
|
56
|
+
if (obj.definitions && typeof obj.definitions === 'object') {
|
|
57
|
+
Object.values(obj.definitions).forEach(applyRecursively);
|
|
58
|
+
}
|
|
59
|
+
if (obj.$defs && typeof obj.$defs === 'object') {
|
|
60
|
+
Object.values(obj.$defs).forEach(applyRecursively);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
applyRecursively(cloned);
|
|
65
|
+
return cloned;
|
|
66
|
+
}
|
|
8
67
|
|
|
9
68
|
/**
|
|
10
69
|
* HTML entity decoder map for common entities that might appear in mermaid diagrams
|
|
@@ -141,12 +200,15 @@ export function cleanSchemaResponse(response) {
|
|
|
141
200
|
* @returns {Object} - {isValid: boolean, parsed?: Object, error?: string}
|
|
142
201
|
*/
|
|
143
202
|
export function validateJsonResponse(response, options = {}) {
|
|
144
|
-
const { debug = false } = options;
|
|
203
|
+
const { debug = false, schema = null, strictSchema = true } = options;
|
|
145
204
|
|
|
146
205
|
if (debug) {
|
|
147
206
|
console.log(`[DEBUG] JSON validation: Starting validation for response (${response.length} chars)`);
|
|
148
207
|
const preview = createMessagePreview(response);
|
|
149
208
|
console.log(`[DEBUG] JSON validation: Preview: ${preview}`);
|
|
209
|
+
if (schema) {
|
|
210
|
+
console.log(`[DEBUG] JSON validation: Schema validation enabled`);
|
|
211
|
+
}
|
|
150
212
|
}
|
|
151
213
|
|
|
152
214
|
try {
|
|
@@ -159,6 +221,140 @@ export function validateJsonResponse(response, options = {}) {
|
|
|
159
221
|
console.log(`[DEBUG] JSON validation: Object type: ${typeof parsed}, keys: ${Object.keys(parsed || {}).length}`);
|
|
160
222
|
}
|
|
161
223
|
|
|
224
|
+
// If schema provided, validate against it
|
|
225
|
+
if (schema) {
|
|
226
|
+
const schemaStart = Date.now();
|
|
227
|
+
let schemaObj;
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
// Parse schema if it's a string
|
|
231
|
+
schemaObj = typeof schema === 'string' ? JSON.parse(schema) : schema;
|
|
232
|
+
|
|
233
|
+
// Apply strict mode: enforce additionalProperties: false on all nested objects
|
|
234
|
+
// unless explicitly disabled via strictSchema: false option
|
|
235
|
+
if (strictSchema) {
|
|
236
|
+
schemaObj = enforceNoAdditionalProperties(schemaObj);
|
|
237
|
+
if (debug) {
|
|
238
|
+
console.log(`[DEBUG] JSON validation: Applied strict mode - additionalProperties: false enforced on all levels`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} catch (schemaParseError) {
|
|
242
|
+
if (debug) {
|
|
243
|
+
console.log(`[DEBUG] JSON validation: Failed to parse schema: ${schemaParseError.message}`);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
isValid: false,
|
|
247
|
+
error: 'Invalid schema provided',
|
|
248
|
+
schemaError: schemaParseError.message
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Create AJV validator with strict mode
|
|
253
|
+
const ajv = new Ajv({
|
|
254
|
+
strict: false, // Don't fail on unknown schema keywords
|
|
255
|
+
allErrors: true, // Return all errors, not just the first
|
|
256
|
+
verbose: true // Include schema and data in errors
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
let validate;
|
|
260
|
+
try {
|
|
261
|
+
validate = ajv.compile(schemaObj);
|
|
262
|
+
} catch (compileError) {
|
|
263
|
+
if (debug) {
|
|
264
|
+
console.log(`[DEBUG] JSON validation: Schema compilation failed: ${compileError.message}`);
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
isValid: false,
|
|
268
|
+
error: 'Schema compilation failed',
|
|
269
|
+
schemaError: compileError.message
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const valid = validate(parsed);
|
|
274
|
+
const schemaTime = Date.now() - schemaStart;
|
|
275
|
+
|
|
276
|
+
if (debug) {
|
|
277
|
+
console.log(`[DEBUG] JSON validation: Schema validation completed in ${schemaTime}ms, valid: ${valid}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!valid) {
|
|
281
|
+
// Format schema errors for better readability and actionability
|
|
282
|
+
const formattedErrors = validate.errors.map(err => {
|
|
283
|
+
// Convert JSON Pointer path to dot notation for readability
|
|
284
|
+
// /user/profile/age -> user.profile.age
|
|
285
|
+
const path = err.instancePath
|
|
286
|
+
? err.instancePath.substring(1).replace(/\//g, '.')
|
|
287
|
+
: '<root>';
|
|
288
|
+
|
|
289
|
+
let message = '';
|
|
290
|
+
let suggestion = '';
|
|
291
|
+
|
|
292
|
+
// Create crisp, actionable error messages based on error type
|
|
293
|
+
if (err.keyword === 'additionalProperties') {
|
|
294
|
+
const extraField = err.params.additionalProperty;
|
|
295
|
+
message = `Extra field '${extraField}' is not allowed`;
|
|
296
|
+
suggestion = `Remove '${extraField}' or add it to the schema`;
|
|
297
|
+
} else if (err.keyword === 'required') {
|
|
298
|
+
const missingField = err.params.missingProperty;
|
|
299
|
+
message = `Missing required field '${missingField}'`;
|
|
300
|
+
suggestion = `Add '${missingField}' to this object`;
|
|
301
|
+
} else if (err.keyword === 'type') {
|
|
302
|
+
const expected = err.params.type;
|
|
303
|
+
const actual = typeof err.data;
|
|
304
|
+
const value = JSON.stringify(err.data);
|
|
305
|
+
message = `Wrong type: expected ${expected}, got ${actual} (value: ${value})`;
|
|
306
|
+
suggestion = `Change value to ${expected} type`;
|
|
307
|
+
} else if (err.keyword === 'enum') {
|
|
308
|
+
const allowed = err.params.allowedValues;
|
|
309
|
+
const value = JSON.stringify(err.data);
|
|
310
|
+
message = `Invalid value ${value}. Allowed: ${allowed.map(v => JSON.stringify(v)).join(', ')}`;
|
|
311
|
+
suggestion = `Use one of the allowed values`;
|
|
312
|
+
} else if (err.keyword === 'minimum' || err.keyword === 'maximum') {
|
|
313
|
+
const limit = err.params.limit;
|
|
314
|
+
const comparison = err.params.comparison;
|
|
315
|
+
message = `Value ${err.data} ${err.message} (${comparison} ${limit})`;
|
|
316
|
+
suggestion = `Adjust value to meet constraint`;
|
|
317
|
+
} else if (err.keyword === 'minLength' || err.keyword === 'maxLength') {
|
|
318
|
+
const limit = err.params.limit;
|
|
319
|
+
message = `String length ${err.message} (current: ${err.data.length}, ${err.keyword}: ${limit})`;
|
|
320
|
+
suggestion = `Adjust string length`;
|
|
321
|
+
} else if (err.keyword === 'pattern') {
|
|
322
|
+
message = `Value doesn't match required pattern: ${err.params.pattern}`;
|
|
323
|
+
suggestion = `Update value to match the pattern`;
|
|
324
|
+
} else {
|
|
325
|
+
// Fallback for other error types
|
|
326
|
+
message = err.message;
|
|
327
|
+
suggestion = '';
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Format: "path: message | suggestion"
|
|
331
|
+
const location = path ? `at '${path}'` : 'at root';
|
|
332
|
+
return suggestion
|
|
333
|
+
? `${location}: ${message} → ${suggestion}`
|
|
334
|
+
: `${location}: ${message}`;
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const errorSummary = formattedErrors.join('\n ');
|
|
338
|
+
|
|
339
|
+
if (debug) {
|
|
340
|
+
console.log(`[DEBUG] JSON validation: Schema validation errors:\n ${errorSummary}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
isValid: false,
|
|
345
|
+
error: 'Schema validation failed',
|
|
346
|
+
schemaErrors: validate.errors,
|
|
347
|
+
formattedErrors: formattedErrors,
|
|
348
|
+
errorSummary: `Schema validation failed:\n ${errorSummary}`,
|
|
349
|
+
parsed: parsed // Still return parsed data for debugging
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (debug) {
|
|
354
|
+
console.log(`[DEBUG] JSON validation: Schema validation passed`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
162
358
|
return { isValid: true, parsed };
|
|
163
359
|
} catch (error) {
|
|
164
360
|
// Extract error position from error message if available
|
|
@@ -886,9 +1082,21 @@ When presented with broken JSON, analyze it thoroughly and provide the corrected
|
|
|
886
1082
|
errorContext = validationResult.enhancedError;
|
|
887
1083
|
}
|
|
888
1084
|
|
|
1085
|
+
// Add schema validation errors if present
|
|
1086
|
+
let schemaErrorDetails = '';
|
|
1087
|
+
if (validationResult.errorSummary) {
|
|
1088
|
+
schemaErrorDetails = `\n\nSchema Validation Errors:\n${validationResult.errorSummary}`;
|
|
1089
|
+
} else if (validationResult.schemaErrors && validationResult.schemaErrors.length > 0) {
|
|
1090
|
+
const errors = validationResult.schemaErrors.map(err => {
|
|
1091
|
+
const path = err.instancePath || '(root)';
|
|
1092
|
+
return ` ${path}: ${err.message}`;
|
|
1093
|
+
}).join('\n');
|
|
1094
|
+
schemaErrorDetails = `\n\nSchema Validation Errors:\n${errors}`;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
889
1097
|
const prompt = `Fix the following invalid JSON.
|
|
890
1098
|
|
|
891
|
-
Error: ${errorContext}
|
|
1099
|
+
Error: ${errorContext}${schemaErrorDetails}
|
|
892
1100
|
|
|
893
1101
|
Invalid JSON:
|
|
894
1102
|
${invalidJson}
|
|
@@ -896,7 +1104,7 @@ ${invalidJson}
|
|
|
896
1104
|
Expected schema structure:
|
|
897
1105
|
${schema}
|
|
898
1106
|
|
|
899
|
-
Provide only the corrected JSON without any markdown formatting or explanations.`;
|
|
1107
|
+
${schemaErrorDetails ? 'CRITICAL: Pay special attention to the schema validation errors above. The JSON may be syntactically valid but does not conform to the required schema. Make sure to:\n- Include all required fields\n- Use correct data types\n- Remove any additional properties not defined in the schema (if additionalProperties is false)\n- Ensure all values match their schema constraints\n\n' : ''}Provide only the corrected JSON without any markdown formatting or explanations.`;
|
|
900
1108
|
|
|
901
1109
|
try {
|
|
902
1110
|
if (this.options.debug) {
|
package/src/extract.js
CHANGED
|
@@ -48,7 +48,7 @@ export async function extract(options) {
|
|
|
48
48
|
const hasContent = options.content !== undefined && options.content !== null;
|
|
49
49
|
|
|
50
50
|
if (!hasFiles && !hasInputFile && !hasContent) {
|
|
51
|
-
throw new Error('
|
|
51
|
+
throw new Error('Extract requires one of: "files" (array of file paths), "inputFile" (path to input file), or "content" (string/buffer for stdin)');
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// Get the binary path
|
package/src/mcp/index.ts
CHANGED
|
@@ -199,18 +199,18 @@ class ProbeServer {
|
|
|
199
199
|
},
|
|
200
200
|
{
|
|
201
201
|
name: 'extract_code',
|
|
202
|
-
description: "Extract code from files
|
|
202
|
+
description: "Extract code blocks from files using tree-sitter AST parsing. Each file path can include optional line numbers or symbol names to extract specific code blocks.",
|
|
203
203
|
inputSchema: {
|
|
204
204
|
type: 'object',
|
|
205
205
|
properties: {
|
|
206
206
|
path: {
|
|
207
207
|
type: 'string',
|
|
208
|
-
description: 'Absolute path to the project directory',
|
|
208
|
+
description: 'Absolute path to the project root directory (used as working directory for relative file paths)',
|
|
209
209
|
},
|
|
210
210
|
files: {
|
|
211
211
|
type: 'array',
|
|
212
212
|
items: { type: 'string' },
|
|
213
|
-
description: 'Array of file paths
|
|
213
|
+
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.',
|
|
214
214
|
}
|
|
215
215
|
},
|
|
216
216
|
required: ['path', 'files'],
|
package/src/tools/common.js
CHANGED
|
@@ -7,13 +7,8 @@ import { z } from 'zod';
|
|
|
7
7
|
|
|
8
8
|
// Common schemas for tool parameters (used for internal execution after XML parsing)
|
|
9
9
|
export const searchSchema = z.object({
|
|
10
|
-
query: z.string().describe('Search query with Elasticsearch syntax. Use
|
|
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')
|
|
10
|
+
query: z.string().describe('Search query with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation.'),
|
|
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.')
|
|
17
12
|
});
|
|
18
13
|
|
|
19
14
|
export const querySchema = z.object({
|
|
@@ -24,13 +19,8 @@ export const querySchema = z.object({
|
|
|
24
19
|
});
|
|
25
20
|
|
|
26
21
|
export const extractSchema = z.object({
|
|
27
|
-
targets: z.string().optional().describe('File paths or symbols to extract from.
|
|
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, xml, color, outline-xml, outline-diff)')
|
|
22
|
+
targets: z.string().optional().describe('File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (symbol). Multiple targets separated by spaces.'),
|
|
23
|
+
input_content: z.string().optional().describe('Text content to extract file paths from (alternative to targets)')
|
|
34
24
|
});
|
|
35
25
|
|
|
36
26
|
export const delegateSchema = z.object({
|
|
@@ -120,13 +110,8 @@ You need to focus on main keywords when constructing the query, and always use e
|
|
|
120
110
|
- Once data is returned, it's cached and won't return on next runs (this is expected behavior)
|
|
121
111
|
|
|
122
112
|
Parameters:
|
|
123
|
-
- query: (required) Search query with Elasticsearch syntax.
|
|
124
|
-
- path: (
|
|
125
|
-
- allow_tests: (optional, default: false) Allow test files in search results (true/false).
|
|
126
|
-
- 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.
|
|
127
|
-
- maxResults: (optional) Maximum number of results to return (number).
|
|
128
|
-
- maxTokens: (optional, default: 10000) Maximum number of tokens to return (number).
|
|
129
|
-
- language: (optional) Limit search to files of a specific programming language (e.g., 'rust', 'js', 'python', 'go' etc.).
|
|
113
|
+
- query: (required) Search query with Elasticsearch syntax. Use quotes for exact matches ("functionName"), AND/OR for boolean logic, - for negation, + for important terms.
|
|
114
|
+
- path: (optional, default: '.') 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.
|
|
130
115
|
|
|
131
116
|
**Workflow:** Always start with search, then use extract for detailed context when needed.
|
|
132
117
|
|
|
@@ -148,30 +133,24 @@ User: How to calculate the total amount in the payments module?
|
|
|
148
133
|
<search>
|
|
149
134
|
<query>calculate AND payment</query>
|
|
150
135
|
<path>src/utils</path>
|
|
151
|
-
<allow_tests>false</allow_tests>
|
|
152
136
|
</search>
|
|
153
137
|
|
|
154
138
|
User: How do the user authentication and authorization work?
|
|
155
139
|
<search>
|
|
156
|
-
<query>+user
|
|
140
|
+
<query>+user AND (authentication OR authorization OR authz)</query>
|
|
157
141
|
<path>.</path>
|
|
158
|
-
<allow_tests>true</allow_tests>
|
|
159
|
-
<language>go</language>
|
|
160
142
|
</search>
|
|
161
143
|
|
|
162
144
|
User: Find all react imports in the project.
|
|
163
145
|
<search>
|
|
164
|
-
<query>import
|
|
146
|
+
<query>"import" AND "react"</query>
|
|
165
147
|
<path>.</path>
|
|
166
|
-
<exact>true</exact>
|
|
167
|
-
<language>js</language>
|
|
168
148
|
</search>
|
|
169
149
|
|
|
170
|
-
User: Find how
|
|
150
|
+
User: Find how decompound library works?
|
|
171
151
|
<search>
|
|
172
|
-
<query>
|
|
152
|
+
<query>decompound</query>
|
|
173
153
|
<path>/dep/rust/decompound</path>
|
|
174
|
-
<language>rust</language>
|
|
175
154
|
</search>
|
|
176
155
|
|
|
177
156
|
</examples>
|
|
@@ -208,11 +187,9 @@ Full file extraction should be the LAST RESORT! Always prefer search.
|
|
|
208
187
|
**Session Awareness:** Reuse context from previous tool calls. Don't re-extract the same symbols you already have.
|
|
209
188
|
|
|
210
189
|
Parameters:
|
|
211
|
-
- targets: (required) File paths or symbols to extract from.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
- end_line: (optional) End line number for extracting a range of lines.
|
|
215
|
-
- allow_tests: (optional, default: false) Allow test files and test code blocks (true/false).
|
|
190
|
+
- targets: (required) File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (code block at line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (specific symbol). Multiple targets separated by spaces.
|
|
191
|
+
- input_content: (optional) Text content to extract file paths from (alternative to targets for processing diffs/logs).
|
|
192
|
+
|
|
216
193
|
Usage Example:
|
|
217
194
|
|
|
218
195
|
<examples>
|
|
@@ -239,9 +216,7 @@ User: Lets read the whole file
|
|
|
239
216
|
|
|
240
217
|
User: Read the first 10 lines of the file
|
|
241
218
|
<extract>
|
|
242
|
-
<targets>src/search/ranking.rs</targets>
|
|
243
|
-
<line>1</line>
|
|
244
|
-
<end_line>10</end_line>
|
|
219
|
+
<targets>src/search/ranking.rs:1-10</targets>
|
|
245
220
|
</extract>
|
|
246
221
|
|
|
247
222
|
User: Read file inside the dependency
|
|
@@ -349,7 +324,7 @@ export const bashDescription = 'Execute bash commands for system exploration and
|
|
|
349
324
|
// Valid tool names that should be parsed as tool calls
|
|
350
325
|
const DEFAULT_VALID_TOOLS = [
|
|
351
326
|
'search',
|
|
352
|
-
'query',
|
|
327
|
+
'query',
|
|
353
328
|
'extract',
|
|
354
329
|
'delegate',
|
|
355
330
|
'listFiles',
|
|
@@ -358,6 +333,43 @@ const DEFAULT_VALID_TOOLS = [
|
|
|
358
333
|
'attempt_completion'
|
|
359
334
|
];
|
|
360
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Get valid parameter names for a specific tool from its schema
|
|
338
|
+
* @param {string} toolName - Name of the tool
|
|
339
|
+
* @returns {string[]} - Array of valid parameter names for this tool
|
|
340
|
+
*/
|
|
341
|
+
function getValidParamsForTool(toolName) {
|
|
342
|
+
// Map tool names to their schemas
|
|
343
|
+
const schemaMap = {
|
|
344
|
+
search: searchSchema,
|
|
345
|
+
query: querySchema,
|
|
346
|
+
extract: extractSchema,
|
|
347
|
+
delegate: delegateSchema,
|
|
348
|
+
bash: bashSchema,
|
|
349
|
+
attempt_completion: attemptCompletionSchema
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const schema = schemaMap[toolName];
|
|
353
|
+
if (!schema) {
|
|
354
|
+
// For tools without schema (listFiles, searchFiles, implement), return common params
|
|
355
|
+
// These are the shared params that appear across multiple tools
|
|
356
|
+
return ['path', 'directory', 'pattern', 'recursive', 'includeHidden', 'task', 'files', 'autoCommits', 'result'];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// For attempt_completion, it has custom validation, just return 'result'
|
|
360
|
+
if (toolName === 'attempt_completion') {
|
|
361
|
+
return ['result'];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Extract keys from Zod schema
|
|
365
|
+
if (schema && schema._def && schema._def.shape) {
|
|
366
|
+
return Object.keys(schema._def.shape());
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Fallback: return empty array if we can't extract schema keys
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
|
|
361
373
|
// Simple XML parser helper - safer string-based approach
|
|
362
374
|
export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
363
375
|
// Look for each valid tool name specifically using string search
|
|
@@ -387,15 +399,12 @@ export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
387
399
|
|
|
388
400
|
const params = {};
|
|
389
401
|
|
|
402
|
+
// Get valid parameters for this specific tool from its schema
|
|
403
|
+
const validParams = getValidParamsForTool(toolName);
|
|
404
|
+
|
|
390
405
|
// Parse parameters using string-based approach for better safety
|
|
391
|
-
//
|
|
392
|
-
|
|
393
|
-
const commonParams = ['query', 'file_path', 'line', 'end_line', 'path', 'recursive', 'includeHidden',
|
|
394
|
-
'max_results', 'maxResults', 'result', 'command', 'description', 'task', 'param', 'pattern',
|
|
395
|
-
'allow_tests', 'exact', 'maxTokens', 'language', 'input_content',
|
|
396
|
-
'context_lines', 'format', 'directory', 'autoCommits', 'files', 'targets'];
|
|
397
|
-
|
|
398
|
-
for (const paramName of commonParams) {
|
|
406
|
+
// Only look for parameters that are valid for this specific tool
|
|
407
|
+
for (const paramName of validParams) {
|
|
399
408
|
const paramOpenTag = `<${paramName}>`;
|
|
400
409
|
const paramCloseTag = `</${paramName}>`;
|
|
401
410
|
|
|
@@ -410,7 +419,7 @@ export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
410
419
|
if (paramCloseIndex === -1) {
|
|
411
420
|
// Find the next opening tag after this parameter
|
|
412
421
|
let nextTagIndex = innerContent.length;
|
|
413
|
-
for (const nextParam of
|
|
422
|
+
for (const nextParam of validParams) {
|
|
414
423
|
const nextOpenTag = `<${nextParam}>`;
|
|
415
424
|
const nextIndex = innerContent.indexOf(nextOpenTag, paramOpenIndex + paramOpenTag.length);
|
|
416
425
|
if (nextIndex !== -1 && nextIndex < nextTagIndex) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|