@morphllm/morphsdk 0.2.57 → 0.2.58

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 (158) hide show
  1. package/dist/anthropic-CaFUHxBW.d.ts +89 -0
  2. package/dist/{chunk-6X5UOY7B.js → chunk-2CASO3ZO.js} +46 -79
  3. package/dist/chunk-2CASO3ZO.js.map +1 -0
  4. package/dist/chunk-374N3GIA.js +118 -0
  5. package/dist/chunk-374N3GIA.js.map +1 -0
  6. package/dist/chunk-3IQIT6MC.js +65 -0
  7. package/dist/chunk-3IQIT6MC.js.map +1 -0
  8. package/dist/chunk-4VGOBA2J.js +57 -0
  9. package/dist/chunk-4VGOBA2J.js.map +1 -0
  10. package/dist/chunk-527P5X2E.js +98 -0
  11. package/dist/chunk-527P5X2E.js.map +1 -0
  12. package/dist/chunk-6N6ZYZYD.js +74 -0
  13. package/dist/chunk-6N6ZYZYD.js.map +1 -0
  14. package/dist/chunk-6Y5JB4JC.js +195 -0
  15. package/dist/chunk-6Y5JB4JC.js.map +1 -0
  16. package/dist/{chunk-QFIHUCTF.js → chunk-7EIHYJSG.js} +18 -18
  17. package/dist/chunk-7EIHYJSG.js.map +1 -0
  18. package/dist/{chunk-TICMYDII.js → chunk-APP75CBN.js} +33 -16
  19. package/dist/chunk-APP75CBN.js.map +1 -0
  20. package/dist/chunk-ILJ3J5IA.js +72 -0
  21. package/dist/chunk-ILJ3J5IA.js.map +1 -0
  22. package/dist/chunk-ISWL67SF.js +1 -0
  23. package/dist/chunk-KW7OEGZK.js +9 -0
  24. package/dist/chunk-KW7OEGZK.js.map +1 -0
  25. package/dist/chunk-Q5AHGIQO.js +205 -0
  26. package/dist/chunk-Q5AHGIQO.js.map +1 -0
  27. package/dist/{chunk-TJIUA27P.js → chunk-XT5ZO6ES.js} +9 -5
  28. package/dist/chunk-XT5ZO6ES.js.map +1 -0
  29. package/dist/{chunk-LVPVVLTI.js → chunk-YV75OQTE.js} +105 -17
  30. package/dist/chunk-YV75OQTE.js.map +1 -0
  31. package/dist/{chunk-ZJIIICRA.js → chunk-ZO4PPFCZ.js} +60 -29
  32. package/dist/chunk-ZO4PPFCZ.js.map +1 -0
  33. package/dist/{client-CFoR--IU.d.ts → client-CextMMm9.d.ts} +10 -15
  34. package/dist/client.cjs +687 -341
  35. package/dist/client.cjs.map +1 -1
  36. package/dist/client.d.ts +3 -2
  37. package/dist/client.js +14 -14
  38. package/dist/finish-kXAcUJyB.d.ts +33 -0
  39. package/dist/gemini-CE80Pbdy.d.ts +117 -0
  40. package/dist/index.cjs +700 -341
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.ts +4 -3
  43. package/dist/index.js +16 -15
  44. package/dist/openai-Fvpqln7F.d.ts +89 -0
  45. package/dist/tools/warp_grep/agent/config.cjs +8 -4
  46. package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
  47. package/dist/tools/warp_grep/agent/config.d.ts +7 -2
  48. package/dist/tools/warp_grep/agent/config.js +1 -1
  49. package/dist/tools/warp_grep/agent/formatter.cjs +32 -15
  50. package/dist/tools/warp_grep/agent/formatter.cjs.map +1 -1
  51. package/dist/tools/warp_grep/agent/formatter.d.ts +1 -1
  52. package/dist/tools/warp_grep/agent/formatter.js +1 -1
  53. package/dist/tools/warp_grep/agent/parser.cjs +104 -17
  54. package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
  55. package/dist/tools/warp_grep/agent/parser.d.ts +3 -5
  56. package/dist/tools/warp_grep/agent/parser.js +1 -3
  57. package/dist/tools/warp_grep/agent/prompt.cjs +132 -56
  58. package/dist/tools/warp_grep/agent/prompt.cjs.map +1 -1
  59. package/dist/tools/warp_grep/agent/prompt.d.ts +1 -1
  60. package/dist/tools/warp_grep/agent/prompt.js +1 -1
  61. package/dist/tools/warp_grep/agent/runner.cjs +459 -192
  62. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  63. package/dist/tools/warp_grep/agent/runner.d.ts +1 -0
  64. package/dist/tools/warp_grep/agent/runner.js +6 -8
  65. package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
  66. package/dist/tools/warp_grep/agent/types.d.ts +9 -2
  67. package/dist/tools/warp_grep/anthropic.cjs +650 -260
  68. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  69. package/dist/tools/warp_grep/anthropic.d.ts +4 -74
  70. package/dist/tools/warp_grep/anthropic.js +13 -15
  71. package/dist/tools/warp_grep/client.cjs +1593 -0
  72. package/dist/tools/warp_grep/client.cjs.map +1 -0
  73. package/dist/tools/warp_grep/client.d.ts +87 -0
  74. package/dist/tools/warp_grep/client.js +26 -0
  75. package/dist/tools/warp_grep/gemini.cjs +1587 -0
  76. package/dist/tools/warp_grep/gemini.cjs.map +1 -0
  77. package/dist/tools/warp_grep/gemini.d.ts +7 -0
  78. package/dist/tools/warp_grep/gemini.js +34 -0
  79. package/dist/tools/warp_grep/harness.cjs +556 -220
  80. package/dist/tools/warp_grep/harness.cjs.map +1 -1
  81. package/dist/tools/warp_grep/harness.d.ts +50 -119
  82. package/dist/tools/warp_grep/harness.js +33 -41
  83. package/dist/tools/warp_grep/harness.js.map +1 -1
  84. package/dist/tools/warp_grep/index.cjs +812 -346
  85. package/dist/tools/warp_grep/index.cjs.map +1 -1
  86. package/dist/tools/warp_grep/index.d.ts +11 -6
  87. package/dist/tools/warp_grep/index.js +43 -22
  88. package/dist/tools/warp_grep/openai.cjs +650 -258
  89. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  90. package/dist/tools/warp_grep/openai.d.ts +4 -74
  91. package/dist/tools/warp_grep/openai.js +13 -13
  92. package/dist/tools/warp_grep/providers/local.cjs +66 -27
  93. package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
  94. package/dist/tools/warp_grep/providers/local.d.ts +4 -9
  95. package/dist/tools/warp_grep/providers/local.js +2 -2
  96. package/dist/tools/warp_grep/providers/remote.cjs +211 -0
  97. package/dist/tools/warp_grep/providers/remote.cjs.map +1 -0
  98. package/dist/tools/warp_grep/providers/remote.d.ts +67 -0
  99. package/dist/tools/warp_grep/providers/remote.js +9 -0
  100. package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
  101. package/dist/tools/warp_grep/providers/types.d.ts +7 -15
  102. package/dist/tools/warp_grep/vercel.cjs +662 -277
  103. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  104. package/dist/tools/warp_grep/vercel.d.ts +4 -51
  105. package/dist/tools/warp_grep/vercel.js +16 -14
  106. package/dist/types-a_hxdPI6.d.ts +144 -0
  107. package/dist/vercel-3yjvfmVB.d.ts +66 -0
  108. package/package.json +12 -2
  109. package/dist/chunk-6X5UOY7B.js.map +0 -1
  110. package/dist/chunk-73RQWOQC.js +0 -16
  111. package/dist/chunk-73RQWOQC.js.map +0 -1
  112. package/dist/chunk-7OQOOB3R.js +0 -1
  113. package/dist/chunk-CFF636UC.js +0 -70
  114. package/dist/chunk-CFF636UC.js.map +0 -1
  115. package/dist/chunk-EK7OQPWD.js +0 -44
  116. package/dist/chunk-EK7OQPWD.js.map +0 -1
  117. package/dist/chunk-GJ5TYNRD.js +0 -107
  118. package/dist/chunk-GJ5TYNRD.js.map +0 -1
  119. package/dist/chunk-HQO45BAJ.js +0 -14
  120. package/dist/chunk-HQO45BAJ.js.map +0 -1
  121. package/dist/chunk-IMYQOKFO.js +0 -83
  122. package/dist/chunk-IMYQOKFO.js.map +0 -1
  123. package/dist/chunk-KBQWGT5L.js +0 -77
  124. package/dist/chunk-KBQWGT5L.js.map +0 -1
  125. package/dist/chunk-LVPVVLTI.js.map +0 -1
  126. package/dist/chunk-QFIHUCTF.js.map +0 -1
  127. package/dist/chunk-TICMYDII.js.map +0 -1
  128. package/dist/chunk-TJIUA27P.js.map +0 -1
  129. package/dist/chunk-WETRQJGU.js +0 -129
  130. package/dist/chunk-WETRQJGU.js.map +0 -1
  131. package/dist/chunk-ZJIIICRA.js.map +0 -1
  132. package/dist/core-CpkYEi_T.d.ts +0 -158
  133. package/dist/tools/warp_grep/tools/analyse.cjs +0 -40
  134. package/dist/tools/warp_grep/tools/analyse.cjs.map +0 -1
  135. package/dist/tools/warp_grep/tools/analyse.d.ts +0 -10
  136. package/dist/tools/warp_grep/tools/analyse.js +0 -8
  137. package/dist/tools/warp_grep/tools/finish.cjs +0 -69
  138. package/dist/tools/warp_grep/tools/finish.cjs.map +0 -1
  139. package/dist/tools/warp_grep/tools/finish.d.ts +0 -10
  140. package/dist/tools/warp_grep/tools/finish.js +0 -10
  141. package/dist/tools/warp_grep/tools/grep.cjs +0 -38
  142. package/dist/tools/warp_grep/tools/grep.cjs.map +0 -1
  143. package/dist/tools/warp_grep/tools/grep.d.ts +0 -8
  144. package/dist/tools/warp_grep/tools/grep.js +0 -15
  145. package/dist/tools/warp_grep/tools/grep.js.map +0 -1
  146. package/dist/tools/warp_grep/tools/read.cjs +0 -38
  147. package/dist/tools/warp_grep/tools/read.cjs.map +0 -1
  148. package/dist/tools/warp_grep/tools/read.d.ts +0 -9
  149. package/dist/tools/warp_grep/tools/read.js +0 -8
  150. package/dist/tools/warp_grep/utils/format.cjs +0 -42
  151. package/dist/tools/warp_grep/utils/format.cjs.map +0 -1
  152. package/dist/tools/warp_grep/utils/format.d.ts +0 -4
  153. package/dist/tools/warp_grep/utils/format.js +0 -18
  154. package/dist/tools/warp_grep/utils/format.js.map +0 -1
  155. /package/dist/{chunk-7OQOOB3R.js.map → chunk-ISWL67SF.js.map} +0 -0
  156. /package/dist/tools/warp_grep/{tools/analyse.js.map → client.js.map} +0 -0
  157. /package/dist/tools/warp_grep/{tools/finish.js.map → gemini.js.map} +0 -0
  158. /package/dist/tools/warp_grep/{tools/read.js.map → providers/remote.js.map} +0 -0
