@morphllm/morphsdk 0.2.57 → 0.2.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/dist/anthropic-CaFUHxBW.d.ts +89 -0
  2. package/dist/{chunk-6X5UOY7B.js → chunk-2CASO3ZO.js} +46 -79
  3. package/dist/chunk-2CASO3ZO.js.map +1 -0
  4. package/dist/chunk-374N3GIA.js +118 -0
  5. package/dist/chunk-374N3GIA.js.map +1 -0
  6. package/dist/chunk-3IQIT6MC.js +65 -0
  7. package/dist/chunk-3IQIT6MC.js.map +1 -0
  8. package/dist/chunk-4VGOBA2J.js +57 -0
  9. package/dist/chunk-4VGOBA2J.js.map +1 -0
  10. package/dist/chunk-527P5X2E.js +98 -0
  11. package/dist/chunk-527P5X2E.js.map +1 -0
  12. package/dist/chunk-6N6ZYZYD.js +74 -0
  13. package/dist/chunk-6N6ZYZYD.js.map +1 -0
  14. package/dist/chunk-6Y5JB4JC.js +195 -0
  15. package/dist/chunk-6Y5JB4JC.js.map +1 -0
  16. package/dist/{chunk-TICMYDII.js → chunk-APP75CBN.js} +33 -16
  17. package/dist/chunk-APP75CBN.js.map +1 -0
  18. package/dist/{chunk-QFIHUCTF.js → chunk-FN4EP3WY.js} +19 -19
  19. package/dist/chunk-FN4EP3WY.js.map +1 -0
  20. package/dist/chunk-ILJ3J5IA.js +72 -0
  21. package/dist/chunk-ILJ3J5IA.js.map +1 -0
  22. package/dist/chunk-ISWL67SF.js +1 -0
  23. package/dist/chunk-KW7OEGZK.js +9 -0
  24. package/dist/chunk-KW7OEGZK.js.map +1 -0
  25. package/dist/chunk-Q5AHGIQO.js +205 -0
  26. package/dist/chunk-Q5AHGIQO.js.map +1 -0
  27. package/dist/{chunk-OXHGFHEU.js → chunk-VJU3BRET.js} +3 -3
  28. package/dist/chunk-VJU3BRET.js.map +1 -0
  29. package/dist/{chunk-TJIUA27P.js → chunk-XT5ZO6ES.js} +9 -5
  30. package/dist/chunk-XT5ZO6ES.js.map +1 -0
  31. package/dist/{chunk-LVPVVLTI.js → chunk-YV75OQTE.js} +105 -17
  32. package/dist/chunk-YV75OQTE.js.map +1 -0
  33. package/dist/{chunk-ZJIIICRA.js → chunk-ZO4PPFCZ.js} +60 -29
  34. package/dist/chunk-ZO4PPFCZ.js.map +1 -0
  35. package/dist/{client-CFoR--IU.d.ts → client-CextMMm9.d.ts} +10 -15
  36. package/dist/client.cjs +689 -343
  37. package/dist/client.cjs.map +1 -1
  38. package/dist/client.d.ts +3 -2
  39. package/dist/client.js +15 -15
  40. package/dist/finish-kXAcUJyB.d.ts +33 -0
  41. package/dist/gemini-CE80Pbdy.d.ts +117 -0
  42. package/dist/git/client.cjs +2 -2
  43. package/dist/git/client.cjs.map +1 -1
  44. package/dist/git/client.d.ts +1 -1
  45. package/dist/git/client.js +1 -1
  46. package/dist/git/index.cjs +2 -2
  47. package/dist/git/index.cjs.map +1 -1
  48. package/dist/git/index.js +1 -1
  49. package/dist/git/types.cjs.map +1 -1
  50. package/dist/git/types.d.ts +1 -1
  51. package/dist/index.cjs +702 -343
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.ts +4 -3
  54. package/dist/index.js +17 -16
  55. package/dist/openai-Fvpqln7F.d.ts +89 -0
  56. package/dist/tools/warp_grep/agent/config.cjs +8 -4
  57. package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
  58. package/dist/tools/warp_grep/agent/config.d.ts +7 -2
  59. package/dist/tools/warp_grep/agent/config.js +1 -1
  60. package/dist/tools/warp_grep/agent/formatter.cjs +32 -15
  61. package/dist/tools/warp_grep/agent/formatter.cjs.map +1 -1
  62. package/dist/tools/warp_grep/agent/formatter.d.ts +1 -1
  63. package/dist/tools/warp_grep/agent/formatter.js +1 -1
  64. package/dist/tools/warp_grep/agent/parser.cjs +104 -17
  65. package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
  66. package/dist/tools/warp_grep/agent/parser.d.ts +3 -5
  67. package/dist/tools/warp_grep/agent/parser.js +1 -3
  68. package/dist/tools/warp_grep/agent/prompt.cjs +132 -56
  69. package/dist/tools/warp_grep/agent/prompt.cjs.map +1 -1
  70. package/dist/tools/warp_grep/agent/prompt.d.ts +1 -1
  71. package/dist/tools/warp_grep/agent/prompt.js +1 -1
  72. package/dist/tools/warp_grep/agent/runner.cjs +459 -192
  73. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  74. package/dist/tools/warp_grep/agent/runner.d.ts +1 -0
  75. package/dist/tools/warp_grep/agent/runner.js +6 -8
  76. package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
  77. package/dist/tools/warp_grep/agent/types.d.ts +9 -2
  78. package/dist/tools/warp_grep/anthropic.cjs +650 -260
  79. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  80. package/dist/tools/warp_grep/anthropic.d.ts +4 -74
  81. package/dist/tools/warp_grep/anthropic.js +13 -15
  82. package/dist/tools/warp_grep/client.cjs +1593 -0
  83. package/dist/tools/warp_grep/client.cjs.map +1 -0
  84. package/dist/tools/warp_grep/client.d.ts +87 -0
  85. package/dist/tools/warp_grep/client.js +26 -0
  86. package/dist/tools/warp_grep/gemini.cjs +1587 -0
  87. package/dist/tools/warp_grep/gemini.cjs.map +1 -0
  88. package/dist/tools/warp_grep/gemini.d.ts +7 -0
  89. package/dist/tools/warp_grep/gemini.js +34 -0
  90. package/dist/tools/warp_grep/harness.cjs +556 -220
  91. package/dist/tools/warp_grep/harness.cjs.map +1 -1
  92. package/dist/tools/warp_grep/harness.d.ts +50 -119
  93. package/dist/tools/warp_grep/harness.js +33 -41
  94. package/dist/tools/warp_grep/harness.js.map +1 -1
  95. package/dist/tools/warp_grep/index.cjs +812 -346
  96. package/dist/tools/warp_grep/index.cjs.map +1 -1
  97. package/dist/tools/warp_grep/index.d.ts +11 -6
  98. package/dist/tools/warp_grep/index.js +43 -22
  99. package/dist/tools/warp_grep/openai.cjs +650 -258
  100. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  101. package/dist/tools/warp_grep/openai.d.ts +4 -74
  102. package/dist/tools/warp_grep/openai.js +13 -13
  103. package/dist/tools/warp_grep/providers/local.cjs +66 -27
  104. package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
  105. package/dist/tools/warp_grep/providers/local.d.ts +4 -9
  106. package/dist/tools/warp_grep/providers/local.js +2 -2
  107. package/dist/tools/warp_grep/providers/remote.cjs +211 -0
  108. package/dist/tools/warp_grep/providers/remote.cjs.map +1 -0
  109. package/dist/tools/warp_grep/providers/remote.d.ts +67 -0
  110. package/dist/tools/warp_grep/providers/remote.js +9 -0
  111. package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
  112. package/dist/tools/warp_grep/providers/types.d.ts +7 -15
  113. package/dist/tools/warp_grep/vercel.cjs +662 -277
  114. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  115. package/dist/tools/warp_grep/vercel.d.ts +4 -51
  116. package/dist/tools/warp_grep/vercel.js +16 -14
  117. package/dist/types-a_hxdPI6.d.ts +144 -0
  118. package/dist/vercel-3yjvfmVB.d.ts +66 -0
  119. package/package.json +12 -2
  120. package/dist/chunk-6X5UOY7B.js.map +0 -1
  121. package/dist/chunk-73RQWOQC.js +0 -16
  122. package/dist/chunk-73RQWOQC.js.map +0 -1
  123. package/dist/chunk-7OQOOB3R.js +0 -1
  124. package/dist/chunk-CFF636UC.js +0 -70
  125. package/dist/chunk-CFF636UC.js.map +0 -1
  126. package/dist/chunk-EK7OQPWD.js +0 -44
  127. package/dist/chunk-EK7OQPWD.js.map +0 -1
  128. package/dist/chunk-GJ5TYNRD.js +0 -107
  129. package/dist/chunk-GJ5TYNRD.js.map +0 -1
  130. package/dist/chunk-HQO45BAJ.js +0 -14
  131. package/dist/chunk-HQO45BAJ.js.map +0 -1
  132. package/dist/chunk-IMYQOKFO.js +0 -83
  133. package/dist/chunk-IMYQOKFO.js.map +0 -1
  134. package/dist/chunk-KBQWGT5L.js +0 -77
  135. package/dist/chunk-KBQWGT5L.js.map +0 -1
  136. package/dist/chunk-LVPVVLTI.js.map +0 -1
  137. package/dist/chunk-OXHGFHEU.js.map +0 -1
  138. package/dist/chunk-QFIHUCTF.js.map +0 -1
  139. package/dist/chunk-TICMYDII.js.map +0 -1
  140. package/dist/chunk-TJIUA27P.js.map +0 -1
  141. package/dist/chunk-WETRQJGU.js +0 -129
  142. package/dist/chunk-WETRQJGU.js.map +0 -1
  143. package/dist/chunk-ZJIIICRA.js.map +0 -1
  144. package/dist/core-CpkYEi_T.d.ts +0 -158
  145. package/dist/tools/warp_grep/tools/analyse.cjs +0 -40
  146. package/dist/tools/warp_grep/tools/analyse.cjs.map +0 -1
  147. package/dist/tools/warp_grep/tools/analyse.d.ts +0 -10
  148. package/dist/tools/warp_grep/tools/analyse.js +0 -8
  149. package/dist/tools/warp_grep/tools/finish.cjs +0 -69
  150. package/dist/tools/warp_grep/tools/finish.cjs.map +0 -1
  151. package/dist/tools/warp_grep/tools/finish.d.ts +0 -10
  152. package/dist/tools/warp_grep/tools/finish.js +0 -10
  153. package/dist/tools/warp_grep/tools/grep.cjs +0 -38
  154. package/dist/tools/warp_grep/tools/grep.cjs.map +0 -1
  155. package/dist/tools/warp_grep/tools/grep.d.ts +0 -8
  156. package/dist/tools/warp_grep/tools/grep.js +0 -15
  157. package/dist/tools/warp_grep/tools/grep.js.map +0 -1
  158. package/dist/tools/warp_grep/tools/read.cjs +0 -38
  159. package/dist/tools/warp_grep/tools/read.cjs.map +0 -1
  160. package/dist/tools/warp_grep/tools/read.d.ts +0 -9
  161. package/dist/tools/warp_grep/tools/read.js +0 -8
  162. package/dist/tools/warp_grep/utils/format.cjs +0 -42
  163. package/dist/tools/warp_grep/utils/format.cjs.map +0 -1
  164. package/dist/tools/warp_grep/utils/format.d.ts +0 -4
  165. package/dist/tools/warp_grep/utils/format.js +0 -18
  166. package/dist/tools/warp_grep/utils/format.js.map +0 -1
  167. /package/dist/{chunk-7OQOOB3R.js.map → chunk-ISWL67SF.js.map} +0 -0
  168. /package/dist/tools/warp_grep/{tools/analyse.js.map → client.js.map} +0 -0
  169. /package/dist/tools/warp_grep/{tools/finish.js.map → gemini.js.map} +0 -0
  170. /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
