@morphllm/morphsdk 0.2.146 → 0.2.147

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 (205) hide show
  1. package/dist/{chunk-J2HIK4GB.js → chunk-334JOOAC.js} +2 -2
  2. package/dist/{chunk-QRSWXP4K.js → chunk-3GXWZ7TI.js} +2 -2
  3. package/dist/{chunk-BDHKL3MT.js → chunk-4LZK67MS.js} +2 -2
  4. package/dist/{chunk-UVNENJ6H.js → chunk-4XNHMFVU.js} +3 -3
  5. package/dist/{chunk-HZOTLGJH.js → chunk-4Y2NM6JD.js} +42 -2
  6. package/dist/chunk-4Y2NM6JD.js.map +1 -0
  7. package/dist/{chunk-V73GO5AJ.js → chunk-7HKJUVAK.js} +2 -2
  8. package/dist/{chunk-NF2QWJDY.js → chunk-B3AKP3RA.js} +31 -2
  9. package/dist/chunk-B3AKP3RA.js.map +1 -0
  10. package/dist/{chunk-VZ6VYRQB.js → chunk-BXQYZR7O.js} +2 -2
  11. package/dist/{chunk-GJUB3ECP.js → chunk-CJD4ZFOX.js} +2 -2
  12. package/dist/chunk-CMSHXALI.js +60 -0
  13. package/dist/chunk-CMSHXALI.js.map +1 -0
  14. package/dist/{chunk-KYKRRF7E.js → chunk-DYCRGTRN.js} +2 -2
  15. package/dist/{chunk-EU7OLX4Z.js → chunk-ETKDGHOB.js} +2 -2
  16. package/dist/{chunk-SJYAKVSS.js → chunk-G4TRYATX.js} +2 -2
  17. package/dist/{chunk-SJYAKVSS.js.map → chunk-G4TRYATX.js.map} +1 -1
  18. package/dist/chunk-GK56GCLU.js +401 -0
  19. package/dist/chunk-GK56GCLU.js.map +1 -0
  20. package/dist/{chunk-I7SFRYTX.js → chunk-HWED34T3.js} +2 -2
  21. package/dist/{chunk-FIVYDIHX.js → chunk-HYRHI2UL.js} +1 -1
  22. package/dist/{chunk-DKODF3YG.js → chunk-I3J46TSB.js} +5 -4
  23. package/dist/chunk-I3J46TSB.js.map +1 -0
  24. package/dist/{chunk-E4YKEKGW.js → chunk-KV2CC4N7.js} +2 -2
  25. package/dist/{chunk-BIQ7234U.js → chunk-LSPEWVCD.js} +2 -2
  26. package/dist/{chunk-OV57JBMB.js → chunk-N7XLC7BK.js} +2 -2
  27. package/dist/{chunk-FBOJJ3UY.js → chunk-O356YR2N.js} +17 -17
  28. package/dist/{chunk-JSWNBCGS.js → chunk-PIHW2GSK.js} +2 -2
  29. package/dist/{chunk-UYPWKQKV.js → chunk-RGNP6FNH.js} +2 -2
  30. package/dist/{chunk-YIETFYCL.js → chunk-T7HF2TDQ.js} +75 -48
  31. package/dist/chunk-T7HF2TDQ.js.map +1 -0
  32. package/dist/{chunk-NKUSUSVI.js → chunk-T7VCGKXB.js} +3 -3
  33. package/dist/{chunk-Q36MNOFA.js → chunk-UZ2QUWJD.js} +2 -2
  34. package/dist/{chunk-FYO46OT6.js → chunk-VTTU7MJB.js} +2 -2
  35. package/dist/{chunk-4PBUB77N.js → chunk-XADFYB6B.js} +2 -2
  36. package/dist/{chunk-T564HFSH.js → chunk-XS4PO4FC.js} +1 -1
  37. package/dist/{chunk-E45FW5EK.js → chunk-YH3XIVF2.js} +2 -2
  38. package/dist/{chunk-MMBQKN4G.js → chunk-ZZW4B4GG.js} +2 -2
  39. package/dist/client.cjs +439 -446
  40. package/dist/client.cjs.map +1 -1
  41. package/dist/client.js +26 -27
  42. package/dist/edge.cjs +1 -1
  43. package/dist/edge.cjs.map +1 -1
  44. package/dist/edge.js +4 -4
  45. package/dist/{finish-DBKuo8yj.d.ts → finish-Ddj1MPGt.d.ts} +1 -1
  46. package/dist/index.cjs +458 -446
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.js +29 -29
  49. package/dist/modelrouter/core.cjs +1 -1
  50. package/dist/modelrouter/core.cjs.map +1 -1
  51. package/dist/modelrouter/core.js +3 -3
  52. package/dist/modelrouter/index.cjs +1 -1
  53. package/dist/modelrouter/index.cjs.map +1 -1
  54. package/dist/modelrouter/index.js +3 -3
  55. package/dist/subagents/anthropic.cjs +435 -442
  56. package/dist/subagents/anthropic.cjs.map +1 -1
  57. package/dist/subagents/anthropic.js +8 -9
  58. package/dist/subagents/vercel.cjs +435 -442
  59. package/dist/subagents/vercel.cjs.map +1 -1
  60. package/dist/subagents/vercel.js +8 -9
  61. package/dist/tools/browser/anthropic.cjs +1 -1
  62. package/dist/tools/browser/anthropic.cjs.map +1 -1
  63. package/dist/tools/browser/anthropic.js +5 -5
  64. package/dist/tools/browser/core.cjs +1 -1
  65. package/dist/tools/browser/core.cjs.map +1 -1
  66. package/dist/tools/browser/core.js +4 -4
  67. package/dist/tools/browser/index.cjs +1 -1
  68. package/dist/tools/browser/index.cjs.map +1 -1
  69. package/dist/tools/browser/index.js +7 -7
  70. package/dist/tools/browser/openai.cjs +1 -1
  71. package/dist/tools/browser/openai.cjs.map +1 -1
  72. package/dist/tools/browser/openai.js +5 -5
  73. package/dist/tools/browser/profiles/core.cjs +1 -1
  74. package/dist/tools/browser/profiles/core.cjs.map +1 -1
  75. package/dist/tools/browser/profiles/core.js +3 -3
  76. package/dist/tools/browser/profiles/index.cjs +1 -1
  77. package/dist/tools/browser/profiles/index.cjs.map +1 -1
  78. package/dist/tools/browser/profiles/index.js +3 -3
  79. package/dist/tools/browser/vercel.cjs +1 -1
  80. package/dist/tools/browser/vercel.cjs.map +1 -1
  81. package/dist/tools/browser/vercel.js +5 -5
  82. package/dist/tools/codebase_search/anthropic.cjs +1 -1
  83. package/dist/tools/codebase_search/anthropic.cjs.map +1 -1
  84. package/dist/tools/codebase_search/anthropic.js +4 -4
  85. package/dist/tools/codebase_search/core.cjs +1 -1
  86. package/dist/tools/codebase_search/core.cjs.map +1 -1
  87. package/dist/tools/codebase_search/core.js +3 -3
  88. package/dist/tools/codebase_search/index.cjs +1 -1
  89. package/dist/tools/codebase_search/index.cjs.map +1 -1
  90. package/dist/tools/codebase_search/index.js +6 -6
  91. package/dist/tools/codebase_search/openai.cjs +1 -1
  92. package/dist/tools/codebase_search/openai.cjs.map +1 -1
  93. package/dist/tools/codebase_search/openai.js +4 -4
  94. package/dist/tools/codebase_search/vercel.cjs +1 -1
  95. package/dist/tools/codebase_search/vercel.cjs.map +1 -1
  96. package/dist/tools/codebase_search/vercel.js +4 -4
  97. package/dist/tools/fastapply/anthropic.cjs +1 -1
  98. package/dist/tools/fastapply/anthropic.cjs.map +1 -1
  99. package/dist/tools/fastapply/anthropic.js +4 -4
  100. package/dist/tools/fastapply/apply.cjs +1 -1
  101. package/dist/tools/fastapply/apply.cjs.map +1 -1
  102. package/dist/tools/fastapply/apply.js +2 -2
  103. package/dist/tools/fastapply/core.cjs +1 -1
  104. package/dist/tools/fastapply/core.cjs.map +1 -1
  105. package/dist/tools/fastapply/core.js +3 -3
  106. package/dist/tools/fastapply/index.cjs +1 -1
  107. package/dist/tools/fastapply/index.cjs.map +1 -1
  108. package/dist/tools/fastapply/index.js +6 -6
  109. package/dist/tools/fastapply/openai.cjs +1 -1
  110. package/dist/tools/fastapply/openai.cjs.map +1 -1
  111. package/dist/tools/fastapply/openai.js +4 -4
  112. package/dist/tools/fastapply/vercel.cjs +1 -1
  113. package/dist/tools/fastapply/vercel.cjs.map +1 -1
  114. package/dist/tools/fastapply/vercel.js +4 -4
  115. package/dist/tools/index.cjs +1 -1
  116. package/dist/tools/index.cjs.map +1 -1
  117. package/dist/tools/index.js +6 -6
  118. package/dist/tools/utils/resilience.cjs +1 -1
  119. package/dist/tools/utils/resilience.cjs.map +1 -1
  120. package/dist/tools/utils/resilience.js +2 -2
  121. package/dist/tools/warp_grep/agent/config.cjs +4 -3
  122. package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
  123. package/dist/tools/warp_grep/agent/config.d.ts +2 -1
  124. package/dist/tools/warp_grep/agent/config.js +1 -1
  125. package/dist/tools/warp_grep/agent/parser.cjs +52 -121
  126. package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
  127. package/dist/tools/warp_grep/agent/parser.d.ts +12 -5
  128. package/dist/tools/warp_grep/agent/parser.js +7 -3
  129. package/dist/tools/warp_grep/agent/runner.cjs +348 -424
  130. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  131. package/dist/tools/warp_grep/agent/runner.d.ts +6 -3
  132. package/dist/tools/warp_grep/agent/runner.js +5 -6
  133. package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
  134. package/dist/tools/warp_grep/agent/types.d.ts +22 -3
  135. package/dist/tools/warp_grep/anthropic.cjs +435 -442
  136. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  137. package/dist/tools/warp_grep/anthropic.js +8 -9
  138. package/dist/tools/warp_grep/client.cjs +435 -442
  139. package/dist/tools/warp_grep/client.cjs.map +1 -1
  140. package/dist/tools/warp_grep/client.js +7 -8
  141. package/dist/tools/warp_grep/gemini.cjs +435 -442
  142. package/dist/tools/warp_grep/gemini.cjs.map +1 -1
  143. package/dist/tools/warp_grep/gemini.js +7 -8
  144. package/dist/tools/warp_grep/gemini.js.map +1 -1
  145. package/dist/tools/warp_grep/harness.cjs +168 -180
  146. package/dist/tools/warp_grep/harness.cjs.map +1 -1
  147. package/dist/tools/warp_grep/harness.d.ts +17 -38
  148. package/dist/tools/warp_grep/harness.js +15 -14
  149. package/dist/tools/warp_grep/harness.js.map +1 -1
  150. package/dist/tools/warp_grep/index.cjs +454 -442
  151. package/dist/tools/warp_grep/index.cjs.map +1 -1
  152. package/dist/tools/warp_grep/index.d.ts +1 -1
  153. package/dist/tools/warp_grep/index.js +10 -10
  154. package/dist/tools/warp_grep/openai.cjs +435 -442
  155. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  156. package/dist/tools/warp_grep/openai.js +8 -9
  157. package/dist/tools/warp_grep/providers/local.cjs +43 -2
  158. package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
  159. package/dist/tools/warp_grep/providers/local.d.ts +5 -1
  160. package/dist/tools/warp_grep/providers/local.js +2 -2
  161. package/dist/tools/warp_grep/providers/remote.cjs +32 -2
  162. package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
  163. package/dist/tools/warp_grep/providers/remote.d.ts +9 -1
  164. package/dist/tools/warp_grep/providers/remote.js +2 -2
  165. package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
  166. package/dist/tools/warp_grep/providers/types.d.ts +14 -1
  167. package/dist/tools/warp_grep/vercel.cjs +435 -442
  168. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  169. package/dist/tools/warp_grep/vercel.js +8 -9
  170. package/dist/version.cjs +1 -1
  171. package/dist/version.cjs.map +1 -1
  172. package/dist/version.js +1 -1
  173. package/package.json +1 -1
  174. package/dist/chunk-DKODF3YG.js.map +0 -1
  175. package/dist/chunk-EUHNJMWL.js +0 -409
  176. package/dist/chunk-EUHNJMWL.js.map +0 -1
  177. package/dist/chunk-HZOTLGJH.js.map +0 -1
  178. package/dist/chunk-NF2QWJDY.js.map +0 -1
  179. package/dist/chunk-VCKJ22DX.js +0 -131
  180. package/dist/chunk-VCKJ22DX.js.map +0 -1
  181. package/dist/chunk-YIETFYCL.js.map +0 -1
  182. /package/dist/{chunk-J2HIK4GB.js.map → chunk-334JOOAC.js.map} +0 -0
  183. /package/dist/{chunk-QRSWXP4K.js.map → chunk-3GXWZ7TI.js.map} +0 -0
  184. /package/dist/{chunk-BDHKL3MT.js.map → chunk-4LZK67MS.js.map} +0 -0
  185. /package/dist/{chunk-UVNENJ6H.js.map → chunk-4XNHMFVU.js.map} +0 -0
  186. /package/dist/{chunk-V73GO5AJ.js.map → chunk-7HKJUVAK.js.map} +0 -0
  187. /package/dist/{chunk-VZ6VYRQB.js.map → chunk-BXQYZR7O.js.map} +0 -0
  188. /package/dist/{chunk-GJUB3ECP.js.map → chunk-CJD4ZFOX.js.map} +0 -0
  189. /package/dist/{chunk-KYKRRF7E.js.map → chunk-DYCRGTRN.js.map} +0 -0
  190. /package/dist/{chunk-EU7OLX4Z.js.map → chunk-ETKDGHOB.js.map} +0 -0
  191. /package/dist/{chunk-I7SFRYTX.js.map → chunk-HWED34T3.js.map} +0 -0
  192. /package/dist/{chunk-FIVYDIHX.js.map → chunk-HYRHI2UL.js.map} +0 -0
  193. /package/dist/{chunk-E4YKEKGW.js.map → chunk-KV2CC4N7.js.map} +0 -0
  194. /package/dist/{chunk-BIQ7234U.js.map → chunk-LSPEWVCD.js.map} +0 -0
  195. /package/dist/{chunk-OV57JBMB.js.map → chunk-N7XLC7BK.js.map} +0 -0
  196. /package/dist/{chunk-FBOJJ3UY.js.map → chunk-O356YR2N.js.map} +0 -0
  197. /package/dist/{chunk-JSWNBCGS.js.map → chunk-PIHW2GSK.js.map} +0 -0
  198. /package/dist/{chunk-UYPWKQKV.js.map → chunk-RGNP6FNH.js.map} +0 -0
  199. /package/dist/{chunk-NKUSUSVI.js.map → chunk-T7VCGKXB.js.map} +0 -0
  200. /package/dist/{chunk-Q36MNOFA.js.map → chunk-UZ2QUWJD.js.map} +0 -0
  201. /package/dist/{chunk-FYO46OT6.js.map → chunk-VTTU7MJB.js.map} +0 -0
  202. /package/dist/{chunk-4PBUB77N.js.map → chunk-XADFYB6B.js.map} +0 -0
  203. /package/dist/{chunk-T564HFSH.js.map → chunk-XS4PO4FC.js.map} +0 -0
  204. /package/dist/{chunk-E45FW5EK.js.map → chunk-YH3XIVF2.js.map} +0 -0
  205. /package/dist/{chunk-MMBQKN4G.js.map → chunk-ZZW4B4GG.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -36,7 +36,7 @@ var init_package = __esm({
36
36
  "package.json"() {
37
37
  package_default = {
38
38
  name: "@morphllm/morphsdk",
39
- version: "0.2.146",
39
+ version: "0.2.147",
40
40
  description: "TypeScript SDK and CLI for Morph Fast Apply integration",
41
41
  type: "module",
42
42
  main: "./dist/index.cjs",
@@ -416,11 +416,12 @@ var init_config = __esm({
416
416
  return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
417
417
  };
418
418
  AGENT_CONFIG = {
419
- MAX_TURNS: 4,
419
+ MAX_TURNS: 6,
420
420
  /** Default timeout for model calls. Can be overridden via MORPH_WARP_GREP_TIMEOUT env var (in ms) */
421
421
  TIMEOUT_MS: parseEnvTimeout(process.env.MORPH_WARP_GREP_TIMEOUT, 3e4),
422
- MAX_CONTEXT_CHARS: 54e4,
422
+ MAX_CONTEXT_CHARS: 321600,
423
423
  MAX_OUTPUT_LINES: 200,
424
+ MAX_LIST_RESULTS: 500,
424
425
  MAX_READ_LINES: 800,
425
426
  MAX_LIST_DEPTH: 3,
426
427
  LIST_TIMEOUT_MS: 2e3
@@ -505,7 +506,7 @@ var init_config = __esm({
505
506
  ".*"
506
507
  ];
507
508
  DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
508
- DEFAULT_MODEL = "morph-warp-grep-v2";
509
+ DEFAULT_MODEL = "morph-warp-grep-v2.1";
509
510
  }
510
511
  });
511
512
 
@@ -578,19 +579,19 @@ var init_ripgrep = __esm({
578
579
 
579
580
  // tools/warp_grep/utils/paths.ts
580
581
  function resolveUnderRepo(repoRoot, targetPath) {
581
- const absRoot = import_path4.default.resolve(repoRoot);
582
- const resolved = import_path4.default.resolve(absRoot, targetPath);
582
+ const absRoot = import_path5.default.resolve(repoRoot);
583
+ const resolved = import_path5.default.resolve(absRoot, targetPath);
583
584
  ensureWithinRepo(absRoot, resolved);
584
585
  return resolved;
585
586
  }
586
587
  function ensureWithinRepo(repoRoot, absTarget) {
587
- const rel = import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absTarget));
588
- if (rel.startsWith("..") || import_path4.default.isAbsolute(rel)) {
588
+ const rel = import_path5.default.relative(import_path5.default.resolve(repoRoot), import_path5.default.resolve(absTarget));
589
+ if (rel.startsWith("..") || import_path5.default.isAbsolute(rel)) {
589
590
  throw new Error(`Path outside repository root: ${absTarget}`);
590
591
  }
591
592
  }
592
593
  function toRepoRelative(repoRoot, absPath) {
593
- return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
594
+ return import_path5.default.relative(import_path5.default.resolve(repoRoot), import_path5.default.resolve(absPath));
594
595
  }
595
596
  function isSymlink(p) {
596
597
  try {
@@ -601,7 +602,7 @@ function isSymlink(p) {
601
602
  }
602
603
  }
603
604
  function fixPathRepetition(fullPath) {
604
- const segments = fullPath.split(import_path4.default.sep).filter(Boolean);
605
+ const segments = fullPath.split(import_path5.default.sep).filter(Boolean);
605
606
  if (segments.length < 2) return null;
606
607
  for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
607
608
  for (let i = 0; i <= segments.length - 2 * len; i++) {
@@ -609,7 +610,7 @@ function fixPathRepetition(fullPath) {
609
610
  const second = segments.slice(i + len, i + 2 * len);
610
611
  if (first.every((seg, idx) => seg === second[idx])) {
611
612
  const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
612
- return import_path4.default.sep + fixed.join(import_path4.default.sep);
613
+ return import_path5.default.sep + fixed.join(import_path5.default.sep);
613
614
  }
614
615
  }
615
616
  }
@@ -633,12 +634,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
633
634
  return false;
634
635
  }
635
636
  }
636
- var import_fs, import_path4;
637
+ var import_fs, import_path5;
637
638
  var init_paths = __esm({
638
639
  "tools/warp_grep/utils/paths.ts"() {
639
640
  "use strict";
640
641
  import_fs = __toESM(require("fs"), 1);
641
- import_path4 = __toESM(require("path"), 1);
642
+ import_path5 = __toESM(require("path"), 1);
642
643
  }
643
644
  });
644
645
 
@@ -669,12 +670,12 @@ function shouldSkip2(name, allowNames) {
669
670
  }
670
671
  return false;
671
672
  }
672
- var import_promises2, import_path5, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
673
+ var import_promises2, import_path6, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
673
674
  var init_local = __esm({
674
675
  "tools/warp_grep/providers/local.ts"() {
675
676
  "use strict";
676
677
  import_promises2 = __toESM(require("fs/promises"), 1);
677
- import_path5 = __toESM(require("path"), 1);
678
+ import_path6 = __toESM(require("path"), 1);
678
679
  init_ripgrep();
679
680
  init_paths();
680
681
  init_files();
@@ -776,7 +777,7 @@ var init_local = __esm({
776
777
  }
777
778
  const stat = await import_promises2.default.stat(abs).catch(() => null);
778
779
  if (!stat) return { lines: [] };
779
- const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
780
+ const targetArg = abs === import_path6.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
780
781
  const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
781
782
  const args = [
782
783
  "--no-config",
@@ -931,7 +932,7 @@ Details: ${res.stderr}` : ""}`
931
932
  if (timedOut || results.length >= maxResults) break;
932
933
  if (shouldSkip2(entry.name, allowNames)) continue;
933
934
  if (regex && !regex.test(entry.name)) continue;
934
- const full = import_path5.default.join(dir, entry.name);
935
+ const full = import_path6.default.join(dir, entry.name);
935
936
  const isDir = entry.isDirectory();
936
937
  results.push({
937
938
  name: entry.name,
@@ -947,6 +948,46 @@ Details: ${res.stderr}` : ""}`
947
948
  await walk(abs, 0);
948
949
  return results;
949
950
  }
951
+ async glob(params) {
952
+ let abs;
953
+ try {
954
+ abs = params.path ? resolveUnderRepo(this.repoRoot, params.path) : this.repoRoot;
955
+ } catch (err) {
956
+ return { files: [], searchDir: this.repoRoot, totalFound: 0, error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}` };
957
+ }
958
+ const stat = await import_promises2.default.stat(abs).catch(() => null);
959
+ if (!stat || !stat.isDirectory()) {
960
+ return { files: [], searchDir: abs, totalFound: 0, error: `[PATH ERROR] Directory not found: ${params.path || "."}` };
961
+ }
962
+ const targetArg = abs === import_path6.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
963
+ const args = [
964
+ "--no-config",
965
+ "--files",
966
+ "--color=never",
967
+ "-g",
968
+ params.pattern,
969
+ ...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
970
+ targetArg || "."
971
+ ];
972
+ const res = await runRipgrep(args, { cwd: this.repoRoot });
973
+ if (res.exitCode === -1) {
974
+ return { files: [], searchDir: abs, totalFound: 0, error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required for glob search.` };
975
+ }
976
+ if (res.exitCode !== 0 && res.exitCode !== 1) {
977
+ return { files: [], searchDir: abs, totalFound: 0, error: `[GLOB ERROR] glob failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}` };
978
+ }
979
+ const absRoot = import_path6.default.resolve(this.repoRoot);
980
+ const relFiles = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
981
+ const absFiles = relFiles.map((f) => import_path6.default.resolve(absRoot, f));
982
+ const withMtime = [];
983
+ for (const f of absFiles) {
984
+ const s = await import_promises2.default.stat(f).catch(() => null);
985
+ withMtime.push({ file: f, mtime: s?.mtimeMs ?? 0 });
986
+ }
987
+ withMtime.sort((a, b) => b.mtime - a.mtime);
988
+ const totalFound = withMtime.length;
989
+ return { files: withMtime.slice(0, 100).map((f) => f.file), searchDir: abs, totalFound };
990
+ }
950
991
  };
951
992
  }
952
993
  });
@@ -2515,131 +2556,58 @@ async function checkHealth(config = {}) {
2515
2556
  init_config();
2516
2557
 
2517
2558
  // tools/warp_grep/agent/parser.ts
2518
- var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
2519
- function isValidCommand(name) {
2520
- return VALID_COMMANDS.includes(name);
2559
+ function parseReadLines(linesStr) {
2560
+ const ranges = [];
2561
+ for (const rangeStr of linesStr.split(",")) {
2562
+ const trimmed = rangeStr.trim();
2563
+ if (!trimmed) continue;
2564
+ const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
2565
+ if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
2566
+ ranges.push([parts[0], parts[1]]);
2567
+ } else if (Number.isFinite(parts[0])) {
2568
+ ranges.push([parts[0], parts[0]]);
2569
+ }
2570
+ }
2571
+ if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
2572
+ if (ranges.length > 1) return { lines: ranges };
2573
+ return {};
2521
2574
  }
2522
- function parseQwen3ToolCalls(text) {
2523
- const tools = [];
2524
- const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
2525
- let match;
2526
- while ((match = toolCallRegex.exec(text)) !== null) {
2527
- const funcName = match[1].toLowerCase();
2528
- const body = match[2];
2529
- if (!isValidCommand(funcName)) continue;
2530
- const params = {};
2531
- const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
2532
- let paramMatch;
2533
- while ((paramMatch = paramRegex.exec(body)) !== null) {
2534
- params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
2535
- }
2536
- if (funcName === "ripgrep") {
2537
- const pattern = params.pattern;
2538
- if (!pattern) continue;
2539
- const args = {
2540
- pattern,
2541
- path: params.path || ".",
2542
- ...params.glob && { glob: params.glob },
2543
- ...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
2544
- ...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
2545
- };
2546
- tools.push({ name: "grep", arguments: args });
2547
- } else if (funcName === "list_directory") {
2548
- const command = params.command;
2549
- const directPath = params.path;
2550
- let dirPath = directPath || ".";
2551
- if (!directPath && command) {
2552
- const tokens = command.trim().split(/\s+/);
2553
- const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
2554
- if (pathTokens.length > 0) {
2555
- dirPath = pathTokens[0];
2556
- }
2557
- }
2558
- tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
2559
- } else if (funcName === "read") {
2560
- const filePath = params.path;
2561
- if (!filePath) continue;
2562
- const args = { path: filePath };
2563
- const linesStr = params.lines;
2564
- if (linesStr) {
2565
- const ranges = [];
2566
- for (const rangeStr of linesStr.split(",")) {
2567
- const trimmed = rangeStr.trim();
2568
- if (!trimmed) continue;
2569
- const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
2570
- if (Number.isFinite(s) && Number.isFinite(e)) {
2571
- ranges.push([s, e]);
2572
- } else if (Number.isFinite(s)) {
2573
- ranges.push([s, s]);
2574
- }
2575
- }
2576
- if (ranges.length === 1) {
2577
- args.start = ranges[0][0];
2578
- args.end = ranges[0][1];
2579
- } else if (ranges.length > 1) {
2580
- args.lines = ranges;
2581
- }
2582
- }
2583
- tools.push({ name: "read", arguments: args });
2584
- } else if (funcName === "finish") {
2585
- if (params.result && !params.files) {
2586
- tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
2587
- continue;
2588
- }
2589
- const filesStr = params.files;
2590
- if (!filesStr) {
2591
- tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
2592
- continue;
2593
- }
2594
- const files = [];
2595
- for (const line of filesStr.split("\n")) {
2596
- const trimmed = line.trim();
2597
- if (!trimmed) continue;
2598
- const colonIdx = trimmed.indexOf(":");
2599
- if (colonIdx === -1) {
2600
- files.push({ path: trimmed, lines: "*" });
2601
- } else {
2602
- const filePath = trimmed.slice(0, colonIdx);
2603
- const rangesPart = trimmed.slice(colonIdx + 1);
2604
- const ranges = [];
2605
- for (const rangeStr of rangesPart.split(",")) {
2606
- const rt = rangeStr.trim();
2607
- if (!rt || rt === "*") {
2608
- files.push({ path: filePath, lines: "*" });
2609
- break;
2610
- }
2611
- const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
2612
- if (Number.isFinite(s) && Number.isFinite(e)) {
2613
- ranges.push([s, e]);
2614
- } else if (Number.isFinite(s)) {
2615
- ranges.push([s, s]);
2616
- }
2617
- }
2618
- if (ranges.length > 0) {
2619
- files.push({ path: filePath, lines: ranges });
2620
- } else if (!files.some((f) => f.path === filePath)) {
2621
- files.push({ path: filePath, lines: "*" });
2622
- }
2623
- }
2624
- }
2625
- if (files.length > 0) {
2626
- tools.push({ name: "finish", arguments: { files } });
2627
- } else {
2628
- tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
2575
+ function parseFinishFiles(filesStr) {
2576
+ const files = [];
2577
+ for (const line of filesStr.trim().split(/\s+/)) {
2578
+ const trimmed = line.trim();
2579
+ if (!trimmed) continue;
2580
+ const colonIdx = trimmed.indexOf(":");
2581
+ if (colonIdx === -1) {
2582
+ files.push({ path: trimmed, lines: "*" });
2583
+ continue;
2584
+ }
2585
+ const filePath = trimmed.slice(0, colonIdx);
2586
+ const rangesPart = trimmed.slice(colonIdx + 1);
2587
+ if (!rangesPart.trim() || rangesPart.trim() === "*") {
2588
+ files.push({ path: filePath, lines: "*" });
2589
+ continue;
2590
+ }
2591
+ const ranges = [];
2592
+ for (const rangeStr of rangesPart.split(",")) {
2593
+ const rt = rangeStr.trim();
2594
+ if (!rt) continue;
2595
+ const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
2596
+ if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
2597
+ ranges.push([parts[0], parts[1]]);
2598
+ } else if (Number.isFinite(parts[0])) {
2599
+ ranges.push([parts[0], parts[0]]);
2629
2600
  }
2630
2601
  }
2602
+ files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
2631
2603
  }
2632
- return tools;
2604
+ return files;
2605
+ }
2606
+ function extractPathFromCommand(command) {
2607
+ const tokens = command.trim().split(/\s+/);
2608
+ const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
2609
+ return pathTokens[0] || ".";
2633
2610
  }
2634
- var LLMResponseParser = class {
2635
- parse(text) {
2636
- if (typeof text !== "string") {
2637
- throw new TypeError("Command text must be a string.");
2638
- }
2639
- const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
2640
- return parseQwen3ToolCalls(withoutThink);
2641
- }
2642
- };
2643
2611
 
2644
2612
  // tools/warp_grep/agent/tools/grep.ts
2645
2613
  async function toolGrep(provider, args) {
@@ -2690,29 +2658,42 @@ async function toolRead(provider, args) {
2690
2658
 
2691
2659
  // tools/warp_grep/agent/tools/list_directory.ts
2692
2660
  init_config();
2693
- async function toolListDirectory(provider, args) {
2694
- const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
2695
- const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
2696
- async function getListRecursive(currentDepth) {
2697
- const entries = await provider.listDirectory({
2698
- path: args.path,
2699
- pattern: args.pattern ?? null,
2700
- maxResults,
2701
- maxDepth: currentDepth
2702
- });
2703
- if (entries.length >= maxResults && currentDepth > 0) {
2704
- return getListRecursive(currentDepth - 1);
2705
- }
2706
- return { entries };
2707
- }
2708
- const { entries: list } = await getListRecursive(initialDepth);
2709
- if (!list.length) return "empty";
2710
- const tree = list.map((e) => {
2711
- const indent = " ".repeat(e.depth);
2712
- const name = e.type === "dir" ? `${e.name}/` : e.name;
2713
- return `${indent}${name}`;
2714
- }).join("\n");
2715
- return tree;
2661
+ var import_path2 = __toESM(require("path"), 1);
2662
+ async function toolListDirectory(provider, args, repoRoot) {
2663
+ const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
2664
+ const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
2665
+ const entries = await provider.listDirectory({
2666
+ path: args.path,
2667
+ pattern: args.pattern ?? null,
2668
+ maxResults,
2669
+ maxDepth
2670
+ });
2671
+ if (!entries.length) return "empty";
2672
+ if (repoRoot) {
2673
+ const absRoot = import_path2.default.resolve(repoRoot);
2674
+ const lines = entries.map((e) => import_path2.default.join(absRoot, e.path));
2675
+ return lines.join("\n");
2676
+ }
2677
+ return entries.map((e) => e.path).join("\n");
2678
+ }
2679
+
2680
+ // tools/warp_grep/agent/tools/glob.ts
2681
+ async function toolGlob(provider, args) {
2682
+ const res = await provider.glob(args);
2683
+ if (res.error) {
2684
+ return res.error;
2685
+ }
2686
+ if (!res.files.length) {
2687
+ return "no matches";
2688
+ }
2689
+ const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
2690
+ const body = res.files.join("\n");
2691
+ const truncated = res.totalFound > res.files.length ? `
2692
+ [${res.totalFound - res.files.length} files truncated]` : "";
2693
+ return `${header}
2694
+ ---
2695
+ ${body}
2696
+ ---${truncated}`;
2716
2697
  }
2717
2698
 
2718
2699
  // tools/warp_grep/agent/tools/finish.ts
@@ -2773,32 +2754,21 @@ function mergeRanges(ranges) {
2773
2754
  return merged;
2774
2755
  }
2775
2756
 
2776
- // tools/warp_grep/agent/formatter.ts
2777
- var ToolOutputFormatter = class {
2778
- format(toolName, _args, output, options = {}) {
2779
- const name = (toolName ?? "").trim();
2780
- if (!name) {
2781
- return "";
2782
- }
2783
- const payload = output?.toString?.()?.trim?.() ?? "";
2784
- const isError = Boolean(options.isError);
2785
- if (!payload && !isError) {
2786
- return "";
2787
- }
2788
- return `<tool_response>
2789
- ${payload}
2790
- </tool_response>`;
2791
- }
2792
- };
2793
- var sharedFormatter = new ToolOutputFormatter();
2794
- function formatAgentToolOutput(toolName, args, output, options = {}) {
2795
- return sharedFormatter.format(toolName, args, output, options);
2796
- }
2797
-
2798
2757
  // tools/warp_grep/agent/helpers.ts
2799
- var import_path2 = __toESM(require("path"), 1);
2758
+ var import_path3 = __toESM(require("path"), 1);
2800
2759
  init_config();
2801
2760
  var TRUNCATED_MARKER = "[truncated for context limit]";
2761
+ function getMessageSize(m) {
2762
+ if (m.role === "tool") return m.content.length;
2763
+ if (m.role === "assistant") {
2764
+ let size = typeof m.content === "string" ? m.content.length : 0;
2765
+ if (m.tool_calls) {
2766
+ size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
2767
+ }
2768
+ return size;
2769
+ }
2770
+ return m.content.length;
2771
+ }
2802
2772
  function formatTurnMessage(turnsUsed, maxTurns) {
2803
2773
  const turnsRemaining = maxTurns - turnsUsed;
2804
2774
  if (turnsRemaining === 1) {
@@ -2809,33 +2779,30 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
2809
2779
  You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
2810
2780
  }
2811
2781
  function calculateContextBudget(messages) {
2812
- const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
2782
+ const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
2813
2783
  const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
2814
- const percent = Math.round(totalChars / maxChars * 100);
2815
- const usedK = Math.round(totalChars / 1e3);
2816
- const maxK = Math.round(maxChars / 1e3);
2817
- return `<context_budget>${percent}% (${usedK}K/${maxK}K chars used)</context_budget>`;
2784
+ const percent = Math.floor(totalChars / maxChars * 100);
2785
+ const usedK = Math.floor(totalChars / 1e3);
2786
+ const maxK = Math.floor(maxChars / 1e3);
2787
+ return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
2818
2788
  }
2819
2789
  async function buildInitialState(repoRoot, searchTerm, provider, options) {
2820
2790
  const budget = calculateContextBudget([]);
2821
- const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
2791
+ const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
2822
2792
  const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
2793
+ const absRoot = import_path3.default.resolve(repoRoot);
2823
2794
  try {
2824
2795
  const entries = await provider.listDirectory({
2825
2796
  path: ".",
2826
2797
  maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
2827
2798
  maxDepth: treeDepth
2828
2799
  });
2829
- const treeLines = entries.map((e) => {
2830
- const indent = " ".repeat(e.depth);
2831
- const name = e.type === "dir" ? `${e.name}/` : e.name;
2832
- return `${indent}${name}`;
2833
- });
2834
- const repoName = import_path2.default.basename(repoRoot);
2835
- const treeOutput = treeLines.length > 0 ? `${repoName}/
2836
- ${treeLines.join("\n")}` : `${repoName}/`;
2800
+ const lines = [absRoot];
2801
+ for (const e of entries) {
2802
+ lines.push(import_path3.default.join(absRoot, e.path));
2803
+ }
2837
2804
  return `<repo_structure>
2838
- ${treeOutput}
2805
+ ${lines.join("\n")}
2839
2806
  </repo_structure>
2840
2807
 
2841
2808
  <search_string>
@@ -2844,9 +2811,8 @@ ${searchTerm}
2844
2811
  ${budget}
2845
2812
  ${turnTag}`;
2846
2813
  } catch {
2847
- const repoName = import_path2.default.basename(repoRoot);
2848
2814
  return `<repo_structure>
2849
- ${repoName}/
2815
+ ${absRoot}
2850
2816
  </repo_structure>
2851
2817
 
2852
2818
  <search_string>
@@ -2857,26 +2823,32 @@ ${turnTag}`;
2857
2823
  }
2858
2824
  }
2859
2825
  function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
2860
- const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
2826
+ const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
2861
2827
  if (getTotalChars() <= maxChars) {
2862
2828
  return messages;
2863
2829
  }
2864
- const userIndices = [];
2830
+ const truncatableIndices = [];
2865
2831
  let firstUserSkipped = false;
2866
2832
  for (let i = 0; i < messages.length; i++) {
2867
- if (messages[i].role === "user") {
2833
+ const m = messages[i];
2834
+ if (m.role === "tool") {
2835
+ truncatableIndices.push(i);
2836
+ } else if (m.role === "user") {
2868
2837
  if (!firstUserSkipped) {
2869
2838
  firstUserSkipped = true;
2870
2839
  continue;
2871
2840
  }
2872
- userIndices.push(i);
2841
+ truncatableIndices.push(i);
2873
2842
  }
2874
2843
  }
2875
- for (const idx of userIndices) {
2844
+ for (const idx of truncatableIndices) {
2876
2845
  if (getTotalChars() <= maxChars) {
2877
2846
  break;
2878
2847
  }
2879
- if (messages[idx].content !== TRUNCATED_MARKER) {
2848
+ const m = messages[idx];
2849
+ if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
2850
+ messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
2851
+ } else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
2880
2852
  messages[idx] = { role: "user", content: TRUNCATED_MARKER };
2881
2853
  }
2882
2854
  }
@@ -2886,9 +2858,115 @@ function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS
2886
2858
  // tools/warp_grep/agent/runner.ts
2887
2859
  var import_openai2 = __toESM(require("openai"), 1);
2888
2860
  init_version();
2889
- var import_path3 = __toESM(require("path"), 1);
2890
- var parser = new LLMResponseParser();
2861
+ var import_path4 = __toESM(require("path"), 1);
2891
2862
  var DEFAULT_API_URL3 = "https://api.morphllm.com";
2863
+ var TOOL_SPECS = [
2864
+ {
2865
+ type: "function",
2866
+ function: {
2867
+ name: "list_directory",
2868
+ description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
2869
+ parameters: {
2870
+ type: "object",
2871
+ properties: {
2872
+ command: {
2873
+ type: "string",
2874
+ description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
2875
+ }
2876
+ },
2877
+ required: ["command"]
2878
+ }
2879
+ }
2880
+ },
2881
+ {
2882
+ type: "function",
2883
+ function: {
2884
+ name: "grep_search",
2885
+ description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
2886
+ parameters: {
2887
+ type: "object",
2888
+ properties: {
2889
+ pattern: {
2890
+ type: "string",
2891
+ description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
2892
+ },
2893
+ path: {
2894
+ type: "string",
2895
+ description: "File or directory to search in. Defaults to current working directory."
2896
+ },
2897
+ glob: {
2898
+ type: "string",
2899
+ description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
2900
+ },
2901
+ limit: {
2902
+ type: "integer",
2903
+ description: "Limit output to first N matching lines. Shows all matches if not specified."
2904
+ }
2905
+ },
2906
+ required: ["pattern"]
2907
+ }
2908
+ }
2909
+ },
2910
+ {
2911
+ type: "function",
2912
+ function: {
2913
+ name: "glob",
2914
+ description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
2915
+ parameters: {
2916
+ type: "object",
2917
+ properties: {
2918
+ pattern: {
2919
+ type: "string",
2920
+ description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
2921
+ },
2922
+ path: {
2923
+ type: "string",
2924
+ description: "Directory to search in. Defaults to repository root."
2925
+ }
2926
+ },
2927
+ required: ["pattern"]
2928
+ }
2929
+ }
2930
+ },
2931
+ {
2932
+ type: "function",
2933
+ function: {
2934
+ name: "read",
2935
+ description: "Read entire files or specific line ranges using absolute paths.",
2936
+ parameters: {
2937
+ type: "object",
2938
+ properties: {
2939
+ path: {
2940
+ type: "string",
2941
+ description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
2942
+ },
2943
+ lines: {
2944
+ type: "string",
2945
+ description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
2946
+ }
2947
+ },
2948
+ required: ["path"]
2949
+ }
2950
+ }
2951
+ },
2952
+ {
2953
+ type: "function",
2954
+ function: {
2955
+ name: "finish",
2956
+ description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
2957
+ parameters: {
2958
+ type: "object",
2959
+ properties: {
2960
+ files: {
2961
+ type: "string",
2962
+ description: "One file per line as path:lines (e.g. 'src/auth.py:1-15,25-50\\nsrc/user.py'). Omit line range to include entire file."
2963
+ }
2964
+ },
2965
+ required: ["files"]
2966
+ }
2967
+ }
2968
+ }
2969
+ ];
2892
2970
  async function callModel(messages, model, options = {}) {
2893
2971
  const baseUrl = options.morphApiUrl || DEFAULT_API_URL3;
2894
2972
  const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
@@ -2909,8 +2987,9 @@ async function callModel(messages, model, options = {}) {
2909
2987
  data = await client.chat.completions.create({
2910
2988
  model,
2911
2989
  temperature: 0,
2912
- max_tokens: 1024,
2990
+ max_tokens: 2048,
2913
2991
  messages,
2992
+ tools: TOOL_SPECS,
2914
2993
  ...options.search_type ? { search_type: options.search_type } : {}
2915
2994
  });
2916
2995
  } catch (error) {
@@ -2922,187 +3001,87 @@ async function callModel(messages, model, options = {}) {
2922
3001
  throw error;
2923
3002
  }
2924
3003
  const choice = data?.choices?.[0];
2925
- const content = choice?.message?.content;
2926
- if (content && typeof content === "string") {
2927
- return content;
3004
+ const message = choice?.message;
3005
+ if (!message) {
3006
+ if (attempt === MAX_EMPTY_RETRIES) {
3007
+ throw new Error("Invalid response from model: no message in response");
3008
+ }
3009
+ await new Promise((resolve2) => setTimeout(resolve2, 200));
3010
+ continue;
3011
+ }
3012
+ const toolCalls = (message.tool_calls || []).map((tc) => ({
3013
+ id: tc.id,
3014
+ type: "function",
3015
+ function: { name: tc.function.name, arguments: tc.function.arguments }
3016
+ }));
3017
+ if (message.content || toolCalls.length > 0) {
3018
+ return { content: message.content ?? null, tool_calls: toolCalls };
2928
3019
  }
2929
3020
  if (attempt === MAX_EMPTY_RETRIES) {
2930
3021
  const finishReason = choice?.finish_reason ?? "unknown";
2931
- const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
2932
- const choicesLen = data?.choices?.length ?? 0;
2933
- const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
2934
3022
  throw new Error(
2935
- `Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
3023
+ `Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
2936
3024
  );
2937
3025
  }
2938
3026
  await new Promise((resolve2) => setTimeout(resolve2, 200));
2939
3027
  }
2940
3028
  throw new Error("Invalid response from model");
2941
3029
  }
2942
- async function runWarpGrep(config) {
2943
- const totalStart = Date.now();
2944
- const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
2945
- const timings = { turns: [], timeout_ms: timeoutMs };
2946
- const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
2947
- const model = config.model || DEFAULT_MODEL;
2948
- const messages = [];
2949
- const maxTurns = AGENT_CONFIG.MAX_TURNS;
2950
- const initialStateStart = Date.now();
2951
- const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
2952
- timings.initial_state_ms = Date.now() - initialStateStart;
2953
- messages.push({ role: "user", content: initialState });
2954
- const provider = config.provider;
2955
- const errors = [];
2956
- let finishMeta;
2957
- let terminationReason = "terminated";
2958
- for (let turn = 1; turn <= maxTurns; turn += 1) {
2959
- const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
2960
- enforceContextLimit(messages);
2961
- const modelCallStart = Date.now();
2962
- const assistantContent = await callModel(messages, model, {
2963
- morphApiKey: config.morphApiKey,
2964
- morphApiUrl: config.morphApiUrl,
2965
- retryConfig: config.retryConfig,
2966
- timeout: timeoutMs,
2967
- search_type: config.search_type
2968
- }).catch((e) => {
2969
- const errMsg = e instanceof Error ? e.message : String(e);
2970
- console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
2971
- errors.push({ message: errMsg });
2972
- return "";
2973
- });
2974
- turnMetrics.morph_api_ms = Date.now() - modelCallStart;
2975
- if (!assistantContent) {
2976
- console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
2977
- timings.turns.push(turnMetrics);
2978
- break;
2979
- }
2980
- messages.push({ role: "assistant", content: assistantContent });
2981
- const toolCalls = parser.parse(assistantContent);
2982
- if (toolCalls.length === 0) {
2983
- console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
2984
- errors.push({ message: "No tool calls produced by the model. Your MCP is likely out of date! Update it by running: rm -rf ~/.npm/_npx && npm cache clean --force && npx -y @morphllm/morphmcp@latest" });
2985
- terminationReason = "terminated";
2986
- timings.turns.push(turnMetrics);
2987
- break;
2988
- }
2989
- const finishCalls = toolCalls.filter((c) => c.name === "finish");
2990
- const grepCalls = toolCalls.filter((c) => c.name === "grep");
2991
- const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
2992
- const readCalls = toolCalls.filter((c) => c.name === "read");
2993
- const skipCalls = toolCalls.filter((c) => c.name === "_skip");
2994
- const formatted = [];
2995
- for (const c of skipCalls) {
2996
- const msg = c.arguments?.message || "Command skipped due to parsing error";
2997
- formatted.push(msg);
2998
- }
2999
- const allPromises = [];
3000
- for (const c of grepCalls) {
3001
- const args = c.arguments ?? {};
3002
- allPromises.push(
3003
- toolGrep(provider, args).then(
3004
- ({ output }) => formatAgentToolOutput("grep", args, output),
3005
- (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
3006
- )
3007
- );
3008
- }
3009
- for (const c of listDirCalls) {
3010
- const args = c.arguments ?? {};
3011
- allPromises.push(
3012
- toolListDirectory(provider, args).then(
3013
- (p) => formatAgentToolOutput("list_directory", args, p),
3014
- (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
3015
- )
3016
- );
3017
- }
3018
- for (const c of readCalls) {
3019
- const args = c.arguments ?? {};
3020
- allPromises.push(
3021
- toolRead(provider, args).then(
3022
- (p) => formatAgentToolOutput("read", args, p),
3023
- (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
3024
- )
3025
- );
3026
- }
3027
- const toolExecStart = Date.now();
3028
- const allResults = await Promise.all(allPromises);
3029
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
3030
- for (const result of allResults) {
3031
- formatted.push(result);
3030
+ function safeParseJSON(s) {
3031
+ try {
3032
+ return JSON.parse(s);
3033
+ } catch {
3034
+ return {};
3035
+ }
3036
+ }
3037
+ async function executeTool(provider, name, args, repoRoot) {
3038
+ switch (name) {
3039
+ case "grep_search": {
3040
+ const grepArgs = {
3041
+ pattern: args.pattern,
3042
+ path: args.path || "."
3043
+ };
3044
+ if (args.glob) grepArgs.glob = args.glob;
3045
+ if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
3046
+ const result = await toolGrep(provider, grepArgs);
3047
+ let output = result.output;
3048
+ if (args.limit && typeof args.limit === "number") {
3049
+ const lines = output.split("\n");
3050
+ if (lines.length > args.limit) {
3051
+ output = lines.slice(0, args.limit).join("\n") + `
3052
+ ... (truncated at ${args.limit} lines)`;
3053
+ }
3054
+ }
3055
+ return output;
3032
3056
  }
3033
- if (formatted.length > 0) {
3034
- const turnMessage = formatTurnMessage(turn, maxTurns);
3035
- const contextBudget = calculateContextBudget(messages);
3036
- messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
3057
+ case "glob": {
3058
+ return toolGlob(provider, {
3059
+ pattern: args.pattern,
3060
+ path: args.path
3061
+ });
3037
3062
  }
3038
- timings.turns.push(turnMetrics);
3039
- if (finishCalls.length) {
3040
- const fc = finishCalls[0];
3041
- const files = fc.arguments?.files ?? [];
3042
- const textResult = fc.arguments?.textResult;
3043
- finishMeta = { files };
3044
- terminationReason = "completed";
3045
- if (files.length === 0) {
3046
- const payload2 = textResult || "No relevant code found.";
3047
- timings.turns.push(turnMetrics);
3048
- timings.total_ms = Date.now() - totalStart;
3049
- return {
3050
- terminationReason: "completed",
3051
- messages,
3052
- finish: { payload: payload2, metadata: finishMeta },
3053
- timings
3054
- };
3055
- }
3056
- break;
3063
+ case "list_directory": {
3064
+ const dirPath = extractPathFromCommand(args.command || ".");
3065
+ return toolListDirectory(provider, { path: dirPath }, repoRoot);
3057
3066
  }
3058
- }
3059
- if (terminationReason !== "completed" || !finishMeta) {
3060
- timings.total_ms = Date.now() - totalStart;
3061
- return { terminationReason, messages, errors, timings };
3062
- }
3063
- const parts = ["Relevant context found:"];
3064
- for (const f of finishMeta.files) {
3065
- const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
3066
- parts.push(`- ${f.path}: ${ranges}`);
3067
- }
3068
- const payload = parts.join("\n");
3069
- const finishResolutionStart = Date.now();
3070
- const fileReadErrors = [];
3071
- const resolved = await readFinishFiles(
3072
- repoRoot,
3073
- finishMeta.files,
3074
- async (p, s, e) => {
3075
- try {
3076
- const rr = await provider.read({ path: p, start: s, end: e });
3077
- return rr.lines.map((l) => {
3078
- const idx = l.indexOf("|");
3079
- return idx >= 0 ? l.slice(idx + 1) : l;
3080
- });
3081
- } catch (err) {
3082
- const errorMsg = err instanceof Error ? err.message : String(err);
3083
- fileReadErrors.push({ path: p, error: errorMsg });
3084
- console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
3085
- return [`[couldn't find: ${p}]`];
3067
+ case "read": {
3068
+ const readArgs = {
3069
+ path: args.path
3070
+ };
3071
+ if (args.lines && typeof args.lines === "string") {
3072
+ Object.assign(readArgs, parseReadLines(args.lines));
3086
3073
  }
3074
+ return toolRead(provider, readArgs);
3087
3075
  }
3088
- );
3089
- timings.finish_resolution_ms = Date.now() - finishResolutionStart;
3090
- if (fileReadErrors.length > 0) {
3091
- errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
3076
+ default:
3077
+ return `Unknown tool: ${name}`;
3092
3078
  }
3093
- timings.total_ms = Date.now() - totalStart;
3094
- return {
3095
- terminationReason: "completed",
3096
- messages,
3097
- finish: { payload, metadata: finishMeta, resolved },
3098
- timings
3099
- };
3100
3079
  }
3101
3080
  async function* runWarpGrepStreaming(config) {
3102
3081
  const totalStart = Date.now();
3103
3082
  const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
3104
3083
  const timings = { turns: [], timeout_ms: timeoutMs };
3105
- const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
3084
+ const repoRoot = import_path4.default.resolve(config.repoRoot || process.cwd());
3106
3085
  const model = config.model || DEFAULT_MODEL;
3107
3086
  const messages = [];
3108
3087
  const maxTurns = AGENT_CONFIG.MAX_TURNS;
@@ -3118,7 +3097,7 @@ async function* runWarpGrepStreaming(config) {
3118
3097
  const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
3119
3098
  enforceContextLimit(messages);
3120
3099
  const modelCallStart = Date.now();
3121
- const assistantContent = await callModel(messages, model, {
3100
+ const response = await callModel(messages, model, {
3122
3101
  morphApiKey: config.morphApiKey,
3123
3102
  morphApiUrl: config.morphApiUrl,
3124
3103
  retryConfig: config.retryConfig,
@@ -3126,90 +3105,45 @@ async function* runWarpGrepStreaming(config) {
3126
3105
  search_type: config.search_type
3127
3106
  }).catch((e) => {
3128
3107
  const errMsg = e instanceof Error ? e.message : String(e);
3129
- console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
3108
+ console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
3130
3109
  errors.push({ message: errMsg });
3131
- return "";
3110
+ return null;
3132
3111
  });
3133
3112
  turnMetrics.morph_api_ms = Date.now() - modelCallStart;
3134
- if (!assistantContent) {
3135
- console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
3113
+ if (!response) {
3136
3114
  timings.turns.push(turnMetrics);
3137
3115
  break;
3138
3116
  }
3139
- messages.push({ role: "assistant", content: assistantContent });
3140
- const toolCalls = parser.parse(assistantContent);
3117
+ const toolCalls = response.tool_calls;
3118
+ messages.push({
3119
+ role: "assistant",
3120
+ content: response.content,
3121
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
3122
+ });
3141
3123
  if (toolCalls.length === 0) {
3142
- console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
3143
- errors.push({ message: "No tool calls produced by the model. Your MCP is likely out of date! Update it by running: rm -rf ~/.npm/_npx && npm cache clean --force && npx -y @morphllm/morphmcp@latest" });
3124
+ console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
3125
+ errors.push({ message: "No tool calls produced by the model." });
3144
3126
  terminationReason = "terminated";
3145
3127
  timings.turns.push(turnMetrics);
3146
3128
  break;
3147
3129
  }
3148
3130
  yield {
3149
3131
  turn,
3150
- toolCalls: toolCalls.map((c) => ({
3151
- name: c.name,
3152
- arguments: c.arguments ?? {}
3132
+ toolCalls: toolCalls.map((tc) => ({
3133
+ name: tc.function.name,
3134
+ arguments: safeParseJSON(tc.function.arguments)
3153
3135
  }))
3154
3136
  };
3155
- const finishCalls = toolCalls.filter((c) => c.name === "finish");
3156
- const grepCalls = toolCalls.filter((c) => c.name === "grep");
3157
- const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
3158
- const readCalls = toolCalls.filter((c) => c.name === "read");
3159
- const skipCalls = toolCalls.filter((c) => c.name === "_skip");
3160
- const formatted = [];
3161
- for (const c of skipCalls) {
3162
- const msg = c.arguments?.message || "Command skipped due to parsing error";
3163
- formatted.push(msg);
3164
- }
3165
- const allPromises = [];
3166
- for (const c of grepCalls) {
3167
- const args = c.arguments ?? {};
3168
- allPromises.push(
3169
- toolGrep(provider, args).then(
3170
- ({ output }) => formatAgentToolOutput("grep", args, output),
3171
- (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
3172
- )
3173
- );
3174
- }
3175
- for (const c of listDirCalls) {
3176
- const args = c.arguments ?? {};
3177
- allPromises.push(
3178
- toolListDirectory(provider, args).then(
3179
- (p) => formatAgentToolOutput("list_directory", args, p),
3180
- (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
3181
- )
3182
- );
3183
- }
3184
- for (const c of readCalls) {
3185
- const args = c.arguments ?? {};
3186
- allPromises.push(
3187
- toolRead(provider, args).then(
3188
- (p) => formatAgentToolOutput("read", args, p),
3189
- (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
3190
- )
3191
- );
3192
- }
3193
- const toolExecStart = Date.now();
3194
- const allResults = await Promise.all(allPromises);
3195
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
3196
- for (const result of allResults) {
3197
- formatted.push(result);
3198
- }
3199
- if (formatted.length > 0) {
3200
- const turnMessage = formatTurnMessage(turn, maxTurns);
3201
- const contextBudget = calculateContextBudget(messages);
3202
- messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
3203
- }
3204
- timings.turns.push(turnMetrics);
3205
- if (finishCalls.length) {
3206
- const fc = finishCalls[0];
3207
- const files = fc.arguments?.files ?? [];
3208
- const textResult = fc.arguments?.textResult;
3137
+ const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
3138
+ if (finishCall) {
3139
+ const args = safeParseJSON(finishCall.function.arguments);
3140
+ const filesStr = args.files || "";
3141
+ const files = parseFinishFiles(filesStr);
3209
3142
  finishMeta = { files };
3210
3143
  terminationReason = "completed";
3211
3144
  if (files.length === 0) {
3212
- const payload2 = textResult || "No relevant code found.";
3145
+ const payload2 = filesStr || "No relevant code found.";
3146
+ timings.turns.push(turnMetrics);
3213
3147
  timings.total_ms = Date.now() - totalStart;
3214
3148
  return {
3215
3149
  terminationReason: "completed",
@@ -3218,8 +3152,25 @@ async function* runWarpGrepStreaming(config) {
3218
3152
  timings
3219
3153
  };
3220
3154
  }
3155
+ timings.turns.push(turnMetrics);
3221
3156
  break;
3222
3157
  }
3158
+ const toolExecStart = Date.now();
3159
+ const results = await Promise.all(
3160
+ toolCalls.map(async (tc) => {
3161
+ const args = safeParseJSON(tc.function.arguments);
3162
+ const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
3163
+ return { tool_call_id: tc.id, content: output };
3164
+ })
3165
+ );
3166
+ turnMetrics.local_tools_ms = Date.now() - toolExecStart;
3167
+ for (const result of results) {
3168
+ messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
3169
+ }
3170
+ const turnMsg = formatTurnMessage(turn, maxTurns);
3171
+ const budget = calculateContextBudget(messages);
3172
+ messages.push({ role: "user", content: turnMsg + "\n" + budget });
3173
+ timings.turns.push(turnMetrics);
3223
3174
  }
3224
3175
  if (terminationReason !== "completed" || !finishMeta) {
3225
3176
  timings.total_ms = Date.now() - totalStart;
@@ -3237,17 +3188,22 @@ async function* runWarpGrepStreaming(config) {
3237
3188
  repoRoot,
3238
3189
  finishMeta.files,
3239
3190
  async (p, s, e) => {
3191
+ let resolvedPath = p;
3192
+ if (!p.startsWith(repoRoot)) {
3193
+ const relative2 = p.startsWith("/") ? p.slice(1) : p;
3194
+ resolvedPath = import_path4.default.join(repoRoot, relative2);
3195
+ }
3240
3196
  try {
3241
- const rr = await provider.read({ path: p, start: s, end: e });
3197
+ const rr = await provider.read({ path: resolvedPath, start: s, end: e });
3242
3198
  return rr.lines.map((l) => {
3243
3199
  const idx = l.indexOf("|");
3244
3200
  return idx >= 0 ? l.slice(idx + 1) : l;
3245
3201
  });
3246
3202
  } catch (err) {
3247
3203
  const errorMsg = err instanceof Error ? err.message : String(err);
3248
- fileReadErrors.push({ path: p, error: errorMsg });
3249
- console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
3250
- return [`[couldn't find: ${p}]`];
3204
+ fileReadErrors.push({ path: resolvedPath, error: errorMsg });
3205
+ console.error(`[warp_grep] Failed to read file: ${resolvedPath} - ${errorMsg}`);
3206
+ return [`[couldn't find: ${resolvedPath}]`];
3251
3207
  }
3252
3208
  }
3253
3209
  );
@@ -3263,6 +3219,14 @@ async function* runWarpGrepStreaming(config) {
3263
3219
  timings
3264
3220
  };
3265
3221
  }
3222
+ async function runWarpGrep(config) {
3223
+ const gen = runWarpGrepStreaming(config);
3224
+ let result = await gen.next();
3225
+ while (!result.done) {
3226
+ result = await gen.next();
3227
+ }
3228
+ return result.value;
3229
+ }
3266
3230
 
3267
3231
  // tools/warp_grep/providers/remote.ts
3268
3232
  init_config();
@@ -3442,6 +3406,35 @@ var RemoteCommandsProvider = class {
3442
3406
  return [];
3443
3407
  }
3444
3408
  }
3409
+ /**
3410
+ * Glob search - finds files matching a pattern.
3411
+ * Falls back to a grep --files approach via the listDir command.
3412
+ */
3413
+ async glob(params) {
3414
+ const searchPath = params.path || this.repoRoot;
3415
+ try {
3416
+ const stdout = await this.commands.listDir(searchPath, 10);
3417
+ const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
3418
+ const globToRegex = (glob) => {
3419
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
3420
+ return new RegExp(escaped);
3421
+ };
3422
+ const regex = globToRegex(params.pattern);
3423
+ const matched = allPaths.filter((p) => {
3424
+ const name = p.split("/").pop() || "";
3425
+ return regex.test(name) && !shouldSkip(name);
3426
+ });
3427
+ const totalFound = matched.length;
3428
+ return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
3429
+ } catch (error) {
3430
+ return {
3431
+ files: [],
3432
+ searchDir: searchPath,
3433
+ totalFound: 0,
3434
+ error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
3435
+ };
3436
+ }
3437
+ }
3445
3438
  };
3446
3439
 
3447
3440
  // tools/warp_grep/providers/code_storage_http.ts
@@ -3468,9 +3461,9 @@ function createCodeStorageHttpCommands(config) {
3468
3461
  const { baseUrl, repoId, branch } = config;
3469
3462
  const encodedRepoId = encodeURIComponent(repoId);
3470
3463
  return {
3471
- grep: (pattern, path5, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path5, glob, branch }, "grep"),
3472
- read: (path5, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path5, start, end, branch }, "read"),
3473
- listDir: (path5, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path5, maxDepth, branch }, "list")
3464
+ grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
3465
+ read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
3466
+ listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
3474
3467
  };
3475
3468
  }
3476
3469
 
@@ -3853,10 +3846,10 @@ var GitHubClient = class {
3853
3846
  /**
3854
3847
  * Make an authenticated API request
3855
3848
  */
3856
- async request(method, path5, body) {
3857
- const url = `${this.baseUrl}${path5}`;
3849
+ async request(method, path6, body) {
3850
+ const url = `${this.baseUrl}${path6}`;
3858
3851
  if (this.debug) {
3859
- console.log(`[GitHub SDK] ${method} ${path5}`, body || "");
3852
+ console.log(`[GitHub SDK] ${method} ${path6}`, body || "");
3860
3853
  }
3861
3854
  const controller = new AbortController();
3862
3855
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
@@ -6424,6 +6417,25 @@ init_paths();
6424
6417
  // tools/warp_grep/agent/index.ts
6425
6418
  init_config();
6426
6419
 
6420
+ // tools/warp_grep/agent/formatter.ts
6421
+ var ToolOutputFormatter = class {
6422
+ format(toolName, _args, output, options = {}) {
6423
+ const name = (toolName ?? "").trim();
6424
+ if (!name) {
6425
+ return "";
6426
+ }
6427
+ const payload = output?.toString?.()?.trim?.() ?? "";
6428
+ const isError = Boolean(options.isError);
6429
+ if (!payload && !isError) {
6430
+ return "";
6431
+ }
6432
+ return `<tool_response>
6433
+ ${payload}
6434
+ </tool_response>`;
6435
+ }
6436
+ };
6437
+ var sharedFormatter = new ToolOutputFormatter();
6438
+
6427
6439
  // tools/warp_grep/index.ts
6428
6440
  var warpGrepInputSchema = import_zod5.z.object({
6429
6441
  search_term: import_zod5.z.string().describe("Search problem statement that this subagent is supposed to research for")