@morphllm/morphsdk 0.2.56 → 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.
- package/dist/anthropic-CaFUHxBW.d.ts +89 -0
- package/dist/{chunk-SALJ2K6S.js → chunk-2CASO3ZO.js} +60 -98
- package/dist/chunk-2CASO3ZO.js.map +1 -0
- package/dist/chunk-374N3GIA.js +118 -0
- package/dist/chunk-374N3GIA.js.map +1 -0
- package/dist/chunk-3IQIT6MC.js +65 -0
- package/dist/chunk-3IQIT6MC.js.map +1 -0
- package/dist/chunk-4VGOBA2J.js +57 -0
- package/dist/chunk-4VGOBA2J.js.map +1 -0
- package/dist/chunk-527P5X2E.js +98 -0
- package/dist/chunk-527P5X2E.js.map +1 -0
- package/dist/chunk-6N6ZYZYD.js +74 -0
- package/dist/chunk-6N6ZYZYD.js.map +1 -0
- package/dist/chunk-6Y5JB4JC.js +195 -0
- package/dist/chunk-6Y5JB4JC.js.map +1 -0
- package/dist/{chunk-WSSSSBWU.js → chunk-7EIHYJSG.js} +18 -18
- package/dist/chunk-7EIHYJSG.js.map +1 -0
- package/dist/{chunk-TICMYDII.js → chunk-APP75CBN.js} +33 -16
- package/dist/chunk-APP75CBN.js.map +1 -0
- package/dist/chunk-ILJ3J5IA.js +72 -0
- package/dist/chunk-ILJ3J5IA.js.map +1 -0
- package/dist/chunk-ISWL67SF.js +1 -0
- package/dist/chunk-KW7OEGZK.js +9 -0
- package/dist/chunk-KW7OEGZK.js.map +1 -0
- package/dist/chunk-Q5AHGIQO.js +205 -0
- package/dist/chunk-Q5AHGIQO.js.map +1 -0
- package/dist/{chunk-TJIUA27P.js → chunk-XT5ZO6ES.js} +9 -5
- package/dist/chunk-XT5ZO6ES.js.map +1 -0
- package/dist/{chunk-LVPVVLTI.js → chunk-YV75OQTE.js} +105 -17
- package/dist/chunk-YV75OQTE.js.map +1 -0
- package/dist/{chunk-ZJIIICRA.js → chunk-ZO4PPFCZ.js} +60 -29
- package/dist/chunk-ZO4PPFCZ.js.map +1 -0
- package/dist/{client-CFoR--IU.d.ts → client-CextMMm9.d.ts} +10 -15
- package/dist/client.cjs +698 -466
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +3 -2
- package/dist/client.js +14 -15
- package/dist/finish-kXAcUJyB.d.ts +33 -0
- package/dist/gemini-CE80Pbdy.d.ts +117 -0
- package/dist/index.cjs +711 -466
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +16 -16
- package/dist/openai-Fvpqln7F.d.ts +89 -0
- package/dist/tools/warp_grep/agent/config.cjs +8 -4
- package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/config.d.ts +7 -2
- package/dist/tools/warp_grep/agent/config.js +1 -1
- package/dist/tools/warp_grep/agent/formatter.cjs +32 -15
- package/dist/tools/warp_grep/agent/formatter.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/formatter.d.ts +1 -1
- package/dist/tools/warp_grep/agent/formatter.js +1 -1
- package/dist/tools/warp_grep/agent/parser.cjs +104 -17
- package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/parser.d.ts +3 -5
- package/dist/tools/warp_grep/agent/parser.js +1 -3
- package/dist/tools/warp_grep/agent/prompt.cjs +132 -56
- package/dist/tools/warp_grep/agent/prompt.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/prompt.d.ts +1 -1
- package/dist/tools/warp_grep/agent/prompt.js +1 -1
- package/dist/tools/warp_grep/agent/runner.cjs +466 -313
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.d.ts +1 -0
- package/dist/tools/warp_grep/agent/runner.js +6 -9
- package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/types.d.ts +9 -2
- package/dist/tools/warp_grep/anthropic.cjs +656 -380
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.d.ts +4 -74
- package/dist/tools/warp_grep/anthropic.js +13 -16
- package/dist/tools/warp_grep/client.cjs +1593 -0
- package/dist/tools/warp_grep/client.cjs.map +1 -0
- package/dist/tools/warp_grep/client.d.ts +87 -0
- package/dist/tools/warp_grep/client.js +26 -0
- package/dist/tools/warp_grep/gemini.cjs +1587 -0
- package/dist/tools/warp_grep/gemini.cjs.map +1 -0
- package/dist/tools/warp_grep/gemini.d.ts +7 -0
- package/dist/tools/warp_grep/gemini.js +34 -0
- package/dist/tools/warp_grep/harness.cjs +1195 -0
- package/dist/tools/warp_grep/harness.cjs.map +1 -0
- package/dist/tools/warp_grep/harness.d.ts +107 -0
- package/dist/tools/warp_grep/harness.js +68 -0
- package/dist/tools/warp_grep/harness.js.map +1 -0
- package/dist/tools/warp_grep/index.cjs +818 -466
- package/dist/tools/warp_grep/index.cjs.map +1 -1
- package/dist/tools/warp_grep/index.d.ts +11 -6
- package/dist/tools/warp_grep/index.js +43 -23
- package/dist/tools/warp_grep/openai.cjs +656 -378
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.d.ts +4 -74
- package/dist/tools/warp_grep/openai.js +13 -14
- package/dist/tools/warp_grep/providers/local.cjs +66 -27
- package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/local.d.ts +4 -9
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/providers/remote.cjs +211 -0
- package/dist/tools/warp_grep/providers/remote.cjs.map +1 -0
- package/dist/tools/warp_grep/providers/remote.d.ts +67 -0
- package/dist/tools/warp_grep/providers/remote.js +9 -0
- package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/types.d.ts +7 -15
- package/dist/tools/warp_grep/vercel.cjs +668 -397
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.d.ts +4 -51
- package/dist/tools/warp_grep/vercel.js +16 -15
- package/dist/types-a_hxdPI6.d.ts +144 -0
- package/dist/vercel-3yjvfmVB.d.ts +66 -0
- package/package.json +17 -2
- package/dist/chunk-4ZHDBKBY.js +0 -83
- package/dist/chunk-4ZHDBKBY.js.map +0 -1
- package/dist/chunk-73RQWOQC.js +0 -16
- package/dist/chunk-73RQWOQC.js.map +0 -1
- package/dist/chunk-7OQOOB3R.js +0 -1
- package/dist/chunk-EK7OQPWD.js +0 -44
- package/dist/chunk-EK7OQPWD.js.map +0 -1
- package/dist/chunk-GJURLQ3L.js +0 -77
- package/dist/chunk-GJURLQ3L.js.map +0 -1
- package/dist/chunk-HQO45BAJ.js +0 -14
- package/dist/chunk-HQO45BAJ.js.map +0 -1
- package/dist/chunk-LVPVVLTI.js.map +0 -1
- package/dist/chunk-NDZO5IPV.js +0 -121
- package/dist/chunk-NDZO5IPV.js.map +0 -1
- package/dist/chunk-QVRXBAMM.js +0 -107
- package/dist/chunk-QVRXBAMM.js.map +0 -1
- package/dist/chunk-SALJ2K6S.js.map +0 -1
- package/dist/chunk-TICMYDII.js.map +0 -1
- package/dist/chunk-TJIUA27P.js.map +0 -1
- package/dist/chunk-UIRJE422.js +0 -70
- package/dist/chunk-UIRJE422.js.map +0 -1
- package/dist/chunk-WETRQJGU.js +0 -129
- package/dist/chunk-WETRQJGU.js.map +0 -1
- package/dist/chunk-WSSSSBWU.js.map +0 -1
- package/dist/chunk-ZJIIICRA.js.map +0 -1
- package/dist/core-CpkYEi_T.d.ts +0 -158
- package/dist/tools/warp_grep/agent/grep_helpers.cjs +0 -148
- package/dist/tools/warp_grep/agent/grep_helpers.cjs.map +0 -1
- package/dist/tools/warp_grep/agent/grep_helpers.d.ts +0 -16
- package/dist/tools/warp_grep/agent/grep_helpers.js +0 -14
- package/dist/tools/warp_grep/tools/analyse.cjs +0 -40
- package/dist/tools/warp_grep/tools/analyse.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/analyse.d.ts +0 -10
- package/dist/tools/warp_grep/tools/analyse.js +0 -8
- package/dist/tools/warp_grep/tools/finish.cjs +0 -69
- package/dist/tools/warp_grep/tools/finish.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/finish.d.ts +0 -10
- package/dist/tools/warp_grep/tools/finish.js +0 -10
- package/dist/tools/warp_grep/tools/grep.cjs +0 -38
- package/dist/tools/warp_grep/tools/grep.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/grep.d.ts +0 -8
- package/dist/tools/warp_grep/tools/grep.js +0 -15
- package/dist/tools/warp_grep/tools/grep.js.map +0 -1
- package/dist/tools/warp_grep/tools/read.cjs +0 -38
- package/dist/tools/warp_grep/tools/read.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/read.d.ts +0 -9
- package/dist/tools/warp_grep/tools/read.js +0 -8
- package/dist/tools/warp_grep/tools/read.js.map +0 -1
- package/dist/tools/warp_grep/utils/format.cjs +0 -42
- package/dist/tools/warp_grep/utils/format.cjs.map +0 -1
- package/dist/tools/warp_grep/utils/format.d.ts +0 -4
- package/dist/tools/warp_grep/utils/format.js +0 -18
- package/dist/tools/warp_grep/utils/format.js.map +0 -1
- /package/dist/{chunk-7OQOOB3R.js.map → chunk-ISWL67SF.js.map} +0 -0
- /package/dist/tools/warp_grep/{agent/grep_helpers.js.map → client.js.map} +0 -0
- /package/dist/tools/warp_grep/{tools/analyse.js.map → gemini.js.map} +0 -0
- /package/dist/tools/warp_grep/{tools/finish.js.map → providers/remote.js.map} +0 -0
|
@@ -31,25 +31,37 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var warp_grep_exports = {};
|
|
32
32
|
__export(warp_grep_exports, {
|
|
33
33
|
LocalRipgrepProvider: () => LocalRipgrepProvider,
|
|
34
|
+
RemoteCommandsProvider: () => RemoteCommandsProvider,
|
|
34
35
|
WARP_GREP_DESCRIPTION: () => WARP_GREP_DESCRIPTION,
|
|
35
36
|
WARP_GREP_SYSTEM_PROMPT: () => SYSTEM_PROMPT,
|
|
36
37
|
WARP_GREP_TOOL_NAME: () => WARP_GREP_TOOL_NAME,
|
|
37
38
|
WarpGrepClient: () => WarpGrepClient,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
createVercelWarpGrepTool: () => createMorphWarpGrepTool3,
|
|
39
|
+
anthropic: () => anthropic_exports,
|
|
40
|
+
executeToolCall: () => executeToolCall,
|
|
41
41
|
executeWarpGrep: () => executeWarpGrep,
|
|
42
42
|
formatResult: () => formatResult,
|
|
43
|
+
gemini: () => gemini_exports,
|
|
43
44
|
getSystemPrompt: () => getSystemPrompt,
|
|
44
|
-
|
|
45
|
+
normalizeFinishFiles: () => normalizeFinishFiles,
|
|
46
|
+
openai: () => openai_exports,
|
|
47
|
+
readFinishFiles: () => readFinishFiles,
|
|
48
|
+
runWarpGrep: () => runWarpGrep,
|
|
49
|
+
toolGrep: () => toolGrep,
|
|
50
|
+
toolListDirectory: () => toolListDirectory,
|
|
51
|
+
toolRead: () => toolRead,
|
|
52
|
+
vercel: () => vercel_exports
|
|
45
53
|
});
|
|
46
54
|
module.exports = __toCommonJS(warp_grep_exports);
|
|
47
55
|
|
|
48
56
|
// tools/warp_grep/agent/config.ts
|
|
49
57
|
var AGENT_CONFIG = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
MAX_TURNS: 4,
|
|
59
|
+
TIMEOUT_MS: 3e4,
|
|
60
|
+
MAX_CONTEXT_CHARS: 54e4,
|
|
61
|
+
MAX_OUTPUT_LINES: 200,
|
|
62
|
+
MAX_READ_LINES: 800,
|
|
63
|
+
MAX_LIST_DEPTH: 3,
|
|
64
|
+
LIST_TIMEOUT_MS: 2e3
|
|
53
65
|
};
|
|
54
66
|
var BUILTIN_EXCLUDES = [
|
|
55
67
|
// Version control
|
|
@@ -131,113 +143,191 @@ var BUILTIN_EXCLUDES = [
|
|
|
131
143
|
".*"
|
|
132
144
|
];
|
|
133
145
|
var DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
134
|
-
var DEFAULT_MODEL = "morph-warp-grep";
|
|
146
|
+
var DEFAULT_MODEL = "morph-warp-grep-v1";
|
|
135
147
|
|
|
136
148
|
// tools/warp_grep/agent/prompt.ts
|
|
137
|
-
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given
|
|
149
|
+
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given search_string.
|
|
138
150
|
|
|
139
|
-
|
|
151
|
+
### workflow
|
|
140
152
|
You have exactly 4 turns. The 4th turn MUST be a \`finish\` call. Each turn allows up to 8 parallel tool calls.
|
|
141
153
|
|
|
142
|
-
- Turn 1: Map the territory OR dive deep (based on
|
|
154
|
+
- Turn 1: Map the territory OR dive deep (based on search_string specificity)
|
|
143
155
|
- Turn 2-3: Refine based on findings
|
|
144
156
|
- Turn 4: MUST call \`finish\` with all relevant code locations
|
|
145
157
|
- You MAY call \`finish\` early if confident\u2014but never before at least 1 search turn.
|
|
158
|
+
- The user strongly prefers if you can call the finish tool early, but you must be correct
|
|
146
159
|
|
|
147
|
-
Remember, if the task feels easy to you, it is strongly desirable to call
|
|
148
|
-
</workflow>
|
|
160
|
+
Remember, if the task feels easy to you, it is strongly desirable to call 'finish' early using fewer turns, but quality over speed
|
|
149
161
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
162
|
+
### tools
|
|
163
|
+
Tool calls use nested XML elements:
|
|
164
|
+
\`\`\`xml
|
|
165
|
+
<tool_name>
|
|
166
|
+
<parameter>value</parameter>
|
|
167
|
+
</tool_name>
|
|
168
|
+
\`\`\`
|
|
169
|
+
|
|
170
|
+
### \`list_directory\`
|
|
171
|
+
Directory tree view. Shows structure of a path, optionally filtered by regex pattern.
|
|
172
|
+
|
|
173
|
+
Elements:
|
|
174
|
+
- \`<path>\` (required): Directory path to list (use \`.\` for repo root)
|
|
175
|
+
- \`<pattern>\` (optional): Regex to filter results
|
|
155
176
|
|
|
156
177
|
Examples:
|
|
157
178
|
\`\`\`
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
179
|
+
<list_directory>
|
|
180
|
+
<path>src/services</path>
|
|
181
|
+
</list_directory>
|
|
182
|
+
|
|
183
|
+
<list_directory>
|
|
184
|
+
<path>lib/utils</path>
|
|
185
|
+
<pattern>.*\\.(ts|js)$</pattern>
|
|
186
|
+
</list_directory>
|
|
162
187
|
\`\`\`
|
|
163
188
|
|
|
164
|
-
### \`read
|
|
165
|
-
Read file contents.
|
|
189
|
+
### \`read\`
|
|
190
|
+
Read file contents. Supports multiple line ranges.
|
|
166
191
|
- Returns numbered lines for easy reference
|
|
167
|
-
-
|
|
192
|
+
- ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
193
|
+
|
|
194
|
+
Elements:
|
|
195
|
+
- \`<path>\` (required): File path to read
|
|
196
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80,100-120" (omit to read entire file)
|
|
168
197
|
|
|
169
198
|
Examples:
|
|
170
199
|
\`\`\`
|
|
171
|
-
read
|
|
172
|
-
|
|
173
|
-
read
|
|
200
|
+
<read>
|
|
201
|
+
<path>src/main.py</path>
|
|
202
|
+
</read>
|
|
203
|
+
|
|
204
|
+
<read>
|
|
205
|
+
<path>src/auth.py</path>
|
|
206
|
+
<lines>1-20,45-80,150-200</lines>
|
|
207
|
+
</read>
|
|
174
208
|
\`\`\`
|
|
175
209
|
|
|
176
|
-
### \`grep
|
|
177
|
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
210
|
+
### \`grep\`
|
|
211
|
+
Search for pattern matches across files. Returns matches with 1 line of context above and below.
|
|
212
|
+
- Match lines use \`:\` separator \u2192 \`filepath:linenum:content\`
|
|
213
|
+
- Context lines use \`-\` separator \u2192 \`filepath-linenum-content\`
|
|
214
|
+
|
|
215
|
+
Elements:
|
|
216
|
+
- \`<pattern>\` (required): Search pattern (regex). Use \`(a|b)\` for OR patterns.
|
|
217
|
+
- \`<sub_dir>\` (optional): Subdirectory to search in (defaults to \`.\`)
|
|
218
|
+
- \`<glob>\` (optional): File pattern filter like \`*.py\` or \`*.{ts,tsx}\`
|
|
180
219
|
|
|
181
220
|
Examples:
|
|
182
221
|
\`\`\`
|
|
183
|
-
grep
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
grep
|
|
222
|
+
<grep>
|
|
223
|
+
<pattern>(authenticate|authorize|login)</pattern>
|
|
224
|
+
<sub_dir>src/auth/</sub_dir>
|
|
225
|
+
</grep>
|
|
226
|
+
|
|
227
|
+
<grep>
|
|
228
|
+
<pattern>class.*(Service|Controller)</pattern>
|
|
229
|
+
<glob>*.{ts,js}</glob>
|
|
230
|
+
</grep>
|
|
231
|
+
|
|
232
|
+
<grep>
|
|
233
|
+
<pattern>(DB_HOST|DATABASE_URL|connection)</pattern>
|
|
234
|
+
<glob>*.{py,yaml,env}</glob>
|
|
235
|
+
<sub_dir>lib/</sub_dir>
|
|
236
|
+
</grep>
|
|
187
237
|
\`\`\`
|
|
188
238
|
|
|
189
|
-
### \`finish
|
|
190
|
-
Submit final answer with all relevant code locations.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
|
|
239
|
+
### \`finish\`
|
|
240
|
+
Submit final answer with all relevant code locations. Uses nested \`<file>\` elements.
|
|
241
|
+
|
|
242
|
+
File elements:
|
|
243
|
+
- \`<path>\` (required): File path
|
|
244
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80" (\`*\` for entire file)
|
|
245
|
+
|
|
246
|
+
ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
196
247
|
|
|
197
248
|
Examples:
|
|
198
249
|
\`\`\`
|
|
199
|
-
finish
|
|
200
|
-
|
|
250
|
+
<finish>
|
|
251
|
+
<file>
|
|
252
|
+
<path>src/auth.py</path>
|
|
253
|
+
<lines>1-15,25-50,75-80</lines>
|
|
254
|
+
</file>
|
|
255
|
+
<file>
|
|
256
|
+
<path>src/models/user.py</path>
|
|
257
|
+
<lines>*</lines>
|
|
258
|
+
</file>
|
|
259
|
+
</finish>
|
|
201
260
|
\`\`\`
|
|
202
261
|
</tools>
|
|
203
262
|
|
|
204
263
|
<strategy>
|
|
205
|
-
**Before your first tool call, classify the
|
|
264
|
+
**Before your first tool call, classify the search_string:**
|
|
206
265
|
|
|
207
|
-
|
|
|
208
|
-
|
|
209
|
-
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by
|
|
210
|
-
| **Conceptual** (how does X work, where is Y handled) |
|
|
211
|
-
| **Exploratory** (find all tests, list API endpoints) |
|
|
266
|
+
| Search_string Type | Round 1 Strategy | Early Finish? |
|
|
267
|
+
|------------|------------------|---------------|
|
|
268
|
+
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by round 2 |
|
|
269
|
+
| **Conceptual** (how does X work, where is Y handled) | list_directory + 2-3 broad greps | Rarely early |
|
|
270
|
+
| **Exploratory** (find all tests, list API endpoints) | list_directory at multiple depths | Usually needs 3 rounds |
|
|
212
271
|
|
|
213
272
|
**Parallel call patterns:**
|
|
214
273
|
- **Shotgun grep**: Same pattern, 8 different directories\u2014fast coverage
|
|
215
274
|
- **Variant grep**: 8 pattern variations (synonyms, naming conventions)\u2014catches inconsistent codebases
|
|
216
|
-
- **Funnel**: 1
|
|
275
|
+
- **Funnel**: 1 list_directory + 7 greps\u2014orient and search simultaneously
|
|
217
276
|
- **Deep read**: 8 reads on files you already identified\u2014gather full context fast
|
|
277
|
+
|
|
278
|
+
**Tool call expectations:**
|
|
279
|
+
- 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.
|
|
280
|
+
- 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.
|
|
281
|
+
- 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.
|
|
282
|
+
- 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.
|
|
218
283
|
</strategy>
|
|
219
284
|
|
|
220
285
|
<output_format>
|
|
221
286
|
EVERY response MUST follow this exact format:
|
|
222
287
|
|
|
223
288
|
1. First, wrap your reasoning in \`<think>...</think>\` tags containing:
|
|
224
|
-
-
|
|
225
|
-
- Confidence estimate (can I finish in 1-2
|
|
226
|
-
- This
|
|
289
|
+
- Search_string classification (specific/conceptual/exploratory)
|
|
290
|
+
- Confidence estimate (can I finish in 1-2 rounds?)
|
|
291
|
+
- This round's parallel strategy
|
|
227
292
|
- What signals would let me finish early?
|
|
228
293
|
|
|
229
|
-
2. Then, output
|
|
294
|
+
2. Then, output up to 8 tool calls using nested XML elements.
|
|
230
295
|
|
|
231
296
|
Example:
|
|
232
297
|
\`\`\`
|
|
233
298
|
<think>
|
|
234
|
-
This is a specific
|
|
235
|
-
High confidence I can finish in 2
|
|
299
|
+
This is a specific search_string about authentication. I'll grep for auth-related patterns.
|
|
300
|
+
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
|
|
236
301
|
Strategy: Shotgun grep across likely directories.
|
|
237
302
|
</think>
|
|
238
|
-
<
|
|
239
|
-
<
|
|
240
|
-
<
|
|
303
|
+
<grep>
|
|
304
|
+
<pattern>(authenticate|login|session)</pattern>
|
|
305
|
+
<sub_dir>src/auth/</sub_dir>
|
|
306
|
+
</grep>
|
|
307
|
+
<grep>
|
|
308
|
+
<pattern>(middleware|interceptor)</pattern>
|
|
309
|
+
<glob>*.{ts,js}</glob>
|
|
310
|
+
</grep>
|
|
311
|
+
<list_directory>
|
|
312
|
+
<path>src/auth</path>
|
|
313
|
+
</list_directory>
|
|
314
|
+
\`\`\`
|
|
315
|
+
|
|
316
|
+
Finishing example:
|
|
317
|
+
\`\`\`
|
|
318
|
+
<think>
|
|
319
|
+
I think I have a rough idea, but this is my last turn so I must call the finish tool regardless.
|
|
320
|
+
</think>
|
|
321
|
+
<finish>
|
|
322
|
+
<file>
|
|
323
|
+
<path>src/auth/login.py</path>
|
|
324
|
+
<lines>1-50</lines>
|
|
325
|
+
</file>
|
|
326
|
+
<file>
|
|
327
|
+
<path>src/middleware/session.py</path>
|
|
328
|
+
<lines>10-80</lines>
|
|
329
|
+
</file>
|
|
330
|
+
</finish>
|
|
241
331
|
\`\`\`
|
|
242
332
|
|
|
243
333
|
No commentary outside \`<think>\`. No explanations after tool calls.
|
|
@@ -250,17 +340,111 @@ When calling \`finish\`:
|
|
|
250
340
|
- Include any type definitions, interfaces, or constants used
|
|
251
341
|
- Better to over-include than leave the user missing context
|
|
252
342
|
- If unsure about boundaries, include more rather than less
|
|
253
|
-
</finishing_requirements
|
|
254
|
-
|
|
255
|
-
Begin your exploration now to find code relevant to the query.`;
|
|
343
|
+
</finishing_requirements>`;
|
|
256
344
|
function getSystemPrompt() {
|
|
257
345
|
return SYSTEM_PROMPT;
|
|
258
346
|
}
|
|
259
347
|
|
|
260
348
|
// tools/warp_grep/agent/parser.ts
|
|
261
|
-
var VALID_COMMANDS = ["
|
|
349
|
+
var VALID_COMMANDS = ["list_directory", "grep", "read", "finish"];
|
|
350
|
+
function isValidCommand(name) {
|
|
351
|
+
return VALID_COMMANDS.includes(name);
|
|
352
|
+
}
|
|
353
|
+
function getXmlElementText(xml, tagName) {
|
|
354
|
+
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i");
|
|
355
|
+
const match = xml.match(regex);
|
|
356
|
+
return match ? match[1].trim() : null;
|
|
357
|
+
}
|
|
358
|
+
function parseNestedXmlTools(text) {
|
|
359
|
+
const tools = [];
|
|
360
|
+
const toolRegex = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
|
|
361
|
+
let match;
|
|
362
|
+
while ((match = toolRegex.exec(text)) !== null) {
|
|
363
|
+
const rawToolName = match[1].toLowerCase();
|
|
364
|
+
const content = match[2];
|
|
365
|
+
if (!isValidCommand(rawToolName)) continue;
|
|
366
|
+
const toolName = rawToolName;
|
|
367
|
+
if (toolName === "list_directory") {
|
|
368
|
+
const path5 = getXmlElementText(content, "path");
|
|
369
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
370
|
+
if (path5) {
|
|
371
|
+
tools.push({ name: "list_directory", arguments: { path: path5, pattern } });
|
|
372
|
+
}
|
|
373
|
+
} else if (toolName === "grep") {
|
|
374
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
375
|
+
const subDir = getXmlElementText(content, "sub_dir");
|
|
376
|
+
const glob = getXmlElementText(content, "glob");
|
|
377
|
+
if (pattern) {
|
|
378
|
+
tools.push({
|
|
379
|
+
name: "grep",
|
|
380
|
+
arguments: {
|
|
381
|
+
pattern,
|
|
382
|
+
path: subDir || ".",
|
|
383
|
+
...glob && { glob }
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
} else if (toolName === "read") {
|
|
388
|
+
const path5 = getXmlElementText(content, "path");
|
|
389
|
+
const linesStr = getXmlElementText(content, "lines");
|
|
390
|
+
if (path5) {
|
|
391
|
+
const args = { path: path5 };
|
|
392
|
+
if (linesStr) {
|
|
393
|
+
const ranges = [];
|
|
394
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
395
|
+
const trimmed = rangeStr.trim();
|
|
396
|
+
if (!trimmed) continue;
|
|
397
|
+
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
398
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
399
|
+
ranges.push([s, e]);
|
|
400
|
+
} else if (Number.isFinite(s)) {
|
|
401
|
+
ranges.push([s, s]);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (ranges.length === 1) {
|
|
405
|
+
args.start = ranges[0][0];
|
|
406
|
+
args.end = ranges[0][1];
|
|
407
|
+
} else if (ranges.length > 1) {
|
|
408
|
+
args.lines = ranges;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
tools.push({ name: "read", arguments: args });
|
|
412
|
+
}
|
|
413
|
+
} else if (toolName === "finish") {
|
|
414
|
+
const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
|
|
415
|
+
const files = [];
|
|
416
|
+
let fileMatch;
|
|
417
|
+
while ((fileMatch = fileRegex.exec(content)) !== null) {
|
|
418
|
+
const fileContent = fileMatch[1];
|
|
419
|
+
const filePath = getXmlElementText(fileContent, "path");
|
|
420
|
+
const linesStr = getXmlElementText(fileContent, "lines");
|
|
421
|
+
if (filePath && linesStr) {
|
|
422
|
+
const ranges = [];
|
|
423
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
424
|
+
if (rangeStr.trim() === "*") {
|
|
425
|
+
ranges.push([1, 999999]);
|
|
426
|
+
} else {
|
|
427
|
+
const [s, e] = rangeStr.split("-").map((v) => parseInt(v.trim(), 10));
|
|
428
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
429
|
+
ranges.push([s, e]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (ranges.length > 0) {
|
|
434
|
+
files.push({ path: filePath, lines: ranges });
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (files.length > 0) {
|
|
439
|
+
tools.push({ name: "finish", arguments: { files } });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return tools;
|
|
444
|
+
}
|
|
262
445
|
function preprocessText(text) {
|
|
263
446
|
let processed = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
447
|
+
const nestedTools = parseNestedXmlTools(processed);
|
|
264
448
|
const openingTagRegex = /<tool_call>|<tool>/gi;
|
|
265
449
|
const closingTagRegex = /<\/tool_call>|<\/tool>/gi;
|
|
266
450
|
const openingMatches = processed.match(openingTagRegex) || [];
|
|
@@ -297,7 +481,7 @@ function preprocessText(text) {
|
|
|
297
481
|
}
|
|
298
482
|
}
|
|
299
483
|
}
|
|
300
|
-
return toolCallLines;
|
|
484
|
+
return { lines: toolCallLines, nestedTools };
|
|
301
485
|
}
|
|
302
486
|
var LLMResponseParser = class {
|
|
303
487
|
finishSpecSplitRe = /,(?=[^,\s]+:)/;
|
|
@@ -305,8 +489,8 @@ var LLMResponseParser = class {
|
|
|
305
489
|
if (typeof text !== "string") {
|
|
306
490
|
throw new TypeError("Command text must be a string.");
|
|
307
491
|
}
|
|
308
|
-
const lines = preprocessText(text);
|
|
309
|
-
const commands = [];
|
|
492
|
+
const { lines, nestedTools } = preprocessText(text);
|
|
493
|
+
const commands = [...nestedTools];
|
|
310
494
|
let finishAccumulator = null;
|
|
311
495
|
lines.forEach((line) => {
|
|
312
496
|
if (!line || line.startsWith("#")) return;
|
|
@@ -314,8 +498,8 @@ var LLMResponseParser = class {
|
|
|
314
498
|
if (parts.length === 0) return;
|
|
315
499
|
const cmd = parts[0];
|
|
316
500
|
switch (cmd) {
|
|
317
|
-
case "
|
|
318
|
-
this.
|
|
501
|
+
case "list_directory":
|
|
502
|
+
this.handleListDirectory(parts, line, commands);
|
|
319
503
|
break;
|
|
320
504
|
case "grep":
|
|
321
505
|
this.handleGrep(parts, line, commands);
|
|
@@ -333,8 +517,8 @@ var LLMResponseParser = class {
|
|
|
333
517
|
if (finishAccumulator) {
|
|
334
518
|
const map = finishAccumulator;
|
|
335
519
|
const entries = [...map.entries()];
|
|
336
|
-
const filesPayload = entries.map(([
|
|
337
|
-
path:
|
|
520
|
+
const filesPayload = entries.map(([path5, ranges]) => ({
|
|
521
|
+
path: path5,
|
|
338
522
|
lines: [...ranges].sort((a, b) => a[0] - b[0])
|
|
339
523
|
}));
|
|
340
524
|
commands.push({ name: "finish", arguments: { files: filesPayload } });
|
|
@@ -366,18 +550,17 @@ var LLMResponseParser = class {
|
|
|
366
550
|
skip(message) {
|
|
367
551
|
return { name: "_skip", arguments: { message } };
|
|
368
552
|
}
|
|
369
|
-
|
|
553
|
+
handleListDirectory(parts, rawLine, commands) {
|
|
370
554
|
if (parts.length < 2) {
|
|
371
555
|
commands.push(this.skip(
|
|
372
|
-
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format:
|
|
556
|
+
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: list_directory <path> [pattern]. Example: list_directory src/`
|
|
373
557
|
));
|
|
374
558
|
return;
|
|
375
559
|
}
|
|
376
|
-
const
|
|
560
|
+
const path5 = parts[1];
|
|
377
561
|
const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
|
|
378
|
-
commands.push({ name: "
|
|
562
|
+
commands.push({ name: "list_directory", arguments: { path: path5, pattern } });
|
|
379
563
|
}
|
|
380
|
-
// no glob tool in MCP
|
|
381
564
|
handleGrep(parts, rawLine, commands) {
|
|
382
565
|
if (parts.length < 3) {
|
|
383
566
|
commands.push(this.skip(
|
|
@@ -448,8 +631,30 @@ var LLMResponseParser = class {
|
|
|
448
631
|
}
|
|
449
632
|
};
|
|
450
633
|
|
|
451
|
-
// tools/warp_grep/tools/
|
|
634
|
+
// tools/warp_grep/agent/tools/grep.ts
|
|
635
|
+
async function toolGrep(provider, args) {
|
|
636
|
+
const res = await provider.grep(args);
|
|
637
|
+
if (res.error) {
|
|
638
|
+
return { output: res.error };
|
|
639
|
+
}
|
|
640
|
+
if (!res.lines.length) {
|
|
641
|
+
return { output: "no matches" };
|
|
642
|
+
}
|
|
643
|
+
return { output: res.lines.join("\n") };
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// tools/warp_grep/agent/tools/read.ts
|
|
452
647
|
async function toolRead(provider, args) {
|
|
648
|
+
if (args.lines && args.lines.length > 0) {
|
|
649
|
+
const chunks = [];
|
|
650
|
+
for (const [start, end] of args.lines) {
|
|
651
|
+
const res2 = await provider.read({ path: args.path, start, end });
|
|
652
|
+
if (res2.error) return res2.error;
|
|
653
|
+
chunks.push(res2.lines.join("\n"));
|
|
654
|
+
}
|
|
655
|
+
if (chunks.every((c) => c === "")) return "(empty file)";
|
|
656
|
+
return chunks.join("\n...\n");
|
|
657
|
+
}
|
|
453
658
|
const res = await provider.read({ path: args.path, start: args.start, end: args.end });
|
|
454
659
|
if (res.error) {
|
|
455
660
|
return res.error;
|
|
@@ -458,16 +663,62 @@ async function toolRead(provider, args) {
|
|
|
458
663
|
return res.lines.join("\n");
|
|
459
664
|
}
|
|
460
665
|
|
|
461
|
-
// tools/warp_grep/tools/
|
|
462
|
-
async function
|
|
463
|
-
const list = await provider.
|
|
666
|
+
// tools/warp_grep/agent/tools/list_directory.ts
|
|
667
|
+
async function toolListDirectory(provider, args) {
|
|
668
|
+
const list = await provider.listDirectory({
|
|
464
669
|
path: args.path,
|
|
465
670
|
pattern: args.pattern ?? null,
|
|
466
|
-
maxResults: args.maxResults ??
|
|
467
|
-
maxDepth: args.maxDepth ??
|
|
671
|
+
maxResults: args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
672
|
+
maxDepth: args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH
|
|
468
673
|
});
|
|
469
674
|
if (!list.length) return "empty";
|
|
470
|
-
|
|
675
|
+
if (list.length >= AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
676
|
+
return "query not specific enough, tool called tried to return too much context and failed";
|
|
677
|
+
}
|
|
678
|
+
return list.map((e) => {
|
|
679
|
+
const indent = " ".repeat(e.depth);
|
|
680
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
681
|
+
return `${indent}${name}`;
|
|
682
|
+
}).join("\n");
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// tools/warp_grep/agent/tools/finish.ts
|
|
686
|
+
function normalizeFinishFiles(files) {
|
|
687
|
+
return files.map((f) => {
|
|
688
|
+
const merged = mergeRanges(f.lines);
|
|
689
|
+
return { path: f.path, lines: merged };
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
async function readFinishFiles(repoRoot, files, reader) {
|
|
693
|
+
const out = [];
|
|
694
|
+
for (const f of files) {
|
|
695
|
+
const ranges = mergeRanges(f.lines);
|
|
696
|
+
const chunks = [];
|
|
697
|
+
for (const [s, e] of ranges) {
|
|
698
|
+
const lines = await reader(f.path, s, e);
|
|
699
|
+
chunks.push(lines.join("\n"));
|
|
700
|
+
}
|
|
701
|
+
out.push({ path: f.path, ranges, content: chunks.join("\n") });
|
|
702
|
+
}
|
|
703
|
+
return out;
|
|
704
|
+
}
|
|
705
|
+
function mergeRanges(ranges) {
|
|
706
|
+
if (!ranges.length) return [];
|
|
707
|
+
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
708
|
+
const merged = [];
|
|
709
|
+
let [cs, ce] = sorted[0];
|
|
710
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
711
|
+
const [s, e] = sorted[i];
|
|
712
|
+
if (s <= ce + 1) {
|
|
713
|
+
ce = Math.max(ce, e);
|
|
714
|
+
} else {
|
|
715
|
+
merged.push([cs, ce]);
|
|
716
|
+
cs = s;
|
|
717
|
+
ce = e;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
merged.push([cs, ce]);
|
|
721
|
+
return merged;
|
|
471
722
|
}
|
|
472
723
|
|
|
473
724
|
// tools/utils/resilience.ts
|
|
@@ -560,8 +811,8 @@ var ToolOutputFormatter = class {
|
|
|
560
811
|
switch (name) {
|
|
561
812
|
case "read":
|
|
562
813
|
return this.formatRead(safeArgs, payload, isError);
|
|
563
|
-
case "
|
|
564
|
-
return this.
|
|
814
|
+
case "list_directory":
|
|
815
|
+
return this.formatListDirectory(safeArgs, payload, isError);
|
|
565
816
|
case "grep":
|
|
566
817
|
return this.formatGrep(safeArgs, payload, isError);
|
|
567
818
|
default:
|
|
@@ -574,39 +825,56 @@ ${payload}
|
|
|
574
825
|
if (isError) {
|
|
575
826
|
return payload;
|
|
576
827
|
}
|
|
577
|
-
const
|
|
578
|
-
|
|
828
|
+
const path5 = this.asString(args.path) || "...";
|
|
829
|
+
const start = args.start;
|
|
830
|
+
const end = args.end;
|
|
831
|
+
const linesArray = args.lines;
|
|
832
|
+
const attributes = [`path="${path5}"`];
|
|
833
|
+
if (linesArray && linesArray.length > 0) {
|
|
834
|
+
const rangeStr = linesArray.map(([s, e]) => `${s}-${e}`).join(",");
|
|
835
|
+
attributes.push(`lines="${rangeStr}"`);
|
|
836
|
+
} else if (start !== void 0 && end !== void 0) {
|
|
837
|
+
attributes.push(`lines="${start}-${end}"`);
|
|
838
|
+
}
|
|
839
|
+
return `<read ${attributes.join(" ")}>
|
|
579
840
|
${payload}
|
|
580
|
-
</
|
|
841
|
+
</read>`;
|
|
581
842
|
}
|
|
582
|
-
|
|
583
|
-
const
|
|
843
|
+
formatListDirectory(args, payload, isError) {
|
|
844
|
+
const path5 = this.asString(args.path) || ".";
|
|
845
|
+
const pattern = this.asString(args.pattern);
|
|
846
|
+
const attributes = [`path="${path5}"`];
|
|
847
|
+
if (pattern) {
|
|
848
|
+
attributes.push(`pattern="${pattern}"`);
|
|
849
|
+
}
|
|
584
850
|
if (isError) {
|
|
585
|
-
|
|
586
|
-
${payload}
|
|
587
|
-
</analyse_results>`;
|
|
851
|
+
attributes.push('status="error"');
|
|
588
852
|
}
|
|
589
|
-
return `<
|
|
853
|
+
return `<list_directory ${attributes.join(" ")}>
|
|
590
854
|
${payload}
|
|
591
|
-
</
|
|
855
|
+
</list_directory>`;
|
|
592
856
|
}
|
|
593
857
|
formatGrep(args, payload, isError) {
|
|
594
858
|
const pattern = this.asString(args.pattern);
|
|
595
|
-
const
|
|
859
|
+
const subDir = this.asString(args.path);
|
|
860
|
+
const glob = this.asString(args.glob);
|
|
596
861
|
const attributes = [];
|
|
597
862
|
if (pattern !== void 0) {
|
|
598
863
|
attributes.push(`pattern="${pattern}"`);
|
|
599
864
|
}
|
|
600
|
-
if (
|
|
601
|
-
attributes.push(`
|
|
865
|
+
if (subDir !== void 0) {
|
|
866
|
+
attributes.push(`sub_dir="${subDir}"`);
|
|
867
|
+
}
|
|
868
|
+
if (glob !== void 0) {
|
|
869
|
+
attributes.push(`glob="${glob}"`);
|
|
602
870
|
}
|
|
603
871
|
if (isError) {
|
|
604
872
|
attributes.push('status="error"');
|
|
605
873
|
}
|
|
606
874
|
const attrText = attributes.length ? ` ${attributes.join(" ")}` : "";
|
|
607
|
-
return `<
|
|
875
|
+
return `<grep${attrText}>
|
|
608
876
|
${payload}
|
|
609
|
-
</
|
|
877
|
+
</grep>`;
|
|
610
878
|
}
|
|
611
879
|
asString(value) {
|
|
612
880
|
if (value === null || value === void 0) {
|
|
@@ -620,180 +888,100 @@ function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
|
620
888
|
return sharedFormatter.format(toolName, args, output, options);
|
|
621
889
|
}
|
|
622
890
|
|
|
623
|
-
// tools/warp_grep/agent/
|
|
624
|
-
var
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
this.seenLines.add(this.makeKey(path4, lineNumber));
|
|
632
|
-
}
|
|
633
|
-
makeKey(path4, lineNumber) {
|
|
634
|
-
return `${path4}:${lineNumber}`;
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
var MAX_GREP_OUTPUT_CHARS_PER_TURN = 6e4;
|
|
638
|
-
function extractMatchFields(payload) {
|
|
639
|
-
const text = payload.replace(/\r?\n$/, "");
|
|
640
|
-
if (!text || text.startsWith("[error]")) {
|
|
641
|
-
return null;
|
|
642
|
-
}
|
|
643
|
-
const firstSep = text.indexOf(":");
|
|
644
|
-
if (firstSep === -1) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
let filePath = text.slice(0, firstSep).trim();
|
|
648
|
-
if (!filePath) {
|
|
649
|
-
return null;
|
|
650
|
-
}
|
|
651
|
-
if (filePath.startsWith("./") || filePath.startsWith(".\\")) {
|
|
652
|
-
filePath = filePath.slice(2);
|
|
653
|
-
}
|
|
654
|
-
const remainder = text.slice(firstSep + 1);
|
|
655
|
-
const secondSep = remainder.indexOf(":");
|
|
656
|
-
if (secondSep === -1) {
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
const linePart = remainder.slice(0, secondSep);
|
|
660
|
-
const lineNumber = Number.parseInt(linePart, 10);
|
|
661
|
-
if (!Number.isInteger(lineNumber) || lineNumber <= 0) {
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
let contentSegment = remainder.slice(secondSep + 1);
|
|
665
|
-
const columnSep = contentSegment.indexOf(":");
|
|
666
|
-
if (columnSep !== -1 && /^\d+$/.test(contentSegment.slice(0, columnSep))) {
|
|
667
|
-
contentSegment = contentSegment.slice(columnSep + 1);
|
|
668
|
-
}
|
|
669
|
-
const content = contentSegment.trim();
|
|
670
|
-
if (!content) {
|
|
671
|
-
return null;
|
|
891
|
+
// tools/warp_grep/agent/helpers.ts
|
|
892
|
+
var import_path = __toESM(require("path"), 1);
|
|
893
|
+
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
894
|
+
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
895
|
+
const turnsRemaining = maxTurns - turnsUsed;
|
|
896
|
+
if (turnsRemaining === 1) {
|
|
897
|
+
return `
|
|
898
|
+
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`;
|
|
672
899
|
}
|
|
673
|
-
return
|
|
900
|
+
return `
|
|
901
|
+
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
674
902
|
}
|
|
675
|
-
function
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (!fields) {
|
|
683
|
-
continue;
|
|
684
|
-
}
|
|
685
|
-
if (state.isNew(fields.path, fields.lineNumber)) {
|
|
686
|
-
matches.push(fields);
|
|
687
|
-
state.add(fields.path, fields.lineNumber);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
return matches;
|
|
903
|
+
function calculateContextBudget(messages) {
|
|
904
|
+
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
905
|
+
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
906
|
+
const percent = Math.round(totalChars / maxChars * 100);
|
|
907
|
+
const usedK = Math.round(totalChars / 1e3);
|
|
908
|
+
const maxK = Math.round(maxChars / 1e3);
|
|
909
|
+
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
|
|
691
910
|
}
|
|
692
|
-
function
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
911
|
+
async function buildInitialState(repoRoot, query, provider) {
|
|
912
|
+
try {
|
|
913
|
+
const entries = await provider.listDirectory({
|
|
914
|
+
path: ".",
|
|
915
|
+
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
916
|
+
maxDepth: 2
|
|
917
|
+
});
|
|
918
|
+
const treeLines = entries.map((e) => {
|
|
919
|
+
const indent = " ".repeat(e.depth);
|
|
920
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
921
|
+
return `${indent}${name}`;
|
|
922
|
+
});
|
|
923
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
924
|
+
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
925
|
+
${treeLines.join("\n")}` : `${repoName}/`;
|
|
926
|
+
return `<repo_structure>
|
|
927
|
+
${treeOutput}
|
|
928
|
+
</repo_structure>
|
|
929
|
+
|
|
930
|
+
<search_string>
|
|
931
|
+
${query}
|
|
932
|
+
</search_string>`;
|
|
933
|
+
} catch {
|
|
934
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
935
|
+
return `<repo_structure>
|
|
936
|
+
${repoName}/
|
|
937
|
+
</repo_structure>
|
|
938
|
+
|
|
939
|
+
<search_string>
|
|
940
|
+
${query}
|
|
941
|
+
</search_string>`;
|
|
704
942
|
}
|
|
705
|
-
const core = payload.slice(0, Math.max(0, available - 1));
|
|
706
|
-
const trimmed = core.replace(/\n$/, "").replace(/\s+$/, "");
|
|
707
|
-
const snippet = trimmed ? `${trimmed}\u2026` : "\u2026";
|
|
708
|
-
return `${snippet}
|
|
709
|
-
${note}`;
|
|
710
943
|
}
|
|
711
|
-
function
|
|
712
|
-
|
|
713
|
-
|
|
944
|
+
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
945
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
946
|
+
if (getTotalChars() <= maxChars) {
|
|
947
|
+
return messages;
|
|
714
948
|
}
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
949
|
+
const userIndices = [];
|
|
950
|
+
let firstUserSkipped = false;
|
|
951
|
+
for (let i = 0; i < messages.length; i++) {
|
|
952
|
+
if (messages[i].role === "user") {
|
|
953
|
+
if (!firstUserSkipped) {
|
|
954
|
+
firstUserSkipped = true;
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
userIndices.push(i);
|
|
719
958
|
}
|
|
720
|
-
matchesByFile.get(match.path).push(match);
|
|
721
959
|
}
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
if (index > 0) {
|
|
726
|
-
lines.push("");
|
|
727
|
-
}
|
|
728
|
-
lines.push(filePath);
|
|
729
|
-
const sortedMatches = matchesByFile.get(filePath).slice().sort((a, b) => a.lineNumber - b.lineNumber);
|
|
730
|
-
for (const match of sortedMatches) {
|
|
731
|
-
lines.push(`${match.lineNumber}:${match.content}`);
|
|
960
|
+
for (const idx of userIndices) {
|
|
961
|
+
if (getTotalChars() <= maxChars) {
|
|
962
|
+
break;
|
|
732
963
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// tools/warp_grep/tools/finish.ts
|
|
738
|
-
async function readFinishFiles(repoRoot, files, reader) {
|
|
739
|
-
const out = [];
|
|
740
|
-
for (const f of files) {
|
|
741
|
-
const ranges = mergeRanges(f.lines);
|
|
742
|
-
const chunks = [];
|
|
743
|
-
for (const [s, e] of ranges) {
|
|
744
|
-
const lines = await reader(f.path, s, e);
|
|
745
|
-
chunks.push(lines.join("\n"));
|
|
964
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
965
|
+
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
746
966
|
}
|
|
747
|
-
out.push({ path: f.path, ranges, content: chunks.join("\n") });
|
|
748
967
|
}
|
|
749
|
-
return
|
|
750
|
-
}
|
|
751
|
-
function mergeRanges(ranges) {
|
|
752
|
-
if (!ranges.length) return [];
|
|
753
|
-
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
754
|
-
const merged = [];
|
|
755
|
-
let [cs, ce] = sorted[0];
|
|
756
|
-
for (let i = 1; i < sorted.length; i++) {
|
|
757
|
-
const [s, e] = sorted[i];
|
|
758
|
-
if (s <= ce + 1) {
|
|
759
|
-
ce = Math.max(ce, e);
|
|
760
|
-
} else {
|
|
761
|
-
merged.push([cs, ce]);
|
|
762
|
-
cs = s;
|
|
763
|
-
ce = e;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
merged.push([cs, ce]);
|
|
767
|
-
return merged;
|
|
968
|
+
return messages;
|
|
768
969
|
}
|
|
769
970
|
|
|
770
971
|
// tools/warp_grep/agent/runner.ts
|
|
771
|
-
var
|
|
972
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
772
973
|
var parser = new LLMResponseParser();
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
const files = entries.filter((e) => e.type === "file").map((f) => f.name).slice(0, 50);
|
|
778
|
-
const parts = [
|
|
779
|
-
`<repo_root>${repoRoot}</repo_root>`,
|
|
780
|
-
`<top_dirs>${dirs.join(", ")}</top_dirs>`,
|
|
781
|
-
`<top_files>${files.join(", ")}</top_files>`
|
|
782
|
-
];
|
|
783
|
-
return parts.join("\n");
|
|
784
|
-
} catch {
|
|
785
|
-
return `<repo_root>${repoRoot}</repo_root>`;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
async function callModel(messages, model, apiKey) {
|
|
789
|
-
const api = "https://api.morphllm.com/v1/chat/completions";
|
|
974
|
+
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
975
|
+
async function callModel(messages, model, options = {}) {
|
|
976
|
+
const baseUrl = DEFAULT_API_URL;
|
|
977
|
+
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
790
978
|
const fetchPromise = fetchWithRetry(
|
|
791
|
-
|
|
979
|
+
`${baseUrl}/v1/chat/completions`,
|
|
792
980
|
{
|
|
793
981
|
method: "POST",
|
|
794
982
|
headers: {
|
|
795
983
|
"Content-Type": "application/json",
|
|
796
|
-
Authorization: `Bearer ${apiKey
|
|
984
|
+
Authorization: `Bearer ${apiKey}`
|
|
797
985
|
},
|
|
798
986
|
body: JSON.stringify({
|
|
799
987
|
model,
|
|
@@ -802,10 +990,15 @@ async function callModel(messages, model, apiKey) {
|
|
|
802
990
|
messages
|
|
803
991
|
})
|
|
804
992
|
},
|
|
805
|
-
|
|
993
|
+
options.retryConfig
|
|
806
994
|
);
|
|
807
995
|
const resp = await withTimeout(fetchPromise, AGENT_CONFIG.TIMEOUT_MS, "morph-warp-grep request timed out");
|
|
808
996
|
if (!resp.ok) {
|
|
997
|
+
if (resp.status === 404) {
|
|
998
|
+
throw new Error(
|
|
999
|
+
"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"
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
809
1002
|
const t = await resp.text();
|
|
810
1003
|
throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
|
|
811
1004
|
}
|
|
@@ -817,23 +1010,24 @@ async function callModel(messages, model, apiKey) {
|
|
|
817
1010
|
return content;
|
|
818
1011
|
}
|
|
819
1012
|
async function runWarpGrep(config) {
|
|
820
|
-
const repoRoot =
|
|
1013
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
821
1014
|
const messages = [];
|
|
822
|
-
|
|
823
|
-
messages.push(systemMessage);
|
|
824
|
-
const queryContent = `<query>${config.query}</query>`;
|
|
825
|
-
messages.push({ role: "user", content: queryContent });
|
|
1015
|
+
messages.push({ role: "system", content: getSystemPrompt() });
|
|
826
1016
|
const initialState = await buildInitialState(repoRoot, config.query, config.provider);
|
|
827
1017
|
messages.push({ role: "user", content: initialState });
|
|
828
|
-
const
|
|
1018
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
829
1019
|
const model = config.model || DEFAULT_MODEL;
|
|
830
1020
|
const provider = config.provider;
|
|
831
1021
|
const errors = [];
|
|
832
|
-
const grepState = new GrepState();
|
|
833
1022
|
let finishMeta;
|
|
834
1023
|
let terminationReason = "terminated";
|
|
835
|
-
for (let
|
|
836
|
-
|
|
1024
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1025
|
+
enforceContextLimit(messages);
|
|
1026
|
+
const assistantContent = await callModel(messages, model, {
|
|
1027
|
+
morphApiKey: config.morphApiKey,
|
|
1028
|
+
morphApiUrl: config.morphApiUrl,
|
|
1029
|
+
retryConfig: config.retryConfig
|
|
1030
|
+
}).catch((e) => {
|
|
837
1031
|
errors.push({ message: e instanceof Error ? e.message : String(e) });
|
|
838
1032
|
return "";
|
|
839
1033
|
});
|
|
@@ -841,13 +1035,13 @@ async function runWarpGrep(config) {
|
|
|
841
1035
|
messages.push({ role: "assistant", content: assistantContent });
|
|
842
1036
|
const toolCalls = parser.parse(assistantContent);
|
|
843
1037
|
if (toolCalls.length === 0) {
|
|
844
|
-
errors.push({ message: "No tool calls produced by the model." });
|
|
1038
|
+
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" });
|
|
845
1039
|
terminationReason = "terminated";
|
|
846
1040
|
break;
|
|
847
1041
|
}
|
|
848
1042
|
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
849
1043
|
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
850
|
-
const
|
|
1044
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
851
1045
|
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
852
1046
|
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
853
1047
|
const formatted = [];
|
|
@@ -855,69 +1049,42 @@ async function runWarpGrep(config) {
|
|
|
855
1049
|
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
856
1050
|
formatted.push(msg);
|
|
857
1051
|
}
|
|
858
|
-
const
|
|
859
|
-
for (const c of
|
|
1052
|
+
const allPromises = [];
|
|
1053
|
+
for (const c of grepCalls) {
|
|
1054
|
+
const args = c.arguments ?? {};
|
|
1055
|
+
allPromises.push(
|
|
1056
|
+
toolGrep(provider, args).then(
|
|
1057
|
+
({ output }) => formatAgentToolOutput("grep", args, output, { isError: false }),
|
|
1058
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1059
|
+
)
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
for (const c of listDirCalls) {
|
|
860
1063
|
const args = c.arguments ?? {};
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
(p) => formatAgentToolOutput("
|
|
864
|
-
(err) => formatAgentToolOutput("
|
|
1064
|
+
allPromises.push(
|
|
1065
|
+
toolListDirectory(provider, args).then(
|
|
1066
|
+
(p) => formatAgentToolOutput("list_directory", args, p, { isError: false }),
|
|
1067
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
865
1068
|
)
|
|
866
1069
|
);
|
|
867
1070
|
}
|
|
868
1071
|
for (const c of readCalls) {
|
|
869
1072
|
const args = c.arguments ?? {};
|
|
870
|
-
|
|
1073
|
+
allPromises.push(
|
|
871
1074
|
toolRead(provider, args).then(
|
|
872
1075
|
(p) => formatAgentToolOutput("read", args, p, { isError: false }),
|
|
873
1076
|
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
874
1077
|
)
|
|
875
1078
|
);
|
|
876
1079
|
}
|
|
877
|
-
const
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
const args = c.arguments ?? {};
|
|
881
|
-
try {
|
|
882
|
-
const grepRes = await provider.grep({ pattern: args.pattern, path: args.path });
|
|
883
|
-
if (grepRes.error) {
|
|
884
|
-
errors.push({ message: grepRes.error });
|
|
885
|
-
terminationReason = "terminated";
|
|
886
|
-
return {
|
|
887
|
-
terminationReason: "terminated",
|
|
888
|
-
messages,
|
|
889
|
-
errors
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
const rawOutput = Array.isArray(grepRes.lines) ? grepRes.lines.join("\n") : "";
|
|
893
|
-
const newMatches = parseAndFilterGrepOutput(rawOutput, grepState);
|
|
894
|
-
let formattedPayload = formatTurnGrepOutput(newMatches);
|
|
895
|
-
if (formattedPayload === "No new matches found.") {
|
|
896
|
-
formattedPayload = "no new matches";
|
|
897
|
-
}
|
|
898
|
-
formatted.push(formatAgentToolOutput("grep", args, formattedPayload, { isError: false }));
|
|
899
|
-
} catch (err) {
|
|
900
|
-
formatted.push(formatAgentToolOutput("grep", args, String(err), { isError: true }));
|
|
901
|
-
}
|
|
1080
|
+
const allResults = await Promise.all(allPromises);
|
|
1081
|
+
for (const result of allResults) {
|
|
1082
|
+
formatted.push(result);
|
|
902
1083
|
}
|
|
903
1084
|
if (formatted.length > 0) {
|
|
904
|
-
const
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
if (turnsRemaining === 0) {
|
|
908
|
-
turnMessage = `
|
|
909
|
-
|
|
910
|
-
[Turn ${turnsUsed}/4] This is your LAST turn. You MUST call the finish tool now.`;
|
|
911
|
-
} else if (turnsRemaining === 1) {
|
|
912
|
-
turnMessage = `
|
|
913
|
-
|
|
914
|
-
[Turn ${turnsUsed}/4] You have 1 turn remaining. Next turn you MUST call the finish tool.`;
|
|
915
|
-
} else {
|
|
916
|
-
turnMessage = `
|
|
917
|
-
|
|
918
|
-
[Turn ${turnsUsed}/4] You have ${turnsRemaining} turns remaining.`;
|
|
919
|
-
}
|
|
920
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage });
|
|
1085
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1086
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1087
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
921
1088
|
}
|
|
922
1089
|
if (finishCalls.length) {
|
|
923
1090
|
const fc = finishCalls[0];
|
|
@@ -967,7 +1134,7 @@ async function runWarpGrep(config) {
|
|
|
967
1134
|
|
|
968
1135
|
// tools/warp_grep/providers/local.ts
|
|
969
1136
|
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
970
|
-
var
|
|
1137
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
971
1138
|
|
|
972
1139
|
// tools/warp_grep/utils/ripgrep.ts
|
|
973
1140
|
var import_child_process = require("child_process");
|
|
@@ -1032,21 +1199,21 @@ async function runRipgrep(args, opts) {
|
|
|
1032
1199
|
|
|
1033
1200
|
// tools/warp_grep/utils/paths.ts
|
|
1034
1201
|
var import_fs = __toESM(require("fs"), 1);
|
|
1035
|
-
var
|
|
1202
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
1036
1203
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
1037
|
-
const absRoot =
|
|
1038
|
-
const resolved =
|
|
1204
|
+
const absRoot = import_path3.default.resolve(repoRoot);
|
|
1205
|
+
const resolved = import_path3.default.resolve(absRoot, targetPath);
|
|
1039
1206
|
ensureWithinRepo(absRoot, resolved);
|
|
1040
1207
|
return resolved;
|
|
1041
1208
|
}
|
|
1042
1209
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
1043
|
-
const rel =
|
|
1044
|
-
if (rel.startsWith("..") ||
|
|
1210
|
+
const rel = import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absTarget));
|
|
1211
|
+
if (rel.startsWith("..") || import_path3.default.isAbsolute(rel)) {
|
|
1045
1212
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
1046
1213
|
}
|
|
1047
1214
|
}
|
|
1048
1215
|
function toRepoRelative(repoRoot, absPath) {
|
|
1049
|
-
return
|
|
1216
|
+
return import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absPath));
|
|
1050
1217
|
}
|
|
1051
1218
|
function isSymlink(p) {
|
|
1052
1219
|
try {
|
|
@@ -1089,10 +1256,18 @@ var LocalRipgrepProvider = class {
|
|
|
1089
1256
|
this.excludes = excludes;
|
|
1090
1257
|
}
|
|
1091
1258
|
async grep(params) {
|
|
1092
|
-
|
|
1259
|
+
let abs;
|
|
1260
|
+
try {
|
|
1261
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
1262
|
+
} catch (err) {
|
|
1263
|
+
return {
|
|
1264
|
+
lines: [],
|
|
1265
|
+
error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1093
1268
|
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
1094
1269
|
if (!stat) return { lines: [] };
|
|
1095
|
-
const targetArg = abs ===
|
|
1270
|
+
const targetArg = abs === import_path4.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
1096
1271
|
const args = [
|
|
1097
1272
|
"--no-config",
|
|
1098
1273
|
"--no-heading",
|
|
@@ -1101,6 +1276,9 @@ var LocalRipgrepProvider = class {
|
|
|
1101
1276
|
"--color=never",
|
|
1102
1277
|
"--trim",
|
|
1103
1278
|
"--max-columns=400",
|
|
1279
|
+
"-C",
|
|
1280
|
+
"1",
|
|
1281
|
+
...params.glob ? ["--glob", params.glob] : [],
|
|
1104
1282
|
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1105
1283
|
params.pattern,
|
|
1106
1284
|
targetArg || "."
|
|
@@ -1125,29 +1303,24 @@ Details: ${res.stderr}` : ""}`
|
|
|
1125
1303
|
};
|
|
1126
1304
|
}
|
|
1127
1305
|
const lines = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
const args = [
|
|
1134
|
-
"--no-config",
|
|
1135
|
-
"--files",
|
|
1136
|
-
"-g",
|
|
1137
|
-
params.pattern,
|
|
1138
|
-
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1139
|
-
targetArg || "."
|
|
1140
|
-
];
|
|
1141
|
-
const res = await runRipgrep(args, { cwd: this.repoRoot });
|
|
1142
|
-
if (res.exitCode === -1) {
|
|
1143
|
-
console.warn(`[warp_grep] ripgrep not available for glob: ${res.stderr || "execution failed"}`);
|
|
1144
|
-
return { files: [] };
|
|
1306
|
+
if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
1307
|
+
return {
|
|
1308
|
+
lines: [],
|
|
1309
|
+
error: "query not specific enough, tool tried to return too much context and failed"
|
|
1310
|
+
};
|
|
1145
1311
|
}
|
|
1146
|
-
|
|
1147
|
-
return { files };
|
|
1312
|
+
return { lines };
|
|
1148
1313
|
}
|
|
1149
1314
|
async read(params) {
|
|
1150
|
-
|
|
1315
|
+
let abs;
|
|
1316
|
+
try {
|
|
1317
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
1318
|
+
} catch (err) {
|
|
1319
|
+
return {
|
|
1320
|
+
lines: [],
|
|
1321
|
+
error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1151
1324
|
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
1152
1325
|
if (!stat || !stat.isFile()) {
|
|
1153
1326
|
return {
|
|
@@ -1167,7 +1340,15 @@ Details: ${res.stderr}` : ""}`
|
|
|
1167
1340
|
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.`
|
|
1168
1341
|
};
|
|
1169
1342
|
}
|
|
1170
|
-
|
|
1343
|
+
let lines;
|
|
1344
|
+
try {
|
|
1345
|
+
lines = await readAllLines(abs);
|
|
1346
|
+
} catch (err) {
|
|
1347
|
+
return {
|
|
1348
|
+
lines: [],
|
|
1349
|
+
error: `[READ ERROR] Failed to read "${params.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1171
1352
|
const total = lines.length;
|
|
1172
1353
|
let s = params.start ?? 1;
|
|
1173
1354
|
let e = Math.min(params.end ?? total, total);
|
|
@@ -1180,30 +1361,46 @@ Details: ${res.stderr}` : ""}`
|
|
|
1180
1361
|
const content = lines[i - 1] ?? "";
|
|
1181
1362
|
out.push(`${i}|${content}`);
|
|
1182
1363
|
}
|
|
1364
|
+
if (out.length > AGENT_CONFIG.MAX_READ_LINES) {
|
|
1365
|
+
const truncated = out.slice(0, AGENT_CONFIG.MAX_READ_LINES);
|
|
1366
|
+
truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${out.length} lines]`);
|
|
1367
|
+
return { lines: truncated };
|
|
1368
|
+
}
|
|
1183
1369
|
return { lines: out };
|
|
1184
1370
|
}
|
|
1185
|
-
async
|
|
1186
|
-
|
|
1371
|
+
async listDirectory(params) {
|
|
1372
|
+
let abs;
|
|
1373
|
+
try {
|
|
1374
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
1375
|
+
} catch {
|
|
1376
|
+
return [];
|
|
1377
|
+
}
|
|
1187
1378
|
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
1188
1379
|
if (!stat || !stat.isDirectory()) {
|
|
1189
1380
|
return [];
|
|
1190
1381
|
}
|
|
1191
|
-
const maxResults = params.maxResults ??
|
|
1192
|
-
const maxDepth = params.maxDepth ??
|
|
1382
|
+
const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
1383
|
+
const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
1193
1384
|
const regex = params.pattern ? new RegExp(params.pattern) : null;
|
|
1194
1385
|
const results = [];
|
|
1386
|
+
let timedOut = false;
|
|
1387
|
+
const startTime = Date.now();
|
|
1195
1388
|
async function walk(dir, depth) {
|
|
1389
|
+
if (Date.now() - startTime > AGENT_CONFIG.LIST_TIMEOUT_MS) {
|
|
1390
|
+
timedOut = true;
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1196
1393
|
if (depth > maxDepth || results.length >= maxResults) return;
|
|
1197
1394
|
const entries = await import_promises2.default.readdir(dir, { withFileTypes: true });
|
|
1198
1395
|
for (const entry of entries) {
|
|
1199
|
-
|
|
1396
|
+
if (timedOut || results.length >= maxResults) break;
|
|
1397
|
+
const full = import_path4.default.join(dir, entry.name);
|
|
1200
1398
|
const rel = toRepoRelative(abs, full).replace(/^[.][/\\]?/, "");
|
|
1201
|
-
if (DEFAULT_EXCLUDES.some((ex) => rel.split(
|
|
1399
|
+
if (DEFAULT_EXCLUDES.some((ex) => rel.split(import_path4.default.sep).includes(ex))) continue;
|
|
1202
1400
|
if (regex && !regex.test(entry.name)) continue;
|
|
1203
|
-
if (results.length >= maxResults) break;
|
|
1204
1401
|
results.push({
|
|
1205
1402
|
name: entry.name,
|
|
1206
|
-
path: toRepoRelative(
|
|
1403
|
+
path: toRepoRelative(import_path4.default.resolve(""), full),
|
|
1207
1404
|
// relative display
|
|
1208
1405
|
type: entry.isDirectory() ? "dir" : "file",
|
|
1209
1406
|
depth
|
|
@@ -1218,12 +1415,103 @@ Details: ${res.stderr}` : ""}`
|
|
|
1218
1415
|
}
|
|
1219
1416
|
};
|
|
1220
1417
|
|
|
1221
|
-
// tools/warp_grep/
|
|
1418
|
+
// tools/warp_grep/providers/remote.ts
|
|
1419
|
+
var RemoteCommandsProvider = class {
|
|
1420
|
+
constructor(repoRoot, commands) {
|
|
1421
|
+
this.repoRoot = repoRoot;
|
|
1422
|
+
this.commands = commands;
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Run grep command and parse ripgrep output
|
|
1426
|
+
*/
|
|
1427
|
+
async grep(params) {
|
|
1428
|
+
try {
|
|
1429
|
+
const stdout = await this.commands.grep(params.pattern, params.path, params.glob);
|
|
1430
|
+
const lines = (stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
1431
|
+
if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
1432
|
+
return {
|
|
1433
|
+
lines: [],
|
|
1434
|
+
error: "Query not specific enough - too many results returned. Try a more specific pattern."
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
return { lines };
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
return {
|
|
1440
|
+
lines: [],
|
|
1441
|
+
error: `[GREP ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Read file and add line numbers
|
|
1447
|
+
*/
|
|
1448
|
+
async read(params) {
|
|
1449
|
+
const start = params.start ?? 1;
|
|
1450
|
+
const end = params.end ?? 1e6;
|
|
1451
|
+
try {
|
|
1452
|
+
const stdout = await this.commands.read(params.path, start, end);
|
|
1453
|
+
const contentLines = (stdout || "").split("\n");
|
|
1454
|
+
if (contentLines.length > 0 && contentLines[contentLines.length - 1] === "") {
|
|
1455
|
+
contentLines.pop();
|
|
1456
|
+
}
|
|
1457
|
+
const lines = contentLines.map((content, idx) => `${start + idx}|${content}`);
|
|
1458
|
+
if (lines.length > AGENT_CONFIG.MAX_READ_LINES) {
|
|
1459
|
+
const truncated = lines.slice(0, AGENT_CONFIG.MAX_READ_LINES);
|
|
1460
|
+
truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${lines.length} lines]`);
|
|
1461
|
+
return { lines: truncated };
|
|
1462
|
+
}
|
|
1463
|
+
return { lines };
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
return {
|
|
1466
|
+
lines: [],
|
|
1467
|
+
error: `[READ ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* List directory and parse find output
|
|
1473
|
+
*/
|
|
1474
|
+
async listDirectory(params) {
|
|
1475
|
+
const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
1476
|
+
const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
1477
|
+
try {
|
|
1478
|
+
const stdout = await this.commands.listDir(params.path, maxDepth);
|
|
1479
|
+
const paths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
|
|
1480
|
+
const regex = params.pattern ? new RegExp(params.pattern) : null;
|
|
1481
|
+
const entries = [];
|
|
1482
|
+
for (const fullPath of paths) {
|
|
1483
|
+
if (fullPath === params.path || fullPath === this.repoRoot) continue;
|
|
1484
|
+
const name = fullPath.split("/").pop() || "";
|
|
1485
|
+
if (regex && !regex.test(name)) continue;
|
|
1486
|
+
let relativePath = fullPath;
|
|
1487
|
+
if (fullPath.startsWith(this.repoRoot)) {
|
|
1488
|
+
relativePath = fullPath.slice(this.repoRoot.length).replace(/^\//, "");
|
|
1489
|
+
}
|
|
1490
|
+
const depth = relativePath.split("/").filter(Boolean).length - 1;
|
|
1491
|
+
const hasExtension = name.includes(".") && !name.startsWith(".");
|
|
1492
|
+
const type = hasExtension ? "file" : "dir";
|
|
1493
|
+
entries.push({
|
|
1494
|
+
name,
|
|
1495
|
+
path: relativePath,
|
|
1496
|
+
type,
|
|
1497
|
+
depth: Math.max(0, depth)
|
|
1498
|
+
});
|
|
1499
|
+
if (entries.length >= maxResults) break;
|
|
1500
|
+
}
|
|
1501
|
+
return entries;
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
return [];
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
|
|
1508
|
+
// tools/warp_grep/client.ts
|
|
1222
1509
|
var WarpGrepClient = class {
|
|
1223
1510
|
config;
|
|
1224
1511
|
constructor(config = {}) {
|
|
1225
1512
|
this.config = {
|
|
1226
|
-
|
|
1513
|
+
morphApiKey: config.morphApiKey,
|
|
1514
|
+
morphApiUrl: config.morphApiUrl,
|
|
1227
1515
|
debug: config.debug,
|
|
1228
1516
|
timeout: config.timeout,
|
|
1229
1517
|
retryConfig: config.retryConfig
|
|
@@ -1251,38 +1539,50 @@ var WarpGrepClient = class {
|
|
|
1251
1539
|
* ```
|
|
1252
1540
|
*/
|
|
1253
1541
|
async execute(input) {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
error: "Search did not complete"
|
|
1269
|
-
};
|
|
1270
|
-
}
|
|
1271
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
1272
|
-
file: r.path,
|
|
1273
|
-
content: r.content
|
|
1274
|
-
}));
|
|
1275
|
-
return {
|
|
1276
|
-
success: true,
|
|
1277
|
-
contexts,
|
|
1278
|
-
summary: finish.payload
|
|
1279
|
-
};
|
|
1542
|
+
return executeToolCall(
|
|
1543
|
+
{ query: input.query },
|
|
1544
|
+
{
|
|
1545
|
+
repoRoot: input.repoRoot,
|
|
1546
|
+
remoteCommands: input.remoteCommands,
|
|
1547
|
+
provider: input.provider,
|
|
1548
|
+
excludes: input.excludes,
|
|
1549
|
+
includes: input.includes,
|
|
1550
|
+
debug: input.debug ?? this.config.debug,
|
|
1551
|
+
morphApiKey: this.config.morphApiKey,
|
|
1552
|
+
morphApiUrl: this.config.morphApiUrl,
|
|
1553
|
+
retryConfig: this.config.retryConfig
|
|
1554
|
+
}
|
|
1555
|
+
);
|
|
1280
1556
|
}
|
|
1281
1557
|
};
|
|
1282
1558
|
async function executeWarpGrep(input, config) {
|
|
1283
1559
|
const client = new WarpGrepClient(config);
|
|
1284
1560
|
return client.execute(input);
|
|
1285
1561
|
}
|
|
1562
|
+
async function executeToolCall(input, config) {
|
|
1563
|
+
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
1564
|
+
const provider = config.remoteCommands ? new RemoteCommandsProvider(config.repoRoot, config.remoteCommands) : config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
1565
|
+
const result = await runWarpGrep({
|
|
1566
|
+
query: parsed.query,
|
|
1567
|
+
repoRoot: config.repoRoot,
|
|
1568
|
+
provider,
|
|
1569
|
+
excludes: config.excludes,
|
|
1570
|
+
includes: config.includes,
|
|
1571
|
+
debug: config.debug ?? false,
|
|
1572
|
+
morphApiKey: config.morphApiKey,
|
|
1573
|
+
morphApiUrl: config.morphApiUrl,
|
|
1574
|
+
retryConfig: config.retryConfig
|
|
1575
|
+
});
|
|
1576
|
+
const finish = result.finish;
|
|
1577
|
+
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
1578
|
+
return { success: false, error: "Search did not complete" };
|
|
1579
|
+
}
|
|
1580
|
+
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
1581
|
+
file: r.path,
|
|
1582
|
+
content: r.content
|
|
1583
|
+
}));
|
|
1584
|
+
return { success: true, contexts, summary: finish.payload };
|
|
1585
|
+
}
|
|
1286
1586
|
function formatResult(result) {
|
|
1287
1587
|
if (!result.success) {
|
|
1288
1588
|
return `Search failed: ${result.error}`;
|
|
@@ -1310,48 +1610,39 @@ function formatResult(result) {
|
|
|
1310
1610
|
var WARP_GREP_TOOL_NAME = "warpgrep_codebase_search";
|
|
1311
1611
|
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.';
|
|
1312
1612
|
|
|
1313
|
-
// tools/warp_grep/
|
|
1314
|
-
var
|
|
1613
|
+
// tools/warp_grep/anthropic.ts
|
|
1614
|
+
var anthropic_exports = {};
|
|
1615
|
+
__export(anthropic_exports, {
|
|
1616
|
+
createWarpGrepTool: () => createWarpGrepTool,
|
|
1617
|
+
execute: () => execute,
|
|
1618
|
+
formatResult: () => formatResult,
|
|
1619
|
+
getSystemPrompt: () => getSystemPrompt,
|
|
1620
|
+
warpGrepTool: () => warpGrepTool
|
|
1621
|
+
});
|
|
1622
|
+
var INPUT_SCHEMA = {
|
|
1315
1623
|
type: "object",
|
|
1316
1624
|
properties: {
|
|
1317
1625
|
query: { type: "string", description: "Free-form repository question" }
|
|
1318
1626
|
},
|
|
1319
1627
|
required: ["query"]
|
|
1320
1628
|
};
|
|
1629
|
+
var warpGrepTool = {
|
|
1630
|
+
name: WARP_GREP_TOOL_NAME,
|
|
1631
|
+
description: WARP_GREP_DESCRIPTION,
|
|
1632
|
+
input_schema: INPUT_SCHEMA
|
|
1633
|
+
};
|
|
1321
1634
|
async function execute(input, config) {
|
|
1322
|
-
|
|
1323
|
-
const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
1324
|
-
const result = await runWarpGrep({
|
|
1325
|
-
query: parsed.query,
|
|
1326
|
-
repoRoot: config.repoRoot,
|
|
1327
|
-
provider,
|
|
1328
|
-
excludes: config.excludes,
|
|
1329
|
-
includes: config.includes,
|
|
1330
|
-
debug: config.debug ?? false,
|
|
1331
|
-
apiKey: config.apiKey
|
|
1332
|
-
});
|
|
1333
|
-
const finish = result.finish;
|
|
1334
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
1335
|
-
return { success: false, error: "Search did not complete" };
|
|
1336
|
-
}
|
|
1337
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
1338
|
-
file: r.path,
|
|
1339
|
-
content: r.content
|
|
1340
|
-
}));
|
|
1341
|
-
return { success: true, contexts, summary: finish.payload };
|
|
1635
|
+
return executeToolCall(input, config);
|
|
1342
1636
|
}
|
|
1343
|
-
function
|
|
1637
|
+
function createWarpGrepTool(config) {
|
|
1344
1638
|
const tool2 = {
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
1349
|
-
parameters: TOOL_PARAMETERS
|
|
1350
|
-
}
|
|
1639
|
+
name: config.name ?? WARP_GREP_TOOL_NAME,
|
|
1640
|
+
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
1641
|
+
input_schema: INPUT_SCHEMA
|
|
1351
1642
|
};
|
|
1352
1643
|
return Object.assign(tool2, {
|
|
1353
1644
|
execute: async (input) => {
|
|
1354
|
-
return
|
|
1645
|
+
return executeToolCall(input, config);
|
|
1355
1646
|
},
|
|
1356
1647
|
formatResult: (result) => {
|
|
1357
1648
|
return formatResult(result);
|
|
@@ -1362,45 +1653,46 @@ function createMorphWarpGrepTool(config) {
|
|
|
1362
1653
|
});
|
|
1363
1654
|
}
|
|
1364
1655
|
|
|
1365
|
-
// tools/warp_grep/
|
|
1366
|
-
var
|
|
1656
|
+
// tools/warp_grep/openai.ts
|
|
1657
|
+
var openai_exports = {};
|
|
1658
|
+
__export(openai_exports, {
|
|
1659
|
+
createWarpGrepTool: () => createWarpGrepTool2,
|
|
1660
|
+
default: () => openai_default,
|
|
1661
|
+
execute: () => execute2,
|
|
1662
|
+
formatResult: () => formatResult,
|
|
1663
|
+
getSystemPrompt: () => getSystemPrompt,
|
|
1664
|
+
warpGrepTool: () => warpGrepTool2
|
|
1665
|
+
});
|
|
1666
|
+
var TOOL_PARAMETERS = {
|
|
1367
1667
|
type: "object",
|
|
1368
1668
|
properties: {
|
|
1369
1669
|
query: { type: "string", description: "Free-form repository question" }
|
|
1370
1670
|
},
|
|
1371
1671
|
required: ["query"]
|
|
1372
1672
|
};
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
provider,
|
|
1380
|
-
excludes: config.excludes,
|
|
1381
|
-
includes: config.includes,
|
|
1382
|
-
debug: config.debug ?? false,
|
|
1383
|
-
apiKey: config.apiKey
|
|
1384
|
-
});
|
|
1385
|
-
const finish = result.finish;
|
|
1386
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
1387
|
-
return { success: false, error: "Search did not complete" };
|
|
1673
|
+
var warpGrepTool2 = {
|
|
1674
|
+
type: "function",
|
|
1675
|
+
function: {
|
|
1676
|
+
name: WARP_GREP_TOOL_NAME,
|
|
1677
|
+
description: WARP_GREP_DESCRIPTION,
|
|
1678
|
+
parameters: TOOL_PARAMETERS
|
|
1388
1679
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
}));
|
|
1393
|
-
return { success: true, contexts, summary: finish.payload };
|
|
1680
|
+
};
|
|
1681
|
+
async function execute2(input, config) {
|
|
1682
|
+
return executeToolCall(input, config);
|
|
1394
1683
|
}
|
|
1395
|
-
function
|
|
1684
|
+
function createWarpGrepTool2(config) {
|
|
1396
1685
|
const tool2 = {
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1686
|
+
type: "function",
|
|
1687
|
+
function: {
|
|
1688
|
+
name: config.name ?? WARP_GREP_TOOL_NAME,
|
|
1689
|
+
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
1690
|
+
parameters: TOOL_PARAMETERS
|
|
1691
|
+
}
|
|
1400
1692
|
};
|
|
1401
1693
|
return Object.assign(tool2, {
|
|
1402
1694
|
execute: async (input) => {
|
|
1403
|
-
return
|
|
1695
|
+
return executeToolCall(input, config);
|
|
1404
1696
|
},
|
|
1405
1697
|
formatResult: (result) => {
|
|
1406
1698
|
return formatResult(result);
|
|
@@ -1410,53 +1702,113 @@ function createMorphWarpGrepTool2(config) {
|
|
|
1410
1702
|
}
|
|
1411
1703
|
});
|
|
1412
1704
|
}
|
|
1705
|
+
var openai_default = warpGrepTool2;
|
|
1413
1706
|
|
|
1414
1707
|
// tools/warp_grep/vercel.ts
|
|
1708
|
+
var vercel_exports = {};
|
|
1709
|
+
__export(vercel_exports, {
|
|
1710
|
+
createWarpGrepTool: () => createWarpGrepTool3,
|
|
1711
|
+
default: () => vercel_default,
|
|
1712
|
+
execute: () => execute3,
|
|
1713
|
+
formatResult: () => formatResult,
|
|
1714
|
+
getSystemPrompt: () => getSystemPrompt
|
|
1715
|
+
});
|
|
1415
1716
|
var import_ai = require("ai");
|
|
1416
1717
|
var import_zod = require("zod");
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
}
|
|
1420
|
-
function
|
|
1718
|
+
async function execute3(input, config) {
|
|
1719
|
+
return executeToolCall(input, config);
|
|
1720
|
+
}
|
|
1721
|
+
function createWarpGrepTool3(config) {
|
|
1722
|
+
const schema = import_zod.z.object({
|
|
1723
|
+
query: import_zod.z.string().describe("Free-form repository question")
|
|
1724
|
+
});
|
|
1421
1725
|
return (0, import_ai.tool)({
|
|
1422
1726
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
1423
|
-
inputSchema:
|
|
1727
|
+
inputSchema: schema,
|
|
1424
1728
|
execute: async (params) => {
|
|
1425
|
-
const
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
repoRoot: config.repoRoot,
|
|
1429
|
-
provider,
|
|
1430
|
-
excludes: config.excludes,
|
|
1431
|
-
includes: config.includes,
|
|
1432
|
-
debug: config.debug ?? false,
|
|
1433
|
-
apiKey: config.apiKey
|
|
1434
|
-
});
|
|
1435
|
-
const finish = result.finish;
|
|
1436
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
1437
|
-
return { success: false, error: "Search did not complete" };
|
|
1729
|
+
const result = await executeToolCall(params, config);
|
|
1730
|
+
if (!result.success) {
|
|
1731
|
+
throw new Error(`Failed to search codebase: ${result.error}`);
|
|
1438
1732
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1733
|
+
return {
|
|
1734
|
+
success: true,
|
|
1735
|
+
contexts: result.contexts,
|
|
1736
|
+
summary: result.summary
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
var vercel_default = createWarpGrepTool3;
|
|
1742
|
+
|
|
1743
|
+
// tools/warp_grep/gemini.ts
|
|
1744
|
+
var gemini_exports = {};
|
|
1745
|
+
__export(gemini_exports, {
|
|
1746
|
+
createMorphWarpGrepTool: () => createMorphWarpGrepTool,
|
|
1747
|
+
default: () => gemini_default,
|
|
1748
|
+
execute: () => execute4,
|
|
1749
|
+
formatResult: () => formatResult,
|
|
1750
|
+
getSystemPrompt: () => getSystemPrompt,
|
|
1751
|
+
warpGrepFunctionDeclaration: () => warpGrepFunctionDeclaration
|
|
1752
|
+
});
|
|
1753
|
+
var import_generative_ai = require("@google/generative-ai");
|
|
1754
|
+
var TOOL_PARAMETERS2 = {
|
|
1755
|
+
type: import_generative_ai.SchemaType.OBJECT,
|
|
1756
|
+
properties: {
|
|
1757
|
+
query: {
|
|
1758
|
+
type: import_generative_ai.SchemaType.STRING,
|
|
1759
|
+
description: "Free-form repository question"
|
|
1760
|
+
}
|
|
1761
|
+
},
|
|
1762
|
+
required: ["query"]
|
|
1763
|
+
};
|
|
1764
|
+
var warpGrepFunctionDeclaration = {
|
|
1765
|
+
name: WARP_GREP_TOOL_NAME,
|
|
1766
|
+
description: WARP_GREP_DESCRIPTION,
|
|
1767
|
+
parameters: TOOL_PARAMETERS2
|
|
1768
|
+
};
|
|
1769
|
+
async function execute4(input, config) {
|
|
1770
|
+
return executeToolCall(input, config);
|
|
1771
|
+
}
|
|
1772
|
+
function createMorphWarpGrepTool(config) {
|
|
1773
|
+
const declaration = {
|
|
1774
|
+
name: config.name ?? WARP_GREP_TOOL_NAME,
|
|
1775
|
+
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
1776
|
+
parameters: TOOL_PARAMETERS2
|
|
1777
|
+
};
|
|
1778
|
+
return Object.assign(declaration, {
|
|
1779
|
+
execute: async (input) => {
|
|
1780
|
+
return executeToolCall(input, config);
|
|
1781
|
+
},
|
|
1782
|
+
formatResult: (result) => {
|
|
1783
|
+
return formatResult(result);
|
|
1784
|
+
},
|
|
1785
|
+
getSystemPrompt: () => {
|
|
1786
|
+
return getSystemPrompt();
|
|
1444
1787
|
}
|
|
1445
1788
|
});
|
|
1446
1789
|
}
|
|
1790
|
+
var gemini_default = warpGrepFunctionDeclaration;
|
|
1447
1791
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1448
1792
|
0 && (module.exports = {
|
|
1449
1793
|
LocalRipgrepProvider,
|
|
1794
|
+
RemoteCommandsProvider,
|
|
1450
1795
|
WARP_GREP_DESCRIPTION,
|
|
1451
1796
|
WARP_GREP_SYSTEM_PROMPT,
|
|
1452
1797
|
WARP_GREP_TOOL_NAME,
|
|
1453
1798
|
WarpGrepClient,
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
createVercelWarpGrepTool,
|
|
1799
|
+
anthropic,
|
|
1800
|
+
executeToolCall,
|
|
1457
1801
|
executeWarpGrep,
|
|
1458
1802
|
formatResult,
|
|
1803
|
+
gemini,
|
|
1459
1804
|
getSystemPrompt,
|
|
1460
|
-
|
|
1805
|
+
normalizeFinishFiles,
|
|
1806
|
+
openai,
|
|
1807
|
+
readFinishFiles,
|
|
1808
|
+
runWarpGrep,
|
|
1809
|
+
toolGrep,
|
|
1810
|
+
toolListDirectory,
|
|
1811
|
+
toolRead,
|
|
1812
|
+
vercel
|
|
1461
1813
|
});
|
|
1462
1814
|
//# sourceMappingURL=index.cjs.map
|