@wrongstack/tools 0.1.1 → 0.1.3

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 (103) hide show
  1. package/dist/audit.d.ts +25 -0
  2. package/dist/audit.js +209 -0
  3. package/dist/audit.js.map +1 -0
  4. package/dist/bash.d.ts +16 -0
  5. package/dist/bash.js +180 -0
  6. package/dist/bash.js.map +1 -0
  7. package/dist/batch-tool-use.d.ts +26 -0
  8. package/dist/batch-tool-use.js +106 -0
  9. package/dist/batch-tool-use.js.map +1 -0
  10. package/dist/builtin.d.ts +5 -0
  11. package/dist/builtin.js +3735 -0
  12. package/dist/builtin.js.map +1 -0
  13. package/dist/diff.d.ts +20 -0
  14. package/dist/diff.js +142 -0
  15. package/dist/diff.js.map +1 -0
  16. package/dist/document.d.ts +27 -0
  17. package/dist/document.js +148 -0
  18. package/dist/document.js.map +1 -0
  19. package/dist/edit.d.ts +22 -0
  20. package/dist/edit.js +138 -0
  21. package/dist/edit.js.map +1 -0
  22. package/dist/exec.d.ts +21 -0
  23. package/dist/exec.js +159 -0
  24. package/dist/exec.js.map +1 -0
  25. package/dist/fetch.d.ts +15 -0
  26. package/dist/fetch.js +213 -0
  27. package/dist/fetch.js.map +1 -0
  28. package/dist/format.d.ts +18 -0
  29. package/dist/format.js +194 -0
  30. package/dist/format.js.map +1 -0
  31. package/dist/git.d.ts +27 -0
  32. package/dist/git.js +174 -0
  33. package/dist/git.js.map +1 -0
  34. package/dist/glob.d.ts +14 -0
  35. package/dist/glob.js +101 -0
  36. package/dist/glob.js.map +1 -0
  37. package/dist/grep.d.ts +20 -0
  38. package/dist/grep.js +264 -0
  39. package/dist/grep.js.map +1 -0
  40. package/dist/index.d.ts +34 -563
  41. package/dist/index.js +717 -442
  42. package/dist/index.js.map +1 -1
  43. package/dist/install.d.ts +19 -0
  44. package/dist/install.js +186 -0
  45. package/dist/install.js.map +1 -0
  46. package/dist/json.d.ts +20 -0
  47. package/dist/json.js +124 -0
  48. package/dist/json.js.map +1 -0
  49. package/dist/lint.d.ts +20 -0
  50. package/dist/lint.js +191 -0
  51. package/dist/lint.js.map +1 -0
  52. package/dist/logs.d.ts +27 -0
  53. package/dist/logs.js +180 -0
  54. package/dist/logs.js.map +1 -0
  55. package/dist/memory.d.ts +22 -0
  56. package/dist/memory.js +53 -0
  57. package/dist/memory.js.map +1 -0
  58. package/dist/mode.d.ts +20 -0
  59. package/dist/mode.js +81 -0
  60. package/dist/mode.js.map +1 -0
  61. package/dist/outdated.d.ts +26 -0
  62. package/dist/outdated.js +138 -0
  63. package/dist/outdated.js.map +1 -0
  64. package/dist/patch.d.ts +18 -0
  65. package/dist/patch.js +101 -0
  66. package/dist/patch.js.map +1 -0
  67. package/dist/read.d.ts +16 -0
  68. package/dist/read.js +81 -0
  69. package/dist/read.js.map +1 -0
  70. package/dist/replace.d.ts +23 -0
  71. package/dist/replace.js +196 -0
  72. package/dist/replace.js.map +1 -0
  73. package/dist/scaffold.d.ts +20 -0
  74. package/dist/scaffold.js +185 -0
  75. package/dist/scaffold.js.map +1 -0
  76. package/dist/search.d.ts +20 -0
  77. package/dist/search.js +212 -0
  78. package/dist/search.js.map +1 -0
  79. package/dist/test.d.ts +24 -0
  80. package/dist/test.js +247 -0
  81. package/dist/test.js.map +1 -0
  82. package/dist/todo.d.ts +12 -0
  83. package/dist/todo.js +53 -0
  84. package/dist/todo.js.map +1 -0
  85. package/dist/tool-help.d.ts +23 -0
  86. package/dist/tool-help.js +122 -0
  87. package/dist/tool-help.js.map +1 -0
  88. package/dist/tool-search.d.ts +22 -0
  89. package/dist/tool-search.js +70 -0
  90. package/dist/tool-search.js.map +1 -0
  91. package/dist/tool-use.d.ts +16 -0
  92. package/dist/tool-use.js +79 -0
  93. package/dist/tool-use.js.map +1 -0
  94. package/dist/tree.d.ts +21 -0
  95. package/dist/tree.js +176 -0
  96. package/dist/tree.js.map +1 -0
  97. package/dist/typecheck.d.ts +19 -0
  98. package/dist/typecheck.js +181 -0
  99. package/dist/typecheck.js.map +1 -0
  100. package/dist/write.d.ts +15 -0
  101. package/dist/write.js +77 -0
  102. package/dist/write.js.map +1 -0
  103. package/package.json +137 -4
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import * as fs4 from 'fs/promises';
2
2
  import * as path from 'path';
3
- import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, stripAnsi } from '@wrongstack/core';
3
+ import { dirname } from 'path';
4
4
  import { spawn } from 'child_process';
5
+ import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, stripAnsi } from '@wrongstack/core';
5
6
  import * as os from 'os';
6
7
  import * as dns from 'dns/promises';
7
8
  import * as fsSync from 'fs';
9
+ import { statSync } from 'fs';
8
10
 
9
11
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
12
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -41,6 +43,83 @@ function isBinaryBuffer(buf) {
41
43
  }
42
44
  return false;
43
45
  }
46
+ async function* spawnStream(opts) {
47
+ const max = opts.maxBytes ?? 2e5;
48
+ const flushAt = opts.flushBytes ?? 4 * 1024;
49
+ let stdout = "";
50
+ let stderr = "";
51
+ let pending = "";
52
+ let error;
53
+ const child = spawn(opts.cmd, opts.args, {
54
+ cwd: opts.cwd,
55
+ signal: opts.signal,
56
+ stdio: ["ignore", "pipe", "pipe"]
57
+ });
58
+ const queue = [];
59
+ let waiter;
60
+ const wake = () => {
61
+ if (waiter) {
62
+ const w = waiter;
63
+ waiter = void 0;
64
+ w();
65
+ }
66
+ };
67
+ child.stdout?.on("data", (c) => {
68
+ const s = c.toString();
69
+ if (stdout.length < max) stdout += s;
70
+ queue.push({ kind: "out", data: s });
71
+ wake();
72
+ });
73
+ child.stderr?.on("data", (c) => {
74
+ const s = c.toString();
75
+ if (stderr.length < max) stderr += s;
76
+ queue.push({ kind: "err", data: s });
77
+ wake();
78
+ });
79
+ child.on("error", (e) => {
80
+ error = e.message;
81
+ queue.push({ kind: "error", data: e.message });
82
+ wake();
83
+ });
84
+ child.on("close", (code) => {
85
+ queue.push({ kind: "close", data: "", code: code ?? 0 });
86
+ wake();
87
+ });
88
+ let exitCode = 0;
89
+ let spawnFailed = false;
90
+ for (; ; ) {
91
+ while (queue.length === 0) {
92
+ await new Promise((resolve2) => {
93
+ waiter = resolve2;
94
+ });
95
+ }
96
+ const chunk = queue.shift();
97
+ if (chunk.kind === "close") {
98
+ if (!spawnFailed) exitCode = chunk.code ?? 0;
99
+ break;
100
+ }
101
+ if (chunk.kind === "error") {
102
+ spawnFailed = true;
103
+ exitCode = 1;
104
+ continue;
105
+ }
106
+ pending += chunk.data;
107
+ if (pending.length >= flushAt) {
108
+ yield { type: "partial_output", text: pending };
109
+ pending = "";
110
+ }
111
+ }
112
+ if (pending.length > 0) {
113
+ yield { type: "partial_output", text: pending };
114
+ }
115
+ return {
116
+ stdout,
117
+ stderr,
118
+ exitCode,
119
+ truncated: stdout.length >= max || stderr.length >= max,
120
+ error
121
+ };
122
+ }
44
123
 
45
124
  // src/read.ts
46
125
  var MAX_BYTES = 5 * 1024 * 1024;
@@ -347,7 +426,7 @@ async function resolveFiles(filesInput, ctx, extraGlob) {
347
426
  return resolved;
348
427
  }
