amai 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -5,9 +5,9 @@ var pc5 = require('picocolors');
5
5
  var WebSocket = require('ws');
6
6
  var zod = require('zod');
7
7
  var path10 = require('path');
8
- var fs10 = require('fs');
8
+ var fs11 = require('fs');
9
+ var fsp = require('fs/promises');
9
10
  var os3 = require('os');
10
- var fs8 = require('fs/promises');
11
11
  var hono = require('hono');
12
12
  var nodeServer = require('@hono/node-server');
13
13
  var cors = require('hono/cors');
@@ -19,19 +19,37 @@ var readline = require('readline');
19
19
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
20
20
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
21
 
22
+ function _interopNamespace(e) {
23
+ if (e && e.__esModule) return e;
24
+ var n = Object.create(null);
25
+ if (e) {
26
+ Object.keys(e).forEach(function (k) {
27
+ if (k !== 'default') {
28
+ var d = Object.getOwnPropertyDescriptor(e, k);
29
+ Object.defineProperty(n, k, d.get ? d : {
30
+ enumerable: true,
31
+ get: function () { return e[k]; }
32
+ });
33
+ }
34
+ });
35
+ }
36
+ n.default = e;
37
+ return Object.freeze(n);
38
+ }
39
+
22
40
  var pc5__default = /*#__PURE__*/_interopDefault(pc5);
23
41
  var WebSocket__default = /*#__PURE__*/_interopDefault(WebSocket);
24
42
  var path10__default = /*#__PURE__*/_interopDefault(path10);
25
- var fs10__default = /*#__PURE__*/_interopDefault(fs10);
43
+ var fs11__default = /*#__PURE__*/_interopDefault(fs11);
44
+ var fsp__namespace = /*#__PURE__*/_interopNamespace(fsp);
26
45
  var os3__default = /*#__PURE__*/_interopDefault(os3);
27
- var fs8__default = /*#__PURE__*/_interopDefault(fs8);
28
46
  var readline__default = /*#__PURE__*/_interopDefault(readline);
29
47
 
30
48
  var MUTATING_TOOLS = /* @__PURE__ */ new Set([
31
49
  "editFile",
32
50
  "deleteFile",
33
51
  "stringReplace",
34
- "runTerminalCommand"
52
+ "bash"
35
53
  ]);
