@morphllm/morphsdk 0.2.57 → 0.2.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anthropic-CaFUHxBW.d.ts +89 -0
- package/dist/{chunk-6X5UOY7B.js → chunk-2CASO3ZO.js} +46 -79
- 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-QFIHUCTF.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 +687 -341
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +3 -2
- package/dist/client.js +14 -14
- package/dist/finish-kXAcUJyB.d.ts +33 -0
- package/dist/gemini-CE80Pbdy.d.ts +117 -0
- package/dist/index.cjs +700 -341
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +16 -15
- 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 +459 -192
- 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 -8
- 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 +650 -260
- 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 -15
- 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 +556 -220
- package/dist/tools/warp_grep/harness.cjs.map +1 -1
- package/dist/tools/warp_grep/harness.d.ts +50 -119
- package/dist/tools/warp_grep/harness.js +33 -41
- package/dist/tools/warp_grep/harness.js.map +1 -1
- package/dist/tools/warp_grep/index.cjs +812 -346
- 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 -22
- package/dist/tools/warp_grep/openai.cjs +650 -258
- 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 -13
- 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 +662 -277
- 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 -14
- package/dist/types-a_hxdPI6.d.ts +144 -0
- package/dist/vercel-3yjvfmVB.d.ts +66 -0
- package/package.json +12 -2
- package/dist/chunk-6X5UOY7B.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-CFF636UC.js +0 -70
- package/dist/chunk-CFF636UC.js.map +0 -1
- package/dist/chunk-EK7OQPWD.js +0 -44
- package/dist/chunk-EK7OQPWD.js.map +0 -1
- package/dist/chunk-GJ5TYNRD.js +0 -107
- package/dist/chunk-GJ5TYNRD.js.map +0 -1
- package/dist/chunk-HQO45BAJ.js +0 -14
- package/dist/chunk-HQO45BAJ.js.map +0 -1
- package/dist/chunk-IMYQOKFO.js +0 -83
- package/dist/chunk-IMYQOKFO.js.map +0 -1
- package/dist/chunk-KBQWGT5L.js +0 -77
- package/dist/chunk-KBQWGT5L.js.map +0 -1
- package/dist/chunk-LVPVVLTI.js.map +0 -1
- package/dist/chunk-QFIHUCTF.js.map +0 -1
- package/dist/chunk-TICMYDII.js.map +0 -1
- package/dist/chunk-TJIUA27P.js.map +0 -1
- package/dist/chunk-WETRQJGU.js +0 -129
- package/dist/chunk-WETRQJGU.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/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/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/{tools/analyse.js.map → client.js.map} +0 -0
- /package/dist/tools/warp_grep/{tools/finish.js.map → gemini.js.map} +0 -0
- /package/dist/tools/warp_grep/{tools/read.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,66 +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/
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
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>`;
|
|
1736
|
+
}
|
|
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>`;
|
|
1474
1768
|
}
|
|
1475
|
-
return out;
|
|
1476
1769
|
}
|
|
1477
|
-
function
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
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);
|
|
1490
1784
|
}
|
|
1491
1785
|
}
|
|
1492
|
-
|
|
1493
|
-
|
|
1786
|
+
for (const idx of userIndices) {
|
|
1787
|
+
if (getTotalChars() <= maxChars) {
|
|
1788
|
+
break;
|
|
1789
|
+
}
|
|
1790
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
1791
|
+
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
return messages;
|
|
1494
1795
|
}
|
|
1495
1796
|
|
|
1496
1797
|
// tools/warp_grep/agent/runner.ts
|
|
1497
|
-
var
|
|
1798
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
1498
1799
|
var parser = new LLMResponseParser();
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
const files = entries.filter((e) => e.type === "file").map((f) => f.name).slice(0, 50);
|
|
1504
|
-
const parts = [
|
|
1505
|
-
`<repo_root>${repoRoot}</repo_root>`,
|
|
1506
|
-
`<top_dirs>${dirs.join(", ")}</top_dirs>`,
|
|
1507
|
-
`<top_files>${files.join(", ")}</top_files>`
|
|
1508
|
-
];
|
|
1509
|
-
return parts.join("\n");
|
|
1510
|
-
} catch {
|
|
1511
|
-
return `<repo_root>${repoRoot}</repo_root>`;
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
async function callModel(messages, model, apiKey) {
|
|
1515
|
-
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 || "";
|
|
1516
1804
|
const fetchPromise = fetchWithRetry(
|
|
1517
|
-
|
|
1805
|
+
`${baseUrl}/v1/chat/completions`,
|
|
1518
1806
|
{
|
|
1519
1807
|
method: "POST",
|
|
1520
1808
|
headers: {
|
|
1521
1809
|
"Content-Type": "application/json",
|
|
1522
|
-
Authorization: `Bearer ${apiKey
|
|
1810
|
+
Authorization: `Bearer ${apiKey}`
|
|
1523
1811
|
},
|
|
1524
1812
|
body: JSON.stringify({
|
|
1525
1813
|
model,
|
|
@@ -1528,10 +1816,15 @@ async function callModel(messages, model, apiKey) {
|
|
|
1528
1816
|
messages
|
|
1529
1817
|
})
|
|
1530
1818
|
},
|
|
1531
|
-
|
|
1819
|
+
options.retryConfig
|
|
1532
1820
|
);
|
|
1533
1821
|
const resp = await withTimeout(fetchPromise, AGENT_CONFIG.TIMEOUT_MS, "morph-warp-grep request timed out");
|
|
1534
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
|
+
}
|
|
1535
1828
|
const t = await resp.text();
|
|
1536
1829
|
throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
|
|
1537
1830
|
}
|
|
@@ -1543,22 +1836,24 @@ async function callModel(messages, model, apiKey) {
|
|
|
1543
1836
|
return content;
|
|
1544
1837
|
}
|
|
1545
1838
|
async function runWarpGrep(config) {
|
|
1546
|
-
const repoRoot =
|
|
1839
|
+
const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
|
|
1547
1840
|
const messages = [];
|
|
1548
|
-
|
|
1549
|
-
messages.push(systemMessage);
|
|
1550
|
-
const queryContent = `<query>${config.query}</query>`;
|
|
1551
|
-
messages.push({ role: "user", content: queryContent });
|
|
1841
|
+
messages.push({ role: "system", content: getSystemPrompt() });
|
|
1552
1842
|
const initialState = await buildInitialState(repoRoot, config.query, config.provider);
|
|
1553
1843
|
messages.push({ role: "user", content: initialState });
|
|
1554
|
-
const
|
|
1844
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
1555
1845
|
const model = config.model || DEFAULT_MODEL;
|
|
1556
1846
|
const provider = config.provider;
|
|
1557
1847
|
const errors = [];
|
|
1558
1848
|
let finishMeta;
|
|
1559
1849
|
let terminationReason = "terminated";
|
|
1560
|
-
for (let
|
|
1561
|
-
|
|
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) => {
|
|
1562
1857
|
errors.push({ message: e instanceof Error ? e.message : String(e) });
|
|
1563
1858
|
return "";
|
|
1564
1859
|
});
|
|
@@ -1566,13 +1861,13 @@ async function runWarpGrep(config) {
|
|
|
1566
1861
|
messages.push({ role: "assistant", content: assistantContent });
|
|
1567
1862
|
const toolCalls = parser.parse(assistantContent);
|
|
1568
1863
|
if (toolCalls.length === 0) {
|
|
1569
|
-
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" });
|
|
1570
1865
|
terminationReason = "terminated";
|
|
1571
1866
|
break;
|
|
1572
1867
|
}
|
|
1573
1868
|
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1574
1869
|
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1575
|
-
const
|
|
1870
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1576
1871
|
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1577
1872
|
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1578
1873
|
const formatted = [];
|
|
@@ -1584,24 +1879,18 @@ async function runWarpGrep(config) {
|
|
|
1584
1879
|
for (const c of grepCalls) {
|
|
1585
1880
|
const args = c.arguments ?? {};
|
|
1586
1881
|
allPromises.push(
|
|
1587
|
-
provider
|
|
1588
|
-
(
|
|
1589
|
-
if (grepRes.error) {
|
|
1590
|
-
return { terminate: true, error: grepRes.error };
|
|
1591
|
-
}
|
|
1592
|
-
const output = grepRes.lines.join("\n") || "no matches";
|
|
1593
|
-
return formatAgentToolOutput("grep", args, output, { isError: false });
|
|
1594
|
-
},
|
|
1882
|
+
toolGrep(provider, args).then(
|
|
1883
|
+
({ output }) => formatAgentToolOutput("grep", args, output, { isError: false }),
|
|
1595
1884
|
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1596
1885
|
)
|
|
1597
1886
|
);
|
|
1598
1887
|
}
|
|
1599
|
-
for (const c of
|
|
1888
|
+
for (const c of listDirCalls) {
|
|
1600
1889
|
const args = c.arguments ?? {};
|
|
1601
1890
|
allPromises.push(
|
|
1602
|
-
|
|
1603
|
-
(p) => formatAgentToolOutput("
|
|
1604
|
-
(err) => formatAgentToolOutput("
|
|
1891
|
+
toolListDirectory(provider, args).then(
|
|
1892
|
+
(p) => formatAgentToolOutput("list_directory", args, p, { isError: false }),
|
|
1893
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1605
1894
|
)
|
|
1606
1895
|
);
|
|
1607
1896
|
}
|
|
@@ -1616,34 +1905,12 @@ async function runWarpGrep(config) {
|
|
|
1616
1905
|
}
|
|
1617
1906
|
const allResults = await Promise.all(allPromises);
|
|
1618
1907
|
for (const result of allResults) {
|
|
1619
|
-
if (typeof result === "object" && "terminate" in result) {
|
|
1620
|
-
errors.push({ message: result.error });
|
|
1621
|
-
return {
|
|
1622
|
-
terminationReason: "terminated",
|
|
1623
|
-
messages,
|
|
1624
|
-
errors
|
|
1625
|
-
};
|
|
1626
|
-
}
|
|
1627
1908
|
formatted.push(result);
|
|
1628
1909
|
}
|
|
1629
1910
|
if (formatted.length > 0) {
|
|
1630
|
-
const
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
if (turnsRemaining === 0) {
|
|
1634
|
-
turnMessage = `
|
|
1635
|
-
|
|
1636
|
-
[Turn ${turnsUsed}/4] This is your LAST turn. You MUST call the finish tool now.`;
|
|
1637
|
-
} else if (turnsRemaining === 1) {
|
|
1638
|
-
turnMessage = `
|
|
1639
|
-
|
|
1640
|
-
[Turn ${turnsUsed}/4] You have 1 turn remaining. Next turn you MUST call the finish tool.`;
|
|
1641
|
-
} else {
|
|
1642
|
-
turnMessage = `
|
|
1643
|
-
|
|
1644
|
-
[Turn ${turnsUsed}/4] You have ${turnsRemaining} turns remaining.`;
|
|
1645
|
-
}
|
|
1646
|
-
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 });
|
|
1647
1914
|
}
|
|
1648
1915
|
if (finishCalls.length) {
|
|
1649
1916
|
const fc = finishCalls[0];
|
|
@@ -1693,7 +1960,7 @@ async function runWarpGrep(config) {
|
|
|
1693
1960
|
|
|
1694
1961
|
// tools/warp_grep/providers/local.ts
|
|
1695
1962
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
1696
|
-
var
|
|
1963
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
1697
1964
|
|
|
1698
1965
|
// tools/warp_grep/utils/ripgrep.ts
|
|
1699
1966
|
var import_child_process = require("child_process");
|
|
@@ -1758,21 +2025,21 @@ async function runRipgrep(args, opts) {
|
|
|
1758
2025
|
|
|
1759
2026
|
// tools/warp_grep/utils/paths.ts
|
|
1760
2027
|
var import_fs = __toESM(require("fs"), 1);
|
|
1761
|
-
var
|
|
2028
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
1762
2029
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
1763
|
-
const absRoot =
|
|
1764
|
-
const resolved =
|
|
2030
|
+
const absRoot = import_path4.default.resolve(repoRoot);
|
|
2031
|
+
const resolved = import_path4.default.resolve(absRoot, targetPath);
|
|
1765
2032
|
ensureWithinRepo(absRoot, resolved);
|
|
1766
2033
|
return resolved;
|
|
1767
2034
|
}
|
|
1768
2035
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
1769
|
-
const rel =
|
|
1770
|
-
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)) {
|
|
1771
2038
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
1772
2039
|
}
|
|
1773
2040
|
}
|
|
1774
2041
|
function toRepoRelative(repoRoot, absPath) {
|
|
1775
|
-
return
|
|
2042
|
+
return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
|
|
1776
2043
|
}
|
|
1777
2044
|
function isSymlink(p) {
|
|
1778
2045
|
try {
|
|
@@ -1815,10 +2082,18 @@ var LocalRipgrepProvider = class {
|
|
|
1815
2082
|
this.excludes = excludes;
|
|
1816
2083
|
}
|
|
1817
2084
|
async grep(params) {
|
|
1818
|
-
|
|
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
|
+
}
|
|
1819
2094
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
1820
2095
|
if (!stat) return { lines: [] };
|
|
1821
|
-
const targetArg = abs ===
|
|
2096
|
+
const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
1822
2097
|
const args = [
|
|
1823
2098
|
"--no-config",
|
|
1824
2099
|
"--no-heading",
|
|
@@ -1827,6 +2102,9 @@ var LocalRipgrepProvider = class {
|
|
|
1827
2102
|
"--color=never",
|
|
1828
2103
|
"--trim",
|
|
1829
2104
|
"--max-columns=400",
|
|
2105
|
+
"-C",
|
|
2106
|
+
"1",
|
|
2107
|
+
...params.glob ? ["--glob", params.glob] : [],
|
|
1830
2108
|
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1831
2109
|
params.pattern,
|
|
1832
2110
|
targetArg || "."
|
|
@@ -1851,29 +2129,24 @@ Details: ${res.stderr}` : ""}`
|
|
|
1851
2129
|
};
|
|
1852
2130
|
}
|
|
1853
2131
|
const lines = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
const args = [
|
|
1860
|
-
"--no-config",
|
|
1861
|
-
"--files",
|
|
1862
|
-
"-g",
|
|
1863
|
-
params.pattern,
|
|
1864
|
-
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1865
|
-
targetArg || "."
|
|
1866
|
-
];
|
|
1867
|
-
const res = await runRipgrep(args, { cwd: this.repoRoot });
|
|
1868
|
-
if (res.exitCode === -1) {
|
|
1869
|
-
console.warn(`[warp_grep] ripgrep not available for glob: ${res.stderr || "execution failed"}`);
|
|
1870
|
-
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
|
+
};
|
|
1871
2137
|
}
|
|
1872
|
-
|
|
1873
|
-
return { files };
|
|
2138
|
+
return { lines };
|
|
1874
2139
|
}
|
|
1875
2140
|
async read(params) {
|
|
1876
|
-
|
|
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
|
+
}
|
|
1877
2150
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
1878
2151
|
if (!stat || !stat.isFile()) {
|
|
1879
2152
|
return {
|
|
@@ -1893,7 +2166,15 @@ Details: ${res.stderr}` : ""}`
|
|
|
1893
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.`
|
|
1894
2167
|
};
|
|
1895
2168
|
}
|
|
1896
|
-
|
|
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
|
+
}
|
|
1897
2178
|
const total = lines.length;
|
|
1898
2179
|
let s = params.start ?? 1;
|
|
1899
2180
|
let e = Math.min(params.end ?? total, total);
|
|
@@ -1906,30 +2187,46 @@ Details: ${res.stderr}` : ""}`
|
|
|
1906
2187
|
const content = lines[i - 1] ?? "";
|
|
1907
2188
|
out.push(`${i}|${content}`);
|
|
1908
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
|
+
}
|
|
1909
2195
|
return { lines: out };
|
|
1910
2196
|
}
|
|
1911
|
-
async
|
|
1912
|
-
|
|
2197
|
+
async listDirectory(params) {
|
|
2198
|
+
let abs;
|
|
2199
|
+
try {
|
|
2200
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
2201
|
+
} catch {
|
|
2202
|
+
return [];
|
|
2203
|
+
}
|
|
1913
2204
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
1914
2205
|
if (!stat || !stat.isDirectory()) {
|
|
1915
2206
|
return [];
|
|
1916
2207
|
}
|
|
1917
|
-
const maxResults = params.maxResults ??
|
|
1918
|
-
const maxDepth = params.maxDepth ??
|
|
2208
|
+
const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
2209
|
+
const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
1919
2210
|
const regex = params.pattern ? new RegExp(params.pattern) : null;
|
|
1920
2211
|
const results = [];
|
|
2212
|
+
let timedOut = false;
|
|
2213
|
+
const startTime = Date.now();
|
|
1921
2214
|
async function walk(dir, depth) {
|
|
2215
|
+
if (Date.now() - startTime > AGENT_CONFIG.LIST_TIMEOUT_MS) {
|
|
2216
|
+
timedOut = true;
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
1922
2219
|
if (depth > maxDepth || results.length >= maxResults) return;
|
|
1923
2220
|
const entries = await import_promises3.default.readdir(dir, { withFileTypes: true });
|
|
1924
2221
|
for (const entry of entries) {
|
|
1925
|
-
|
|
2222
|
+
if (timedOut || results.length >= maxResults) break;
|
|
2223
|
+
const full = import_path5.default.join(dir, entry.name);
|
|
1926
2224
|
const rel = toRepoRelative(abs, full).replace(/^[.][/\\]?/, "");
|
|
1927
|
-
if (DEFAULT_EXCLUDES.some((ex) => rel.split(
|
|
2225
|
+
if (DEFAULT_EXCLUDES.some((ex) => rel.split(import_path5.default.sep).includes(ex))) continue;
|
|
1928
2226
|
if (regex && !regex.test(entry.name)) continue;
|
|
1929
|
-
if (results.length >= maxResults) break;
|
|
1930
2227
|
results.push({
|
|
1931
2228
|
name: entry.name,
|
|
1932
|
-
path: toRepoRelative(
|
|
2229
|
+
path: toRepoRelative(import_path5.default.resolve(""), full),
|
|
1933
2230
|
// relative display
|
|
1934
2231
|
type: entry.isDirectory() ? "dir" : "file",
|
|
1935
2232
|
depth
|
|
@@ -1944,12 +2241,103 @@ Details: ${res.stderr}` : ""}`
|
|
|
1944
2241
|
}
|
|
1945
2242
|
};
|
|
1946
2243
|
|
|
1947
|
-
// 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
|
|
1948
2335
|
var WarpGrepClient = class {
|
|
1949
2336
|
config;
|
|
1950
2337
|
constructor(config = {}) {
|
|
1951
2338
|
this.config = {
|
|
1952
|
-
|
|
2339
|
+
morphApiKey: config.morphApiKey,
|
|
2340
|
+
morphApiUrl: config.morphApiUrl,
|
|
1953
2341
|
debug: config.debug,
|
|
1954
2342
|
timeout: config.timeout,
|
|
1955
2343
|
retryConfig: config.retryConfig
|
|
@@ -1977,34 +2365,46 @@ var WarpGrepClient = class {
|
|
|
1977
2365
|
* ```
|
|
1978
2366
|
*/
|
|
1979
2367
|
async execute(input) {
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
error: "Search did not complete"
|
|
1995
|
-
};
|
|
1996
|
-
}
|
|
1997
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
1998
|
-
file: r.path,
|
|
1999
|
-
content: r.content
|
|
2000
|
-
}));
|
|
2001
|
-
return {
|
|
2002
|
-
success: true,
|
|
2003
|
-
contexts,
|
|
2004
|
-
summary: finish.payload
|
|
2005
|
-
};
|
|
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
|
+
);
|
|
2006
2382
|
}
|
|
2007
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
|
+
}
|
|
2008
2408
|
function formatResult(result) {
|
|
2009
2409
|
if (!result.success) {
|
|
2010
2410
|
return `Search failed: ${result.error}`;
|
|
@@ -2784,29 +3184,7 @@ var TOOL_PARAMETERS = {
|
|
|
2784
3184
|
},
|
|
2785
3185
|
required: ["query"]
|
|
2786
3186
|
};
|
|
2787
|
-
|
|
2788
|
-
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
2789
|
-
const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
2790
|
-
const result = await runWarpGrep({
|
|
2791
|
-
query: parsed.query,
|
|
2792
|
-
repoRoot: config.repoRoot,
|
|
2793
|
-
provider,
|
|
2794
|
-
excludes: config.excludes,
|
|
2795
|
-
includes: config.includes,
|
|
2796
|
-
debug: config.debug ?? false,
|
|
2797
|
-
apiKey: config.apiKey
|
|
2798
|
-
});
|
|
2799
|
-
const finish = result.finish;
|
|
2800
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
2801
|
-
return { success: false, error: "Search did not complete" };
|
|
2802
|
-
}
|
|
2803
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
2804
|
-
file: r.path,
|
|
2805
|
-
content: r.content
|
|
2806
|
-
}));
|
|
2807
|
-
return { success: true, contexts, summary: finish.payload };
|
|
2808
|
-
}
|
|
2809
|
-
function createMorphWarpGrepTool(config) {
|
|
3187
|
+
function createWarpGrepTool(config) {
|
|
2810
3188
|
const tool4 = {
|
|
2811
3189
|
type: "function",
|
|
2812
3190
|
function: {
|
|
@@ -2817,7 +3195,7 @@ function createMorphWarpGrepTool(config) {
|
|
|
2817
3195
|
};
|
|
2818
3196
|
return Object.assign(tool4, {
|
|
2819
3197
|
execute: async (input) => {
|
|
2820
|
-
return
|
|
3198
|
+
return executeToolCall(input, config);
|
|
2821
3199
|
},
|
|
2822
3200
|
formatResult: (result) => {
|
|
2823
3201
|
return formatResult(result);
|
|
@@ -2993,7 +3371,7 @@ var editFileTool = {
|
|
|
2993
3371
|
}
|
|
2994
3372
|
}
|
|
2995
3373
|
};
|
|
2996
|
-
async function
|
|
3374
|
+
async function execute(input, config) {
|
|
2997
3375
|
return executeEditFile(input, config);
|
|
2998
3376
|
}
|
|
2999
3377
|
function getSystemPrompt2() {
|
|
@@ -3031,7 +3409,7 @@ function createEditFileTool(config = {}) {
|
|
|
3031
3409
|
return Object.assign({}, toolDef, {
|
|
3032
3410
|
execute: async (input) => {
|
|
3033
3411
|
const parsedInput = typeof input === "string" ? JSON.parse(input) : input;
|
|
3034
|
-
return
|
|
3412
|
+
return execute(parsedInput, config);
|
|
3035
3413
|
},
|
|
3036
3414
|
formatResult: (result) => {
|
|
3037
3415
|
return formatResult3(result);
|
|
@@ -3050,13 +3428,13 @@ var OpenAIToolFactory = class {
|
|
|
3050
3428
|
/**
|
|
3051
3429
|
* Create an OpenAI-compatible warp grep tool
|
|
3052
3430
|
*
|
|
3053
|
-
* @param toolConfig - Tool configuration (
|
|
3431
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3054
3432
|
* @returns OpenAI ChatCompletionTool with execute and formatResult methods
|
|
3055
3433
|
*/
|
|
3056
3434
|
createWarpGrepTool(toolConfig) {
|
|
3057
|
-
return
|
|
3435
|
+
return createWarpGrepTool({
|
|
3058
3436
|
...toolConfig,
|
|
3059
|
-
|
|
3437
|
+
morphApiKey: this.config.apiKey
|
|
3060
3438
|
});
|
|
3061
3439
|
}
|
|
3062
3440
|
/**
|
|
@@ -3093,29 +3471,7 @@ var INPUT_SCHEMA = {
|
|
|
3093
3471
|
},
|
|
3094
3472
|
required: ["query"]
|
|
3095
3473
|
};
|
|
3096
|
-
|
|
3097
|
-
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
3098
|
-
const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
3099
|
-
const result = await runWarpGrep({
|
|
3100
|
-
query: parsed.query,
|
|
3101
|
-
repoRoot: config.repoRoot,
|
|
3102
|
-
provider,
|
|
3103
|
-
excludes: config.excludes,
|
|
3104
|
-
includes: config.includes,
|
|
3105
|
-
debug: config.debug ?? false,
|
|
3106
|
-
apiKey: config.apiKey
|
|
3107
|
-
});
|
|
3108
|
-
const finish = result.finish;
|
|
3109
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
3110
|
-
return { success: false, error: "Search did not complete" };
|
|
3111
|
-
}
|
|
3112
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
3113
|
-
file: r.path,
|
|
3114
|
-
content: r.content
|
|
3115
|
-
}));
|
|
3116
|
-
return { success: true, contexts, summary: finish.payload };
|
|
3117
|
-
}
|
|
3118
|
-
function createMorphWarpGrepTool2(config) {
|
|
3474
|
+
function createWarpGrepTool2(config) {
|
|
3119
3475
|
const tool4 = {
|
|
3120
3476
|
name: config.name ?? WARP_GREP_TOOL_NAME,
|
|
3121
3477
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
@@ -3123,7 +3479,7 @@ function createMorphWarpGrepTool2(config) {
|
|
|
3123
3479
|
};
|
|
3124
3480
|
return Object.assign(tool4, {
|
|
3125
3481
|
execute: async (input) => {
|
|
3126
|
-
return
|
|
3482
|
+
return executeToolCall(input, config);
|
|
3127
3483
|
},
|
|
3128
3484
|
formatResult: (result) => {
|
|
3129
3485
|
return formatResult(result);
|
|
@@ -3273,13 +3629,13 @@ var AnthropicToolFactory = class {
|
|
|
3273
3629
|
/**
|
|
3274
3630
|
* Create an Anthropic-compatible warp grep tool
|
|
3275
3631
|
*
|
|
3276
|
-
* @param toolConfig - Tool configuration (
|
|
3632
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3277
3633
|
* @returns Anthropic Tool with execute and formatResult methods
|
|
3278
3634
|
*/
|
|
3279
3635
|
createWarpGrepTool(toolConfig) {
|
|
3280
|
-
return
|
|
3636
|
+
return createWarpGrepTool2({
|
|
3281
3637
|
...toolConfig,
|
|
3282
|
-
|
|
3638
|
+
morphApiKey: this.config.apiKey
|
|
3283
3639
|
});
|
|
3284
3640
|
}
|
|
3285
3641
|
/**
|
|
@@ -3311,33 +3667,23 @@ var AnthropicToolFactory = class {
|
|
|
3311
3667
|
// tools/warp_grep/vercel.ts
|
|
3312
3668
|
var import_ai = require("ai");
|
|
3313
3669
|
var import_zod = require("zod");
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3670
|
+
function createWarpGrepTool3(config) {
|
|
3671
|
+
const schema = import_zod.z.object({
|
|
3672
|
+
query: import_zod.z.string().describe("Free-form repository question")
|
|
3673
|
+
});
|
|
3318
3674
|
return (0, import_ai.tool)({
|
|
3319
3675
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
3320
|
-
inputSchema:
|
|
3676
|
+
inputSchema: schema,
|
|
3321
3677
|
execute: async (params) => {
|
|
3322
|
-
const
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
repoRoot: config.repoRoot,
|
|
3326
|
-
provider,
|
|
3327
|
-
excludes: config.excludes,
|
|
3328
|
-
includes: config.includes,
|
|
3329
|
-
debug: config.debug ?? false,
|
|
3330
|
-
apiKey: config.apiKey
|
|
3331
|
-
});
|
|
3332
|
-
const finish = result.finish;
|
|
3333
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
3334
|
-
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}`);
|
|
3335
3681
|
}
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3682
|
+
return {
|
|
3683
|
+
success: true,
|
|
3684
|
+
contexts: result.contexts,
|
|
3685
|
+
summary: result.summary
|
|
3686
|
+
};
|
|
3341
3687
|
}
|
|
3342
3688
|
});
|
|
3343
3689
|
}
|
|
@@ -3450,13 +3796,13 @@ var VercelToolFactory = class {
|
|
|
3450
3796
|
/**
|
|
3451
3797
|
* Create a Vercel AI SDK-compatible warp grep tool
|
|
3452
3798
|
*
|
|
3453
|
-
* @param toolConfig - Tool configuration (
|
|
3799
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3454
3800
|
* @returns Vercel AI SDK tool
|
|
3455
3801
|
*/
|
|
3456
3802
|
createWarpGrepTool(toolConfig) {
|
|
3457
|
-
return
|
|
3803
|
+
return createWarpGrepTool3({
|
|
3458
3804
|
...toolConfig,
|
|
3459
|
-
|
|
3805
|
+
morphApiKey: this.config.apiKey
|
|
3460
3806
|
});
|
|
3461
3807
|
}
|
|
3462
3808
|
/**
|
|
@@ -3536,7 +3882,7 @@ var MorphClient = class {
|
|
|
3536
3882
|
retryConfig: config.retryConfig
|
|
3537
3883
|
});
|
|
3538
3884
|
this.warpGrep = new WarpGrepClient({
|
|
3539
|
-
|
|
3885
|
+
morphApiKey: config.apiKey,
|
|
3540
3886
|
debug: config.debug,
|
|
3541
3887
|
timeout: config.timeout,
|
|
3542
3888
|
retryConfig: config.retryConfig
|
|
@@ -3582,6 +3928,19 @@ var MorphClient = class {
|
|
|
3582
3928
|
this.vercel = new VercelToolFactory(config);
|
|
3583
3929
|
}
|
|
3584
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
|
+
};
|
|
3585
3944
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3586
3945
|
0 && (module.exports = {
|
|
3587
3946
|
AnthropicRouter,
|