@evalops/maestro 0.10.39 → 0.10.41

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/dist/cli.js CHANGED
@@ -73409,16 +73409,37 @@ function cloneToolResultForCache(message) {
73409
73409
  content: message.content.map((item) => ({ ...item }))
73410
73410
  };
73411
73411
  }
73412
- function cloneToolOutcomeForCall(outcome, toolCall, timestamp3) {
73412
+ function localToolExecutionIdForCall(toolCall) {
73413
+ return `local-tool-exec-${toolCall.id}`;
73414
+ }
73415
+ function attachToolExecutionMetadata(message, metadata, options = {}) {
73416
+ const details = message.details && typeof message.details === "object" ? { ...message.details } : {};
73417
+ if (metadata.toolExecutionId) {
73418
+ details.toolExecutionId = metadata.toolExecutionId;
73419
+ }
73420
+ if (metadata.approvalRequestId) {
73421
+ details.approvalRequestId = metadata.approvalRequestId;
73422
+ } else if (options.replaceApprovalRequestId) {
73423
+ delete details.approvalRequestId;
73424
+ }
73413
73425
  return {
73414
- message: {
73426
+ ...message,
73427
+ details
73428
+ };
73429
+ }
73430
+ function cloneToolOutcomeForCall(outcome, toolCall, timestamp3, metadata = {}) {
73431
+ const toolExecutionId = localToolExecutionIdForCall(toolCall);
73432
+ return {
73433
+ message: attachToolExecutionMetadata({
73415
73434
  ...outcome.message,
73416
73435
  toolCallId: toolCall.id,
73417
73436
  toolName: toolCall.name,
73418
73437
  content: outcome.message.content.map((item) => ({ ...item })),
73419
73438
  timestamp: timestamp3
73420
- },
73421
- isError: outcome.isError
73439
+ }, { toolExecutionId, approvalRequestId: metadata.approvalRequestId }, { replaceApprovalRequestId: true }),
73440
+ isError: outcome.isError,
73441
+ toolExecutionId,
73442
+ ...metadata.approvalRequestId ? { approvalRequestId: metadata.approvalRequestId } : {}
73422
73443
  };
73423
73444
  }
73424
73445
  function resolvePlatformToolExecutionBridge(option) {
@@ -73439,9 +73460,11 @@ async function recordReusableToolExecutionBridgeOutput({
73439
73460
  }
73440
73461
  const observed = plan.kind === "observe" ? await bridge.recordObservation(plan, outcome.message, signal) : void 0;
73441
73462
  const governedOutput = plan.kind === "governed" ? await bridge.recordGovernedOutput(plan, outcome.message, durationMs, signal) : void 0;
73463
+ const metadata = buildObservedResultMetadata(plan, observed ?? governedOutput);
73442
73464
  return {
73443
73465
  ...outcome,
73444
- ...buildObservedResultMetadata(plan, observed ?? governedOutput)
73466
+ ...metadata,
73467
+ message: attachToolExecutionMetadata(outcome.message, metadata)
73445
73468
  };
73446
73469
  }
73447
73470
  function hasReusableToolResultState(cacheKey, cache4, pending, policyCheckedKeys, pendingSafetyChecks) {
@@ -82771,6 +82794,7 @@ function createToolExecutionPromise(ctx) {
82771
82794
  const autoMaxAttempts = isClientTool ? 1 : toolHasRetryConfig ? (tool.maxRetries ?? resolvedRetryConfig.maxAutoRetries) + 1 : allowAutoRetries ? resolvedRetryConfig.maxAutoRetries + 1 : 1;
82772
82795
  const retryDelay = toolHasRetryConfig ? tool.retryDelayMs ?? resolvedRetryConfig.initialDelayMs : resolvedRetryConfig.initialDelayMs;
82773
82796
  const shouldRetryFn = tool.shouldRetry ?? isRetryableToolError;
82797
+ const fallbackToolExecutionId = `local-tool-exec-${toolCall.id}`;
82774
82798
  const context2 = cfg.sandbox ? { sandbox: cfg.sandbox } : void 0;
82775
82799
  const onUpdate = (partialResult) => {
82776
82800
  toolUpdateQueue.push({
@@ -82798,20 +82822,25 @@ function createToolExecutionPromise(ctx) {
82798
82822
  }
82799
82823
  return runWithMcpClientToolService(clientToolService2, () => tool.execute(toolCall.id, validatedArgs, signal, context2, onUpdate));
82800
82824
  };
82801
- const attachApprovalRequestId = (message) => {
82802
- if (!approvalRequestId) {
82803
- return;
82804
- }
82805
- const details = message.details && typeof message.details === "object" ? { ...message.details } : {};
82806
- if (typeof details.approvalRequestId !== "string") {
82807
- details.approvalRequestId = approvalRequestId;
82808
- }
82809
- message.details = details;
82825
+ const toolExecutionIdFromMessage = (message) => {
82826
+ const details = message.details && typeof message.details === "object" ? message.details : {};
82827
+ return typeof details.toolExecutionId === "string" && details.toolExecutionId.length > 0 ? details.toolExecutionId : void 0;
82810
82828
  };
82811
- const withLocalApprovalMetadata = (metadata) => ({
82829
+ const withLocalExecutionMetadata = (metadata, message) => ({
82812
82830
  ...metadata,
82831
+ toolExecutionId: metadata.toolExecutionId ?? (message ? toolExecutionIdFromMessage(message) : void 0) ?? fallbackToolExecutionId,
82813
82832
  ...approvalRequestId && !metadata.approvalRequestId ? { approvalRequestId } : {}
82814
82833
  });
82834
+ const attachToolResultLinkage = (message, metadata) => {
82835
+ const resolvedMetadata = withLocalExecutionMetadata(metadata, message);
82836
+ const details = message.details && typeof message.details === "object" ? { ...message.details } : {};
82837
+ details.toolExecutionId = resolvedMetadata.toolExecutionId;
82838
+ if (resolvedMetadata.approvalRequestId && typeof details.approvalRequestId !== "string") {
82839
+ details.approvalRequestId = resolvedMetadata.approvalRequestId;
82840
+ }
82841
+ message.details = details;
82842
+ return resolvedMetadata;
82843
+ };
82815
82844
  const executeWithRetry = async () => {
82816
82845
  let totalAttempts = 0;
82817
82846
  let userRetryRounds = 0;
@@ -82905,7 +82934,10 @@ function createToolExecutionPromise(ctx) {
82905
82934
  isError: result2.isError || false,
82906
82935
  timestamp: clock.now()
82907
82936
  };
82908
- attachApprovalRequestId(toolResultMsg);
82937
+ attachToolResultLinkage(toolResultMsg, {
82938
+ toolExecutionId: result2.toolExecutionId ?? toolExecutionBridgePlan?.metadata.toolExecutionId,
82939
+ approvalRequestId: result2.approvalRequestId ?? toolExecutionBridgePlan?.metadata.approvalRequestId
82940
+ });
82909
82941
  if (hookService && !result2.isError) {
82910
82942
  const postHookResult = await hookService.runPostToolUseHooks(effectiveToolCall, toolResultMsg, signal);
82911
82943
  if (postHookResult.additionalContext) {
@@ -82978,10 +83010,11 @@ function createToolExecutionPromise(ctx) {
82978
83010
  }
82979
83011
  const observed = toolExecutionBridge && toolExecutionBridgePlan?.kind === "observe" ? await toolExecutionBridge.recordObservation(toolExecutionBridgePlan, toolResultMsg, signal) : void 0;
82980
83012
  const governedOutput = toolExecutionBridge && toolExecutionBridgePlan?.kind === "governed" ? await toolExecutionBridge.recordGovernedOutput(toolExecutionBridgePlan, toolResultMsg, clock.now() - startTime, signal) : void 0;
83013
+ const linkageMetadata = attachToolResultLinkage(toolResultMsg, buildObservedResultMetadata(toolExecutionBridgePlan, observed ?? governedOutput));
82981
83014
  return {
82982
83015
  message: toolResultMsg,
82983
83016
  isError: toolResultMsg.isError,
82984
- ...withLocalApprovalMetadata(buildObservedResultMetadata(toolExecutionBridgePlan, observed ?? governedOutput))
83017
+ ...linkageMetadata
82985
83018
  };
82986
83019
  } catch (error) {
82987
83020
  if (error instanceof Error && error.name === "AbortError") {
@@ -83006,7 +83039,10 @@ function createToolExecutionPromise(ctx) {
83006
83039
  isError: true,
83007
83040
  timestamp: clock.now()
83008
83041
  };
83009
- attachApprovalRequestId(toolResultMsg);
83042
+ attachToolResultLinkage(toolResultMsg, {
83043
+ toolExecutionId: toolExecutionBridgePlan?.metadata.toolExecutionId,
83044
+ approvalRequestId: toolExecutionBridgePlan?.metadata.approvalRequestId
83045
+ });
83010
83046
  if (hookService) {
83011
83047
  const failureHookResult = await hookService.runPostToolUseFailureHooks(effectiveToolCall, baseErrorMessage, signal);
83012
83048
  if (failureHookResult.additionalContext) {
@@ -83022,10 +83058,11 @@ function createToolExecutionPromise(ctx) {
83022
83058
  }
83023
83059
  const observed = toolExecutionBridge && toolExecutionBridgePlan?.kind === "observe" ? await toolExecutionBridge.recordObservation(toolExecutionBridgePlan, toolResultMsg, signal) : void 0;
83024
83060
  const governedOutput = toolExecutionBridge && toolExecutionBridgePlan?.kind === "governed" ? await toolExecutionBridge.recordGovernedOutput(toolExecutionBridgePlan, toolResultMsg, clock.now() - startTime, signal) : void 0;
83061
+ const linkageMetadata = attachToolResultLinkage(toolResultMsg, buildObservedResultMetadata(toolExecutionBridgePlan, observed ?? governedOutput));
83025
83062
  return {
83026
83063
  message: toolResultMsg,
83027
83064
  isError: true,
83028
- ...withLocalApprovalMetadata(buildObservedResultMetadata(toolExecutionBridgePlan, observed ?? governedOutput))
83065
+ ...linkageMetadata
83029
83066
  };
83030
83067
  }
83031
83068
  })();
@@ -84240,18 +84277,7 @@ var ProviderTransport = class _ProviderTransport {
84240
84277
  const cachedOutcome = await recordReusableToolExecutionBridgeOutput({
84241
84278
  bridge: platformToolExecutionBridge,
84242
84279
  plan: safetyVerdict.toolExecutionBridgePlan,
84243
- outcome: {
84244
- message: {
84245
- ...cachedEntry.message,
84246
- toolCallId: toolCall.id,
84247
- toolName: toolCall.name,
84248
- content: cachedEntry.message.content.map((item) => ({
84249
- ...item
84250
- })),
84251
- timestamp: this.clock.now()
84252
- },
84253
- isError: false
84254
- },
84280
+ outcome: cloneToolOutcomeForCall({ message: cachedEntry.message, isError: false }, toolCall, this.clock.now(), { approvalRequestId: safetyVerdict.approvalRequestId }),
84255
84281
  durationMs: this.clock.now() - cacheHitStart,
84256
84282
  signal
84257
84283
  });
@@ -84267,7 +84293,7 @@ var ProviderTransport = class _ProviderTransport {
84267
84293
  const cachedOutcome = await recordReusableToolExecutionBridgeOutput({
84268
84294
  bridge: platformToolExecutionBridge,
84269
84295
  plan: safetyVerdict.toolExecutionBridgePlan,
84270
- outcome: cloneToolOutcomeForCall(await pendingReusable, toolCall, this.clock.now()),
84296
+ outcome: cloneToolOutcomeForCall(await pendingReusable, toolCall, this.clock.now(), { approvalRequestId: safetyVerdict.approvalRequestId }),
84271
84297
  durationMs: this.clock.now() - cacheHitStart,
84272
84298
  signal
84273
84299
  });
@@ -85189,18 +85215,7 @@ var ProviderTransport = class _ProviderTransport {
85189
85215
  const cachedOutcome = await recordReusableToolExecutionBridgeOutput({
85190
85216
  bridge: platformToolExecutionBridge,
85191
85217
  plan: safetyVerdict.toolExecutionBridgePlan,
85192
- outcome: {
85193
- message: {
85194
- ...cachedEntry.message,
85195
- toolCallId: toolCall.id,
85196
- toolName: toolCall.name,
85197
- content: cachedEntry.message.content.map((item) => ({
85198
- ...item
85199
- })),
85200
- timestamp: this.clock.now()
85201
- },
85202
- isError: false
85203
- },
85218
+ outcome: cloneToolOutcomeForCall({ message: cachedEntry.message, isError: false }, toolCall, this.clock.now(), { approvalRequestId: safetyVerdict.approvalRequestId }),
85204
85219
  durationMs: this.clock.now() - cacheHitStart,
85205
85220
  signal
85206
85221
  });
@@ -85254,7 +85269,9 @@ var ProviderTransport = class _ProviderTransport {
85254
85269
  return recordReusableToolExecutionBridgeOutput({
85255
85270
  bridge: platformToolExecutionBridge,
85256
85271
  plan: safetyVerdict.toolExecutionBridgePlan,
85257
- outcome: cloneToolOutcomeForCall(await pendingReusable, toolCall, this.clock.now()),
85272
+ outcome: cloneToolOutcomeForCall(await pendingReusable, toolCall, this.clock.now(), {
85273
+ approvalRequestId: safetyVerdict.approvalRequestId
85274
+ }),
85258
85275
  durationMs: this.clock.now() - cacheHitStart,
85259
85276
  signal
85260
85277
  });
@@ -160457,6 +160474,9 @@ function shellQuoteArg(value) {
160457
160474
  function formatRipgrepCommand(args) {
160458
160475
  return ["rg", ...args].map(shellQuoteArg).join(" ");
160459
160476
  }
160477
+ function isRipgrepPathError(message) {
160478
+ return /No such file or directory|IO error|os error 2|system cannot find the path/i.test(message);
160479
+ }
160460
160480
  async function runRipgrep(args, signal, cwd) {
160461
160481
  const executable = await resolveRipgrepExecutableWithAbort(signal);
160462
160482
  throwIfRipgrepAborted(signal);
@@ -160786,6 +160806,18 @@ ${reason}`).detail({
160786
160806
  for (const { pattern, result: result2 } of results) {
160787
160807
  if (result2.exitCode === 2) {
160788
160808
  const message = result2.stderr.trim() || result2.stdout.trim();
160809
+ if (isRipgrepPathError(message)) {
160810
+ return respond.error(`ripgrep failed
160811
+
160812
+ ${message.length > 0 ? message : "ripgrep path lookup failed"}`).detail({
160813
+ commands,
160814
+ cwd: commandCwd,
160815
+ matchCount: 0,
160816
+ rangeCount: 0,
160817
+ ranges: [],
160818
+ truncated: false
160819
+ });
160820
+ }
160789
160821
  throw new Error(message.length > 0 ? message : "ripgrep exited with an error");
160790
160822
  }
160791
160823
  if (result2.exitCode === 1 || result2.stdout.trim().length === 0) {
@@ -161063,6 +161095,11 @@ ${reason}`).detail({ command, cwd: commandCwd });
161063
161095
  }
161064
161096
  if (result2.exitCode === 2) {
161065
161097
  const message = result2.stderr.trim() || result2.stdout.trim();
161098
+ if (isRipgrepPathError(message)) {
161099
+ return respond.error(`ripgrep failed
161100
+
161101
+ ${message.length > 0 ? message : "ripgrep path lookup failed"}`).detail({ command, cwd: commandCwd });
161102
+ }
161066
161103
  throw new Error(message.length > 0 ? message : "ripgrep exited with an error");
161067
161104
  }
161068
161105
  if (result2.exitCode === 1 || result2.stdout.trim().length === 0) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evalops/contracts",
3
- "version": "0.10.39",
3
+ "version": "0.10.41",
4
4
  "description": "Shared Maestro contracts for frontend/backend integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evalops/tui",
3
- "version": "0.10.39",
3
+ "version": "0.10.41",
4
4
  "description": "Terminal UI library with differential rendering for Maestro",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1 +1 @@
1
- {"version":3,"file":"parallel-ripgrep.d.ts","sourceRoot":"","sources":["../../src/tools/parallel-ripgrep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA6GH;;;GAGG;AACH,KAAK,WAAW,GAAG;IAClB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,KAAK,sBAAsB,GAAG;IAC7B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC;AA+HF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4N9B,CAAC"}
1
+ {"version":3,"file":"parallel-ripgrep.d.ts","sourceRoot":"","sources":["../../src/tools/parallel-ripgrep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA8GH;;;GAGG;AACH,KAAK,WAAW,GAAG;IAClB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,KAAK,sBAAsB,GAAG;IAC7B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC;AA+HF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0O9B,CAAC"}
@@ -22,7 +22,7 @@
22
22
  import { promises as fs } from "node:fs";
23
23
  import { resolve as resolvePath } from "node:path";
24
24
  import { Type } from "@sinclair/typebox";
25
- import { formatRipgrepCommand, globSchema, parseRipgrepJson, pathSchema, runRipgrep, toArray, } from "./ripgrep-utils.js";
25
+ import { formatRipgrepCommand, globSchema, isRipgrepPathError, parseRipgrepJson, pathSchema, runRipgrep, toArray, } from "./ripgrep-utils.js";
26
26
  import { createTool, expandUserPath } from "./tool-dsl.js";
27
27
  /** Maximum number of line ranges to include in detailed output */
28
28
  const RANGE_DETAIL_LIMIT = 200;
@@ -279,6 +279,18 @@ export const parallelRipgrepTool = createTool({
279
279
  for (const { pattern, result } of results) {
280
280
  if (result.exitCode === 2) {
281
281
  const message = result.stderr.trim() || result.stdout.trim();
282
+ if (isRipgrepPathError(message)) {
283
+ return respond
284
+ .error(`ripgrep failed\n\n${message.length > 0 ? message : "ripgrep path lookup failed"}`)
285
+ .detail({
286
+ commands,
287
+ cwd: commandCwd,
288
+ matchCount: 0,
289
+ rangeCount: 0,
290
+ ranges: [],
291
+ truncated: false,
292
+ });
293
+ }
282
294
  throw new Error(message.length > 0 ? message : "ripgrep exited with an error");
283
295
  }
284
296
  if (result.exitCode === 1 || result.stdout.trim().length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"parallel-ripgrep.js","sourceRoot":"","sources":["../../src/tools/parallel-ripgrep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EACN,oBAAoB,EACpB,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,OAAO,GACP,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE3D,kEAAkE;AAClE,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,kEAAkE;AAClE,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,MAAM,qBAAqB,GAAG,IAAI,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE;QACnD,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,EAAE;QACZ,WAAW,EACV,2FAA2F;KAC5F,CAAC;IACF,KAAK,EAAE,UAAU;IACjB,IAAI,EAAE,UAAU;IAChB,UAAU,EAAE,IAAI,CAAC,QAAQ,CACxB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,+CAA+C;QAC5D,OAAO,EAAE,KAAK;KACd,CAAC,CACF;IACD,OAAO,EAAE,IAAI,CAAC,QAAQ,CACrB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EACV,gEAAgE;QACjE,OAAO,EAAE,KAAK;KACd,CAAC,CACF;IACD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAClB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,6BAA6B;QAC1C,OAAO,EAAE,KAAK;KACd,CAAC,CACF;IACD,SAAS,EAAE,IAAI,CAAC,QAAQ,CACvB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,yCAAyC;QACtD,OAAO,EAAE,KAAK;KACd,CAAC,CACF;IACD,UAAU,EAAE,IAAI,CAAC,QAAQ,CACxB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,8BAA8B;QAC3C,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,IAAI;KACb,CAAC,CACF;IACD,OAAO,EAAE,IAAI,CAAC,QAAQ,CACrB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,mDAAmD;QAChE,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,EAAE;KACX,CAAC,CACF;IACD,aAAa,EAAE,IAAI,CAAC,QAAQ,CAC3B,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,yCAAyC;QACtD,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,EAAE;KACX,CAAC,CACF;IACD,YAAY,EAAE,IAAI,CAAC,QAAQ,CAC1B,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,wCAAwC;QACrD,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,EAAE;KACX,CAAC,CACF;IACD,GAAG,EAAE,IAAI,CAAC,QAAQ,CACjB,IAAI,CAAC,MAAM,CAAC;QACX,WAAW,EAAE,+BAA+B;QAC5C,SAAS,EAAE,CAAC;KACZ,CAAC,CACF;IACD,aAAa,EAAE,IAAI,CAAC,QAAQ,CAC3B,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,iCAAiC;QAC9C,OAAO,EAAE,KAAK;KACd,CAAC,CACF;IACD,YAAY,EAAE,IAAI,CAAC,QAAQ,CAC1B,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,gDAAgD;QAC7D,OAAO,EAAE,IAAI;KACb,CAAC,CACF;IACD,SAAS,EAAE,IAAI,CAAC,QAAQ,CACvB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,4CAA4C;QACzD,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,kBAAkB;KAC3B,CAAC,CACF;CACD,CAAC,CAAC;AAsCH;;;;;;;;;;;;GAYG;AACH,SAAS,WAAW,CACnB,MAAoE;IAMpE,mDAAmD;IACnD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC7D,MAAM,MAAM,GACX,EAAE,CAAC;IAEJ,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,kEAAkE;QAClE,yDAAyD;QACzD,IAAI,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YACzC,+CAA+C;YAC/C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YACzC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACtC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5B,CAAC;QACF,CAAC;aAAM,CAAC;YACP,2BAA2B;YAC3B,MAAM,CAAC,IAAI,CAAC;gBACX,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,QAAQ,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;aACjC,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,KAAK,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,MAKE,EACF,KAAa;IAEb,8DAA8D;IAC9D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACzC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QAC1B,CAAC;QACD,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,GAAG,KAAK,CAAC;IAC9C,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAE9C,uEAAuE;IACvE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC9C,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzD,IAAI,KAAe,CAAC;QAEpB,oBAAoB;QACpB,IAAI,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;YACjC,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,YAAY,CAAa,CAAC;QACjD,CAAC;aAAM,CAAC;YACP,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;gBACzD,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC/B,SAAS,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACR,6DAA6D;gBAC7D,SAAS;YACV,CAAC;QACF,CAAC;QAED,oCAAoC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;YACjB,SAAS;QACV,CAAC;QAED,2EAA2E;QAC3E,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK;YACL,GAAG;YACH,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE;YAC3C,OAAO,EAAE,OAAO;SAChB,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAG3C;IACD,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,kBAAkB;IACzB,WAAW,EACV,+RAA+R;IAChS,MAAM,EAAE,qBAAqB;IAC7B,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;QACpC,MAAM,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,UAAU,EACV,OAAO,EACP,IAAI,EACJ,SAAS,EACT,UAAU,EACV,OAAO,EACP,aAAa,EACb,YAAY,EACZ,GAAG,EACH,aAAa,EACb,YAAY,GAAG,IAAI,EACnB,SAAS,GACT,GAAG,MAAM,CAAC;QAEX,IACC,OAAO,KAAK,SAAS;YACrB,CAAC,aAAa,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS,CAAC,EAC1D,CAAC;YACF,MAAM,IAAI,KAAK,CACd,+DAA+D,CAC/D,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1E,MAAM,MAAM,GAAG,aAAa,IAAI,OAAO,IAAI,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,YAAY,IAAI,OAAO,IAAI,CAAC,CAAC;QAE3C,MAAM,QAAQ,GAAa;YAC1B,eAAe;YACf,QAAQ;YACR,IAAI;YACJ,iBAAiB;SACjB,CAAC;QAEF,IAAI,UAAU,EAAE,CAAC;YAChB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAClC,CAAC;QAED,IAAI,IAAI,EAAE,CAAC;YACV,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,mBAAmB,GAAG,UAAU,IAAI,mBAAmB,CAAC;QAC9D,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAEjD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;QAC/B,CAAC;QAED,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,CAAC,KAAK,aAAa,EAAE,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAChC,QAAQ,CAAC,IAAI,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,WAAW,IAAI,QAAQ,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;QAED,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,IAAI,gBAAgB,GAAG,KAAK,CAAC;QAC7B,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACnD,MAAM,IAAI,GAAG,CAAC,GAAG,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC;YACvD,QAAQ,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;YAC1D,gBAAgB,KAAK,MAAM,CAAC,SAAS,CAAC;YACtC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,OAGF,CAAC;QACH,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,MAAM,GACX,KAAK,YAAY,KAAK;gBACrB,CAAC,CAAC,KAAK,CAAC,OAAO;gBACf,CAAC,CAAC,kBAAkB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,OAAO,CAAC,KAAK,CAAC,qBAAqB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;gBAC1D,QAAQ;gBACR,GAAG,EAAE,UAAU;gBACf,UAAU,EAAE,CAAC;gBACb,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,EAAE;gBACV,SAAS,EAAE,KAAK;aAChB,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,MAAM,YAAY,GAAG,IAAI,GAAG,EAGzB,CAAC;QAEJ,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;YAC3C,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC7D,MAAM,IAAI,KAAK,CACd,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAC7D,CAAC;YACH,CAAC;YAED,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChE,SAAS;YACV,CAAC;YAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAChD,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;YAE7B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;gBAC/C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;gBAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBAClD,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC1D,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;QACF,CAAC;QAED,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,MAAM,CAAC;gBAC/C,QAAQ;gBACR,GAAG,EAAE,UAAU;gBACf,UAAU;gBACV,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,EAAE;gBACV,SAAS,EAAE,KAAK;aAChB,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAKb,EAAE,CAAC;QACR,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;YACrD,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzC,YAAY,CAAC,IAAI,CAAC;oBACjB,IAAI;oBACJ,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,GAAG,EAAE,KAAK,CAAC,GAAG;oBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;iBACxB,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QAED,MAAM,UAAU,GAAG,SAAS,IAAI,kBAAkB,CAAC;QACnD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,iBAAiB,CACpD,UAAU,EACV,YAAY,EACZ,UAAU,CACV,CAAC;QAEF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAClE,MAAM,YAAY,GAAG,MAAM;aACzB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACd,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3F,OAAO,GAAG,MAAM,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QACtC,CAAC,CAAC;aACD,IAAI,CAAC,MAAM,CAAC,CAAC;QAEf,MAAM,aAAa,GAAG,SAAS;YAC9B,CAAC,CAAC,oBAAoB,MAAM,CAAC,MAAM,OAAO,YAAY,CAAC,MAAM,UAAU;YACvE,CAAC,CAAC,gBAAgB;gBACjB,CAAC,CAAC,8CAA8C;gBAChD,CAAC,CAAC,EAAE,CAAC;QAEP,OAAO,OAAO;aACZ,IAAI,CACJ,SAAS,UAAU,qBAAqB,SAAS,gBAAgB,QAAQ,CAAC,MAAM,0BAA0B,YAAY,CAAC,MAAM,sBAAsB,YAAY,GAAG,aAAa,EAAE,CACjL;aACA,MAAM,CAAC;YACP,QAAQ;YACR,GAAG,EAAE,UAAU;YACf,UAAU;YACV,UAAU,EAAE,YAAY,CAAC,MAAM;YAC/B,MAAM;YACN,SAAS,EAAE,SAAS,IAAI,gBAAgB;SACxC,CAAC,CAAC;IACL,CAAC;CACD,CAAC,CAAC","sourcesContent":["/**\n * Parallel Ripgrep Tool\n *\n * This tool enables searching for multiple regex patterns simultaneously\n * across a codebase. Unlike running separate ripgrep searches, this tool:\n *\n * 1. Runs all pattern searches in parallel for performance\n * 2. Merges overlapping line ranges to avoid duplicate context\n * 3. Returns consolidated results with per-pattern attribution\n *\n * Use cases:\n * - Finding function definitions AND their usages\n * - Searching for multiple related patterns (e.g., import + export)\n * - Finding all occurrences of related symbols\n *\n * The merging algorithm:\n * 1. Each pattern search returns line matches with context\n * 2. Matches are grouped by file\n * 3. Overlapping/adjacent ranges within a file are merged\n * 4. Final output shows which patterns matched each range\n */\n\nimport { promises as fs } from \"node:fs\";\nimport { resolve as resolvePath } from \"node:path\";\nimport { Type } from \"@sinclair/typebox\";\nimport {\n\tformatRipgrepCommand,\n\tglobSchema,\n\tparseRipgrepJson,\n\tpathSchema,\n\trunRipgrep,\n\ttoArray,\n} from \"./ripgrep-utils.js\";\nimport { createTool, expandUserPath } from \"./tool-dsl.js\";\n\n/** Maximum number of line ranges to include in detailed output */\nconst RANGE_DETAIL_LIMIT = 200;\n/** Default max matches per pattern (prevents runaway searches) */\nconst DEFAULT_MAX_RESULTS = 500;\n\nconst parallelRipgrepSchema = Type.Object({\n\tpatterns: Type.Array(Type.String({ minLength: 1 }), {\n\t\tminItems: 1,\n\t\tmaxItems: 10,\n\t\tdescription:\n\t\t\t\"Regex patterns to search for in parallel (1-10). Results are merged when matches overlap.\",\n\t}),\n\tpaths: pathSchema,\n\tglob: globSchema,\n\tignoreCase: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Case-insensitive search (-i) for all patterns\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tliteral: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Treat patterns as literal strings, not regex (--fixed-strings)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tword: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Match whole words only (-w)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tmultiline: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Enable multiline matching (--multiline)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tmaxResults: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Max matches per pattern (-m)\",\n\t\t\tminimum: 1,\n\t\t\tmaximum: 1000,\n\t\t}),\n\t),\n\tcontext: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Lines of context before and after each match (-C)\",\n\t\t\tminimum: 0,\n\t\t\tmaximum: 20,\n\t\t}),\n\t),\n\tbeforeContext: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Lines of context before each match (-B)\",\n\t\t\tminimum: 0,\n\t\t\tmaximum: 20,\n\t\t}),\n\t),\n\tafterContext: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Lines of context after each match (-A)\",\n\t\t\tminimum: 0,\n\t\t\tmaximum: 20,\n\t\t}),\n\t),\n\tcwd: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Working directory for ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t),\n\tincludeHidden: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Include hidden files (--hidden)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tuseGitIgnore: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Respect .gitignore (false to pass --no-ignore)\",\n\t\t\tdefault: true,\n\t\t}),\n\t),\n\theadLimit: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Max number of merged line ranges to return\",\n\t\t\tminimum: 1,\n\t\t\tmaximum: RANGE_DETAIL_LIMIT,\n\t\t}),\n\t),\n});\n\n/**\n * A single merged line range with its content.\n * Represents a contiguous block of lines that matched one or more patterns.\n */\ntype RangeDetail = {\n\t/** Relative path to the file */\n\tfile: string;\n\t/** First line number (1-based) */\n\tstart: number;\n\t/** Last line number (1-based, inclusive) */\n\tend: number;\n\t/** List of patterns that matched within this range */\n\tpatterns: string[];\n\t/** Actual content of the lines */\n\tcontent: string;\n};\n\n/**\n * Detailed output from the parallel ripgrep tool.\n * Includes both summary statistics and the full range details.\n */\ntype ParallelRipgrepDetails = {\n\t/** The ripgrep commands that were executed */\n\tcommands: string[];\n\t/** Working directory for the search */\n\tcwd: string;\n\t/** Total number of individual matches across all patterns */\n\tmatchCount: number;\n\t/** Number of merged line ranges in output */\n\trangeCount: number;\n\t/** The merged and deduplicated line ranges */\n\tranges: RangeDetail[];\n\t/** Whether output was truncated due to limits */\n\ttruncated: boolean;\n};\n\n/**\n * Merge overlapping or adjacent line ranges within a single file.\n *\n * This is the core deduplication logic. Given ranges like:\n * [1-5, 3-8, 10-12]\n * It produces:\n * [1-8, 10-12]\n *\n * Ranges are considered mergeable if they overlap or are exactly adjacent\n * (e.g., 1-5 and 6-10 merge to 1-10).\n *\n * Pattern sets are unioned when ranges merge.\n */\nfunction mergeRanges(\n\tranges: Array<{ start: number; end: number; patterns: Set<string> }>,\n): Array<{\n\tstart: number;\n\tend: number;\n\tpatterns: Set<string>;\n}> {\n\t// Sort by start line to enable single-pass merging\n\tconst sorted = [...ranges].sort((a, b) => a.start - b.start);\n\tconst merged: Array<{ start: number; end: number; patterns: Set<string> }> =\n\t\t[];\n\n\tfor (const range of sorted) {\n\t\tconst last = merged.at(-1);\n\t\t// Check if this range overlaps or is adjacent to the previous one\n\t\t// (start <= end + 1 allows for adjacent ranges to merge)\n\t\tif (last && range.start <= last.end + 1) {\n\t\t\t// Extend the previous range and merge patterns\n\t\t\tlast.end = Math.max(last.end, range.end);\n\t\t\tfor (const pattern of range.patterns) {\n\t\t\t\tlast.patterns.add(pattern);\n\t\t\t}\n\t\t} else {\n\t\t\t// Start a new merged range\n\t\t\tmerged.push({\n\t\t\t\tstart: range.start,\n\t\t\t\tend: range.end,\n\t\t\t\tpatterns: new Set(range.patterns),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n/**\n * Read file contents for each merged range and build detailed output.\n *\n * This function:\n * 1. Sorts ranges by file then line number for consistent output\n * 2. Reads file contents (with caching to avoid re-reading)\n * 3. Extracts the actual line content for each range\n * 4. Applies the limit to avoid excessive output\n *\n * @param commandCwd - Working directory to resolve relative paths\n * @param ranges - The merged ranges to populate with content\n * @param limit - Maximum number of ranges to include\n */\nasync function buildRangeContent(\n\tcommandCwd: string,\n\tranges: Array<{\n\t\tfile: string;\n\t\tstart: number;\n\t\tend: number;\n\t\tpatterns: Set<string>;\n\t}>,\n\tlimit: number,\n): Promise<{ ranges: RangeDetail[]; truncated: boolean }> {\n\t// Sort for deterministic output: by file, then by line number\n\tconst mergedRanges = ranges.sort((a, b) => {\n\t\tif (a.file === b.file) {\n\t\t\treturn a.start - b.start;\n\t\t}\n\t\treturn a.file.localeCompare(b.file);\n\t});\n\n\tconst truncated = mergedRanges.length > limit;\n\tconst selected = mergedRanges.slice(0, limit);\n\n\t// Cache file contents to avoid re-reading the same file multiple times\n\tconst fileCache = new Map<string, string[]>();\n\tconst results: RangeDetail[] = [];\n\n\tfor (const range of selected) {\n\t\tconst absolutePath = resolvePath(commandCwd, range.file);\n\t\tlet lines: string[];\n\n\t\t// Check cache first\n\t\tif (fileCache.has(absolutePath)) {\n\t\t\tlines = fileCache.get(absolutePath) as string[];\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst content = await fs.readFile(absolutePath, \"utf-8\");\n\t\t\t\tlines = content.split(/\\r?\\n/);\n\t\t\t\tfileCache.set(absolutePath, lines);\n\t\t\t} catch {\n\t\t\t\t// Skip files that can't be read (deleted, permissions, etc.)\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Clamp range to actual file bounds\n\t\tconst start = Math.max(1, range.start);\n\t\tconst end = Math.min(range.end, lines.length);\n\t\tif (end < start) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Extract the content (lines array is 0-indexed, our ranges are 1-indexed)\n\t\tconst snippet = lines.slice(start - 1, end).join(\"\\n\");\n\t\tresults.push({\n\t\t\tfile: range.file,\n\t\t\tstart,\n\t\t\tend,\n\t\t\tpatterns: Array.from(range.patterns).sort(),\n\t\t\tcontent: snippet,\n\t\t});\n\t}\n\n\treturn { ranges: results, truncated };\n}\n\nexport const parallelRipgrepTool = createTool<\n\ttypeof parallelRipgrepSchema,\n\tParallelRipgrepDetails\n>({\n\tname: \"parallel_ripgrep\",\n\tlabel: \"parallel_ripgrep\",\n\tdescription:\n\t\t\"Search for multiple patterns simultaneously. Runs ripgrep queries in parallel, automatically merges overlapping line ranges, and returns consolidated content. Ideal for finding related code (e.g., function definitions AND usages) or multiple related patterns without duplicate context.\",\n\tschema: parallelRipgrepSchema,\n\tasync run(params, { signal, respond }) {\n\t\tconst {\n\t\t\tpatterns,\n\t\t\tpaths,\n\t\t\tglob,\n\t\t\tignoreCase,\n\t\t\tliteral,\n\t\t\tword,\n\t\t\tmultiline,\n\t\t\tmaxResults,\n\t\t\tcontext,\n\t\t\tbeforeContext,\n\t\t\tafterContext,\n\t\t\tcwd,\n\t\t\tincludeHidden,\n\t\t\tuseGitIgnore = true,\n\t\t\theadLimit,\n\t\t} = params;\n\n\t\tif (\n\t\t\tcontext !== undefined &&\n\t\t\t(beforeContext !== undefined || afterContext !== undefined)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Use either context or before/after context options, not both.\",\n\t\t\t);\n\t\t}\n\n\t\tconst pathArgs = toArray(paths);\n\t\tconst globArgs = toArray(glob);\n\t\tconst commandCwd = cwd ? resolvePath(expandUserPath(cwd)) : process.cwd();\n\t\tconst before = beforeContext ?? context ?? 0;\n\t\tconst after = afterContext ?? context ?? 0;\n\n\t\tconst baseArgs: string[] = [\n\t\t\t\"--color=never\",\n\t\t\t\"--json\",\n\t\t\t\"-n\",\n\t\t\t\"--with-filename\",\n\t\t];\n\n\t\tif (ignoreCase) {\n\t\t\tbaseArgs.push(\"-i\");\n\t\t}\n\n\t\tif (literal) {\n\t\t\tbaseArgs.push(\"--fixed-strings\");\n\t\t}\n\n\t\tif (word) {\n\t\t\tbaseArgs.push(\"-w\");\n\t\t}\n\n\t\tif (multiline) {\n\t\t\tbaseArgs.push(\"--multiline\");\n\t\t}\n\n\t\tif (includeHidden) {\n\t\t\tbaseArgs.push(\"--hidden\");\n\t\t}\n\n\t\tif (!useGitIgnore) {\n\t\t\tbaseArgs.push(\"--no-ignore\");\n\t\t}\n\n\t\tconst effectiveMaxResults = maxResults ?? DEFAULT_MAX_RESULTS;\n\t\tbaseArgs.push(\"-m\", String(effectiveMaxResults));\n\n\t\tif (context !== undefined) {\n\t\t\tbaseArgs.push(`-C${context}`);\n\t\t}\n\n\t\tif (beforeContext !== undefined) {\n\t\t\tbaseArgs.push(`-B${beforeContext}`);\n\t\t}\n\n\t\tif (afterContext !== undefined) {\n\t\t\tbaseArgs.push(`-A${afterContext}`);\n\t\t}\n\n\t\tfor (const globPattern of globArgs) {\n\t\t\tbaseArgs.push(\"--glob\", globPattern);\n\t\t}\n\n\t\tif (pathArgs.length === 0) {\n\t\t\tpathArgs.push(\".\");\n\t\t}\n\n\t\tconst commands: string[] = [];\n\t\tlet truncatedByBytes = false;\n\t\tconst ripgrepCalls = patterns.map(async (pattern) => {\n\t\t\tconst args = [...baseArgs, \"--\", pattern, ...pathArgs];\n\t\t\tcommands.push(formatRipgrepCommand(args));\n\t\t\tconst result = await runRipgrep(args, signal, commandCwd);\n\t\t\ttruncatedByBytes ||= result.truncated;\n\t\t\treturn { pattern, result };\n\t\t});\n\n\t\tlet results: Array<{\n\t\t\tpattern: string;\n\t\t\tresult: Awaited<ReturnType<typeof runRipgrep>>;\n\t\t}>;\n\t\ttry {\n\t\t\tresults = await Promise.all(ripgrepCalls);\n\t\t} catch (error) {\n\t\t\tconst reason =\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? error.message\n\t\t\t\t\t: `Unknown error: ${String(error)}`;\n\t\t\treturn respond.error(`ripgrep failed\\n\\n${reason}`).detail({\n\t\t\t\tcommands,\n\t\t\t\tcwd: commandCwd,\n\t\t\t\tmatchCount: 0,\n\t\t\t\trangeCount: 0,\n\t\t\t\tranges: [],\n\t\t\t\ttruncated: false,\n\t\t\t});\n\t\t}\n\n\t\tlet matchCount = 0;\n\t\tconst rangesByFile = new Map<\n\t\t\tstring,\n\t\t\tArray<{ start: number; end: number; patterns: Set<string> }>\n\t\t>();\n\n\t\tfor (const { pattern, result } of results) {\n\t\t\tif (result.exitCode === 2) {\n\t\t\t\tconst message = result.stderr.trim() || result.stdout.trim();\n\t\t\t\tthrow new Error(\n\t\t\t\t\tmessage.length > 0 ? message : \"ripgrep exited with an error\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (result.exitCode === 1 || result.stdout.trim().length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst matches = parseRipgrepJson(result.stdout);\n\t\t\tmatchCount += matches.length;\n\n\t\t\tfor (const match of matches) {\n\t\t\t\tconst start = Math.max(1, match.line - before);\n\t\t\t\tconst end = match.line + after;\n\t\t\t\tconst ranges = rangesByFile.get(match.file) ?? [];\n\t\t\t\tranges.push({ start, end, patterns: new Set([pattern]) });\n\t\t\t\trangesByFile.set(match.file, ranges);\n\t\t\t}\n\t\t}\n\n\t\tif (matchCount === 0) {\n\t\t\treturn respond.text(\"No matches found.\").detail({\n\t\t\t\tcommands,\n\t\t\t\tcwd: commandCwd,\n\t\t\t\tmatchCount,\n\t\t\t\trangeCount: 0,\n\t\t\t\tranges: [],\n\t\t\t\ttruncated: false,\n\t\t\t});\n\t\t}\n\n\t\tconst mergedRanges: Array<{\n\t\t\tfile: string;\n\t\t\tstart: number;\n\t\t\tend: number;\n\t\t\tpatterns: Set<string>;\n\t\t}> = [];\n\t\tfor (const [file, ranges] of rangesByFile.entries()) {\n\t\t\tfor (const range of mergeRanges(ranges)) {\n\t\t\t\tmergedRanges.push({\n\t\t\t\t\tfile,\n\t\t\t\t\tstart: range.start,\n\t\t\t\t\tend: range.end,\n\t\t\t\t\tpatterns: range.patterns,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst rangeLimit = headLimit ?? RANGE_DETAIL_LIMIT;\n\t\tconst { ranges, truncated } = await buildRangeContent(\n\t\t\tcommandCwd,\n\t\t\tmergedRanges,\n\t\t\trangeLimit,\n\t\t);\n\n\t\tconst fileCount = new Set(ranges.map((range) => range.file)).size;\n\t\tconst summaryLines = ranges\n\t\t\t.map((range) => {\n\t\t\t\tconst header = `${range.file}:${range.start}-${range.end} [${range.patterns.join(\", \")}]:`;\n\t\t\t\treturn `${header}\\n${range.content}`;\n\t\t\t})\n\t\t\t.join(\"\\n\\n\");\n\n\t\tconst truncatedNote = truncated\n\t\t\t? `\\n\\n... (showing ${ranges.length} of ${mergedRanges.length} ranges)`\n\t\t\t: truncatedByBytes\n\t\t\t\t? \"\\n\\n... (output truncated due to size limit)\"\n\t\t\t\t: \"\";\n\n\t\treturn respond\n\t\t\t.text(\n\t\t\t\t`Found ${matchCount} match(es) across ${fileCount} file(s) for ${patterns.length} pattern(s). Extracted ${mergedRanges.length} line range(s).\\n\\n${summaryLines}${truncatedNote}`,\n\t\t\t)\n\t\t\t.detail({\n\t\t\t\tcommands,\n\t\t\t\tcwd: commandCwd,\n\t\t\t\tmatchCount,\n\t\t\t\trangeCount: mergedRanges.length,\n\t\t\t\tranges,\n\t\t\t\ttruncated: truncated || truncatedByBytes,\n\t\t\t});\n\t},\n});\n"]}
1
+ {"version":3,"file":"parallel-ripgrep.js","sourceRoot":"","sources":["../../src/tools/parallel-ripgrep.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Parallel Ripgrep Tool\n *\n * This tool enables searching for multiple regex patterns simultaneously\n * across a codebase. Unlike running separate ripgrep searches, this tool:\n *\n * 1. Runs all pattern searches in parallel for performance\n * 2. Merges overlapping line ranges to avoid duplicate context\n * 3. Returns consolidated results with per-pattern attribution\n *\n * Use cases:\n * - Finding function definitions AND their usages\n * - Searching for multiple related patterns (e.g., import + export)\n * - Finding all occurrences of related symbols\n *\n * The merging algorithm:\n * 1. Each pattern search returns line matches with context\n * 2. Matches are grouped by file\n * 3. Overlapping/adjacent ranges within a file are merged\n * 4. Final output shows which patterns matched each range\n */\n\nimport { promises as fs } from \"node:fs\";\nimport { resolve as resolvePath } from \"node:path\";\nimport { Type } from \"@sinclair/typebox\";\nimport {\n\tformatRipgrepCommand,\n\tglobSchema,\n\tisRipgrepPathError,\n\tparseRipgrepJson,\n\tpathSchema,\n\trunRipgrep,\n\ttoArray,\n} from \"./ripgrep-utils.js\";\nimport { createTool, expandUserPath } from \"./tool-dsl.js\";\n\n/** Maximum number of line ranges to include in detailed output */\nconst RANGE_DETAIL_LIMIT = 200;\n/** Default max matches per pattern (prevents runaway searches) */\nconst DEFAULT_MAX_RESULTS = 500;\n\nconst parallelRipgrepSchema = Type.Object({\n\tpatterns: Type.Array(Type.String({ minLength: 1 }), {\n\t\tminItems: 1,\n\t\tmaxItems: 10,\n\t\tdescription:\n\t\t\t\"Regex patterns to search for in parallel (1-10). Results are merged when matches overlap.\",\n\t}),\n\tpaths: pathSchema,\n\tglob: globSchema,\n\tignoreCase: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Case-insensitive search (-i) for all patterns\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tliteral: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Treat patterns as literal strings, not regex (--fixed-strings)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tword: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Match whole words only (-w)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tmultiline: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Enable multiline matching (--multiline)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tmaxResults: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Max matches per pattern (-m)\",\n\t\t\tminimum: 1,\n\t\t\tmaximum: 1000,\n\t\t}),\n\t),\n\tcontext: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Lines of context before and after each match (-C)\",\n\t\t\tminimum: 0,\n\t\t\tmaximum: 20,\n\t\t}),\n\t),\n\tbeforeContext: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Lines of context before each match (-B)\",\n\t\t\tminimum: 0,\n\t\t\tmaximum: 20,\n\t\t}),\n\t),\n\tafterContext: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Lines of context after each match (-A)\",\n\t\t\tminimum: 0,\n\t\t\tmaximum: 20,\n\t\t}),\n\t),\n\tcwd: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Working directory for ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t),\n\tincludeHidden: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Include hidden files (--hidden)\",\n\t\t\tdefault: false,\n\t\t}),\n\t),\n\tuseGitIgnore: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Respect .gitignore (false to pass --no-ignore)\",\n\t\t\tdefault: true,\n\t\t}),\n\t),\n\theadLimit: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Max number of merged line ranges to return\",\n\t\t\tminimum: 1,\n\t\t\tmaximum: RANGE_DETAIL_LIMIT,\n\t\t}),\n\t),\n});\n\n/**\n * A single merged line range with its content.\n * Represents a contiguous block of lines that matched one or more patterns.\n */\ntype RangeDetail = {\n\t/** Relative path to the file */\n\tfile: string;\n\t/** First line number (1-based) */\n\tstart: number;\n\t/** Last line number (1-based, inclusive) */\n\tend: number;\n\t/** List of patterns that matched within this range */\n\tpatterns: string[];\n\t/** Actual content of the lines */\n\tcontent: string;\n};\n\n/**\n * Detailed output from the parallel ripgrep tool.\n * Includes both summary statistics and the full range details.\n */\ntype ParallelRipgrepDetails = {\n\t/** The ripgrep commands that were executed */\n\tcommands: string[];\n\t/** Working directory for the search */\n\tcwd: string;\n\t/** Total number of individual matches across all patterns */\n\tmatchCount: number;\n\t/** Number of merged line ranges in output */\n\trangeCount: number;\n\t/** The merged and deduplicated line ranges */\n\tranges: RangeDetail[];\n\t/** Whether output was truncated due to limits */\n\ttruncated: boolean;\n};\n\n/**\n * Merge overlapping or adjacent line ranges within a single file.\n *\n * This is the core deduplication logic. Given ranges like:\n * [1-5, 3-8, 10-12]\n * It produces:\n * [1-8, 10-12]\n *\n * Ranges are considered mergeable if they overlap or are exactly adjacent\n * (e.g., 1-5 and 6-10 merge to 1-10).\n *\n * Pattern sets are unioned when ranges merge.\n */\nfunction mergeRanges(\n\tranges: Array<{ start: number; end: number; patterns: Set<string> }>,\n): Array<{\n\tstart: number;\n\tend: number;\n\tpatterns: Set<string>;\n}> {\n\t// Sort by start line to enable single-pass merging\n\tconst sorted = [...ranges].sort((a, b) => a.start - b.start);\n\tconst merged: Array<{ start: number; end: number; patterns: Set<string> }> =\n\t\t[];\n\n\tfor (const range of sorted) {\n\t\tconst last = merged.at(-1);\n\t\t// Check if this range overlaps or is adjacent to the previous one\n\t\t// (start <= end + 1 allows for adjacent ranges to merge)\n\t\tif (last && range.start <= last.end + 1) {\n\t\t\t// Extend the previous range and merge patterns\n\t\t\tlast.end = Math.max(last.end, range.end);\n\t\t\tfor (const pattern of range.patterns) {\n\t\t\t\tlast.patterns.add(pattern);\n\t\t\t}\n\t\t} else {\n\t\t\t// Start a new merged range\n\t\t\tmerged.push({\n\t\t\t\tstart: range.start,\n\t\t\t\tend: range.end,\n\t\t\t\tpatterns: new Set(range.patterns),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n/**\n * Read file contents for each merged range and build detailed output.\n *\n * This function:\n * 1. Sorts ranges by file then line number for consistent output\n * 2. Reads file contents (with caching to avoid re-reading)\n * 3. Extracts the actual line content for each range\n * 4. Applies the limit to avoid excessive output\n *\n * @param commandCwd - Working directory to resolve relative paths\n * @param ranges - The merged ranges to populate with content\n * @param limit - Maximum number of ranges to include\n */\nasync function buildRangeContent(\n\tcommandCwd: string,\n\tranges: Array<{\n\t\tfile: string;\n\t\tstart: number;\n\t\tend: number;\n\t\tpatterns: Set<string>;\n\t}>,\n\tlimit: number,\n): Promise<{ ranges: RangeDetail[]; truncated: boolean }> {\n\t// Sort for deterministic output: by file, then by line number\n\tconst mergedRanges = ranges.sort((a, b) => {\n\t\tif (a.file === b.file) {\n\t\t\treturn a.start - b.start;\n\t\t}\n\t\treturn a.file.localeCompare(b.file);\n\t});\n\n\tconst truncated = mergedRanges.length > limit;\n\tconst selected = mergedRanges.slice(0, limit);\n\n\t// Cache file contents to avoid re-reading the same file multiple times\n\tconst fileCache = new Map<string, string[]>();\n\tconst results: RangeDetail[] = [];\n\n\tfor (const range of selected) {\n\t\tconst absolutePath = resolvePath(commandCwd, range.file);\n\t\tlet lines: string[];\n\n\t\t// Check cache first\n\t\tif (fileCache.has(absolutePath)) {\n\t\t\tlines = fileCache.get(absolutePath) as string[];\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst content = await fs.readFile(absolutePath, \"utf-8\");\n\t\t\t\tlines = content.split(/\\r?\\n/);\n\t\t\t\tfileCache.set(absolutePath, lines);\n\t\t\t} catch {\n\t\t\t\t// Skip files that can't be read (deleted, permissions, etc.)\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Clamp range to actual file bounds\n\t\tconst start = Math.max(1, range.start);\n\t\tconst end = Math.min(range.end, lines.length);\n\t\tif (end < start) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Extract the content (lines array is 0-indexed, our ranges are 1-indexed)\n\t\tconst snippet = lines.slice(start - 1, end).join(\"\\n\");\n\t\tresults.push({\n\t\t\tfile: range.file,\n\t\t\tstart,\n\t\t\tend,\n\t\t\tpatterns: Array.from(range.patterns).sort(),\n\t\t\tcontent: snippet,\n\t\t});\n\t}\n\n\treturn { ranges: results, truncated };\n}\n\nexport const parallelRipgrepTool = createTool<\n\ttypeof parallelRipgrepSchema,\n\tParallelRipgrepDetails\n>({\n\tname: \"parallel_ripgrep\",\n\tlabel: \"parallel_ripgrep\",\n\tdescription:\n\t\t\"Search for multiple patterns simultaneously. Runs ripgrep queries in parallel, automatically merges overlapping line ranges, and returns consolidated content. Ideal for finding related code (e.g., function definitions AND usages) or multiple related patterns without duplicate context.\",\n\tschema: parallelRipgrepSchema,\n\tasync run(params, { signal, respond }) {\n\t\tconst {\n\t\t\tpatterns,\n\t\t\tpaths,\n\t\t\tglob,\n\t\t\tignoreCase,\n\t\t\tliteral,\n\t\t\tword,\n\t\t\tmultiline,\n\t\t\tmaxResults,\n\t\t\tcontext,\n\t\t\tbeforeContext,\n\t\t\tafterContext,\n\t\t\tcwd,\n\t\t\tincludeHidden,\n\t\t\tuseGitIgnore = true,\n\t\t\theadLimit,\n\t\t} = params;\n\n\t\tif (\n\t\t\tcontext !== undefined &&\n\t\t\t(beforeContext !== undefined || afterContext !== undefined)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Use either context or before/after context options, not both.\",\n\t\t\t);\n\t\t}\n\n\t\tconst pathArgs = toArray(paths);\n\t\tconst globArgs = toArray(glob);\n\t\tconst commandCwd = cwd ? resolvePath(expandUserPath(cwd)) : process.cwd();\n\t\tconst before = beforeContext ?? context ?? 0;\n\t\tconst after = afterContext ?? context ?? 0;\n\n\t\tconst baseArgs: string[] = [\n\t\t\t\"--color=never\",\n\t\t\t\"--json\",\n\t\t\t\"-n\",\n\t\t\t\"--with-filename\",\n\t\t];\n\n\t\tif (ignoreCase) {\n\t\t\tbaseArgs.push(\"-i\");\n\t\t}\n\n\t\tif (literal) {\n\t\t\tbaseArgs.push(\"--fixed-strings\");\n\t\t}\n\n\t\tif (word) {\n\t\t\tbaseArgs.push(\"-w\");\n\t\t}\n\n\t\tif (multiline) {\n\t\t\tbaseArgs.push(\"--multiline\");\n\t\t}\n\n\t\tif (includeHidden) {\n\t\t\tbaseArgs.push(\"--hidden\");\n\t\t}\n\n\t\tif (!useGitIgnore) {\n\t\t\tbaseArgs.push(\"--no-ignore\");\n\t\t}\n\n\t\tconst effectiveMaxResults = maxResults ?? DEFAULT_MAX_RESULTS;\n\t\tbaseArgs.push(\"-m\", String(effectiveMaxResults));\n\n\t\tif (context !== undefined) {\n\t\t\tbaseArgs.push(`-C${context}`);\n\t\t}\n\n\t\tif (beforeContext !== undefined) {\n\t\t\tbaseArgs.push(`-B${beforeContext}`);\n\t\t}\n\n\t\tif (afterContext !== undefined) {\n\t\t\tbaseArgs.push(`-A${afterContext}`);\n\t\t}\n\n\t\tfor (const globPattern of globArgs) {\n\t\t\tbaseArgs.push(\"--glob\", globPattern);\n\t\t}\n\n\t\tif (pathArgs.length === 0) {\n\t\t\tpathArgs.push(\".\");\n\t\t}\n\n\t\tconst commands: string[] = [];\n\t\tlet truncatedByBytes = false;\n\t\tconst ripgrepCalls = patterns.map(async (pattern) => {\n\t\t\tconst args = [...baseArgs, \"--\", pattern, ...pathArgs];\n\t\t\tcommands.push(formatRipgrepCommand(args));\n\t\t\tconst result = await runRipgrep(args, signal, commandCwd);\n\t\t\ttruncatedByBytes ||= result.truncated;\n\t\t\treturn { pattern, result };\n\t\t});\n\n\t\tlet results: Array<{\n\t\t\tpattern: string;\n\t\t\tresult: Awaited<ReturnType<typeof runRipgrep>>;\n\t\t}>;\n\t\ttry {\n\t\t\tresults = await Promise.all(ripgrepCalls);\n\t\t} catch (error) {\n\t\t\tconst reason =\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? error.message\n\t\t\t\t\t: `Unknown error: ${String(error)}`;\n\t\t\treturn respond.error(`ripgrep failed\\n\\n${reason}`).detail({\n\t\t\t\tcommands,\n\t\t\t\tcwd: commandCwd,\n\t\t\t\tmatchCount: 0,\n\t\t\t\trangeCount: 0,\n\t\t\t\tranges: [],\n\t\t\t\ttruncated: false,\n\t\t\t});\n\t\t}\n\n\t\tlet matchCount = 0;\n\t\tconst rangesByFile = new Map<\n\t\t\tstring,\n\t\t\tArray<{ start: number; end: number; patterns: Set<string> }>\n\t\t>();\n\n\t\tfor (const { pattern, result } of results) {\n\t\t\tif (result.exitCode === 2) {\n\t\t\t\tconst message = result.stderr.trim() || result.stdout.trim();\n\t\t\t\tif (isRipgrepPathError(message)) {\n\t\t\t\t\treturn respond\n\t\t\t\t\t\t.error(\n\t\t\t\t\t\t\t`ripgrep failed\\n\\n${message.length > 0 ? message : \"ripgrep path lookup failed\"}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.detail({\n\t\t\t\t\t\t\tcommands,\n\t\t\t\t\t\t\tcwd: commandCwd,\n\t\t\t\t\t\t\tmatchCount: 0,\n\t\t\t\t\t\t\trangeCount: 0,\n\t\t\t\t\t\t\tranges: [],\n\t\t\t\t\t\t\ttruncated: false,\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\tmessage.length > 0 ? message : \"ripgrep exited with an error\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (result.exitCode === 1 || result.stdout.trim().length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst matches = parseRipgrepJson(result.stdout);\n\t\t\tmatchCount += matches.length;\n\n\t\t\tfor (const match of matches) {\n\t\t\t\tconst start = Math.max(1, match.line - before);\n\t\t\t\tconst end = match.line + after;\n\t\t\t\tconst ranges = rangesByFile.get(match.file) ?? [];\n\t\t\t\tranges.push({ start, end, patterns: new Set([pattern]) });\n\t\t\t\trangesByFile.set(match.file, ranges);\n\t\t\t}\n\t\t}\n\n\t\tif (matchCount === 0) {\n\t\t\treturn respond.text(\"No matches found.\").detail({\n\t\t\t\tcommands,\n\t\t\t\tcwd: commandCwd,\n\t\t\t\tmatchCount,\n\t\t\t\trangeCount: 0,\n\t\t\t\tranges: [],\n\t\t\t\ttruncated: false,\n\t\t\t});\n\t\t}\n\n\t\tconst mergedRanges: Array<{\n\t\t\tfile: string;\n\t\t\tstart: number;\n\t\t\tend: number;\n\t\t\tpatterns: Set<string>;\n\t\t}> = [];\n\t\tfor (const [file, ranges] of rangesByFile.entries()) {\n\t\t\tfor (const range of mergeRanges(ranges)) {\n\t\t\t\tmergedRanges.push({\n\t\t\t\t\tfile,\n\t\t\t\t\tstart: range.start,\n\t\t\t\t\tend: range.end,\n\t\t\t\t\tpatterns: range.patterns,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst rangeLimit = headLimit ?? RANGE_DETAIL_LIMIT;\n\t\tconst { ranges, truncated } = await buildRangeContent(\n\t\t\tcommandCwd,\n\t\t\tmergedRanges,\n\t\t\trangeLimit,\n\t\t);\n\n\t\tconst fileCount = new Set(ranges.map((range) => range.file)).size;\n\t\tconst summaryLines = ranges\n\t\t\t.map((range) => {\n\t\t\t\tconst header = `${range.file}:${range.start}-${range.end} [${range.patterns.join(\", \")}]:`;\n\t\t\t\treturn `${header}\\n${range.content}`;\n\t\t\t})\n\t\t\t.join(\"\\n\\n\");\n\n\t\tconst truncatedNote = truncated\n\t\t\t? `\\n\\n... (showing ${ranges.length} of ${mergedRanges.length} ranges)`\n\t\t\t: truncatedByBytes\n\t\t\t\t? \"\\n\\n... (output truncated due to size limit)\"\n\t\t\t\t: \"\";\n\n\t\treturn respond\n\t\t\t.text(\n\t\t\t\t`Found ${matchCount} match(es) across ${fileCount} file(s) for ${patterns.length} pattern(s). Extracted ${mergedRanges.length} line range(s).\\n\\n${summaryLines}${truncatedNote}`,\n\t\t\t)\n\t\t\t.detail({\n\t\t\t\tcommands,\n\t\t\t\tcwd: commandCwd,\n\t\t\t\tmatchCount,\n\t\t\t\trangeCount: mergedRanges.length,\n\t\t\t\tranges,\n\t\t\t\ttruncated: truncated || truncatedByBytes,\n\t\t\t});\n\t},\n});\n"]}
@@ -2,6 +2,7 @@ export declare const pathSchema: import("@sinclair/typebox").TOptional<import("@
2
2
  export declare const globSchema: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>]>>;
3
3
  export declare function toArray<T>(value: T | T[] | undefined): T[];
4
4
  export declare function formatRipgrepCommand(args: string[]): string;
5
+ export declare function isRipgrepPathError(message: string): boolean;
5
6
  export declare function runRipgrep(args: string[], signal?: AbortSignal, cwd?: string): Promise<{
6
7
  stdout: string;
7
8
  stderr: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ripgrep-utils.d.ts","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,CAAC,EAAE,CAK1D;AAgHD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAsB,UAAU,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,GAAG,CAAC,EAAE,MAAM,GACV,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC,CAoDD;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAmC/D"}
1
+ {"version":3,"file":"ripgrep-utils.d.ts","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,CAAC,EAAE,CAK1D;AAgHD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI3D;AAED,wBAAsB,UAAU,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,GAAG,CAAC,EAAE,MAAM,GACV,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC,CAoDD;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAmC/D"}
@@ -122,6 +122,9 @@ function shellQuoteArg(value) {
122
122
  export function formatRipgrepCommand(args) {
123
123
  return ["rg", ...args].map(shellQuoteArg).join(" ");
124
124
  }
125
+ export function isRipgrepPathError(message) {
126
+ return /No such file or directory|IO error|os error 2|system cannot find the path/i.test(message);
127
+ }
125
128
  export async function runRipgrep(args, signal, cwd) {
126
129
  const executable = await resolveRipgrepExecutableWithAbort(signal);
127
130
  throwIfRipgrepAborted(signal);
@@ -1 +1 @@
1
- {"version":3,"file":"ripgrep-utils.js","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Type } from \"@sinclair/typebox\";\nimport { safeJsonParse } from \"../utils/json.js\";\nimport { ensureTool } from \"./tools-manager.js\";\n\nexport const pathSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Directory or file to search\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple directories or files to search\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport const globSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Glob pattern passed to ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple glob patterns\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n\tif (value === undefined) {\n\t\treturn [];\n\t}\n\treturn Array.isArray(value) ? value : [value];\n}\n\nconst MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard\nlet ripgrepExecutablePromise: Promise<string | null> | null = null;\nlet ripgrepInstallController: AbortController | null = null;\nlet ripgrepExecutableWaiters = 0;\n\nfunction ripgrepAbortError(): Error {\n\treturn new Error(\"ripgrep search aborted before start\");\n}\n\nfunction resetRipgrepExecutablePromise(): void {\n\tripgrepExecutablePromise = null;\n\tripgrepInstallController = null;\n}\n\nfunction getRipgrepExecutablePromise(): Promise<string | null> {\n\tif (!ripgrepExecutablePromise) {\n\t\tconst controller = new AbortController();\n\t\tripgrepInstallController = controller;\n\t\tconst promise = ensureTool(\"rg\", true, controller.signal).then(\n\t\t\t(executable) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise && !executable) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\treturn executable;\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\tripgrepExecutablePromise = promise;\n\t}\n\treturn ripgrepExecutablePromise;\n}\n\nfunction releaseRipgrepExecutableWaiter(signal?: AbortSignal): void {\n\tripgrepExecutableWaiters = Math.max(0, ripgrepExecutableWaiters - 1);\n\tif (signal?.aborted && ripgrepExecutableWaiters === 0) {\n\t\tconst controller = ripgrepInstallController;\n\t\tresetRipgrepExecutablePromise();\n\t\tcontroller?.abort(signal.reason);\n\t}\n}\n\nasync function waitForRipgrepExecutableWithAbort(\n\tpromise: Promise<string | null>,\n\tsignal: AbortSignal,\n): Promise<string | null> {\n\tthrowIfRipgrepAborted(signal);\n\treturn await new Promise<string | null>((resolve, reject) => {\n\t\tconst onAbort = (): void => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\treject(ripgrepAbortError());\n\t\t};\n\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tpromise.then(resolve, reject).finally(() => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t});\n\t});\n}\n\nasync function resolveRipgrepExecutable(signal?: AbortSignal): Promise<string> {\n\tripgrepExecutableWaiters += 1;\n\ttry {\n\t\tconst promise = getRipgrepExecutablePromise();\n\t\tconst executable = signal\n\t\t\t? await waitForRipgrepExecutableWithAbort(promise, signal)\n\t\t\t: await promise;\n\t\tif (!executable) {\n\t\t\tthrow new Error(\"ripgrep is not available and could not be downloaded\");\n\t\t}\n\t\treturn executable;\n\t} finally {\n\t\treleaseRipgrepExecutableWaiter(signal);\n\t}\n}\n\nfunction throwIfRipgrepAborted(signal?: AbortSignal): void {\n\tif (signal?.aborted) {\n\t\tthrow ripgrepAbortError();\n\t}\n}\n\nasync function resolveRipgrepExecutableWithAbort(\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tthrowIfRipgrepAborted(signal);\n\tif (!signal) {\n\t\treturn await resolveRipgrepExecutable();\n\t}\n\n\treturn await resolveRipgrepExecutable(signal);\n}\n\nfunction shellQuoteArg(value: string): string {\n\tif (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function formatRipgrepCommand(args: string[]): string {\n\treturn [\"rg\", ...args].map(shellQuoteArg).join(\" \");\n}\n\nexport async function runRipgrep(\n\targs: string[],\n\tsignal?: AbortSignal,\n\tcwd?: string,\n): Promise<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n\ttruncated: boolean;\n}> {\n\tconst executable = await resolveRipgrepExecutableWithAbort(signal);\n\tthrowIfRipgrepAborted(signal);\n\tconst child = spawn(executable, args, {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tsignal,\n\t});\n\n\treturn await new Promise((resolve, reject) => {\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet truncated = false;\n\n\t\tchild.stdout.setEncoding(\"utf-8\");\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tif (stdout.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstdout += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stdout.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr.setEncoding(\"utf-8\");\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tif (stderr.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstderr += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stderr.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\treject(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? new Error(`Failed to start ripgrep: ${error.message}`)\n\t\t\t\t\t: new Error(`Failed to start ripgrep: ${String(error)}`),\n\t\t\t);\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tresolve({ stdout, stderr, exitCode: code ?? 0, truncated });\n\t\t});\n\t});\n}\n\nexport type RipgrepMatch = {\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\tmatch: string;\n\tlines: string;\n};\n\nexport function parseRipgrepJson(output: string): RipgrepMatch[] {\n\tconst matches: RipgrepMatch[] = [];\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.trim()) continue;\n\t\tconst parsed = safeJsonParse<unknown>(line, \"ripgrep output\");\n\t\tif (!parsed.success) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst event = parsed.data as {\n\t\t\ttype?: string;\n\t\t\tdata?: {\n\t\t\t\tpath?: { text?: string };\n\t\t\t\tline_number?: number;\n\t\t\t\tlines?: { text?: string };\n\t\t\t\tsubmatches?: Array<{\n\t\t\t\t\tstart?: number;\n\t\t\t\t\tmatch?: { text?: string };\n\t\t\t\t}>;\n\t\t\t};\n\t\t};\n\t\tif (event.type !== \"match\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pathText = event.data?.path?.text ?? \"\";\n\t\tfor (const submatch of event.data?.submatches ?? []) {\n\t\t\tmatches.push({\n\t\t\t\tfile: pathText,\n\t\t\t\tline: event.data?.line_number ?? 0,\n\t\t\t\tcolumn: submatch.start ?? 0,\n\t\t\t\tmatch: submatch.match?.text ?? \"\",\n\t\t\t\tlines: event.data?.lines?.text ?? \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn matches;\n}\n"]}
1
+ {"version":3,"file":"ripgrep-utils.js","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Type } from \"@sinclair/typebox\";\nimport { safeJsonParse } from \"../utils/json.js\";\nimport { ensureTool } from \"./tools-manager.js\";\n\nexport const pathSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Directory or file to search\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple directories or files to search\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport const globSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Glob pattern passed to ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple glob patterns\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n\tif (value === undefined) {\n\t\treturn [];\n\t}\n\treturn Array.isArray(value) ? value : [value];\n}\n\nconst MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard\nlet ripgrepExecutablePromise: Promise<string | null> | null = null;\nlet ripgrepInstallController: AbortController | null = null;\nlet ripgrepExecutableWaiters = 0;\n\nfunction ripgrepAbortError(): Error {\n\treturn new Error(\"ripgrep search aborted before start\");\n}\n\nfunction resetRipgrepExecutablePromise(): void {\n\tripgrepExecutablePromise = null;\n\tripgrepInstallController = null;\n}\n\nfunction getRipgrepExecutablePromise(): Promise<string | null> {\n\tif (!ripgrepExecutablePromise) {\n\t\tconst controller = new AbortController();\n\t\tripgrepInstallController = controller;\n\t\tconst promise = ensureTool(\"rg\", true, controller.signal).then(\n\t\t\t(executable) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise && !executable) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\treturn executable;\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\tripgrepExecutablePromise = promise;\n\t}\n\treturn ripgrepExecutablePromise;\n}\n\nfunction releaseRipgrepExecutableWaiter(signal?: AbortSignal): void {\n\tripgrepExecutableWaiters = Math.max(0, ripgrepExecutableWaiters - 1);\n\tif (signal?.aborted && ripgrepExecutableWaiters === 0) {\n\t\tconst controller = ripgrepInstallController;\n\t\tresetRipgrepExecutablePromise();\n\t\tcontroller?.abort(signal.reason);\n\t}\n}\n\nasync function waitForRipgrepExecutableWithAbort(\n\tpromise: Promise<string | null>,\n\tsignal: AbortSignal,\n): Promise<string | null> {\n\tthrowIfRipgrepAborted(signal);\n\treturn await new Promise<string | null>((resolve, reject) => {\n\t\tconst onAbort = (): void => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\treject(ripgrepAbortError());\n\t\t};\n\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tpromise.then(resolve, reject).finally(() => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t});\n\t});\n}\n\nasync function resolveRipgrepExecutable(signal?: AbortSignal): Promise<string> {\n\tripgrepExecutableWaiters += 1;\n\ttry {\n\t\tconst promise = getRipgrepExecutablePromise();\n\t\tconst executable = signal\n\t\t\t? await waitForRipgrepExecutableWithAbort(promise, signal)\n\t\t\t: await promise;\n\t\tif (!executable) {\n\t\t\tthrow new Error(\"ripgrep is not available and could not be downloaded\");\n\t\t}\n\t\treturn executable;\n\t} finally {\n\t\treleaseRipgrepExecutableWaiter(signal);\n\t}\n}\n\nfunction throwIfRipgrepAborted(signal?: AbortSignal): void {\n\tif (signal?.aborted) {\n\t\tthrow ripgrepAbortError();\n\t}\n}\n\nasync function resolveRipgrepExecutableWithAbort(\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tthrowIfRipgrepAborted(signal);\n\tif (!signal) {\n\t\treturn await resolveRipgrepExecutable();\n\t}\n\n\treturn await resolveRipgrepExecutable(signal);\n}\n\nfunction shellQuoteArg(value: string): string {\n\tif (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function formatRipgrepCommand(args: string[]): string {\n\treturn [\"rg\", ...args].map(shellQuoteArg).join(\" \");\n}\n\nexport function isRipgrepPathError(message: string): boolean {\n\treturn /No such file or directory|IO error|os error 2|system cannot find the path/i.test(\n\t\tmessage,\n\t);\n}\n\nexport async function runRipgrep(\n\targs: string[],\n\tsignal?: AbortSignal,\n\tcwd?: string,\n): Promise<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n\ttruncated: boolean;\n}> {\n\tconst executable = await resolveRipgrepExecutableWithAbort(signal);\n\tthrowIfRipgrepAborted(signal);\n\tconst child = spawn(executable, args, {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tsignal,\n\t});\n\n\treturn await new Promise((resolve, reject) => {\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet truncated = false;\n\n\t\tchild.stdout.setEncoding(\"utf-8\");\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tif (stdout.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstdout += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stdout.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr.setEncoding(\"utf-8\");\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tif (stderr.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstderr += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stderr.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\treject(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? new Error(`Failed to start ripgrep: ${error.message}`)\n\t\t\t\t\t: new Error(`Failed to start ripgrep: ${String(error)}`),\n\t\t\t);\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tresolve({ stdout, stderr, exitCode: code ?? 0, truncated });\n\t\t});\n\t});\n}\n\nexport type RipgrepMatch = {\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\tmatch: string;\n\tlines: string;\n};\n\nexport function parseRipgrepJson(output: string): RipgrepMatch[] {\n\tconst matches: RipgrepMatch[] = [];\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.trim()) continue;\n\t\tconst parsed = safeJsonParse<unknown>(line, \"ripgrep output\");\n\t\tif (!parsed.success) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst event = parsed.data as {\n\t\t\ttype?: string;\n\t\t\tdata?: {\n\t\t\t\tpath?: { text?: string };\n\t\t\t\tline_number?: number;\n\t\t\t\tlines?: { text?: string };\n\t\t\t\tsubmatches?: Array<{\n\t\t\t\t\tstart?: number;\n\t\t\t\t\tmatch?: { text?: string };\n\t\t\t\t}>;\n\t\t\t};\n\t\t};\n\t\tif (event.type !== \"match\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pathText = event.data?.path?.text ?? \"\";\n\t\tfor (const submatch of event.data?.submatches ?? []) {\n\t\t\tmatches.push({\n\t\t\t\tfile: pathText,\n\t\t\t\tline: event.data?.line_number ?? 0,\n\t\t\t\tcolumn: submatch.start ?? 0,\n\t\t\t\tmatch: submatch.match?.text ?? \"\",\n\t\t\t\tlines: event.data?.lines?.text ?? \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn matches;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAIH,OAAO,EACN,KAAK,YAAY,EAOjB,MAAM,oBAAoB,CAAC;AAkI5B,KAAK,iBAAiB,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7C,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqUrB,CAAC"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAIH,OAAO,EACN,KAAK,YAAY,EAQjB,MAAM,oBAAoB,CAAC;AAkI5B,KAAK,iBAAiB,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7C,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4UrB,CAAC"}
@@ -51,7 +51,7 @@
51
51
  */
52
52
  import { resolve as resolvePath } from "node:path";
53
53
  import { Type } from "@sinclair/typebox";
54
- import { formatRipgrepCommand, globSchema, parseRipgrepJson, pathSchema, runRipgrep, toArray, } from "./ripgrep-utils.js";
54
+ import { formatRipgrepCommand, globSchema, isRipgrepPathError, parseRipgrepJson, pathSchema, runRipgrep, toArray, } from "./ripgrep-utils.js";
55
55
  import { createTool, expandUserPath } from "./tool-dsl.js";
56
56
  const searchSchema = Type.Object({
57
57
  pattern: Type.String({
@@ -255,6 +255,11 @@ Examples:
255
255
  }
256
256
  if (result.exitCode === 2) {
257
257
  const message = result.stderr.trim() || result.stdout.trim();
258
+ if (isRipgrepPathError(message)) {
259
+ return respond
260
+ .error(`ripgrep failed\n\n${message.length > 0 ? message : "ripgrep path lookup failed"}`)
261
+ .detail({ command, cwd: commandCwd });
262
+ }
258
263
  throw new Error(message.length > 0 ? message : "ripgrep exited with an error");
259
264
  }
260
265
  if (result.exitCode === 1 || result.stdout.trim().length === 0) {