349
428
  async function globFiles(pattern, base, extraGlob) {
350
- const { spawn: spawn16 } = await import('child_process');
429
+ const { spawn: spawn11 } = await import('child_process');
351
430
  const rgAvailable = await checkRg();
352
431
  if (rgAvailable) {
353
432
  try {
@@ -511,6 +590,14 @@ var grepTool = {
511
590
  required: ["pattern"]
512
591
  },
513
592
  async execute(input, ctx, opts) {
593
+ let final;
594
+ for await (const ev of grepTool.executeStream(input, ctx, opts)) {
595
+ if (ev.type === "final") final = ev.output;
596
+ }
597
+ if (!final) throw new Error("grep: stream ended without final event");
598
+ return final;
599
+ },
600
+ async *executeStream(input, ctx, opts) {
514
601
  if (!input?.pattern) throw new Error("grep: pattern is required");
515
602
  const base = input.path ? safeResolve(input.path, ctx) : ctx.cwd;
516
603
  const mode = input.output_mode ?? "content";
@@ -518,11 +605,14 @@ var grepTool = {
518
605
  const rgAvailable = await detectRg(opts.signal);
519
606
  if (rgAvailable) {
520
607
  try {
521
- return await runRg(input, base, mode, limit, opts.signal);
608
+ yield* runRgStream(input, base, mode, limit, opts.signal);
609
+ return;
522
610
  } catch {
523
611
  }
524
612
  }
525
- return await runNative(input, base, mode, limit, opts.signal);
613
+ yield { type: "log", text: "Falling back to native grep\u2026" };
614
+ const out = await runNative(input, base, mode, limit, opts.signal);
615
+ yield { type: "final", output: out };
526
616
  }
527
617
  };
528
618
  async function detectRg(signal) {
@@ -536,35 +626,109 @@ async function detectRg(signal) {
536
626
  }
537
627
  });
538
628
  }
539
- function runRg(input, base, mode, limit, signal) {
540
- return new Promise((resolve2, reject) => {
541
- const args = ["--no-heading"];
542
- if (input.case_insensitive) args.push("-i");
543
- if (mode === "files_with_matches") args.push("-l");
544
- if (mode === "count") args.push("-c");
545
- if (mode === "content") {
546
- args.push("-n");
547
- if (input.context_lines) args.push("-C", String(input.context_lines));
548
- }
549
- if (input.glob) args.push("--glob", input.glob);
550
- args.push("--", input.pattern, base);
551
- const child = spawn("rg", args, { signal, stdio: ["ignore", "pipe", "pipe"] });
552
- let buf = "";
553
- child.stdout?.on("data", (chunk) => {
554
- buf += chunk.toString();
555
- });
556
- child.on("error", reject);
557
- child.on("close", () => {
558
- const lines = buf.split("\n").filter(Boolean);
559
- const sliced = lines.slice(0, limit);
560
- resolve2({
561
- matches: sliced,
562
- count: lines.length,
563
- truncated: lines.length > limit,
564
- used: "rg"
565
- });
566
- });
629
+ async function* runRgStream(input, base, mode, limit, signal) {
630
+ const args = ["--no-heading"];
631
+ if (input.case_insensitive) args.push("-i");
632
+ if (mode === "files_with_matches") args.push("-l");
633
+ if (mode === "count") args.push("-c");
634
+ if (mode === "content") {
635
+ args.push("-n");
636
+ if (input.context_lines) args.push("-C", String(input.context_lines));
637
+ }
638
+ if (input.glob) args.push("--glob", input.glob);
639
+ args.push("--", input.pattern, base);
640
+ const matches = [];
641
+ let buf = "";
642
+ let totalLines = 0;
643
+ let batchSinceFlush = 0;
644
+ const FLUSH_AT = 16;
645
+ const child = spawn("rg", args, { signal, stdio: ["ignore", "pipe", "pipe"] });
646
+ const queue = [];
647
+ let waiter;
648
+ const wake = () => {
649
+ if (waiter) {
650
+ const w = waiter;
651
+ waiter = void 0;
652
+ w();
653
+ }
654
+ };
655
+ child.stdout?.on("data", (c) => {
656
+ queue.push({ kind: "out", data: c.toString() });
657
+ wake();
567
658
  });
659
+ child.on("error", (e) => {
660
+ queue.push({ kind: "error", data: e.message });
661
+ wake();
662
+ });
663
+ child.on("close", () => {
664
+ queue.push({ kind: "close", data: "" });
665
+ wake();
666
+ });
667
+ let pendingBatch = [];
668
+ let errored = false;
669
+ for (; ; ) {
670
+ while (queue.length === 0) {
671
+ await new Promise((r) => {
672
+ waiter = r;
673
+ });
674
+ }
675
+ const c = queue.shift();
676
+ if (c.kind === "error") {
677
+ errored = true;
678
+ continue;
679
+ }
680
+ if (c.kind === "close") break;
681
+ buf += c.data;
682
+ const idx = buf.lastIndexOf("\n");
683
+ if (idx === -1) continue;
684
+ const ready = buf.slice(0, idx);
685
+ buf = buf.slice(idx + 1);
686
+ for (const line of ready.split("\n")) {
687
+ if (!line) continue;
688
+ totalLines++;
689
+ if (matches.length < limit) {
690
+ matches.push(line);
691
+ pendingBatch.push(line);
692
+ batchSinceFlush++;
693
+ }
694
+ }
695
+ if (batchSinceFlush >= FLUSH_AT) {
696
+ yield {
697
+ type: "partial_output",
698
+ text: pendingBatch.join("\n"),
699
+ data: { matches_so_far: matches.length }
700
+ };
701
+ pendingBatch = [];
702
+ batchSinceFlush = 0;
703
+ }
704
+ }
705
+ if (buf.trim()) {
706
+ for (const line of buf.split("\n")) {
707
+ if (!line) continue;
708
+ totalLines++;
709
+ if (matches.length < limit) {
710
+ matches.push(line);
711
+ pendingBatch.push(line);
712
+ }
713
+ }
714
+ }
715
+ if (pendingBatch.length > 0) {
716
+ yield {
717
+ type: "partial_output",
718
+ text: pendingBatch.join("\n"),
719
+ data: { matches_so_far: matches.length }
720
+ };
721
+ }
722
+ if (errored) throw new Error("rg: spawn error");
723
+ yield {
724
+ type: "final",
725
+ output: {
726
+ matches,
727
+ count: totalLines,
728
+ truncated: totalLines > limit,
729
+ used: "rg"
730
+ }
731
+ };
568
732
  }
569
733
  async function runNative(input, base, mode, limit, signal) {
570
734
  const flags = input.case_insensitive ? "i" : "";
@@ -635,6 +799,8 @@ async function runNative(input, base, mode, limit, signal) {
635
799
  }
636
800
  var MAX_OUTPUT = 32768;
637
801
  var DEFAULT_TIMEOUT = 3e4;
802
+ var STREAM_FLUSH_INTERVAL_MS = 200;
803
+ var STREAM_FLUSH_BYTES = 4 * 1024;
638
804
  var bashTool = {
639
805
  name: "bash",
640
806
  description: "Run a shell command. stdout and stderr are merged.",
@@ -643,6 +809,7 @@ var bashTool = {
643
809
  mutating: true,
644
810
  timeoutMs: 3e4,
645
811
  maxOutputBytes: MAX_OUTPUT,
812
+ estimatedDurationMs: 3e3,
646
813
  inputSchema: {
647
814
  type: "object",
648
815
  properties: {
@@ -653,6 +820,14 @@ var bashTool = {
653
820
  required: ["command"]
654
821
  },
655
822
  async execute(input, ctx, opts) {
823
+ let final;
824
+ for await (const ev of bashTool.executeStream(input, ctx, opts)) {
825
+ if (ev.type === "final") final = ev.output;
826
+ }
827
+ if (!final) throw new Error("bash: stream ended without final event");
828
+ return final;
829
+ },
830
+ async *executeStream(input, ctx, opts) {
656
831
  if (!input?.command) throw new Error("bash: command is required");
657
832
  const timeoutMs = Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT, 6e5);
658
833
  const isWin = os.platform() === "win32";
@@ -660,75 +835,127 @@ var bashTool = {
660
835
  const args = isWin ? ["/c", input.command] : ["-c", input.command];
661
836
  const env = { ...process.env };
662
837
  env["WRONGSTACK_SESSION_ID"] = ctx.session.id;
663
- return new Promise((resolve2, reject) => {
664
- let child;
665
- try {
666
- child = spawn(shell, args, {
667
- cwd: ctx.projectRoot,
668
- env,
669
- stdio: input.background ? "ignore" : ["ignore", "pipe", "pipe"],
670
- detached: input.background,
671
- signal: opts.signal
672
- });
673
- } catch (err) {
674
- return reject(err);
675
- }
676
- if (input.background) {
677
- const pid = child.pid;
678
- if (typeof pid === "number") child.unref();
679
- return resolve2({
838
+ const child = spawn(shell, args, {
839
+ cwd: ctx.projectRoot,
840
+ env,
841
+ stdio: input.background ? "ignore" : ["ignore", "pipe", "pipe"],
842
+ detached: input.background,
843
+ signal: opts.signal
844
+ });
845
+ if (input.background) {
846
+ const pid = child.pid;
847
+ if (typeof pid === "number") child.unref();
848
+ yield {
849
+ type: "final",
850
+ output: {
680
851
  output: `[background] pid=${pid ?? "unknown"}`,
681
852
  exit_code: null,
682
853
  timed_out: false,
683
854
  pid
684
- });
855
+ }
856
+ };
857
+ return;
858
+ }
859
+ let buf = "";
860
+ let pending = "";
861
+ let timedOut = false;
862
+ const timers = [];
863
+ const timer = setTimeout(() => {
864
+ timedOut = true;
865
+ if (isWin) {
866
+ try {
867
+ child.kill();
868
+ } catch {
869
+ }
870
+ } else {
871
+ try {
872
+ child.kill("SIGTERM");
873
+ const killTimer = setTimeout(() => {
874
+ try {
875
+ child.kill("SIGKILL");
876
+ } catch {
877
+ }
878
+ }, 2e3);
879
+ timers.push(killTimer);
880
+ } catch {
881
+ }
685
882
  }
686
- let buf = "";
687
- let timedOut = false;
688
- const timers = [];
689
- const timer = setTimeout(() => {
690
- timedOut = true;
691
- if (isWin) {
692
- try {
693
- child.kill();
694
- } catch {
695
- }
696
- } else {
697
- try {
698
- child.kill("SIGTERM");
699
- const killTimer = setTimeout(() => {
700
- try {
701
- child.kill("SIGKILL");
702
- } catch {
703
- }
704
- }, 2e3);
705
- timers.push(killTimer);
706
- } catch {
883
+ }, timeoutMs);
884
+ timers.push(timer);
885
+ timer.unref?.();
886
+ const queue = [];
887
+ let resolveNext = null;
888
+ const push = (c) => {
889
+ if (resolveNext) {
890
+ const r = resolveNext;
891
+ resolveNext = null;
892
+ r(c);
893
+ } else {
894
+ queue.push(c);
895
+ }
896
+ };
897
+ const next = () => new Promise((resolve2) => {
898
+ const c = queue.shift();
899
+ if (c) resolve2(c);
900
+ else resolveNext = resolve2;
901
+ });
902
+ let lastFlush = Date.now();
903
+ const flush = () => {
904
+ if (pending.length === 0) return null;
905
+ const text = pending;
906
+ pending = "";
907
+ lastFlush = Date.now();
908
+ return text;
909
+ };
910
+ child.stdout?.on("data", (chunk) => {
911
+ const text = chunk.toString();
912
+ buf += text;
913
+ pending += text;
914
+ push({ kind: "data", text });
915
+ });
916
+ child.stderr?.on("data", (chunk) => {
917
+ const text = chunk.toString();
918
+ buf += text;
919
+ pending += text;
920
+ push({ kind: "data", text });
921
+ });
922
+ child.on("error", (err) => {
923
+ for (const t of timers) clearTimeout(t);
924
+ push({ kind: "error", err });
925
+ });
926
+ child.on("close", (code) => {
927
+ for (const t of timers) clearTimeout(t);
928
+ push({ kind: "end", code });
929
+ });
930
+ try {
931
+ while (true) {
932
+ const c = await next();
933
+ if (c.kind === "error") throw c.err;
934
+ if (c.kind === "end") {
935
+ const remainder = flush();
936
+ if (remainder !== null) {
937
+ yield { type: "partial_output", text: remainder };
707
938
  }
939
+ const cleaned = stripAnsi(buf).replace(/\r\n?/g, "\n");
940
+ yield {
941
+ type: "final",
942
+ output: {
943
+ output: truncateMiddle(cleaned, MAX_OUTPUT),
944
+ exit_code: c.code,
945
+ timed_out: timedOut
946
+ }
947
+ };
948
+ return;
708
949
  }
709
- }, timeoutMs);
710
- timers.push(timer);
711
- timer.unref?.();
712
- child.stdout?.on("data", (chunk) => {
713
- buf += chunk.toString();
714
- });
715
- child.stderr?.on("data", (chunk) => {
716
- buf += chunk.toString();
717
- });
718
- child.on("error", (err) => {
719
- for (const t of timers) clearTimeout(t);
720
- reject(err);
721
- });
722
- child.on("close", (code) => {
723
- for (const t of timers) clearTimeout(t);
724
- const cleaned = stripAnsi(buf).replace(/\r\n?/g, "\n");
725
- resolve2({
726
- output: truncateMiddle(cleaned, MAX_OUTPUT),
727
- exit_code: code,
728
- timed_out: timedOut
729
- });
730
- });
731
- });
950
+ const now = Date.now();
951
+ if (pending.length >= STREAM_FLUSH_BYTES || now - lastFlush >= STREAM_FLUSH_INTERVAL_MS) {
952
+ const text = flush();
953
+ if (text) yield { type: "partial_output", text };
954
+ }
955
+ }
956
+ } finally {
957
+ for (const t of timers) clearTimeout(t);
958
+ }
732
959
  }
733
960
  };
734
961
  var ALLOWED_COMMANDS = {
@@ -940,7 +1167,15 @@ var fetchTool = {
940
1167
  },
941
1168
  required: ["url"]
942
1169
  },
943
- async execute(input, _ctx, opts) {
1170
+ async execute(input, ctx, opts) {
1171
+ let final;
1172
+ for await (const ev of fetchTool.executeStream(input, ctx, opts)) {
1173
+ if (ev.type === "final") final = ev.output;
1174
+ }
1175
+ if (!final) throw new Error("fetch: stream ended without final event");
1176
+ return final;
1177
+ },
1178
+ async *executeStream(input, _ctx, opts) {
944
1179
  if (!input?.url) throw new Error("fetch: url is required");
945
1180
  const u = new URL(input.url);
946
1181
  if (u.protocol !== "https:" && u.protocol !== "http:") {
@@ -950,6 +1185,7 @@ var fetchTool = {
950
1185
  throw new Error("fetch: http:// blocked (HTTPS required by default)");
951
1186
  }
952
1187
  await assertNotPrivate(u.hostname);
1188
+ yield { type: "log", text: `GET ${input.url}` };
953
1189
  const ctrl = new AbortController();
954
1190
  const timer = setTimeout(() => ctrl.abort(new Error("fetch timeout")), TIMEOUT_MS2);
955
1191
  const combined = combineSignals(opts.signal, ctrl.signal);
@@ -959,16 +1195,29 @@ var fetchTool = {
959
1195
  if (/^image\/|^audio\/|^video\/|application\/octet-stream/.test(ct)) {
960
1196
  throw new Error(`fetch: refusing to read binary content-type "${ct}"`);
961
1197
  }
1198
+ yield { type: "log", text: `HTTP ${res.status} ${ct}`, data: { status: res.status, contentType: ct } };
962
1199
  const reader = res.body?.getReader();
963
1200
  let received = 0;
964
1201
  const chunks = [];
1202
+ let pendingBytes = 0;
1203
+ const FLUSH_AT = 4 * 1024;
965
1204
  if (reader) {
966
1205
  for (; ; ) {
967
1206
  const { value, done } = await reader.read();
968
1207
  if (done) break;
969
1208
  if (!value) continue;
970
1209
  received += value.byteLength;
1210
+ pendingBytes += value.byteLength;
971
1211
  chunks.push(value);
1212
+ if (pendingBytes >= FLUSH_AT) {
1213
+ const recent = Buffer.from(value).toString("utf8");
1214
+ yield {
1215
+ type: "partial_output",
1216
+ text: recent,
1217
+ data: { received }
1218
+ };
1219
+ pendingBytes = 0;
1220
+ }
972
1221
  if (received > MAX_BYTES2) break;
973
1222
  }
974
1223
  }
@@ -979,11 +1228,14 @@ var fetchTool = {
979
1228
  else if (format === "markdown" && ct.includes("text/html")) content = htmlToMarkdown(text);
980
1229
  else if (ct.includes("application/json")) content = prettyJson(text);
981
1230
  else content = text;
982
- return {
983
- content: truncateMiddle(content, MAX_BYTES2),
984
- status: res.status,
985
- content_type: ct,
986
- url: res.url
1231
+ yield {
1232
+ type: "final",
1233
+ output: {
1234
+ content: truncateMiddle(content, MAX_BYTES2),
1235
+ status: res.status,
1236
+ content_type: ct,
1237
+ url: res.url
1238
+ }
987
1239
  };
988
1240
  } finally {
989
1241
  clearTimeout(timer);
@@ -1083,20 +1335,39 @@ var searchTool = {
1083
1335
  },
1084
1336
  required: ["query"]
1085
1337
  },
1086
- async execute(input, _ctx, opts) {
1338
+ async execute(input, ctx, opts) {
1339
+ let final;
1340
+ for await (const ev of searchTool.executeStream(input, ctx, opts)) {
1341
+ if (ev.type === "final") final = ev.output;
1342
+ }
1343
+ if (!final) throw new Error("search: stream ended without final event");
1344
+ return final;
1345
+ },
1346
+ async *executeStream(input, _ctx, opts) {
1087
1347
  if (!input?.query) throw new Error("search: query is required");
1088
1348
  const num = Math.max(1, Math.min(input.num_results ?? DEFAULT_NUM, MAX_RESULTS));
1089
1349
  const source = input.source ?? "duckduckgo";
1350
+ yield { type: "log", text: `Querying ${source} for "${input.query}"\u2026`, data: { source, query: input.query } };
1351
+ let output;
1090
1352
  switch (source) {
1091
1353
  case "duckduckgo":
1092
- return await duckduckgoSearch(input.query, num, opts.signal);
1354
+ output = await duckduckgoSearch(input.query, num, opts.signal);
1355
+ break;
1093
1356
  case "google":
1094
- return await googleSearch(input.query, num, opts.signal);
1357
+ output = await googleSearch(input.query, num, opts.signal);
1358
+ break;
1095
1359
  case "bing":
1096
- return await bingSearch(input.query, num, opts.signal);
1360
+ output = await bingSearch(input.query, num, opts.signal);
1361
+ break;
1097
1362
  default:
1098
1363
  throw new Error(`search: unknown source "${source}"`);
1099
1364
  }
1365
+ yield {
1366
+ type: "partial_output",
1367
+ text: `${output.results.length} results from ${output.source}`,
1368
+ data: { count: output.results.length }
1369
+ };
1370
+ yield { type: "final", output };
1100
1371
  }
1101
1372
  };
1102
1373
  async function duckduckgoSearch(query2, num, signal) {
@@ -1110,21 +1381,26 @@ async function duckduckgoSearch(query2, num, signal) {
1110
1381
  truncated: results.length >= num
1111
1382
  };
1112
1383
  }
1384
+ function takeFrom(iter, max) {
1385
+ const out = [];
1386
+ for (const item of iter) {
1387
+ if (out.length >= max) break;
1388
+ out.push(item);
1389
+ }
1390
+ return out;
1391
+ }
1113
1392
  function parseDuckDuckGo(html, num) {
1114
1393
  const results = [];
1115
1394
  const snippetRegex = /<a class="result-link"[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>/gi;
1116
1395
  const snippet2Regex = /<a class="result-snippet"[^>]*>([^<]+)<\/a>/gi;
1117
- let match;
1118
- const linkMatches = [];
1119
- while ((match = snippetRegex.exec(html)) !== null && linkMatches.length < num) {
1120
- if (!match[1] || !match[2]) continue;
1121
- linkMatches.push({ url: match[1], title: stripTags2(match[2]) });
1122
- }
1123
- const snippetMatches = [];
1124
- while ((match = snippet2Regex.exec(html)) !== null && snippetMatches.length < num) {
1125
- if (!match[1]) continue;
1126
- snippetMatches.push(stripTags2(match[1]));
1127
- }
1396
+ const linkMatches = takeFrom(
1397
+ [...html.matchAll(snippetRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: m[1], title: stripTags2(m[2]) })),
1398
+ num
1399
+ );
1400
+ const snippetMatches = takeFrom(
1401
+ [...html.matchAll(snippet2Regex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
1402
+ num
1403
+ );
1128
1404
  for (let i = 0; i < linkMatches.length && i < num; i++) {
1129
1405
  const entry = linkMatches[i];
1130
1406
  results.push({
@@ -1152,23 +1428,18 @@ function parseGoogleResults(html, num) {
1152
1428
  const titleRegex = /<h3[^>]*class="[^"]*DKV84"[^>]*>([^<]+)<\/h3>/gi;
1153
1429
  const urlRegex = /<cite[^>]*>([^<]+)<\/cite>/gi;
1154
1430
  const snippetRegex = /<span[^>]*class="[^"]*aXCZ0b[^>]*>([^<]+)<\/span>/gi;
1155
- const titles = [];
1156
- let m;
1157
- while ((m = titleRegex.exec(html)) !== null && titles.length < num) {
1158
- if (!m[1]) continue;
1159
- titles.push(stripTags2(m[1]));
1160
- }
1161
- const urls = [];
1162
- while ((m = urlRegex.exec(html)) !== null && urls.length < num) {
1163
- if (!m[1]) continue;
1164
- const url = stripTags2(m[1]).replace(/^\*(https?:\/\/[^\s]+).*$/, "$1");
1165
- if (url.startsWith("http")) urls.push(url);
1166
- }
1167
- const snippets = [];
1168
- while ((m = snippetRegex.exec(html)) !== null && snippets.length < num) {
1169
- if (!m[1]) continue;
1170
- snippets.push(stripTags2(m[1]));
1171
- }
1431
+ const titles = takeFrom(
1432
+ [...html.matchAll(titleRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
1433
+ num
1434
+ );
1435
+ const urls = takeFrom(
1436
+ [...html.matchAll(urlRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1]).replace(/^\*(https?:\/\/[^\s]+).*$/, "$1")).filter((u) => u.startsWith("http")),
1437
+ num
1438
+ );
1439
+ const snippets = takeFrom(
1440
+ [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
1441
+ num
1442
+ );
1172
1443
  for (let i = 0; i < Math.min(titles.length, num); i++) {
1173
1444
  results.push({
1174
1445
  title: titles[i] ?? "",
@@ -1194,17 +1465,14 @@ function parseBingResults(html, num) {
1194
1465
  const results = [];
1195
1466
  const titleRegex = /<h2[^>]*>\s*<a[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>\s*<\/h2>/gi;
1196
1467
  const snippetRegex = /<p[^>]*class="[^"]*b_paractl[^"]*"[^>]*>([^<]+)<\/p>/gi;
1197
- const entries = [];
1198
- let m;
1199
- while ((m = titleRegex.exec(html)) !== null && entries.length < num) {
1200
- if (!m[1] || !m[2]) continue;
1201
- entries.push({ url: m[1], title: stripTags2(m[2]) });
1202
- }
1203
- const snippets = [];
1204
- while ((m = snippetRegex.exec(html)) !== null && snippets.length < num) {
1205
- if (!m[1]) continue;
1206
- snippets.push(stripTags2(m[1]));
1207
- }
1468
+ const entries = takeFrom(
1469
+ [...html.matchAll(titleRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: m[1], title: stripTags2(m[2]) })),
1470
+ num
1471
+ );
1472
+ const snippets = takeFrom(
1473
+ [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
1474
+ num
1475
+ );
1208
1476
  for (let i = 0; i < entries.length; i++) {
1209
1477
  results.push({
1210
1478
  title: entries[i]?.title ?? "",
@@ -1365,11 +1633,11 @@ function findGitDir(cwd) {
1365
1633
  let dir = cwd;
1366
1634
  for (let i = 0; i < 20; i++) {
1367
1635
  try {
1368
- const stat9 = __require("fs").statSync(`${dir}/.git`);
1636
+ const stat9 = statSync(`${dir}/.git`);
1369
1637
  if (stat9.isDirectory()) return dir;
1370
1638
  } catch {
1371
1639
  }
1372
- const parent = __require("path").dirname(dir);
1640
+ const parent = dirname(dir);
1373
1641
  if (parent === dir) break;
1374
1642
  dir = parent;
1375
1643
  }
@@ -1531,8 +1799,7 @@ function runPatch(args, cwd, signal) {
1531
1799
  function extractPatchedFiles(output) {
1532
1800
  const files = [];
1533
1801
  const re = /patching file (.+)/gi;
1534
- let m;
1535
- while ((m = re.exec(output)) !== null) {
1802
+ for (const m of output.matchAll(re)) {
1536
1803
  if (m[1]) files.push(m[1]);
1537
1804
  }
1538
1805
  return files;
@@ -1803,7 +2070,15 @@ var treeTool = {
1803
2070
  }
1804
2071
  }
1805
2072
  },
1806
- async execute(input, ctx) {
2073
+ async execute(input, ctx, opts) {
2074
+ let final;
2075
+ for await (const ev of treeTool.executeStream(input, ctx, opts)) {
2076
+ if (ev.type === "final") final = ev.output;
2077
+ }
2078
+ if (!final) throw new Error("tree: stream ended without final event");
2079
+ return final;
2080
+ },
2081
+ async *executeStream(input, ctx) {
1807
2082
  const basePath = input.path ? safeResolve(input.path, ctx) : ctx.cwd;
1808
2083
  const maxDepth = input.depth ?? 3;
1809
2084
  const showFiles = input.show_files ?? true;
@@ -1812,9 +2087,22 @@ var treeTool = {
1812
2087
  const exclude = /* @__PURE__ */ new Set([...DEFAULT_IGNORE4, ...input.exclude ?? []]);
1813
2088
  const filterGlob = input.glob;
1814
2089
  const lines = [basePath];
1815
- let totalFiles = 0;
1816
- let totalDirs = 0;
1817
- await walkDir(basePath, "", 0, {
2090
+ const totals = { totalFiles: { value: 0 }, totalDirs: { value: 0 } };
2091
+ const queue = [];
2092
+ const FLUSH_EVERY = 200;
2093
+ let lastEmittedTotal = 0;
2094
+ const tickProgress = () => {
2095
+ const seen = totals.totalFiles.value + totals.totalDirs.value;
2096
+ if (seen - lastEmittedTotal >= FLUSH_EVERY) {
2097
+ queue.push({
2098
+ type: "metric",
2099
+ text: `${seen} entries`,
2100
+ data: { files: totals.totalFiles.value, dirs: totals.totalDirs.value }
2101
+ });
2102
+ lastEmittedTotal = seen;
2103
+ }
2104
+ };
2105
+ const walkPromise = walkDir(basePath, 0, {
1818
2106
  maxDepth,
1819
2107
  exclude,
1820
2108
  showFiles,
@@ -1824,19 +2112,38 @@ var treeTool = {
1824
2112
  lines,
1825
2113
  prefix: "",
1826
2114
  isLast: true,
1827
- totalFiles: { value: 0 },
1828
- totalDirs: { value: 0 }
2115
+ totalFiles: totals.totalFiles,
2116
+ totalDirs: totals.totalDirs,
2117
+ onProgress: tickProgress
1829
2118
  });
1830
- return {
1831
- tree: lines.join("\n"),
1832
- total_files: totalFiles,
1833
- total_dirs: totalDirs,
1834
- truncated: false,
1835
- path: basePath
2119
+ let walkDone = false;
2120
+ walkPromise.finally(() => {
2121
+ walkDone = true;
2122
+ });
2123
+ while (!walkDone || queue.length > 0) {
2124
+ if (queue.length > 0) {
2125
+ yield queue.shift();
2126
+ } else {
2127
+ await Promise.race([
2128
+ walkPromise,
2129
+ new Promise((r) => setTimeout(r, 50))
2130
+ ]).catch(() => void 0);
2131
+ }
2132
+ }
2133
+ await walkPromise;
2134
+ yield {
2135
+ type: "final",
2136
+ output: {
2137
+ tree: lines.join("\n"),
2138
+ total_files: totals.totalFiles.value,
2139
+ total_dirs: totals.totalDirs.value,
2140
+ truncated: false,
2141
+ path: basePath
2142
+ }
1836
2143
  };
1837
2144
  }
1838
2145
  };
1839
- async function walkDir(dir, name, depth, opts) {
2146
+ async function walkDir(dir, depth, opts) {
1840
2147
  const entries = await fs4.readdir(dir, { withFileTypes: true }).catch(() => []);
1841
2148
  const filtered = entries.filter((e) => {
1842
2149
  if (!opts.showHidden && e.name.startsWith(".")) return false;
@@ -1848,6 +2155,7 @@ async function walkDir(dir, name, depth, opts) {
1848
2155
  const fileCount = filtered.filter((e) => e.isFile()).length;
1849
2156
  opts.totalDirs.value += dirCount;
1850
2157
  opts.totalFiles.value += fileCount;
2158
+ opts.onProgress?.();
1851
2159
  }
1852
2160
  const items = filtered.sort((a, b) => {
1853
2161
  if (a.isDirectory() && !b.isDirectory()) return -1;
@@ -1866,7 +2174,7 @@ async function walkDir(dir, name, depth, opts) {
1866
2174
  opts.lines.push(opts.prefix + branch + displayName);
1867
2175
  if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
1868
2176
  const childPrefix = opts.prefix + connector;
1869
- await walkDir(path.join(dir, entry.name), entry.name, depth + 1, {
2177
+ await walkDir(path.join(dir, entry.name), depth + 1, {
1870
2178
  ...opts,
1871
2179
  prefix: childPrefix,
1872
2180
  isLast
@@ -1874,6 +2182,8 @@ async function walkDir(dir, name, depth, opts) {
1874
2182
  }
1875
2183
  }
1876
2184
  }
2185
+
2186
+ // src/lint.ts
1877
2187
  var lintTool = {
1878
2188
  name: "lint",
1879
2189
  description: "Run a linter on files. Auto-detects biome, eslint, or tslint. Use `fix` to auto-fix issues.",
@@ -1898,25 +2208,59 @@ var lintTool = {
1898
2208
  }
1899
2209
  },
1900
2210
  async execute(input, ctx, opts) {
2211
+ let final;
2212
+ for await (const ev of lintTool.executeStream(input, ctx, opts)) {
2213
+ if (ev.type === "final") final = ev.output;
2214
+ }
2215
+ if (!final) throw new Error("lint: stream ended without final event");
2216
+ return final;
2217
+ },
2218
+ async *executeStream(input, ctx, opts) {
1901
2219
  const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
1902
2220
  const linter = input.linter ?? "auto";
1903
2221
  const detected = linter === "auto" ? await detectLinter(cwd) : linter;
1904
2222
  if (!detected) {
1905
- return {
1906
- linter: "none",
1907
- files_checked: 0,
1908
- errors: 0,
1909
- warnings: 0,
1910
- output: "No linter found (biome.json, .eslintrc, tslint.json)",
1911
- fix_applied: false,
1912
- truncated: false
2223
+ yield {
2224
+ type: "final",
2225
+ output: {
2226
+ linter: "none",
2227
+ files_checked: 0,
2228
+ errors: 0,
2229
+ warnings: 0,
2230
+ output: "No linter found (biome.json, .eslintrc, tslint.json)",
2231
+ fix_applied: false,
2232
+ truncated: false
2233
+ }
1913
2234
  };
2235
+ return;
1914
2236
  }
1915
- return await runLinter(detected, input, cwd, opts.signal);
2237
+ yield { type: "log", text: `Running ${detected}\u2026`, data: { linter: detected } };
2238
+ const args = ["lint"];
2239
+ if (input.fix) args.push("--write");
2240
+ if (input.files) {
2241
+ const files = Array.isArray(input.files) ? input.files : input.files.split(",");
2242
+ args.push("--", ...files.map((f) => f.trim()));
2243
+ }
2244
+ const cmd = detected === "biome" ? "biome" : detected;
2245
+ const result = yield* spawnStream({ cmd, args, cwd, signal: opts.signal, maxBytes: 1e5 });
2246
+ const errors = (result.stdout.match(/error/g) || []).length;
2247
+ const warnings = (result.stdout.match(/warning/g) || []).length;
2248
+ yield {
2249
+ type: "final",
2250
+ output: {
2251
+ linter: detected,
2252
+ files_checked: input.files ? Array.isArray(input.files) ? input.files.length : input.files.split(",").length : 0,
2253
+ errors,
2254
+ warnings,
2255
+ output: result.stdout,
2256
+ fix_applied: input.fix ?? false,
2257
+ truncated: result.truncated
2258
+ }
2259
+ };
1916
2260
  }
1917
2261
  };
1918
2262
  async function detectLinter(cwd) {
1919
- const { stat: stat9 } = __require("fs/promises");
2263
+ const { stat: stat9 } = await import('fs/promises');
1920
2264
  const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
1921
2265
  for (const f of checks) {
1922
2266
  try {
@@ -1929,42 +2273,8 @@ async function detectLinter(cwd) {
1929
2273
  }
1930
2274
  return "biome";
1931
2275
  }
1932
- async function runLinter(linter, input, cwd, signal) {
1933
- const args = ["lint"];
1934
- if (input.fix) args.push("--write");
1935
- if (input.files) {
1936
- const files = Array.isArray(input.files) ? input.files : input.files.split(",");
1937
- args.push("--", ...files.map((f) => f.trim()));
1938
- }
1939
- const result = await runCommand2(linter === "biome" ? "biome" : linter, args, cwd, signal);
1940
- const errors = (result.stdout.match(/error/g) || []).length;
1941
- const warnings = (result.stdout.match(/warning/g) || []).length;
1942
- return {
1943
- linter,
1944
- files_checked: input.files ? Array.isArray(input.files) ? input.files.length : input.files.split(",").length : 0,
1945
- errors,
1946
- warnings,
1947
- output: result.stdout,
1948
- fix_applied: input.fix ?? false,
1949
- truncated: result.truncated
1950
- };
1951
- }
1952
- function runCommand2(cmd, args, cwd, signal) {
1953
- return new Promise((resolve2) => {
1954
- let stdout = "";
1955
- let stderr = "";
1956
- const MAX = 1e5;
1957
- const child = spawn(cmd, args, { cwd, signal, stdio: ["ignore", "pipe", "pipe"] });
1958
- child.stdout?.on("data", (c) => {
1959
- if (stdout.length < MAX) stdout += c.toString();
1960
- });
1961
- child.stderr?.on("data", (c) => {
1962
- if (stderr.length < MAX) stderr += c.toString();
1963
- });
1964
- child.on("close", (code) => resolve2({ stdout, stderr, exitCode: code ?? 0, truncated: stdout.length >= MAX }));
1965
- child.on("error", (e) => resolve2({ stdout: "", stderr: e.message, exitCode: 1, truncated: false }));
1966
- });
1967
- }
2276
+
2277
+ // src/format.ts
1968
2278
  var formatTool = {
1969
2279
  name: "format",
1970
2280
  description: "Format files with biome or prettier. Use `check` to verify without modifying.",
@@ -1992,23 +2302,59 @@ var formatTool = {
1992
2302
  }
1993
2303
  },
1994
2304
  async execute(input, ctx, opts) {
2305
+ let final;
2306
+ for await (const ev of formatTool.executeStream(input, ctx, opts)) {
2307
+ if (ev.type === "final") final = ev.output;
2308
+ }
2309
+ if (!final) throw new Error("format: stream ended without final event");
2310
+ return final;
2311
+ },
2312
+ async *executeStream(input, ctx, opts) {
1995
2313
  const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
1996
2314
  const fixer = input.fixer ?? "auto";
1997
2315
  const detected = fixer === "auto" ? await detectFixer(cwd) : fixer;
1998
2316
  if (!detected) {
1999
- return {
2000
- fixer: "none",
2001
- files_checked: 0,
2002
- files_changed: 0,
2003
- output: "No formatter found (biome.json, .prettierrc)",
2004
- truncated: false
2317
+ yield {
2318
+ type: "final",
2319
+ output: {
2320
+ fixer: "none",
2321
+ files_checked: 0,
2322
+ files_changed: 0,
2323
+ output: "No formatter found (biome.json, .prettierrc)",
2324
+ truncated: false
2325
+ }
2005
2326
  };
2327
+ return;
2328
+ }
2329
+ yield { type: "log", text: `Running ${detected}\u2026`, data: { fixer: detected, check: !!input.check } };
2330
+ const args = ["format", "--write"];
2331
+ if (input.check) args[args.length - 1] = "--check";
2332
+ if (input.files) {
2333
+ const files = Array.isArray(input.files) ? input.files : input.files.split(",");
2334
+ args.push("--", ...files.map((f) => f.trim()));
2006
2335
  }
2007
- return await runFormatter(detected, input, cwd, opts.signal);
2336
+ const result = yield* spawnStream({
2337
+ cmd: detected,
2338
+ args,
2339
+ cwd,
2340
+ signal: opts.signal,
2341
+ maxBytes: 1e5
2342
+ });
2343
+ const changed = (result.stdout.match(/changed/g) || []).length;
2344
+ yield {
2345
+ type: "final",
2346
+ output: {
2347
+ fixer: detected,
2348
+ files_checked: 0,
2349
+ files_changed: changed,
2350
+ output: result.stdout || result.stderr || result.error || "",
2351
+ truncated: result.truncated
2352
+ }
2353
+ };
2008
2354
  }
2009
2355
  };
2010
2356
  async function detectFixer(cwd) {
2011
- const { stat: stat9 } = __require("fs/promises");
2357
+ const { stat: stat9 } = await import('fs/promises');
2012
2358
  try {
2013
2359
  await stat9(`${cwd}/biome.json`);
2014
2360
  return "biome";
@@ -2021,46 +2367,6 @@ async function detectFixer(cwd) {
2021
2367
  }
2022
2368
  }
2023
2369
  }
2024
- async function runFormatter(fixer, input, cwd, signal) {
2025
- const args = ["format", "--write"];
2026
- if (input.check) args[args.length - 1] = "--check";
2027
- if (input.files) {
2028
- const files = Array.isArray(input.files) ? input.files : input.files.split(",");
2029
- args.push("--", ...files.map((f) => f.trim()));
2030
- }
2031
- return runCommand3(fixer, args, cwd, signal);
2032
- }
2033
- function runCommand3(cmd, args, cwd, signal) {
2034
- return new Promise((resolve2) => {
2035
- let stdout = "";
2036
- let stderr = "";
2037
- const MAX = 1e5;
2038
- const child = spawn(cmd, args, { cwd, signal, stdio: ["ignore", "pipe", "pipe"] });
2039
- child.stdout?.on("data", (c) => {
2040
- if (stdout.length < MAX) stdout += c.toString();
2041
- });
2042
- child.stderr?.on("data", (c) => {
2043
- if (stderr.length < MAX) stderr += c.toString();
2044
- });
2045
- child.on("close", (code) => {
2046
- const changed = (stdout.match(/changed/g) || []).length;
2047
- resolve2({
2048
- fixer: cmd,
2049
- files_checked: 0,
2050
- files_changed: changed,
2051
- output: stdout || stderr,
2052
- truncated: stdout.length >= MAX
2053
- });
2054
- });
2055
- child.on("error", (e) => resolve2({
2056
- fixer: cmd,
2057
- files_checked: 0,
2058
- files_changed: 0,
2059
- output: e.message,
2060
- truncated: false
2061
- }));
2062
- });
2063
- }
2064
2370
  var typecheckTool = {
2065
2371
  name: "typecheck",
2066
2372
  description: "Run TypeScript type checking with `tsc --noEmit`. Checks for type errors without compiling.",
@@ -2084,19 +2390,52 @@ var typecheckTool = {
2084
2390
  }
2085
2391
  },
2086
2392
  async execute(input, ctx, opts) {
2393
+ let final;
2394
+ for await (const ev of typecheckTool.executeStream(input, ctx, opts)) {
2395
+ if (ev.type === "final") final = ev.output;
2396
+ }
2397
+ if (!final) throw new Error("typecheck: stream ended without final event");
2398
+ return final;
2399
+ },
2400
+ async *executeStream(input, ctx, opts) {
2087
2401
  const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
2402
+ let args;
2403
+ let project;
2088
2404
  if (input.all) {
2089
- return runTsc(["--noEmit"], cwd, opts.signal, "workspace");
2090
- }
2091
- const tsconfig = input.project ? safeResolve(input.project, ctx) : await findTsConfig(cwd);
2092
- const args = ["--noEmit"];
2093
- if (input.strict) args.push("--strict");
2094
- if (tsconfig) args.push("--project", tsconfig);
2095
- return runTsc(args, cwd, opts.signal, tsconfig ?? "default");
2405
+ args = ["--noEmit"];
2406
+ project = "workspace";
2407
+ } else {
2408
+ const tsconfig = input.project ? safeResolve(input.project, ctx) : await findTsConfig(cwd);
2409
+ args = ["--noEmit"];
2410
+ if (input.strict) args.push("--strict");
2411
+ if (tsconfig) args.push("--project", tsconfig);
2412
+ project = tsconfig ?? "default";
2413
+ }
2414
+ yield { type: "log", text: `tsc ${args.join(" ")}`, data: { project } };
2415
+ const result = yield* spawnStream({
2416
+ cmd: "npx",
2417
+ args: ["tsc", ...args],
2418
+ cwd,
2419
+ signal: opts.signal,
2420
+ maxBytes: 2e5
2421
+ });
2422
+ const errors = (result.stdout.match(/error TS/g) || []).length;
2423
+ const warnings = (result.stdout.match(/warning/g) || []).length;
2424
+ yield {
2425
+ type: "final",
2426
+ output: {
2427
+ project,
2428
+ exit_code: result.exitCode,
2429
+ errors,
2430
+ warnings,
2431
+ output: result.stdout || result.stderr || result.error || "",
2432
+ truncated: result.truncated
2433
+ }
2434
+ };
2096
2435
  }
2097
2436
  };
2098
2437
  async function findTsConfig(cwd) {
2099
- const { stat: stat9 } = __require("fs/promises");
2438
+ const { stat: stat9 } = await import('fs/promises');
2100
2439
  const candidates = ["tsconfig.json", "tsconfig.base.json"];
2101
2440
  for (const f of candidates) {
2102
2441
  try {
@@ -2107,39 +2446,6 @@ async function findTsConfig(cwd) {
2107
2446
  }
2108
2447
  return null;
2109
2448
  }
2110
- function runTsc(args, cwd, signal, project) {
2111
- return new Promise((resolve2) => {
2112
- let stdout = "";
2113
- let stderr = "";
2114
- const MAX = 2e5;
2115
- const child = spawn("npx", ["tsc", ...args], { cwd, signal, stdio: ["ignore", "pipe", "pipe"] });
2116
- child.stdout?.on("data", (c) => {
2117
- if (stdout.length < MAX) stdout += c.toString();
2118
- });
2119
- child.stderr?.on("data", (c) => {
2120
- if (stderr.length < MAX) stderr += c.toString();
2121
- });
2122
- child.on("close", (code) => {
2123
- const errors = (stdout.match(/error TS/g) || []).length;
2124
- resolve2({
2125
- project,
2126
- exit_code: code ?? 0,
2127
- errors,
2128
- warnings: (stdout.match(/warning/g) || []).length,
2129
- output: stdout || stderr,
2130
- truncated: stdout.length >= MAX
2131
- });
2132
- });
2133
- child.on("error", (e) => resolve2({
2134
- project,
2135
- exit_code: 1,
2136
- errors: 0,
2137
- warnings: 0,
2138
- output: e.message,
2139
- truncated: false
2140
- }));
2141
- });
2142
- }
2143
2449
  var testTool = {
2144
2450
  name: "test",
2145
2451
  description: "Run tests with vitest, jest, or mocha. Returns pass/fail counts and output.",
@@ -2159,46 +2465,57 @@ var testTool = {
2159
2465
  enum: ["vitest", "jest", "mocha", "auto"],
2160
2466
  description: "Test runner (default: auto-detect)"
2161
2467
  },
2162
- watch: {
2163
- type: "boolean",
2164
- description: "Run in watch mode (default: false)"
2165
- },
2166
- coverage: {
2167
- type: "boolean",
2168
- description: "Generate coverage report (default: false)"
2169
- },
2468
+ watch: { type: "boolean", description: "Run in watch mode (default: false)" },
2469
+ coverage: { type: "boolean", description: "Generate coverage report (default: false)" },
2170
2470
  cwd: { type: "string", description: "Working directory (default: cwd)" },
2171
- grep: {
2172
- type: "string",
2173
- description: "Filter tests by name pattern (default: none)"
2174
- },
2175
- timeout: {
2176
- type: "integer",
2177
- description: "Test timeout in ms (default: 30000)"
2178
- }
2471
+ grep: { type: "string", description: "Filter tests by name pattern (default: none)" },
2472
+ timeout: { type: "integer", description: "Test timeout in ms (default: 30000)" }
2179
2473
  }
2180
2474
  },
2181
2475
  async execute(input, ctx, opts) {
2476
+ let final;
2477
+ for await (const ev of testTool.executeStream(input, ctx, opts)) {
2478
+ if (ev.type === "final") final = ev.output;
2479
+ }
2480
+ if (!final) throw new Error("test: stream ended without final event");
2481
+ return final;
2482
+ },
2483
+ async *executeStream(input, ctx, opts) {
2182
2484
  const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
2183
2485
  const runner = input.runner ?? "auto";
2184
2486
  const detected = runner === "auto" ? await detectRunner(cwd) : runner;
2185
2487
  if (!detected) {
2186
- return {
2187
- runner: "none",
2188
- exit_code: 1,
2189
- tests_run: 0,
2190
- passed: 0,
2191
- failed: 0,
2192
- duration_ms: 0,
2193
- output: "No test runner found (vitest.config.ts, jest.config.js, .mocharc.json)",
2194
- truncated: false
2488
+ yield {
2489
+ type: "final",
2490
+ output: {
2491
+ runner: "none",
2492
+ exit_code: 1,
2493
+ tests_run: 0,
2494
+ passed: 0,
2495
+ failed: 0,
2496
+ duration_ms: 0,
2497
+ output: "No test runner found (vitest.config.ts, jest.config.js, .mocharc.json)",
2498
+ truncated: false
2499
+ }
2195
2500
  };
2501
+ return;
2196
2502
  }
2197
- return await runTests(detected, input, cwd, opts.signal);
2503
+ yield { type: "log", text: `Running ${detected}\u2026`, data: { runner: detected } };
2504
+ const start = Date.now();
2505
+ const args = buildArgs2(detected, input);
2506
+ const result = yield* spawnStream({
2507
+ cmd: detected,
2508
+ args,
2509
+ cwd,
2510
+ signal: opts.signal,
2511
+ maxBytes: 2e5
2512
+ });
2513
+ const duration = Date.now() - start;
2514
+ yield { type: "final", output: parseResult(detected, result, duration) };
2198
2515
  }
2199
2516
  };
2200
2517
  async function detectRunner(cwd) {
2201
- const { stat: stat9 } = __require("fs/promises");
2518
+ const { stat: stat9 } = await import('fs/promises');
2202
2519
  const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
2203
2520
  for (const f of candidates) {
2204
2521
  try {
@@ -2211,13 +2528,6 @@ async function detectRunner(cwd) {
2211
2528
  }
2212
2529
  return "vitest";
2213
2530
  }
2214
- async function runTests(runner, input, cwd, signal) {
2215
- const start = Date.now();
2216
- const args = buildArgs2(runner, input);
2217
- const result = await runCommand4(runner, args, cwd, signal);
2218
- const duration = Date.now() - start;
2219
- return parseResult(runner, result, duration);
2220
- }
2221
2531
  function buildArgs2(runner, input) {
2222
2532
  const args = [];
2223
2533
  const timeout = input.timeout ?? 3e4;
@@ -2251,22 +2561,6 @@ function buildArgs2(runner, input) {
2251
2561
  }
2252
2562
  return args;
2253
2563
  }
2254
- function runCommand4(cmd, args, cwd, signal) {
2255
- return new Promise((resolve2) => {
2256
- let stdout = "";
2257
- let stderr = "";
2258
- const MAX = 2e5;
2259
- const child = spawn(cmd, args, { cwd, signal, stdio: ["ignore", "pipe", "pipe"] });
2260
- child.stdout?.on("data", (c) => {
2261
- if (stdout.length < MAX) stdout += c.toString();
2262
- });
2263
- child.stderr?.on("data", (c) => {
2264
- if (stderr.length < MAX) stderr += c.toString();
2265
- });
2266
- child.on("close", (code) => resolve2({ stdout, stderr, exitCode: code ?? 0, truncated: stdout.length >= MAX }));
2267
- child.on("error", (e) => resolve2({ stdout: "", stderr: e.message, exitCode: 1, truncated: false }));
2268
- });
2269
- }
2270
2564
  function parseResult(runner, result, duration) {
2271
2565
  const out = result.stdout + result.stderr;
2272
2566
  let tests_run = 0;
@@ -2275,16 +2569,16 @@ function parseResult(runner, result, duration) {
2275
2569
  if (runner === "vitest") {
2276
2570
  const passedMatch = out.match(/(\d+) passed/);
2277
2571
  const failedMatch = out.match(/(\d+) failed/);
2278
- if (passedMatch?.[1]) passed = parseInt(passedMatch[1], 10);
2279
- if (failedMatch?.[1]) failed = parseInt(failedMatch[1], 10);
2572
+ if (passedMatch?.[1]) passed = Number.parseInt(passedMatch[1], 10);
2573
+ if (failedMatch?.[1]) failed = Number.parseInt(failedMatch[1], 10);
2280
2574
  tests_run = passed + failed;
2281
2575
  } else if (runner === "jest") {
2282
2576
  const suitesMatch = out.match(/Test Suites:\s+(\d+)\s+total/);
2283
2577
  const passedMatch = out.match(/Tests:\s+(\d+)\s+passed/);
2284
2578
  const failedMatch = out.match(/Tests:\s+(\d+)\s+failed/);
2285
- tests_run = parseInt(suitesMatch?.[1] ?? "0", 10);
2286
- passed = parseInt(passedMatch?.[1] ?? "0", 10);
2287
- failed = parseInt(failedMatch?.[1] ?? "0", 10);
2579
+ tests_run = Number.parseInt(suitesMatch?.[1] ?? "0", 10);
2580
+ passed = Number.parseInt(passedMatch?.[1] ?? "0", 10);
2581
+ failed = Number.parseInt(failedMatch?.[1] ?? "0", 10);
2288
2582
  }
2289
2583
  return {
2290
2584
  runner,
@@ -2293,10 +2587,12 @@ function parseResult(runner, result, duration) {
2293
2587
  passed,
2294
2588
  failed,
2295
2589
  duration_ms: duration,
2296
- output: result.stdout,
2590
+ output: result.stdout || result.error || "",
2297
2591
  truncated: result.truncated
2298
2592
  };
2299
2593
  }
2594
+
2595
+ // src/install.ts
2300
2596
  var installTool = {
2301
2597
  name: "install",
2302
2598
  description: "Install npm packages. Detects pnpm/npm/yarn and uses the right package manager.",
@@ -2322,8 +2618,17 @@ var installTool = {
2322
2618
  }
2323
2619
  },
2324
2620
  async execute(input, ctx, opts) {
2621
+ let final;
2622
+ for await (const ev of installTool.executeStream(input, ctx, opts)) {
2623
+ if (ev.type === "final") final = ev.output;
2624
+ }
2625
+ if (!final) throw new Error("install: stream ended without final event");
2626
+ return final;
2627
+ },
2628
+ async *executeStream(input, ctx, opts) {
2325
2629
  const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
2326
2630
  const pkgManager = await detectPackageManager(cwd);
2631
+ yield { type: "log", text: `Resolving with ${pkgManager}\u2026`, data: { phase: "resolve" } };
2327
2632
  const save = input.save === "dev" ? "-D" : input.save === "optional" ? "-O" : "";
2328
2633
  const globalFlag = input.global ? ["-g"] : [];
2329
2634
  const args = [];
@@ -2336,15 +2641,30 @@ var installTool = {
2336
2641
  } else {
2337
2642
  args.push("install", ...globalFlag);
2338
2643
  }
2339
- if (input.packages) {
2340
- const pkgs = Array.isArray(input.packages) ? input.packages : input.packages.split(",");
2341
- args.push(...pkgs.map((p) => p.trim()));
2342
- }
2343
- return runInstall(pkgManager, args, cwd, opts.signal, input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map((p) => p.trim()) : []);
2644
+ const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map((p) => p.trim()) : [];
2645
+ if (pkgList.length > 0) args.push(...pkgList);
2646
+ yield { type: "log", text: `Fetching ${pkgList.length || "all"} packages\u2026`, data: { phase: "fetch" } };
2647
+ const result = yield* spawnStream({
2648
+ cmd: pkgManager,
2649
+ args,
2650
+ cwd,
2651
+ signal: opts.signal,
2652
+ maxBytes: 1e5
2653
+ });
2654
+ yield {
2655
+ type: "final",
2656
+ output: {
2657
+ packages: pkgList,
2658
+ exit_code: result.exitCode,
2659
+ output: result.stdout || result.stderr || result.error || "",
2660
+ dry_run: args.includes("--dry-run"),
2661
+ truncated: result.truncated
2662
+ }
2663
+ };
2344
2664
  }
2345
2665
  };
2346
2666
  async function detectPackageManager(cwd) {
2347
- const { stat: stat9 } = __require("fs/promises");
2667
+ const { stat: stat9 } = await import('fs/promises');
2348
2668
  try {
2349
2669
  await stat9(`${cwd}/pnpm-lock.yaml`);
2350
2670
  return "pnpm";
@@ -2357,34 +2677,8 @@ async function detectPackageManager(cwd) {
2357
2677
  }
2358
2678
  }
2359
2679
  }
2360
- function runInstall(manager, args, cwd, signal, packages) {
2361
- return new Promise((resolve2) => {
2362
- let stdout = "";
2363
- let stderr = "";
2364
- const MAX = 1e5;
2365
- const child = spawn(manager, args, { cwd, signal, stdio: ["ignore", "pipe", "pipe"] });
2366
- child.stdout?.on("data", (c) => {
2367
- if (stdout.length < MAX) stdout += c.toString();
2368
- });
2369
- child.stderr?.on("data", (c) => {
2370
- if (stderr.length < MAX) stderr += c.toString();
2371
- });
2372
- child.on("close", (code) => resolve2({
2373
- packages,
2374
- exit_code: code ?? 0,
2375
- output: stdout || stderr,
2376
- dry_run: args.includes("--dry-run"),
2377
- truncated: stdout.length >= MAX
2378
- }));
2379
- child.on("error", (e) => resolve2({
2380
- packages,
2381
- exit_code: 1,
2382
- output: e.message,
2383
- dry_run: false,
2384
- truncated: false
2385
- }));
2386
- });
2387
- }
2680
+
2681
+ // src/audit.ts
2388
2682
  var auditTool = {
2389
2683
  name: "audit",
2390
2684
  description: "Run npm/pnpm security audit. Returns vulnerabilities sorted by severity.",
@@ -2401,30 +2695,40 @@ var auditTool = {
2401
2695
  enum: ["low", "moderate", "high", "critical"],
2402
2696
  description: "Minimum severity level to report"
2403
2697
  },
2404
- fix: {
2405
- type: "boolean",
2406
- description: "Attempt to fix vulnerabilities (default: false)"
2407
- },
2408
- packages: {
2409
- type: "string",
2410
- description: "Specific package(s) to audit (comma-separated)"
2411
- }
2698
+ fix: { type: "boolean", description: "Attempt to fix vulnerabilities (default: false)" },
2699
+ packages: { type: "string", description: "Specific package(s) to audit (comma-separated)" }
2412
2700
  }
2413
2701
  },
2414
2702
  async execute(input, ctx, opts) {
2703
+ let final;
2704
+ for await (const ev of auditTool.executeStream(input, ctx, opts)) {
2705
+ if (ev.type === "final") final = ev.output;
2706
+ }
2707
+ if (!final) throw new Error("audit: stream ended without final event");
2708
+ return final;
2709
+ },
2710
+ async *executeStream(input, ctx, opts) {
2415
2711
  const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
2416
2712
  const manager = await detectManager(cwd);
2713
+ yield { type: "log", text: `Auditing with ${manager}\u2026`, data: { manager } };
2417
2714
  const args = ["audit", "--json"];
2418
2715
  if (input.fix) args.push("--fix");
2419
2716
  if (input.packages) {
2420
2717
  const pkgs = Array.isArray(input.packages) ? input.packages : input.packages.split(",");
2421
2718
  args.push(...pkgs.map((p) => p.trim()));
2422
2719
  }
2423
- return runAudit(manager, args, cwd, opts.signal);
2720
+ const result = yield* spawnStream({
2721
+ cmd: manager,
2722
+ args,
2723
+ cwd,
2724
+ signal: opts.signal,
2725
+ maxBytes: 1e5
2726
+ });
2727
+ yield { type: "final", output: parseAuditOutput(result.stdout, result.exitCode) };
2424
2728
  }
2425
2729
  };
2426
2730
  async function detectManager(cwd) {
2427
- const { stat: stat9 } = __require("fs/promises");
2731
+ const { stat: stat9 } = await import('fs/promises');
2428
2732
  try {
2429
2733
  await stat9(`${cwd}/pnpm-lock.yaml`);
2430
2734
  return "pnpm";
@@ -2437,32 +2741,6 @@ async function detectManager(cwd) {
2437
2741
  }
2438
2742
  return "npm";
2439
2743
  }
2440
- function runAudit(manager, args, cwd, signal) {
2441
- return new Promise((resolve2) => {
2442
- let stdout = "";
2443
- let stderr = "";
2444
- const MAX = 1e5;
2445
- const child = spawn(manager, args, { cwd, signal, stdio: ["ignore", "pipe", "pipe"] });
2446
- child.stdout?.on("data", (c) => {
2447
- if (stdout.length < MAX) stdout += c.toString();
2448
- });
2449
- child.stderr?.on("data", (c) => {
2450
- if (stderr.length < MAX) stderr += c.toString();
2451
- });
2452
- child.on("close", (code) => {
2453
- const result = parseAuditOutput(stdout, code ?? 0);
2454
- resolve2(result);
2455
- });
2456
- child.on("error", (e) => resolve2({
2457
- exit_code: 1,
2458
- vulnerabilities: [],
2459
- total: 0,
2460
- summary: e.message,
2461
- output: e.message,
2462
- truncated: false
2463
- }));
2464
- });
2465
- }
2466
2744
  function parseAuditOutput(json, exitCode) {
2467
2745
  if (!json) {
2468
2746
  return {
@@ -2717,7 +2995,7 @@ async function fileLogs(path10, lines, filterRe, stream) {
2717
2995
  const allLines = [];
2718
2996
  const rl = createInterface({
2719
2997
  input: createReadStream(path10),
2720
- crlfDelay: Infinity
2998
+ crlfDelay: Number.POSITIVE_INFINITY
2721
2999
  });
2722
3000
  for await (const line of rl) {
2723
3001
  if (filterRe && !filterRe.test(line)) continue;
@@ -2859,30 +3137,27 @@ function processFile(content, absPath, style, overwrite, target) {
2859
3137
  const arrowRegex = /(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*=>/g;
2860
3138
  const classRegex = /class\s+(\w+)/g;
2861
3139
  const typeRegex = /(?:type|interface)\s+(\w+)\s*[=<]/g;
2862
- let match;
2863
3140
  const allMatches = [];
2864
3141
  if (target === "all" || target === "function") {
2865
- while ((match = functionRegex.exec(content)) !== null) {
2866
- if (!match[1]) continue;
2867
- allMatches.push({ name: match[1], sig: match[2] ?? "", type: "function", line: content.slice(0, match.index).split("\n").length });
3142
+ for (const m of content.matchAll(functionRegex)) {
3143
+ if (!m[1]) continue;
3144
+ allMatches.push({ name: m[1], sig: m[2] ?? "", type: "function", line: content.slice(0, m.index).split("\n").length });
2868
3145
  }
2869
- while ((match = arrowRegex.exec(content)) !== null) {
2870
- if (!match[1]) continue;
2871
- allMatches.push({ name: match[1], sig: match[2] ?? "", type: "arrow", line: content.slice(0, match.index).split("\n").length });
3146
+ for (const m of content.matchAll(arrowRegex)) {
3147
+ if (!m[1]) continue;
3148
+ allMatches.push({ name: m[1], sig: m[2] ?? "", type: "arrow", line: content.slice(0, m.index).split("\n").length });
2872
3149
  }
2873
3150
  }
2874
3151
  if (target === "all" || target === "class") {
2875
- functionRegex.lastIndex = 0;
2876
- while ((match = classRegex.exec(content)) !== null) {
2877
- if (!match[1]) continue;
2878
- allMatches.push({ name: match[1], sig: "", type: "class", line: content.slice(0, match.index).split("\n").length });
3152
+ for (const m of content.matchAll(classRegex)) {
3153
+ if (!m[1]) continue;
3154
+ allMatches.push({ name: m[1], sig: "", type: "class", line: content.slice(0, m.index).split("\n").length });
2879
3155
  }
2880
3156
  }
2881
3157
  if (target === "all" || target === "type") {
2882
- functionRegex.lastIndex = 0;
2883
- while ((match = typeRegex.exec(content)) !== null) {
2884
- if (!match[1]) continue;
2885
- allMatches.push({ name: match[1], sig: match[0] ?? "", type: "type", line: content.slice(0, match.index).split("\n").length });
3158
+ for (const m of content.matchAll(typeRegex)) {
3159
+ if (!m[1]) continue;
3160
+ allMatches.push({ name: m[1], sig: m[0] ?? "", type: "type", line: content.slice(0, m.index).split("\n").length });
2886
3161
  }
2887
3162
  }
2888
3163
  for (const m of allMatches) {
@@ -2902,7 +3177,7 @@ var BUILT_IN_TEMPLATES = {
2902
3177
  files: {
2903
3178
  "package.json": JSON.stringify({
2904
3179
  name: "{{name}}",
2905
- version: "0.0.1",
3180
+ version: "0.1.1",
2906
3181
  type: "module",
2907
3182
  main: "./dist/index.js",
2908
3183
  scripts: { build: "tsc", test: "vitest run" },
@@ -2932,7 +3207,7 @@ describe('hello', () => {
2932
3207
  files: {
2933
3208
  "package.json": JSON.stringify({
2934
3209
  name: "{{name}}",
2935
- version: "0.0.1",
3210
+ version: "0.1.1",
2936
3211
  type: "module",
2937
3212
  bin: { "{{name}}": "./src/index.js" },
2938
3213
  scripts: { build: "tsc", start: "node dist/index.js" }
@@ -3097,7 +3372,7 @@ var toolSearchTool = {
3097
3372
  const limit = Math.min(input.limit ?? 20, 100);
3098
3373
  const tools = ctx.tools;
3099
3374
  const query2 = input.query?.toLowerCase() ?? "";
3100
- let filtered = tools.filter((t) => {
3375
+ const filtered = tools.filter((t) => {
3101
3376
  if (query2 && !t.name.toLowerCase().includes(query2) && !t.description.toLowerCase().includes(query2)) {
3102
3377
  return false;
3103
3378
  }
@@ -3549,7 +3824,7 @@ ${mode.description}`
3549
3824
  };
3550
3825
  }
3551
3826
 
3552
- // src/index.ts
3827
+ // src/builtin.ts
3553
3828
  var builtinTools = [
3554
3829
  readTool,
3555
3830
  writeTool,