@robota-sdk/agent-tools 3.0.0-beta.1

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.
@@ -0,0 +1,1352 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ FunctionTool: () => FunctionTool,
34
+ OpenAPITool: () => OpenAPITool,
35
+ ToolRegistry: () => ToolRegistry,
36
+ bashTool: () => bashTool,
37
+ createFunctionTool: () => createFunctionTool,
38
+ createOpenAPITool: () => createOpenAPITool,
39
+ createZodFunctionTool: () => createZodFunctionTool,
40
+ editTool: () => editTool,
41
+ globTool: () => globTool,
42
+ grepTool: () => grepTool,
43
+ readTool: () => readTool,
44
+ writeTool: () => writeTool,
45
+ zodToJsonSchema: () => zodToJsonSchema
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // src/registry/tool-registry.ts
50
+ var import_agent_core = require("@robota-sdk/agent-core");
51
+ var import_agent_core2 = require("@robota-sdk/agent-core");
52
+ var ToolRegistry = class {
53
+ tools = /* @__PURE__ */ new Map();
54
+ /**
55
+ * Register a tool
56
+ */
57
+ register(tool) {
58
+ if (!tool.schema?.name) {
59
+ throw new import_agent_core.ValidationError("Tool must have a valid schema with name");
60
+ }
61
+ const toolName = tool.schema.name;
62
+ this.validateToolSchema(tool.schema);
63
+ if (this.tools.has(toolName)) {
64
+ import_agent_core2.logger.warn(`Tool "${toolName}" is already registered, overriding`, {
65
+ toolName,
66
+ existingTool: this.tools.get(toolName)?.constructor.name
67
+ });
68
+ }
69
+ this.tools.set(toolName, tool);
70
+ import_agent_core2.logger.debug(`Tool "${toolName}" registered successfully`, {
71
+ toolName,
72
+ toolType: tool.constructor.name,
73
+ parameters: Object.keys(tool.schema.parameters?.properties || {})
74
+ });
75
+ }
76
+ /**
77
+ * Unregister a tool
78
+ */
79
+ unregister(name) {
80
+ if (!this.tools.has(name)) {
81
+ import_agent_core2.logger.warn(`Attempted to unregister non-existent tool "${name}"`);
82
+ return;
83
+ }
84
+ this.tools.delete(name);
85
+ import_agent_core2.logger.debug(`Tool "${name}" unregistered successfully`);
86
+ }
87
+ /**
88
+ * Get tool by name
89
+ */
90
+ get(name) {
91
+ return this.tools.get(name);
92
+ }
93
+ /**
94
+ * Get all registered tools
95
+ */
96
+ getAll() {
97
+ return Array.from(this.tools.values());
98
+ }
99
+ /**
100
+ * Get tool schemas
101
+ */
102
+ getSchemas() {
103
+ const tools = this.getAll();
104
+ import_agent_core2.logger.debug("[TOOL-FLOW] ToolRegistry.getSchemas() - Tools before schema extraction", {
105
+ count: tools.length,
106
+ tools: tools.map((t) => ({
107
+ name: t.schema?.name ?? "unnamed",
108
+ hasSchema: !!t.schema,
109
+ schemaType: typeof t.schema,
110
+ toolType: t.constructor?.name || "unknown"
111
+ }))
112
+ });
113
+ return this.getAll().map((tool) => tool.schema);
114
+ }
115
+ /**
116
+ * Check if tool exists
117
+ */
118
+ has(name) {
119
+ return this.tools.has(name);
120
+ }
121
+ /**
122
+ * Clear all tools
123
+ */
124
+ clear() {
125
+ const toolCount = this.tools.size;
126
+ this.tools.clear();
127
+ import_agent_core2.logger.debug(`Cleared ${toolCount} tools from registry`);
128
+ }
129
+ /**
130
+ * Get tool names
131
+ */
132
+ getToolNames() {
133
+ return Array.from(this.tools.keys());
134
+ }
135
+ /**
136
+ * Get tools by pattern
137
+ */
138
+ getToolsByPattern(pattern) {
139
+ const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
140
+ return this.getAll().filter((tool) => regex.test(tool.schema.name));
141
+ }
142
+ /**
143
+ * Get tool count
144
+ */
145
+ size() {
146
+ return this.tools.size;
147
+ }
148
+ /**
149
+ * Validate tool schema
150
+ */
151
+ validateToolSchema(schema) {
152
+ if (!schema.name || typeof schema.name !== "string") {
153
+ throw new import_agent_core.ValidationError("Tool schema must have a valid name");
154
+ }
155
+ if (!schema.description || typeof schema.description !== "string") {
156
+ throw new import_agent_core.ValidationError("Tool schema must have a description");
157
+ }
158
+ if (!schema.parameters || typeof schema.parameters !== "object" || schema.parameters === null || Array.isArray(schema.parameters)) {
159
+ throw new import_agent_core.ValidationError("Tool schema must have parameters object");
160
+ }
161
+ if (schema.parameters.type !== "object") {
162
+ throw new import_agent_core.ValidationError('Tool parameters type must be "object"');
163
+ }
164
+ if (schema.parameters.properties) {
165
+ for (const propName of Object.keys(schema.parameters.properties)) {
166
+ const propSchema = schema.parameters.properties[propName];
167
+ if (!propSchema?.type) {
168
+ throw new import_agent_core.ValidationError(`Parameter "${propName}" must have a type`);
169
+ }
170
+ const validTypes = ["string", "number", "boolean", "array", "object"];
171
+ if (!validTypes.includes(propSchema.type)) {
172
+ throw new import_agent_core.ValidationError(
173
+ `Parameter "${propName}" has invalid type "${propSchema.type}"`
174
+ );
175
+ }
176
+ }
177
+ }
178
+ if (schema.parameters.required) {
179
+ const properties = schema.parameters.properties || {};
180
+ for (const requiredField of schema.parameters.required) {
181
+ if (!properties[requiredField]) {
182
+ throw new import_agent_core.ValidationError(
183
+ `Required parameter "${requiredField}" is not defined in properties`
184
+ );
185
+ }
186
+ }
187
+ }
188
+ }
189
+ };
190
+
191
+ // src/implementations/function-tool.ts
192
+ var import_agent_core3 = require("@robota-sdk/agent-core");
193
+
194
+ // src/implementations/function-tool/schema-converter.ts
195
+ function zodToJsonSchema(schema, options = {}) {
196
+ const properties = {};
197
+ const required = [];
198
+ const schemaDef = schema._def;
199
+ if (!schemaDef) {
200
+ throw new Error("Zod schema is missing _def; cannot convert to JSON schema.");
201
+ }
202
+ if (schemaDef.typeName === "ZodObject" && schemaDef.shape) {
203
+ const shape = typeof schemaDef.shape === "function" ? schemaDef.shape() : schemaDef.shape;
204
+ for (const [key, typeObj] of Object.entries(shape)) {
205
+ const property = convertZodTypeToProperty(typeObj);
206
+ properties[key] = property;
207
+ if (isRequiredField(typeObj)) {
208
+ required.push(key);
209
+ }
210
+ }
211
+ }
212
+ return {
213
+ type: "object",
214
+ properties,
215
+ required,
216
+ ...options.allowAdditionalProperties && { additionalProperties: true }
217
+ };
218
+ }
219
+ function convertZodTypeToProperty(typeObj) {
220
+ const typeDef = typeObj._def;
221
+ if (!typeDef) {
222
+ throw new Error("Zod type is missing _def; cannot convert to JSON schema.");
223
+ }
224
+ const base = {};
225
+ if (typeDef.description) {
226
+ base.description = typeDef.description;
227
+ }
228
+ switch (typeDef.typeName) {
229
+ case "ZodString":
230
+ return { type: "string", ...base };
231
+ case "ZodNumber":
232
+ return { type: "number", ...base };
233
+ case "ZodBoolean":
234
+ return { type: "boolean", ...base };
235
+ case "ZodArray": {
236
+ if (!typeDef.type) {
237
+ throw new Error("ZodArray is missing item type; cannot convert to JSON schema.");
238
+ }
239
+ const arrayItems = convertZodTypeToProperty(typeDef.type);
240
+ return {
241
+ type: "array",
242
+ items: arrayItems,
243
+ ...base
244
+ };
245
+ }
246
+ case "ZodObject":
247
+ return { type: "object", ...base };
248
+ case "ZodEnum": {
249
+ const enumValues = typeDef.values;
250
+ if (!enumValues || !Array.isArray(enumValues)) {
251
+ throw new Error("ZodEnum is missing enum values; cannot convert to JSON schema.");
252
+ }
253
+ return {
254
+ type: "string",
255
+ enum: enumValues,
256
+ ...base
257
+ };
258
+ }
259
+ case "ZodOptional":
260
+ if (typeDef.innerType) {
261
+ const innerProperty = convertZodTypeToProperty(typeDef.innerType);
262
+ return { ...innerProperty, ...base };
263
+ }
264
+ throw new Error("ZodOptional is missing innerType; cannot convert to JSON schema.");
265
+ case "ZodNullable":
266
+ if (typeDef.innerType) {
267
+ const innerProperty = convertZodTypeToProperty(typeDef.innerType);
268
+ return { ...innerProperty, ...base };
269
+ }
270
+ throw new Error("ZodNullable is missing innerType; cannot convert to JSON schema.");
271
+ case "ZodDefault":
272
+ if (typeDef.innerType) {
273
+ const innerProperty = convertZodTypeToProperty(typeDef.innerType);
274
+ return { ...innerProperty, ...base };
275
+ }
276
+ throw new Error("ZodDefault is missing innerType; cannot convert to JSON schema.");
277
+ default:
278
+ throw new Error(`Unsupported Zod type: ${String(typeDef.typeName)}`);
279
+ }
280
+ }
281
+ function isRequiredField(typeObj) {
282
+ const typeDef = typeObj._def;
283
+ if (!typeDef) {
284
+ throw new Error("Zod schema is missing _def; cannot determine required fields.");
285
+ }
286
+ return typeDef.typeName !== "ZodOptional" && typeDef.typeName !== "ZodNullable" && typeDef.typeName !== "ZodDefault";
287
+ }
288
+
289
+ // src/implementations/function-tool.ts
290
+ var FunctionTool = class {
291
+ schema;
292
+ fn;
293
+ eventService;
294
+ constructor(schema, fn) {
295
+ this.schema = schema;
296
+ this.fn = fn;
297
+ this.validateConstructorInputs();
298
+ }
299
+ /**
300
+ * Get tool name
301
+ */
302
+ getName() {
303
+ return this.schema.name;
304
+ }
305
+ /**
306
+ * Set EventService for post-construction injection.
307
+ * Accepts EventService as-is without transformation.
308
+ * Caller is responsible for providing properly configured EventService.
309
+ */
310
+ setEventService(eventService) {
311
+ this.eventService = eventService;
312
+ }
313
+ /**
314
+ * Execute the function tool
315
+ */
316
+ async execute(parameters, context) {
317
+ const toolName = this.schema.name;
318
+ if (!this.validate(parameters)) {
319
+ const errors = this.getValidationErrors(parameters);
320
+ throw new import_agent_core3.ValidationError(`Invalid parameters for tool "${toolName}": ${errors.join(", ")}`);
321
+ }
322
+ const startTime = Date.now();
323
+ let result;
324
+ try {
325
+ result = await this.fn(parameters, context);
326
+ } catch (error) {
327
+ if (error instanceof import_agent_core3.ToolExecutionError || error instanceof import_agent_core3.ValidationError) {
328
+ throw error;
329
+ }
330
+ throw new import_agent_core3.ToolExecutionError(
331
+ `Function tool execution failed: ${error instanceof Error ? error.message : String(error)}`,
332
+ toolName,
333
+ error instanceof Error ? error : new Error(String(error)),
334
+ {
335
+ parameterCount: Object.keys(parameters || {}).length,
336
+ hasContext: !!context
337
+ }
338
+ );
339
+ }
340
+ const executionTime = Date.now() - startTime;
341
+ return {
342
+ success: true,
343
+ data: result,
344
+ metadata: {
345
+ executionTime,
346
+ toolName,
347
+ parameters
348
+ }
349
+ };
350
+ }
351
+ /**
352
+ * Validate parameters (simple boolean result)
353
+ */
354
+ validate(parameters) {
355
+ return this.getValidationErrors(parameters).length === 0;
356
+ }
357
+ /**
358
+ * Validate tool parameters with detailed result
359
+ */
360
+ validateParameters(parameters) {
361
+ const errors = this.getValidationErrors(parameters);
362
+ return {
363
+ isValid: errors.length === 0,
364
+ errors
365
+ };
366
+ }
367
+ /**
368
+ * Get tool description
369
+ */
370
+ getDescription() {
371
+ return this.schema.description;
372
+ }
373
+ /**
374
+ * Get detailed validation errors
375
+ */
376
+ getValidationErrors(parameters) {
377
+ const errors = [];
378
+ const required = this.schema.parameters.required || [];
379
+ const properties = this.schema.parameters.properties || {};
380
+ for (const field of required) {
381
+ if (!(field in parameters)) {
382
+ errors.push(`Missing required parameter: ${field}`);
383
+ }
384
+ }
385
+ for (const [key, value] of Object.entries(parameters)) {
386
+ const paramSchema = properties[key];
387
+ if (!paramSchema) {
388
+ errors.push(`Unknown parameter: ${key}`);
389
+ continue;
390
+ }
391
+ const typeError = this.validateParameterType(key, value, paramSchema);
392
+ if (typeError) {
393
+ errors.push(typeError);
394
+ }
395
+ }
396
+ return errors;
397
+ }
398
+ /**
399
+ * Validate individual parameter type
400
+ */
401
+ validateParameterType(key, value, schema) {
402
+ const expectedType = schema["type"];
403
+ switch (expectedType) {
404
+ case "string":
405
+ if (typeof value !== "string") {
406
+ return `Parameter "${key}" must be a string, got ${typeof value}`;
407
+ }
408
+ break;
409
+ case "number":
410
+ if (typeof value !== "number" || isNaN(value)) {
411
+ return `Parameter "${key}" must be a number, got ${typeof value}`;
412
+ }
413
+ break;
414
+ case "boolean":
415
+ if (typeof value !== "boolean") {
416
+ return `Parameter "${key}" must be a boolean, got ${typeof value}`;
417
+ }
418
+ break;
419
+ case "array":
420
+ if (!Array.isArray(value)) {
421
+ return `Parameter "${key}" must be an array, got ${typeof value}`;
422
+ }
423
+ if (schema.items) {
424
+ for (let i = 0; i < value.length; i++) {
425
+ const itemError = this.validateParameterType(`${key}[${i}]`, value[i], schema.items);
426
+ if (itemError) {
427
+ return itemError;
428
+ }
429
+ }
430
+ }
431
+ break;
432
+ case "object":
433
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
434
+ return `Parameter "${key}" must be an object, got ${typeof value}`;
435
+ }
436
+ break;
437
+ }
438
+ if (schema.enum && schema.enum.length > 0) {
439
+ const enumValues = schema.enum;
440
+ let isValidEnum = false;
441
+ for (const enumValue of enumValues) {
442
+ if (value === enumValue) {
443
+ isValidEnum = true;
444
+ break;
445
+ }
446
+ }
447
+ if (!isValidEnum) {
448
+ return `Parameter "${key}" must be one of: ${enumValues.join(", ")}, got ${value}`;
449
+ }
450
+ }
451
+ return void 0;
452
+ }
453
+ /**
454
+ * Validate constructor inputs
455
+ */
456
+ validateConstructorInputs() {
457
+ if (!this.schema) {
458
+ throw new import_agent_core3.ValidationError("Tool schema is required");
459
+ }
460
+ if (!this.fn || typeof this.fn !== "function") {
461
+ throw new import_agent_core3.ValidationError("Tool function is required and must be a function");
462
+ }
463
+ if (!this.schema.name) {
464
+ throw new import_agent_core3.ValidationError("Tool schema must have a name");
465
+ }
466
+ }
467
+ };
468
+ function createFunctionTool(name, description, parameters, fn) {
469
+ const schema = {
470
+ name,
471
+ description,
472
+ parameters
473
+ };
474
+ return new FunctionTool(schema, fn);
475
+ }
476
+ function createZodFunctionTool(name, description, zodSchema, fn) {
477
+ const parameters = zodToJsonSchema(zodSchema);
478
+ const schema = {
479
+ name,
480
+ description,
481
+ parameters
482
+ };
483
+ const wrappedFn = async (parameters2, context) => {
484
+ const parseResult = zodSchema.safeParse(parameters2);
485
+ if (!parseResult.success) {
486
+ throw new import_agent_core3.ValidationError(`Zod validation failed: ${parseResult.error}`);
487
+ }
488
+ const result = await fn(parseResult.data || parameters2, context);
489
+ return typeof result === "string" ? result : JSON.stringify(result);
490
+ };
491
+ return new FunctionTool(schema, wrappedFn);
492
+ }
493
+
494
+ // src/implementations/openapi-tool.ts
495
+ var import_agent_core4 = require("@robota-sdk/agent-core");
496
+ var OpenAPITool = class {
497
+ schema;
498
+ apiSpec;
499
+ operationId;
500
+ baseURL;
501
+ config;
502
+ eventService;
503
+ constructor(config) {
504
+ this.config = config;
505
+ if (typeof config.spec !== "object" || config.spec === null || typeof config.spec.openapi !== "string" || typeof config.spec.paths !== "object") {
506
+ throw new Error(
507
+ 'Invalid OpenAPI spec: must contain "openapi" (string) and "paths" (object) fields'
508
+ );
509
+ }
510
+ this.apiSpec = config.spec;
511
+ this.operationId = config.operationId;
512
+ this.baseURL = config.baseURL;
513
+ this.schema = this.createSchemaFromOpenAPI();
514
+ }
515
+ /**
516
+ * Execute the OpenAPI tool
517
+ */
518
+ async execute(parameters, context) {
519
+ const toolName = this.schema.name;
520
+ const validation = this.validateParameters(parameters);
521
+ if (!validation.isValid) {
522
+ throw new import_agent_core4.ValidationError(
523
+ `Invalid parameters for OpenAPI tool "${toolName}": ${validation.errors.join(", ")}`
524
+ );
525
+ }
526
+ try {
527
+ const startTime = Date.now();
528
+ const result = await this.executeAPICall(parameters, context);
529
+ const executionTime = Date.now() - startTime;
530
+ return {
531
+ success: true,
532
+ data: result,
533
+ metadata: {
534
+ executionTime,
535
+ toolName,
536
+ operationId: this.operationId,
537
+ baseURL: this.baseURL
538
+ }
539
+ };
540
+ } catch (error) {
541
+ if (error instanceof import_agent_core4.ToolExecutionError || error instanceof import_agent_core4.ValidationError) {
542
+ throw error;
543
+ }
544
+ const safeError = error instanceof Error ? error : new Error(String(error));
545
+ throw new import_agent_core4.ToolExecutionError(
546
+ `OpenAPI tool execution failed: ${safeError.message}`,
547
+ toolName,
548
+ safeError,
549
+ {
550
+ operationId: this.operationId,
551
+ baseURL: this.baseURL,
552
+ parametersCount: Object.keys(parameters).length
553
+ }
554
+ );
555
+ }
556
+ }
557
+ /**
558
+ * Validate tool parameters
559
+ */
560
+ validate(parameters) {
561
+ return this.validateParameters(parameters).isValid;
562
+ }
563
+ /**
564
+ * Validate tool parameters with detailed result
565
+ */
566
+ validateParameters(parameters) {
567
+ const required = this.schema.parameters.required || [];
568
+ const errors = [];
569
+ for (const field of required) {
570
+ if (!(field in parameters)) {
571
+ errors.push(`Missing required parameter: ${field}`);
572
+ }
573
+ }
574
+ return {
575
+ isValid: errors.length === 0,
576
+ errors
577
+ };
578
+ }
579
+ /**
580
+ * Get tool name
581
+ */
582
+ getName() {
583
+ return this.schema.name;
584
+ }
585
+ /**
586
+ * Set EventService for post-construction injection.
587
+ */
588
+ setEventService(eventService) {
589
+ this.eventService = eventService;
590
+ }
591
+ /**
592
+ * Get tool description
593
+ */
594
+ getDescription() {
595
+ return this.schema.description;
596
+ }
597
+ /**
598
+ * Execute the actual API call
599
+ * @private
600
+ */
601
+ async executeAPICall(parameters, _context) {
602
+ const operation = this.findOperation();
603
+ if (!operation) {
604
+ throw new Error(`Operation ${this.operationId} not found in OpenAPI spec`);
605
+ }
606
+ const requestConfig = this.buildRequestConfig(operation, parameters);
607
+ throw new Error("Not implemented: actual API execution is not yet available");
608
+ }
609
+ /**
610
+ * Find the operation in the OpenAPI specification
611
+ */
612
+ findOperation() {
613
+ for (const [path, pathItem] of Object.entries(this.apiSpec.paths || {})) {
614
+ if (!pathItem) continue;
615
+ for (const method of [
616
+ "get",
617
+ "post",
618
+ "put",
619
+ "delete",
620
+ "patch",
621
+ "head",
622
+ "options"
623
+ ]) {
624
+ const operation = pathItem[method];
625
+ if (operation?.operationId === this.operationId) {
626
+ return { method, path, operation };
627
+ }
628
+ }
629
+ }
630
+ return void 0;
631
+ }
632
+ /**
633
+ * Build HTTP request configuration from OpenAPI operation and parameters
634
+ */
635
+ buildRequestConfig(opInfo, parameters) {
636
+ const { method, path, operation } = opInfo;
637
+ let url = this.baseURL + path;
638
+ const headers = {};
639
+ let body;
640
+ const params = operation.parameters || [];
641
+ for (const param of params) {
642
+ const value = parameters[param.name];
643
+ if (value === void 0 && param.required) {
644
+ throw new Error(`Required parameter ${param.name} is missing`);
645
+ }
646
+ if (value !== void 0) {
647
+ switch (param.in) {
648
+ case "path":
649
+ url = url.replace(`{${param.name}}`, encodeURIComponent(String(value)));
650
+ break;
651
+ case "query": {
652
+ const separator = url.includes("?") ? "&" : "?";
653
+ url += `${separator}${param.name}=${encodeURIComponent(String(value))}`;
654
+ break;
655
+ }
656
+ case "header":
657
+ headers[param.name] = String(value);
658
+ break;
659
+ }
660
+ }
661
+ }
662
+ if (["post", "put", "patch"].includes(method) && operation.requestBody) {
663
+ const requestBody = operation.requestBody;
664
+ const jsonContent = requestBody.content?.["application/json"];
665
+ if (jsonContent) {
666
+ headers["Content-Type"] = "application/json";
667
+ const bodyParams = {};
668
+ for (const [key, value] of Object.entries(parameters)) {
669
+ const isParamUsed = params.some((p) => p.name === key);
670
+ if (!isParamUsed) {
671
+ bodyParams[key] = value;
672
+ }
673
+ }
674
+ body = JSON.stringify(bodyParams);
675
+ }
676
+ }
677
+ if (this.config.auth) {
678
+ switch (this.config.auth.type) {
679
+ case "bearer":
680
+ headers["Authorization"] = `Bearer ${this.config.auth.token}`;
681
+ break;
682
+ case "apiKey": {
683
+ const headerName = this.config.auth.header || "X-API-Key";
684
+ headers[headerName] = this.config.auth.apiKey || "";
685
+ break;
686
+ }
687
+ }
688
+ }
689
+ const result = {
690
+ method,
691
+ url,
692
+ headers
693
+ };
694
+ if (body !== void 0) {
695
+ result.body = body;
696
+ }
697
+ return result;
698
+ }
699
+ /**
700
+ * Create tool schema from OpenAPI operation specification
701
+ */
702
+ createSchemaFromOpenAPI() {
703
+ const operation = this.findOperation();
704
+ if (!operation) {
705
+ throw new Error(
706
+ `[STRICT-POLICY][EMITTER-CONTRACT] OpenAPI operation not found: ${this.operationId}. Emitter contract must provide a valid operationId present in the OpenAPI document.`
707
+ );
708
+ }
709
+ const { operation: opSpec } = operation;
710
+ const properties = {};
711
+ const required = [];
712
+ const params = opSpec.parameters || [];
713
+ for (const param of params) {
714
+ properties[param.name] = this.convertOpenAPIParamToSchema(param);
715
+ if (param.required) {
716
+ required.push(param.name);
717
+ }
718
+ }
719
+ if (opSpec.requestBody) {
720
+ const requestBody = opSpec.requestBody;
721
+ const jsonContent = requestBody.content?.["application/json"];
722
+ if (jsonContent?.schema) {
723
+ const bodySchema = this.convertOpenAPISchemaToParameterSchema(jsonContent.schema);
724
+ if (bodySchema.type === "object" && bodySchema.properties) {
725
+ Object.assign(properties, bodySchema.properties);
726
+ const schemaWithRequired = bodySchema;
727
+ if (schemaWithRequired.required) {
728
+ required.push(...schemaWithRequired.required);
729
+ }
730
+ }
731
+ }
732
+ }
733
+ const schemaParams = {
734
+ type: "object",
735
+ properties
736
+ };
737
+ if (required.length > 0) {
738
+ schemaParams.required = required;
739
+ }
740
+ return {
741
+ name: this.operationId,
742
+ description: opSpec.summary || opSpec.description || `OpenAPI operation: ${this.operationId}`,
743
+ parameters: schemaParams
744
+ };
745
+ }
746
+ /**
747
+ * Convert OpenAPI parameter to tool parameter schema
748
+ */
749
+ convertOpenAPIParamToSchema(param) {
750
+ const schema = param.schema;
751
+ return this.convertOpenAPISchemaToParameterSchema(schema);
752
+ }
753
+ /**
754
+ * Convert OpenAPI schema to parameter schema
755
+ */
756
+ convertOpenAPISchemaToParameterSchema(schema) {
757
+ if ("$ref" in schema) {
758
+ return { type: "object" };
759
+ }
760
+ const result = {
761
+ type: this.mapOpenAPIType(schema.type)
762
+ };
763
+ if (schema.description) {
764
+ result.description = schema.description;
765
+ }
766
+ if (schema.enum) {
767
+ result.enum = schema.enum;
768
+ }
769
+ if (schema.minimum !== void 0) {
770
+ result.minimum = schema.minimum;
771
+ }
772
+ if (schema.maximum !== void 0) {
773
+ result.maximum = schema.maximum;
774
+ }
775
+ if (schema.pattern) {
776
+ result.pattern = schema.pattern;
777
+ }
778
+ if (schema.format) {
779
+ result.format = schema.format;
780
+ }
781
+ if (schema.default !== void 0) {
782
+ result.default = schema.default;
783
+ }
784
+ if (schema.type === "array" && schema.items) {
785
+ result.items = this.convertOpenAPISchemaToParameterSchema(schema.items);
786
+ }
787
+ if (schema.type === "object" && schema.properties) {
788
+ result.properties = {};
789
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
790
+ result.properties[propName] = this.convertOpenAPISchemaToParameterSchema(propSchema);
791
+ }
792
+ if (schema.required && schema.required.length > 0) {
793
+ result.required = schema.required;
794
+ }
795
+ }
796
+ return result;
797
+ }
798
+ /**
799
+ * Map OpenAPI type to JSON schema type
800
+ */
801
+ mapOpenAPIType(type) {
802
+ switch (type) {
803
+ case "string":
804
+ return "string";
805
+ case "number":
806
+ return "number";
807
+ case "integer":
808
+ return "integer";
809
+ case "boolean":
810
+ return "boolean";
811
+ case "array":
812
+ return "array";
813
+ case "object":
814
+ return "object";
815
+ default:
816
+ return "string";
817
+ }
818
+ }
819
+ };
820
+ function createOpenAPITool(config) {
821
+ return new OpenAPITool(config);
822
+ }
823
+
824
+ // src/builtins/bash-tool.ts
825
+ var import_node_child_process = require("child_process");
826
+ var import_zod = require("zod");
827
+ var DEFAULT_TIMEOUT_MS = 12e4;
828
+ var BashSchema = import_zod.z.object({
829
+ command: import_zod.z.string().describe("The bash command to execute"),
830
+ timeout: import_zod.z.number().optional().describe("Optional timeout in milliseconds (max 600000). Default is 120000 (2 minutes)"),
831
+ workingDirectory: import_zod.z.string().optional().describe("Working directory for the command. Defaults to the current working directory")
832
+ });
833
+ async function runBash(args) {
834
+ const { command, timeout = DEFAULT_TIMEOUT_MS, workingDirectory } = args;
835
+ return new Promise((resolve3) => {
836
+ const stdoutChunks = [];
837
+ const stderrChunks = [];
838
+ let timedOut = false;
839
+ let settled = false;
840
+ const child = (0, import_node_child_process.spawn)("sh", ["-c", command], {
841
+ cwd: workingDirectory ?? process.cwd(),
842
+ env: process.env,
843
+ stdio: ["pipe", "pipe", "pipe"]
844
+ });
845
+ child.stdout.on("data", (chunk) => {
846
+ stdoutChunks.push(chunk);
847
+ });
848
+ child.stderr.on("data", (chunk) => {
849
+ stderrChunks.push(chunk);
850
+ });
851
+ const timer = setTimeout(() => {
852
+ timedOut = true;
853
+ child.kill("SIGTERM");
854
+ }, timeout);
855
+ function settle(result) {
856
+ if (settled) return;
857
+ settled = true;
858
+ clearTimeout(timer);
859
+ resolve3(JSON.stringify(result));
860
+ }
861
+ child.on("error", (err) => {
862
+ settle({
863
+ success: false,
864
+ output: "",
865
+ error: err.message
866
+ });
867
+ });
868
+ child.on("close", (code) => {
869
+ if (timedOut) {
870
+ settle({
871
+ success: false,
872
+ output: Buffer.concat(stdoutChunks).toString("utf8"),
873
+ error: `Command timed out after ${timeout}ms`,
874
+ exitCode: code ?? void 0
875
+ });
876
+ return;
877
+ }
878
+ const stdout = Buffer.concat(stdoutChunks).toString("utf8");
879
+ const stderr = Buffer.concat(stderrChunks).toString("utf8");
880
+ const exitCode = code ?? 0;
881
+ const output = stderr ? `${stdout}
882
+ stderr:
883
+ ${stderr}` : stdout;
884
+ settle({
885
+ success: true,
886
+ output,
887
+ exitCode
888
+ });
889
+ });
890
+ });
891
+ }
892
+ var bashTool = createZodFunctionTool(
893
+ "Bash",
894
+ "Executes a given bash command and returns its output.\n\nThe working directory persists between commands, but shell state does not.\n\nIMPORTANT: Avoid using this tool to run `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo` commands. Instead, use the appropriate dedicated tool:\n - File search: Use Glob (NOT find or ls)\n - Content search: Use Grep (NOT grep or rg)\n - Read files: Use Read (NOT cat/head/tail)\n - Edit files: Use Edit (NOT sed/awk)\n\nFor simple commands, keep the description brief (5-10 words). For complex commands, include enough context to clarify what the command does.\n\nOutput is limited to 30,000 characters. Longer output will be middle-truncated.",
895
+ BashSchema,
896
+ async (params) => {
897
+ return runBash(params);
898
+ }
899
+ );
900
+
901
+ // src/builtins/read-tool.ts
902
+ var import_promises = require("fs/promises");
903
+ var import_zod2 = require("zod");
904
+ var DEFAULT_LIMIT = 2e3;
905
+ var ReadSchema = import_zod2.z.object({
906
+ filePath: import_zod2.z.string().describe("The absolute path to the file to read"),
907
+ offset: import_zod2.z.number().optional().describe(
908
+ "The line number to start reading from (1-based). Only provide if the file is too large to read at once"
909
+ ),
910
+ limit: import_zod2.z.number().optional().describe(
911
+ `The number of lines to read (default: ${DEFAULT_LIMIT}). Only provide if the file is too large to read at once`
912
+ )
913
+ });
914
+ function isBinary(buffer) {
915
+ const checkLength = Math.min(buffer.length, 8192);
916
+ for (let i = 0; i < checkLength; i++) {
917
+ if (buffer[i] === 0) return true;
918
+ }
919
+ return false;
920
+ }
921
+ function formatWithLineNumbers(lines, startLine) {
922
+ const lastLineNum = startLine + lines.length - 1;
923
+ const width = String(lastLineNum).length;
924
+ return lines.map((line, idx) => {
925
+ const lineNum = String(startLine + idx).padStart(width, " ");
926
+ return `${lineNum} ${line}`;
927
+ }).join("\n");
928
+ }
929
+ async function readFileTool(args) {
930
+ const { filePath, offset, limit = DEFAULT_LIMIT } = args;
931
+ const startLine = offset !== void 0 && offset > 0 ? offset : 1;
932
+ let fileStats;
933
+ try {
934
+ fileStats = await (0, import_promises.stat)(filePath);
935
+ } catch (err) {
936
+ const result2 = {
937
+ success: false,
938
+ output: "",
939
+ error: `File not found: ${filePath}`
940
+ };
941
+ return JSON.stringify(result2);
942
+ }
943
+ if (!fileStats.isFile()) {
944
+ const result2 = {
945
+ success: false,
946
+ output: "",
947
+ error: `Path is not a file: ${filePath}`
948
+ };
949
+ return JSON.stringify(result2);
950
+ }
951
+ let buffer;
952
+ try {
953
+ buffer = await (0, import_promises.readFile)(filePath);
954
+ } catch (err) {
955
+ const result2 = {
956
+ success: false,
957
+ output: "",
958
+ error: err instanceof Error ? err.message : String(err)
959
+ };
960
+ return JSON.stringify(result2);
961
+ }
962
+ if (isBinary(buffer)) {
963
+ const result2 = {
964
+ success: false,
965
+ output: "",
966
+ error: `Binary file not supported: ${filePath}`
967
+ };
968
+ return JSON.stringify(result2);
969
+ }
970
+ const content = buffer.toString("utf8");
971
+ const allLines = content.split("\n");
972
+ if (allLines[allLines.length - 1] === "") {
973
+ allLines.pop();
974
+ }
975
+ const zeroBasedStart = startLine - 1;
976
+ const selectedLines = allLines.slice(zeroBasedStart, zeroBasedStart + limit);
977
+ const output = formatWithLineNumbers(selectedLines, startLine);
978
+ const totalLines = allLines.length;
979
+ const returnedLines = selectedLines.length;
980
+ const header = returnedLines < totalLines ? `[File: ${filePath} (lines ${startLine}-${startLine + returnedLines - 1} of ${totalLines})]
981
+ ` : `[File: ${filePath} (${totalLines} lines)]
982
+ `;
983
+ const result = {
984
+ success: true,
985
+ output: header + output
986
+ };
987
+ return JSON.stringify(result);
988
+ }
989
+ var readTool = createZodFunctionTool(
990
+ "Read",
991
+ "Reads a file from the local filesystem.\n\nBy default, reads up to 2000 lines from the beginning of the file. You can optionally specify offset and limit for partial reads.\n\nResults are returned using cat -n format, with line numbers starting at 1.\n\nThe file_path parameter must be an absolute path, not a relative path.",
992
+ ReadSchema,
993
+ async (params) => {
994
+ return readFileTool(params);
995
+ }
996
+ );
997
+
998
+ // src/builtins/write-tool.ts
999
+ var import_promises2 = require("fs/promises");
1000
+ var import_node_path = require("path");
1001
+ var import_zod3 = require("zod");
1002
+ var WriteSchema = import_zod3.z.object({
1003
+ filePath: import_zod3.z.string().describe("The absolute path to the file to write"),
1004
+ content: import_zod3.z.string().describe("The content to write to the file")
1005
+ });
1006
+ async function writeFileTool(args) {
1007
+ const { filePath, content } = args;
1008
+ try {
1009
+ await (0, import_promises2.mkdir)((0, import_node_path.dirname)(filePath), { recursive: true });
1010
+ await (0, import_promises2.writeFile)(filePath, content, "utf8");
1011
+ const result = {
1012
+ success: true,
1013
+ output: `Written ${content.length} bytes to ${filePath}`
1014
+ };
1015
+ return JSON.stringify(result);
1016
+ } catch (err) {
1017
+ const result = {
1018
+ success: false,
1019
+ output: "",
1020
+ error: err instanceof Error ? err.message : String(err)
1021
+ };
1022
+ return JSON.stringify(result);
1023
+ }
1024
+ }
1025
+ var writeTool = createZodFunctionTool(
1026
+ "Write",
1027
+ "Writes a file to the local filesystem. This will overwrite an existing file if one exists.\n\nALWAYS prefer the Edit tool for modifying existing files \u2014 it only sends the diff. Only use this tool to create new files or for complete rewrites.\n\nNEVER create documentation files (*.md) or README files unless explicitly requested by the user.",
1028
+ WriteSchema,
1029
+ async (params) => {
1030
+ return writeFileTool(params);
1031
+ }
1032
+ );
1033
+
1034
+ // src/builtins/edit-tool.ts
1035
+ var import_promises3 = require("fs/promises");
1036
+ var import_zod4 = require("zod");
1037
+ var EditSchema = import_zod4.z.object({
1038
+ filePath: import_zod4.z.string().describe("The absolute path to the file to modify"),
1039
+ oldString: import_zod4.z.string().describe("The text to replace (must be an exact match of existing content)"),
1040
+ newString: import_zod4.z.string().describe("The text to replace it with (must be different from old_string)"),
1041
+ replaceAll: import_zod4.z.boolean().optional().describe(
1042
+ "Replace all occurrences of old_string (default: false). Useful for renaming variables"
1043
+ )
1044
+ });
1045
+ async function editFileTool(args) {
1046
+ const { filePath, oldString, newString, replaceAll = false } = args;
1047
+ let content;
1048
+ try {
1049
+ content = await (0, import_promises3.readFile)(filePath, "utf8");
1050
+ } catch (err) {
1051
+ const result2 = {
1052
+ success: false,
1053
+ output: "",
1054
+ error: `File not found: ${filePath}`
1055
+ };
1056
+ return JSON.stringify(result2);
1057
+ }
1058
+ if (!content.includes(oldString)) {
1059
+ const result2 = {
1060
+ success: false,
1061
+ output: "",
1062
+ error: `oldString not found in file: ${filePath}`
1063
+ };
1064
+ return JSON.stringify(result2);
1065
+ }
1066
+ if (!replaceAll) {
1067
+ const firstIdx = content.indexOf(oldString);
1068
+ const lastIdx = content.lastIndexOf(oldString);
1069
+ if (firstIdx !== lastIdx) {
1070
+ const occurrences = content.split(oldString).length - 1;
1071
+ const result2 = {
1072
+ success: false,
1073
+ output: "",
1074
+ error: `oldString is not unique in file (found ${occurrences} occurrences). Provide more context to make it unique, or use replaceAll:true.`
1075
+ };
1076
+ return JSON.stringify(result2);
1077
+ }
1078
+ }
1079
+ const updated = replaceAll ? content.split(oldString).join(newString) : content.slice(0, content.indexOf(oldString)) + newString + content.slice(content.indexOf(oldString) + oldString.length);
1080
+ try {
1081
+ await (0, import_promises3.writeFile)(filePath, updated, "utf8");
1082
+ } catch (err) {
1083
+ const result2 = {
1084
+ success: false,
1085
+ output: "",
1086
+ error: err instanceof Error ? err.message : String(err)
1087
+ };
1088
+ return JSON.stringify(result2);
1089
+ }
1090
+ const count = replaceAll ? content.split(oldString).length - 1 : 1;
1091
+ const result = {
1092
+ success: true,
1093
+ output: `Replaced ${count} occurrence(s) in ${filePath}`
1094
+ };
1095
+ return JSON.stringify(result);
1096
+ }
1097
+ var editTool = createZodFunctionTool(
1098
+ "Edit",
1099
+ "Performs exact string replacements in files.\n\nYou must use the Read tool at least once before editing. When editing text from Read output, preserve the exact indentation.\n\nThe edit will FAIL if old_string is not unique in the file. Either provide more surrounding context to make it unique, or use replace_all to change every instance.\n\nALWAYS prefer editing existing files over creating new ones.",
1100
+ EditSchema,
1101
+ async (params) => {
1102
+ return editFileTool(params);
1103
+ }
1104
+ );
1105
+
1106
+ // src/builtins/glob-tool.ts
1107
+ var import_promises4 = require("fs/promises");
1108
+ var import_node_path2 = require("path");
1109
+ var import_fast_glob = __toESM(require("fast-glob"), 1);
1110
+ var import_zod5 = require("zod");
1111
+ var DEFAULT_MAX_RESULTS = 1e3;
1112
+ var GlobSchema = import_zod5.z.object({
1113
+ pattern: import_zod5.z.string().describe('The glob pattern to match files against (e.g. "**/*.ts", "src/**/*.tsx")'),
1114
+ path: import_zod5.z.string().optional().describe(
1115
+ "The directory to search in. Defaults to the current working directory. Must be a valid directory path if provided"
1116
+ ),
1117
+ limit: import_zod5.z.number().optional().describe(
1118
+ "Maximum number of results to return (default: 1000). Use a smaller limit to save context space"
1119
+ )
1120
+ });
1121
+ async function globFileTool(args) {
1122
+ const { pattern, path: basePath } = args;
1123
+ const cwd = basePath ? (0, import_node_path2.resolve)(basePath) : process.cwd();
1124
+ let matches;
1125
+ try {
1126
+ matches = await (0, import_fast_glob.default)(pattern, {
1127
+ cwd,
1128
+ ignore: ["**/node_modules/**", "**/.git/**"],
1129
+ dot: true,
1130
+ absolute: false
1131
+ });
1132
+ } catch (err) {
1133
+ const result2 = {
1134
+ success: false,
1135
+ output: "",
1136
+ error: err instanceof Error ? err.message : String(err)
1137
+ };
1138
+ return JSON.stringify(result2);
1139
+ }
1140
+ const withMtime = await Promise.all(
1141
+ matches.map(async (p) => {
1142
+ const absPath = (0, import_node_path2.resolve)(cwd, p);
1143
+ try {
1144
+ const s = await (0, import_promises4.stat)(absPath);
1145
+ return { path: p, mtime: s.mtimeMs };
1146
+ } catch {
1147
+ return { path: p, mtime: 0 };
1148
+ }
1149
+ })
1150
+ );
1151
+ withMtime.sort((a, b) => b.mtime - a.mtime);
1152
+ const maxResults = args.limit ?? DEFAULT_MAX_RESULTS;
1153
+ const totalMatches = withMtime.length;
1154
+ const truncated = totalMatches > maxResults;
1155
+ const limited = truncated ? withMtime.slice(0, maxResults) : withMtime;
1156
+ const sorted = limited.map((f) => f.path);
1157
+ let output = sorted.length > 0 ? sorted.join("\n") : "(no matches)";
1158
+ if (truncated) {
1159
+ output += `
1160
+
1161
+ [Showing ${maxResults} of ${totalMatches} matches. Use limit parameter to see more.]`;
1162
+ }
1163
+ const result = {
1164
+ success: true,
1165
+ output
1166
+ };
1167
+ return JSON.stringify(result);
1168
+ }
1169
+ var globTool = createZodFunctionTool(
1170
+ "Glob",
1171
+ "Fast file pattern matching tool that works with any codebase size.\n\nSupports glob patterns like '**/*.js' or 'src/**/*.ts'. Returns matching file paths sorted by modification time.\n\nUse this tool when you need to find files by name patterns. When doing an open-ended search that may require multiple rounds, use the Agent tool instead.\n\nDefault limit is 1000 results. Use the limit parameter if you need fewer results to save context space.",
1172
+ GlobSchema,
1173
+ async (params) => {
1174
+ return globFileTool(params);
1175
+ }
1176
+ );
1177
+
1178
+ // src/builtins/grep-tool.ts
1179
+ var import_promises5 = require("fs/promises");
1180
+ var import_node_path3 = require("path");
1181
+ var import_zod6 = require("zod");
1182
+ var GrepSchema = import_zod6.z.object({
1183
+ pattern: import_zod6.z.string().describe("The regular expression pattern to search for in file contents"),
1184
+ path: import_zod6.z.string().optional().describe("File or directory to search in. Defaults to the current working directory"),
1185
+ glob: import_zod6.z.string().optional().describe(
1186
+ 'Glob pattern to filter files (e.g. "*.ts", "*.{ts,tsx}"). Only files matching this pattern will be searched'
1187
+ ),
1188
+ contextLines: import_zod6.z.number().optional().describe(
1189
+ 'Number of context lines to show before and after each match. Only applies when outputMode is "content". Default: 0'
1190
+ ),
1191
+ outputMode: import_zod6.z.enum(["files_with_matches", "content"]).optional().describe(
1192
+ 'Output mode: "files_with_matches" shows only file paths (default), "content" shows matching lines with context'
1193
+ )
1194
+ });
1195
+ function globToRegex(glob) {
1196
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".+").replace(/\*/g, "[^/]*");
1197
+ return new RegExp(`^${escaped}$`);
1198
+ }
1199
+ function matchesGlob(filename, glob) {
1200
+ if (glob === void 0) return true;
1201
+ return globToRegex(glob).test(filename);
1202
+ }
1203
+ async function collectFiles(dirPath, glob) {
1204
+ const results = [];
1205
+ async function walk(current) {
1206
+ let entryNames;
1207
+ try {
1208
+ entryNames = await (0, import_promises5.readdir)(current);
1209
+ } catch {
1210
+ return;
1211
+ }
1212
+ for (const name of entryNames) {
1213
+ if (name === "node_modules" || name === ".git") continue;
1214
+ const fullPath = (0, import_node_path3.join)(current, name);
1215
+ let fileStat;
1216
+ try {
1217
+ fileStat = await (0, import_promises5.stat)(fullPath);
1218
+ } catch {
1219
+ continue;
1220
+ }
1221
+ if (fileStat.isDirectory()) {
1222
+ await walk(fullPath);
1223
+ } else if (fileStat.isFile()) {
1224
+ if (matchesGlob(name, glob)) {
1225
+ results.push(fullPath);
1226
+ }
1227
+ }
1228
+ }
1229
+ }
1230
+ await walk(dirPath);
1231
+ return results;
1232
+ }
1233
+ function searchFile(content, filePath, regex, contextLines, outputMode) {
1234
+ const lines = content.split("\n");
1235
+ const matchingIndices = [];
1236
+ for (let i = 0; i < lines.length; i++) {
1237
+ if (regex.test(lines[i])) {
1238
+ matchingIndices.push(i);
1239
+ }
1240
+ }
1241
+ if (matchingIndices.length === 0) return [];
1242
+ if (outputMode === "files_with_matches") {
1243
+ return [filePath];
1244
+ }
1245
+ const includedIndices = /* @__PURE__ */ new Set();
1246
+ for (const idx of matchingIndices) {
1247
+ for (let c = Math.max(0, idx - contextLines); c <= Math.min(lines.length - 1, idx + contextLines); c++) {
1248
+ includedIndices.add(c);
1249
+ }
1250
+ }
1251
+ const outputLines = [];
1252
+ const sortedIndices = Array.from(includedIndices).sort((a, b) => a - b);
1253
+ let prevIdx;
1254
+ for (const idx of sortedIndices) {
1255
+ if (prevIdx !== void 0 && idx > prevIdx + 1) {
1256
+ outputLines.push("--");
1257
+ }
1258
+ const lineNum = idx + 1;
1259
+ const marker = matchingIndices.includes(idx) ? ":" : "-";
1260
+ outputLines.push(`${filePath}:${lineNum}${marker}${lines[idx]}`);
1261
+ prevIdx = idx;
1262
+ }
1263
+ return outputLines;
1264
+ }
1265
+ async function grepFileTool(args) {
1266
+ const {
1267
+ pattern,
1268
+ path: searchPath,
1269
+ glob,
1270
+ contextLines = 0,
1271
+ outputMode = "files_with_matches"
1272
+ } = args;
1273
+ const targetPath = searchPath ? (0, import_node_path3.resolve)(searchPath) : process.cwd();
1274
+ let regex;
1275
+ try {
1276
+ regex = new RegExp(pattern);
1277
+ } catch (err) {
1278
+ const result2 = {
1279
+ success: false,
1280
+ output: "",
1281
+ error: `Invalid regex pattern: ${pattern}`
1282
+ };
1283
+ return JSON.stringify(result2);
1284
+ }
1285
+ let targetStat;
1286
+ try {
1287
+ targetStat = await (0, import_promises5.stat)(targetPath);
1288
+ } catch {
1289
+ const result2 = {
1290
+ success: false,
1291
+ output: "",
1292
+ error: `Path not found: ${targetPath}`
1293
+ };
1294
+ return JSON.stringify(result2);
1295
+ }
1296
+ let files;
1297
+ if (targetStat.isFile()) {
1298
+ files = [targetPath];
1299
+ } else {
1300
+ files = await collectFiles(targetPath, glob);
1301
+ }
1302
+ const allOutputLines = [];
1303
+ for (const filePath of files) {
1304
+ let content;
1305
+ try {
1306
+ const buffer = await (0, import_promises5.readFile)(filePath);
1307
+ const checkLen = Math.min(buffer.length, 8192);
1308
+ let hasBinary = false;
1309
+ for (let i = 0; i < checkLen; i++) {
1310
+ if (buffer[i] === 0) {
1311
+ hasBinary = true;
1312
+ break;
1313
+ }
1314
+ }
1315
+ if (hasBinary) continue;
1316
+ content = buffer.toString("utf8");
1317
+ } catch {
1318
+ continue;
1319
+ }
1320
+ const fileMatches = searchFile(content, filePath, regex, contextLines, outputMode);
1321
+ allOutputLines.push(...fileMatches);
1322
+ }
1323
+ const result = {
1324
+ success: true,
1325
+ output: allOutputLines.length > 0 ? allOutputLines.join("\n") : "(no matches)"
1326
+ };
1327
+ return JSON.stringify(result);
1328
+ }
1329
+ var grepTool = createZodFunctionTool(
1330
+ "Grep",
1331
+ "A powerful search tool built on regex matching.\n\nSupports full regex syntax (e.g., 'log.*Error', 'function\\\\s+\\\\w+'). Filter files with glob parameter (e.g., '*.js', '**/*.tsx').\n\nOutput modes: 'content' shows matching lines with context, 'files_with_matches' shows only file paths (default), 'count' shows match counts.\n\nUse this tool for ALL search tasks. NEVER invoke grep or rg as a Bash command.\n\nUse head_limit to control result size and save context space.",
1332
+ GrepSchema,
1333
+ async (params) => {
1334
+ return grepFileTool(params);
1335
+ }
1336
+ );
1337
+ // Annotate the CommonJS export names for ESM import in node:
1338
+ 0 && (module.exports = {
1339
+ FunctionTool,
1340
+ OpenAPITool,
1341
+ ToolRegistry,
1342
+ bashTool,
1343
+ createFunctionTool,
1344
+ createOpenAPITool,
1345
+ createZodFunctionTool,
1346
+ editTool,
1347
+ globTool,
1348
+ grepTool,
1349
+ readTool,
1350
+ writeTool,
1351
+ zodToJsonSchema
1352
+ });