@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/client.cjs
CHANGED
|
@@ -947,9 +947,13 @@ async function checkHealth(config = {}) {
|
|
|
947
947
|
|
|
948
948
|
// tools/warp_grep/agent/config.ts
|
|
949
949
|
var AGENT_CONFIG = {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
950
|
+
MAX_TURNS: 4,
|
|
951
|
+
TIMEOUT_MS: 3e4,
|
|
952
|
+
MAX_CONTEXT_CHARS: 54e4,
|
|
953
|
+
MAX_OUTPUT_LINES: 200,
|
|
954
|
+
MAX_READ_LINES: 800,
|
|
955
|
+
MAX_LIST_DEPTH: 3,
|
|
956
|
+
LIST_TIMEOUT_MS: 2e3
|
|
953
957
|
};
|
|
954
958
|
var BUILTIN_EXCLUDES = [
|
|
955
959
|
// Version control
|
|
@@ -1031,113 +1035,191 @@ var BUILTIN_EXCLUDES = [
|
|
|
1031
1035
|
".*"
|
|
1032
1036
|
];
|
|
1033
1037
|
var DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
1034
|
-
var DEFAULT_MODEL = "morph-warp-grep";
|
|
1038
|
+
var DEFAULT_MODEL = "morph-warp-grep-v1";
|
|
1035
1039
|
|
|
1036
1040
|
// tools/warp_grep/agent/prompt.ts
|
|
1037
|
-
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given
|
|
1041
|
+
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given search_string.
|
|
1038
1042
|
|
|
1039
|
-
|
|
1043
|
+
### workflow
|
|
1040
1044
|
You have exactly 4 turns. The 4th turn MUST be a \`finish\` call. Each turn allows up to 8 parallel tool calls.
|
|
1041
1045
|
|
|
1042
|
-
- Turn 1: Map the territory OR dive deep (based on
|
|
1046
|
+
- Turn 1: Map the territory OR dive deep (based on search_string specificity)
|
|
1043
1047
|
- Turn 2-3: Refine based on findings
|
|
1044
1048
|
- Turn 4: MUST call \`finish\` with all relevant code locations
|
|
1045
1049
|
- You MAY call \`finish\` early if confident\u2014but never before at least 1 search turn.
|
|
1050
|
+
- The user strongly prefers if you can call the finish tool early, but you must be correct
|
|
1046
1051
|
|
|
1047
|
-
Remember, if the task feels easy to you, it is strongly desirable to call
|
|
1048
|
-
</workflow>
|
|
1052
|
+
Remember, if the task feels easy to you, it is strongly desirable to call 'finish' early using fewer turns, but quality over speed
|
|
1049
1053
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1054
|
+
### tools
|
|
1055
|
+
Tool calls use nested XML elements:
|
|
1056
|
+
\`\`\`xml
|
|
1057
|
+
<tool_name>
|
|
1058
|
+
<parameter>value</parameter>
|
|
1059
|
+
</tool_name>
|
|
1060
|
+
\`\`\`
|
|
1061
|
+
|
|
1062
|
+
### \`list_directory\`
|
|
1063
|
+
Directory tree view. Shows structure of a path, optionally filtered by regex pattern.
|
|
1064
|
+
|
|
1065
|
+
Elements:
|
|
1066
|
+
- \`<path>\` (required): Directory path to list (use \`.\` for repo root)
|
|
1067
|
+
- \`<pattern>\` (optional): Regex to filter results
|
|
1055
1068
|
|
|
1056
1069
|
Examples:
|
|
1057
1070
|
\`\`\`
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1071
|
+
<list_directory>
|
|
1072
|
+
<path>src/services</path>
|
|
1073
|
+
</list_directory>
|
|
1074
|
+
|
|
1075
|
+
<list_directory>
|
|
1076
|
+
<path>lib/utils</path>
|
|
1077
|
+
<pattern>.*\\.(ts|js)$</pattern>
|
|
1078
|
+
</list_directory>
|
|
1062
1079
|
\`\`\`
|
|
1063
1080
|
|
|
1064
|
-
### \`read
|
|
1065
|
-
Read file contents.
|
|
1081
|
+
### \`read\`
|
|
1082
|
+
Read file contents. Supports multiple line ranges.
|
|
1066
1083
|
- Returns numbered lines for easy reference
|
|
1067
|
-
-
|
|
1084
|
+
- ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
1085
|
+
|
|
1086
|
+
Elements:
|
|
1087
|
+
- \`<path>\` (required): File path to read
|
|
1088
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80,100-120" (omit to read entire file)
|
|
1068
1089
|
|
|
1069
1090
|
Examples:
|
|
1070
1091
|
\`\`\`
|
|
1071
|
-
read
|
|
1072
|
-
|
|
1073
|
-
read
|
|
1092
|
+
<read>
|
|
1093
|
+
<path>src/main.py</path>
|
|
1094
|
+
</read>
|
|
1095
|
+
|
|
1096
|
+
<read>
|
|
1097
|
+
<path>src/auth.py</path>
|
|
1098
|
+
<lines>1-20,45-80,150-200</lines>
|
|
1099
|
+
</read>
|
|
1074
1100
|
\`\`\`
|
|
1075
1101
|
|
|
1076
|
-
### \`grep
|
|
1077
|
-
|
|
1078
|
-
-
|
|
1079
|
-
-
|
|
1102
|
+
### \`grep\`
|
|
1103
|
+
Search for pattern matches across files. Returns matches with 1 line of context above and below.
|
|
1104
|
+
- Match lines use \`:\` separator \u2192 \`filepath:linenum:content\`
|
|
1105
|
+
- Context lines use \`-\` separator \u2192 \`filepath-linenum-content\`
|
|
1106
|
+
|
|
1107
|
+
Elements:
|
|
1108
|
+
- \`<pattern>\` (required): Search pattern (regex). Use \`(a|b)\` for OR patterns.
|
|
1109
|
+
- \`<sub_dir>\` (optional): Subdirectory to search in (defaults to \`.\`)
|
|
1110
|
+
- \`<glob>\` (optional): File pattern filter like \`*.py\` or \`*.{ts,tsx}\`
|
|
1080
1111
|
|
|
1081
1112
|
Examples:
|
|
1082
1113
|
\`\`\`
|
|
1083
|
-
grep
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
grep
|
|
1114
|
+
<grep>
|
|
1115
|
+
<pattern>(authenticate|authorize|login)</pattern>
|
|
1116
|
+
<sub_dir>src/auth/</sub_dir>
|
|
1117
|
+
</grep>
|
|
1118
|
+
|
|
1119
|
+
<grep>
|
|
1120
|
+
<pattern>class.*(Service|Controller)</pattern>
|
|
1121
|
+
<glob>*.{ts,js}</glob>
|
|
1122
|
+
</grep>
|
|
1123
|
+
|
|
1124
|
+
<grep>
|
|
1125
|
+
<pattern>(DB_HOST|DATABASE_URL|connection)</pattern>
|
|
1126
|
+
<glob>*.{py,yaml,env}</glob>
|
|
1127
|
+
<sub_dir>lib/</sub_dir>
|
|
1128
|
+
</grep>
|
|
1087
1129
|
\`\`\`
|
|
1088
1130
|
|
|
1089
|
-
### \`finish
|
|
1090
|
-
Submit final answer with all relevant code locations.
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
-
|
|
1094
|
-
-
|
|
1095
|
-
|
|
1131
|
+
### \`finish\`
|
|
1132
|
+
Submit final answer with all relevant code locations. Uses nested \`<file>\` elements.
|
|
1133
|
+
|
|
1134
|
+
File elements:
|
|
1135
|
+
- \`<path>\` (required): File path
|
|
1136
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80" (\`*\` for entire file)
|
|
1137
|
+
|
|
1138
|
+
ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
1096
1139
|
|
|
1097
1140
|
Examples:
|
|
1098
1141
|
\`\`\`
|
|
1099
|
-
finish
|
|
1100
|
-
|
|
1142
|
+
<finish>
|
|
1143
|
+
<file>
|
|
1144
|
+
<path>src/auth.py</path>
|
|
1145
|
+
<lines>1-15,25-50,75-80</lines>
|
|
1146
|
+
</file>
|
|
1147
|
+
<file>
|
|
1148
|
+
<path>src/models/user.py</path>
|
|
1149
|
+
<lines>*</lines>
|
|
1150
|
+
</file>
|
|
1151
|
+
</finish>
|
|
1101
1152
|
\`\`\`
|
|
1102
1153
|
</tools>
|
|
1103
1154
|
|
|
1104
1155
|
<strategy>
|
|
1105
|
-
**Before your first tool call, classify the
|
|
1156
|
+
**Before your first tool call, classify the search_string:**
|
|
1106
1157
|
|
|
1107
|
-
|
|
|
1108
|
-
|
|
1109
|
-
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by
|
|
1110
|
-
| **Conceptual** (how does X work, where is Y handled) |
|
|
1111
|
-
| **Exploratory** (find all tests, list API endpoints) |
|
|
1158
|
+
| Search_string Type | Round 1 Strategy | Early Finish? |
|
|
1159
|
+
|------------|------------------|---------------|
|
|
1160
|
+
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by round 2 |
|
|
1161
|
+
| **Conceptual** (how does X work, where is Y handled) | list_directory + 2-3 broad greps | Rarely early |
|
|
1162
|
+
| **Exploratory** (find all tests, list API endpoints) | list_directory at multiple depths | Usually needs 3 rounds |
|
|
1112
1163
|
|
|
1113
1164
|
**Parallel call patterns:**
|
|
1114
1165
|
- **Shotgun grep**: Same pattern, 8 different directories\u2014fast coverage
|
|
1115
1166
|
- **Variant grep**: 8 pattern variations (synonyms, naming conventions)\u2014catches inconsistent codebases
|
|
1116
|
-
- **Funnel**: 1
|
|
1167
|
+
- **Funnel**: 1 list_directory + 7 greps\u2014orient and search simultaneously
|
|
1117
1168
|
- **Deep read**: 8 reads on files you already identified\u2014gather full context fast
|
|
1169
|
+
|
|
1170
|
+
**Tool call expectations:**
|
|
1171
|
+
- 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.
|
|
1172
|
+
- 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.
|
|
1173
|
+
- 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.
|
|
1174
|
+
- 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.
|
|
1118
1175
|
</strategy>
|
|
1119
1176
|
|
|
1120
1177
|
<output_format>
|
|
1121
1178
|
EVERY response MUST follow this exact format:
|
|
1122
1179
|
|
|
1123
1180
|
1. First, wrap your reasoning in \`<think>...</think>\` tags containing:
|
|
1124
|
-
-
|
|
1125
|
-
- Confidence estimate (can I finish in 1-2
|
|
1126
|
-
- This
|
|
1181
|
+
- Search_string classification (specific/conceptual/exploratory)
|
|
1182
|
+
- Confidence estimate (can I finish in 1-2 rounds?)
|
|
1183
|
+
- This round's parallel strategy
|
|
1127
1184
|
- What signals would let me finish early?
|
|
1128
1185
|
|
|
1129
|
-
2. Then, output
|
|
1186
|
+
2. Then, output up to 8 tool calls using nested XML elements.
|
|
1130
1187
|
|
|
1131
1188
|
Example:
|
|
1132
1189
|
\`\`\`
|
|
1133
1190
|
<think>
|
|
1134
|
-
This is a specific
|
|
1135
|
-
High confidence I can finish in 2
|
|
1191
|
+
This is a specific search_string about authentication. I'll grep for auth-related patterns.
|
|
1192
|
+
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
|
|
1136
1193
|
Strategy: Shotgun grep across likely directories.
|
|
1137
1194
|
</think>
|
|
1138
|
-
<
|
|
1139
|
-
<
|
|
1140
|
-
<
|
|
1195
|
+
<grep>
|
|
1196
|
+
<pattern>(authenticate|login|session)</pattern>
|
|
1197
|
+
<sub_dir>src/auth/</sub_dir>
|
|
1198
|
+
</grep>
|
|
1199
|
+
<grep>
|
|
1200
|
+
<pattern>(middleware|interceptor)</pattern>
|
|
1201
|
+
<glob>*.{ts,js}</glob>
|
|
1202
|
+
</grep>
|
|
1203
|
+
<list_directory>
|
|
1204
|
+
<path>src/auth</path>
|
|
1205
|
+
</list_directory>
|
|
1206
|
+
\`\`\`
|
|
1207
|
+
|
|
1208
|
+
Finishing example:
|
|
1209
|
+
\`\`\`
|
|
1210
|
+
<think>
|
|
1211
|
+
I think I have a rough idea, but this is my last turn so I must call the finish tool regardless.
|
|
1212
|
+
</think>
|
|
1213
|
+
<finish>
|
|
1214
|
+
<file>
|
|
1215
|
+
<path>src/auth/login.py</path>
|
|
1216
|
+
<lines>1-50</lines>
|
|
1217
|
+
</file>
|
|
1218
|
+
<file>
|
|
1219
|
+
<path>src/middleware/session.py</path>
|
|
1220
|
+
<lines>10-80</lines>
|
|
1221
|
+
</file>
|
|
1222
|
+
</finish>
|
|
1141
1223
|
\`\`\`
|
|
1142
1224
|
|
|
1143
1225
|
No commentary outside \`<think>\`. No explanations after tool calls.
|
|
@@ -1150,17 +1232,111 @@ When calling \`finish\`:
|
|
|
1150
1232
|
- Include any type definitions, interfaces, or constants used
|
|
1151
1233
|
- Better to over-include than leave the user missing context
|
|
1152
1234
|
- If unsure about boundaries, include more rather than less
|
|
1153
|
-
</finishing_requirements
|
|
1154
|
-
|
|
1155
|
-
Begin your exploration now to find code relevant to the query.`;
|
|
1235
|
+
</finishing_requirements>`;
|
|
1156
1236
|
function getSystemPrompt() {
|
|
1157
1237
|
return SYSTEM_PROMPT;
|
|
1158
1238
|
}
|
|
1159
1239
|
|
|
1160
1240
|
// tools/warp_grep/agent/parser.ts
|
|
1161
|
-
var VALID_COMMANDS = ["
|
|
1241
|
+
var VALID_COMMANDS = ["list_directory", "grep", "read", "finish"];
|
|
1242
|
+
function isValidCommand(name) {
|
|
1243
|
+
return VALID_COMMANDS.includes(name);
|
|
1244
|
+
}
|
|
1245
|
+
function getXmlElementText(xml, tagName) {
|
|
1246
|
+
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i");
|
|
1247
|
+
const match = xml.match(regex);
|
|
1248
|
+
return match ? match[1].trim() : null;
|
|
1249
|
+
}
|
|
1250
|
+
function parseNestedXmlTools(text) {
|
|
1251
|
+
const tools = [];
|
|
1252
|
+
const toolRegex = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
|
|
1253
|
+
let match;
|
|
1254
|
+
while ((match = toolRegex.exec(text)) !== null) {
|
|
1255
|
+
const rawToolName = match[1].toLowerCase();
|
|
1256
|
+
const content = match[2];
|
|
1257
|
+
if (!isValidCommand(rawToolName)) continue;
|
|
1258
|
+
const toolName = rawToolName;
|
|
1259
|
+
if (toolName === "list_directory") {
|
|
1260
|
+
const path5 = getXmlElementText(content, "path");
|
|
1261
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
1262
|
+
if (path5) {
|
|
1263
|
+
tools.push({ name: "list_directory", arguments: { path: path5, pattern } });
|
|
1264
|
+
}
|
|
1265
|
+
} else if (toolName === "grep") {
|
|
1266
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
1267
|
+
const subDir = getXmlElementText(content, "sub_dir");
|
|
1268
|
+
const glob = getXmlElementText(content, "glob");
|
|
1269
|
+
if (pattern) {
|
|
1270
|
+
tools.push({
|
|
1271
|
+
name: "grep",
|
|
1272
|
+
arguments: {
|
|
1273
|
+
pattern,
|
|
1274
|
+
path: subDir || ".",
|
|
1275
|
+
...glob && { glob }
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
} else if (toolName === "read") {
|
|
1280
|
+
const path5 = getXmlElementText(content, "path");
|
|
1281
|
+
const linesStr = getXmlElementText(content, "lines");
|
|
1282
|
+
if (path5) {
|
|
1283
|
+
const args = { path: path5 };
|
|
1284
|
+
if (linesStr) {
|
|
1285
|
+
const ranges = [];
|
|
1286
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
1287
|
+
const trimmed = rangeStr.trim();
|
|
1288
|
+
if (!trimmed) continue;
|
|
1289
|
+
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
1290
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
1291
|
+
ranges.push([s, e]);
|
|
1292
|
+
} else if (Number.isFinite(s)) {
|
|
1293
|
+
ranges.push([s, s]);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
if (ranges.length === 1) {
|
|
1297
|
+
args.start = ranges[0][0];
|
|
1298
|
+
args.end = ranges[0][1];
|
|
1299
|
+
} else if (ranges.length > 1) {
|
|
1300
|
+
args.lines = ranges;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
tools.push({ name: "read", arguments: args });
|
|
1304
|
+
}
|
|
1305
|
+
} else if (toolName === "finish") {
|
|
1306
|
+
const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
|
|
1307
|
+
const files = [];
|
|
1308
|
+
let fileMatch;
|
|
1309
|
+
while ((fileMatch = fileRegex.exec(content)) !== null) {
|
|
1310
|
+
const fileContent = fileMatch[1];
|
|
1311
|
+
const filePath = getXmlElementText(fileContent, "path");
|
|
1312
|
+
const linesStr = getXmlElementText(fileContent, "lines");
|
|
1313
|
+
if (filePath && linesStr) {
|
|
1314
|
+
const ranges = [];
|
|
1315
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
1316
|
+
if (rangeStr.trim() === "*") {
|
|
1317
|
+
ranges.push([1, 999999]);
|
|
1318
|
+
} else {
|
|
1319
|
+
const [s, e] = rangeStr.split("-").map((v) => parseInt(v.trim(), 10));
|
|
1320
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
1321
|
+
ranges.push([s, e]);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
if (ranges.length > 0) {
|
|
1326
|
+
files.push({ path: filePath, lines: ranges });
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (files.length > 0) {
|
|
1331
|
+
tools.push({ name: "finish", arguments: { files } });
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return tools;
|
|
1336
|
+
}
|
|
1162
1337
|
function preprocessText(text) {
|
|
1163
1338
|
let processed = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
1339
|
+
const nestedTools = parseNestedXmlTools(processed);
|
|
1164
1340
|
const openingTagRegex = /<tool_call>|<tool>/gi;
|
|
1165
1341
|
const closingTagRegex = /<\/tool_call>|<\/tool>/gi;
|
|
1166
1342
|
const openingMatches = processed.match(openingTagRegex) || [];
|
|
@@ -1197,7 +1373,7 @@ function preprocessText(text) {
|
|
|
1197
1373
|
}
|
|
1198
1374
|
}
|
|
1199
1375
|
}
|
|
1200
|
-
return toolCallLines;
|
|
1376
|
+
return { lines: toolCallLines, nestedTools };
|
|
1201
1377
|
}
|
|
1202
1378
|
var LLMResponseParser = class {
|
|
1203
1379
|
finishSpecSplitRe = /,(?=[^,\s]+:)/;
|
|
@@ -1205,8 +1381,8 @@ var LLMResponseParser = class {
|
|
|
1205
1381
|
if (typeof text !== "string") {
|
|
1206
1382
|
throw new TypeError("Command text must be a string.");
|
|
1207
1383
|
}
|
|
1208
|
-
const lines = preprocessText(text);
|
|
1209
|
-
const commands = [];
|
|
1384
|
+
const { lines, nestedTools } = preprocessText(text);
|
|
1385
|
+
const commands = [...nestedTools];
|
|
1210
1386
|
let finishAccumulator = null;
|
|
1211
1387
|
lines.forEach((line) => {
|
|
1212
1388
|
if (!line || line.startsWith("#")) return;
|
|
@@ -1214,8 +1390,8 @@ var LLMResponseParser = class {
|
|
|
1214
1390
|
if (parts.length === 0) return;
|
|
1215
1391
|
const cmd = parts[0];
|
|
1216
1392
|
switch (cmd) {
|
|
1217
|
-
case "
|
|
1218
|
-
this.
|
|
1393
|
+
case "list_directory":
|
|
1394
|
+
this.handleListDirectory(parts, line, commands);
|
|
1219
1395
|
break;
|
|
1220
1396
|
case "grep":
|
|
1221
1397
|
this.handleGrep(parts, line, commands);
|
|
@@ -1233,8 +1409,8 @@ var LLMResponseParser = class {
|
|
|
1233
1409
|
if (finishAccumulator) {
|
|
1234
1410
|
const map = finishAccumulator;
|
|
1235
1411
|
const entries = [...map.entries()];
|
|
1236
|
-
const filesPayload = entries.map(([
|
|
1237
|
-
path:
|
|
1412
|
+
const filesPayload = entries.map(([path5, ranges]) => ({
|
|
1413
|
+
path: path5,
|
|
1238
1414
|
lines: [...ranges].sort((a, b) => a[0] - b[0])
|
|
1239
1415
|
}));
|
|
1240
1416
|
commands.push({ name: "finish", arguments: { files: filesPayload } });
|
|
@@ -1266,18 +1442,17 @@ var LLMResponseParser = class {
|
|
|
1266
1442
|
skip(message) {
|
|
1267
1443
|
return { name: "_skip", arguments: { message } };
|
|
1268
1444
|
}
|
|
1269
|
-
|
|
1445
|
+
handleListDirectory(parts, rawLine, commands) {
|
|
1270
1446
|
if (parts.length < 2) {
|
|
1271
1447
|
commands.push(this.skip(
|
|
1272
|
-
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format:
|
|
1448
|
+
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: list_directory <path> [pattern]. Example: list_directory src/`
|
|
1273
1449
|
));
|
|
1274
1450
|
return;
|
|
1275
1451
|
}
|
|
1276
|
-
const
|
|
1452
|
+
const path5 = parts[1];
|
|
1277
1453
|
const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
|
|
1278
|
-
commands.push({ name: "
|
|
1454
|
+
commands.push({ name: "list_directory", arguments: { path: path5, pattern } });
|
|
1279
1455
|
}
|
|
1280
|
-
// no glob tool in MCP
|
|
1281
1456
|
handleGrep(parts, rawLine, commands) {
|
|
1282
1457
|
if (parts.length < 3) {
|
|
1283
1458
|
commands.push(this.skip(
|
|
@@ -1348,8 +1523,30 @@ var LLMResponseParser = class {
|
|
|
1348
1523
|
}
|
|
1349
1524
|
};
|
|
1350
1525
|
|
|
1351
|
-
// tools/warp_grep/tools/
|
|
1526
|
+
// tools/warp_grep/agent/tools/grep.ts
|
|
1527
|
+
async function toolGrep(provider, args) {
|
|
1528
|
+
const res = await provider.grep(args);
|
|
1529
|
+
if (res.error) {
|
|
1530
|
+
return { output: res.error };
|
|
1531
|
+
}
|
|
1532
|
+
if (!res.lines.length) {
|
|
1533
|
+
return { output: "no matches" };
|
|
1534
|
+
}
|
|
1535
|
+
return { output: res.lines.join("\n") };
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// tools/warp_grep/agent/tools/read.ts
|
|
1352
1539
|
async function toolRead(provider, args) {
|
|
1540
|
+
if (args.lines && args.lines.length > 0) {
|
|
1541
|
+
const chunks = [];
|
|
1542
|
+
for (const [start, end] of args.lines) {
|
|
1543
|
+
const res2 = await provider.read({ path: args.path, start, end });
|
|
1544
|
+
if (res2.error) return res2.error;
|
|
1545
|
+
chunks.push(res2.lines.join("\n"));
|
|
1546
|
+
}
|
|
1547
|
+
if (chunks.every((c) => c === "")) return "(empty file)";
|
|
1548
|
+
return chunks.join("\n...\n");
|
|
1549
|
+
}
|
|
1353
1550
|
const res = await provider.read({ path: args.path, start: args.start, end: args.end });
|
|
1354
1551
|
if (res.error) {
|
|
1355
1552
|
return res.error;
|
|
@@ -1358,16 +1555,56 @@ async function toolRead(provider, args) {
|
|
|
1358
1555
|
return res.lines.join("\n");
|
|
1359
1556
|
}
|
|
1360
1557
|
|
|
1361
|
-
// tools/warp_grep/tools/
|
|
1362
|
-
async function
|
|
1363
|
-
const list = await provider.
|
|
1558
|
+
// tools/warp_grep/agent/tools/list_directory.ts
|
|
1559
|
+
async function toolListDirectory(provider, args) {
|
|
1560
|
+
const list = await provider.listDirectory({
|
|
1364
1561
|
path: args.path,
|
|
1365
1562
|
pattern: args.pattern ?? null,
|
|
1366
|
-
maxResults: args.maxResults ??
|
|
1367
|
-
maxDepth: args.maxDepth ??
|
|
1563
|
+
maxResults: args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
1564
|
+
maxDepth: args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH
|
|
1368
1565
|
});
|
|
1369
1566
|
if (!list.length) return "empty";
|
|
1370
|
-
|
|
1567
|
+
if (list.length >= AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
1568
|
+
return "query not specific enough, tool called tried to return too much context and failed";
|
|
1569
|
+
}
|
|
1570
|
+
return list.map((e) => {
|
|
1571
|
+
const indent = " ".repeat(e.depth);
|
|
1572
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
1573
|
+
return `${indent}${name}`;
|
|
1574
|
+
}).join("\n");
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// tools/warp_grep/agent/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"));
|
|
1586
|
+
}
|
|
1587
|
+
out.push({ path: f.path, ranges, content: chunks.join("\n") });
|
|
1588
|
+
}
|
|
1589
|
+
return out;
|
|
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;
|
|
1371
1608
|
}
|
|
1372
1609
|
|
|
1373
1610
|
// tools/warp_grep/agent/formatter.ts
|
|
@@ -1386,8 +1623,8 @@ var ToolOutputFormatter = class {
|
|
|
1386
1623
|
switch (name) {
|
|
1387
1624
|
case "read":
|
|
1388
1625
|
return this.formatRead(safeArgs, payload, isError);
|
|
1389
|
-
case "
|
|
1390
|
-
return this.
|
|
1626
|
+
case "list_directory":
|
|
1627
|
+
return this.formatListDirectory(safeArgs, payload, isError);
|
|
1391
1628
|
case "grep":
|
|
1392
1629
|
return this.formatGrep(safeArgs, payload, isError);
|
|
1393
1630
|
default:
|
|
@@ -1400,39 +1637,56 @@ ${payload}
|
|
|
1400
1637
|
if (isError) {
|
|
1401
1638
|
return payload;
|
|
1402
1639
|
}
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1640
|
+
const path5 = this.asString(args.path) || "...";
|
|
1641
|
+
const start = args.start;
|
|
1642
|
+
const end = args.end;
|
|
1643
|
+
const linesArray = args.lines;
|
|
1644
|
+
const attributes = [`path="${path5}"`];
|
|
1645
|
+
if (linesArray && linesArray.length > 0) {
|
|
1646
|
+
const rangeStr = linesArray.map(([s, e]) => `${s}-${e}`).join(",");
|
|
1647
|
+
attributes.push(`lines="${rangeStr}"`);
|
|
1648
|
+
} else if (start !== void 0 && end !== void 0) {
|
|
1649
|
+
attributes.push(`lines="${start}-${end}"`);
|
|
1650
|
+
}
|
|
1651
|
+
return `<read ${attributes.join(" ")}>
|
|
1405
1652
|
${payload}
|
|
1406
|
-
</
|
|
1653
|
+
</read>`;
|
|
1407
1654
|
}
|
|
1408
|
-
|
|
1409
|
-
const
|
|
1655
|
+
formatListDirectory(args, payload, isError) {
|
|
1656
|
+
const path5 = this.asString(args.path) || ".";
|
|
1657
|
+
const pattern = this.asString(args.pattern);
|
|
1658
|
+
const attributes = [`path="${path5}"`];
|
|
1659
|
+
if (pattern) {
|
|
1660
|
+
attributes.push(`pattern="${pattern}"`);
|
|
1661
|
+
}
|
|
1410
1662
|
if (isError) {
|
|
1411
|
-
|
|
1412
|
-
${payload}
|
|
1413
|
-
</analyse_results>`;
|
|
1663
|
+
attributes.push('status="error"');
|
|
1414
1664
|
}
|
|
1415
|
-
return `<
|
|
1665
|
+
return `<list_directory ${attributes.join(" ")}>
|
|
1416
1666
|
${payload}
|
|
1417
|
-
</
|
|
1667
|
+
</list_directory>`;
|
|
1418
1668
|
}
|
|
1419
1669
|
formatGrep(args, payload, isError) {
|
|
1420
1670
|
const pattern = this.asString(args.pattern);
|
|
1421
|
-
const
|
|
1671
|
+
const subDir = this.asString(args.path);
|
|
1672
|
+
const glob = this.asString(args.glob);
|
|
1422
1673
|
const attributes = [];
|
|
1423
1674
|
if (pattern !== void 0) {
|
|
1424
1675
|
attributes.push(`pattern="${pattern}"`);
|
|
1425
1676
|
}
|
|
1426
|
-
if (
|
|
1427
|
-
attributes.push(`
|
|
1677
|
+
if (subDir !== void 0) {
|
|
1678
|
+
attributes.push(`sub_dir="${subDir}"`);
|
|
1679
|
+
}
|
|
1680
|
+
if (glob !== void 0) {
|
|
1681
|
+
attributes.push(`glob="${glob}"`);
|
|
1428
1682
|
}
|
|
1429
1683
|
if (isError) {
|
|
1430
1684
|
attributes.push('status="error"');
|
|
1431
1685
|
}
|
|
1432
1686
|
const attrText = attributes.length ? ` ${attributes.join(" ")}` : "";
|
|
1433
|
-
return `<
|
|
1687
|
+
return `<grep${attrText}>
|
|
1434
1688
|
${payload}
|
|
1435
|
-
</
|
|
1689
|
+
</grep>`;
|
|
1436
1690
|
}
|
|
1437
1691
|
asString(value) {
|
|
1438
1692
|
if (value === null || value === void 0) {
|
|
@@ -1446,180 +1700,100 @@ function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
|
1446
1700
|
return sharedFormatter.format(toolName, args, output, options);
|
|
1447
1701
|
}
|
|
1448
1702
|
|
|
1449
|
-
// tools/warp_grep/agent/
|
|
1450
|
-
var
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
}
|
|
1469
|
-
const firstSep = text.indexOf(":");
|
|
1470
|
-
if (firstSep === -1) {
|
|
1471
|
-
return null;
|
|
1472
|
-
}
|
|
1473
|
-
let filePath = text.slice(0, firstSep).trim();
|
|
1474
|
-
if (!filePath) {
|
|
1475
|
-
return null;
|
|
1476
|
-
}
|
|
1477
|
-
if (filePath.startsWith("./") || filePath.startsWith(".\\")) {
|
|
1478
|
-
filePath = filePath.slice(2);
|
|
1479
|
-
}
|
|
1480
|
-
const remainder = text.slice(firstSep + 1);
|
|
1481
|
-
const secondSep = remainder.indexOf(":");
|
|
1482
|
-
if (secondSep === -1) {
|
|
1483
|
-
return null;
|
|
1484
|
-
}
|
|
1485
|
-
const linePart = remainder.slice(0, secondSep);
|
|
1486
|
-
const lineNumber = Number.parseInt(linePart, 10);
|
|
1487
|
-
if (!Number.isInteger(lineNumber) || lineNumber <= 0) {
|
|
1488
|
-
return null;
|
|
1489
|
-
}
|
|
1490
|
-
let contentSegment = remainder.slice(secondSep + 1);
|
|
1491
|
-
const columnSep = contentSegment.indexOf(":");
|
|
1492
|
-
if (columnSep !== -1 && /^\d+$/.test(contentSegment.slice(0, columnSep))) {
|
|
1493
|
-
contentSegment = contentSegment.slice(columnSep + 1);
|
|
1494
|
-
}
|
|
1495
|
-
const content = contentSegment.trim();
|
|
1496
|
-
if (!content) {
|
|
1497
|
-
return null;
|
|
1498
|
-
}
|
|
1499
|
-
return { path: filePath, lineNumber, content };
|
|
1500
|
-
}
|
|
1501
|
-
function parseAndFilterGrepOutput(rawOutput, state) {
|
|
1502
|
-
const matches = [];
|
|
1503
|
-
if (typeof rawOutput !== "string" || !rawOutput.trim()) {
|
|
1504
|
-
return matches;
|
|
1505
|
-
}
|
|
1506
|
-
for (const line of rawOutput.split(/\r?\n/)) {
|
|
1507
|
-
const fields = extractMatchFields(line);
|
|
1508
|
-
if (!fields) {
|
|
1509
|
-
continue;
|
|
1510
|
-
}
|
|
1511
|
-
if (state.isNew(fields.path, fields.lineNumber)) {
|
|
1512
|
-
matches.push(fields);
|
|
1513
|
-
state.add(fields.path, fields.lineNumber);
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
return matches;
|
|
1517
|
-
}
|
|
1518
|
-
function truncateOutput(payload, maxChars) {
|
|
1519
|
-
if (payload.length <= maxChars) {
|
|
1520
|
-
return payload;
|
|
1521
|
-
}
|
|
1522
|
-
const note = "... (output truncated)";
|
|
1523
|
-
const available = maxChars - note.length - 1;
|
|
1524
|
-
if (available <= 0) {
|
|
1525
|
-
return note;
|
|
1526
|
-
}
|
|
1527
|
-
if (payload.length <= available) {
|
|
1528
|
-
return `${payload.slice(0, available).replace(/\n$/, "")}
|
|
1529
|
-
${note}`;
|
|
1530
|
-
}
|
|
1531
|
-
const core = payload.slice(0, Math.max(0, available - 1));
|
|
1532
|
-
const trimmed = core.replace(/\n$/, "").replace(/\s+$/, "");
|
|
1533
|
-
const snippet = trimmed ? `${trimmed}\u2026` : "\u2026";
|
|
1534
|
-
return `${snippet}
|
|
1535
|
-
${note}`;
|
|
1703
|
+
// tools/warp_grep/agent/helpers.ts
|
|
1704
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
1705
|
+
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
1706
|
+
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
1707
|
+
const turnsRemaining = maxTurns - turnsUsed;
|
|
1708
|
+
if (turnsRemaining === 1) {
|
|
1709
|
+
return `
|
|
1710
|
+
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`;
|
|
1711
|
+
}
|
|
1712
|
+
return `
|
|
1713
|
+
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
1714
|
+
}
|
|
1715
|
+
function calculateContextBudget(messages) {
|
|
1716
|
+
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
1717
|
+
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
1718
|
+
const percent = Math.round(totalChars / maxChars * 100);
|
|
1719
|
+
const usedK = Math.round(totalChars / 1e3);
|
|
1720
|
+
const maxK = Math.round(maxChars / 1e3);
|
|
1721
|
+
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
|
|
1536
1722
|
}
|
|
1537
|
-
function
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1723
|
+
async function buildInitialState(repoRoot, query, provider) {
|
|
1724
|
+
try {
|
|
1725
|
+
const entries = await provider.listDirectory({
|
|
1726
|
+
path: ".",
|
|
1727
|
+
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
1728
|
+
maxDepth: 2
|
|
1729
|
+
});
|
|
1730
|
+
const treeLines = entries.map((e) => {
|
|
1731
|
+
const indent = " ".repeat(e.depth);
|
|
1732
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
1733
|
+
return `${indent}${name}`;
|
|
1734
|
+
});
|
|
1735
|
+
const repoName = import_path2.default.basename(repoRoot);
|
|
1736
|
+
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
1737
|
+
${treeLines.join("\n")}` : `${repoName}/`;
|
|
1738
|
+
return `<repo_structure>
|
|
1739
|
+
${treeOutput}
|
|
1740
|
+
</repo_structure>
|
|
1741
|
+
|
|
1742
|
+
<search_string>
|
|
1743
|
+
${query}
|
|
1744
|
+
</search_string>`;
|
|
1745
|
+
} catch {
|
|
1746
|
+
const repoName = import_path2.default.basename(repoRoot);
|
|
1747
|
+
return `<repo_structure>
|
|
1748
|
+
${repoName}/
|
|
1749
|
+
</repo_structure>
|
|
1750
|
+
|
|
1751
|
+
<search_string>
|
|
1752
|
+
${query}
|
|
1753
|
+
</search_string>`;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
1757
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
1758
|
+
if (getTotalChars() <= maxChars) {
|
|
1759
|
+
return messages;
|
|
1760
|
+
}
|
|
1761
|
+
const userIndices = [];
|
|
1762
|
+
let firstUserSkipped = false;
|
|
1763
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1764
|
+
if (messages[i].role === "user") {
|
|
1765
|
+
if (!firstUserSkipped) {
|
|
1766
|
+
firstUserSkipped = true;
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
userIndices.push(i);
|
|
1545
1770
|
}
|
|
1546
|
-
matchesByFile.get(match.path).push(match);
|
|
1547
1771
|
}
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
if (index > 0) {
|
|
1552
|
-
lines.push("");
|
|
1553
|
-
}
|
|
1554
|
-
lines.push(filePath);
|
|
1555
|
-
const sortedMatches = matchesByFile.get(filePath).slice().sort((a, b) => a.lineNumber - b.lineNumber);
|
|
1556
|
-
for (const match of sortedMatches) {
|
|
1557
|
-
lines.push(`${match.lineNumber}:${match.content}`);
|
|
1558
|
-
}
|
|
1559
|
-
});
|
|
1560
|
-
return truncateOutput(lines.join("\n"), maxChars);
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
// tools/warp_grep/tools/finish.ts
|
|
1564
|
-
async function readFinishFiles(repoRoot, files, reader) {
|
|
1565
|
-
const out = [];
|
|
1566
|
-
for (const f of files) {
|
|
1567
|
-
const ranges = mergeRanges(f.lines);
|
|
1568
|
-
const chunks = [];
|
|
1569
|
-
for (const [s, e] of ranges) {
|
|
1570
|
-
const lines = await reader(f.path, s, e);
|
|
1571
|
-
chunks.push(lines.join("\n"));
|
|
1772
|
+
for (const idx of userIndices) {
|
|
1773
|
+
if (getTotalChars() <= maxChars) {
|
|
1774
|
+
break;
|
|
1572
1775
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
return out;
|
|
1576
|
-
}
|
|
1577
|
-
function mergeRanges(ranges) {
|
|
1578
|
-
if (!ranges.length) return [];
|
|
1579
|
-
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
1580
|
-
const merged = [];
|
|
1581
|
-
let [cs, ce] = sorted[0];
|
|
1582
|
-
for (let i = 1; i < sorted.length; i++) {
|
|
1583
|
-
const [s, e] = sorted[i];
|
|
1584
|
-
if (s <= ce + 1) {
|
|
1585
|
-
ce = Math.max(ce, e);
|
|
1586
|
-
} else {
|
|
1587
|
-
merged.push([cs, ce]);
|
|
1588
|
-
cs = s;
|
|
1589
|
-
ce = e;
|
|
1776
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
1777
|
+
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
1590
1778
|
}
|
|
1591
1779
|
}
|
|
1592
|
-
|
|
1593
|
-
return merged;
|
|
1780
|
+
return messages;
|
|
1594
1781
|
}
|
|
1595
1782
|
|
|
1596
1783
|
// tools/warp_grep/agent/runner.ts
|
|
1597
|
-
var
|
|
1784
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
1598
1785
|
var parser = new LLMResponseParser();
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
const files = entries.filter((e) => e.type === "file").map((f) => f.name).slice(0, 50);
|
|
1604
|
-
const parts = [
|
|
1605
|
-
`<repo_root>${repoRoot}</repo_root>`,
|
|
1606
|
-
`<top_dirs>${dirs.join(", ")}</top_dirs>`,
|
|
1607
|
-
`<top_files>${files.join(", ")}</top_files>`
|
|
1608
|
-
];
|
|
1609
|
-
return parts.join("\n");
|
|
1610
|
-
} catch {
|
|
1611
|
-
return `<repo_root>${repoRoot}</repo_root>`;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
async function callModel(messages, model, apiKey) {
|
|
1615
|
-
const api = "https://api.morphllm.com/v1/chat/completions";
|
|
1786
|
+
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
1787
|
+
async function callModel(messages, model, options = {}) {
|
|
1788
|
+
const baseUrl = DEFAULT_API_URL;
|
|
1789
|
+
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
1616
1790
|
const fetchPromise = fetchWithRetry(
|
|
1617
|
-
|
|
1791
|
+
`${baseUrl}/v1/chat/completions`,
|
|
1618
1792
|
{
|
|
1619
1793
|
method: "POST",
|
|
1620
1794
|
headers: {
|
|
1621
1795
|
"Content-Type": "application/json",
|
|
1622
|
-
Authorization: `Bearer ${apiKey
|
|
1796
|
+
Authorization: `Bearer ${apiKey}`
|
|
1623
1797
|
},
|
|
1624
1798
|
body: JSON.stringify({
|
|
1625
1799
|
model,
|
|
@@ -1628,10 +1802,15 @@ async function callModel(messages, model, apiKey) {
|
|
|
1628
1802
|
messages
|
|
1629
1803
|
})
|
|
1630
1804
|
},
|
|
1631
|
-
|
|
1805
|
+
options.retryConfig
|
|
1632
1806
|
);
|
|
1633
1807
|
const resp = await withTimeout(fetchPromise, AGENT_CONFIG.TIMEOUT_MS, "morph-warp-grep request timed out");
|
|
1634
1808
|
if (!resp.ok) {
|
|
1809
|
+
if (resp.status === 404) {
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
"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"
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1635
1814
|
const t = await resp.text();
|
|
1636
1815
|
throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
|
|
1637
1816
|
}
|
|
@@ -1643,23 +1822,24 @@ async function callModel(messages, model, apiKey) {
|
|
|
1643
1822
|
return content;
|
|
1644
1823
|
}
|
|
1645
1824
|
async function runWarpGrep(config) {
|
|
1646
|
-
const repoRoot =
|
|
1825
|
+
const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
|
|
1647
1826
|
const messages = [];
|
|
1648
|
-
|
|
1649
|
-
messages.push(systemMessage);
|
|
1650
|
-
const queryContent = `<query>${config.query}</query>`;
|
|
1651
|
-
messages.push({ role: "user", content: queryContent });
|
|
1827
|
+
messages.push({ role: "system", content: getSystemPrompt() });
|
|
1652
1828
|
const initialState = await buildInitialState(repoRoot, config.query, config.provider);
|
|
1653
1829
|
messages.push({ role: "user", content: initialState });
|
|
1654
|
-
const
|
|
1830
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
1655
1831
|
const model = config.model || DEFAULT_MODEL;
|
|
1656
1832
|
const provider = config.provider;
|
|
1657
1833
|
const errors = [];
|
|
1658
|
-
const grepState = new GrepState();
|
|
1659
1834
|
let finishMeta;
|
|
1660
1835
|
let terminationReason = "terminated";
|
|
1661
|
-
for (let
|
|
1662
|
-
|
|
1836
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1837
|
+
enforceContextLimit(messages);
|
|
1838
|
+
const assistantContent = await callModel(messages, model, {
|
|
1839
|
+
morphApiKey: config.morphApiKey,
|
|
1840
|
+
morphApiUrl: config.morphApiUrl,
|
|
1841
|
+
retryConfig: config.retryConfig
|
|
1842
|
+
}).catch((e) => {
|
|
1663
1843
|
errors.push({ message: e instanceof Error ? e.message : String(e) });
|
|
1664
1844
|
return "";
|
|
1665
1845
|
});
|
|
@@ -1667,13 +1847,13 @@ async function runWarpGrep(config) {
|
|
|
1667
1847
|
messages.push({ role: "assistant", content: assistantContent });
|
|
1668
1848
|
const toolCalls = parser.parse(assistantContent);
|
|
1669
1849
|
if (toolCalls.length === 0) {
|
|
1670
|
-
errors.push({ message: "No tool calls produced by the model." });
|
|
1850
|
+
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" });
|
|
1671
1851
|
terminationReason = "terminated";
|
|
1672
1852
|
break;
|
|
1673
1853
|
}
|
|
1674
1854
|
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1675
1855
|
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1676
|
-
const
|
|
1856
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1677
1857
|
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1678
1858
|
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1679
1859
|
const formatted = [];
|
|
@@ -1681,69 +1861,42 @@ async function runWarpGrep(config) {
|
|
|
1681
1861
|
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1682
1862
|
formatted.push(msg);
|
|
1683
1863
|
}
|
|
1684
|
-
const
|
|
1685
|
-
for (const c of
|
|
1864
|
+
const allPromises = [];
|
|
1865
|
+
for (const c of grepCalls) {
|
|
1686
1866
|
const args = c.arguments ?? {};
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
(
|
|
1690
|
-
(err) => formatAgentToolOutput("
|
|
1867
|
+
allPromises.push(
|
|
1868
|
+
toolGrep(provider, args).then(
|
|
1869
|
+
({ output }) => formatAgentToolOutput("grep", args, output, { isError: false }),
|
|
1870
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1871
|
+
)
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1874
|
+
for (const c of listDirCalls) {
|
|
1875
|
+
const args = c.arguments ?? {};
|
|
1876
|
+
allPromises.push(
|
|
1877
|
+
toolListDirectory(provider, args).then(
|
|
1878
|
+
(p) => formatAgentToolOutput("list_directory", args, p, { isError: false }),
|
|
1879
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1691
1880
|
)
|
|
1692
1881
|
);
|
|
1693
1882
|
}
|
|
1694
1883
|
for (const c of readCalls) {
|
|
1695
1884
|
const args = c.arguments ?? {};
|
|
1696
|
-
|
|
1885
|
+
allPromises.push(
|
|
1697
1886
|
toolRead(provider, args).then(
|
|
1698
1887
|
(p) => formatAgentToolOutput("read", args, p, { isError: false }),
|
|
1699
1888
|
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1700
1889
|
)
|
|
1701
1890
|
);
|
|
1702
1891
|
}
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
const args = c.arguments ?? {};
|
|
1707
|
-
try {
|
|
1708
|
-
const grepRes = await provider.grep({ pattern: args.pattern, path: args.path });
|
|
1709
|
-
if (grepRes.error) {
|
|
1710
|
-
errors.push({ message: grepRes.error });
|
|
1711
|
-
terminationReason = "terminated";
|
|
1712
|
-
return {
|
|
1713
|
-
terminationReason: "terminated",
|
|
1714
|
-
messages,
|
|
1715
|
-
errors
|
|
1716
|
-
};
|
|
1717
|
-
}
|
|
1718
|
-
const rawOutput = Array.isArray(grepRes.lines) ? grepRes.lines.join("\n") : "";
|
|
1719
|
-
const newMatches = parseAndFilterGrepOutput(rawOutput, grepState);
|
|
1720
|
-
let formattedPayload = formatTurnGrepOutput(newMatches);
|
|
1721
|
-
if (formattedPayload === "No new matches found.") {
|
|
1722
|
-
formattedPayload = "no new matches";
|
|
1723
|
-
}
|
|
1724
|
-
formatted.push(formatAgentToolOutput("grep", args, formattedPayload, { isError: false }));
|
|
1725
|
-
} catch (err) {
|
|
1726
|
-
formatted.push(formatAgentToolOutput("grep", args, String(err), { isError: true }));
|
|
1727
|
-
}
|
|
1892
|
+
const allResults = await Promise.all(allPromises);
|
|
1893
|
+
for (const result of allResults) {
|
|
1894
|
+
formatted.push(result);
|
|
1728
1895
|
}
|
|
1729
1896
|
if (formatted.length > 0) {
|
|
1730
|
-
const
|
|
1731
|
-
const
|
|
1732
|
-
|
|
1733
|
-
if (turnsRemaining === 0) {
|
|
1734
|
-
turnMessage = `
|
|
1735
|
-
|
|
1736
|
-
[Turn ${turnsUsed}/4] This is your LAST turn. You MUST call the finish tool now.`;
|
|
1737
|
-
} else if (turnsRemaining === 1) {
|
|
1738
|
-
turnMessage = `
|
|
1739
|
-
|
|
1740
|
-
[Turn ${turnsUsed}/4] You have 1 turn remaining. Next turn you MUST call the finish tool.`;
|
|
1741
|
-
} else {
|
|
1742
|
-
turnMessage = `
|
|
1743
|
-
|
|
1744
|
-
[Turn ${turnsUsed}/4] You have ${turnsRemaining} turns remaining.`;
|
|
1745
|
-
}
|
|
1746
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage });
|
|
1897
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1898
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1899
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1747
1900
|
}
|
|
1748
1901
|
if (finishCalls.length) {
|
|
1749
1902
|
const fc = finishCalls[0];
|
|
@@ -1793,7 +1946,7 @@ async function runWarpGrep(config) {
|
|
|
1793
1946
|
|
|
1794
1947
|
// tools/warp_grep/providers/local.ts
|
|
1795
1948
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
1796
|
-
var
|
|
1949
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
1797
1950
|
|
|
1798
1951
|
// tools/warp_grep/utils/ripgrep.ts
|
|
1799
1952
|
var import_child_process = require("child_process");
|
|
@@ -1858,21 +2011,21 @@ async function runRipgrep(args, opts) {
|
|
|
1858
2011
|
|
|
1859
2012
|
// tools/warp_grep/utils/paths.ts
|
|
1860
2013
|
var import_fs = __toESM(require("fs"), 1);
|
|
1861
|
-
var
|
|
2014
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
1862
2015
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
1863
|
-
const absRoot =
|
|
1864
|
-
const resolved =
|
|
2016
|
+
const absRoot = import_path4.default.resolve(repoRoot);
|
|
2017
|
+
const resolved = import_path4.default.resolve(absRoot, targetPath);
|
|
1865
2018
|
ensureWithinRepo(absRoot, resolved);
|
|
1866
2019
|
return resolved;
|
|
1867
2020
|
}
|
|
1868
2021
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
1869
|
-
const rel =
|
|
1870
|
-
if (rel.startsWith("..") ||
|
|
2022
|
+
const rel = import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absTarget));
|
|
2023
|
+
if (rel.startsWith("..") || import_path4.default.isAbsolute(rel)) {
|
|
1871
2024
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
1872
2025
|
}
|
|
1873
2026
|
}
|
|
1874
2027
|
function toRepoRelative(repoRoot, absPath) {
|
|
1875
|
-
return
|
|
2028
|
+
return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
|
|
1876
2029
|
}
|
|
1877
2030
|
function isSymlink(p) {
|
|
1878
2031
|
try {
|
|
@@ -1915,10 +2068,18 @@ var LocalRipgrepProvider = class {
|
|
|
1915
2068
|
this.excludes = excludes;
|
|
1916
2069
|
}
|
|
1917
2070
|
async grep(params) {
|
|
1918
|
-
|
|
2071
|
+
let abs;
|
|
2072
|
+
try {
|
|
2073
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
2074
|
+
} catch (err) {
|
|
2075
|
+
return {
|
|
2076
|
+
lines: [],
|
|
2077
|
+
error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
1919
2080
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
1920
2081
|
if (!stat) return { lines: [] };
|
|
1921
|
-
const targetArg = abs ===
|
|
2082
|
+
const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
1922
2083
|
const args = [
|
|
1923
2084
|
"--no-config",
|
|
1924
2085
|
"--no-heading",
|
|
@@ -1927,6 +2088,9 @@ var LocalRipgrepProvider = class {
|
|
|
1927
2088
|
"--color=never",
|
|
1928
2089
|
"--trim",
|
|
1929
2090
|
"--max-columns=400",
|
|
2091
|
+
"-C",
|
|
2092
|
+
"1",
|
|
2093
|
+
...params.glob ? ["--glob", params.glob] : [],
|
|
1930
2094
|
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1931
2095
|
params.pattern,
|
|
1932
2096
|
targetArg || "."
|
|
@@ -1951,29 +2115,24 @@ Details: ${res.stderr}` : ""}`
|
|
|
1951
2115
|
};
|
|
1952
2116
|
}
|
|
1953
2117
|
const lines = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
const args = [
|
|
1960
|
-
"--no-config",
|
|
1961
|
-
"--files",
|
|
1962
|
-
"-g",
|
|
1963
|
-
params.pattern,
|
|
1964
|
-
...this.excludes.flatMap((e) => ["-g", `!${e}`]),
|
|
1965
|
-
targetArg || "."
|
|
1966
|
-
];
|
|
1967
|
-
const res = await runRipgrep(args, { cwd: this.repoRoot });
|
|
1968
|
-
if (res.exitCode === -1) {
|
|
1969
|
-
console.warn(`[warp_grep] ripgrep not available for glob: ${res.stderr || "execution failed"}`);
|
|
1970
|
-
return { files: [] };
|
|
2118
|
+
if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
2119
|
+
return {
|
|
2120
|
+
lines: [],
|
|
2121
|
+
error: "query not specific enough, tool tried to return too much context and failed"
|
|
2122
|
+
};
|
|
1971
2123
|
}
|
|
1972
|
-
|
|
1973
|
-
return { files };
|
|
2124
|
+
return { lines };
|
|
1974
2125
|
}
|
|
1975
2126
|
async read(params) {
|
|
1976
|
-
|
|
2127
|
+
let abs;
|
|
2128
|
+
try {
|
|
2129
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
2130
|
+
} catch (err) {
|
|
2131
|
+
return {
|
|
2132
|
+
lines: [],
|
|
2133
|
+
error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
1977
2136
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
1978
2137
|
if (!stat || !stat.isFile()) {
|
|
1979
2138
|
return {
|
|
@@ -1993,7 +2152,15 @@ Details: ${res.stderr}` : ""}`
|
|
|
1993
2152
|
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.`
|
|
1994
2153
|
};
|
|
1995
2154
|
}
|
|
1996
|
-
|
|
2155
|
+
let lines;
|
|
2156
|
+
try {
|
|
2157
|
+
lines = await readAllLines(abs);
|
|
2158
|
+
} catch (err) {
|
|
2159
|
+
return {
|
|
2160
|
+
lines: [],
|
|
2161
|
+
error: `[READ ERROR] Failed to read "${params.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
1997
2164
|
const total = lines.length;
|
|
1998
2165
|
let s = params.start ?? 1;
|
|
1999
2166
|
let e = Math.min(params.end ?? total, total);
|
|
@@ -2006,30 +2173,46 @@ Details: ${res.stderr}` : ""}`
|
|
|
2006
2173
|
const content = lines[i - 1] ?? "";
|
|
2007
2174
|
out.push(`${i}|${content}`);
|
|
2008
2175
|
}
|
|
2176
|
+
if (out.length > AGENT_CONFIG.MAX_READ_LINES) {
|
|
2177
|
+
const truncated = out.slice(0, AGENT_CONFIG.MAX_READ_LINES);
|
|
2178
|
+
truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${out.length} lines]`);
|
|
2179
|
+
return { lines: truncated };
|
|
2180
|
+
}
|
|
2009
2181
|
return { lines: out };
|
|
2010
2182
|
}
|
|
2011
|
-
async
|
|
2012
|
-
|
|
2183
|
+
async listDirectory(params) {
|
|
2184
|
+
let abs;
|
|
2185
|
+
try {
|
|
2186
|
+
abs = resolveUnderRepo(this.repoRoot, params.path);
|
|
2187
|
+
} catch {
|
|
2188
|
+
return [];
|
|
2189
|
+
}
|
|
2013
2190
|
const stat = await import_promises3.default.stat(abs).catch(() => null);
|
|
2014
2191
|
if (!stat || !stat.isDirectory()) {
|
|
2015
2192
|
return [];
|
|
2016
2193
|
}
|
|
2017
|
-
const maxResults = params.maxResults ??
|
|
2018
|
-
const maxDepth = params.maxDepth ??
|
|
2194
|
+
const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
2195
|
+
const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
2019
2196
|
const regex = params.pattern ? new RegExp(params.pattern) : null;
|
|
2020
2197
|
const results = [];
|
|
2198
|
+
let timedOut = false;
|
|
2199
|
+
const startTime = Date.now();
|
|
2021
2200
|
async function walk(dir, depth) {
|
|
2201
|
+
if (Date.now() - startTime > AGENT_CONFIG.LIST_TIMEOUT_MS) {
|
|
2202
|
+
timedOut = true;
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2022
2205
|
if (depth > maxDepth || results.length >= maxResults) return;
|
|
2023
2206
|
const entries = await import_promises3.default.readdir(dir, { withFileTypes: true });
|
|
2024
2207
|
for (const entry of entries) {
|
|
2025
|
-
|
|
2208
|
+
if (timedOut || results.length >= maxResults) break;
|
|
2209
|
+
const full = import_path5.default.join(dir, entry.name);
|
|
2026
2210
|
const rel = toRepoRelative(abs, full).replace(/^[.][/\\]?/, "");
|
|
2027
|
-
if (DEFAULT_EXCLUDES.some((ex) => rel.split(
|
|
2211
|
+
if (DEFAULT_EXCLUDES.some((ex) => rel.split(import_path5.default.sep).includes(ex))) continue;
|
|
2028
2212
|
if (regex && !regex.test(entry.name)) continue;
|
|
2029
|
-
if (results.length >= maxResults) break;
|
|
2030
2213
|
results.push({
|
|
2031
2214
|
name: entry.name,
|
|
2032
|
-
path: toRepoRelative(
|
|
2215
|
+
path: toRepoRelative(import_path5.default.resolve(""), full),
|
|
2033
2216
|
// relative display
|
|
2034
2217
|
type: entry.isDirectory() ? "dir" : "file",
|
|
2035
2218
|
depth
|
|
@@ -2044,12 +2227,103 @@ Details: ${res.stderr}` : ""}`
|
|
|
2044
2227
|
}
|
|
2045
2228
|
};
|
|
2046
2229
|
|
|
2047
|
-
// tools/warp_grep/
|
|
2230
|
+
// tools/warp_grep/providers/remote.ts
|
|
2231
|
+
var RemoteCommandsProvider = class {
|
|
2232
|
+
constructor(repoRoot, commands) {
|
|
2233
|
+
this.repoRoot = repoRoot;
|
|
2234
|
+
this.commands = commands;
|
|
2235
|
+
}
|
|
2236
|
+
/**
|
|
2237
|
+
* Run grep command and parse ripgrep output
|
|
2238
|
+
*/
|
|
2239
|
+
async grep(params) {
|
|
2240
|
+
try {
|
|
2241
|
+
const stdout = await this.commands.grep(params.pattern, params.path, params.glob);
|
|
2242
|
+
const lines = (stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
2243
|
+
if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
2244
|
+
return {
|
|
2245
|
+
lines: [],
|
|
2246
|
+
error: "Query not specific enough - too many results returned. Try a more specific pattern."
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
return { lines };
|
|
2250
|
+
} catch (error) {
|
|
2251
|
+
return {
|
|
2252
|
+
lines: [],
|
|
2253
|
+
error: `[GREP ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
/**
|
|
2258
|
+
* Read file and add line numbers
|
|
2259
|
+
*/
|
|
2260
|
+
async read(params) {
|
|
2261
|
+
const start = params.start ?? 1;
|
|
2262
|
+
const end = params.end ?? 1e6;
|
|
2263
|
+
try {
|
|
2264
|
+
const stdout = await this.commands.read(params.path, start, end);
|
|
2265
|
+
const contentLines = (stdout || "").split("\n");
|
|
2266
|
+
if (contentLines.length > 0 && contentLines[contentLines.length - 1] === "") {
|
|
2267
|
+
contentLines.pop();
|
|
2268
|
+
}
|
|
2269
|
+
const lines = contentLines.map((content, idx) => `${start + idx}|${content}`);
|
|
2270
|
+
if (lines.length > AGENT_CONFIG.MAX_READ_LINES) {
|
|
2271
|
+
const truncated = lines.slice(0, AGENT_CONFIG.MAX_READ_LINES);
|
|
2272
|
+
truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${lines.length} lines]`);
|
|
2273
|
+
return { lines: truncated };
|
|
2274
|
+
}
|
|
2275
|
+
return { lines };
|
|
2276
|
+
} catch (error) {
|
|
2277
|
+
return {
|
|
2278
|
+
lines: [],
|
|
2279
|
+
error: `[READ ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
/**
|
|
2284
|
+
* List directory and parse find output
|
|
2285
|
+
*/
|
|
2286
|
+
async listDirectory(params) {
|
|
2287
|
+
const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
2288
|
+
const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
2289
|
+
try {
|
|
2290
|
+
const stdout = await this.commands.listDir(params.path, maxDepth);
|
|
2291
|
+
const paths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
|
|
2292
|
+
const regex = params.pattern ? new RegExp(params.pattern) : null;
|
|
2293
|
+
const entries = [];
|
|
2294
|
+
for (const fullPath of paths) {
|
|
2295
|
+
if (fullPath === params.path || fullPath === this.repoRoot) continue;
|
|
2296
|
+
const name = fullPath.split("/").pop() || "";
|
|
2297
|
+
if (regex && !regex.test(name)) continue;
|
|
2298
|
+
let relativePath = fullPath;
|
|
2299
|
+
if (fullPath.startsWith(this.repoRoot)) {
|
|
2300
|
+
relativePath = fullPath.slice(this.repoRoot.length).replace(/^\//, "");
|
|
2301
|
+
}
|
|
2302
|
+
const depth = relativePath.split("/").filter(Boolean).length - 1;
|
|
2303
|
+
const hasExtension = name.includes(".") && !name.startsWith(".");
|
|
2304
|
+
const type = hasExtension ? "file" : "dir";
|
|
2305
|
+
entries.push({
|
|
2306
|
+
name,
|
|
2307
|
+
path: relativePath,
|
|
2308
|
+
type,
|
|
2309
|
+
depth: Math.max(0, depth)
|
|
2310
|
+
});
|
|
2311
|
+
if (entries.length >= maxResults) break;
|
|
2312
|
+
}
|
|
2313
|
+
return entries;
|
|
2314
|
+
} catch (error) {
|
|
2315
|
+
return [];
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
|
|
2320
|
+
// tools/warp_grep/client.ts
|
|
2048
2321
|
var WarpGrepClient = class {
|
|
2049
2322
|
config;
|
|
2050
2323
|
constructor(config = {}) {
|
|
2051
2324
|
this.config = {
|
|
2052
|
-
|
|
2325
|
+
morphApiKey: config.morphApiKey,
|
|
2326
|
+
morphApiUrl: config.morphApiUrl,
|
|
2053
2327
|
debug: config.debug,
|
|
2054
2328
|
timeout: config.timeout,
|
|
2055
2329
|
retryConfig: config.retryConfig
|
|
@@ -2077,34 +2351,46 @@ var WarpGrepClient = class {
|
|
|
2077
2351
|
* ```
|
|
2078
2352
|
*/
|
|
2079
2353
|
async execute(input) {
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
error: "Search did not complete"
|
|
2095
|
-
};
|
|
2096
|
-
}
|
|
2097
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
2098
|
-
file: r.path,
|
|
2099
|
-
content: r.content
|
|
2100
|
-
}));
|
|
2101
|
-
return {
|
|
2102
|
-
success: true,
|
|
2103
|
-
contexts,
|
|
2104
|
-
summary: finish.payload
|
|
2105
|
-
};
|
|
2354
|
+
return executeToolCall(
|
|
2355
|
+
{ query: input.query },
|
|
2356
|
+
{
|
|
2357
|
+
repoRoot: input.repoRoot,
|
|
2358
|
+
remoteCommands: input.remoteCommands,
|
|
2359
|
+
provider: input.provider,
|
|
2360
|
+
excludes: input.excludes,
|
|
2361
|
+
includes: input.includes,
|
|
2362
|
+
debug: input.debug ?? this.config.debug,
|
|
2363
|
+
morphApiKey: this.config.morphApiKey,
|
|
2364
|
+
morphApiUrl: this.config.morphApiUrl,
|
|
2365
|
+
retryConfig: this.config.retryConfig
|
|
2366
|
+
}
|
|
2367
|
+
);
|
|
2106
2368
|
}
|
|
2107
2369
|
};
|
|
2370
|
+
async function executeToolCall(input, config) {
|
|
2371
|
+
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
2372
|
+
const provider = config.remoteCommands ? new RemoteCommandsProvider(config.repoRoot, config.remoteCommands) : config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
2373
|
+
const result = await runWarpGrep({
|
|
2374
|
+
query: parsed.query,
|
|
2375
|
+
repoRoot: config.repoRoot,
|
|
2376
|
+
provider,
|
|
2377
|
+
excludes: config.excludes,
|
|
2378
|
+
includes: config.includes,
|
|
2379
|
+
debug: config.debug ?? false,
|
|
2380
|
+
morphApiKey: config.morphApiKey,
|
|
2381
|
+
morphApiUrl: config.morphApiUrl,
|
|
2382
|
+
retryConfig: config.retryConfig
|
|
2383
|
+
});
|
|
2384
|
+
const finish = result.finish;
|
|
2385
|
+
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
2386
|
+
return { success: false, error: "Search did not complete" };
|
|
2387
|
+
}
|
|
2388
|
+
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
2389
|
+
file: r.path,
|
|
2390
|
+
content: r.content
|
|
2391
|
+
}));
|
|
2392
|
+
return { success: true, contexts, summary: finish.payload };
|
|
2393
|
+
}
|
|
2108
2394
|
function formatResult(result) {
|
|
2109
2395
|
if (!result.success) {
|
|
2110
2396
|
return `Search failed: ${result.error}`;
|
|
@@ -2884,29 +3170,7 @@ var TOOL_PARAMETERS = {
|
|
|
2884
3170
|
},
|
|
2885
3171
|
required: ["query"]
|
|
2886
3172
|
};
|
|
2887
|
-
|
|
2888
|
-
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
2889
|
-
const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
2890
|
-
const result = await runWarpGrep({
|
|
2891
|
-
query: parsed.query,
|
|
2892
|
-
repoRoot: config.repoRoot,
|
|
2893
|
-
provider,
|
|
2894
|
-
excludes: config.excludes,
|
|
2895
|
-
includes: config.includes,
|
|
2896
|
-
debug: config.debug ?? false,
|
|
2897
|
-
apiKey: config.apiKey
|
|
2898
|
-
});
|
|
2899
|
-
const finish = result.finish;
|
|
2900
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
2901
|
-
return { success: false, error: "Search did not complete" };
|
|
2902
|
-
}
|
|
2903
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
2904
|
-
file: r.path,
|
|
2905
|
-
content: r.content
|
|
2906
|
-
}));
|
|
2907
|
-
return { success: true, contexts, summary: finish.payload };
|
|
2908
|
-
}
|
|
2909
|
-
function createMorphWarpGrepTool(config) {
|
|
3173
|
+
function createWarpGrepTool(config) {
|
|
2910
3174
|
const tool4 = {
|
|
2911
3175
|
type: "function",
|
|
2912
3176
|
function: {
|
|
@@ -2917,7 +3181,7 @@ function createMorphWarpGrepTool(config) {
|
|
|
2917
3181
|
};
|
|
2918
3182
|
return Object.assign(tool4, {
|
|
2919
3183
|
execute: async (input) => {
|
|
2920
|
-
return
|
|
3184
|
+
return executeToolCall(input, config);
|
|
2921
3185
|
},
|
|
2922
3186
|
formatResult: (result) => {
|
|
2923
3187
|
return formatResult(result);
|
|
@@ -3093,7 +3357,7 @@ var editFileTool = {
|
|
|
3093
3357
|
}
|
|
3094
3358
|
}
|
|
3095
3359
|
};
|
|
3096
|
-
async function
|
|
3360
|
+
async function execute(input, config) {
|
|
3097
3361
|
return executeEditFile(input, config);
|
|
3098
3362
|
}
|
|
3099
3363
|
function getSystemPrompt2() {
|
|
@@ -3131,7 +3395,7 @@ function createEditFileTool(config = {}) {
|
|
|
3131
3395
|
return Object.assign({}, toolDef, {
|
|
3132
3396
|
execute: async (input) => {
|
|
3133
3397
|
const parsedInput = typeof input === "string" ? JSON.parse(input) : input;
|
|
3134
|
-
return
|
|
3398
|
+
return execute(parsedInput, config);
|
|
3135
3399
|
},
|
|
3136
3400
|
formatResult: (result) => {
|
|
3137
3401
|
return formatResult3(result);
|
|
@@ -3150,13 +3414,13 @@ var OpenAIToolFactory = class {
|
|
|
3150
3414
|
/**
|
|
3151
3415
|
* Create an OpenAI-compatible warp grep tool
|
|
3152
3416
|
*
|
|
3153
|
-
* @param toolConfig - Tool configuration (
|
|
3417
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3154
3418
|
* @returns OpenAI ChatCompletionTool with execute and formatResult methods
|
|
3155
3419
|
*/
|
|
3156
3420
|
createWarpGrepTool(toolConfig) {
|
|
3157
|
-
return
|
|
3421
|
+
return createWarpGrepTool({
|
|
3158
3422
|
...toolConfig,
|
|
3159
|
-
|
|
3423
|
+
morphApiKey: this.config.apiKey
|
|
3160
3424
|
});
|
|
3161
3425
|
}
|
|
3162
3426
|
/**
|
|
@@ -3193,29 +3457,7 @@ var INPUT_SCHEMA = {
|
|
|
3193
3457
|
},
|
|
3194
3458
|
required: ["query"]
|
|
3195
3459
|
};
|
|
3196
|
-
|
|
3197
|
-
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
3198
|
-
const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
|
|
3199
|
-
const result = await runWarpGrep({
|
|
3200
|
-
query: parsed.query,
|
|
3201
|
-
repoRoot: config.repoRoot,
|
|
3202
|
-
provider,
|
|
3203
|
-
excludes: config.excludes,
|
|
3204
|
-
includes: config.includes,
|
|
3205
|
-
debug: config.debug ?? false,
|
|
3206
|
-
apiKey: config.apiKey
|
|
3207
|
-
});
|
|
3208
|
-
const finish = result.finish;
|
|
3209
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
3210
|
-
return { success: false, error: "Search did not complete" };
|
|
3211
|
-
}
|
|
3212
|
-
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
3213
|
-
file: r.path,
|
|
3214
|
-
content: r.content
|
|
3215
|
-
}));
|
|
3216
|
-
return { success: true, contexts, summary: finish.payload };
|
|
3217
|
-
}
|
|
3218
|
-
function createMorphWarpGrepTool2(config) {
|
|
3460
|
+
function createWarpGrepTool2(config) {
|
|
3219
3461
|
const tool4 = {
|
|
3220
3462
|
name: config.name ?? WARP_GREP_TOOL_NAME,
|
|
3221
3463
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
@@ -3223,7 +3465,7 @@ function createMorphWarpGrepTool2(config) {
|
|
|
3223
3465
|
};
|
|
3224
3466
|
return Object.assign(tool4, {
|
|
3225
3467
|
execute: async (input) => {
|
|
3226
|
-
return
|
|
3468
|
+
return executeToolCall(input, config);
|
|
3227
3469
|
},
|
|
3228
3470
|
formatResult: (result) => {
|
|
3229
3471
|
return formatResult(result);
|
|
@@ -3373,13 +3615,13 @@ var AnthropicToolFactory = class {
|
|
|
3373
3615
|
/**
|
|
3374
3616
|
* Create an Anthropic-compatible warp grep tool
|
|
3375
3617
|
*
|
|
3376
|
-
* @param toolConfig - Tool configuration (
|
|
3618
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3377
3619
|
* @returns Anthropic Tool with execute and formatResult methods
|
|
3378
3620
|
*/
|
|
3379
3621
|
createWarpGrepTool(toolConfig) {
|
|
3380
|
-
return
|
|
3622
|
+
return createWarpGrepTool2({
|
|
3381
3623
|
...toolConfig,
|
|
3382
|
-
|
|
3624
|
+
morphApiKey: this.config.apiKey
|
|
3383
3625
|
});
|
|
3384
3626
|
}
|
|
3385
3627
|
/**
|
|
@@ -3411,33 +3653,23 @@ var AnthropicToolFactory = class {
|
|
|
3411
3653
|
// tools/warp_grep/vercel.ts
|
|
3412
3654
|
var import_ai = require("ai");
|
|
3413
3655
|
var import_zod = require("zod");
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3656
|
+
function createWarpGrepTool3(config) {
|
|
3657
|
+
const schema = import_zod.z.object({
|
|
3658
|
+
query: import_zod.z.string().describe("Free-form repository question")
|
|
3659
|
+
});
|
|
3418
3660
|
return (0, import_ai.tool)({
|
|
3419
3661
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
3420
|
-
inputSchema:
|
|
3662
|
+
inputSchema: schema,
|
|
3421
3663
|
execute: async (params) => {
|
|
3422
|
-
const
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
repoRoot: config.repoRoot,
|
|
3426
|
-
provider,
|
|
3427
|
-
excludes: config.excludes,
|
|
3428
|
-
includes: config.includes,
|
|
3429
|
-
debug: config.debug ?? false,
|
|
3430
|
-
apiKey: config.apiKey
|
|
3431
|
-
});
|
|
3432
|
-
const finish = result.finish;
|
|
3433
|
-
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
3434
|
-
return { success: false, error: "Search did not complete" };
|
|
3664
|
+
const result = await executeToolCall(params, config);
|
|
3665
|
+
if (!result.success) {
|
|
3666
|
+
throw new Error(`Failed to search codebase: ${result.error}`);
|
|
3435
3667
|
}
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3668
|
+
return {
|
|
3669
|
+
success: true,
|
|
3670
|
+
contexts: result.contexts,
|
|
3671
|
+
summary: result.summary
|
|
3672
|
+
};
|
|
3441
3673
|
}
|
|
3442
3674
|
});
|
|
3443
3675
|
}
|
|
@@ -3550,13 +3782,13 @@ var VercelToolFactory = class {
|
|
|
3550
3782
|
/**
|
|
3551
3783
|
* Create a Vercel AI SDK-compatible warp grep tool
|
|
3552
3784
|
*
|
|
3553
|
-
* @param toolConfig - Tool configuration (
|
|
3785
|
+
* @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
|
|
3554
3786
|
* @returns Vercel AI SDK tool
|
|
3555
3787
|
*/
|
|
3556
3788
|
createWarpGrepTool(toolConfig) {
|
|
3557
|
-
return
|
|
3789
|
+
return createWarpGrepTool3({
|
|
3558
3790
|
...toolConfig,
|
|
3559
|
-
|
|
3791
|
+
morphApiKey: this.config.apiKey
|
|
3560
3792
|
});
|
|
3561
3793
|
}
|
|
3562
3794
|
/**
|
|
@@ -3636,7 +3868,7 @@ var MorphClient = class {
|
|
|
3636
3868
|
retryConfig: config.retryConfig
|
|
3637
3869
|
});
|
|
3638
3870
|
this.warpGrep = new WarpGrepClient({
|
|
3639
|
-
|
|
3871
|
+
morphApiKey: config.apiKey,
|
|
3640
3872
|
debug: config.debug,
|
|
3641
3873
|
timeout: config.timeout,
|
|
3642
3874
|
retryConfig: config.retryConfig
|