- // Give the model freedom; failsafe cap to prevent infinite loops
965
- MAX_ROUNDS: 10,
966
- TIMEOUT_MS: 3e4
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 query.
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
- <workflow>
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 query specificity)
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 \`finish\` early using fewer turns, but quality over speed.
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
- <tools>
1065
- ### \`analyse <path> [pattern]\`
1066
- Directory tree or file search. Shows structure of a path, optionally filtered by regex pattern.
1067
- - \`path\`: Required. Directory or file path (use \`.\` for repo root)
1068
- - \`pattern\`: Optional regex to filter results
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
- analyse .
1073
- analyse src/api
1074
- analyse . ".*\\.ts$"
1075
- analyse src "test.*"
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 <path>[:start-end]\`
1079
- Read file contents. Line range is 1-based, inclusive.
1095
+ ### \`read\`
1096
+ Read file contents. Supports multiple line ranges.
1080
1097
  - Returns numbered lines for easy reference
1081
- - Omit range to read entire file
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 src/main.py
1086
- read src/db/conn.py:10-50
1087
- read package.json:1-20
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 '<pattern>' <path>\`
1091
- Ripgrep search. Finds pattern matches across files.
1092
- - \`'<pattern>'\`: Required. Regex pattern wrapped in single quotes
1093
- - \`<path>\`: Required. Directory or file to search (use \`.\` for repo root)
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 'class.*Service' src/
1098
- grep 'def authenticate' .
1099
- grep 'import.*from' src/components/
1100
- grep 'TODO' .
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 <file1:ranges> [file2:ranges ...]\`
1104
- Submit final answer with all relevant code locations.
1105
- - Include generous line ranges\u2014don't be stingy with context
1106
- - Ranges are comma-separated: \`file.py:10-30,50-60\`
1107
- - ALWAYS include import statements at the top of files (usually lines 1-20)
1108
- - If code spans multiple files, include ALL of them
1109
- - Small files can be returned in full
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 src/auth.py:1-15,25-50,75-80 src/models/user.py:1-10,20-45
1114
- finish src/index.ts:1-100
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 query:**
1170
+ **Before your first tool call, classify the search_string:**
1120
1171
 
1121
- | Query Type | Turn 1 Strategy | Early Finish? |
1122
- |------------|-----------------|---------------|
1123
- | **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by turn 2 |
1124
- | **Conceptual** (how does X work, where is Y handled) | analyse + 2-3 broad greps | Rarely early |
1125
- | **Exploratory** (find all tests, list API endpoints) | analyse at multiple depths | Usually needs 3 turns |
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 analyse + 7 greps\u2014orient and search simultaneously
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
- - Query classification (specific/conceptual/exploratory)
1139
- - Confidence estimate (can I finish in 1-2 turns?)
1140
- - This turn's parallel strategy
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 tool calls wrapped in \`<tool_call>...</tool_call>\` tags, one per line.
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 query about authentication. I'll grep for auth-related patterns.
1149
- High confidence I can finish in 2 turns if I find the auth module.
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
- <tool_call>grep 'authenticate' src/</tool_call>
1153
- <tool_call>grep 'login' src/</tool_call>
1154
- <tool_call>analyse src/auth</tool_call>
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 = ["analyse", "grep", "read", "finish"];
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 "analyse":
1232
- this.handleAnalyse(parts, line, commands);
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(([path4, ranges]) => ({
1251
- path: path4,
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
- handleAnalyse(parts, rawLine, commands) {
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: analyse <path> [pattern]. Example: analyse src/`
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 path4 = parts[1];
1466
+ const path5 = parts[1];
1291
1467
  const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