@@ -0,0 +1,1587 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // tools/warp_grep/gemini.ts
31
+ var gemini_exports = {};
32
+ __export(gemini_exports, {
33
+ createMorphWarpGrepTool: () => createMorphWarpGrepTool,
34
+ default: () => gemini_default,
35
+ execute: () => execute,
36
+ formatResult: () => formatResult,
37
+ getSystemPrompt: () => getSystemPrompt,
38
+ warpGrepFunctionDeclaration: () => warpGrepFunctionDeclaration
39
+ });
40
+ module.exports = __toCommonJS(gemini_exports);
41
+ var import_generative_ai = require("@google/generative-ai");
42
+
43
+ // tools/warp_grep/agent/config.ts
44
+ var AGENT_CONFIG = {
45
+ MAX_TURNS: 4,
46
+ TIMEOUT_MS: 3e4,
47
+ MAX_CONTEXT_CHARS: 54e4,
48
+ MAX_OUTPUT_LINES: 200,
49
+ MAX_READ_LINES: 800,
50
+ MAX_LIST_DEPTH: 3,
51
+ LIST_TIMEOUT_MS: 2e3
52
+ };
53
+ var BUILTIN_EXCLUDES = [
54
+ // Version control
55
+ ".git",
56
+ ".svn",
57
+ ".hg",
58
+ ".bzr",
59
+ // Dependencies
60
+ "node_modules",
61
+ "bower_components",
62
+ ".pnpm",
63
+ ".yarn",
64
+ "vendor",
65
+ "packages",
66
+ "Pods",
67
+ ".bundle",
68
+ // Python
69
+ "__pycache__",
70
+ ".pytest_cache",
71
+ ".mypy_cache",
72
+ ".ruff_cache",
73
+ ".venv",
74
+ "venv",
75
+ ".tox",
76
+ ".nox",
77
+ ".eggs",
78
+ "*.egg-info",
79
+ // Build outputs
80
+ "dist",
81
+ "build",
82
+ "out",
83
+ "output",
84
+ "target",
85
+ "_build",
86
+ ".next",
87
+ ".nuxt",
88
+ ".output",
89
+ ".vercel",
90
+ ".netlify",
91
+ // Cache directories
92
+ ".cache",
93
+ ".parcel-cache",
94
+ ".turbo",
95
+ ".nx",
96
+ ".gradle",
97
+ // IDE/Editor
98
+ ".idea",
99
+ ".vscode",
100
+ ".vs",
101
+ // Coverage
102
+ "coverage",
103
+ ".coverage",
104
+ "htmlcov",
105
+ ".nyc_output",
106
+ // Temporary
107
+ "tmp",
108
+ "temp",
109
+ ".tmp",
110
+ ".temp",
111
+ // Lock files
112
+ "package-lock.json",
113
+ "yarn.lock",
114
+ "pnpm-lock.yaml",
115
+ "bun.lockb",
116
+ "Cargo.lock",
117
+ "Gemfile.lock",
118
+ "poetry.lock",
119
+ // Binary/minified
120
+ "*.min.js",
121
+ "*.min.css",
122
+ "*.bundle.js",
123
+ "*.wasm",
124
+ "*.so",
125
+ "*.dll",
126
+ "*.pyc",
127
+ "*.map",
128
+ "*.js.map",
129
+ // Hidden directories catch-all
130
+ ".*"
131
+ ];
132
+ var DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
133
+ var DEFAULT_MODEL = "morph-warp-grep-v1";
134
+
135
+ // tools/warp_grep/agent/prompt.ts
136
+ var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given search_string.
137
+
138
+ ### workflow
139
+ You have exactly 4 turns. The 4th turn MUST be a \`finish\` call. Each turn allows up to 8 parallel tool calls.
140
+
141
+ - Turn 1: Map the territory OR dive deep (based on search_string specificity)
142
+ - Turn 2-3: Refine based on findings
143
+ - Turn 4: MUST call \`finish\` with all relevant code locations
144
+ - You MAY call \`finish\` early if confident\u2014but never before at least 1 search turn.
145
+ - The user strongly prefers if you can call the finish tool early, but you must be correct
146
+
147
+ Remember, if the task feels easy to you, it is strongly desirable to call 'finish' early using fewer turns, but quality over speed
148
+
149
+ ### tools
150
+ Tool calls use nested XML elements:
151
+ \`\`\`xml
152
+ <tool_name>
153
+ <parameter>value</parameter>
154
+ </tool_name>
155
+ \`\`\`
156
+
157
+ ### \`list_directory\`
158
+ Directory tree view. Shows structure of a path, optionally filtered by regex pattern.
159
+
160
+ Elements:
161
+ - \`<path>\` (required): Directory path to list (use \`.\` for repo root)
162
+ - \`<pattern>\` (optional): Regex to filter results
163
+
164
+ Examples:
165
+ \`\`\`
166
+ <list_directory>
167
+ <path>src/services</path>
168
+ </list_directory>
169
+
170
+ <list_directory>
171
+ <path>lib/utils</path>
172
+ <pattern>.*\\.(ts|js)$</pattern>
173
+ </list_directory>
174
+ \`\`\`
175
+
176
+ ### \`read\`
177
+ Read file contents. Supports multiple line ranges.
178
+ - Returns numbered lines for easy reference
179
+ - ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
180
+
181
+ Elements:
182
+ - \`<path>\` (required): File path to read
183
+ - \`<lines>\` (optional): Line ranges like "1-50,75-80,100-120" (omit to read entire file)
184
+
185
+ Examples:
186
+ \`\`\`
187
+ <read>
188
+ <path>src/main.py</path>
189
+ </read>
190
+
191
+ <read>
192
+ <path>src/auth.py</path>
193
+ <lines>1-20,45-80,150-200</lines>
194
+ </read>
195
+ \`\`\`
196
+
197
+ ### \`grep\`
198
+ Search for pattern matches across files. Returns matches with 1 line of context above and below.
199
+ - Match lines use \`:\` separator \u2192 \`filepath:linenum:content\`
200
+ - Context lines use \`-\` separator \u2192 \`filepath-linenum-content\`
201
+
202
+ Elements:
203
+ - \`<pattern>\` (required): Search pattern (regex). Use \`(a|b)\` for OR patterns.
204
+ - \`<sub_dir>\` (optional): Subdirectory to search in (defaults to \`.\`)
205
+ - \`<glob>\` (optional): File pattern filter like \`*.py\` or \`*.{ts,tsx}\`
206
+
207
+ Examples:
208
+ \`\`\`
209
+ <grep>
210
+ <pattern>(authenticate|authorize|login)</pattern>
211
+ <sub_dir>src/auth/</sub_dir>
212
+ </grep>
213
+
214
+ <grep>
215
+ <pattern>class.*(Service|Controller)</pattern>
216
+ <glob>*.{ts,js}</glob>
217
+ </grep>
218
+
219
+ <grep>
220
+ <pattern>(DB_HOST|DATABASE_URL|connection)</pattern>
221
+ <glob>*.{py,yaml,env}</glob>
222
+ <sub_dir>lib/</sub_dir>
223
+ </grep>
224
+ \`\`\`
225
+
226
+ ### \`finish\`
227
+ Submit final answer with all relevant code locations. Uses nested \`<file>\` elements.
228
+
229
+ File elements:
230
+ - \`<path>\` (required): File path
231
+ - \`<lines>\` (optional): Line ranges like "1-50,75-80" (\`*\` for entire file)
232
+
233
+ ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
234
+
235
+ Examples:
236
+ \`\`\`
237
+ <finish>
238
+ <file>
239
+ <path>src/auth.py</path>
240
+ <lines>1-15,25-50,75-80</lines>
241
+ </file>
242
+ <file>
243
+ <path>src/models/user.py</path>
244
+ <lines>*</lines>
245
+ </file>
246
+ </finish>
247
+ \`\`\`
248
+ </tools>
249
+
250
+ <strategy>
251
+ **Before your first tool call, classify the search_string:**
252
+
253
+ | Search_string Type | Round 1 Strategy | Early Finish? |
254
+ |------------|------------------|---------------|
255
+ | **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by round 2 |
256
+ | **Conceptual** (how does X work, where is Y handled) | list_directory + 2-3 broad greps | Rarely early |
257
+ | **Exploratory** (find all tests, list API endpoints) | list_directory at multiple depths | Usually needs 3 rounds |
258
+
259
+ **Parallel call patterns:**
260
+ - **Shotgun grep**: Same pattern, 8 different directories\u2014fast coverage
261
+ - **Variant grep**: 8 pattern variations (synonyms, naming conventions)\u2014catches inconsistent codebases
262
+ - **Funnel**: 1 list_directory + 7 greps\u2014orient and search simultaneously
263
+ - **Deep read**: 8 reads on files you already identified\u2014gather full context fast
264
+
265
+ **Tool call expectations:**
266
+ - Low quality tool calls are ones that give back sparse information. This either means they are not well thought out and are not educated guesses OR, they are too broad and give back too many results.
267
+ - High quality tool calls strike a balance between complexity in the tool call to exclude results we know we don't want, and how wide the search space is so that we don't miss anything. It is ok to start off with wider search spaces, but is imperative that you use your intuition from there on out and seek high quality tool calls only.
268
+ - You are not starting blind, you have some information about root level repo structure going in, so use that to prevent making trivial repo wide queries.
269
+ - The grep tool shows you which file path and line numbers the pattern was found in, use this information smartly when trying to read the file.
270
+ </strategy>
271
+
272
+ <output_format>
273
+ EVERY response MUST follow this exact format:
274
+
275
+ 1. First, wrap your reasoning in \`<think>...</think>\` tags containing:
276
+ - Search_string classification (specific/conceptual/exploratory)
277
+ - Confidence estimate (can I finish in 1-2 rounds?)
278
+ - This round's parallel strategy
279
+ - What signals would let me finish early?
280
+
281
+ 2. Then, output up to 8 tool calls using nested XML elements.
282
+
283
+ Example:
284
+ \`\`\`
285
+ <think>
286
+ This is a specific search_string about authentication. I'll grep for auth-related patterns.
287
+ High confidence I can finish in 2 rounds if I find the auth module. I have already been shown the repo's structure at root
288
+ Strategy: Shotgun grep across likely directories.
289
+ </think>
290
+ <grep>
291
+ <pattern>(authenticate|login|session)</pattern>
292
+ <sub_dir>src/auth/</sub_dir>
293
+ </grep>
294
+ <grep>
295
+ <pattern>(middleware|interceptor)</pattern>
296
+ <glob>*.{ts,js}</glob>
297
+ </grep>
298
+ <list_directory>
299
+ <path>src/auth</path>
300
+ </list_directory>
301
+ \`\`\`
302
+
303
+ Finishing example:
304
+ \`\`\`
305
+ <think>
306
+ I think I have a rough idea, but this is my last turn so I must call the finish tool regardless.
307
+ </think>
308
+ <finish>
309
+ <file>
310
+ <path>src/auth/login.py</path>
311
+ <lines>1-50</lines>
312
+ </file>
313
+ <file>
314
+ <path>src/middleware/session.py</path>
315
+ <lines>10-80</lines>
316
+ </file>
317
+ </finish>
318
+ \`\`\`
319
+
320
+ No commentary outside \`<think>\`. No explanations after tool calls.
321
+ </output_format>
322
+
323
+ <finishing_requirements>
324
+ When calling \`finish\`:
325
+ - Include the import section (typically lines 1-20) of each file
326
+ - Include all function/class definitions that are relevant
327
+ - Include any type definitions, interfaces, or constants used
328
+ - Better to over-include than leave the user missing context
329
+ - If unsure about boundaries, include more rather than less
330
+ </finishing_requirements>`;
331
+ function getSystemPrompt() {
332
+ return SYSTEM_PROMPT;
333
+ }
334
+
335
+ // tools/warp_grep/agent/parser.ts
336
+ var VALID_COMMANDS = ["list_directory", "grep", "read", "finish"];
337
+ function isValidCommand(name) {
338
+ return VALID_COMMANDS.includes(name);
339
+ }
340
+ function getXmlElementText(xml, tagName) {
341
+ const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i");
342
+ const match = xml.match(regex);
343
+ return match ? match[1].trim() : null;
344
+ }
345
+ function parseNestedXmlTools(text) {
346
+ const tools = [];
347
+ const toolRegex = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
348
+ let match;
349
+ while ((match = toolRegex.exec(text)) !== null) {
350
+ const rawToolName = match[1].toLowerCase();
351
+ const content = match[2];
352
+ if (!isValidCommand(rawToolName)) continue;
353
+ const toolName = rawToolName;
354
+ if (toolName === "list_directory") {
355
+ const path5 = getXmlElementText(content, "path");
356
+ const pattern = getXmlElementText(content, "pattern");
357
+ if (path5) {
358
+ tools.push({ name: "list_directory", arguments: { path: path5, pattern } });
359
+ }
360
+ } else if (toolName === "grep") {
361
+ const pattern = getXmlElementText(content, "pattern");
362
+ const subDir = getXmlElementText(content, "sub_dir");
363
+ const glob = getXmlElementText(content, "glob");
364
+ if (pattern) {
365
+ tools.push({
366
+ name: "grep",
367
+ arguments: {
368
+ pattern,
369
+ path: subDir || ".",
370
+ ...glob && { glob }
371
+ }
372
+ });
373
+ }
374
+ } else if (toolName === "read") {
375
+ const path5 = getXmlElementText(content, "path");
376
+ const linesStr = getXmlElementText(content, "lines");
377
+ if (path5) {
378
+ const args = { path: path5 };
379
+ if (linesStr) {
380
+ const ranges = [];
381
+ for (const rangeStr of linesStr.split(",")) {
382
+ const trimmed = rangeStr.trim();
383
+ if (!trimmed) continue;
384
+ const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
385
+ if (Number.isFinite(s) && Number.isFinite(e)) {
386
+ ranges.push([s, e]);
387
+ } else if (Number.isFinite(s)) {
388
+ ranges.push([s, s]);
389
+ }
390
+ }
391
+ if (ranges.length === 1) {
392
+ args.start = ranges[0][0];
393
+ args.end = ranges[0][1];
394
+ } else if (ranges.length > 1) {
395
+ args.lines = ranges;
396
+ }
397
+ }
398
+ tools.push({ name: "read", arguments: args });
399
+ }
400
+ } else if (toolName === "finish") {
401
+ const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
402
+ const files = [];
403
+ let fileMatch;
404
+ while ((fileMatch = fileRegex.exec(content)) !== null) {
405
+ const fileContent = fileMatch[1];
406
+ const filePath = getXmlElementText(fileContent, "path");
407
+ const linesStr = getXmlElementText(fileContent, "lines");
408
+ if (filePath && linesStr) {
409
+ const ranges = [];
410
+ for (const rangeStr of linesStr.split(",")) {
411
+ if (rangeStr.trim() === "*") {
412
+ ranges.push([1, 999999]);
413
+ } else {
414
+ const [s, e] = rangeStr.split("-").map((v) => parseInt(v.trim(), 10));
415
+ if (Number.isFinite(s) && Number.isFinite(e)) {
416
+ ranges.push([s, e]);
417
+ }
418
+ }
419
+ }
420
+ if (ranges.length > 0) {
421
+ files.push({ path: filePath, lines: ranges });
422
+ }
423
+ }
424
+ }
425
+ if (files.length > 0) {
426
+ tools.push({ name: "finish", arguments: { files } });
427
+ }
428
+ }
429
+ }
430
+ return tools;
431
+ }
432
+ function preprocessText(text) {
433
+ let processed = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
434
+ const nestedTools = parseNestedXmlTools(processed);
435
+ const openingTagRegex = /<tool_call>|<tool>/gi;
436
+ const closingTagRegex = /<\/tool_call>|<\/tool>/gi;
437
+ const openingMatches = processed.match(openingTagRegex) || [];
438
+ const closingMatches = processed.match(closingTagRegex) || [];
439
+ if (openingMatches.length > closingMatches.length) {
440
+ const lastClosingMatch = /<\/tool_call>|<\/tool>/gi;
441
+ let lastClosingIndex = -1;
442
+ let match;
443
+ while ((match = lastClosingMatch.exec(processed)) !== null) {
444
+ lastClosingIndex = match.index + match[0].length;
445
+ }
446
+ if (lastClosingIndex > 0) {
447
+ processed = processed.slice(0, lastClosingIndex);
448
+ }
449
+ }
450
+ const toolCallLines = [];
451
+ const toolTagRegex = /<tool_call>([\s\S]*?)<\/tool_call>|<tool>([\s\S]*?)<\/tool>/gi;
452
+ let tagMatch;
453
+ while ((tagMatch = toolTagRegex.exec(processed)) !== null) {
454
+ const content = (tagMatch[1] || tagMatch[2] || "").trim();
455
+ if (content) {
456
+ const lines = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l);
457
+ toolCallLines.push(...lines);
458
+ }
459
+ }
460
+ const allLines = processed.split(/\r?\n/).map((l) => l.trim());
461
+ for (const line of allLines) {
462
+ if (!line) continue;
463
+ if (line.startsWith("<")) continue;
464
+ const firstWord = line.split(/\s/)[0];
465
+ if (VALID_COMMANDS.includes(firstWord)) {
466
+ if (!toolCallLines.includes(line)) {
467
+ toolCallLines.push(line);
468
+ }
469
+ }
470
+ }
471
+ return { lines: toolCallLines, nestedTools };
472
+ }
473
+ var LLMResponseParser = class {
474
+ finishSpecSplitRe = /,(?=[^,\s]+:)/;
475
+ parse(text) {
476
+ if (typeof text !== "string") {
477
+ throw new TypeError("Command text must be a string.");
478
+ }
479
+ const { lines, nestedTools } = preprocessText(text);
480
+ const commands = [...nestedTools];
481
+ let finishAccumulator = null;
482
+ lines.forEach((line) => {
483
+ if (!line || line.startsWith("#")) return;
484
+ const parts = this.splitLine(line);
485
+ if (parts.length === 0) return;
486
+ const cmd = parts[0];
487
+ switch (cmd) {
488
+ case "list_directory":
489
+ this.handleListDirectory(parts, line, commands);
490
+ break;
491
+ case "grep":
492
+ this.handleGrep(parts, line, commands);
493
+ break;
494
+ case "read":
495
+ this.handleRead(parts, line, commands);
496
+ break;
497
+ case "finish":
498
+ finishAccumulator = this.handleFinish(parts, line, commands, finishAccumulator);
499
+ break;
500
+ default:
501
+ break;
502
+ }
503
+ });
504
+ if (finishAccumulator) {
505
+ const map = finishAccumulator;
506
+ const entries = [...map.entries()];
507
+ const filesPayload = entries.map(([path5, ranges]) => ({
508
+ path: path5,
509
+ lines: [...ranges].sort((a, b) => a[0] - b[0])
510
+ }));
511
+ commands.push({ name: "finish", arguments: { files: filesPayload } });
512
+ }
513
+ return commands;
514
+ }
515
+ splitLine(line) {
516
+ const parts = [];
517
+ let current = "";
518
+ let inSingle = false;
519
+ for (let i = 0; i < line.length; i++) {
520
+ const ch = line[i];
521
+ if (ch === "'" && line[i - 1] !== "\\") {
522
+ inSingle = !inSingle;
523
+ current += ch;
524
+ } else if (!inSingle && /\s/.test(ch)) {
525
+ if (current) {
526
+ parts.push(current);
527
+ current = "";
528
+ }
529
+ } else {
530
+ current += ch;
531
+ }
532
+ }
533
+ if (current) parts.push(current);
534
+ return parts;
535
+ }
536
+ /** Helper to create a _skip tool call with an error message */
537
+ skip(message) {
538
+ return { name: "_skip", arguments: { message } };
539
+ }
540
+ handleListDirectory(parts, rawLine, commands) {
541
+ if (parts.length < 2) {
542
+ commands.push(this.skip(
543
+ `[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: list_directory <path> [pattern]. Example: list_directory src/`
544
+ ));
545
+ return;
546
+ }
547
+ const path5 = parts[1];
548
+ const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
549
+ commands.push({ name: "list_directory", arguments: { path: path5, pattern } });
550
+ }
551
+ handleGrep(parts, rawLine, commands) {
552
+ if (parts.length < 3) {
553
+ commands.push(this.skip(
554
+ `[SKIPPED] Your command "${rawLine}" is missing arguments. Correct format: grep '<pattern>' <path>. Example: grep 'TODO' src/`
555
+ ));
556
+ return;
557
+ }
558
+ let pat = parts[1];
559
+ if (pat.startsWith("'") && pat.endsWith("'")) {
560
+ pat = pat.slice(1, -1);
561
+ }
562
+ if (!pat) {
563
+ commands.push(this.skip(
564
+ `[SKIPPED] Your command "${rawLine}" has an empty pattern. Provide a non-empty search pattern. Example: grep 'function' src/`
565
+ ));
566
+ return;
567
+ }
568
+ commands.push({ name: "grep", arguments: { pattern: pat, path: parts[2] } });
569
+ }
570
+ handleRead(parts, rawLine, commands) {
571
+ if (parts.length < 2) {
572
+ commands.push(this.skip(
573
+ `[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: read <path> or read <path>:<start>-<end>. Example: read src/index.ts:1-50`
574
+ ));
575
+ return;
576
+ }
577
+ const spec = parts[1];
578
+ const rangeIdx = spec.indexOf(":");
579
+ if (rangeIdx === -1) {
580
+ commands.push({ name: "read", arguments: { path: spec } });
581
+ return;
582
+ }
583
+ const filePath = spec.slice(0, rangeIdx);
584
+ const range = spec.slice(rangeIdx + 1);
585
+ const [s, e] = range.split("-").map((v) => parseInt(v, 10));
586
+ if (!Number.isFinite(s) || !Number.isFinite(e)) {
587
+ commands.push({ name: "read", arguments: { path: filePath } });
588
+ return;
589
+ }
590
+ commands.push({ name: "read", arguments: { path: filePath, start: s, end: e } });
591
+ }
592
+ handleFinish(parts, rawLine, commands, acc) {
593
+ const map = acc ?? /* @__PURE__ */ new Map();
594
+ const args = parts.slice(1);
595
+ for (const token of args) {
596
+ const [filePath, rangesText] = token.split(":", 2);
597
+ if (!filePath || !rangesText) {
598
+ commands.push(this.skip(
599
+ `[SKIPPED] Invalid finish token "${token}". Correct format: finish <path>:<start>-<end>. Example: finish src/index.ts:1-50`
600
+ ));
601
+ continue;
602
+ }
603
+ const rangeSpecs = rangesText.split(",").filter(Boolean);
604
+ for (const spec of rangeSpecs) {
605
+ const [s, e] = spec.split("-").map((v) => parseInt(v, 10));
606
+ if (!Number.isFinite(s) || !Number.isFinite(e) || e < s) {
607
+ commands.push(this.skip(
608
+ `[SKIPPED] Invalid range "${spec}" in "${token}". Ranges must be <start>-<end> where start <= end. Example: 1-50`
609
+ ));
610
+ continue;
611
+ }
612
+ const arr = map.get(filePath) ?? [];
613
+ arr.push([s, e]);
614
+ map.set(filePath, arr);
615
+ }
616
+ }
617
+ return map;
618
+ }
619
+ };
620
+
621
+ // tools/warp_grep/agent/tools/grep.ts
622
+ async function toolGrep(provider, args) {
623
+ const res = await provider.grep(args);
624
+ if (res.error) {
625
+ return { output: res.error };
626
+ }
627
+ if (!res.lines.length) {
628
+ return { output: "no matches" };
629
+ }
630
+ return { output: res.lines.join("\n") };
631
+ }
632
+
633
+ // tools/warp_grep/agent/tools/read.ts
634
+ async function toolRead(provider, args) {
635
+ if (args.lines && args.lines.length > 0) {
636
+ const chunks = [];
637
+ for (const [start, end] of args.lines) {
638
+ const res2 = await provider.read({ path: args.path, start, end });
639
+ if (res2.error) return res2.error;
640
+ chunks.push(res2.lines.join("\n"));
641
+ }
642
+ if (chunks.every((c) => c === "")) return "(empty file)";
643
+ return chunks.join("\n...\n");
644
+ }
645
+ const res = await provider.read({ path: args.path, start: args.start, end: args.end });
646
+ if (res.error) {
647
+ return res.error;
648
+ }
649
+ if (!res.lines.length) return "(empty file)";
650
+ return res.lines.join("\n");
651
+ }
652
+
653
+ // tools/warp_grep/agent/tools/list_directory.ts
654
+ async function toolListDirectory(provider, args) {
655
+ const list = await provider.listDirectory({
656
+ path: args.path,
657
+ pattern: args.pattern ?? null,
658
+ maxResults: args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES,
659
+ maxDepth: args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH
660
+ });
661
+ if (!list.length) return "empty";
662
+ if (list.length >= AGENT_CONFIG.MAX_OUTPUT_LINES) {
663
+ return "query not specific enough, tool called tried to return too much context and failed";
664
+ }
665
+ return list.map((e) => {
666
+ const indent = " ".repeat(e.depth);
667
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
668
+ return `${indent}${name}`;
669
+ }).join("\n");
670
+ }
671
+
672
+ // tools/warp_grep/agent/tools/finish.ts
673
+ async function readFinishFiles(repoRoot, files, reader) {
674
+ const out = [];
675
+ for (const f of files) {
676
+ const ranges = mergeRanges(f.lines);
677
+ const chunks = [];
678
+ for (const [s, e] of ranges) {
679
+ const lines = await reader(f.path, s, e);
680
+ chunks.push(lines.join("\n"));
681
+ }
682
+ out.push({ path: f.path, ranges, content: chunks.join("\n") });
683
+ }
684
+ return out;
685
+ }
686
+ function mergeRanges(ranges) {
687
+ if (!ranges.length) return [];
688
+ const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
689
+ const merged = [];
690
+ let [cs, ce] = sorted[0];
691
+ for (let i = 1; i < sorted.length; i++) {
692
+ const [s, e] = sorted[i];
693
+ if (s <= ce + 1) {
694
+ ce = Math.max(ce, e);
695
+ } else {
696
+ merged.push([cs, ce]);
697
+ cs = s;
698
+ ce = e;
699
+ }
700
+ }
701
+ merged.push([cs, ce]);
702
+ return merged;
703
+ }
704
+
705
+ // tools/utils/resilience.ts
706
+ var DEFAULT_RETRY_CONFIG = {
707
+ maxRetries: 3,
708
+ initialDelay: 1e3,
709
+ maxDelay: 3e4,
710
+ backoffMultiplier: 2,
711
+ retryableErrors: ["ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND"]
712
+ };
713
+ async function fetchWithRetry(url, options, retryConfig = {}) {
714
+ const {
715
+ maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,
716
+ initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,
717
+ maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,
718
+ backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,
719
+ retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,
720
+ onRetry
721
+ } = retryConfig;
722
+ let lastError = null;
723
+ let delay = initialDelay;
724
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
725
+ try {
726
+ const response = await fetch(url, options);
727
+ if (response.status === 429 || response.status === 503) {
728
+ if (attempt < maxRetries) {
729
+ const retryAfter = response.headers.get("Retry-After");
730
+ const waitTime = retryAfter ? parseInt(retryAfter) * 1e3 : Math.min(delay, maxDelay);
731
+ const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);
732
+ if (onRetry) {
733
+ onRetry(attempt + 1, error);
734
+ }
735
+ await sleep(waitTime);
736
+ delay *= backoffMultiplier;
737
+ continue;
738
+ }
739
+ }
740
+ return response;
741
+ } catch (error) {
742
+ lastError = error;
743
+ const isRetryable = retryableErrors.some(
744
+ (errType) => lastError?.message?.includes(errType)
745
+ );
746
+ if (!isRetryable || attempt === maxRetries) {
747
+ throw lastError;
748
+ }
749
+ const waitTime = Math.min(delay, maxDelay);
750
+ if (onRetry) {
751
+ onRetry(attempt + 1, lastError);
752
+ }
753
+ await sleep(waitTime);
754
+ delay *= backoffMultiplier;
755
+ }
756
+ }
757
+ throw lastError || new Error("Max retries exceeded");
758
+ }
759
+ async function withTimeout(promise, timeoutMs, errorMessage) {
760
+ let timeoutId;
761
+ const timeoutPromise = new Promise((_, reject) => {
762
+ timeoutId = setTimeout(() => {
763
+ reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));
764
+ }, timeoutMs);
765
+ });
766
+ try {
767
+ const result = await Promise.race([promise, timeoutPromise]);
768
+ clearTimeout(timeoutId);
769
+ return result;
770
+ } catch (error) {
771
+ clearTimeout(timeoutId);
772
+ throw error;
773
+ }
774
+ }
775
+ function sleep(ms) {
776
+ return new Promise((resolve) => setTimeout(resolve, ms));
777
+ }
778
+
779
+ // tools/warp_grep/agent/formatter.ts
780
+ var ToolOutputFormatter = class {
781
+ format(toolName, args, output, options = {}) {
782
+ const name = (toolName ?? "").trim();
783
+ if (!name) {
784
+ return "";
785
+ }
786
+ const payload = output?.toString?.()?.trim?.() ?? "";
787
+ const isError = Boolean(options.isError);
788
+ const safeArgs = args ?? {};
789
+ if (!payload && !isError) {
790
+ return "";
791
+ }
792
+ switch (name) {
793
+ case "read":
794
+ return this.formatRead(safeArgs, payload, isError);
795
+ case "list_directory":
796
+ return this.formatListDirectory(safeArgs, payload, isError);
797
+ case "grep":
798
+ return this.formatGrep(safeArgs, payload, isError);
799
+ default:
800
+ return payload ? `<tool_output>
801
+ ${payload}
802
+ </tool_output>` : "";
803
+ }
804
+ }
805
+ formatRead(args, payload, isError) {
806
+ if (isError) {
807
+ return payload;
808
+ }
809
+ const path5 = this.asString(args.path) || "...";
810
+ const start = args.start;
811
+ const end = args.end;
812
+ const linesArray = args.lines;
813
+ const attributes = [`path="${path5}"`];
814
+ if (linesArray && linesArray.length > 0) {
815
+ const rangeStr = linesArray.map(([s, e]) => `${s}-${e}`).join(",");
816
+ attributes.push(`lines="${rangeStr}"`);
817
+ } else if (start !== void 0 && end !== void 0) {
818
+ attributes.push(`lines="${start}-${end}"`);
819
+ }
820
+ return `<read ${attributes.join(" ")}>
821
+ ${payload}
822
+ </read>`;
823
+ }
824
+ formatListDirectory(args, payload, isError) {
825
+ const path5 = this.asString(args.path) || ".";
826
+ const pattern = this.asString(args.pattern);
827
+ const attributes = [`path="${path5}"`];
828
+ if (pattern) {
829
+ attributes.push(`pattern="${pattern}"`);
830
+ }
831
+ if (isError) {
832
+ attributes.push('status="error"');
833
+ }
834
+ return `<list_directory ${attributes.join(" ")}>
835
+ ${payload}
836
+ </list_directory>`;
837
+ }
838
+ formatGrep(args, payload, isError) {
839
+ const pattern = this.asString(args.pattern);
840
+ const subDir = this.asString(args.path);
841
+ const glob = this.asString(args.glob);
842
+ const attributes = [];
843
+ if (pattern !== void 0) {
844
+ attributes.push(`pattern="${pattern}"`);
845
+ }
846
+ if (subDir !== void 0) {
847
+ attributes.push(`sub_dir="${subDir}"`);
848
+ }
849
+ if (glob !== void 0) {
850
+ attributes.push(`glob="${glob}"`);
851
+ }
852
+ if (isError) {
853
+ attributes.push('status="error"');
854
+ }
855
+ const attrText = attributes.length ? ` ${attributes.join(" ")}` : "";
856
+ return `<grep${attrText}>
857
+ ${payload}
858
+ </grep>`;
859
+ }
860
+ asString(value) {
861
+ if (value === null || value === void 0) {
862
+ return void 0;
863
+ }
864
+ return String(value);
865
+ }
866
+ };
867
+ var sharedFormatter = new ToolOutputFormatter();
868
+ function formatAgentToolOutput(toolName, args, output, options = {}) {
869
+ return sharedFormatter.format(toolName, args, output, options);
870
+ }
871
+
872
+ // tools/warp_grep/agent/helpers.ts
873
+ var import_path = __toESM(require("path"), 1);
874
+ var TRUNCATED_MARKER = "[truncated for context limit]";
875
+ function formatTurnMessage(turnsUsed, maxTurns) {
876
+ const turnsRemaining = maxTurns - turnsUsed;
877
+ if (turnsRemaining === 1) {
878
+ return `
879
+ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run out of turns to explore the code base and MUST call the finish tool now`;
880
+ }
881
+ return `
882
+ You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
883
+ }
884
+ function calculateContextBudget(messages) {
885
+ const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
886
+ const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
887
+ const percent = Math.round(totalChars / maxChars * 100);
888
+ const usedK = Math.round(totalChars / 1e3);
889
+ const maxK = Math.round(maxChars / 1e3);
890
+ return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
891
+ }
892
+ async function buildInitialState(repoRoot, query, provider) {
893
+ try {
894
+ const entries = await provider.listDirectory({
895
+ path: ".",
896
+ maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
897
+ maxDepth: 2
898
+ });
899
+ const treeLines = entries.map((e) => {
900
+ const indent = " ".repeat(e.depth);
901
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
902
+ return `${indent}${name}`;
903
+ });
904
+ const repoName = import_path.default.basename(repoRoot);
905
+ const treeOutput = treeLines.length > 0 ? `${repoName}/
906
+ ${treeLines.join("\n")}` : `${repoName}/`;
907
+ return `<repo_structure>
908
+ ${treeOutput}
909
+ </repo_structure>
910
+
911
+ <search_string>
912
+ ${query}
913
+ </search_string>`;
914
+ } catch {
915
+ const repoName = import_path.default.basename(repoRoot);
916
+ return `<repo_structure>
917
+ ${repoName}/
918
+ </repo_structure>
919
+
920
+ <search_string>
921
+ ${query}
922
+ </search_string>`;
923
+ }
924
+ }
925
+ function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
926
+ const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
927
+ if (getTotalChars() <= maxChars) {
928
+ return messages;
929
+ }
930
+ const userIndices = [];
931
+ let firstUserSkipped = false;
932
+ for (let i = 0; i < messages.length; i++) {
933
+ if (messages[i].role === "user") {
934
+ if (!firstUserSkipped) {
935
+ firstUserSkipped = true;
936
+ continue;
937
+ }
938
+ userIndices.push(i);
939
+ }
940
+ }
941
+ for (const idx of userIndices) {
942
+ if (getTotalChars() <= maxChars) {
943
+ break;
944
+ }
945
+ if (messages[idx].content !== TRUNCATED_MARKER) {
946
+ messages[idx] = { role: "user", content: TRUNCATED_MARKER };
947
+ }
948
+ }
949
+ return messages;
950
+ }
951
+
952
+ // tools/warp_grep/agent/runner.ts
953
+ var import_path2 = __toESM(require("path"), 1);
954
+ var parser = new LLMResponseParser();
955
+ var DEFAULT_API_URL = "https://api.morphllm.com";
956
+ async function callModel(messages, model, options = {}) {
957
+ const baseUrl = DEFAULT_API_URL;
958
+ const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
959
+ const fetchPromise = fetchWithRetry(
960
+ `${baseUrl}/v1/chat/completions`,
961
+ {
962
+ method: "POST",
963
+ headers: {
964
+ "Content-Type": "application/json",
965
+ Authorization: `Bearer ${apiKey}`
966
+ },
967
+ body: JSON.stringify({
968
+ model,
969
+ temperature: 0,
970
+ max_tokens: 1024,
971
+ messages
972
+ })
973
+ },
974
+ options.retryConfig
975
+ );
976
+ const resp = await withTimeout(fetchPromise, AGENT_CONFIG.TIMEOUT_MS, "morph-warp-grep request timed out");
977
+ if (!resp.ok) {
978
+ if (resp.status === 404) {
979
+ throw new Error(
980
+ "The endpoint you are trying to call is likely deprecated. Please update with: npm cache clean --force && npx -y @morphllm/morphmcp@latest or visit: https://morphllm.com/mcp"
981
+ );
982
+ }
983
+ const t = await resp.text();
984
+ throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
985
+ }
986
+ const data = await resp.json();
987
+ const content = data?.choices?.[0]?.message?.content;
988
+ if (!content || typeof content !== "string") {
989
+ throw new Error("Invalid response from model");
990
+ }
991
+ return content;
992
+ }
993
+ async function runWarpGrep(config) {
994
+ const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
995
+ const messages = [];
996
+ messages.push({ role: "system", content: getSystemPrompt() });
997
+ const initialState = await buildInitialState(repoRoot, config.query, config.provider);
998
+ messages.push({ role: "user", content: initialState });
999
+ const maxTurns = AGENT_CONFIG.MAX_TURNS;
1000
+ const model = config.model || DEFAULT_MODEL;
1001
+ const provider = config.provider;
1002
+ const errors = [];
1003
+ let finishMeta;
1004
+ let terminationReason = "terminated";
1005
+ for (let turn = 1; turn <= maxTurns; turn += 1) {
1006
+ enforceContextLimit(messages);
1007
+ const assistantContent = await callModel(messages, model, {
1008
+ morphApiKey: config.morphApiKey,
1009
+ morphApiUrl: config.morphApiUrl,
1010
+ retryConfig: config.retryConfig
1011
+ }).catch((e) => {
1012
+ errors.push({ message: e instanceof Error ? e.message : String(e) });
1013
+ return "";
1014
+ });
1015
+ if (!assistantContent) break;
1016
+ messages.push({ role: "assistant", content: assistantContent });
1017
+ const toolCalls = parser.parse(assistantContent);
1018
+ if (toolCalls.length === 0) {
1019
+ errors.push({ message: "No tool calls produced by the model. Your MCP is likely out of date! Update it by running: npm cache clean --force && npx -y @morphllm/morphmcp@latest" });
1020
+ terminationReason = "terminated";
1021
+ break;
1022
+ }
1023
+ const finishCalls = toolCalls.filter((c) => c.name === "finish");
1024
+ const grepCalls = toolCalls.filter((c) => c.name === "grep");
1025
+ const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
1026
+ const readCalls = toolCalls.filter((c) => c.name === "read");
1027
+ const skipCalls = toolCalls.filter((c) => c.name === "_skip");
1028
+ const formatted = [];
1029
+ for (const c of skipCalls) {
1030
+ const msg = c.arguments?.message || "Command skipped due to parsing error";
1031
+ formatted.push(msg);
1032
+ }
1033
+ const allPromises = [];
1034
+ for (const c of grepCalls) {
1035
+ const args = c.arguments ?? {};
1036
+ allPromises.push(
1037
+ toolGrep(provider, args).then(
1038
+ ({ output }) => formatAgentToolOutput("grep", args, output, { isError: false }),
1039
+ (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
1040
+ )
1041
+ );
1042
+ }
1043
+ for (const c of listDirCalls) {
1044
+ const args = c.arguments ?? {};
1045
+ allPromises.push(
1046
+ toolListDirectory(provider, args).then(
1047
+ (p) => formatAgentToolOutput("list_directory", args, p, { isError: false }),
1048
+ (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
1049
+ )
1050
+ );
1051
+ }
1052
+ for (const c of readCalls) {
1053
+ const args = c.arguments ?? {};
1054
+ allPromises.push(
1055
+ toolRead(provider, args).then(
1056
+ (p) => formatAgentToolOutput("read", args, p, { isError: false }),
1057
+ (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
1058
+ )
1059
+ );
1060
+ }
1061
+ const allResults = await Promise.all(allPromises);
1062
+ for (const result of allResults) {
1063
+ formatted.push(result);
1064
+ }
1065
+ if (formatted.length > 0) {
1066
+ const turnMessage = formatTurnMessage(turn, maxTurns);
1067
+ const contextBudget = calculateContextBudget(messages);
1068
+ messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
1069
+ }
1070
+ if (finishCalls.length) {
1071
+ const fc = finishCalls[0];
1072
+ const files = fc.arguments?.files ?? [];
1073
+ finishMeta = { files };
1074
+ terminationReason = "completed";
1075
+ break;
1076
+ }
1077
+ }
1078
+ if (terminationReason !== "completed" || !finishMeta) {
1079
+ return { terminationReason, messages, errors };
1080
+ }
1081
+ const parts = ["Relevant context found:"];
1082
+ for (const f of finishMeta.files) {
1083
+ const ranges = f.lines.map(([s, e]) => `${s}-${e}`).join(", ");
1084
+ parts.push(`- ${f.path}: ${ranges}`);
1085
+ }
1086
+ const payload = parts.join("\n");
1087
+ const fileReadErrors = [];
1088
+ const resolved = await readFinishFiles(
1089
+ repoRoot,
1090
+ finishMeta.files,
1091
+ async (p, s, e) => {
1092
+ try {
1093
+ const rr = await provider.read({ path: p, start: s, end: e });
1094
+ return rr.lines.map((l) => {
1095
+ const idx = l.indexOf("|");
1096
+ return idx >= 0 ? l.slice(idx + 1) : l;
1097
+ });
1098
+ } catch (err) {
1099
+ const errorMsg = err instanceof Error ? err.message : String(err);
1100
+ fileReadErrors.push({ path: p, error: errorMsg });
1101
+ console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
1102
+ return [`[couldn't find: ${p}]`];
1103
+ }
1104
+ }
1105
+ );
1106
+ if (fileReadErrors.length > 0) {
1107
+ errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
1108
+ }
1109
+ return {
1110
+ terminationReason: "completed",
1111
+ messages,
1112
+ finish: { payload, metadata: finishMeta, resolved }
1113
+ };
1114
+ }
1115
+
1116
+ // tools/warp_grep/providers/local.ts
1117
+ var import_promises2 = __toESM(require("fs/promises"), 1);
1118
+ var import_path4 = __toESM(require("path"), 1);
1119
+
1120
+ // tools/warp_grep/utils/ripgrep.ts
1121
+ var import_child_process = require("child_process");
1122
+ var import_ripgrep = require("@vscode/ripgrep");
1123
+ var resolvedRgPath = null;
1124
+ var rgPathChecked = false;
1125
+ function spawnRg(rgBinary, args, opts) {
1126
+ return new Promise((resolve) => {
1127
+ const child = (0, import_child_process.spawn)(rgBinary, args, {
1128
+ cwd: opts?.cwd,
1129
+ env: { ...process.env, ...opts?.env || {} },
1130
+ stdio: ["ignore", "pipe", "pipe"]
1131
+ });
1132
+ let stdout = "";
1133
+ let stderr = "";
1134
+ child.stdout.on("data", (d) => stdout += d.toString());
1135
+ child.stderr.on("data", (d) => stderr += d.toString());
1136
+ child.on("close", (code) => {
1137
+ resolve({ stdout, stderr, exitCode: typeof code === "number" ? code : -1 });
1138
+ });
1139
+ child.on("error", () => {
1140
+ resolve({ stdout: "", stderr: "Failed to spawn ripgrep.", exitCode: -1 });
1141
+ });
1142
+ });
1143
+ }
1144
+ function isBinaryFailure(result) {
1145
+ if (result.exitCode === -1) return true;
1146
+ if (result.stderr.includes("jemalloc") || result.stderr.includes("Unsupported system page size")) return true;
1147
+ if (result.exitCode === 134) return true;
1148
+ return false;
1149
+ }
1150
+ async function runRipgrep(args, opts) {
1151
+ if (rgPathChecked && resolvedRgPath) {
1152
+ return spawnRg(resolvedRgPath, args, opts);
1153
+ }
1154
+ if (!rgPathChecked) {
1155
+ const result = await spawnRg(import_ripgrep.rgPath, args, opts);
1156
+ if (!isBinaryFailure(result)) {
1157
+ resolvedRgPath = import_ripgrep.rgPath;
1158
+ rgPathChecked = true;
1159
+ return result;
1160
+ }
1161
+ const fallbackResult = await spawnRg("rg", args, opts);
1162
+ if (!isBinaryFailure(fallbackResult)) {
1163
+ resolvedRgPath = "rg";
1164
+ rgPathChecked = true;
1165
+ return fallbackResult;
1166
+ }
1167
+ rgPathChecked = true;
1168
+ return {
1169
+ stdout: "",
1170
+ stderr: "Failed to spawn ripgrep. Neither bundled nor system rg is available.",
1171
+ exitCode: -1
1172
+ };
1173
+ }
1174
+ return {
1175
+ stdout: "",
1176
+ stderr: "Failed to spawn ripgrep. Neither bundled nor system rg is available.",
1177
+ exitCode: -1
1178
+ };
1179
+ }
1180
+
1181
+ // tools/warp_grep/utils/paths.ts
1182
+ var import_fs = __toESM(require("fs"), 1);
1183
+ var import_path3 = __toESM(require("path"), 1);
1184
+ function resolveUnderRepo(repoRoot, targetPath) {
1185
+ const absRoot = import_path3.default.resolve(repoRoot);
1186
+ const resolved = import_path3.default.resolve(absRoot, targetPath);
1187
+ ensureWithinRepo(absRoot, resolved);
1188
+ return resolved;
1189
+ }
1190
+ function ensureWithinRepo(repoRoot, absTarget) {
1191
+ const rel = import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absTarget));
1192
+ if (rel.startsWith("..") || import_path3.default.isAbsolute(rel)) {
1193
+ throw new Error(`Path outside repository root: ${absTarget}`);
1194
+ }
1195
+ }
1196
+ function toRepoRelative(repoRoot, absPath) {
1197
+ return import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absPath));
1198
+ }
1199
+ function isSymlink(p) {
1200
+ try {
1201
+ const st = import_fs.default.lstatSync(p);
1202
+ return st.isSymbolicLink();
1203
+ } catch {
1204
+ return false;
1205
+ }
1206
+ }
1207
+ function isTextualFile(filePath, maxBytes = 2e6) {
1208
+ try {
1209
+ const st = import_fs.default.statSync(filePath);
1210
+ if (!st.isFile()) return false;
1211
+ if (st.size > maxBytes) return false;
1212
+ const fd = import_fs.default.openSync(filePath, "r");
1213
+ const buf = Buffer.alloc(512);
1214
+ const read = import_fs.default.readSync(fd, buf, 0, buf.length, 0);
1215
+ import_fs.default.closeSync(fd);
1216
+ for (let i = 0; i < read; i++) {
1217
+ const c = buf[i];
1218
+ if (c === 0) return false;
1219
+ }
1220
+ return true;
1221
+ } catch {
1222
+ return false;
1223
+ }
1224
+ }
1225
+
1226
+ // tools/warp_grep/utils/files.ts
1227
+ var import_promises = __toESM(require("fs/promises"), 1);
1228
+ async function readAllLines(filePath) {
1229
+ const content = await import_promises.default.readFile(filePath, "utf8");
1230
+ return content.split(/\r?\n/);
1231
+ }
1232
+
1233
+ // tools/warp_grep/providers/local.ts
1234
+ var LocalRipgrepProvider = class {
1235
+ constructor(repoRoot, excludes = DEFAULT_EXCLUDES) {
1236
+ this.repoRoot = repoRoot;
1237
+ this.excludes = excludes;
1238
+ }
1239
+ async grep(params) {
1240
+ let abs;
1241
+ try {
1242
+ abs = resolveUnderRepo(this.repoRoot, params.path);
1243
+ } catch (err) {
1244
+ return {
1245
+ lines: [],
1246
+ error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
1247
+ };
1248
+ }
1249
+ const stat = await import_promises2.default.stat(abs).catch(() => null);
1250
+ if (!stat) return { lines: [] };
1251
+ const targetArg = abs === import_path4.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
1252
+ const args = [
1253
+ "--no-config",
1254
+ "--no-heading",
1255
+ "--with-filename",
1256
+ "--line-number",
1257
+ "--color=never",
1258
+ "--trim",
1259
+ "--max-columns=400",
1260
+ "-C",
1261
+ "1",
1262
+ ...params.glob ? ["--glob", params.glob] : [],
1263
+ ...this.excludes.flatMap((e) => ["-g", `!${e}`]),
1264
+ params.pattern,
1265
+ targetArg || "."
1266
+ ];
1267
+ const res = await runRipgrep(args, { cwd: this.repoRoot });
1268
+ if (res.exitCode === -1) {
1269
+ return {
1270
+ lines: [],
1271
+ error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required but failed to execute. Please install it:
1272
+ \u2022 macOS: brew install ripgrep
1273
+ \u2022 Ubuntu/Debian: apt install ripgrep
1274
+ \u2022 Windows: choco install ripgrep
1275
+ \u2022 Or visit: https://github.com/BurntSushi/ripgrep#installation
1276
+ Exit code: ${res.exitCode}${res.stderr ? `
1277
+ Details: ${res.stderr}` : ""}`
1278
+ };
1279
+ }
1280
+ if (res.exitCode !== 0 && res.exitCode !== 1) {
1281
+ return {
1282
+ lines: [],
1283
+ error: `[RIPGREP ERROR] grep failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}`
1284
+ };
1285
+ }
1286
+ const lines = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
1287
+ if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
1288
+ return {
1289
+ lines: [],
1290
+ error: "query not specific enough, tool tried to return too much context and failed"
1291
+ };
1292
+ }
1293
+ return { lines };
1294
+ }
1295
+ async read(params) {
1296
+ let abs;
1297
+ try {
1298
+ abs = resolveUnderRepo(this.repoRoot, params.path);
1299
+ } catch (err) {
1300
+ return {
1301
+ lines: [],
1302
+ error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
1303
+ };
1304
+ }
1305
+ const stat = await import_promises2.default.stat(abs).catch(() => null);
1306
+ if (!stat || !stat.isFile()) {
1307
+ return {
1308
+ lines: [],
1309
+ error: `[FILE NOT FOUND] You tried to read "${params.path}" but there is no file at this path. Double-check the path exists and is spelled correctly.`
1310
+ };
1311
+ }
1312
+ if (isSymlink(abs)) {
1313
+ return {
1314
+ lines: [],
1315
+ error: `[SYMLINK] You tried to read "${params.path}" but this is a symlink. Try reading the actual file it points to instead.`
1316
+ };
1317
+ }
1318
+ if (!isTextualFile(abs)) {
1319
+ return {
1320
+ lines: [],
1321
+ error: `[UNREADABLE FILE] You tried to read "${params.path}" but this file is either too large or not a text file, so it cannot be read. Try a different file.`
1322
+ };
1323
+ }
1324
+ let lines;
1325
+ try {
1326
+ lines = await readAllLines(abs);
1327
+ } catch (err) {
1328
+ return {
1329
+ lines: [],
1330
+ error: `[READ ERROR] Failed to read "${params.path}": ${err instanceof Error ? err.message : String(err)}`
1331
+ };
1332
+ }
1333
+ const total = lines.length;
1334
+ let s = params.start ?? 1;
1335
+ let e = Math.min(params.end ?? total, total);
1336
+ if (s > total && total > 0) {
1337
+ s = 1;
1338
+ e = total;
1339
+ }
1340
+ const out = [];
1341
+ for (let i = s; i <= e; i += 1) {
1342
+ const content = lines[i - 1] ?? "";
1343
+ out.push(`${i}|${content}`);
1344
+ }
1345
+ if (out.length > AGENT_CONFIG.MAX_READ_LINES) {
1346
+ const truncated = out.slice(0, AGENT_CONFIG.MAX_READ_LINES);
1347
+ truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${out.length} lines]`);
1348
+ return { lines: truncated };
1349
+ }
1350
+ return { lines: out };
1351
+ }
1352
+ async listDirectory(params) {
1353
+ let abs;
1354
+ try {
1355
+ abs = resolveUnderRepo(this.repoRoot, params.path);
1356
+ } catch {
1357
+ return [];
1358
+ }
1359
+ const stat = await import_promises2.default.stat(abs).catch(() => null);
1360
+ if (!stat || !stat.isDirectory()) {
1361
+ return [];
1362
+ }
1363
+ const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
1364
+ const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
1365
+ const regex = params.pattern ? new RegExp(params.pattern) : null;
1366
+ const results = [];
1367
+ let timedOut = false;
1368
+ const startTime = Date.now();
1369
+ async function walk(dir, depth) {
1370
+ if (Date.now() - startTime > AGENT_CONFIG.LIST_TIMEOUT_MS) {
1371
+ timedOut = true;
1372
+ return;
1373
+ }
1374
+ if (depth > maxDepth || results.length >= maxResults) return;
1375
+ const entries = await import_promises2.default.readdir(dir, { withFileTypes: true });
1376
+ for (const entry of entries) {
1377
+ if (timedOut || results.length >= maxResults) break;
1378
+ const full = import_path4.default.join(dir, entry.name);
1379
+ const rel = toRepoRelative(abs, full).replace(/^[.][/\\]?/, "");
1380
+ if (DEFAULT_EXCLUDES.some((ex) => rel.split(import_path4.default.sep).includes(ex))) continue;
1381
+ if (regex && !regex.test(entry.name)) continue;
1382
+ results.push({
1383
+ name: entry.name,
1384
+ path: toRepoRelative(import_path4.default.resolve(""), full),
1385
+ // relative display
1386
+ type: entry.isDirectory() ? "dir" : "file",
1387
+ depth
1388
+ });
1389
+ if (entry.isDirectory()) {
1390
+ await walk(full, depth + 1);
1391
+ }
1392
+ }
1393
+ }
1394
+ await walk(abs, 0);
1395
+ return results;
1396
+ }
1397
+ };
1398
+
1399
+ // tools/warp_grep/providers/remote.ts
1400
+ var RemoteCommandsProvider = class {
1401
+ constructor(repoRoot, commands) {
1402
+ this.repoRoot = repoRoot;
1403
+ this.commands = commands;
1404
+ }
1405
+ /**
1406
+ * Run grep command and parse ripgrep output
1407
+ */
1408
+ async grep(params) {
1409
+ try {
1410
+ const stdout = await this.commands.grep(params.pattern, params.path, params.glob);
1411
+ const lines = (stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
1412
+ if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
1413
+ return {
1414
+ lines: [],
1415
+ error: "Query not specific enough - too many results returned. Try a more specific pattern."
1416
+ };
1417
+ }
1418
+ return { lines };
1419
+ } catch (error) {
1420
+ return {
1421
+ lines: [],
1422
+ error: `[GREP ERROR] ${error instanceof Error ? error.message : String(error)}`
1423
+ };
1424
+ }
1425
+ }
1426
+ /**
1427
+ * Read file and add line numbers
1428
+ */
1429
+ async read(params) {
1430
+ const start = params.start ?? 1;
1431
+ const end = params.end ?? 1e6;
1432
+ try {
1433
+ const stdout = await this.commands.read(params.path, start, end);
1434
+ const contentLines = (stdout || "").split("\n");
1435
+ if (contentLines.length > 0 && contentLines[contentLines.length - 1] === "") {
1436
+ contentLines.pop();
1437
+ }
1438
+ const lines = contentLines.map((content, idx) => `${start + idx}|${content}`);
1439
+ if (lines.length > AGENT_CONFIG.MAX_READ_LINES) {
1440
+ const truncated = lines.slice(0, AGENT_CONFIG.MAX_READ_LINES);
1441
+ truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${lines.length} lines]`);
1442
+ return { lines: truncated };
1443
+ }
1444
+ return { lines };
1445
+ } catch (error) {
1446
+ return {
1447
+ lines: [],
1448
+ error: `[READ ERROR] ${error instanceof Error ? error.message : String(error)}`
1449
+ };
1450
+ }
1451
+ }
1452
+ /**
1453
+ * List directory and parse find output
1454
+ */
1455
+ async listDirectory(params) {
1456
+ const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
1457
+ const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
1458
+ try {
1459
+ const stdout = await this.commands.listDir(params.path, maxDepth);
1460
+ const paths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
1461
+ const regex = params.pattern ? new RegExp(params.pattern) : null;
1462
+ const entries = [];
1463
+ for (const fullPath of paths) {
1464
+ if (fullPath === params.path || fullPath === this.repoRoot) continue;
1465
+ const name = fullPath.split("/").pop() || "";
1466
+ if (regex && !regex.test(name)) continue;
1467
+ let relativePath = fullPath;
1468
+ if (fullPath.startsWith(this.repoRoot)) {
1469
+ relativePath = fullPath.slice(this.repoRoot.length).replace(/^\//, "");
1470
+ }
1471
+ const depth = relativePath.split("/").filter(Boolean).length - 1;
1472
+ const hasExtension = name.includes(".") && !name.startsWith(".");
1473
+ const type = hasExtension ? "file" : "dir";
1474
+ entries.push({
1475
+ name,
1476
+ path: relativePath,
1477
+ type,
1478
+ depth: Math.max(0, depth)
1479
+ });
1480
+ if (entries.length >= maxResults) break;
1481
+ }
1482
+ return entries;
1483
+ } catch (error) {
1484
+ return [];
1485
+ }
1486
+ }
1487
+ };
1488
+
1489
+ // tools/warp_grep/client.ts
1490
+ async function executeToolCall(input, config) {
1491
+ const parsed = typeof input === "string" ? JSON.parse(input) : input;
1492
+ const provider = config.remoteCommands ? new RemoteCommandsProvider(config.repoRoot, config.remoteCommands) : config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
1493
+ const result = await runWarpGrep({
1494
+ query: parsed.query,
1495
+ repoRoot: config.repoRoot,
1496
+ provider,
1497
+ excludes: config.excludes,
1498
+ includes: config.includes,
1499
+ debug: config.debug ?? false,
1500
+ morphApiKey: config.morphApiKey,
1501
+ morphApiUrl: config.morphApiUrl,
1502
+ retryConfig: config.retryConfig
1503
+ });
1504
+ const finish = result.finish;
1505
+ if (result.terminationReason !== "completed" || !finish?.metadata) {
1506
+ return { success: false, error: "Search did not complete" };
1507
+ }
1508
+ const contexts = (finish.resolved ?? []).map((r) => ({
1509
+ file: r.path,
1510
+ content: r.content
1511
+ }));
1512
+ return { success: true, contexts, summary: finish.payload };
1513
+ }
1514
+ function formatResult(result) {
1515
+ if (!result.success) {
1516
+ return `Search failed: ${result.error}`;
1517
+ }
1518
+ if (!result.contexts || result.contexts.length === 0) {
1519
+ return "No relevant code found. Try rephrasing your query.";
1520
+ }
1521
+ const lines = [];
1522
+ lines.push(`Found ${result.contexts.length} relevant code sections:
1523
+ `);
1524
+ result.contexts.forEach((ctx, i) => {
1525
+ lines.push(`${i + 1}. ${ctx.file}`);
1526
+ lines.push("```");
1527
+ lines.push(ctx.content);
1528
+ lines.push("```");
1529
+ lines.push("");
1530
+ });
1531
+ if (result.summary) {
1532
+ lines.push(`Summary: ${result.summary}`);
1533
+ }
1534
+ return lines.join("\n");
1535
+ }
1536
+
1537
+ // tools/warp_grep/prompts.ts
1538
+ var WARP_GREP_TOOL_NAME = "warpgrep_codebase_search";
1539
+ var WARP_GREP_DESCRIPTION = 'IMPORTANT: If you need to explore the codebase to gather context, and the task does not involve a single file or function which is provided by name, you should ALWAYS use the warpgrep codebase search tool first instead of running search commands. When the task requires exploration beyond a single known file, invoke warpgrep first with a natural-language query describing the target functionality, bug, or architectural concern. Warp Grep is a fast and accurate tool that can search for all relevant context in a codebase. Keep queries action-oriented (e.g., "Find where billing invoices are generated and emailed"), and after reviewing the summarized results, you may verify important findings with other search tools or direct file reads to ensure completeness. Warp Grep can be used for query types like: find function responsible for <small feature>; find code that does <description>; find code path for <big feature>; Where does <minimal error message> come from?; or any query of that type. When a task requires exploration beyond a single known file, ALWAYS default to warpgrep codebase search before other search mechanisms.';
1540
+
1541
+ // tools/warp_grep/gemini.ts
1542
+ var TOOL_PARAMETERS = {
1543
+ type: import_generative_ai.SchemaType.OBJECT,
1544
+ properties: {
1545
+ query: {
1546
+ type: import_generative_ai.SchemaType.STRING,
1547
+ description: "Free-form repository question"
1548
+ }
1549
+ },
1550
+ required: ["query"]
1551
+ };
1552
+ var warpGrepFunctionDeclaration = {
1553
+ name: WARP_GREP_TOOL_NAME,
1554
+ description: WARP_GREP_DESCRIPTION,
1555
+ parameters: TOOL_PARAMETERS
1556
+ };
1557
+ async function execute(input, config) {
1558
+ return executeToolCall(input, config);
1559
+ }
1560
+ function createMorphWarpGrepTool(config) {
1561
+ const declaration = {
1562
+ name: config.name ?? WARP_GREP_TOOL_NAME,
1563
+ description: config.description ?? WARP_GREP_DESCRIPTION,
1564
+ parameters: TOOL_PARAMETERS
1565
+ };
1566
+ return Object.assign(declaration, {
1567
+ execute: async (input) => {
1568
+ return executeToolCall(input, config);
1569
+ },
1570
+ formatResult: (result) => {
1571
+ return formatResult(result);
1572
+ },
1573
+ getSystemPrompt: () => {
1574
+ return getSystemPrompt();
1575
+ }
1576
+ });
1577
+ }
1578
+ var gemini_default = warpGrepFunctionDeclaration;
1579
+ // Annotate the CommonJS export names for ESM import in node:
1580
+ 0 && (module.exports = {
1581
+ createMorphWarpGrepTool,
1582
+ execute,
1583
+ formatResult,
1584
+ getSystemPrompt,
1585
+ warpGrepFunctionDeclaration
1586
+ });
1587
+ //# sourceMappingURL=gemini.cjs.map