@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
package/dist/index.cjs
CHANGED
|
@@ -961,9 +961,13 @@ async function checkHealth(config = {}) {
|
|
|
961
961
|
|
|
962
962
|
// tools/warp_grep/agent/config.ts
|
|
963
963
|
var AGENT_CONFIG = {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
964
|
+
MAX_TURNS: 4,
|
|
965
|
+
TIMEOUT_MS: 3e4,
|
|
966
|
+
MAX_CONTEXT_CHARS: 54e4,
|
|
967
|
+
MAX_OUTPUT_LINES: 200,
|
|
968
|
+
MAX_READ_LINES: 800,
|
|
969
|
+
MAX_LIST_DEPTH: 3,
|
|
970
|
+
LIST_TIMEOUT_MS: 2e3
|
|
967
971
|
};
|
|
968
972
|
var BUILTIN_EXCLUDES = [
|
|
969
973
|
// Version control
|
|
@@ -1045,113 +1049,191 @@ var BUILTIN_EXCLUDES = [
|
|
|
1045
1049
|
".*"
|
|
1046
1050
|
];
|
|
1047
1051
|
var DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
1048
|
-
var DEFAULT_MODEL = "morph-warp-grep";
|
|
1052
|
+
var DEFAULT_MODEL = "morph-warp-grep-v1";
|
|
1049
1053
|
|
|
1050
1054
|
// tools/warp_grep/agent/prompt.ts
|
|
1051
|
-
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given
|
|
1055
|
+
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given search_string.
|
|
1052
1056
|
|
|
1053
|
-
|
|
1057
|
+
### workflow
|
|
1054
1058
|
You have exactly 4 turns. The 4th turn MUST be a \`finish\` call. Each turn allows up to 8 parallel tool calls.
|
|
1055
1059
|
|
|
1056
|
-
- Turn 1: Map the territory OR dive deep (based on
|
|
1060
|
+
- Turn 1: Map the territory OR dive deep (based on search_string specificity)
|
|
1057
1061
|
- Turn 2-3: Refine based on findings
|
|
1058
1062
|
- Turn 4: MUST call \`finish\` with all relevant code locations
|
|
1059
1063
|
- You MAY call \`finish\` early if confident\u2014but never before at least 1 search turn.
|
|
1064
|
+
- The user strongly prefers if you can call the finish tool early, but you must be correct
|
|
1060
1065
|
|
|
1061
|
-
Remember, if the task feels easy to you, it is strongly desirable to call
|
|
1062
|
-
</workflow>
|
|
1066
|
+
Remember, if the task feels easy to you, it is strongly desirable to call 'finish' early using fewer turns, but quality over speed
|
|
1063
1067
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1068
|
+
### tools
|
|
1069
|
+
Tool calls use nested XML elements:
|
|
1070
|
+
\`\`\`xml
|
|
1071
|
+
<tool_name>
|
|
1072
|
+
<parameter>value</parameter>
|
|
1073
|
+
</tool_name>
|
|
1074
|
+
\`\`\`
|
|
1075
|
+
|
|
1076
|
+
### \`list_directory\`
|
|
1077
|
+
Directory tree view. Shows structure of a path, optionally filtered by regex pattern.
|
|
1078
|
+
|
|
1079
|
+
Elements:
|
|
1080
|
+
- \`<path>\` (required): Directory path to list (use \`.\` for repo root)
|
|
1081
|
+
- \`<pattern>\` (optional): Regex to filter results
|
|
1069
1082
|
|
|
1070
1083
|
Examples:
|
|
1071
1084
|
\`\`\`
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1085
|
+
<list_directory>
|
|
1086
|
+
<path>src/services</path>
|
|
1087
|
+
</list_directory>
|
|
1088
|
+
|
|
1089
|
+
<list_directory>
|
|
1090
|
+
<path>lib/utils</path>
|
|
1091
|
+
<pattern>.*\\.(ts|js)$</pattern>
|
|
1092
|
+
</list_directory>
|
|
1076
1093
|
\`\`\`
|
|
1077
1094
|
|
|
1078
|
-
### \`read
|
|
1079
|
-
Read file contents.
|
|
1095
|
+
### \`read\`
|
|
1096
|
+
Read file contents. Supports multiple line ranges.
|
|
1080
1097
|
- Returns numbered lines for easy reference
|
|
1081
|
-
-
|
|
1098
|
+
- ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
1099
|
+
|
|
1100
|
+
Elements:
|
|
1101
|
+
- \`<path>\` (required): File path to read
|
|
1102
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80,100-120" (omit to read entire file)
|
|
1082
1103
|
|
|
1083
1104
|
Examples:
|
|
1084
1105
|
\`\`\`
|
|
1085
|
-
read
|
|
1086
|
-
|
|
1087
|
-
read
|
|
1106
|
+
<read>
|
|
1107
|
+
<path>src/main.py</path>
|
|
1108
|
+
</read>
|
|
1109
|
+
|
|
1110
|
+
<read>
|
|
1111
|
+
<path>src/auth.py</path>
|
|
1112
|
+
<lines>1-20,45-80,150-200</lines>
|
|
1113
|
+
</read>
|
|
1088
1114
|
\`\`\`
|
|
1089
1115
|
|
|
1090
|
-
### \`grep
|
|
1091
|
-
|
|
1092
|
-
-
|
|
1093
|
-
-
|
|
1116
|
+
### \`grep\`
|
|
1117
|
+
Search for pattern matches across files. Returns matches with 1 line of context above and below.
|
|
1118
|
+
- Match lines use \`:\` separator \u2192 \`filepath:linenum:content\`
|
|
1119
|
+
- Context lines use \`-\` separator \u2192 \`filepath-linenum-content\`
|
|
1120
|
+
|
|
1121
|
+
Elements:
|
|
1122
|
+
- \`<pattern>\` (required): Search pattern (regex). Use \`(a|b)\` for OR patterns.
|
|
1123
|
+
- \`<sub_dir>\` (optional): Subdirectory to search in (defaults to \`.\`)
|
|
1124
|
+
- \`<glob>\` (optional): File pattern filter like \`*.py\` or \`*.{ts,tsx}\`
|
|
1094
1125
|
|
|
1095
1126
|
Examples:
|
|
1096
1127
|
\`\`\`
|
|
1097
|
-
grep
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
grep
|
|
1128
|
+
<grep>
|
|
1129
|
+
<pattern>(authenticate|authorize|login)</pattern>
|
|
1130
|
+
<sub_dir>src/auth/</sub_dir>
|
|
1131
|
+
</grep>
|
|
1132
|
+
|
|
1133
|
+
<grep>
|
|
1134
|
+
<pattern>class.*(Service|Controller)</pattern>
|
|
1135
|
+
<glob>*.{ts,js}</glob>
|
|
1136
|
+
</grep>
|
|
1137
|
+
|
|
1138
|
+
<grep>
|
|
1139
|
+
<pattern>(DB_HOST|DATABASE_URL|connection)</pattern>
|
|
1140
|
+
<glob>*.{py,yaml,env}</glob>
|
|
1141
|
+
<sub_dir>lib/</sub_dir>
|
|
1142
|
+
</grep>
|
|
1101
1143
|
\`\`\`
|
|
1102
1144
|
|
|
1103
|
-
### \`finish
|
|
1104
|
-
Submit final answer with all relevant code locations.
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
-
|
|
1108
|
-
-
|
|
1109
|
-
|
|
1145
|
+
### \`finish\`
|
|
1146
|
+
Submit final answer with all relevant code locations. Uses nested \`<file>\` elements.
|
|
1147
|
+
|
|
1148
|
+
File elements:
|
|
1149
|
+
- \`<path>\` (required): File path
|
|
1150
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80" (\`*\` for entire file)
|
|
1151
|
+
|
|
1152
|
+
ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
1110
1153
|
|
|
1111
1154
|
Examples:
|
|
1112
1155
|
\`\`\`
|
|
1113
|
-
finish
|
|
1114
|
-
|
|
1156
|
+
<finish>
|
|
1157
|
+
<file>
|
|
1158
|
+
<path>src/auth.py</path>
|
|
1159
|
+
<lines>1-15,25-50,75-80</lines>
|
|
1160
|
+
</file>
|
|
1161
|
+
<file>
|
|
1162
|
+
<path>src/models/user.py</path>
|
|
1163
|
+
<lines>*</lines>
|
|
1164
|
+
</file>
|
|
1165
|
+
</finish>
|
|
1115
1166
|
\`\`\`
|
|
1116
1167
|
</tools>
|
|
1117
1168
|
|
|
1118
1169
|
<strategy>
|
|
1119
|
-
**Before your first tool call, classify the
|
|
1170
|
+
**Before your first tool call, classify the search_string:**
|
|
1120
1171
|
|
|
1121
|
-
|
|
|
1122
|
-
|
|
1123
|
-
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by
|
|
1124
|
-
| **Conceptual** (how does X work, where is Y handled) |
|
|
1125
|
-
| **Exploratory** (find all tests, list API endpoints) |
|
|
1172
|
+
| Search_string Type | Round 1 Strategy | Early Finish? |
|
|
1173
|
+
|------------|------------------|---------------|
|
|
1174
|
+
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by round 2 |
|
|
1175
|
+
| **Conceptual** (how does X work, where is Y handled) | list_directory + 2-3 broad greps | Rarely early |
|
|
1176
|
+
| **Exploratory** (find all tests, list API endpoints) | list_directory at multiple depths | Usually needs 3 rounds |
|
|
1126
1177
|
|
|
1127
1178
|
**Parallel call patterns:**
|
|
1128
1179
|
- **Shotgun grep**: Same pattern, 8 different directories\u2014fast coverage
|
|
1129
1180
|
- **Variant grep**: 8 pattern variations (synonyms, naming conventions)\u2014catches inconsistent codebases
|
|
1130
|
-
- **Funnel**: 1
|
|
1181
|
+
- **Funnel**: 1 list_directory + 7 greps\u2014orient and search simultaneously
|
|
1131
1182
|
- **Deep read**: 8 reads on files you already identified\u2014gather full context fast
|
|
1183
|
+
|
|
1184
|
+
**Tool call expectations:**
|
|
1185
|
+
- 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.
|
|
1186
|
+
- 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.
|
|
1187
|
+
- 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.
|
|
1188
|
+
- 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.
|
|
1132
1189
|
</strategy>
|
|
1133
1190
|
|
|
1134
1191
|
<output_format>
|
|
1135
1192
|
EVERY response MUST follow this exact format:
|
|
1136
1193
|
|
|
1137
1194
|
1. First, wrap your reasoning in \`<think>...</think>\` tags containing:
|
|
1138
|
-
-
|
|
1139
|
-
- Confidence estimate (can I finish in 1-2
|
|
1140
|
-
- This
|
|
1195
|
+
- Search_string classification (specific/conceptual/exploratory)
|
|
1196
|
+
- Confidence estimate (can I finish in 1-2 rounds?)
|
|
1197
|
+
- This round's parallel strategy
|
|
1141
1198
|
- What signals would let me finish early?
|
|
1142
1199
|
|
|
1143
|
-
2. Then, output
|
|
1200
|
+
2. Then, output up to 8 tool calls using nested XML elements.
|
|
1144
1201
|
|
|
1145
1202
|
Example:
|
|
1146
1203
|
\`\`\`
|
|
1147
1204
|
<think>
|
|
1148
|
-
This is a specific
|
|
1149
|
-
High confidence I can finish in 2
|
|
1205
|
+
This is a specific search_string about authentication. I'll grep for auth-related patterns.
|
|
1206
|
+
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
|
|
1150
1207
|
Strategy: Shotgun grep across likely directories.
|
|
1151
1208
|
</think>
|
|
1152
|
-
<
|
|
1153
|
-
<
|
|
1154
|
-
<
|
|
1209
|
+
<grep>
|
|
1210
|
+
<pattern>(authenticate|login|session)</pattern>
|
|
1211
|
+
<sub_dir>src/auth/</sub_dir>
|
|
1212
|
+
</grep>
|
|
1213
|
+
<grep>
|
|
1214
|
+
<pattern>(middleware|interceptor)</pattern>
|
|
1215
|
+
<glob>*.{ts,js}</glob>
|
|
1216
|
+
</grep>
|
|
1217
|
+
<list_directory>
|
|
1218
|
+
<path>src/auth</path>
|
|
1219
|
+
</list_directory>
|
|
1220
|
+
\`\`\`
|
|
1221
|
+
|
|
1222
|
+
Finishing example:
|
|
1223
|
+
\`\`\`
|
|
1224
|
+
<think>
|
|
1225
|
+
I think I have a rough idea, but this is my last turn so I must call the finish tool regardless.
|
|
1226
|
+
</think>
|
|
1227
|
+
<finish>
|
|
1228
|
+
<file>
|
|
1229
|
+
<path>src/auth/login.py</path>
|
|
1230
|
+
<lines>1-50</lines>
|
|
1231
|
+
</file>
|
|
1232
|
+
<file>
|
|
1233
|
+
<path>src/middleware/session.py</path>
|
|
1234
|
+
<lines>10-80</lines>
|
|
1235
|
+
</file>
|
|
1236
|
+
</finish>
|
|
1155
1237
|
\`\`\`
|
|
1156
1238
|
|
|
1157
1239
|
No commentary outside \`<think>\`. No explanations after tool calls.
|
|
@@ -1164,17 +1246,111 @@ When calling \`finish\`:
|
|
|
1164
1246
|
- Include any type definitions, interfaces, or constants used
|
|
1165
1247
|
- Better to over-include than leave the user missing context
|
|
1166
1248
|
- If unsure about boundaries, include more rather than less
|
|
1167
|
-
</finishing_requirements
|
|
1168
|
-
|
|
1169
|
-
Begin your exploration now to find code relevant to the query.`;
|
|
1249
|
+
</finishing_requirements>`;
|
|
1170
1250
|
function getSystemPrompt() {
|
|
1171
1251
|
return SYSTEM_PROMPT;
|
|
1172
1252
|
}
|
|
1173
1253
|
|
|
1174
1254
|
// tools/warp_grep/agent/parser.ts
|
|
1175
|
-
var VALID_COMMANDS = ["
|
|
1255
|
+
var VALID_COMMANDS = ["list_directory", "grep", "read", "finish"];
|
|
1256
|
+
function isValidCommand(name) {
|
|
1257
|
+
return VALID_COMMANDS.includes(name);
|
|
1258
|
+
}
|
|
1259
|
+
function getXmlElementText(xml, tagName) {
|
|
1260
|
+
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i");
|
|
1261
|
+
const match = xml.match(regex);
|
|
1262
|
+
return match ? match[1].trim() : null;
|
|
1263
|
+
}
|
|
1264
|
+
function parseNestedXmlTools(text) {
|
|
1265
|
+
const tools = [];
|
|
1266
|
+
const toolRegex = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
|
|
1267
|
+
let match;
|
|
1268
|
+
while ((match = toolRegex.exec(text)) !== null) {
|
|
1269
|
+
const rawToolName = match[1].toLowerCase();
|
|
1270
|
+
const content = match[2];
|
|
1271
|
+
if (!isValidCommand(rawToolName)) continue;
|
|
1272
|
+
const toolName = rawToolName;
|
|
1273
|
+
if (toolName === "list_directory") {
|
|
1274
|
+
const path5 = getXmlElementText(content, "path");
|
|
1275
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
1276
|
+
if (path5) {
|
|
1277
|
+
tools.push({ name: "list_directory", arguments: { path: path5, pattern } });
|
|
1278
|
+
}
|
|
1279
|
+
} else if (toolName === "grep") {
|
|
1280
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
1281
|
+
const subDir = getXmlElementText(content, "sub_dir");
|
|
1282
|
+
const glob = getXmlElementText(content, "glob");
|
|
1283
|
+
if (pattern) {
|
|
1284
|
+
tools.push({
|
|
1285
|
+
name: "grep",
|
|
1286
|
+
arguments: {
|
|
1287
|
+
pattern,
|
|
1288
|
+
path: subDir || ".",
|
|
1289
|
+
...glob && { glob }
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
} else if (toolName === "read") {
|
|
1294
|
+
const path5 = getXmlElementText(content, "path");
|
|
1295
|
+
const linesStr = getXmlElementText(content, "lines");
|
|
1296
|
+
if (path5) {
|
|
1297
|
+
const args = { path: path5 };
|
|
1298
|
+
if (linesStr) {
|
|
1299
|
+
const ranges = [];
|
|
1300
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
1301
|
+
const trimmed = rangeStr.trim();
|
|
1302
|
+
if (!trimmed) continue;
|
|
1303
|
+
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
1304
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
1305
|
+
ranges.push([s, e]);
|
|
1306
|
+
} else if (Number.isFinite(s)) {
|
|
1307
|
+
ranges.push([s, s]);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (ranges.length === 1) {
|
|
1311
|
+
args.start = ranges[0][0];
|
|
1312
|
+
args.end = ranges[0][1];
|
|
1313
|
+
} else if (ranges.length > 1) {
|
|
1314
|
+
args.lines = ranges;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
tools.push({ name: "read", arguments: args });
|
|
1318
|
+
}
|
|
1319
|
+
} else if (toolName === "finish") {
|
|
1320
|
+
const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
|
|
1321
|
+
const files = [];
|
|
1322
|
+
let fileMatch;
|
|
1323
|
+
while ((fileMatch = fileRegex.exec(content)) !== null) {
|
|
1324
|
+
const fileContent = fileMatch[1];
|
|
1325
|
+
const filePath = getXmlElementText(fileContent, "path");
|
|
1326
|
+
const linesStr = getXmlElementText(fileContent, "lines");
|
|
1327
|
+
if (filePath && linesStr) {
|
|
1328
|
+
const ranges = [];
|
|
1329
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
1330
|
+
if (rangeStr.trim() === "*") {
|
|
1331
|
+
ranges.push([1, 999999]);
|
|
1332
|
+
} else {
|
|
1333
|
+
const [s, e] = rangeStr.split("-").map((v) => parseInt(v.trim(), 10));
|
|
1334
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
1335
|
+
ranges.push([s, e]);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
if (ranges.length > 0) {
|
|
1340
|
+
files.push({ path: filePath, lines: ranges });
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
if (files.length > 0) {
|
|
1345
|
+
tools.push({ name: "finish", arguments: { files } });
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
return tools;
|
|
1350
|
+
}
|
|
1176
1351
|
function preprocessText(text) {
|
|
1177
1352
|
let processed = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
1353
|
+
const nestedTools = parseNestedXmlTools(processed);
|
|
1178
1354
|
const openingTagRegex = /<tool_call>|<tool>/gi;
|
|
1179
1355
|
const closingTagRegex = /<\/tool_call>|<\/tool>/gi;
|
|
1180
1356
|
const openingMatches = processed.match(openingTagRegex) || [];
|
|
@@ -1211,7 +1387,7 @@ function preprocessText(text) {
|
|
|
1211
1387
|
}
|
|
1212
1388
|
}
|
|
1213
1389
|
}
|
|
1214
|
-
return toolCallLines;
|
|
1390
|
+
return { lines: toolCallLines, nestedTools };
|
|
1215
1391
|
}
|
|
1216
1392
|
var LLMResponseParser = class {
|
|
1217
1393
|
finishSpecSplitRe = /,(?=[^,\s]+:)/;
|
|
@@ -1219,8 +1395,8 @@ var LLMResponseParser = class {
|
|
|
1219
1395
|
if (typeof text !== "string") {
|
|
1220
1396
|
throw new TypeError("Command text must be a string.");
|
|
1221
1397
|
}
|
|
1222
|
-
const lines = preprocessText(text);
|
|
1223
|
-
const commands = [];
|
|
1398
|
+
const { lines, nestedTools } = preprocessText(text);
|
|
1399
|
+
const commands = [...nestedTools];
|
|
1224
1400
|
let finishAccumulator = null;
|
|
1225
1401
|
lines.forEach((line) => {
|
|
1226
1402
|
if (!line || line.startsWith("#")) return;
|
|
@@ -1228,8 +1404,8 @@ var LLMResponseParser = class {
|
|
|
1228
1404
|
if (parts.length === 0) return;
|
|
1229
1405
|
const cmd = parts[0];
|
|
1230
1406
|
switch (cmd) {
|
|
1231
|
-
case "
|
|
1232
|
-
this.
|
|
1407
|
+
case "list_directory":
|
|
1408
|
+
this.handleListDirectory(parts, line, commands);
|
|
1233
1409
|
break;
|
|
1234
1410
|
case "grep":
|
|
1235
1411
|
this.handleGrep(parts, line, commands);
|
|
@@ -1247,8 +1423,8 @@ var LLMResponseParser = class {
|
|
|
1247
1423
|
if (finishAccumulator) {
|
|
1248
1424
|
const map = finishAccumulator;
|
|
1249
1425
|
const entries = [...map.entries()];
|
|
1250
|
-
const filesPayload = entries.map(([
|
|
1251
|
-
path:
|
|
1426
|
+
const filesPayload = entries.map(([path5, ranges]) => ({
|
|
1427
|
+
path: path5,
|
|
1252
1428
|
lines: [...ranges].sort((a, b) => a[0] - b[0])
|
|
1253
1429
|
}));
|
|
1254
1430
|
commands.push({ name: "finish", arguments: { files: filesPayload } });
|
|
@@ -1280,18 +1456,17 @@ var LLMResponseParser = class {
|
|
|
1280
1456
|
skip(message) {
|
|
1281
1457
|
return { name: "_skip", arguments: { message } };
|
|
1282
1458
|
}
|
|
1283
|
-
|
|
1459
|
+
handleListDirectory(parts, rawLine, commands) {
|
|
1284
1460
|
if (parts.length < 2) {
|
|
1285
1461
|
commands.push(this.skip(
|
|
1286
|
-
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format:
|
|
1462
|
+
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: list_directory <path> [pattern]. Example: list_directory src/`
|
|
1287
1463
|
));
|
|
1288
1464
|
return;
|
|
1289
1465
|
}
|
|
1290
|
-
const
|
|
1466
|
+
const path5 = parts[1];
|
|
1291
1467
|
const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
|
|
1292
|
-
commands.push({ name: "
|
|
1468
|
+
commands.push({ name: "list_directory", arguments: { path: path5, pattern } });
|
|
1293
1469
|
}
|
|
1294
|
-
// no glob tool in MCP
|
|
1295
1470
|
handleGrep(parts, rawLine, commands) {
|
|
1296
1471
|
if (parts.length < 3) {
|
|
1297
1472
|
commands.push(this.skip(
|
|
@@ -1362,8 +1537,30 @@ var LLMResponseParser = class {
|
|
|
1362
1537
|
}
|
|
1363
1538
|
};
|
|
1364
1539
|
|
|
1365
|
-
// tools/warp_grep/tools/
|
|
1540
|
+
// tools/warp_grep/agent/tools/grep.ts
|
|
1541
|
+
async function toolGrep(provider, args) {
|
|
1542
|
+
const res = await provider.grep(args);
|
|
1543
|
+
if (res.error) {
|
|
1544
|
+
return { output: res.error };
|
|
1545
|
+
}
|
|
1546
|
+
if (!res.lines.length) {
|
|
1547
|
+
return { output: "no matches" };
|
|
1548
|
+
}
|
|
1549
|
+
return { output: res.lines.join("\n") };
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// tools/warp_grep/agent/tools/read.ts
|
|
1366
1553
|
async function toolRead(provider, args) {
|
|
1554
|
+
if (args.lines && args.lines.length > 0) {
|
|
1555
|
+
const chunks = [];
|
|
1556
|
+
for (const [start, end] of args.lines) {
|
|
1557
|
+
const res2 = await provider.read({ path: args.path, start, end });
|
|
1558
|
+
if (res2.error) return res2.error;
|
|
1559
|
+
chunks.push(res2.lines.join("\n"));
|
|
1560
|
+
}
|
|
1561
|
+
if (chunks.every((c) => c === "")) return "(empty file)";
|
|
1562
|
+
return chunks.join("\n...\n");
|
|
1563
|
+
}
|
|
1367
1564
|
const res = await provider.read({ path: args.path, start: args.start, end: args.end });
|
|
1368
1565
|
if (res.error) {
|
|
1369
1566
|
return res.error;
|
|
@@ -1372,16 +1569,56 @@ async function toolRead(provider, args) {
|
|
|
1372
1569
|
return res.lines.join("\n");
|
|
1373
1570
|
}
|
|
1374
1571
|
|
|
1375
|
-
// tools/warp_grep/tools/
|
|
1376
|
-
async function
|
|
1377
|
-
const list = await provider.
|
|
1572
|
+
// tools/warp_grep/agent/tools/list_directory.ts
|
|
1573
|
+
async function toolListDirectory(provider, args) {
|
|
1574
|
+
const list = await provider.listDirectory({
|
|
1378
1575
|
path: args.path,
|
|
1379
1576
|
pattern: args.pattern ?? null,
|
|
1380
|
-
maxResults: args.maxResults ??
|
|
1381
|
-
maxDepth: args.maxDepth ??
|
|
1577
|
+
maxResults: args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
1578
|
+
maxDepth: args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH
|
|
1382
1579
|
});
|
|
1383
1580
|
if (!list.length) return "empty";
|
|
1384
|
-
|
|
1581
|
+
if (list.length >= AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
1582
|
+
return "query not specific enough, tool called tried to return too much context and failed";
|
|
1583
|
+
}
|
|
1584
|
+
return list.map((e) => {
|
|
1585
|
+
const indent = " ".repeat(e.depth);
|
|
1586
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
1587
|
+
return `${indent}${name}`;
|
|
1588
|
+
}).join("\n");
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// tools/warp_grep/agent/tools/finish.ts
|
|
1592
|
+
async function readFinishFiles(repoRoot, files, reader) {
|
|
1593
|
+
const out = [];
|
|
1594
|
+
for (const f of files) {
|
|
1595
|
+
const ranges = mergeRanges(f.lines);
|
|
1596
|
+
const chunks = [];
|
|
1597
|
+
for (const [s, e] of ranges) {
|
|
1598
|
+
const lines = await reader(f.path, s, e);
|
|
1599
|
+
chunks.push(lines.join("\n"));
|
|
1600
|
+
}
|
|
1601
|
+
out.push({ path: f.path, ranges, content: chunks.join("\n") });
|
|
1602
|
+
}
|
|
1603
|
+
return out;
|
|
1604
|
+
}
|
|
1605
|
+
function mergeRanges(ranges) {
|
|
1606
|
+
if (!ranges.length) return [];
|
|
1607
|
+
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
1608
|
+
const merged = [];
|
|
1609
|
+
let [cs, ce] = sorted[0];
|
|
1610
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
1611
|
+
const [s, e] = sorted[i];
|
|
1612
|
+
if (s <= ce + 1) {
|
|
1613
|
+
ce = Math.max(ce, e);
|
|
1614
|
+
} else {
|
|
1615
|
+
merged.push([cs, ce]);
|
|
1616
|
+
cs = s;
|
|
1617
|
+
ce = e;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
merged.push([cs, ce]);
|
|
1621
|
+
return merged;
|
|
1385
1622
|
}
|
|
1386
1623
|
|
|
1387
1624
|
// tools/warp_grep/agent/formatter.ts
|
|
@@ -1400,8 +1637,8 @@ var ToolOutputFormatter = class {
|
|
|
1400
1637
|
switch (name) {
|
|
1401
1638
|
case "read":
|
|
1402
1639
|
return this.formatRead(safeArgs, payload, isError);
|
|
1403
|
-
case "
|
|
1404
|
-
return this.
|
|
1640
|
+
case "list_directory":
|
|
1641
|
+
return this.formatListDirectory(safeArgs, payload, isError);
|
|
1405
1642
|
case "grep":
|
|
1406
1643
|
return this.formatGrep(safeArgs, payload, isError);
|
|
1407
1644
|
default:
|
|
@@ -1414,39 +1651,56 @@ ${payload}
|
|
|
1414
1651
|
if (isError) {
|
|
1415
1652
|
return payload;
|
|
1416
1653
|
}
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1654
|
+
const path5 = this.asString(args.path) || "...";
|
|
1655
|
+
const start = args.start;
|
|
1656
|
+
const end = args.end;
|
|
1657
|
+
const linesArray = args.lines;
|
|
1658
|
+
const attributes = [`path="${path5}"`];
|
|
1659
|
+
if (linesArray && linesArray.length > 0) {
|
|
1660
|
+
const rangeStr = linesArray.map(([s, e]) => `${s}-${e}`).join(",");
|
|
1661
|
+
attributes.push(`lines="${rangeStr}"`);
|
|
1662
|
+
} else if (start !== void 0 && end !== void 0) {
|
|
1663
|
+
attributes.push(`lines="${start}-${end}"`);
|
|
1664
|
+
}
|
|
1665
|
+
return `<read ${attributes.join(" ")}>
|
|
1419
1666
|
${payload}
|
|
1420
|
-
</
|
|
1667
|
+
</read>`;
|
|
1421
1668
|
}
|
|
1422
|
-
|
|
1423
|
-
const
|
|
1669
|
+
formatListDirectory(args, payload, isError) {
|
|
1670
|
+
const path5 = this.asString(args.path) || ".";
|
|
1671
|
+
const pattern = this.asString(args.pattern);
|
|
1672
|
+
const attributes = [`path="${path5}"`];
|
|
1673
|
+
if (pattern) {
|
|
1674
|
+
attributes.push(`pattern="${pattern}"`);
|
|
1675
|
+
}
|
|
1424
1676
|
if (isError) {
|
|
1425
|
-
|
|
1426
|
-
${payload}
|
|
1427
|
-
</analyse_results>`;
|
|
1677
|
+
attributes.push('status="error"');
|
|
1428
1678
|
}
|
|
1429
|
-
return `<
|
|
1679
|
+
return `<list_directory ${attributes.join(" ")}>
|
|
1430
1680
|
${payload}
|
|
1431
|
-
</
|
|
1681
|
+
</list_directory>`;
|
|
1432
1682
|
}
|
|
1433
1683
|
formatGrep(args, payload, isError) {
|
|
1434
1684
|
const pattern = this.asString(args.pattern);
|
|
1435
|
-
const
|
|
1685
|
+
const subDir = this.asString(args.path);
|
|
1686
|
+
const glob = this.asString(args.glob);
|
|
1436
1687
|
const attributes = [];
|
|
1437
1688
|
if (pattern !== void 0) {
|
|
1438
1689
|
attributes.push(`pattern="${pattern}"`);
|
|
1439
1690
|
}
|
|
1440
|
-
if (
|
|
1441
|
-
attributes.push(`
|
|
1691
|
+
if (subDir !== void 0) {
|
|
1692
|
+
attributes.push(`sub_dir="${subDir}"`);
|
|
1693
|
+
}
|
|
1694
|
+
if (glob !== void 0) {
|
|
1695
|
+
attributes.push(`glob="${glob}"`);
|
|
1442
1696
|
}
|
|
1443
1697
|
if (isError) {
|
|
1444
1698
|
attributes.push('status="error"');
|
|
1445
1699
|
}
|
|
1446
1700
|
const attrText = attributes.length ? ` ${attributes.join(" ")}` : "";
|
|
1447
|
-
return `<
|
|
1701
|
+
return `<grep${attrText}>
|
|
1448
1702
|
${payload}
|
|
1449
|
-
</
|
|
1703
|
+
</grep>`;
|
|
1450
1704
|
}
|
|
1451
1705
|
asString(value) {
|
|
1452
1706
|
if (value === null || value === void 0) {
|
|
@@ -1460,180 +1714,100 @@ function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
|
1460
1714
|
return sharedFormatter.format(toolName, args, output, options);
|
|
1461
1715
|
}
|
|
1462
1716
|
|
|
1463
|
-
// tools/warp_grep/agent/
|
|
1464
|
-
var
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
}
|
|
1483
|
-
const firstSep = text.indexOf(":");
|
|
1484
|
-
if (firstSep === -1) {
|
|
1485
|
-
return null;
|
|
1486
|
-
}
|
|
1487
|
-
let filePath = text.slice(0, firstSep).trim();
|
|
1488
|
-
if (!filePath) {
|
|
1489
|
-
return null;
|
|
1490
|
-
}
|
|
1491
|
-
if (filePath.startsWith("./") || filePath.startsWith(".\\")) {
|
|
1492
|
-
filePath = filePath.slice(2);
|
|
1493
|
-
}
|
|
1494
|
-
const remainder = text.slice(firstSep + 1);
|
|
1495
|
-
const secondSep = remainder.indexOf(":");
|
|
1496
|
-
if (secondSep === -1) {
|
|
1497
|
-
return null;
|
|
1498
|
-
}
|
|
1499
|
-
const linePart = remainder.slice(0, secondSep);
|
|
1500
|
-
const lineNumber = Number.parseInt(linePart, 10);
|
|
1501
|
-
if (!Number.isInteger(lineNumber) || lineNumber <= 0) {
|
|
1502
|
-
return null;
|
|
1503
|
-
}
|
|
1504
|
-
let contentSegment = remainder.slice(secondSep + 1);
|
|
1505
|
-
const columnSep = contentSegment.indexOf(":");
|
|
1506
|
-
if (columnSep !== -1 && /^\d+$/.test(contentSegment.slice(0, columnSep))) {
|
|
1507
|
-
contentSegment = contentSegment.slice(columnSep + 1);
|
|
1508
|
-
}
|
|
1509
|
-
const content = contentSegment.trim();
|
|
1510
|
-
if (!content) {
|
|
1511
|
-
return null;
|
|
1512
|
-
}
|
|
1513
|
-
return { path: filePath, lineNumber, content };
|
|
1514
|
-
}
|
|
1515
|
-
function parseAndFilterGrepOutput(rawOutput, state) {
|
|
1516
|
-
const matches = [];
|
|
1517
|
-
if (typeof rawOutput !== "string" || !rawOutput.trim()) {
|
|
1518
|
-
return matches;
|
|
1519
|
-
}
|
|
1520
|
-
for (const line of rawOutput.split(/\r?\n/)) {
|
|
1521
|
-
const fields = extractMatchFields(line);
|
|
1522
|
-
if (!fields) {
|
|
1523
|
-
continue;
|
|
1524
|
-
}
|
|
1525
|
-
if (state.isNew(fields.path, fields.lineNumber)) {
|
|
1526
|
-
matches.push(fields);
|
|
1527
|
-
state.add(fields.path, fields.lineNumber);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
return matches;
|
|
1531
|
-
}
|
|
1532
|
-
function truncateOutput(payload, maxChars) {
|
|
1533
|
-
if (payload.length <= maxChars) {
|
|
1534
|
-
return payload;
|
|
1535
|
-
}
|
|
1536
|
-
const note = "... (output truncated)";
|
|
1537
|
-
const available = maxChars - note.length - 1;
|
|
1538
|
-
if (available <= 0) {
|
|
1539
|
-
return note;
|
|
1540
|
-
}
|
|
1541
|
-
if (payload.length <= available) {
|
|
1542
|
-
return `${payload.slice(0, available).replace(/\n$/, "")}
|
|
1543
|
-
${note}`;
|
|
1544
|
-
}
|
|
1545
|
-
const core = payload.slice(0, Math.max(0, available - 1));
|
|
1546
|
-
const trimmed = core.replace(/\n$/, "").replace(/\s+$/, "");
|
|
1547
|
-
const snippet = trimmed ? `${trimmed}\u2026` : "\u2026";
|
|
1548
|
-
return `${snippet}
|
|
1549
|
-
${note}`;
|
|
1717
|
+
// tools/warp_grep/agent/helpers.ts
|
|
1718
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
1719
|
+
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
1720
|
+
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
1721
|
+
const turnsRemaining = maxTurns - turnsUsed;
|
|
1722
|
+
if (turnsRemaining === 1) {
|
|
1723
|
+
return `
|
|
1724
|
+
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`;
|
|
1725
|
+
}
|
|
1726
|
+
return `
|
|
1727
|
+
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
1728
|
+
}
|
|
1729
|
+
function calculateContextBudget(messages) {
|
|
1730
|
+
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
1731
|
+
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
1732
|
+
const percent = Math.round(totalChars / maxChars * 100);
|
|
1733
|
+
const usedK = Math.round(totalChars / 1e3);
|
|
1734
|
+
const maxK = Math.round(maxChars / 1e3);
|
|
1735
|
+
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
|
|
1550
1736
|
}
|
|
1551
|
-
function
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1737
|
+
async function buildInitialState(repoRoot, query, provider) {
|
|
1738
|
+
try {
|
|
1739
|
+
const entries = await provider.listDirectory({
|
|
1740
|
+
path: ".",
|
|
1741
|
+
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
1742
|
+
maxDepth: 2
|
|
1743
|
+
});
|
|
1744
|
+
const treeLines = entries.map((e) => {
|
|
1745
|
+
const indent = " ".repeat(e.depth);
|
|
1746
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
1747
|
+
return `${indent}${name}`;
|
|
1748
|
+
});
|
|
1749
|
+
const repoName = import_path2.default.basename(repoRoot);
|
|
1750
|
+
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
1751
|
+
${treeLines.join("\n")}` : `${repoName}/`;
|
|
1752
|
+
return `<repo_structure>
|
|
1753
|
+
${treeOutput}
|
|
1754
|
+
</repo_structure>
|
|
1755
|
+
|
|
1756
|
+
<search_string>
|
|
1757
|
+
${query}
|
|
1758
|
+
</search_string>`;
|
|
1759
|
+
} catch {
|
|
1760
|
+
const repoName = import_path2.default.basename(repoRoot);
|
|
1761
|
+
return `<repo_structure>
|
|
1762
|
+
${repoName}/
|
|
1763
|
+
</repo_structure>
|
|
1764
|
+
|
|
1765
|
+
<search_string>
|
|
1766
|
+
${query}
|
|
1767
|
+
</search_string>`;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
1771
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
1772
|
+
if (getTotalChars() <= maxChars) {
|
|
1773
|
+
return messages;
|
|
1774
|
+
}
|
|
1775
|
+
const userIndices = [];
|
|
1776
|
+
let firstUserSkipped = false;
|
|
1777
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1778
|
+
if (messages[i].role === "user") {
|
|
1779
|
+
if (!firstUserSkipped) {
|
|
1780
|
+
firstUserSkipped = true;
|
|
1781
|
+
continue;
|
|
1782
|
+
}
|
|
1783
|
+
userIndices.push(i);
|
|
1559
1784
|
}
|
|
1560
|
-
matchesByFile.get(match.path).push(match);
|
|
1561
1785
|
}
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
if (index > 0) {
|
|
1566
|
-
lines.push("");
|
|
1567
|
-
}
|
|
1568
|
-
lines.push(filePath);
|
|
1569
|
-
const sortedMatches = matchesByFile.get(filePath).slice().sort((a, b) => a.lineNumber - b.lineNumber);
|
|
1570
|
-
for (const match of sortedMatches) {
|
|
1571
|
-
lines.push(`${match.lineNumber}:${match.content}`);
|
|
1786
|
+
for (const idx of userIndices) {
|
|
1787
|
+
if (getTotalChars() <= maxChars) {
|
|
1788
|
+
break;
|
|
1572
1789
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
// tools/warp_grep/tools/finish.ts
|
|
1578
|
-
async function readFinishFiles(repoRoot, files, reader) {
|
|
1579
|
-
const out = [];
|
|
1580
|
-
for (const f of files) {
|
|
1581
|
-
const ranges = mergeRanges(f.lines);
|
|
1582
|
-
const chunks = [];
|
|
1583
|
-
for (const [s, e] of ranges) {
|
|
1584
|
-
const lines = await reader(f.path, s, e);
|
|
1585
|
-
chunks.push(lines.join("\n"));
|
|
1790
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
1791
|
+
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
1586
1792
|
}
|
|
1587
|
-
out.push({ path: f.path, ranges, content: chunks.join("\n") });
|
|
1588
1793
|
}
|
|
1589
|
-
return
|
|
1590
|
-
}
|
|
1591
|
-
function mergeRanges(ranges) {
|
|
1592
|
-
if (!ranges.length) return [];
|
|
1593
|
-
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
1594
|
-
const merged = [];
|
|
1595
|
-
let [cs, ce] = sorted[0];
|
|
1596
|
-
for (let i = 1; i < sorted.length; i++) {
|
|
1597
|
-
const [s, e] = sorted[i];
|
|
1598
|
-
if (s <= ce + 1) {
|
|
1599
|
-
ce = Math.max(ce, e);
|
|
1600
|
-
} else {
|
|
1601
|
-
merged.push([cs, ce]);
|
|
1602
|
-
cs = s;
|
|
1603
|
-
ce = e;
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
merged.push([cs, ce]);
|
|
1607
|
-
return merged;
|
|
1794
|
+
return messages;
|
|
1608
1795
|
}
|
|
1609
1796
|
|
|
1610
1797
|
// tools/warp_grep/agent/runner.ts
|
|
1611
|
-
var
|
|
1798
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
1612
1799
|
var parser = new LLMResponseParser();
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
const files = entries.filter((e) => e.type === "file").map((f) => f.name).slice(0, 50);
|
|
1618
|
-
const parts = [
|
|
1619
|
-
`<repo_root>${repoRoot}</repo_root>`,
|
|
1620
|
-
`<top_dirs>${dirs.join(", ")}</top_dirs>`,
|
|
1621
|
-
`<top_files>${files.join(", ")}</top_files>`
|
|
1622
|
-
];
|
|
1623
|
-
return parts.join("\n");
|
|
1624
|
-
} catch {
|
|
1625
|
-
return `<repo_root>${repoRoot}</repo_root>`;
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
async function callModel(messages, model, apiKey) {
|
|
1629
|
-
const api = "https://api.morphllm.com/v1/chat/completions";
|
|
1800
|
+
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
1801
|
+
async function callModel(messages, model, options = {}) {
|
|
1802
|
+
const baseUrl = DEFAULT_API_URL;
|
|
1803
|
+
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
1630
1804
|
const fetchPromise = fetchWithRetry(
|
|
1631
|
-
|
|
1805
|
+
`${baseUrl}/v1/chat/completions`,
|
|
1632
1806
|
{
|
|
1633
1807
|
method: "POST",
|
|
1634
1808
|
headers: {
|
|
1635
1809
|
"Content-Type": "application/json",
|
|
1636
|
-
Authorization: `Bearer ${apiKey
|
|
1810
|
+
Authorization: `Bearer ${apiKey}`
|
|
1637
1811
|
},
|
|
1638
1812
|
body: JSON.stringify({
|
|
1639
1813
|
model,
|
|
@@ -1642,10 +1816,15 @@ async function callModel(messages, model, apiKey) {
|
|
|
1642
1816
|
messages
|
|
1643
1817
|
})
|
|
1644
1818
|
},
|
|
1645
|
-
|
|
1819
|
+
options.retryConfig
|
|
1646
1820
|
);
|
|
1647
1821
|
const resp = await withTimeout(fetchPromise, AGENT_CONFIG.TIMEOUT_MS, "morph-warp-grep request timed out");
|
|
1648
1822
|
if (!resp.ok) {
|
|
1823
|
+
if (resp.status === 404) {
|
|
1824
|
+
throw new Error(
|
|
1825
|
+
"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"
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1649
1828
|
const t = await resp.text();
|
|
1650
1829
|
throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
|
|
1651
1830
|
}
|
|
@@ -1657,23 +1836,24 @@ async function callModel(messages, model, apiKey) {
|
|
|
1657
1836
|
return content;
|
|
1658
1837
|
}
|
|
1659
1838
|
async function runWarpGrep(config) {
|
|
1660
|
-
const repoRoot =
|
|
1839
|
+
const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
|
|
1661
1840
|
const messages = [];
|
|
1662
|
-
|
|
1663
|
-
messages.push(systemMessage);
|
|
1664
|
-
const queryContent = `<query>${config.query}</query>`;
|
|
1665
|
-
messages.push({ role: "user", content: queryContent });
|
|
1841
|
+
messages.push({ role: "system", content: getSystemPrompt() });
|
|
1666
1842
|
const initialState = await buildInitialState(repoRoot, config.query, config.provider);
|
|
1667
1843
|
messages.push({ role: "user", content: initialState });
|
|
1668
|
-
const
|
|
1844
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
1669
1845
|
const model = config.model || DEFAULT_MODEL;
|
|
1670
1846
|
const provider = config.provider;
|
|
1671
1847
|
const errors = [];
|
|
1672
|
-
const grepState = new GrepState();
|
|
1673
1848
|
let finishMeta;
|
|
1674
1849
|
let terminationReason = "terminated";
|
|
1675
|
-
for (let
|
|
1676
|
-
|
|
1850
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1851
|
+
enforceContextLimit(messages);
|
|
1852
|
+
const assistantContent = await callModel(messages, model, {
|
|
1853
|
+
morphApiKey: config.morphApiKey,
|
|
1854
|
+
morphApiUrl: config.morphApiUrl,
|
|
1855
|
+
retryConfig: config.retryConfig
|
|
1856
|
+
}).catch((e) => {
|
|
1677
1857
|
errors.push({ message: e instanceof Error ? e.message : String(e) });
|
|
1678
1858
|
return "";
|
|
1679
1859
|
});
|
|
@@ -1681,13 +1861,13 @@ async function runWarpGrep(config) {
|
|
|
1681
1861
|
messages.push({ role: "assistant", content: assistantContent });
|
|
1682
1862
|
const toolCalls = parser.parse(assistantContent);
|
|
1683
1863
|
if (toolCalls.length === 0) {
|
|
1684
|
-
errors.push({ message: "No tool calls produced by the model." });
|
|
1864
|
+
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" });
|
|
1685
1865
|
terminationReason = "terminated";
|
|
1686
1866
|
break;
|
|
1687
1867
|
}
|
|
1688
1868
|
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1689
1869
|
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1690
|
-
const
|
|
1870
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1691
1871
|
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1692
1872
|
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1693
1873
|
const formatted = [];
|
|
@@ -1695,69 +1875,42 @@ async function runWarpGrep(config) {
|
|
|
1695
1875
|
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1696
1876
|
formatted.push(msg);
|
|
1697
1877
|
}
|
|
1698
|
-
const
|
|
1699
|
-
for (const c of
|
|
1878
|
+
const allPromises = [];
|
|
1879
|
+
for (const c of grepCalls) {
|
|
1700
1880
|
const args = c.arguments ?? {};
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
(
|
|
1704
|
-
(err) => formatAgentToolOutput("
|
|
1881
|
+
allPromises.push(
|
|
1882
|
+
toolGrep(provider, args).then(
|
|
1883
|
+
({ output }) => formatAgentToolOutput("grep", args, output, { isError: false }),
|
|
1884
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1885
|
+
)
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
for (const c of listDirCalls) {
|
|
1889
|
+
const args = c.arguments ?? {};
|
|
1890
|
+
allPromises.push(
|
|
1891
|
+
toolListDirectory(provider, args).then(
|
|
1892
|
+
(p) => formatAgentToolOutput("list_directory", args, p, { isError: false }),
|
|
1893
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1705
1894
|
)
|
|
1706
1895
|
);
|
|
1707
1896
|
}
|
|
1708
1897
|
for (const c of readCalls) {
|
|
1709
1898
|
const args = c.arguments ?? {};
|
|
1710
|
-
|
|
1899
|
+
allPromises.push(
|
|
1711
1900
|
toolRead(provider, args).then(
|
|
1712
1901
|
(p) => formatAgentToolOutput("read", args, p, { isError: false }),
|
|
1713
1902
|
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1714
1903
|
)
|
|
1715
1904
|
);
|
|
1716
1905
|
}
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const args = c.arguments ?? {};
|
|
1721
|
-
try {
|
|
1722
|
-
const grepRes = await provider.grep({ pattern: args.pattern, path: args.path });
|
|
1723
|
-
if (grepRes.error) {
|
|
1724
|
-
errors.push({ message: grepRes.error });
|
|
1725
|
-
terminationReason = "terminated";
|
|
1726
|
-
return {
|
|
1727
|
-
terminationReason: "terminated",
|
|
1728
|
-
messages,
|
|
1729
|
-
errors
|
|
1730
|
-
};
|
|
1731
|
-
}
|
|
1732
|
-
const rawOutput = Array.isArray(grepRes.lines) ? grepRes.lines.join("\n") : "";
|
|
1733
|
-
const newMatches = parseAndFilterGrepOutput(rawOutput, grepState);
|
|
1734
|
-
let formattedPayload = formatTurnGrepOutput(newMatches);
|
|
1735
|
-
if (formattedPayload === "No new matches found.") {
|
|
1736
|
-
formattedPayload = "no new matches";
|
|
1737
|
-
}
|
|
1738
|
-
formatted.push(formatAgentToolOutput("grep", args, formattedPayload, { isError: false }));
|
|
1739
|
-
} catch (err) {
|
|
1740
|
-
formatted.push(formatAgentToolOutput("grep", args, String(err), { isError: true }));
|
|
1741
|
-
}
|
|
1906
|
+
const allResults = await Promise.all(allPromises);
|
|
1907
|
+
for (const result of allResults) {
|
|
1908
|
+
formatted.push(result);
|
|
1742
1909
|
}
|
|
1743
1910
|
if (formatted.length > 0) {
|
|
1744
|
-
const
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
if (turnsRemaining === 0) {
|
|
1748
|
-
turnMessage = `
|
|
1749
|
-
|
|
1750
|
-
[Turn ${turnsUsed}/4] This is your LAST turn. You MUST call the finish tool now.`;
|
|
1751
|
-
} else if (turnsRemaining === 1) {
|
|
1752
|
-
turnMessage = `
|
|
1753
|
-
|
|
1754
|
-
[Turn ${turnsUsed}/4] You have 1 turn remaining. Next turn you MUST call the finish tool.`;
|
|
1755
|
-
} else {
|
|
1756
|
-
turnMessage = `
|
|
1757
|
-
|
|
1758
|
-
[Turn ${turnsUsed}/4] You have ${turnsRemaining} turns remaining.`;
|
|
1759
|
-
}
|
|
1760
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage });
|
|
1911
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1912
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1913
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1761
1914
|
}
|
|
1762
1915
|
if (finishCalls.length) {
|
|
1763
1916
|
const fc = finishCalls[0];
|
|
@@ -1807,7 +1960,7 @@ async function runWarpGrep(config) {
|
|
|
1807
1960
|
|
|
1808
1961
|
// tools/warp_grep/providers/local.ts
|
|
1809
1962
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
1810
|
-
var
|
|
1963
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
1811
1964
|
|
|
1812
1965
|
// tools/warp_grep/utils/ripgrep.ts
|
|
1813
1966
|
var import_child_process = require("child_process");
|
|
@@ -1872,21 +2025,21 @@ async function runRipgrep(args, opts) {
|
|
|
1872
2025
|
|
|
1873
2026
|
// tools/warp_grep/utils/paths.ts
|
|
1874
2027
|
var import_fs = __toESM(require("fs"), 1);
|
|
1875
|
-
var
|
|
2028
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
1876
2029
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
1877
|
-
const absRoot =
|
|
1878
|
-
const resolved =
|
|
2030
|
+
const absRoot = import_path4.default.resolve(repoRoot);
|
|
2031
|
+
const resolved = import_path4.default.resolve(absRoot, targetPath);
|
|
1879
2032
|
ensureWithinRepo(absRoot, resolved);
|
|
1880
2033
|
return resolved;
|
|
1881
2034
|
}
|
|
1882
2035
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
1883
|
-
const rel =
|
|
1884
|
-
if (rel.startsWith("..") ||
|
|
2036
|
+
const rel = import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absTarget));
|
|
2037
|
+
if (rel.startsWith("..") || import_path4.default.isAbsolute(rel)) {
|
|
1885
2038
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
1886
2039
|
}
|
|
1887
2040
|
}
|
|
1888
2041
|
function toRepoRelative(repoRoot, absPath) {
|
|
1889
|
-
return
|
|
2042
|
+
return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
|
|
1890
2043
|
}
|
|
1891
2044
|
function isSymlink(p) {
|
|
1892
2045
|
try {
|
|
@@ -1929,10 +2082,18 @@ var LocalRipgrepProvider = class {
|
|
|
1929
2082
|
this.excludes = excludes;
|
|
1930
2083
|
}
|
|
1931
2084
|
async grep(params) {
|
|
1932
|
-
|
|
2085
|
+
let abs;
|
|
2086
|
+
try {
|
|
2087
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
2088
|
+
} catch (err) {
|
|
2089
|
+
return {
|
|
2090
|
+
lines: [],
|
|
2091
|
+
error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
1933
2094
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
1934
2095
|
if (!stat) return { lines: [] };
|
|
1935
|
-
const targetArg = abs ===
|
|
2096
|
+
const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
1936
2097
|
const args = [
|
|
1937
2098
|
"--no-config",
|
|
1938
2099
|
"--no-heading",
|
|
@@ -1941,6 +2102,9 @@ var LocalRipgrepProvider = class {
|
|
|
1941
2102
|
"--color=never",
|
|
1942
2103
|
"--trim",
|
|
1943
2104
|
"--max-columns=400",
|
|
2105
|
+
"-C",
|
|
2106
|
+
"1",
|
|
2107
|
+
...params.glob ? ["--glob", params.glob] : [],
|
|
1944
2108
|
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1945
2109
|
params.pattern,
|
|
1946
2110
|
targetArg || "."
|
|
@@ -1965,29 +2129,24 @@ Details: ${res.stderr}` : ""}`
|
|
|
1965
2129
|
};
|
|
1966
2130
|
}
|
|
1967
2131
|
const lines = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
const args = [
|
|
1974
|
-
"--no-config",
|
|
1975
|
-
"--files",
|
|
1976
|
-
"-g",
|
|
1977
|
-
params.pattern,
|
|
1978
|
-
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1979
|
-
targetArg || "."
|
|
1980
|
-
];
|
|
1981
|
-
const res = await runRipgrep(args, { cwd: this.repoRoot });
|
|
1982
|
-
if (res.exitCode === -1) {
|
|
1983
|
-
console.warn(`[warp_grep] ripgrep not available for glob: ${res.stderr || "execution failed"}`);
|
|
1984
|
-
return { files: [] };
|
|
2132
|
+
if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
2133
|
+
return {
|
|
2134
|
+
lines: [],
|
|
2135
|
+
error: "query not specific enough, tool tried to return too much context and failed"
|
|
2136
|
+
};
|
|
1985
2137
|
}
|
|
1986
|
-
|
|
1987
|
-
return { files };
|
|
2138
|
+
return { lines };
|
|
1988
2139
|
}
|
|
1989
2140
|
async read(params) {
|
|
1990
|
-
|
|
2141
|
+
let abs;
|
|
2142
|
+
try {
|
|
2143
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
2144
|
+
} catch (err) {
|
|
2145
|
+
return {
|
|
2146
|
+
lines: [],
|
|
2147
|
+
error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
1991
2150
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
1992
2151
|
if (!stat || !stat.isFile()) {
|
|
1993
2152
|
return {
|
|
@@ -2007,7 +2166,15 @@ Details: ${res.stderr}` : ""}`
|
|
|
2007
2166
|
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.`
|
|
2008
2167
|
};
|
|
2009
2168
|
}
|
|
2010
|
-
|
|
2169
|
+
let lines;
|
|
2170
|
+
try {
|
|
2171
|
+
lines = await readAllLines(abs);
|
|
2172
|
+
} catch (err) {
|
|
2173
|
+
return {
|
|
2174
|
+
lines: [],
|
|
2175
|
+
error: `[READ ERROR] Failed to read "${params.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2011
2178
|
const total = lines.length;
|
|
2012
2179
|
let s = params.start ?? 1;
|
|
2013
2180
|
let e = Math.min(params.end ?? total, total);
|
|
@@ -2020,30 +2187,46 @@ Details: ${res.stderr}` : ""}`
|
|
|
2020
2187
|
const content = lines[i - 1] ?? "";
|
|
2021
2188
|
out.push(`${i}|${content}`);
|
|
2022
2189
|
}
|
|
2190
|
+
if (out.length > AGENT_CONFIG.MAX_READ_LINES) {
|
|
2191
|
+
const truncated = out.slice(0, AGENT_CONFIG.MAX_READ_LINES);
|
|
2192
|
+
truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${out.length} lines]`);
|
|
2193
|
+
return { lines: truncated };
|
|
2194
|
+
}
|
|
2023
2195
|
return { lines: out };
|
|
2024
2196
|
}
|
|
2025
|
-
async
|
|
2026
|
-
|
|
2197
|
+
async listDirectory(params) {
|
|
2198
|
+
let abs;
|
|
2199
|
+
try {
|
|
2200
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
2201
|
+
} catch {
|
|
2202
|
+
return [];
|
|
2203
|
+
}
|
|
2027
2204
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
2028
2205
|
if (!stat || !stat.isDirectory()) {
|
|
2029
2206
|
return [];
|
|
2030
2207
|
}
|
|
2031
|
-
const maxResults = params.maxResults ??
|
|
2032
|
-
const maxDepth = params.maxDepth ??
|
|
2208
|
+
const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
2209
|
+
const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
2033
2210
|
const regex = params.pattern ? new RegExp(params.pattern) : null;
|
|
2034
2211
|
const results = [];
|
|
2212
|
+
let timedOut = false;
|
|
2213
|
+
const startTime = Date.now();
|
|
2035
2214
|
async function walk(dir, depth) {
|
|
2215
|
+
if (Date.now() - startTime > AGENT_CONFIG.LIST_TIMEOUT_MS) {
|
|
2216
|
+
timedOut = true;
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2036
2219
|
if (depth > maxDepth || results.length >= maxResults) return;
|
|
2037
2220
|
const entries = await import_promises3.default.readdir(dir, { withFileTypes: true });
|
|
2038
2221
|
for (const entry of entries) {
|
|
2039
|
-
|
|
2222
|
+
if (timedOut || results.length >= maxResults) break;
|
|
2223
|
+
const full = import_path5.default.join(dir, entry.name);
|
|
2040
2224
|
const rel = toRepoRelative(abs, full).replace(/^[.][/\\]?/, "");
|
|
2041
|
-
if (DEFAULT_EXCLUDES.some((ex) => rel.split(
|
|
2225
|
+
if (DEFAULT_EXCLUDES.some((ex) => rel.split(import_path5.default.sep).includes(ex))) continue;
|
|
2042
2226
|
if (regex && !regex.test(entry.name)) continue;
|
|
2043
|
-
if (results.length >= maxResults) break;
|
|
2044
2227
|
results.push({
|
|
2045
2228
|
name: entry.name,
|
|
2046
|
-
path: toRepoRelative(
|
|
2229
|
+
path: toRepoRelative(import_path5.default.resolve(""), full),
|
|
2047
2230
|
// relative display
|
|
2048
2231
|
type: entry.isDirectory() ? "dir" : "file",
|
|
2049
2232
|
depth
|
|
@@ -2058,12 +2241,103 @@ Details: ${res.stderr}` : ""}`
|
|
|
2058
2241
|
}
|
|
2059
2242
|
};
|
|
2060
2243
|
|
|
2061
|
-
// tools/warp_grep/
|
|
2244
|
+
// tools/warp_grep/providers/remote.ts
|
|
2245
|
+
var RemoteCommandsProvider = class {
|
|
2246
|
+
constructor(repoRoot, commands) {
|
|
2247
|
+
this.repoRoot = repoRoot;
|
|
2248
|
+
this.commands = commands;
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* Run grep command and parse ripgrep output
|
|
2252
|
+
*/
|
|
2253
|
+
async grep(params) {
|
|
2254
|
+
try {
|
|
2255
|
+
const stdout = await this.commands.grep(params.pattern, params.path, params.glob);
|
|
2256
|
+
const lines = (stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
2257
|
+
if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
2258
|
+
return {
|
|
2259
|
+
lines: [],
|
|
2260
|
+
error: "Query not specific enough - too many results returned. Try a more specific pattern."
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
return { lines };
|
|
2264
|
+
} catch (error) {
|
|
2265
|
+
return {
|
|
2266
|
+
lines: [],
|
|
2267
|
+
error: `[GREP ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* Read file and add line numbers
|
|
2273
|
+
*/
|
|
2274
|
+
async read(params) {
|
|
2275
|
+
const start = params.start ?? 1;
|
|
2276
|
+
const end = params.end ?? 1e6;
|
|
2277
|
+
try {
|
|
2278
|
+
const stdout = await this.commands.read(params.path, start, end);
|
|
2279
|
+
const contentLines = (stdout || "").split("\n");
|
|
2280
|
+
if (contentLines.length > 0 && contentLines[contentLines.length - 1] === "") {
|
|
2281
|
+
contentLines.pop();
|
|
2282
|
+
}
|
|
2283
|
+
const lines = contentLines.map((content, idx) => `${start + idx}|${content}`);
|
|
2284
|
+
if (lines.length > AGENT_CONFIG.MAX_READ_LINES) {
|
|
2285
|
+
const truncated = lines.slice(0, AGENT_CONFIG.MAX_READ_LINES);
|
|
2286
|
+
truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${lines.length} lines]`);
|
|
2287
|
+
return { lines: truncated };
|
|
2288
|
+
}
|
|
2289
|
+
return { lines };
|
|
2290
|
+
} catch (error) {
|
|
2291
|
+
return {
|
|
2292
|
+
lines: [],
|
|
2293
|
+
error: `[READ ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* List directory and parse find output
|
|
2299
|
+
*/
|
|
2300
|
+
async listDirectory(params) {
|
|
2301
|
+
const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
2302
|
+
const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
2303
|
+
try {
|
|
2304
|
+
const stdout = await this.commands.listDir(params.path, maxDepth);
|
|
2305
|
+
const paths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
|
|
2306
|
+
const regex = params.pattern ? new RegExp(params.pattern) : null;
|
|
2307
|
+
const entries = [];
|
|
2308
|
+
for (const fullPath of paths) {
|
|
2309
|
+
if (fullPath === params.path || fullPath === this.repoRoot) continue;
|
|
2310
|
+
const name = fullPath.split("/").pop() || "";
|
|
2311
|
+
if (regex && !regex.test(name)) continue;
|
|
2312
|
+
let relativePath = fullPath;
|
|
2313
|
+
if (fullPath.startsWith(this.repoRoot)) {
|
|
2314
|
+
relativePath = fullPath.slice(this.repoRoot.length).replace(/^\//, "");
|
|
2315
|
+
}
|
|
2316
|
+
const depth = relativePath.split("/").filter(Boolean).length - 1;
|
|
2317
|
+
const hasExtension = name.includes(".") && !name.startsWith(".");
|
|
2318
|
+
const type = hasExtension ? "file" : "dir";
|
|
2319
|
+
entries.push({
|
|
2320
|
+
name,
|
|
2321
|
+
path: relativePath,
|
|
2322
|
+
type,
|
|
2323
|
+
depth: Math.max(0, depth)
|
|
2324
|
+
});
|
|
2325
|
+
if (entries.length >= maxResults) break;
|
|
2326
|
+
}
|
|
2327
|
+
return entries;
|
|
2328
|
+
} catch (error) {
|
|
2329
|
+
return [];
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
};
|
|
2333
|
+
|
|
2334
|
+
// tools/warp_grep/client.ts
|
|
2062
2335
|
var WarpGrepClient = class {
|
|
2063
2336
|
config;
|
|
2064
2337
|
constructor(config = {}) {
|
|
2065
2338
|
this.config = {
|
|
2066
|
-
|
|
2339
|
+
morphApiKey: config.morphApiKey,
|
|
2340
|
+
morphApiUrl: config.morphApiUrl,
|
|
2067
2341
|
debug: config.debug,
|
|
2068
2342
|
timeout: config.timeout,
|
|
2069
2343
|
retryConfig: config.retryConfig
|
|
@@ -2091,34 +2365,46 @@ var WarpGrepClient = class {
|
|
|
2091
2365
|
* ```
|
|
2092
2366
|
*/
|
|
2093
2367
|
async execute(input) {
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
error: "Search did not complete"
|
|
2109
|
-
};
|
|
2110
|
-
}
|
|
2111
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
2112
|
-
file: r.path,
|
|
2113
|
-
content: r.content
|
|
2114
|
-
}));
|
|
2115
|
-
return {
|
|
2116
|
-
success: true,
|
|
2117
|
-
contexts,
|
|
2118
|
-
summary: finish.payload
|
|
2119
|
-
};
|
|
2368
|
+
return executeToolCall(
|
|
2369
|
+
{ query: input.query },
|
|
2370
|
+
{
|
|
2371
|
+
repoRoot: input.repoRoot,
|
|
2372
|
+
remoteCommands: input.remoteCommands,
|
|
2373
|
+
provider: input.provider,
|
|
2374
|
+
excludes: input.excludes,
|
|
2375
|
+
includes: input.includes,
|
|
2376
|
+
debug: input.debug ?? this.config.debug,
|
|
2377
|
+
morphApiKey: this.config.morphApiKey,
|
|
2378
|
+
morphApiUrl: this.config.morphApiUrl,
|
|
2379
|
+
retryConfig: this.config.retryConfig
|
|
2380
|
+
}
|
|
2381
|
+
);
|
|
2120
2382
|
}
|
|
2121
2383
|
};
|
|
2384
|
+
async function executeToolCall(input, config) {
|
|
2385
|
+
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
2386
|
+
const provider = config.remoteCommands ? new RemoteCommandsProvider(config.repoRoot, config.remoteCommands) : config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
2387
|
+
const result = await runWarpGrep({
|
|
2388
|
+
query: parsed.query,
|
|
2389
|
+
repoRoot: config.repoRoot,
|
|
2390
|
+
provider,
|
|
2391
|
+
excludes: config.excludes,
|
|
2392
|
+
includes: config.includes,
|
|
2393
|
+
debug: config.debug ?? false,
|
|
2394
|
+
morphApiKey: config.morphApiKey,
|
|
2395
|
+
morphApiUrl: config.morphApiUrl,
|
|
2396
|
+
retryConfig: config.retryConfig
|
|
2397
|
+
});
|
|
2398
|
+
const finish = result.finish;
|
|
2399
|
+
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
2400
|
+
return { success: false, error: "Search did not complete" };
|
|
2401
|
+
}
|
|
2402
|
+
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
2403
|
+
file: r.path,
|
|
2404
|
+
content: r.content
|
|
2405
|
+
}));
|
|
2406
|
+
return { success: true, contexts, summary: finish.payload };
|
|
2407
|
+
}
|
|
2122
2408
|
function formatResult(result) {
|
|
2123
2409
|
if (!result.success) {
|
|
2124
2410
|
return `Search failed: ${result.error}`;
|
|
@@ -2898,29 +3184,7 @@ var TOOL_PARAMETERS = {
|
|
|
2898
3184
|
},
|
|
2899
3185
|
required: ["query"]
|
|
2900
3186
|
};
|
|
2901
|
-
|
|
2902
|
-
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
2903
|
-
const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
2904
|
-
const result = await runWarpGrep({
|
|
2905
|
-
query: parsed.query,
|
|
2906
|
-
repoRoot: config.repoRoot,
|
|
2907
|
-
provider,
|
|
2908
|
-
excludes: config.excludes,
|
|
2909
|
-
includes: config.includes,
|
|
2910
|
-
debug: config.debug ?? false,
|
|
2911
|
-
apiKey: config.apiKey
|
|
2912
|
-
});
|
|
2913
|
-
const finish = result.finish;
|
|
2914
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
2915
|
-
return { success: false, error: "Search did not complete" };
|
|
2916
|
-
}
|
|
2917
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
2918
|
-
file: r.path,
|
|
2919
|
-
content: r.content
|
|
2920
|
-
}));
|
|
2921
|
-
return { success: true, contexts, summary: finish.payload };
|
|
2922
|
-
}
|
|
2923
|
-
function createMorphWarpGrepTool(config) {
|
|
3187
|
+
function createWarpGrepTool(config) {
|
|
2924
3188
|
const tool4 = {
|
|
2925
3189
|
type: "function",
|
|
2926
3190
|
function: {
|
|
@@ -2931,7 +3195,7 @@ function createMorphWarpGrepTool(config) {
|
|
|
2931
3195
|
};
|
|
2932
3196
|
return Object.assign(tool4, {
|
|
2933
3197
|
execute: async (input) => {
|
|
2934
|
-
return
|
|
3198
|
+
return executeToolCall(input, config);
|
|
2935
3199
|
},
|
|
2936
3200
|
formatResult: (result) => {
|
|
2937
3201
|
return formatResult(result);
|
|
@@ -3107,7 +3371,7 @@ var editFileTool = {
|
|
|
3107
3371
|
}
|
|
3108
3372
|
}
|
|
3109
3373
|
};
|
|
3110
|
-
async function
|
|
3374
|
+
async function execute(input, config) {
|
|
3111
3375
|
return executeEditFile(input, config);
|
|
3112
3376
|
}
|
|
3113
3377
|
function getSystemPrompt2() {
|
|
@@ -3145,7 +3409,7 @@ function createEditFileTool(config = {}) {
|
|
|
3145
3409
|
return Object.assign({}, toolDef, {
|
|
3146
3410
|
execute: async (input) => {
|
|
3147
3411
|
const parsedInput = typeof input === "string" ? JSON.parse(input) : input;
|
|
3148
|
-
return
|
|
3412
|
+
return execute(parsedInput, config);
|
|
3149
3413
|
},
|
|
3150
3414
|
formatResult: (result) => {
|
|
3151
3415
|
return formatResult3(result);
|
|
@@ -3164,13 +3428,13 @@ var OpenAIToolFactory = class {
|
|
|
3164
3428
|
/**
|
|
3165
3429
|
* Create an OpenAI-compatible warp grep tool
|
|
3166
3430
|
*
|
|
3167
|
-
* @param toolConfig - Tool configuration (
|
|
3431
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3168
3432
|
* @returns OpenAI ChatCompletionTool with execute and formatResult methods
|
|
3169
3433
|
*/
|
|
3170
3434
|
createWarpGrepTool(toolConfig) {
|
|
3171
|
-
return
|
|
3435
|
+
return createWarpGrepTool({
|
|
3172
3436
|
...toolConfig,
|
|
3173
|
-
|
|
3437
|
+
morphApiKey: this.config.apiKey
|
|
3174
3438
|
});
|
|
3175
3439
|
}
|
|
3176
3440
|
/**
|
|
@@ -3207,29 +3471,7 @@ var INPUT_SCHEMA = {
|
|
|
3207
3471
|
},
|
|
3208
3472
|
required: ["query"]
|
|
3209
3473
|
};
|
|
3210
|
-
|
|
3211
|
-
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
3212
|
-
const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
3213
|
-
const result = await runWarpGrep({
|
|
3214
|
-
query: parsed.query,
|
|
3215
|
-
repoRoot: config.repoRoot,
|
|
3216
|
-
provider,
|
|
3217
|
-
excludes: config.excludes,
|
|
3218
|
-
includes: config.includes,
|
|
3219
|
-
debug: config.debug ?? false,
|
|
3220
|
-
apiKey: config.apiKey
|
|
3221
|
-
});
|
|
3222
|
-
const finish = result.finish;
|
|
3223
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
3224
|
-
return { success: false, error: "Search did not complete" };
|
|
3225
|
-
}
|
|
3226
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
3227
|
-
file: r.path,
|
|
3228
|
-
content: r.content
|
|
3229
|
-
}));
|
|
3230
|
-
return { success: true, contexts, summary: finish.payload };
|
|
3231
|
-
}
|
|
3232
|
-
function createMorphWarpGrepTool2(config) {
|
|
3474
|
+
function createWarpGrepTool2(config) {
|
|
3233
3475
|
const tool4 = {
|
|
3234
3476
|
name: config.name ?? WARP_GREP_TOOL_NAME,
|
|
3235
3477
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
@@ -3237,7 +3479,7 @@ function createMorphWarpGrepTool2(config) {
|
|
|
3237
3479
|
};
|
|
3238
3480
|
return Object.assign(tool4, {
|
|
3239
3481
|
execute: async (input) => {
|
|
3240
|
-
return
|
|
3482
|
+
return executeToolCall(input, config);
|
|
3241
3483
|
},
|
|
3242
3484
|
formatResult: (result) => {
|
|
3243
3485
|
return formatResult(result);
|
|
@@ -3387,13 +3629,13 @@ var AnthropicToolFactory = class {
|
|
|
3387
3629
|
/**
|
|
3388
3630
|
* Create an Anthropic-compatible warp grep tool
|
|
3389
3631
|
*
|
|
3390
|
-
* @param toolConfig - Tool configuration (
|
|
3632
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3391
3633
|
* @returns Anthropic Tool with execute and formatResult methods
|
|
3392
3634
|
*/
|
|
3393
3635
|
createWarpGrepTool(toolConfig) {
|
|
3394
|
-
return
|
|
3636
|
+
return createWarpGrepTool2({
|
|
3395
3637
|
...toolConfig,
|
|
3396
|
-
|
|
3638
|
+
morphApiKey: this.config.apiKey
|
|
3397
3639
|
});
|
|
3398
3640
|
}
|
|
3399
3641
|
/**
|
|
@@ -3425,33 +3667,23 @@ var AnthropicToolFactory = class {
|
|
|
3425
3667
|
// tools/warp_grep/vercel.ts
|
|
3426
3668
|
var import_ai = require("ai");
|
|
3427
3669
|
var import_zod = require("zod");
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3670
|
+
function createWarpGrepTool3(config) {
|
|
3671
|
+
const schema = import_zod.z.object({
|
|
3672
|
+
query: import_zod.z.string().describe("Free-form repository question")
|
|
3673
|
+
});
|
|
3432
3674
|
return (0, import_ai.tool)({
|
|
3433
3675
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
3434
|
-
inputSchema:
|
|
3676
|
+
inputSchema: schema,
|
|
3435
3677
|
execute: async (params) => {
|
|
3436
|
-
const
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
repoRoot: config.repoRoot,
|
|
3440
|
-
provider,
|
|
3441
|
-
excludes: config.excludes,
|
|
3442
|
-
includes: config.includes,
|
|
3443
|
-
debug: config.debug ?? false,
|
|
3444
|
-
apiKey: config.apiKey
|
|
3445
|
-
});
|
|
3446
|
-
const finish = result.finish;
|
|
3447
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
3448
|
-
return { success: false, error: "Search did not complete" };
|
|
3678
|
+
const result = await executeToolCall(params, config);
|
|
3679
|
+
if (!result.success) {
|
|
3680
|
+
throw new Error(`Failed to search codebase: ${result.error}`);
|
|
3449
3681
|
}
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3682
|
+
return {
|
|
3683
|
+
success: true,
|
|
3684
|
+
contexts: result.contexts,
|
|
3685
|
+
summary: result.summary
|
|
3686
|
+
};
|
|
3455
3687
|
}
|
|
3456
3688
|
});
|
|
3457
3689
|
}
|
|
@@ -3564,13 +3796,13 @@ var VercelToolFactory = class {
|
|
|
3564
3796
|
/**
|
|
3565
3797
|
* Create a Vercel AI SDK-compatible warp grep tool
|
|
3566
3798
|
*
|
|
3567
|
-
* @param toolConfig - Tool configuration (
|
|
3799
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3568
3800
|
* @returns Vercel AI SDK tool
|
|
3569
3801
|
*/
|
|
3570
3802
|
createWarpGrepTool(toolConfig) {
|
|
3571
|
-
return
|
|
3803
|
+
return createWarpGrepTool3({
|
|
3572
3804
|
...toolConfig,
|
|
3573
|
-
|
|
3805
|
+
morphApiKey: this.config.apiKey
|
|
3574
3806
|
});
|
|
3575
3807
|
}
|
|
3576
3808
|
/**
|
|
@@ -3650,7 +3882,7 @@ var MorphClient = class {
|
|
|
3650
3882
|
retryConfig: config.retryConfig
|
|
3651
3883
|
});
|
|
3652
3884
|
this.warpGrep = new WarpGrepClient({
|
|
3653
|
-
|
|
3885
|
+
morphApiKey: config.apiKey,
|
|
3654
3886
|
debug: config.debug,
|
|
3655
3887
|
timeout: config.timeout,
|
|
3656
3888
|
retryConfig: config.retryConfig
|
|
@@ -3696,6 +3928,19 @@ var MorphClient = class {
|
|
|
3696
3928
|
this.vercel = new VercelToolFactory(config);
|
|
3697
3929
|
}
|
|
3698
3930
|
};
|
|
3931
|
+
|
|
3932
|
+
// tools/warp_grep/gemini.ts
|
|
3933
|
+
var import_generative_ai = require("@google/generative-ai");
|
|
3934
|
+
var TOOL_PARAMETERS2 = {
|
|
3935
|
+
type: import_generative_ai.SchemaType.OBJECT,
|
|
3936
|
+
properties: {
|
|
3937
|
+
query: {
|
|
3938
|
+
type: import_generative_ai.SchemaType.STRING,
|
|
3939
|
+
description: "Free-form repository question"
|
|
3940
|
+
}
|
|
3941
|
+
},
|
|
3942
|
+
required: ["query"]
|
|
3943
|
+
};
|
|
3699
3944
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3700
3945
|
0 && (module.exports = {
|
|
3701
3946
|
AnthropicRouter,
|