36
54
  function isMutatingTool(toolName) {
37
55
  return MUTATING_TOOLS.has(toolName);
@@ -52,11 +70,11 @@ function isPathWithinProject(filePath, projectCwd) {
52
70
  }
53
71
  function safeRealpath(p) {
54
72
  try {
55
- return fs10__default.default.realpathSync(p);
73
+ return fs11__default.default.realpathSync(p);
56
74
  } catch {
57
75
  const parent = path10__default.default.dirname(p);
58
76
  try {
59
- const realParent = fs10__default.default.realpathSync(parent);
77
+ const realParent = fs11__default.default.realpathSync(parent);
60
78
  return path10__default.default.join(realParent, path10__default.default.basename(p));
61
79
  } catch {
62
80
  return path10__default.default.resolve(p);
@@ -105,77 +123,231 @@ function requireProjectCwd(toolName, projectCwd) {
105
123
 
106
124
  // src/tools/read-file.ts
107
125
  var MAX_FILE_SIZE = 10 * 1024 * 1024;
108
- var MAX_LINES_RETURNED = 1e4;
126
+ var MAX_LINES_RETURNED = 2e3;
127
+ var MAX_LINE_LENGTH = 2e3;
128
+ var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
129
+ var MAX_BYTES = 50 * 1024;
130
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
131
+ ".zip",
132
+ ".tar",
133
+ ".gz",
134
+ ".exe",
135
+ ".dll",
136
+ ".so",
137
+ ".class",
138
+ ".jar",
139
+ ".war",
140
+ ".7z",
141
+ ".doc",
142
+ ".docx",
143
+ ".xls",
144
+ ".xlsx",
145
+ ".ppt",
146
+ ".pptx",
147
+ ".odt",
148
+ ".ods",
149
+ ".odp",
150
+ ".bin",
151
+ ".dat",
152
+ ".obj",
153
+ ".o",
154
+ ".a",
155
+ ".lib",
156
+ ".wasm",
157
+ ".pyc",
158
+ ".pyo",
159
+ ".ico",
160
+ ".bmp",
161
+ ".ttf",
162
+ ".woff",
163
+ ".woff2",
164
+ ".eot",
165
+ ".mp3",
166
+ ".mp4",
167
+ ".avi",
168
+ ".mov",
169
+ ".flv"
170
+ ]);
171
+ async function isBinaryFile(filepath, fileSize) {
172
+ const ext = path10__default.default.extname(filepath).toLowerCase();
173
+ if (BINARY_EXTENSIONS.has(ext)) return true;
174
+ if (fileSize === 0) return false;
175
+ try {
176
+ const fh = await fsp__namespace.open(filepath, "r");
177
+ try {
178
+ const sampleSize = Math.min(4096, fileSize);
179
+ const bytes = Buffer.alloc(sampleSize);
180
+ const result = await fh.read(bytes, 0, sampleSize, 0);
181
+ if (result.bytesRead === 0) return false;
182
+ let nonPrintableCount = 0;
183
+ for (let i = 0; i < result.bytesRead; i++) {
184
+ if (bytes[i] === 0) return true;
185
+ if (bytes[i] < 9 || bytes[i] > 13 && bytes[i] < 32) {
186
+ nonPrintableCount++;
187
+ }
188
+ }
189
+ return nonPrintableCount / result.bytesRead > 0.3;
190
+ } finally {
191
+ await fh.close();
192
+ }
193
+ } catch {
194
+ return false;
195
+ }
196
+ }
197
+ async function findSimilarFiles(filepath) {
198
+ const dir = path10__default.default.dirname(filepath);
199
+ const base = path10__default.default.basename(filepath).toLowerCase();
200
+ try {
201
+ const entries = await fsp__namespace.readdir(dir);
202
+ return entries.filter(
203
+ (entry) => entry.toLowerCase().includes(base) || base.includes(entry.toLowerCase())
204
+ ).map((entry) => path10__default.default.join(dir, entry)).slice(0, 3);
205
+ } catch {
206
+ return [];
207
+ }
208
+ }
109
209
  zod.z.object({
110
- relative_file_path: zod.z.string().describe("The relative path to the file to read."),
111
- should_read_entire_file: zod.z.boolean().describe("Whether to read the entire file."),
112
- start_line_one_indexed: zod.z.number().optional().describe(
113
- "The one-indexed line number to start reading from (inclusive)."
114
- ),
115
- end_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to end reading at (inclusive).")
210
+ relative_file_path: zod.z.string().describe("The path to the file or directory to read."),
211
+ should_read_entire_file: zod.z.boolean().describe("Whether to read the entire file.").optional().default(true),
212
+ start_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to start reading from (inclusive). Alias: offset."),
213
+ end_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to end reading at (inclusive). Alias: offset + limit.")
116
214
  });
117
215
  async function readFileContent(absolute_file_path, relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed) {
118
- const file = Bun.file(absolute_file_path);
119
- const exists = await file.exists();
120
- if (!exists) {
216
+ let stat2;
217
+ try {
218
+ stat2 = fs11__default.default.statSync(absolute_file_path);
219
+ } catch {
220
+ const suggestions = await findSimilarFiles(absolute_file_path);
221
+ let message = `File not found: ${relative_file_path}`;
222
+ if (suggestions.length > 0) {
223
+ message += `
224
+
225
+ Did you mean one of these?
226
+ ${suggestions.join("\n")}`;
227
+ }
121
228
  return {
122
229
  success: false,
123
- message: `File not found: ${relative_file_path}`,
230
+ message,
124
231
  error: "FILE_NOT_FOUND"
125
232
  };
126
233
  }
234
+ if (stat2.isDirectory()) {
235
+ try {
236
+ const dirents = await fsp__namespace.readdir(absolute_file_path, { withFileTypes: true });
237
+ const entries = await Promise.all(
238
+ dirents.map(async (dirent) => {
239
+ if (dirent.isDirectory()) return dirent.name + "/";
240
+ if (dirent.isSymbolicLink()) {
241
+ const target = await fsp__namespace.stat(path10__default.default.join(absolute_file_path, dirent.name)).catch(() => void 0);
242
+ if (target?.isDirectory()) return dirent.name + "/";
243
+ }
244
+ return dirent.name;
245
+ })
246
+ );
247
+ entries.sort((a, b) => a.localeCompare(b));
248
+ const truncated = entries.length > MAX_LINES_RETURNED;
249
+ const sliced = entries.slice(0, MAX_LINES_RETURNED);
250
+ const output = [
251
+ `<path>${absolute_file_path}</path>`,
252
+ `<type>directory</type>`,
253
+ `<entries>`,
254
+ sliced.join("\n"),
255
+ truncated ? `
256
+ (Showing ${sliced.length} of ${entries.length} entries)` : `
257
+ (${entries.length} entries)`,
258
+ `</entries>`
259
+ ].join("\n");
260
+ return {
261
+ success: true,
262
+ message: `Listed directory: ${relative_file_path} (${entries.length} entries)`,
263
+ content: output,
264
+ totalLines: entries.length,
265
+ truncated
266
+ };
267
+ } catch (err) {
268
+ return {
269
+ success: false,
270
+ message: `Failed to list directory: ${relative_file_path}`,
271
+ error: "READ_ERROR"
272
+ };
273
+ }
274
+ }
127
275
  try {
128
- const stat = await file.stat();
129
- if (stat.size > MAX_FILE_SIZE) {
276
+ if (stat2.size > MAX_FILE_SIZE) {
130
277
  return {
131
278
  success: false,
132
- message: `File too large (${Math.round(stat.size / 1024 / 1024)}MB). Maximum is ${MAX_FILE_SIZE / 1024 / 1024}MB. Use line ranges to read portions.`,
279
+ message: `File too large (${Math.round(stat2.size / 1024 / 1024)}MB). Maximum is ${MAX_FILE_SIZE / 1024 / 1024}MB. Use line ranges to read portions.`,
133
280
  error: "FILE_TOO_LARGE"
134
281
  };
135
282
  }
136
- const fileContent = await file.text();
137
- const lines = fileContent.split(/\r?\n/);
138
- const totalLines = lines.length;
139
- if (should_read_entire_file) {
140
- const cappedLines = lines.slice(0, MAX_LINES_RETURNED);
141
- const truncated = totalLines > MAX_LINES_RETURNED;
142
- const content = cappedLines.join("\n");
283
+ const binary = await isBinaryFile(absolute_file_path, stat2.size);
284
+ if (binary) {
143
285
  return {
144
- success: true,
145
- message: truncated ? `Read first ${MAX_LINES_RETURNED} of ${totalLines} lines from: ${relative_file_path} (truncated)` : `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
146
- content,
147
- totalLines,
148
- truncated
286
+ success: false,
287
+ message: `Cannot read binary file: ${relative_file_path}`,
288
+ error: "BINARY_FILE"
149
289
  };
150
290
  }
151
- const startIndex = start_line_one_indexed - 1;
152
- if (startIndex >= totalLines) {
291
+ const fileContent = await Bun.file(absolute_file_path).text();
292
+ const lines = fileContent.split(/\r?\n/);
293
+ const totalLines = lines.length;
294
+ const start = should_read_entire_file ? 0 : (start_line_one_indexed ?? 1) - 1;
295
+ const end = should_read_entire_file ? Math.min(totalLines, MAX_LINES_RETURNED) : Math.min(end_line_one_indexed ?? totalLines, totalLines);
296
+ if (start >= totalLines && !(totalLines === 0 && start === 0)) {
153
297
  return {
154
298
  success: false,
155
- message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
299
+ message: `Offset ${start + 1} is out of range for this file (${totalLines} lines)`,
156
300
  error: "INVALID_LINE_RANGE"
157
301
  };
158
302
  }
159
- const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
160
- const rangeSize = normalizedEnd - startIndex;
161
- const cappedEnd = rangeSize > MAX_LINES_RETURNED ? startIndex + MAX_LINES_RETURNED : normalizedEnd;
162
- const selectedLines = lines.slice(startIndex, cappedEnd).join("\n");
163
- const linesRead = cappedEnd - startIndex;
303
+ const outputLines = [];
304
+ let bytes = 0;
305
+ let truncatedByBytes = false;
306
+ let actualEnd = start;
307
+ for (let i = start; i < end; i++) {
308
+ let line = lines[i];
309
+ if (line.length > MAX_LINE_LENGTH) {
310
+ line = line.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX;
311
+ }
312
+ const numberedLine = `${i + 1}: ${line}`;
313
+ const lineBytes = Buffer.byteLength(numberedLine, "utf-8") + (outputLines.length > 0 ? 1 : 0);
314
+ if (bytes + lineBytes > MAX_BYTES && outputLines.length > 0) {
315
+ truncatedByBytes = true;
316
+ break;
317
+ }
318
+ outputLines.push(numberedLine);
319
+ bytes += lineBytes;
320
+ actualEnd = i + 1;
321
+ }
322
+ const hasMoreLines = actualEnd < totalLines;
323
+ const truncated = truncatedByBytes || hasMoreLines || should_read_entire_file && totalLines > MAX_LINES_RETURNED;
324
+ let output = `<path>${absolute_file_path}</path>
325
+ <type>file</type>
326
+ <content>
327
+ `;
328
+ output += outputLines.join("\n");
329
+ if (truncatedByBytes) {
330
+ output += `
331
+
332
+ (Output capped at ${MAX_BYTES / 1024} KB. Showing lines ${start + 1}-${actualEnd}. Use start_line_one_indexed=${actualEnd + 1} to continue.)`;
333
+ } else if (hasMoreLines && !should_read_entire_file) {
334
+ output += `
335
+
336
+ (Showing lines ${start + 1}-${actualEnd} of ${totalLines}. Use start_line_one_indexed=${actualEnd + 1} to continue.)`;
337
+ } else {
338
+ output += `
339
+
340
+ (End of file - total ${totalLines} lines)`;
341
+ }
342
+ output += "\n</content>";
164
343
  return {
165
344
  success: true,
166
- message: `Successfully read lines ${start_line_one_indexed}-${cappedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
167
- content: selectedLines,
345
+ message: truncated ? `Read lines ${start + 1}-${actualEnd} of ${totalLines} from: ${relative_file_path} (truncated)` : `Successfully read file: ${relative_file_path} (${totalLines} lines)`,
346
+ content: output,
168
347
  totalLines,
169
- truncated: cappedEnd < normalizedEnd
348
+ truncated
170
349
  };
171
350
  } catch (error) {
172
- if (error?.code === "EISDIR") {
173
- return {
174
- success: false,
175
- message: `Path is not a file: ${relative_file_path}`,
176
- error: "NOT_A_FILE"
177
- };
178
- }
179
351
  return {
180
352
  success: false,
181
353
  message: `Failed to read file: ${relative_file_path}`,
@@ -184,12 +356,12 @@ async function readFileContent(absolute_file_path, relative_file_path, should_re
184
356
  }
185
357
  }
186
358
  var read_file = async function(input, projectCwd) {
187
- const { relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed } = input;
359
+ const { relative_file_path, should_read_entire_file = true, start_line_one_indexed, end_line_one_indexed } = input;
188
360
  try {
189
361
  if (!relative_file_path) {
190
362
  return {
191
363
  success: false,
192
- message: "Missing required parameter: target_file",
364
+ message: "Missing required parameter: relative_file_path",
193
365
  error: "MISSING_TARGET_FILE"
194
366
  };
195
367
  }
@@ -522,10 +694,310 @@ function calculateDiffStats(oldContent, newContent) {
522
694
  zod.z.object({
523
695
  file_path: zod.z.string().describe("The path to the file you want to search and replace in. You can use either a relative path in the workspace or an absolute path. If an absolute path is provided, it will be preserved as is"),
524
696
  new_string: zod.z.string().describe("The edited text to replace the old_string (must be different from the old_string)"),
525
- old_string: zod.z.string().describe("The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)")
697
+ old_string: zod.z.string().describe("The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)"),
698
+ replaceAll: zod.z.boolean().optional().describe("Replace all occurrences of old_string (default false)")
526
699
  });
700
+ function levenshtein(a, b) {
701
+ if (a === "" || b === "") return Math.max(a.length, b.length);
702
+ const matrix = Array.from(
703
+ { length: a.length + 1 },
704
+ (_, i) => Array.from({ length: b.length + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
705
+ );
706
+ for (let i = 1; i <= a.length; i++) {
707
+ for (let j = 1; j <= b.length; j++) {
708
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
709
+ matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
710
+ }
711
+ }
712
+ return matrix[a.length][b.length];
713
+ }
714
+ var SINGLE_CANDIDATE_SIMILARITY_THRESHOLD = 0;
715
+ var MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD = 0.3;
716
+ var SimpleReplacer = function* (_content, find) {
717
+ yield find;
718
+ };
719
+ var LineTrimmedReplacer = function* (content, find) {
720
+ const originalLines = content.split("\n");
721
+ const searchLines = find.split("\n");
722
+ if (searchLines[searchLines.length - 1] === "") searchLines.pop();
723
+ for (let i = 0; i <= originalLines.length - searchLines.length; i++) {
724
+ let matches = true;
725
+ for (let j = 0; j < searchLines.length; j++) {
726
+ if (originalLines[i + j].trim() !== searchLines[j].trim()) {
727
+ matches = false;
728
+ break;
729
+ }
730
+ }
731
+ if (matches) {
732
+ let matchStartIndex = 0;
733
+ for (let k = 0; k < i; k++) matchStartIndex += originalLines[k].length + 1;
734
+ let matchEndIndex = matchStartIndex;
735
+ for (let k = 0; k < searchLines.length; k++) {
736
+ matchEndIndex += originalLines[i + k].length;
737
+ if (k < searchLines.length - 1) matchEndIndex += 1;
738
+ }
739
+ yield content.substring(matchStartIndex, matchEndIndex);
740
+ }
741
+ }
742
+ };
743
+ var BlockAnchorReplacer = function* (content, find) {
744
+ const originalLines = content.split("\n");
745
+ const searchLines = find.split("\n");
746
+ if (searchLines.length < 3) return;
747
+ if (searchLines[searchLines.length - 1] === "") searchLines.pop();
748
+ const firstLineSearch = searchLines[0].trim();
749
+ const lastLineSearch = searchLines[searchLines.length - 1].trim();
750
+ const searchBlockSize = searchLines.length;
751
+ const candidates = [];
752
+ for (let i = 0; i < originalLines.length; i++) {
753
+ if (originalLines[i].trim() !== firstLineSearch) continue;
754
+ for (let j = i + 2; j < originalLines.length; j++) {
755
+ if (originalLines[j].trim() === lastLineSearch) {
756
+ candidates.push({ startLine: i, endLine: j });
757
+ break;
758
+ }
759
+ }
760
+ }
761
+ if (candidates.length === 0) return;
762
+ const computeSimilarity = (startLine, endLine) => {
763
+ const actualBlockSize = endLine - startLine + 1;
764
+ const linesToCheck = Math.min(searchBlockSize - 2, actualBlockSize - 2);
765
+ if (linesToCheck <= 0) return 1;
766
+ let similarity = 0;
767
+ for (let j = 1; j < searchBlockSize - 1 && j < actualBlockSize - 1; j++) {
768
+ const originalLine = originalLines[startLine + j].trim();
769
+ const searchLine = searchLines[j].trim();
770
+ const maxLen = Math.max(originalLine.length, searchLine.length);
771
+ if (maxLen === 0) continue;
772
+ const distance = levenshtein(originalLine, searchLine);
773
+ similarity += (1 - distance / maxLen) / linesToCheck;
774
+ }
775
+ return similarity;
776
+ };
777
+ const extractBlock = (startLine, endLine) => {
778
+ let matchStartIndex = 0;
779
+ for (let k = 0; k < startLine; k++) matchStartIndex += originalLines[k].length + 1;
780
+ let matchEndIndex = matchStartIndex;
781
+ for (let k = startLine; k <= endLine; k++) {
782
+ matchEndIndex += originalLines[k].length;
783
+ if (k < endLine) matchEndIndex += 1;
784
+ }
785
+ return content.substring(matchStartIndex, matchEndIndex);
786
+ };
787
+ if (candidates.length === 1) {
788
+ const { startLine, endLine } = candidates[0];
789
+ if (computeSimilarity(startLine, endLine) >= SINGLE_CANDIDATE_SIMILARITY_THRESHOLD) {
790
+ yield extractBlock(startLine, endLine);
791
+ }
792
+ return;
793
+ }
794
+ let bestMatch = null;
795
+ let maxSimilarity = -1;
796
+ for (const candidate of candidates) {
797
+ const similarity = computeSimilarity(candidate.startLine, candidate.endLine);
798
+ if (similarity > maxSimilarity) {
799
+ maxSimilarity = similarity;
800
+ bestMatch = candidate;
801
+ }
802
+ }
803
+ if (maxSimilarity >= MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD && bestMatch) {
804
+ yield extractBlock(bestMatch.startLine, bestMatch.endLine);
805
+ }
806
+ };
807
+ var WhitespaceNormalizedReplacer = function* (content, find) {
808
+ const normalizeWhitespace = (text) => text.replace(/\s+/g, " ").trim();
809
+ const normalizedFind = normalizeWhitespace(find);
810
+ const lines = content.split("\n");
811
+ for (let i = 0; i < lines.length; i++) {
812
+ const line = lines[i];
813
+ if (normalizeWhitespace(line) === normalizedFind) {
814
+ yield line;
815
+ } else {
816
+ const normalizedLine = normalizeWhitespace(line);
817
+ if (normalizedLine.includes(normalizedFind)) {
818
+ const words = find.trim().split(/\s+/);
819
+ if (words.length > 0) {
820
+ const pattern = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+");
821
+ try {
822
+ const regex = new RegExp(pattern);
823
+ const match = line.match(regex);
824
+ if (match) yield match[0];
825
+ } catch {
826
+ }
827
+ }
828
+ }
829
+ }
830
+ }
831
+ const findLines = find.split("\n");
832
+ if (findLines.length > 1) {
833
+ for (let i = 0; i <= lines.length - findLines.length; i++) {
834
+ const block = lines.slice(i, i + findLines.length);
835
+ if (normalizeWhitespace(block.join("\n")) === normalizedFind) {
836
+ yield block.join("\n");
837
+ }
838
+ }
839
+ }
840
+ };
841
+ var IndentationFlexibleReplacer = function* (content, find) {
842
+ const removeIndentation = (text) => {
843
+ const lines = text.split("\n");
844
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
845
+ if (nonEmptyLines.length === 0) return text;
846
+ const minIndent = Math.min(
847
+ ...nonEmptyLines.map((line) => {
848
+ const match = line.match(/^(\s*)/);
849
+ return match ? match[1].length : 0;
850
+ })
851
+ );
852
+ return lines.map((line) => line.trim().length === 0 ? line : line.slice(minIndent)).join("\n");
853
+ };
854
+ const normalizedFind = removeIndentation(find);
855
+ const contentLines = content.split("\n");
856
+ const findLines = find.split("\n");
857
+ for (let i = 0; i <= contentLines.length - findLines.length; i++) {
858
+ const block = contentLines.slice(i, i + findLines.length).join("\n");
859
+ if (removeIndentation(block) === normalizedFind) {
860
+ yield block;
861
+ }
862
+ }
863
+ };
864
+ var EscapeNormalizedReplacer = function* (content, find) {
865
+ const unescapeString = (str) => {
866
+ return str.replace(/\\(n|t|r|'|"|`|\\|\n|\$)/g, (match, capturedChar) => {
867
+ switch (capturedChar) {
868
+ case "n":
869
+ return "\n";
870
+ case "t":
871
+ return " ";
872
+ case "r":
873
+ return "\r";
874
+ case "'":
875
+ return "'";
876
+ case '"':
877
+ return '"';
878
+ case "`":
879
+ return "`";
880
+ case "\\":
881
+ return "\\";
882
+ case "\n":
883
+ return "\n";
884
+ case "$":
885
+ return "$";
886
+ default:
887
+ return match;
888
+ }
889
+ });
890
+ };
891
+ const unescapedFind = unescapeString(find);
892
+ if (content.includes(unescapedFind)) {
893
+ yield unescapedFind;
894
+ }
895
+ const lines = content.split("\n");
896
+ const findLines = unescapedFind.split("\n");
897
+ for (let i = 0; i <= lines.length - findLines.length; i++) {
898
+ const block = lines.slice(i, i + findLines.length).join("\n");
899
+ if (unescapeString(block) === unescapedFind) {
900
+ yield block;
901
+ }
902
+ }
903
+ };
904
+ var TrimmedBoundaryReplacer = function* (content, find) {
905
+ const trimmedFind = find.trim();
906
+ if (trimmedFind === find) return;
907
+ if (content.includes(trimmedFind)) {
908
+ yield trimmedFind;
909
+ }
910
+ const lines = content.split("\n");
911
+ const findLines = find.split("\n");
912
+ for (let i = 0; i <= lines.length - findLines.length; i++) {
913
+ const block = lines.slice(i, i + findLines.length).join("\n");
914
+ if (block.trim() === trimmedFind) {
915
+ yield block;
916
+ }
917
+ }
918
+ };
919
+ var ContextAwareReplacer = function* (content, find) {
920
+ const findLines = find.split("\n");
921
+ if (findLines.length < 3) return;
922
+ if (findLines[findLines.length - 1] === "") findLines.pop();
923
+ const contentLines = content.split("\n");
924
+ const firstLine = findLines[0].trim();
925
+ const lastLine = findLines[findLines.length - 1].trim();
926
+ for (let i = 0; i < contentLines.length; i++) {
927
+ if (contentLines[i].trim() !== firstLine) continue;
928
+ for (let j = i + 2; j < contentLines.length; j++) {
929
+ if (contentLines[j].trim() === lastLine) {
930
+ const blockLines = contentLines.slice(i, j + 1);
931
+ if (blockLines.length === findLines.length) {
932
+ let matchingLines = 0;
933
+ let totalNonEmptyLines = 0;
934
+ for (let k = 1; k < blockLines.length - 1; k++) {
935
+ const blockLine = blockLines[k].trim();
936
+ const findLine = findLines[k].trim();
937
+ if (blockLine.length > 0 || findLine.length > 0) {
938
+ totalNonEmptyLines++;
939
+ if (blockLine === findLine) matchingLines++;
940
+ }
941
+ }
942
+ if (totalNonEmptyLines === 0 || matchingLines / totalNonEmptyLines >= 0.5) {
943
+ yield blockLines.join("\n");
944
+ break;
945
+ }
946
+ }
947
+ break;
948
+ }
949
+ }
950
+ }
951
+ };
952
+ var MultiOccurrenceReplacer = function* (content, find) {
953
+ let startIndex = 0;
954
+ while (true) {
955
+ const index = content.indexOf(find, startIndex);
956
+ if (index === -1) break;
957
+ yield find;
958
+ startIndex = index + find.length;
959
+ }
960
+ };
961
+ var REPLACERS = [
962
+ SimpleReplacer,
963
+ LineTrimmedReplacer,
964
+ BlockAnchorReplacer,
965
+ WhitespaceNormalizedReplacer,
966
+ IndentationFlexibleReplacer,
967
+ EscapeNormalizedReplacer,
968
+ TrimmedBoundaryReplacer,
969
+ ContextAwareReplacer,
970
+ MultiOccurrenceReplacer
971
+ ];
972
+ function smartReplace(content, oldString, newString, replaceAll = false) {
973
+ if (oldString === newString) {
974
+ throw new Error("No changes to apply: oldString and newString are identical.");
975
+ }
976
+ let notFound = true;
977
+ for (const replacer of REPLACERS) {
978
+ for (const search of replacer(content, oldString)) {
979
+ const index = content.indexOf(search);
980
+ if (index === -1) continue;
981
+ notFound = false;
982
+ if (replaceAll) {
983
+ return content.replaceAll(search, newString);
984
+ }
985
+ const lastIndex = content.lastIndexOf(search);
986
+ if (index !== lastIndex) continue;
987
+ return content.substring(0, index) + newString + content.substring(index + search.length);
988
+ }
989
+ }
990
+ if (notFound) {
991
+ throw new Error(
992
+ "oldString not found in content. It must match the file contents exactly, including whitespace, indentation, and line endings."
993
+ );
994
+ }
995
+ throw new Error(
996
+ "Found multiple matches for oldString. Provide more surrounding lines in oldString to identify the correct match."
997
+ );
998
+ }
527
999
  var apply_patch = async function(input, projectCwd) {
528
- const { file_path, new_string, old_string } = input;
1000
+ const { file_path, new_string, old_string, replaceAll: shouldReplaceAll = false } = input;
529
1001
  try {
530
1002
  if (!file_path) {
531
1003
  return {
@@ -570,6 +1042,22 @@ var apply_patch = async function(input, projectCwd) {
570
1042
  const file = Bun.file(absolute_file_path);
571
1043
  const exists = await file.exists();
572
1044
  if (!exists) {
1045
+ if (old_string === "") {
1046
+ const { mkdir: mkdir2 } = await import('fs/promises');
1047
+ const path17 = await import('path');
1048
+ await mkdir2(path17.dirname(absolute_file_path), { recursive: true });
1049
+ await Bun.write(absolute_file_path, new_string);
1050
+ const diffStats = calculateDiffStats("", new_string);
1051
+ return {
1052
+ success: true,
1053
+ isNewFile: true,
1054
+ old_string: "",
1055
+ new_string,
1056
+ linesAdded: diffStats.linesAdded,
1057
+ linesRemoved: diffStats.linesRemoved,
1058
+ message: `Created new file: ${file_path}`
1059
+ };
1060
+ }
573
1061
  return {
574
1062
  success: false,
575
1063
  message: `File not found: ${file_path}`,
@@ -586,22 +1074,31 @@ var apply_patch = async function(input, projectCwd) {
586
1074
  error: "READ_ERROR"
587
1075
  };
588
1076
  }
589
- if (!fileContent.includes(old_string)) {
590
- return {
591
- success: false,
592
- message: `old_string not found in file: ${file_path}`,
593
- error: "STRING_NOT_FOUND"
594
- };
595
- }
596
- const occurrences = fileContent.split(old_string).length - 1;
597
- if (occurrences > 1) {
1077
+ let newContent;
1078
+ try {
1079
+ newContent = smartReplace(fileContent, old_string, new_string, shouldReplaceAll);
1080
+ } catch (err) {
1081
+ if (err.message.includes("not found")) {
1082
+ return {
1083
+ success: false,
1084
+ message: `old_string not found in file: ${file_path}. Ensure it matches the file contents exactly, including whitespace and indentation.`,
1085
+ error: "STRING_NOT_FOUND"
1086
+ };
1087
+ }
1088
+ if (err.message.includes("multiple matches")) {
1089
+ const occurrences = fileContent.split(old_string).length - 1;
1090
+ return {
1091
+ success: false,
1092
+ message: `old_string appears ${occurrences > 1 ? occurrences + " times" : "multiple times (via fuzzy match)"} in the file. Provide more surrounding context to make it unique, or set replaceAll to true.`,
1093
+ error: "STRING_NOT_UNIQUE"
1094
+ };
1095
+ }
598
1096
  return {
599
1097
  success: false,
600
- message: `old_string appears ${occurrences} times in the file. It must be unique. Please include more context to make it unique.`,
601
- error: "STRING_NOT_UNIQUE"
1098
+ message: err.message,
1099
+ error: "REPLACE_ERROR"
602
1100
  };
603
1101
  }
604
- const newContent = fileContent.replace(old_string, new_string);
605
1102
  try {
606
1103
  await Bun.write(absolute_file_path, newContent);
607
1104
  const diffStats = calculateDiffStats(fileContent, newContent);
@@ -635,11 +1132,18 @@ var CODE_DIR = path10__default.default.join(AMA_DIR, "code");
635
1132
  var STORAGE_DIR = path10__default.default.join(AMA_DIR, "storage");
636
1133
  zod.z.object({
637
1134
  target_file: zod.z.string().describe("The relative path to the file to modify. The tool will create any directories in the path that don't exist"),
638
- content: zod.z.string().describe("The content to write to the file"),
639
- providedNewFile: zod.z.boolean().describe("The new file content to write to the file").optional()
1135
+ content: zod.z.string().describe("The full content to write to the file"),
1136
+ providedNewFile: zod.z.boolean().describe("Whether this is a new file (true) or an edit to an existing file (false). Auto-detected if omitted.").optional()
640
1137
  });
641
1138
  var editFiles = async function(input, projectCwd) {
642
1139
  const { target_file, content, providedNewFile } = input;
1140
+ if (!target_file) {
1141
+ return {
1142
+ success: false,
1143
+ error: "Missing required parameter: target_file",
1144
+ message: "target_file is required"
1145
+ };
1146
+ }
643
1147
  try {
644
1148
  if (projectCwd) {
645
1149
  const validation = validatePath(target_file, projectCwd);
@@ -654,7 +1158,7 @@ var editFiles = async function(input, projectCwd) {
654
1158
  const basePath = projectCwd || process.cwd();
655
1159
  const filePath = resolveProjectPath(target_file, basePath);
656
1160
  const dirPath = path10__default.default.dirname(filePath);
657
- await fs8.mkdir(dirPath, { recursive: true });
1161
+ await fsp.mkdir(dirPath, { recursive: true });
658
1162
  let isNewFile = providedNewFile;
659
1163
  let existingContent = "";
660
1164
  const file = Bun.file(filePath);
@@ -674,6 +1178,15 @@ var editFiles = async function(input, projectCwd) {
674
1178
  isNewFile = true;
675
1179
  }
676
1180
  }
1181
+ if (!isNewFile && existingContent === content) {
1182
+ return {
1183
+ success: true,
1184
+ isNewFile: false,
1185
+ message: `No changes needed: ${target_file} (content identical)`,
1186
+ linesAdded: 0,
1187
+ linesRemoved: 0
1188
+ };
1189
+ }
677
1190
  await Bun.write(filePath, content);
678
1191
  const diffStats = calculateDiffStats(existingContent, content);
679
1192
  if (isNewFile) {
@@ -682,7 +1195,7 @@ var editFiles = async function(input, projectCwd) {
682
1195
  isNewFile: true,
683
1196
  old_string: "",
684
1197
  new_string: content,
685
- message: `Created new file: ${target_file}`,
1198
+ message: `Created new file: ${target_file} (+${diffStats.linesAdded} lines)`,
686
1199
  linesAdded: diffStats.linesAdded,
687
1200
  linesRemoved: diffStats.linesRemoved
688
1201
  };
@@ -692,7 +1205,7 @@ var editFiles = async function(input, projectCwd) {
692
1205
  isNewFile: false,
693
1206
  old_string: existingContent,
694
1207
  new_string: content,
695
- message: `Modified file: ${target_file}`,
1208
+ message: `Modified file: ${target_file} (+${diffStats.linesAdded} -${diffStats.linesRemoved} lines)`,
696
1209
  linesAdded: diffStats.linesAdded,
697
1210
  linesRemoved: diffStats.linesRemoved
698
1211
  };
@@ -757,7 +1270,7 @@ var deleteFile = async function(input, projectCwd) {
757
1270
  };
758
1271
  }
759
1272
  try {
760
- await fs8.unlink(absolute_file_path);
1273
+ await fsp.unlink(absolute_file_path);
761
1274
  } catch {
762
1275
  return {
763
1276
  success: false,
@@ -780,7 +1293,8 @@ var deleteFile = async function(input, projectCwd) {
780
1293
  };
781
1294
  var GREP_LIMITS = {
782
1295
  DEFAULT_MAX_MATCHES: 200,
783
- MAX_LINE_LENGTH: 500,
1296
+ MAX_LINE_LENGTH: 2e3,
1297
+ // aligned with OpenCode's 2000-char truncation
784
1298
  MAX_TOTAL_OUTPUT_SIZE: 1 * 1024 * 1024,
785
1299
  EXECUTION_TIMEOUT_MS: 15e3,
786
1300
  TRUNCATION_MESSAGE: "\n[Results truncated due to size limits. Use more specific patterns or file filters to narrow your search.]"
@@ -788,11 +1302,11 @@ var GREP_LIMITS = {
788
1302
  zod.z.object({
789
1303
  query: zod.z.string().describe("The regex pattern to search for"),
790
1304
  options: zod.z.object({
791
- includePattern: zod.z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts")'),
1305
+ includePattern: zod.z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts", "*.{ts,tsx}")'),
792
1306
  excludePattern: zod.z.string().optional().describe("Glob pattern for files to exclude"),
793
1307
  caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive"),
794
1308
  path: zod.z.string().optional().describe("Subdirectory to search in"),
795
- sortByMtime: zod.z.boolean().optional().describe("Sort results by file modification time (default: false)")
1309
+ sortByMtime: zod.z.boolean().optional().describe("Sort results by file modification time (default: true)")
796
1310
  }).optional()
797
1311
  });
798
1312
  var _cachedRgPath = null;
@@ -804,7 +1318,7 @@ async function getRipgrepPath() {
804
1318
  "/usr/bin/rg"
805
1319
  ];
806
1320
  for (const rgPath of paths) {
807
- if (fs10__default.default.existsSync(rgPath)) {
1321
+ if (fs11__default.default.existsSync(rgPath)) {
808
1322
  _cachedRgPath = rgPath;
809
1323
  return rgPath;
810
1324
  }
@@ -838,7 +1352,7 @@ var grepTool = async function(input, projectCwd) {
838
1352
  };
839
1353
  }
840
1354
  try {
841
- const { includePattern, excludePattern, caseSensitive, path: subPath, sortByMtime = false } = options || {};
1355
+ const { includePattern, excludePattern, caseSensitive, path: subPath, sortByMtime = true } = options || {};
842
1356
  let searchDir = projectCwd || process.cwd();
843
1357
  if (subPath) {
844
1358
  searchDir = path10__default.default.isAbsolute(subPath) ? subPath : path10__default.default.resolve(searchDir, subPath);
@@ -853,7 +1367,7 @@ var grepTool = async function(input, projectCwd) {
853
1367
  }
854
1368
  }
855
1369
  }
856
- if (!fs10__default.default.existsSync(searchDir)) {
1370
+ if (!fs11__default.default.existsSync(searchDir)) {
857
1371
  return {
858
1372
  success: false,
859
1373
  message: `Directory not found: ${searchDir}`,
@@ -862,12 +1376,15 @@ var grepTool = async function(input, projectCwd) {
862
1376
  }
863
1377
  const rgPath = await getRipgrepPath();
864
1378
  const args2 = [
865
- "-n",
866
- "--with-filename",
867
- "--no-heading",
1379
+ "-nH",
1380
+ // line numbers + filename (compact form, matching OpenCode)
1381
+ "--hidden",
1382
+ // search hidden files (aligned with OpenCode)
1383
+ "--no-messages",
1384
+ // suppress error messages for unreadable files
868
1385
  "--color=never",
869
1386
  "--max-count=100",
870
- "--max-columns=1000"
1387
+ "--max-columns=2000"
871
1388
  ];
872
1389
  if (!caseSensitive) {
873
1390
  args2.push("-i");
@@ -887,6 +1404,7 @@ var grepTool = async function(input, projectCwd) {
887
1404
  args2.push("--glob", "!package-lock.json");
888
1405
  args2.push("--glob", "!yarn.lock");
889
1406
  args2.push("--glob", "!bun.lockb");
1407
+ args2.push("--glob", "!pnpm-lock.yaml");
890
1408
  args2.push("--regexp", query);
891
1409
  args2.push(searchDir);
892
1410
  const proc = Bun.spawn([rgPath, ...args2], {
@@ -919,7 +1437,7 @@ var grepTool = async function(input, projectCwd) {
919
1437
  message: `No matches found for pattern: ${query}`
920
1438
  };
921
1439
  }
922
- if (exitCode !== 0) {
1440
+ if (exitCode !== 0 && exitCode !== 2) {
923
1441
  return {
924
1442
  success: false,
925
1443
  message: `Ripgrep error: ${stderr || "Unknown error"}`,
@@ -1005,8 +1523,7 @@ var grepTool = async function(input, projectCwd) {
1005
1523
  };
1006
1524
  zod.z.object({
1007
1525
  pattern: zod.z.string().describe('Glob pattern to match files (e.g., "**/*.js", "src/**/*.ts", "*.json"). Supports standard glob syntax with *, **, and ? wildcards'),
1008
- path: zod.z.string().optional().describe("Optional relative directory path within the project to limit the search scope. If not provided, searches from the project root"),
1009
- sortByMtime: zod.z.boolean().optional().describe("Sort results by modification time (default: false \u2014 faster without I/O)")
1526
+ path: zod.z.string().optional().describe("Optional directory path to limit the search scope. If not provided, searches from the project root")
1010
1527
  });
1011
1528
  var RESULT_LIMIT = 100;
1012
1529
  var MTIME_BATCH_SIZE = 50;
@@ -1025,7 +1542,7 @@ async function getMtimesBatched2(files) {
1025
1542
  return results;
1026
1543
  }
1027
1544
  var globTool = async function(input, projectCwd) {
1028
- const { pattern, path: inputPath, sortByMtime = false } = input;
1545
+ const { pattern, path: inputPath } = input;
1029
1546
  if (!pattern) {
1030
1547
  return {
1031
1548
  success: false,
@@ -1036,7 +1553,7 @@ var globTool = async function(input, projectCwd) {
1036
1553
  try {
1037
1554
  const basePath = projectCwd || process.cwd();
1038
1555
  const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
1039
- if (!fs10__default.default.existsSync(searchPath)) {
1556
+ if (!fs11__default.default.existsSync(searchPath)) {
1040
1557
  return {
1041
1558
  success: false,
1042
1559
  message: `Directory not found: ${searchPath}`,
@@ -1072,7 +1589,7 @@ var globTool = async function(input, projectCwd) {
1072
1589
  files.push(match);
1073
1590
  }
1074
1591
  let sortedFiles;
1075
- if (sortByMtime && files.length > 0) {
1592
+ if (files.length > 0) {
1076
1593
  const filesWithMtime = await getMtimesBatched2(files);
1077
1594
  filesWithMtime.sort((a, b) => b.mtime - a.mtime);
1078
1595
  sortedFiles = filesWithMtime.map((f) => f.path);
@@ -1109,7 +1626,7 @@ var globTool = async function(input, projectCwd) {
1109
1626
  };
1110
1627
  }
1111
1628
  };
1112
- var IGNORE_PATTERNS = [
1629
+ var DEFAULT_IGNORE_PATTERNS = [
1113
1630
  "node_modules",
1114
1631
  "__pycache__",
1115
1632
  ".git",
@@ -1141,18 +1658,19 @@ var IGNORE_PATTERNS = [
1141
1658
  var RESULT_LIMIT2 = 500;
1142
1659
  var MTIME_BATCH_SIZE2 = 50;
1143
1660
  zod.z.object({
1144
- path: zod.z.string().optional().describe("Relative path to the directory to list"),
1661
+ path: zod.z.string().optional().describe("Path to the directory to list"),
1145
1662
  recursive: zod.z.boolean().optional().describe("Whether to list files recursively (default: true)"),
1146
1663
  maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: 3)"),
1147
1664
  pattern: zod.z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
1148
1665
  showHidden: zod.z.boolean().optional().describe("Whether to show hidden files (default: false)"),
1149
- includeMetadata: zod.z.boolean().optional().describe("Whether to fetch file metadata like mtime (default: false \u2014 faster without I/O)")
1666
+ includeMetadata: zod.z.boolean().optional().describe("Whether to fetch file metadata like mtime (default: false -- faster without I/O)"),
1667
+ ignore: zod.z.array(zod.z.string()).optional().describe("Additional glob patterns to ignore (added to default ignore list)")
1150
1668
  });
1151
- function shouldIgnore(name, showHidden) {
1669
+ function shouldIgnore(name, showHidden, ignoreSet) {
1152
1670
  if (!showHidden && name.startsWith(".") && name !== ".") {
1153
1671
  return true;
1154
1672
  }
1155
- return IGNORE_PATTERNS.includes(name);
1673
+ return ignoreSet.has(name);
1156
1674
  }
1157
1675
  function matchPattern(name, pattern) {
1158
1676
  if (!pattern) return true;
@@ -1218,7 +1736,8 @@ var list = async function(input, projectCwd) {
1218
1736
  maxDepth = 3,
1219
1737
  pattern,
1220
1738
  showHidden = false,
1221
- includeMetadata = false
1739
+ includeMetadata = false,
1740
+ ignore: extraIgnore
1222
1741
  } = input;
1223
1742
  if (maxDepth !== void 0 && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
1224
1743
  return {
@@ -1240,14 +1759,14 @@ var list = async function(input, projectCwd) {
1240
1759
  };
1241
1760
  }
1242
1761
  }
1243
- if (!fs10__default.default.existsSync(absolutePath)) {
1762
+ if (!fs11__default.default.existsSync(absolutePath)) {
1244
1763
  return {
1245
1764
  success: false,
1246
1765
  message: `Directory not found: ${absolutePath}`,
1247
1766
  error: "DIR_NOT_FOUND"
1248
1767
  };
1249
1768
  }
1250
- const stats = fs10__default.default.statSync(absolutePath);
1769
+ const stats = fs11__default.default.statSync(absolutePath);
1251
1770
  if (!stats.isDirectory()) {
1252
1771
  return {
1253
1772
  success: false,
@@ -1255,6 +1774,12 @@ var list = async function(input, projectCwd) {
1255
1774
  error: "NOT_A_DIRECTORY"
1256
1775
  };
1257
1776
  }
1777
+ const ignoreSet = new Set(DEFAULT_IGNORE_PATTERNS);
1778
+ if (extraIgnore && extraIgnore.length > 0) {
1779
+ for (const pat of extraIgnore) {
1780
+ ignoreSet.add(pat);
1781
+ }
1782
+ }
1258
1783
  const collected = [];
1259
1784
  let truncated = false;
1260
1785
  const walk = async (currentDir, depth) => {
@@ -1264,7 +1789,7 @@ var list = async function(input, projectCwd) {
1264
1789
  }
1265
1790
  let entries;
1266
1791
  try {
1267
- entries = fs10__default.default.readdirSync(currentDir, { withFileTypes: true });
1792
+ entries = fs11__default.default.readdirSync(currentDir, { withFileTypes: true });
1268
1793
  } catch {
1269
1794
  return;
1270
1795
  }
@@ -1279,7 +1804,7 @@ var list = async function(input, projectCwd) {
1279
1804
  truncated = true;
1280
1805
  break;
1281
1806
  }
1282
- if (shouldIgnore(entry.name, showHidden)) {
1807
+ if (shouldIgnore(entry.name, showHidden, ignoreSet)) {
1283
1808
  continue;
1284
1809
  }
1285
1810
  const entryAbsolutePath = path10__default.default.join(currentDir, entry.name);
@@ -1379,10 +1904,10 @@ var CREDENTIALS_DIR = path10__default.default.join(os3__default.default.homedir(
1379
1904
  var CREDENTIALS_PATH = path10__default.default.join(CREDENTIALS_DIR, "credentials.json");
1380
1905
  function isAuthenticated() {
1381
1906
  try {
1382
- if (!fs10__default.default.existsSync(CREDENTIALS_PATH)) {
1907
+ if (!fs11__default.default.existsSync(CREDENTIALS_PATH)) {
1383
1908
  return false;
1384
1909
  }
1385
- const raw = fs10__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1910
+ const raw = fs11__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1386
1911
  const data = JSON.parse(raw);
1387
1912
  return Boolean(data && data.access_token);
1388
1913
  } catch {
@@ -1391,10 +1916,10 @@ function isAuthenticated() {
1391
1916
  }
1392
1917
  function saveTokens(tokens) {
1393
1918
  try {
1394
- if (!fs10__default.default.existsSync(CREDENTIALS_DIR)) {
1395
- fs10__default.default.mkdirSync(CREDENTIALS_DIR, { recursive: true });
1919
+ if (!fs11__default.default.existsSync(CREDENTIALS_DIR)) {
1920
+ fs11__default.default.mkdirSync(CREDENTIALS_DIR, { recursive: true });
1396
1921
  }
1397
- fs10__default.default.writeFileSync(
1922
+ fs11__default.default.writeFileSync(
1398
1923
  CREDENTIALS_PATH,
1399
1924
  JSON.stringify(tokens, null, 2),
1400
1925
  "utf8"
@@ -1405,18 +1930,18 @@ function saveTokens(tokens) {
1405
1930
  }
1406
1931
  function logout() {
1407
1932
  try {
1408
- if (fs10__default.default.existsSync(CREDENTIALS_PATH)) {
1409
- fs10__default.default.unlinkSync(CREDENTIALS_PATH);
1933
+ if (fs11__default.default.existsSync(CREDENTIALS_PATH)) {
1934
+ fs11__default.default.unlinkSync(CREDENTIALS_PATH);
1410
1935
  }
1411
1936
  } catch (error) {
1412
1937
  console.error(pc5__default.default.red("Failed to logout"), error);
1413
1938
  }
1414
1939
  }
1415
1940
  function getTokens() {
1416
- if (!fs10__default.default.existsSync(CREDENTIALS_PATH)) {
1941
+ if (!fs11__default.default.existsSync(CREDENTIALS_PATH)) {
1417
1942
  return null;
1418
1943
  }
1419
- const raw = fs10__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1944
+ const raw = fs11__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1420
1945
  const data = JSON.parse(raw);
1421
1946
  return data;
1422
1947
  }
@@ -1515,10 +2040,10 @@ async function login() {
1515
2040
  }
1516
2041
  var getUserId = () => {
1517
2042
  try {
1518
- if (!fs10__default.default.existsSync(CREDENTIALS_PATH)) {
2043
+ if (!fs11__default.default.existsSync(CREDENTIALS_PATH)) {
1519
2044
  return;
1520
2045
  }
1521
- const raw = fs10__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
2046
+ const raw = fs11__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1522
2047
  const data = JSON.parse(raw);
1523
2048
  const fromUserObject = data.user?.id;
1524
2049
  const fromTopLevel = data.sub ?? data.user_id;
@@ -1544,26 +2069,20 @@ var getUserId = () => {
1544
2069
  }
1545
2070
  };
1546
2071
  var ExplanationSchema = zod.z.object({
1547
- explanation: zod.z.string().describe("One sentence explanation as to why this tool is being used")
2072
+ description: zod.z.string().describe(
2073
+ "Clear, concise description of what this command does in 5-10 words. Examples:\nInput: ls\nOutput: Lists files in current directory\n\nInput: git status\nOutput: Shows working tree status\n\nInput: npm install\nOutput: Installs package dependencies\n\nInput: mkdir foo\nOutput: Creates directory 'foo'"
2074
+ )
1548
2075
  });
1549
2076
  var BLOCKED_PATTERNS = [
1550
- // rm with -r/-f flags (combined or separate) targeting /, ~, $HOME, or *
1551
2077
  /\brm\s+(-\w+\s+)*(\/ |\/\s*$|~|\/\*|\*)/,
1552
- // Disk-wiping commands
1553
2078
  /\bdd\s+.*of=\/dev\//,
1554
2079
  /\bmkfs\b/,
1555
- // Fork bomb
1556
2080
  /:\(\)\{.*\|.*&\}\s*;?\s*:/,
1557
- // Recursive chmod/chown on root
1558
2081
  /\bchmod\s+.*-R.*\s+\/\s*$/,
1559
2082
  /\bchown\s+.*-R.*\s+\/\s*$/,
1560
- // Pipe from remote URL directly into shell
1561
2083
  /\b(curl|wget)\s+.*\|\s*(ba)?sh/,
1562
- // Move root filesystem
1563
2084
  /\bmv\s+(\/|\*)\s/,
1564
- // Write random data to disk device
1565
2085
  /\bcat\s+\/dev\/(u?random|zero)\s*>\s*\/dev\//,
1566
- // Windows-specific destructive
1567
2086
  /\bformat\s+[A-Z]:/i,
1568
2087
  /\bdiskpart\b/i,
1569
2088
  /\bcipher\s+\/w:/i
@@ -1574,6 +2093,7 @@ var DANGEROUS_FLAGS = [
1574
2093
  /\bgit\s+push\s+-f\b/
1575
2094
  ];
1576
2095
  var MAX_OUTPUT_SIZE = 1 * 1024 * 1024;
2096
+ var DEFAULT_TIMEOUT = 12e4;
1577
2097
  function evaluateCommandSafety(command) {
1578
2098
  const trimmed = command.trim();
1579
2099
  for (const pattern of BLOCKED_PATTERNS) {
@@ -1589,8 +2109,10 @@ function evaluateCommandSafety(command) {
1589
2109
  return { safe: true };
1590
2110
  }
1591
2111
  zod.z.object({
1592
- command: zod.z.string().describe("The terminal command to execute (e.g., 'ls -la', 'pwd', 'echo $HOME')"),
1593
- is_background: zod.z.boolean().describe("Whether the command should be run in the background")
2112
+ command: zod.z.string().describe("The terminal command to execute"),
2113
+ is_background: zod.z.boolean().optional().default(false).describe("Whether the command should be run in the background"),
2114
+ timeout: zod.z.number().optional().describe("Optional timeout in milliseconds. If not specified, commands will time out after 120000ms (2 minutes)."),
2115
+ workdir: zod.z.string().optional().describe("The working directory to run the command in. Defaults to the project directory. Use this instead of 'cd' commands.")
1594
2116
  }).merge(ExplanationSchema);
1595
2117
  var runSecureTerminalCommand = async (command, timeout, cwd) => {
1596
2118
  try {
@@ -1647,7 +2169,7 @@ var runSecureTerminalCommand = async (command, timeout, cwd) => {
1647
2169
  };
1648
2170
  }
1649
2171
  };
1650
- var runTerminalCommand = async (input, projectCwd) => {
2172
+ var bashTool = async (input, projectCwd) => {
1651
2173
  try {
1652
2174
  const safety = evaluateCommandSafety(input.command);
1653
2175
  if (!safety.safe) {
@@ -1658,9 +2180,18 @@ var runTerminalCommand = async (input, projectCwd) => {
1658
2180
  error: "BLOCKED_COMMAND"
1659
2181
  };
1660
2182
  }
2183
+ if (input.timeout !== void 0 && input.timeout < 0) {
2184
+ return {
2185
+ success: false,
2186
+ message: `Invalid timeout value: ${input.timeout}. Timeout must be a positive number.`,
2187
+ error: "INVALID_TIMEOUT"
2188
+ };
2189
+ }
2190
+ const cwd = input.workdir || projectCwd || process.cwd();
2191
+ const timeout = input.timeout ?? DEFAULT_TIMEOUT;
1661
2192
  if (input?.is_background) {
1662
2193
  const proc = Bun.spawn(["sh", "-c", input.command], {
1663
- cwd: projectCwd || process.cwd(),
2194
+ cwd,
1664
2195
  stdout: "ignore",
1665
2196
  stderr: "ignore"
1666
2197
  });
@@ -1674,8 +2205,8 @@ var runTerminalCommand = async (input, projectCwd) => {
1674
2205
  } else {
1675
2206
  const result = await runSecureTerminalCommand(
1676
2207
  input.command,
1677
- 3e4,
1678
- projectCwd
2208
+ timeout,
2209
+ cwd
1679
2210
  );
1680
2211
  if (result?.error && !result?.exitCode) {
1681
2212
  return result;
@@ -1706,14 +2237,14 @@ var ProjectRegistry = class {
1706
2237
  }
1707
2238
  load() {
1708
2239
  try {
1709
- if (fs10__default.default.existsSync(REGISTRY_FILE)) {
1710
- const data = fs10__default.default.readFileSync(REGISTRY_FILE, "utf8");
2240
+ if (fs11__default.default.existsSync(REGISTRY_FILE)) {
2241
+ const data = fs11__default.default.readFileSync(REGISTRY_FILE, "utf8");
1711
2242
  const parsed = JSON.parse(data);
1712
2243
  if (!Array.isArray(parsed)) {
1713
2244
  console.error("Invalid project registry format: expected array, got", typeof parsed);
1714
2245
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
1715
- fs10__default.default.copyFileSync(REGISTRY_FILE, backupFile);
1716
- fs10__default.default.unlinkSync(REGISTRY_FILE);
2246
+ fs11__default.default.copyFileSync(REGISTRY_FILE, backupFile);
2247
+ fs11__default.default.unlinkSync(REGISTRY_FILE);
1717
2248
  return;
1718
2249
  }
1719
2250
  const projects = parsed;
@@ -1726,11 +2257,11 @@ var ProjectRegistry = class {
1726
2257
  }
1727
2258
  } catch (error) {
1728
2259
  console.error("Failed to load project registry:", error);
1729
- if (fs10__default.default.existsSync(REGISTRY_FILE)) {
2260
+ if (fs11__default.default.existsSync(REGISTRY_FILE)) {
1730
2261
  try {
1731
2262
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
1732
- fs10__default.default.copyFileSync(REGISTRY_FILE, backupFile);
1733
- fs10__default.default.unlinkSync(REGISTRY_FILE);
2263
+ fs11__default.default.copyFileSync(REGISTRY_FILE, backupFile);
2264
+ fs11__default.default.unlinkSync(REGISTRY_FILE);
1734
2265
  console.log("Corrupted registry file backed up and removed. Starting fresh.");
1735
2266
  } catch (backupError) {
1736
2267
  }
@@ -1739,11 +2270,11 @@ var ProjectRegistry = class {
1739
2270
  }
1740
2271
  save() {
1741
2272
  try {
1742
- if (!fs10__default.default.existsSync(AMA_DIR)) {
1743
- fs10__default.default.mkdirSync(AMA_DIR, { recursive: true });
2273
+ if (!fs11__default.default.existsSync(AMA_DIR)) {
2274
+ fs11__default.default.mkdirSync(AMA_DIR, { recursive: true });
1744
2275
  }
1745
2276
  const projects = Array.from(this.projects.values());
1746
- fs10__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
2277
+ fs11__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
1747
2278
  } catch (error) {
1748
2279
  console.error("Failed to save project registry:", error);
1749
2280
  }
@@ -1783,7 +2314,7 @@ var ProjectRegistry = class {
1783
2314
  var projectRegistry = new ProjectRegistry();
1784
2315
  var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local", ".output", ".turbo", ".vercel", ".next", ".tanstack", ".nitro", ".wrangler", ".alchemy", ".coverage", ".nyc_output", ".cache", "tmp", "temp", ".idea", ".vscode", ".zig-cache", "zig-out", ".coverage", "coverage", "logs", ".venv", "venv", "env", ".next", ".turbo", ".vercel", ".output", ".tanstack", ".nitro", ".wrangler", ".alchemy", ".coverage", ".nyc_output", ".cache", "tmp", "temp", ".idea", ".vscode", ".zig-cache", "zig-out", ".coverage", "coverage", "logs", ".venv", "venv", "env"];
1785
2316
  var getContext = (dir, base = dir, allFiles = []) => {
1786
- const filePath = fs10.readdirSync(dir, { withFileTypes: true });
2317
+ const filePath = fs11.readdirSync(dir, { withFileTypes: true });
1787
2318
  for (const file of filePath) {
1788
2319
  if (ignoreFiles.includes(file.name)) continue;
1789
2320
  const fullPath = path10__default.default.join(dir, file.name);
@@ -1812,7 +2343,7 @@ function getWorkspaceStoragePath(ide) {
1812
2343
  } else {
1813
2344
  const capitalizedPath = path10__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
1814
2345
  const lowercasePath = path10__default.default.join(HOME, ".config", appNameLower, "User", "workspaceStorage");
1815
- if (fs10__default.default.existsSync(capitalizedPath)) {
2346
+ if (fs11__default.default.existsSync(capitalizedPath)) {
1816
2347
  return capitalizedPath;
1817
2348
  }
1818
2349
  return lowercasePath;
@@ -1821,16 +2352,16 @@ function getWorkspaceStoragePath(ide) {
1821
2352
  function scanWorkspaceStorage(ide) {
1822
2353
  const projects = [];
1823
2354
  const storagePath = getWorkspaceStoragePath(ide);
1824
- if (!fs10__default.default.existsSync(storagePath)) {
2355
+ if (!fs11__default.default.existsSync(storagePath)) {
1825
2356
  return projects;
1826
2357
  }
1827
2358
  try {
1828
- const workspaces = fs10__default.default.readdirSync(storagePath);
2359
+ const workspaces = fs11__default.default.readdirSync(storagePath);
1829
2360
  for (const workspace of workspaces) {
1830
2361
  const workspaceJsonPath = path10__default.default.join(storagePath, workspace, "workspace.json");
1831
- if (fs10__default.default.existsSync(workspaceJsonPath)) {
2362
+ if (fs11__default.default.existsSync(workspaceJsonPath)) {
1832
2363
  try {
1833
- const content = fs10__default.default.readFileSync(workspaceJsonPath, "utf-8");
2364
+ const content = fs11__default.default.readFileSync(workspaceJsonPath, "utf-8");
1834
2365
  const data = JSON.parse(content);
1835
2366
  if (data.folder && typeof data.folder === "string") {
1836
2367
  let projectPath = data.folder;
@@ -1838,7 +2369,7 @@ function scanWorkspaceStorage(ide) {
1838
2369
  projectPath = projectPath.replace("file://", "");
1839
2370
  projectPath = decodeURIComponent(projectPath);
1840
2371
  }
1841
- if (fs10__default.default.existsSync(projectPath) && fs10__default.default.statSync(projectPath).isDirectory()) {
2372
+ if (fs11__default.default.existsSync(projectPath) && fs11__default.default.statSync(projectPath).isDirectory()) {
1842
2373
  projects.push({
1843
2374
  name: path10__default.default.basename(projectPath),
1844
2375
  path: projectPath,
@@ -1862,11 +2393,11 @@ var scanIdeProjects = async () => {
1862
2393
  const seenPaths = /* @__PURE__ */ new Set();
1863
2394
  const addProject = (projectPath, ide) => {
1864
2395
  try {
1865
- const resolvedPath = fs10__default.default.realpathSync(projectPath);
1866
- if (fs10__default.default.existsSync(resolvedPath) && fs10__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
2396
+ const resolvedPath = fs11__default.default.realpathSync(projectPath);
2397
+ if (fs11__default.default.existsSync(resolvedPath) && fs11__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1867
2398
  const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
1868
2399
  try {
1869
- return fs10__default.default.realpathSync(ideDir) === resolvedPath;
2400
+ return fs11__default.default.realpathSync(ideDir) === resolvedPath;
1870
2401
  } catch {
1871
2402
  return false;
1872
2403
  }
@@ -1893,30 +2424,30 @@ var scanIdeProjects = async () => {
1893
2424
  }
1894
2425
  for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
1895
2426
  if (ide === "cursor" || ide === "vscode") continue;
1896
- if (fs10__default.default.existsSync(dirPath)) {
1897
- const projects = fs10__default.default.readdirSync(dirPath);
2427
+ if (fs11__default.default.existsSync(dirPath)) {
2428
+ const projects = fs11__default.default.readdirSync(dirPath);
1898
2429
  projects.forEach((project) => {
1899
2430
  const projectPath = path10__default.default.join(dirPath, project);
1900
2431
  try {
1901
- const stats = fs10__default.default.lstatSync(projectPath);
2432
+ const stats = fs11__default.default.lstatSync(projectPath);
1902
2433
  let actualPath = null;
1903
2434
  if (stats.isSymbolicLink()) {
1904
- actualPath = fs10__default.default.realpathSync(projectPath);
2435
+ actualPath = fs11__default.default.realpathSync(projectPath);
1905
2436
  } else if (stats.isFile()) {
1906
2437
  try {
1907
- let content = fs10__default.default.readFileSync(projectPath, "utf-8").trim();
2438
+ let content = fs11__default.default.readFileSync(projectPath, "utf-8").trim();
1908
2439
  if (content.startsWith("~/") || content === "~") {
1909
2440
  content = content.replace(/^~/, HOME);
1910
2441
  }
1911
2442
  const resolvedContent = path10__default.default.isAbsolute(content) ? content : path10__default.default.resolve(path10__default.default.dirname(projectPath), content);
1912
- if (fs10__default.default.existsSync(resolvedContent) && fs10__default.default.statSync(resolvedContent).isDirectory()) {
1913
- actualPath = fs10__default.default.realpathSync(resolvedContent);
2443
+ if (fs11__default.default.existsSync(resolvedContent) && fs11__default.default.statSync(resolvedContent).isDirectory()) {
2444
+ actualPath = fs11__default.default.realpathSync(resolvedContent);
1914
2445
  }
1915
2446
  } catch {
1916
2447
  return;
1917
2448
  }
1918
2449
  } else if (stats.isDirectory()) {
1919
- actualPath = fs10__default.default.realpathSync(projectPath);
2450
+ actualPath = fs11__default.default.realpathSync(projectPath);
1920
2451
  }
1921
2452
  if (actualPath) {
1922
2453
  addProject(actualPath, ide);
@@ -1974,8 +2505,8 @@ var Snapshot;
1974
2505
  const worktree = project.cwd;
1975
2506
  const git = gitdir(projectId);
1976
2507
  try {
1977
- await fs8__default.default.mkdir(git, { recursive: true });
1978
- const gitExists = await fs8__default.default.access(path10__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
2508
+ await fsp__namespace.default.mkdir(git, { recursive: true });
2509
+ const gitExists = await fsp__namespace.default.access(path10__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
1979
2510
  if (!gitExists) {
1980
2511
  await runGit(`git init`, {
1981
2512
  env: { GIT_DIR: git, GIT_WORK_TREE: worktree }
@@ -2060,7 +2591,7 @@ var Snapshot;
2060
2591
  for (const file of newFiles) {
2061
2592
  const fullPath = path10__default.default.join(worktree, file);
2062
2593
  try {
2063
- await fs8__default.default.unlink(fullPath);
2594
+ await fsp__namespace.default.unlink(fullPath);
2064
2595
  log.info("deleted newly created file", { file: fullPath });
2065
2596
  } catch {
2066
2597
  }
@@ -2097,7 +2628,7 @@ var Snapshot;
2097
2628
  log.info("file existed in snapshot but checkout failed, keeping", { file });
2098
2629
  } else {
2099
2630
  log.info("file did not exist in snapshot, deleting", { file });
2100
- await fs8__default.default.unlink(file).catch(() => {
2631
+ await fsp__namespace.default.unlink(file).catch(() => {
2101
2632
  });
2102
2633
  }
2103
2634
  }
@@ -2151,10 +2682,10 @@ var Snapshot;
2151
2682
  const lines = numstatResult.stdout.trim().split("\n").filter(Boolean);
2152
2683
  for (const line of lines) {
2153
2684
  const [additions, deletions, file] = line.split(" ");
2154
- const isBinaryFile = additions === "-" && deletions === "-";
2685
+ const isBinaryFile2 = additions === "-" && deletions === "-";
2155
2686
  let before = "";
2156
2687
  let after = "";
2157
- if (!isBinaryFile) {
2688
+ if (!isBinaryFile2) {
2158
2689
  const beforeResult = await runGit(
2159
2690
  `git -c core.autocrlf=false --git-dir "${git}" --work-tree "${worktree}" show ${from}:${file}`,
2160
2691
  { cwd: worktree }
@@ -2279,19 +2810,19 @@ var HTML_ERROR = (error) => `<!doctype html>
2279
2810
  </body>
2280
2811
  </html>`;
2281
2812
  function ensureCredentialsDir() {
2282
- if (!fs10__default.default.existsSync(AMA_DIR)) {
2283
- fs10__default.default.mkdirSync(AMA_DIR, { recursive: true });
2813
+ if (!fs11__default.default.existsSync(AMA_DIR)) {
2814
+ fs11__default.default.mkdirSync(AMA_DIR, { recursive: true });
2284
2815
  }
2285
2816
  }
2286
2817
  function saveCredentials(credentials) {
2287
2818
  ensureCredentialsDir();
2288
- fs10__default.default.writeFileSync(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2), "utf8");
2819
+ fs11__default.default.writeFileSync(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2), "utf8");
2289
2820
  }
2290
2821
  function readCredentials() {
2291
- if (!fs10__default.default.existsSync(CREDENTIALS_PATH2)) {
2822
+ if (!fs11__default.default.existsSync(CREDENTIALS_PATH2)) {
2292
2823
  return null;
2293
2824
  }
2294
- const raw = fs10__default.default.readFileSync(CREDENTIALS_PATH2, "utf8");
2825
+ const raw = fs11__default.default.readFileSync(CREDENTIALS_PATH2, "utf8");
2295
2826
  const parsed = JSON.parse(raw);
2296
2827
  if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string" || typeof parsed.accountId !== "string" || typeof parsed.expiresAt !== "number") {
2297
2828
  return null;
@@ -2567,8 +3098,8 @@ async function getCodexStatus() {
2567
3098
  async function codexLogout() {
2568
3099
  pendingOAuth = void 0;
2569
3100
  stopOAuthServer();
2570
- if (fs10__default.default.existsSync(CREDENTIALS_PATH2)) {
2571
- fs10__default.default.unlinkSync(CREDENTIALS_PATH2);
3101
+ if (fs11__default.default.existsSync(CREDENTIALS_PATH2)) {
3102
+ fs11__default.default.unlinkSync(CREDENTIALS_PATH2);
2572
3103
  }
2573
3104
  }
2574
3105
 
@@ -2855,18 +3386,21 @@ var toolCallSchema = zod.z.object({
2855
3386
  parameters: zod.z.record(zod.z.string(), zod.z.unknown()).describe("Parameters for the tool")
2856
3387
  });
2857
3388
  zod.z.object({
2858
- tool_calls: zod.z.array(toolCallSchema).min(1, "Provide at least one tool call").max(10, "Maximum of 10 tools allowed in batch").describe("Array of tool calls to execute in parallel")
3389
+ tool_calls: zod.z.array(toolCallSchema).min(1, "Provide at least one tool call").max(25, "Maximum of 25 tools allowed in batch").describe("Array of tool calls to execute in parallel")
2859
3390
  });
2860
3391
  var DISALLOWED_TOOLS = /* @__PURE__ */ new Set(["batch"]);
2861
3392
  var MAX_CONCURRENCY = 5;
2862
3393
  var PER_CALL_TIMEOUT = 3e4;
3394
+ var MAX_BATCH_SIZE = 25;
2863
3395
  var batchableToolExecutors = {
2864
3396
  deleteFile,
2865
3397
  grep: grepTool,
2866
3398
  glob: globTool,
2867
3399
  listDirectory: list,
2868
3400
  readFile: read_file,
2869
- runTerminalCommand
3401
+ bash: bashTool,
3402
+ stringReplace: apply_patch,
3403
+ editFile: editFiles
2870
3404
  };
2871
3405
  function withTimeout(promise, ms) {
2872
3406
  return new Promise((resolve, reject) => {
@@ -2897,8 +3431,8 @@ async function runWithConcurrencyLimit(tasks, limit) {
2897
3431
  }
2898
3432
  var batchTool = async function(input, projectCwd) {
2899
3433
  const { tool_calls } = input;
2900
- const callsToExecute = tool_calls.slice(0, 10);
2901
- const discardedCalls = tool_calls.slice(10);
3434
+ const callsToExecute = tool_calls.slice(0, MAX_BATCH_SIZE);
3435
+ const discardedCalls = tool_calls.slice(MAX_BATCH_SIZE);
2902
3436
  const executeCall = async (call) => {
2903
3437
  const start = performance.now();
2904
3438
  try {
@@ -2948,7 +3482,7 @@ var batchTool = async function(input, projectCwd) {
2948
3482
  results.push({
2949
3483
  tool: call.tool,
2950
3484
  success: false,
2951
- error: "Maximum of 10 tools allowed in batch",
3485
+ error: `Maximum of ${MAX_BATCH_SIZE} tools allowed in batch`,
2952
3486
  durationMs: 0
2953
3487
  });
2954
3488
  }
@@ -2983,7 +3517,7 @@ var TOOL_TIMEOUTS = {
2983
3517
  editFile: 15e3,
2984
3518
  deleteFile: 1e4,
2985
3519
  stringReplace: 15e3,
2986
- runTerminalCommand: 6e4,
3520
+ bash: 6e4,
2987
3521
  batch: 12e4
2988
3522
  };
2989
3523
  function getTimeoutForTool(tool) {
@@ -3096,7 +3630,7 @@ var toolExecutors = {
3096
3630
  listDirectory: list,
3097
3631
  readFile: read_file,
3098
3632
  stringReplace: apply_patch,
3099
- runTerminalCommand,
3633
+ bash: bashTool,
3100
3634
  batch: batchTool
3101
3635
  };
3102
3636
  function connectToServer(serverUrl = DEFAULT_SERVER_URL) {
@@ -3232,20 +3766,20 @@ function getCodeServerBin() {
3232
3766
  }
3233
3767
  function isCodeServerInstalled() {
3234
3768
  const binPath = getCodeServerBin();
3235
- return fs10__default.default.existsSync(binPath);
3769
+ return fs11__default.default.existsSync(binPath);
3236
3770
  }
3237
3771
  async function installCodeServer() {
3238
3772
  const { ext } = getPlatformInfo();
3239
3773
  const downloadUrl = getDownloadUrl();
3240
3774
  const tarballPath = path10__default.default.join(AMA_DIR, `code-server.${ext}`);
3241
- if (!fs10__default.default.existsSync(AMA_DIR)) {
3242
- fs10__default.default.mkdirSync(AMA_DIR, { recursive: true });
3775
+ if (!fs11__default.default.existsSync(AMA_DIR)) {
3776
+ fs11__default.default.mkdirSync(AMA_DIR, { recursive: true });
3243
3777
  }
3244
- if (!fs10__default.default.existsSync(CODE_DIR)) {
3245
- fs10__default.default.mkdirSync(CODE_DIR, { recursive: true });
3778
+ if (!fs11__default.default.existsSync(CODE_DIR)) {
3779
+ fs11__default.default.mkdirSync(CODE_DIR, { recursive: true });
3246
3780
  }
3247
- if (!fs10__default.default.existsSync(STORAGE_DIR)) {
3248
- fs10__default.default.mkdirSync(STORAGE_DIR, { recursive: true });
3781
+ if (!fs11__default.default.existsSync(STORAGE_DIR)) {
3782
+ fs11__default.default.mkdirSync(STORAGE_DIR, { recursive: true });
3249
3783
  }
3250
3784
  console.log(pc5__default.default.cyan(`downloading code-server v${CODE_SERVER_VERSION}...`));
3251
3785
  console.log(pc5__default.default.gray(downloadUrl));
@@ -3254,13 +3788,13 @@ async function installCodeServer() {
3254
3788
  throw new Error(`Failed to download code-server: ${response.statusText}`);
3255
3789
  }
3256
3790
  const buffer = await response.arrayBuffer();
3257
- await fs10__default.default.promises.writeFile(tarballPath, Buffer.from(buffer));
3791
+ await fs11__default.default.promises.writeFile(tarballPath, Buffer.from(buffer));
3258
3792
  console.log(pc5__default.default.cyan("Extracting code-server..."));
3259
3793
  await execAsync2(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
3260
- await fs10__default.default.promises.unlink(tarballPath);
3794
+ await fs11__default.default.promises.unlink(tarballPath);
3261
3795
  const binPath = getCodeServerBin();
3262
- if (fs10__default.default.existsSync(binPath)) {
3263
- await fs10__default.default.promises.chmod(binPath, 493);
3796
+ if (fs11__default.default.existsSync(binPath)) {
3797
+ await fs11__default.default.promises.chmod(binPath, 493);
3264
3798
  }
3265
3799
  console.log(pc5__default.default.green("code-server installed successfully"));
3266
3800
  }
@@ -3285,42 +3819,55 @@ async function killExistingCodeServer() {
3285
3819
  async function setupDefaultSettings() {
3286
3820
  const userDir = path10__default.default.join(STORAGE_DIR, "User");
3287
3821
  const settingsPath = path10__default.default.join(userDir, "settings.json");
3288
- if (!fs10__default.default.existsSync(userDir)) {
3289
- fs10__default.default.mkdirSync(userDir, { recursive: true });
3822
+ if (!fs11__default.default.existsSync(userDir)) {
3823
+ fs11__default.default.mkdirSync(userDir, { recursive: true });
3290
3824
  }
3291
3825
  const defaultSettings = {
3292
3826
  // Disable signature verification for Open VSX extensions
3293
3827
  "extensions.verifySignature": false,
3294
- // Theme settings
3295
- "workbench.colorTheme": "Min Dark",
3828
+ // Theme
3829
+ "workbench.colorTheme": "Vesper",
3296
3830
  "workbench.startupEditor": "none",
3297
- // Editor settings
3831
+ // Editor
3298
3832
  "editor.fontSize": 14,
3299
3833
  "editor.fontFamily": "'JetBrains Mono', 'Fira Code', Menlo, Monaco, 'Courier New', monospace",
3300
3834
  "editor.minimap.enabled": false,
3301
3835
  "editor.wordWrap": "on",
3302
- // UI settings
3303
- "window.menuBarVisibility": "compact",
3304
- "workbench.activityBar.location": "top"
3836
+ "editor.renderLineHighlight": "gutter",
3837
+ "editor.scrollbar.vertical": "auto",
3838
+ "editor.scrollbar.horizontal": "auto",
3839
+ "editor.overviewRulerBorder": false,
3840
+ "editor.hideCursorInOverviewRuler": true,
3841
+ "editor.guides.indentation": false,
3842
+ // Minimal UI
3843
+ "window.menuBarVisibility": "hidden",
3844
+ "window.commandCenter": false,
3845
+ "workbench.layoutControl.enabled": false,
3846
+ "workbench.activityBar.location": "default",
3847
+ "workbench.sideBar.location": "right",
3848
+ "workbench.editor.showTabs": "single",
3849
+ "workbench.statusBar.visible": false,
3850
+ "breadcrumbs.enabled": false
3305
3851
  };
3306
3852
  let existingSettings = {};
3307
- if (fs10__default.default.existsSync(settingsPath)) {
3853
+ if (fs11__default.default.existsSync(settingsPath)) {
3308
3854
  try {
3309
- const content = await fs10__default.default.promises.readFile(settingsPath, "utf-8");
3855
+ const content = await fs11__default.default.promises.readFile(settingsPath, "utf-8");
3310
3856
  existingSettings = JSON.parse(content);
3311
3857
  } catch {
3312
3858
  }
3313
3859
  }
3314
3860
  const mergedSettings = { ...defaultSettings, ...existingSettings };
3315
- mergedSettings["workbench.colorTheme"] = "Min Dark";
3861
+ mergedSettings["workbench.colorTheme"] = "Vesper";
3316
3862
  mergedSettings["extensions.verifySignature"] = false;
3317
- await fs10__default.default.promises.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
3863
+ await fs11__default.default.promises.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
3318
3864
  console.log(pc5__default.default.green("ama code-server settings configured"));
3319
3865
  }
3320
3866
  async function installExtensions() {
3321
3867
  const binPath = getCodeServerBin();
3322
3868
  const extensions = [
3323
- "castrogusttavo.min-theme"
3869
+ "raunofreiberg.vesper",
3870
+ "ziterz.codesandbox-black-theme"
3324
3871
  ];
3325
3872
  for (const ext of extensions) {
3326
3873
  try {
@@ -3335,7 +3882,7 @@ async function installExtensions() {
3335
3882
  async function startCodeServer(cwd) {
3336
3883
  const binPath = getCodeServerBin();
3337
3884
  const workDir = cwd || process.cwd();
3338
- if (!fs10__default.default.existsSync(binPath)) {
3885
+ if (!fs11__default.default.existsSync(binPath)) {
3339
3886
  throw new Error("ama code-server is not installed. Run installCodeServer() first.");
3340
3887
  }
3341
3888
  await killExistingCodeServer();
@@ -3343,12 +3890,12 @@ async function startCodeServer(cwd) {
3343
3890
  await installExtensions();
3344
3891
  const workspaceStoragePath = path10__default.default.join(STORAGE_DIR, "User", "workspaceStorage");
3345
3892
  try {
3346
- if (fs10__default.default.existsSync(workspaceStoragePath)) {
3347
- await fs10__default.default.promises.rm(workspaceStoragePath, { recursive: true, force: true });
3893
+ if (fs11__default.default.existsSync(workspaceStoragePath)) {
3894
+ await fs11__default.default.promises.rm(workspaceStoragePath, { recursive: true, force: true });
3348
3895
  }
3349
3896
  const stateDbPath = path10__default.default.join(STORAGE_DIR, "User", "globalStorage", "state.vscdb");
3350
- if (fs10__default.default.existsSync(stateDbPath)) {
3351
- await fs10__default.default.promises.unlink(stateDbPath);
3897
+ if (fs11__default.default.existsSync(stateDbPath)) {
3898
+ await fs11__default.default.promises.unlink(stateDbPath);
3352
3899
  }
3353
3900
  } catch {
3354
3901
  }
@@ -3379,11 +3926,11 @@ var __dirname$1 = path10.dirname(__filename$1);
3379
3926
  var DAEMON_PID_FILE = path10__default.default.join(AMA_DIR, "daemon.pid");
3380
3927
  var DAEMON_LOG_FILE = path10__default.default.join(AMA_DIR, "daemon.log");
3381
3928
  function isDaemonRunning() {
3382
- if (!fs10__default.default.existsSync(DAEMON_PID_FILE)) {
3929
+ if (!fs11__default.default.existsSync(DAEMON_PID_FILE)) {
3383
3930
  return false;
3384
3931
  }
3385
3932
  try {
3386
- const pid = Number(fs10__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
3933
+ const pid = Number(fs11__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
3387
3934
  process.kill(pid, 0);
3388
3935
  return true;
3389
3936
  } catch {
@@ -3391,30 +3938,30 @@ function isDaemonRunning() {
3391
3938
  }
3392
3939
  }
3393
3940
  function stopDaemon() {
3394
- if (!fs10__default.default.existsSync(DAEMON_PID_FILE)) {
3941
+ if (!fs11__default.default.existsSync(DAEMON_PID_FILE)) {
3395
3942
  return false;
3396
3943
  }
3397
3944
  try {
3398
- const pid = Number(fs10__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
3945
+ const pid = Number(fs11__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
3399
3946
  process.kill(pid, "SIGTERM");
3400
- fs10__default.default.unlinkSync(DAEMON_PID_FILE);
3947
+ fs11__default.default.unlinkSync(DAEMON_PID_FILE);
3401
3948
  return true;
3402
3949
  } catch (error) {
3403
3950
  return false;
3404
3951
  }
3405
3952
  }
3406
3953
  function startDaemon() {
3407
- if (!fs10__default.default.existsSync(AMA_DIR)) {
3408
- fs10__default.default.mkdirSync(AMA_DIR, { recursive: true });
3954
+ if (!fs11__default.default.existsSync(AMA_DIR)) {
3955
+ fs11__default.default.mkdirSync(AMA_DIR, { recursive: true });
3409
3956
  }
3410
3957
  if (isDaemonRunning()) {
3411
3958
  stopDaemon();
3412
3959
  }
3413
3960
  const daemonScript = path10__default.default.join(__dirname$1, "lib", "daemon-entry.js");
3414
- if (!fs10__default.default.existsSync(daemonScript)) {
3961
+ if (!fs11__default.default.existsSync(daemonScript)) {
3415
3962
  throw new Error(`Daemon entry script not found at: ${daemonScript}. Please rebuild the project.`);
3416
3963
  }
3417
- const logFd = fs10__default.default.openSync(DAEMON_LOG_FILE, "a");
3964
+ const logFd = fs11__default.default.openSync(DAEMON_LOG_FILE, "a");
3418
3965
  const daemon = child_process.spawn(process.execPath, [daemonScript], {
3419
3966
  detached: true,
3420
3967
  stdio: ["ignore", logFd, logFd],
@@ -3422,20 +3969,20 @@ function startDaemon() {
3422
3969
  cwd: process.cwd()
3423
3970
  });
3424
3971
  daemon.unref();
3425
- fs10__default.default.writeFileSync(DAEMON_PID_FILE, String(daemon.pid));
3426
- fs10__default.default.closeSync(logFd);
3972
+ fs11__default.default.writeFileSync(DAEMON_PID_FILE, String(daemon.pid));
3973
+ fs11__default.default.closeSync(logFd);
3427
3974
  }
3428
3975
  function getDaemonPid() {
3429
- if (!fs10__default.default.existsSync(DAEMON_PID_FILE)) {
3976
+ if (!fs11__default.default.existsSync(DAEMON_PID_FILE)) {
3430
3977
  return null;
3431
3978
  }
3432
3979
  try {
3433
- return Number(fs10__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
3980
+ return Number(fs11__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
3434
3981
  } catch {
3435
3982
  return null;
3436
3983
  }
3437
3984
  }
3438
- var VERSION = "0.0.19";
3985
+ var VERSION = "0.0.21";
3439
3986
  var PROJECT_DIR = process.cwd();
3440
3987
  var LOGO = `
3441
3988
  __ _ _ __ ___ __ _
@@ -3632,11 +4179,11 @@ if (args[0] === "update") {
3632
4179
  process.exit(1);
3633
4180
  }
3634
4181
  const resolvedPath = path10__default.default.resolve(projectPath);
3635
- if (!fs10__default.default.existsSync(resolvedPath)) {
4182
+ if (!fs11__default.default.existsSync(resolvedPath)) {
3636
4183
  console.error(pc5__default.default.red(`path does not exist: ${resolvedPath}`));
3637
4184
  process.exit(1);
3638
4185
  }
3639
- if (!fs10__default.default.statSync(resolvedPath).isDirectory()) {
4186
+ if (!fs11__default.default.statSync(resolvedPath).isDirectory()) {
3640
4187
  console.error(pc5__default.default.red(`path is not a directory: ${resolvedPath}`));
3641
4188
  process.exit(1);
3642
4189
  }