@travisennis/acai 0.0.10 → 0.0.12

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.
Files changed (179) hide show
  1. package/README.md +7 -4
  2. package/dist/agent/index.d.ts.map +1 -1
  3. package/dist/agent/index.js +29 -27
  4. package/dist/cli/stdin.d.ts +2 -1
  5. package/dist/cli/stdin.d.ts.map +1 -1
  6. package/dist/commands/generate-rules/service.d.ts +3 -2
  7. package/dist/commands/generate-rules/service.d.ts.map +1 -1
  8. package/dist/commands/health/utils.d.ts +3 -2
  9. package/dist/commands/health/utils.d.ts.map +1 -1
  10. package/dist/commands/init-project/utils.d.ts +2 -1
  11. package/dist/commands/init-project/utils.d.ts.map +1 -1
  12. package/dist/commands/init-project/utils.js +0 -11
  13. package/dist/commands/manager.d.ts.map +1 -1
  14. package/dist/commands/manager.js +6 -1
  15. package/dist/commands/resources/index.d.ts.map +1 -1
  16. package/dist/commands/resources/index.js +4 -1
  17. package/dist/commands/review/utils.d.ts +6 -1
  18. package/dist/commands/review/utils.d.ts.map +1 -1
  19. package/dist/commands/session/index.d.ts.map +1 -1
  20. package/dist/commands/session/index.js +6 -0
  21. package/dist/commands/session/types.d.ts +1 -0
  22. package/dist/commands/session/types.d.ts.map +1 -1
  23. package/dist/commands/tools/index.d.ts +3 -0
  24. package/dist/commands/tools/index.d.ts.map +1 -0
  25. package/dist/commands/tools/index.js +190 -0
  26. package/dist/commands/tools/templates.d.ts +6 -0
  27. package/dist/commands/tools/templates.d.ts.map +1 -0
  28. package/dist/commands/tools/templates.js +97 -0
  29. package/dist/config/index.d.ts +5 -0
  30. package/dist/config/index.d.ts.map +1 -1
  31. package/dist/config/index.js +41 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +15 -3
  34. package/dist/models/anthropic-provider.d.ts +1 -1
  35. package/dist/models/deepseek-provider.d.ts +3 -3
  36. package/dist/models/deepseek-provider.js +17 -17
  37. package/dist/models/google-provider.d.ts +2 -4
  38. package/dist/models/google-provider.d.ts.map +1 -1
  39. package/dist/models/google-provider.js +2 -17
  40. package/dist/models/groq-provider.d.ts +2 -4
  41. package/dist/models/groq-provider.d.ts.map +1 -1
  42. package/dist/models/groq-provider.js +3 -21
  43. package/dist/models/opencode-go-provider.d.ts +35 -0
  44. package/dist/models/opencode-go-provider.d.ts.map +1 -0
  45. package/dist/models/opencode-go-provider.js +214 -0
  46. package/dist/models/opencode-zen-provider.d.ts +5 -5
  47. package/dist/models/opencode-zen-provider.d.ts.map +1 -1
  48. package/dist/models/opencode-zen-provider.js +41 -47
  49. package/dist/models/openrouter-provider.d.ts +5 -13
  50. package/dist/models/openrouter-provider.d.ts.map +1 -1
  51. package/dist/models/openrouter-provider.js +34 -138
  52. package/dist/models/providers.d.ts +3 -3
  53. package/dist/models/providers.d.ts.map +1 -1
  54. package/dist/models/providers.js +6 -0
  55. package/dist/models/xai-provider.d.ts +1 -2
  56. package/dist/models/xai-provider.d.ts.map +1 -1
  57. package/dist/models/xai-provider.js +0 -13
  58. package/dist/prompts/manager.d.ts.map +1 -1
  59. package/dist/prompts/manager.js +5 -1
  60. package/dist/prompts/mentions.d.ts.map +1 -1
  61. package/dist/prompts/mentions.js +35 -6
  62. package/dist/prompts/system-prompt.d.ts +1 -0
  63. package/dist/prompts/system-prompt.d.ts.map +1 -1
  64. package/dist/prompts/system-prompt.js +20 -5
  65. package/dist/repl/index.d.ts +1 -2
  66. package/dist/repl/index.d.ts.map +1 -1
  67. package/dist/repl/index.js +14 -53
  68. package/dist/sessions/manager.d.ts +3 -3
  69. package/dist/sessions/manager.d.ts.map +1 -1
  70. package/dist/sessions/manager.js +1 -1
  71. package/dist/skills/activated-tracker.d.ts +11 -0
  72. package/dist/skills/activated-tracker.d.ts.map +1 -0
  73. package/dist/skills/activated-tracker.js +16 -0
  74. package/dist/skills/index.d.ts +3 -2
  75. package/dist/skills/index.d.ts.map +1 -1
  76. package/dist/skills/index.js +7 -1
  77. package/dist/subagents/index.d.ts +2 -1
  78. package/dist/subagents/index.d.ts.map +1 -1
  79. package/dist/terminal/table/utils.d.ts +1 -1
  80. package/dist/terminal/table/utils.d.ts.map +1 -1
  81. package/dist/terminal/wrap-ansi.js +2 -2
  82. package/dist/tools/agent.js +1 -1
  83. package/dist/tools/apply-patch.d.ts +62 -0
  84. package/dist/tools/apply-patch.d.ts.map +1 -0
  85. package/dist/tools/apply-patch.js +377 -0
  86. package/dist/tools/bash.d.ts +4 -4
  87. package/dist/tools/bash.d.ts.map +1 -1
  88. package/dist/tools/bash.js +40 -8
  89. package/dist/tools/directory-tree.d.ts +4 -4
  90. package/dist/tools/directory-tree.d.ts.map +1 -1
  91. package/dist/tools/directory-tree.js +3 -1
  92. package/dist/tools/dynamic-tool-loader.d.ts +12 -3
  93. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  94. package/dist/tools/dynamic-tool-loader.js +299 -39
  95. package/dist/tools/edit-file.d.ts +2 -2
  96. package/dist/tools/edit-file.d.ts.map +1 -1
  97. package/dist/tools/edit-file.js +188 -79
  98. package/dist/tools/glob.d.ts +16 -16
  99. package/dist/tools/glob.d.ts.map +1 -1
  100. package/dist/tools/glob.js +30 -15
  101. package/dist/tools/grep.d.ts +14 -14
  102. package/dist/tools/grep.d.ts.map +1 -1
  103. package/dist/tools/grep.js +50 -29
  104. package/dist/tools/index.d.ts +57 -84
  105. package/dist/tools/index.d.ts.map +1 -1
  106. package/dist/tools/index.js +20 -5
  107. package/dist/tools/ls.d.ts +2 -2
  108. package/dist/tools/ls.d.ts.map +1 -1
  109. package/dist/tools/ls.js +2 -1
  110. package/dist/tools/read-file.d.ts +9 -11
  111. package/dist/tools/read-file.d.ts.map +1 -1
  112. package/dist/tools/read-file.js +21 -16
  113. package/dist/tools/save-file.d.ts +4 -4
  114. package/dist/tools/save-file.d.ts.map +1 -1
  115. package/dist/tools/save-file.js +26 -21
  116. package/dist/tools/skill.d.ts +2 -1
  117. package/dist/tools/skill.d.ts.map +1 -1
  118. package/dist/tools/skill.js +55 -12
  119. package/dist/tools/types.d.ts +8 -2
  120. package/dist/tools/types.d.ts.map +1 -1
  121. package/dist/tools/web-fetch.d.ts +6 -18
  122. package/dist/tools/web-fetch.d.ts.map +1 -1
  123. package/dist/tools/web-fetch.js +45 -9
  124. package/dist/tools/web-search.d.ts +4 -22
  125. package/dist/tools/web-search.d.ts.map +1 -1
  126. package/dist/tools/web-search.js +1 -1
  127. package/dist/tui/autocomplete/file-search-provider.js +1 -1
  128. package/dist/tui/autocomplete/utils.d.ts +2 -1
  129. package/dist/tui/autocomplete/utils.d.ts.map +1 -1
  130. package/dist/tui/autocomplete/utils.js +25 -23
  131. package/dist/tui/components/editor.d.ts +2 -1
  132. package/dist/tui/components/editor.d.ts.map +1 -1
  133. package/dist/tui/components/editor.js +1 -1
  134. package/dist/tui/components/footer.d.ts +0 -2
  135. package/dist/tui/components/footer.d.ts.map +1 -1
  136. package/dist/tui/components/footer.js +1 -17
  137. package/dist/tui/components/markdown.d.ts +2 -2
  138. package/dist/tui/components/markdown.d.ts.map +1 -1
  139. package/dist/tui/components/welcome.d.ts +2 -1
  140. package/dist/tui/components/welcome.d.ts.map +1 -1
  141. package/dist/tui/editor-launcher.d.ts +3 -2
  142. package/dist/tui/editor-launcher.d.ts.map +1 -1
  143. package/dist/tui/index.d.ts +0 -1
  144. package/dist/tui/index.d.ts.map +1 -1
  145. package/dist/tui/tui.d.ts +1 -0
  146. package/dist/tui/tui.d.ts.map +1 -1
  147. package/dist/tui/tui.js +9 -0
  148. package/dist/tui/utils.d.ts +1 -5
  149. package/dist/tui/utils.d.ts.map +1 -1
  150. package/dist/tui/utils.js +271 -44
  151. package/dist/utils/binary-output.d.ts +32 -0
  152. package/dist/utils/binary-output.d.ts.map +1 -0
  153. package/dist/utils/binary-output.js +127 -0
  154. package/dist/utils/command-protection.d.ts.map +1 -1
  155. package/dist/utils/command-protection.js +92 -9
  156. package/dist/utils/parsing.d.ts +1 -1
  157. package/dist/utils/parsing.d.ts.map +1 -1
  158. package/package.json +28 -26
  159. package/dist/commands/add-directory/types.d.ts +0 -6
  160. package/dist/commands/add-directory/types.d.ts.map +0 -1
  161. package/dist/commands/add-directory/types.js +0 -1
  162. package/dist/commands/copy/types.d.ts +0 -3
  163. package/dist/commands/copy/types.d.ts.map +0 -1
  164. package/dist/commands/copy/types.js +0 -1
  165. package/dist/commands/review/types.d.ts +0 -12
  166. package/dist/commands/review/types.d.ts.map +0 -1
  167. package/dist/commands/review/types.js +0 -1
  168. package/dist/modes/manager.d.ts +0 -23
  169. package/dist/modes/manager.d.ts.map +0 -1
  170. package/dist/modes/manager.js +0 -77
  171. package/dist/modes/prompts.d.ts +0 -2
  172. package/dist/modes/prompts.d.ts.map +0 -1
  173. package/dist/modes/prompts.js +0 -143
  174. package/dist/tools/code-search.d.ts +0 -41
  175. package/dist/tools/code-search.d.ts.map +0 -1
  176. package/dist/tools/code-search.js +0 -195
  177. package/dist/utils/iterables.d.ts +0 -2
  178. package/dist/utils/iterables.d.ts.map +0 -1
  179. package/dist/utils/iterables.js +0 -6