1292
- commands.push({ name: "analyse", arguments: { path: path4, pattern } });
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/read.ts
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/analyse.ts
1376
- async function toolAnalyse(provider, args) {
1377
- const list = await provider.analyse({
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 ?? 100,
1381
- maxDepth: args.maxDepth ?? 2
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
- return list.map((e) => `${" ".repeat(e.depth)}- ${e.type === "dir" ? "[D]" : "[F]"} ${e.name}`).join("\n");
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 "analyse":
1404
- return this.formatAnalyse(safeArgs, payload, isError);
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 path4 = this.asString(args.path) || "...";
1418
- return `<file path="${path4}">
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
- </file>`;
1667
+ </read>`;
1421
1668
  }
1422
- formatAnalyse(args, payload, isError) {
1423
- const path4 = this.asString(args.path) || ".";
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
- return `<analyse_results path="${path4}" status="error">
1426
- ${payload}
1427
- </analyse_results>`;
1677
+ attributes.push('status="error"');
1428
1678
  }
1429
- return `<analyse_results path="${path4}">
1679
+ return `<list_directory ${attributes.join(" ")}>
1430
1680
  ${payload}
1431
- </analyse_results>`;
1681
+ </list_directory>`;
1432
1682
  }
1433
1683
  formatGrep(args, payload, isError) {
1434
1684
  const pattern = this.asString(args.pattern);
1435
- const path4 = this.asString(args.path);
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 (path4 !== void 0) {
1441
- attributes.push(`path="${path4}"`);
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 `<grep_output${attrText}>
1701
+ return `<grep${attrText}>
1448
1702
  ${payload}
1449
- </grep_output>`;
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/tools/finish.ts
1464
- async function readFinishFiles(repoRoot, files, reader) {
1465
- const out = [];
1466
- for (const f of files) {
1467
- const ranges = mergeRanges(f.lines);
1468
- const chunks = [];
1469
- for (const [s, e] of ranges) {
1470
- const lines = await reader(f.path, s, e);
1471
- chunks.push(lines.join("\n"));
1472
- }
1473
- out.push({ path: f.path, ranges, content: chunks.join("\n") });
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 mergeRanges(ranges) {
1478
- if (!ranges.length) return [];
1479
- const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
1480
- const merged = [];
1481
- let [cs, ce] = sorted[0];
1482
- for (let i = 1; i < sorted.length; i++) {
1483
- const [s, e] = sorted[i];
1484
- if (s <= ce + 1) {
1485
- ce = Math.max(ce, e);
1486
- } else {
1487
- merged.push([cs, ce]);
1488
- cs = s;
1489
- ce = e;
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
- merged.push([cs, ce]);
1493
- return merged;
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 import_path2 = __toESM(require("path"), 1);
1798
+ var import_path3 = __toESM(require("path"), 1);
1498
1799
  var parser = new LLMResponseParser();
1499
- async function buildInitialState(repoRoot, query, provider) {
1500
- try {
1501
- const entries = await provider.analyse({ path: ".", maxResults: 100 });
1502
- const dirs = entries.filter((e) => e.type === "dir").map((d) => d.name).slice(0, 50);
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
- api,
1805
+ `${baseUrl}/v1/chat/completions`,
1518
1806
  {
1519
1807
  method: "POST",
1520
1808
  headers: {
1521
1809
  "Content-Type": "application/json",
1522
- Authorization: `Bearer ${apiKey || process.env.MORPH_API_KEY || ""}`
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 = import_path2.default.resolve(config.repoRoot || process.cwd());
1839
+ const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
1547
1840
  const messages = [];
1548
- const systemMessage = { role: "system", content: getSystemPrompt() };
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 maxRounds = AGENT_CONFIG.MAX_ROUNDS;
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 round = 1; round <= maxRounds; round += 1) {
1561
- const assistantContent = await callModel(messages, model, config.apiKey).catch((e) => {
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 analyseCalls = toolCalls.filter((c) => c.name === "analyse");
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.grep({ pattern: args.pattern, path: args.path }).then(
1588
- (grepRes) => {
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 analyseCalls) {
1888
+ for (const c of listDirCalls) {
1600
1889
  const args = c.arguments ?? {};
1601
1890
  allPromises.push(
1602
- toolAnalyse(provider, args).then(
1603
- (p) => formatAgentToolOutput("analyse", args, p, { isError: false }),
1604
- (err) => formatAgentToolOutput("analyse", args, String(err), { isError: true })
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 turnsUsed = round;
1631
- const turnsRemaining = 4 - turnsUsed;
1632
- let turnMessage;
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 import_path4 = __toESM(require("path"), 1);
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 import_path3 = __toESM(require("path"), 1);
2028
+ var import_path4 = __toESM(require("path"), 1);
1762
2029
  function resolveUnderRepo(repoRoot, targetPath) {
1763
- const absRoot = import_path3.default.resolve(repoRoot);
1764
- const resolved = import_path3.default.resolve(absRoot, targetPath);
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 = import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absTarget));
1770
- if (rel.startsWith("..") || import_path3.default.isAbsolute(rel)) {
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 import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absPath));
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
- const abs = resolveUnderRepo(this.repoRoot, params.path);
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 === import_path4.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, 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
- return { lines };
1855
- }
1856
- async glob(params) {
1857
- const abs = resolveUnderRepo(this.repoRoot, params.path);
1858
- const targetArg = abs === import_path4.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
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
- const files = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
1873
- return { files };
2138
+ return { lines };
1874
2139
  }
1875
2140
  async read(params) {
1876
- const abs = resolveUnderRepo(this.repoRoot, params.path);
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
- const lines = await readAllLines(abs);
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 analyse(params) {
1912
- const abs = resolveUnderRepo(this.repoRoot, params.path);
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 ?? 100;
1918
- const maxDepth = params.maxDepth ?? 2;
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
- const full = import_path4.default.join(dir, entry.name);
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(import_path4.default.sep).includes(ex))) continue;
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(import_path4.default.resolve(""), full),
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/core.ts
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
- apiKey: config.apiKey,
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
- const provider = input.provider ?? new LocalRipgrepProvider(input.repoRoot, input.excludes);
1981
- const result = await runWarpGrep({
1982
- query: input.query,
1983
- repoRoot: input.repoRoot,
1984
- provider,
1985
- excludes: input.excludes,
1986
- includes: input.includes,
1987
- debug: input.debug ?? this.config.debug ?? false,
1988
- apiKey: this.config.apiKey
1989
- });
1990
- const finish = result.finish;
1991
- if (result.terminationReason !== "completed" || !finish?.metadata) {
1992
- return {
1993
- success: false,
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}`;
@@ -2132,12 +2532,12 @@ var MorphGit = class {
2132
2532
  * await morphGit.push({
2133
2533
  * dir: './my-project',
2134
2534
  * branch: 'main', // Required: explicit branch name
2135
- * index: true // Optional: generate embeddings (default: true)
2535
+ * index: true // Optional: generate embeddings for semantic search
2136
2536
  * });
2137
2537
  * ```
2138
2538
  */
2139
2539
  async push(options) {
2140
- const { dir, remote = "origin", branch, waitForEmbeddings, index = true } = options;
2540
+ const { dir, remote = "origin", branch, waitForEmbeddings, index = false } = options;
2141
2541
  if (!branch) {
2142
2542
  throw new Error(
2143
2543
  'branch is required for push operations. Specify the branch explicitly: { dir: "./my-project", branch: "main" }'
@@ -2784,29 +3184,7 @@ var TOOL_PARAMETERS = {
2784
3184
  },
2785
3185
  required: ["query"]
2786
3186
  };
2787
- async function execute(input, config) {
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 execute(input, config);
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 execute2(input, config) {
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 execute2(parsedInput, config);
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 (apiKey inherited from MorphClient)
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 createMorphWarpGrepTool({
3435
+ return createWarpGrepTool({
3058
3436
  ...toolConfig,
3059
- apiKey: this.config.apiKey
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
- async function execute3(input, config) {
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 execute3(input, config);
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 (apiKey inherited from MorphClient)
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 createMorphWarpGrepTool2({
3636
+ return createWarpGrepTool2({
3281
3637
  ...toolConfig,
3282
- apiKey: this.config.apiKey
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
- var warpGrepSchema = import_zod.z.object({
3315
- query: import_zod.z.string().describe("Free-form repository question")
3316
- });
3317
- function createMorphWarpGrepTool3(config) {
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: warpGrepSchema,
3676
+ inputSchema: schema,
3321
3677
  execute: async (params) => {
3322
- const provider = config.provider ?? new LocalRipgrepProvider(config.repoRoot, config.excludes);
3323
- const result = await runWarpGrep({
3324
- query: params.query,
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
- const contexts = (finish.resolved ?? []).map((r) => ({
3337
- file: r.path,
3338
- content: r.content
3339
- }));
3340
- return { success: true, contexts, summary: finish.payload };
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 (apiKey inherited from MorphClient)
3799
+ * @param toolConfig - Tool configuration (morphApiKey inherited from MorphClient)
3454
3800
  * @returns Vercel AI SDK tool
3455
3801
  */
3456
3802
  createWarpGrepTool(toolConfig) {
3457
- return createMorphWarpGrepTool3({
3803
+ return createWarpGrepTool3({
3458
3804
  ...toolConfig,
3459
- apiKey: this.config.apiKey
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
- apiKey: config.apiKey,
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,