@@ -118,25 +118,180 @@ function validateEdits(edits) {
118
118
  throw new Error("Invalid oldText in edit. The value of oldText must be at least one character");
119
119
  }
120
120
  }
121
- async function applyEditsSequentially(edits, content, abortSignal, filePath) {
122
- let modifiedContent = content;
123
- for (const edit of edits) {
124
- if (abortSignal?.aborted) {
125
- throw new Error("File edit operation aborted during processing");
121
+ /**
122
+ * Normalize text for fuzzy matching by:
123
+ * - Unicode NFKC normalization (canonical compatibility decomposition)
124
+ * - Converting smart quotes to straight quotes
125
+ * - Unifying various dash characters to hyphen
126
+ * - Normalizing whitespace characters to regular space
127
+ * - Removing trailing whitespace from each line
128
+ */
129
+ function normalizeForFuzzyMatch(text) {
130
+ return text
131
+ .normalize("NFKC")
132
+ .split("\n")
133
+ .map((line) => line.trimEnd())
134
+ .join("\n")
135
+ .replace(/[\u2018\u2019\u201A\u201B]/g, "'") // curly single quotes → '
136
+ .replace(/[\u201C\u201D\u201E\u201F]/g, '"') // curly double quotes → "
137
+ .replace(/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/g, "-") // dashes → -
138
+ .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " "); // spaces → space
139
+ }
140
+ /**
141
+ * Find text in content, trying exact match first, then fuzzy match.
142
+ * When fuzzy match is used, positions are in the normalized content.
143
+ * IMPORTANT: When fuzzy matching is needed, the caller must work entirely
144
+ * in normalized space to avoid position mapping issues.
145
+ */
146
+ function fuzzyFindText(content, searchText) {
147
+ // Try exact match first
148
+ const exactIndex = content.indexOf(searchText);
149
+ if (exactIndex !== -1) {
150
+ return {
151
+ found: true,
152
+ index: exactIndex,
153
+ matchLength: searchText.length,
154
+ usedFuzzyMatch: false,
155
+ };
156
+ }
157
+ // Fall back to fuzzy matching
158
+ const fuzzyContent = normalizeForFuzzyMatch(content);
159
+ const fuzzySearch = normalizeForFuzzyMatch(searchText);
160
+ const fuzzyIndex = fuzzyContent.indexOf(fuzzySearch);
161
+ if (fuzzyIndex === -1) {
162
+ return { found: false, index: -1, matchLength: 0, usedFuzzyMatch: false };
163
+ }
164
+ return {
165
+ found: true,
166
+ index: fuzzyIndex,
167
+ matchLength: fuzzySearch.length,
168
+ usedFuzzyMatch: true,
169
+ };
170
+ }
171
+ /**
172
+ * Count how many times searchText appears in content (exact or fuzzy).
173
+ * Used to ensure uniqueness.
174
+ */
175
+ function countMatches(content, searchText) {
176
+ // Count exact matches first
177
+ const exactEscaped = searchText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
178
+ const exactRegex = new RegExp(exactEscaped, "g");
179
+ const exactMatches = content.match(exactRegex);
180
+ const exactCount = exactMatches ? exactMatches.length : 0;
181
+ if (exactCount > 0) {
182
+ return exactCount;
183
+ }
184
+ // Count fuzzy matches
185
+ const fuzzyContent = normalizeForFuzzyMatch(content);
186
+ const fuzzySearch = normalizeForFuzzyMatch(searchText);
187
+ const fuzzyEscaped = fuzzySearch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
188
+ const fuzzyRegex = new RegExp(fuzzyEscaped, "g");
189
+ const fuzzyMatches = fuzzyContent.match(fuzzyRegex);
190
+ return fuzzyMatches ? fuzzyMatches.length : 0;
191
+ }
192
+ /**
193
+ * Preflight validation: Find all edit positions, validate uniqueness and no overlaps.
194
+ * If any edit requires fuzzy matching, normalize the entire content and work in
195
+ * normalized space. This avoids position mapping issues.
196
+ */
197
+ function preflightEdits(edits, content) {
198
+ const normalizedEdits = edits.map((edit) => ({
199
+ oldText: normalizeLineEndings(edit.oldText),
200
+ newText: normalizeLineEndings(edit.newText),
201
+ }));
202
+ // Check if any edit requires fuzzy matching
203
+ const needsFuzzyMatching = normalizedEdits.some((edit) => content.indexOf(edit.oldText) === -1);
204
+ // Use normalized content if fuzzy matching is needed
205
+ const baseContent = needsFuzzyMatching
206
+ ? normalizeForFuzzyMatch(content)
207
+ : content;
208
+ const matchedEdits = [];
209
+ // First pass: Find all match positions
210
+ for (let i = 0; i < normalizedEdits.length; i++) {
211
+ const edit = normalizedEdits[i];
212
+ // Check uniqueness
213
+ const matchCount = countMatches(baseContent, edit.oldText);
214
+ if (matchCount === 0) {
215
+ return {
216
+ success: false,
217
+ matchedEdits: [],
218
+ errorMessage: `Edit ${i + 1}: Could not find the exact text. ` +
219
+ "The oldText must match exactly including all whitespace and newlines.",
220
+ usedFuzzyMatch: needsFuzzyMatching,
221
+ baseContent,
222
+ };
126
223
  }
127
- const result = await applyNormalizedEdit(edit, modifiedContent);
128
- if (result.success) {
129
- modifiedContent = result.content;
224
+ if (matchCount > 1) {
225
+ const fuzzyContext = needsFuzzyMatching
226
+ ? " (including fuzzy matches)"
227
+ : "";
228
+ return {
229
+ success: false,
230
+ matchedEdits: [],
231
+ errorMessage: `Edit ${i + 1}: oldText matches ${matchCount} locations${fuzzyContext} but should match only 1. ` +
232
+ "Please provide a more specific oldText that includes more surrounding context.",
233
+ usedFuzzyMatch: needsFuzzyMatching,
234
+ baseContent,
235
+ };
130
236
  }
131
- else if (result.errorMessage) {
132
- throw new Error(result.errorMessage);
237
+ // Find the match position
238
+ const matchResult = fuzzyFindText(baseContent, edit.oldText);
239
+ if (!matchResult.found) {
240
+ return {
241
+ success: false,
242
+ matchedEdits: [],
243
+ errorMessage: `Edit ${i + 1}: Could not find the text (unexpected error).`,
244
+ usedFuzzyMatch: needsFuzzyMatching,
245
+ baseContent,
246
+ };
133
247
  }
134
- else {
135
- throw new Error(`Could not find the exact text in ${filePath}. The oldText must match exactly including all whitespace and newlines. ` +
136
- "Tip: Check for invisible characters, extra/missing whitespace, or line ending differences.");
248
+ matchedEdits.push({
249
+ ...edit,
250
+ index: matchResult.index,
251
+ matchLength: matchResult.matchLength,
252
+ editIndex: i,
253
+ });
254
+ }
255
+ // Sort by position (ascending) for overlap detection
256
+ matchedEdits.sort((a, b) => a.index - b.index);
257
+ // Check for overlapping edits
258
+ for (let i = 0; i < matchedEdits.length - 1; i++) {
259
+ const current = matchedEdits[i];
260
+ const next = matchedEdits[i + 1];
261
+ // Check if current edit overlaps with next edit
262
+ if (current.index + current.matchLength > next.index) {
263
+ return {
264
+ success: false,
265
+ matchedEdits: [],
266
+ errorMessage: `Edits ${current.editIndex + 1} and ${next.editIndex + 1} overlap in the file. ` +
267
+ "Each edit must target a distinct region. Please combine overlapping edits into a single edit.",
268
+ usedFuzzyMatch: needsFuzzyMatching,
269
+ baseContent,
270
+ };
137
271
  }
138
272
  }
139
- return modifiedContent;
273
+ return {
274
+ success: true,
275
+ matchedEdits,
276
+ usedFuzzyMatch: needsFuzzyMatching,
277
+ baseContent,
278
+ };
279
+ }
280
+ /**
281
+ * Apply edits in reverse position order (highest index first).
282
+ * This prevents position shifting - earlier edits don't affect later ones.
283
+ * All positions are relative to baseContent (normalized if fuzzy matching).
284
+ */
285
+ function applyEditsReverseOrder(content, matchedEdits) {
286
+ let result = content;
287
+ // Process in reverse order (highest index first)
288
+ for (let i = matchedEdits.length - 1; i >= 0; i--) {
289
+ const edit = matchedEdits[i];
290
+ const before = result.slice(0, edit.index);
291
+ const after = result.slice(edit.index + edit.matchLength);
292
+ result = before + edit.newText + after;
293
+ }
294
+ return result;
140
295
  }
141
296
  function formatDiff(diff, _filePath) {
142
297
  let numBackticks = 3;
@@ -158,11 +313,27 @@ export async function applyFileEdits(filePath, edits, dryRun = false, abortSigna
158
313
  const originalLineEnding = detectLineEnding(bomStrippedContent);
159
314
  const content = normalizeLineEndings(bomStrippedContent);
160
315
  validateEdits(edits);
161
- const modifiedContent = await applyEditsSequentially(edits, content, abortSignal, filePath);
316
+ // PREFLIGHT: Find all positions, validate no overlaps
317
+ const preflight = preflightEdits(edits, content);
318
+ if (!preflight.success) {
319
+ throw new Error(`Edit validation failed: ${preflight.errorMessage}`);
320
+ }
321
+ // All edits validated - apply in reverse order
322
+ // Note: baseContent is normalized if fuzzy matching was needed
323
+ const modifiedContent = applyEditsReverseOrder(preflight.baseContent, preflight.matchedEdits);
324
+ // Verify something actually changed
325
+ if (modifiedContent === preflight.baseContent) {
326
+ throw new Error("No changes were made - all edits resulted in identical content");
327
+ }
162
328
  const finalContentWithLineEndings = restoreLineEndings(modifiedContent, originalLineEnding);
163
329
  const finalContent = originalBom + finalContentWithLineEndings;
164
- const diff = createUnifiedDiff(content, finalContent, filePath);
330
+ // Use baseContent for diff (normalized if fuzzy matching)
331
+ const diff = createUnifiedDiff(preflight.baseContent, modifiedContent, filePath);
165
332
  const formattedDiff = formatDiff(diff, filePath);
333
+ // Add fuzzy match indicator if applicable
334
+ const result = preflight.usedFuzzyMatch
335
+ ? `${formattedDiff}\n\n(Note: Used fuzzy matching - file content has been normalized)`
336
+ : formattedDiff;
166
337
  if (!dryRun) {
167
338
  if (abortSignal?.aborted) {
168
339
  throw new Error("File edit operation aborted before writing");
@@ -172,67 +343,5 @@ export async function applyFileEdits(filePath, edits, dryRun = false, abortSigna
172
343
  signal: abortSignal,
173
344
  });
174
345
  }
175
- return formattedDiff;
176
- }
177
- /**
178
- * Applies a single edit with normalized line endings
179
- * Returns an error if oldText matches more than one location in the file
180
- */
181
- async function applyNormalizedEdit(edit, content) {
182
- // Normalize line endings to match the normalized content
183
- const normalizedOldText = normalizeLineEndings(edit.oldText);
184
- const normalizedNewText = normalizeLineEndings(edit.newText);
185
- // First, check how many matches exist (without replacing)
186
- const matchCountResult = countMatches(content, normalizedOldText);
187
- const matchCount = matchCountResult.count;
188
- // If more than one match, require unique oldText
189
- if (matchCount > 1) {
190
- return {
191
- success: false,
192
- content,
193
- matchCount,
194
- errorMessage: `oldText matches ${matchCount} locations in the file but should match only 1. ` +
195
- "Please provide a more specific oldText that includes more surrounding context (e.g., 3+ lines, " +
196
- "function/class names, or unique surrounding code) to uniquely identify the location you want to edit.",
197
- };
198
- }
199
- // If no matches, return failure
200
- if (matchCount === 0) {
201
- return { success: false, content };
202
- }
203
- // Exactly one match - apply the edit
204
- const originalResult = applyLiteralEdit(content, normalizedOldText, normalizedNewText);
205
- return { success: true, content: originalResult.content };
206
- }
207
- /**
208
- * Count the number of literal matches in content without replacing
209
- */
210
- function countMatches(content, search) {
211
- if (search === "") {
212
- return { count: 0 };
213
- }
214
- // Escape special regex characters for literal matching
215
- const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
216
- const regex = new RegExp(escapedSearch, "g");
217
- // Count matches without modifying content
218
- const matches = content.match(regex);
219
- return { count: matches ? matches.length : 0 };
220
- }
221
- /**
222
- * Applies a literal search and replace operation
223
- */
224
- function applyLiteralEdit(content, search, replace) {
225
- if (search === "") {
226
- return { matchCount: 0, content };
227
- }
228
- // Escape special regex characters for literal matching
229
- const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
230
- const regex = new RegExp(escapedSearch, "g");
231
- // Use replace with callback to count matches while replacing all occurrences
232
- let matchCount = 0;
233
- const modifiedContent = content.replace(regex, () => {
234
- matchCount++;
235
- return replace;
236
- });
237
- return { matchCount, content: modifiedContent };
346
+ return result;
238
347
  }
@@ -4,28 +4,28 @@ export declare const GlobTool: {
4
4
  name: "Glob";
5
5
  };
6
6
  export declare const inputSchema: z.ZodObject<{
7
- patterns: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
8
- path: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodString>;
9
- gitignore: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
10
- recursive: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
11
- expandDirectories: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
12
- ignoreFiles: z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
13
- cwd: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedString<unknown>>>;
14
- maxResults: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
7
+ patterns: z.ZodDefault<z.ZodPreprocess<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
8
+ path: z.ZodDefault<z.ZodPreprocess<z.ZodString>>;
9
+ gitignore: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
10
+ recursive: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
11
+ expandDirectories: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
12
+ ignoreFiles: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>>;
13
+ cwd: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedString<unknown>>>>;
14
+ maxResults: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
15
15
  }, z.core.$strip>;
16
16
  type GlobInputSchema = z.infer<typeof inputSchema>;
17
17
  export declare const createGlobTool: () => {
18
18
  toolDef: {
19
19
  description: string;
20
20
  inputSchema: z.ZodObject<{
21
- patterns: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
22
- path: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodString>;
23
- gitignore: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
24
- recursive: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
25
- expandDirectories: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
26
- ignoreFiles: z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
27
- cwd: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedString<unknown>>>;
28
- maxResults: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
21
+ patterns: z.ZodDefault<z.ZodPreprocess<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
22
+ path: z.ZodDefault<z.ZodPreprocess<z.ZodString>>;
23
+ gitignore: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
24
+ recursive: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
25
+ expandDirectories: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
26
+ ignoreFiles: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>>;
27
+ cwd: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedString<unknown>>>>;
28
+ maxResults: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
29
29
  }, z.core.$strip>;
30
30
  };
31
31
  display({ patterns, path }: GlobInputSchema): string;
@@ -1 +1 @@
1
- {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../source/tools/glob.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AA8CvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;iBA0EtB,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;gCAMK,eAAe;wGAmBtC,eAAe,mBACD,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;CA6CrB,CAAC"}
1
+ {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../source/tools/glob.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AA6EvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;iBAkFtB,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;gCAMK,eAAe;wGAmBtC,eAAe,mBACD,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;CAuCrB,CAAC"}
@@ -38,6 +38,24 @@ function formatResult(sortedFiles) {
38
38
  ? sortedFiles.join("\n")
39
39
  : "No files found matching the specified patterns.";
40
40
  }
41
+ function normalizePatternArray(patterns) {
42
+ return Array.isArray(patterns) ? patterns : [patterns];
43
+ }
44
+ function buildGlobOptions(effectivePath, gitignore, recursive, expandDirectories, ignoreFiles, cwd) {
45
+ return {
46
+ cwd: cwd || effectivePath,
47
+ ...(gitignore !== null && { gitignore }),
48
+ ...(recursive !== null && { recursive }),
49
+ ...(expandDirectories !== null && { expandDirectories }),
50
+ ...(ignoreFiles !== null && { ignoreFiles }),
51
+ };
52
+ }
53
+ function limitResults(sortedFiles, effectiveMaxResults) {
54
+ if (effectiveMaxResults > 0 && sortedFiles.length > effectiveMaxResults) {
55
+ return sortedFiles.slice(0, effectiveMaxResults);
56
+ }
57
+ return sortedFiles;
58
+ }
41
59
  export const GlobTool = {
42
60
  name: "Glob",
43
61
  };
@@ -63,18 +81,23 @@ export const inputSchema = z.object({
63
81
  }
64
82
  return val;
65
83
  }, z.union([z.string(), z.array(z.string())]))
84
+ .default("**/*")
66
85
  .describe("Glob patterns to search for (e.g., '*.ts', '**/*.test.ts', 'src/**/*.js')"),
67
86
  path: z
68
87
  .preprocess((val) => (val === null || val === undefined ? process.cwd() : val), z.string())
88
+ .default(process.cwd())
69
89
  .describe("Base directory to search in"),
70
90
  gitignore: z
71
91
  .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
92
+ .default(null)
72
93
  .describe("Respect ignore patterns in .gitignore files. (default: true)"),
73
94
  recursive: z
74
95
  .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
96
+ .default(null)
75
97
  .describe("Search recursively. (default: true)"),
76
98
  expandDirectories: z
77
99
  .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
100
+ .default(null)
78
101
  .describe("Automatically expand directories to files. (default: true)"),
79
102
  ignoreFiles: z
80
103
  .preprocess((val) => {
@@ -98,12 +121,15 @@ export const inputSchema = z.object({
98
121
  }
99
122
  return converted;
100
123
  }, z.union([z.string(), z.array(z.string())]).nullable())
124
+ .default(null)
101
125
  .describe("Glob patterns to look for ignore files (e.g., '.gitignore'). Pass null to use default behavior."),
102
126
  cwd: z
103
127
  .preprocess((val) => convertNullString(val), z.coerce.string().nullable())
128
+ .default(null)
104
129
  .describe("Current working directory override. (default: process.cwd())"),
105
130
  maxResults: z
106
131
  .preprocess((val) => convertNullString(val), z.coerce.number().nullable())
132
+ .default(null)
107
133
  .describe("Maximum number of files to return. Set to 0 for no limit. (Default: 100)"),
108
134
  });
109
135
  export const createGlobTool = () => {
@@ -125,27 +151,16 @@ export const createGlobTool = () => {
125
151
  throw new Error("Glob search aborted");
126
152
  }
127
153
  const effectivePath = typeof path === "string" && path.trim() !== "" ? path : process.cwd();
128
- const patternArray = Array.isArray(patterns) ? patterns : [patterns];
154
+ const patternArray = normalizePatternArray(patterns);
129
155
  const effectiveMaxResults = maxResults ?? DEFAULT_MAX_RESULTS;
130
- const globOptions = {
131
- cwd: cwd || process.cwd(),
132
- ...(gitignore !== null && { gitignore }),
133
- ...(recursive !== null && { recursive }),
134
- ...(expandDirectories !== null && { expandDirectories }),
135
- ...(ignoreFiles !== null && { ignoreFiles }),
136
- };
137
- const matchingFiles = await glob(patternArray, {
138
- ...globOptions,
139
- cwd: effectivePath,
140
- });
156
+ const globOptions = buildGlobOptions(effectivePath, gitignore ?? null, recursive ?? null, expandDirectories ?? null, ignoreFiles ?? null, cwd ?? null);
157
+ const matchingFiles = await glob(patternArray, globOptions);
141
158
  const filesToStat = matchingFiles.length > MAX_STAT_FILES
142
159
  ? matchingFiles.slice(0, MAX_STAT_FILES)
143
160
  : matchingFiles;
144
161
  const filesWithStats = await Promise.all(filesToStat.map((filePath) => getFileWithStats(filePath, effectivePath)));
145
162
  const sortedFiles = sortFilesByMtime(filesWithStats);
146
- const result = effectiveMaxResults > 0 && sortedFiles.length > effectiveMaxResults
147
- ? sortedFiles.slice(0, effectiveMaxResults)
148
- : sortedFiles;
163
+ const result = limitResults(sortedFiles, effectiveMaxResults);
149
164
  return formatResult(result);
150
165
  },
151
166
  };
@@ -6,13 +6,13 @@ export declare const GrepTool: {
6
6
  declare const inputSchema: z.ZodObject<{
7
7
  pattern: z.ZodString;
8
8
  path: z.ZodString;
9
- recursive: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
10
- ignoreCase: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
11
- filePattern: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedString<unknown>>>;
12
- contextLines: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
13
- searchIgnored: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
14
- literal: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
15
- maxResults: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
9
+ recursive: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
10
+ ignoreCase: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
11
+ filePattern: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedString<unknown>>>>;
12
+ contextLines: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
13
+ searchIgnored: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
14
+ literal: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
15
+ maxResults: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
16
16
  }, z.core.$strip>;
17
17
  type GrepInputSchema = z.infer<typeof inputSchema>;
18
18
  export declare const createGrepTool: () => {
@@ -21,13 +21,13 @@ export declare const createGrepTool: () => {
21
21
  inputSchema: z.ZodObject<{
22
22
  pattern: z.ZodString;
23
23
  path: z.ZodString;
24
- recursive: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
25
- ignoreCase: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
26
- filePattern: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedString<unknown>>>;
27
- contextLines: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
28
- searchIgnored: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
29
- literal: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
30
- maxResults: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
24
+ recursive: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
25
+ ignoreCase: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
26
+ filePattern: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedString<unknown>>>>;
27
+ contextLines: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
28
+ searchIgnored: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
29
+ literal: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedBoolean<unknown>>>>;
30
+ maxResults: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
31
31
  }, z.core.$strip>;
32
32
  };
33
33
  display({ pattern, path, filePattern, recursive, ignoreCase, contextLines, }: GrepInputSchema): string;
@@ -1 +1 @@
1
- {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../source/tools/grep.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,QAAA,MAAM,WAAW;;;;;;;;;;iBAoCf,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;kFAapB,eAAe;sHAoCb,eAAe,mBACD,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;CAkErB,CAAC;AAEF,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAqLD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA8B9D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,GACxB,MAAM,EAAE,CAgEV;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,UAAU,UAAU;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,WAAW,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAqDrE;AA8BD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAEhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAE/D;AAyBD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACpC;IAAE,SAAS,EAAE,WAAW,EAAE,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAqDpD;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,EACzB,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,GAC/B,OAAO,CAAC,UAAU,CAAC,CAgGrB"}
1
+ {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../source/tools/grep.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,QAAA,MAAM,WAAW;;;;;;;;;;iBA2Cf,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;kFAapB,eAAe;sHAoCb,eAAe,mBACD,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;CAkErB,CAAC;AAEF,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAuMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA8B9D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,GACxB,MAAM,EAAE,CAgEV;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,UAAU,UAAU;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,WAAW,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAqDrE;AA8BD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAEhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAE/D;AAyBD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACpC;IAAE,SAAS,EAAE,WAAW,EAAE,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAoDpD;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,EACzB,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,GAC/B,OAAO,CAAC,UAAU,CAAC,CAgGrB"}
@@ -16,24 +16,31 @@ const inputSchema = z.object({
16
16
  path: z.string().describe("The path to search in"),
17
17
  recursive: z
18
18
  .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
19
+ .default(null)
19
20
  .describe("Search recursively. (default: true))"),
20
21
  ignoreCase: z
21
22
  .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
23
+ .default(null)
22
24
  .describe("Use case-sensitive search. (default: false)"),
23
25
  filePattern: z
24
26
  .preprocess((val) => convertNullString(val), z.coerce.string().nullable())
27
+ .default(null)
25
28
  .describe("Glob pattern to filter files (e.g., '*.ts', '**/*.test.js'). (Default: no filtering)"),
26
29
  contextLines: z
27
30
  .preprocess((val) => convertNullString(val), z.coerce.number().nullable())
31
+ .default(null)
28
32
  .describe("The number of context lines needed in search results. (Default: 0)"),
29
33
  searchIgnored: z
30
34
  .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
35
+ .default(null)
31
36
  .describe("Search ignored files. (Default: false)"),
32
37
  literal: z
33
38
  .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
39
+ .default(null)
34
40
  .describe("Pass true for fixed-string search (-F), false for regex, (Default: auto-detects unbalanced patterns like mismatched parentheses/brackets.)"),
35
41
  maxResults: z
36
42
  .preprocess((val) => convertNullString(val), z.coerce.number().nullable())
43
+ .default(null)
37
44
  .describe("Maximum number of matches to return. Set to 0 for no limit. (Default: 100)"),
38
45
  });
39
46
  export const createGrepTool = () => {
@@ -133,39 +140,47 @@ function skipCharacterClass(pattern, startIndex) {
133
140
  }
134
141
  return i;
135
142
  }
143
+ /**
144
+ * Checks if a character is valid inside braces (digits 0-9 or comma).
145
+ */
146
+ function isValidBraceChar(c) {
147
+ return (c >= "0" && c <= "9") || c === ",";
148
+ }
149
+ /**
150
+ * Checks if empty braces have a preceding atom.
151
+ * Empty braces with no preceding atom are treated as literal.
152
+ */
153
+ function hasPrecedingAtom(pattern, startIndex) {
154
+ if (startIndex === 0)
155
+ return false;
156
+ const prev = pattern[startIndex - 1];
157
+ return /\S/.test(prev);
158
+ }
136
159
  /**
137
160
  * Parses and validates the content inside braces {..}.
138
161
  * Returns true if the brace content is invalid.
139
162
  */
140
163
  function isInvalidBraceContent(pattern, startIndex) {
141
- let j = startIndex + 1;
164
+ const j = startIndex + 1;
165
+ // Find closing brace and check for invalid characters
142
166
  let hasDigits = false;
143
- let hasComma = false;
144
- // Parse content inside braces
145
- while (j < pattern.length && pattern[j] !== "}") {
146
- const c = pattern[j];
147
- if (c >= "0" && c <= "9") {
148
- hasDigits = true;
149
- }
150
- else if (c === "," && !hasComma) {
151
- hasComma = true;
152
- }
153
- else {
154
- // Invalid character inside braces
167
+ let k = j;
168
+ while (k < pattern.length && pattern[k] !== "}") {
169
+ if (!isValidBraceChar(pattern[k])) {
155
170
  return true;
156
171
  }
157
- j++;
172
+ if (pattern[k] >= "0" && pattern[k] <= "9") {
173
+ hasDigits = true;
174
+ }
175
+ k++;
158
176
  }
159
177
  // No closing brace found
160
- if (j >= pattern.length) {
178
+ if (k >= pattern.length) {
161
179
  return true;
162
180
  }
163
- // Empty braces {} with no preceding atom are treated as literal
164
- if (!hasDigits) {
165
- const prev = startIndex > 0 ? pattern[startIndex - 1] : undefined;
166
- if (prev !== undefined && /\S/.test(prev)) {
167
- return true;
168
- }
181
+ // Empty braces {} with preceding atom are invalid
182
+ if (!hasDigits && hasPrecedingAtom(pattern, startIndex)) {
183
+ return true;
169
184
  }
170
185
  return false;
171
186
  }
@@ -184,24 +199,31 @@ function hasInvalidRepetition(pattern) {
184
199
  i = skipCharacterClass(pattern, i);
185
200
  continue;
186
201
  }
202
+ // Unmatched closing brace is invalid
187
203
  if (ch === "}") {
188
- // Unmatched closing brace is invalid
189
204
  return true;
190
205
  }
206
+ // Handle opening brace
191
207
  if (ch === "{") {
192
208
  if (isInvalidBraceContent(pattern, i)) {
193
209
  return true;
194
210
  }
195
- // Find closing brace to move past it
196
- let j = i + 1;
197
- while (j < pattern.length && pattern[j] !== "}") {
198
- j++;
199
- }
200
- i = j;
211
+ i = findClosingBrace(pattern, i);
201
212
  }
202
213
  }
203
214
  return false;
204
215
  }
216
+ /**
217
+ * Finds the index of the closing brace matching an opening brace.
218
+ * Returns the index of the closing brace, or the last index of pattern if not found.
219
+ */
220
+ function findClosingBrace(pattern, startIndex) {
221
+ let j = startIndex + 1;
222
+ while (j < pattern.length && pattern[j] !== "}") {
223
+ j++;
224
+ }
225
+ return j;
226
+ }
205
227
  /**
206
228
  * Count bracket/paren/brace pairs in a regex pattern, excluding character classes.
207
229
  */
@@ -489,7 +511,6 @@ export function truncateMatches(matches, maxResults) {
489
511
  matchesKept++;
490
512
  }
491
513
  else {
492
- break;
493
514
  }
494
515
  }
495
516
  else if (match.isContext) {