amai 0.0.10 → 0.0.11

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.
@@ -1,26 +1,26 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  'use strict';
3
3
 
4
4
  var WebSocket = require('ws');
5
5
  var zod = require('zod');
6
- var fs5 = require('fs/promises');
7
6
  var path10 = require('path');
8
- var fs6 = require('fs');
7
+ var fs8 = require('fs');
9
8
  var os3 = require('os');
10
- var child_process = require('child_process');
11
- var util = require('util');
9
+ var fs7 = require('fs/promises');
12
10
  var pc4 = require('picocolors');
13
11
  var hono = require('hono');
14
12
  var nodeServer = require('@hono/node-server');
15
13
  var cors = require('hono/cors');
14
+ var child_process = require('child_process');
15
+ var util = require('util');
16
16
 
17
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
18
 
19
19
  var WebSocket__default = /*#__PURE__*/_interopDefault(WebSocket);
20
- var fs5__default = /*#__PURE__*/_interopDefault(fs5);
21
20
  var path10__default = /*#__PURE__*/_interopDefault(path10);
22
- var fs6__default = /*#__PURE__*/_interopDefault(fs6);
21
+ var fs8__default = /*#__PURE__*/_interopDefault(fs8);
23
22
  var os3__default = /*#__PURE__*/_interopDefault(os3);
23
+ var fs7__default = /*#__PURE__*/_interopDefault(fs7);
24
24
  var pc4__default = /*#__PURE__*/_interopDefault(pc4);
25
25
 
26
26
  var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
@@ -37,14 +37,14 @@ var ProjectRegistry = class {
37
37
  }
38
38
  load() {
39
39
  try {
40
- if (fs6__default.default.existsSync(REGISTRY_FILE)) {
41
- const data = fs6__default.default.readFileSync(REGISTRY_FILE, "utf8");
40
+ if (fs8__default.default.existsSync(REGISTRY_FILE)) {
41
+ const data = fs8__default.default.readFileSync(REGISTRY_FILE, "utf8");
42
42
  const parsed = JSON.parse(data);
43
43
  if (!Array.isArray(parsed)) {
44
44
  console.error("Invalid project registry format: expected array, got", typeof parsed);
45
45
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
46
- fs6__default.default.copyFileSync(REGISTRY_FILE, backupFile);
47
- fs6__default.default.unlinkSync(REGISTRY_FILE);
46
+ fs8__default.default.copyFileSync(REGISTRY_FILE, backupFile);
47
+ fs8__default.default.unlinkSync(REGISTRY_FILE);
48
48
  return;
49
49
  }
50
50
  const projects = parsed;
@@ -57,11 +57,11 @@ var ProjectRegistry = class {
57
57
  }
58
58
  } catch (error) {
59
59
  console.error("Failed to load project registry:", error);
60
- if (fs6__default.default.existsSync(REGISTRY_FILE)) {
60
+ if (fs8__default.default.existsSync(REGISTRY_FILE)) {
61
61
  try {
62
62
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
63
- fs6__default.default.copyFileSync(REGISTRY_FILE, backupFile);
64
- fs6__default.default.unlinkSync(REGISTRY_FILE);
63
+ fs8__default.default.copyFileSync(REGISTRY_FILE, backupFile);
64
+ fs8__default.default.unlinkSync(REGISTRY_FILE);
65
65
  console.log("Corrupted registry file backed up and removed. Starting fresh.");
66
66
  } catch (backupError) {
67
67
  }
@@ -70,11 +70,11 @@ var ProjectRegistry = class {
70
70
  }
71
71
  save() {
72
72
  try {
73
- if (!fs6__default.default.existsSync(AMA_DIR)) {
74
- fs6__default.default.mkdirSync(AMA_DIR, { recursive: true });
73
+ if (!fs8__default.default.existsSync(AMA_DIR)) {
74
+ fs8__default.default.mkdirSync(AMA_DIR, { recursive: true });
75
75
  }
76
76
  const projects = Array.from(this.projects.values());
77
- fs6__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
77
+ fs8__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
78
78
  } catch (error) {
79
79
  console.error("Failed to save project registry:", error);
80
80
  }
@@ -163,6 +163,60 @@ zod.z.object({
163
163
  ),
164
164
  end_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to end reading at (inclusive).")
165
165
  });
166
+ async function readFileContent(absolute_file_path, relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed) {
167
+ const file = Bun.file(absolute_file_path);
168
+ const exists = await file.exists();
169
+ if (!exists) {
170
+ return {
171
+ success: false,
172
+ message: `File not found: ${relative_file_path}`,
173
+ error: "FILE_NOT_FOUND"
174
+ };
175
+ }
176
+ try {
177
+ const fileContent = await file.text();
178
+ const lines = fileContent.split(/\r?\n/);
179
+ const totalLines = lines.length;
180
+ if (should_read_entire_file) {
181
+ return {
182
+ success: true,
183
+ message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
184
+ content: fileContent,
185
+ totalLines
186
+ };
187
+ }
188
+ const startIndex = start_line_one_indexed - 1;
189
+ if (startIndex >= totalLines) {
190
+ return {
191
+ success: false,
192
+ message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
193
+ error: "INVALID_LINE_RANGE"
194
+ };
195
+ }
196
+ const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
197
+ const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
198
+ const linesRead = normalizedEnd - start_line_one_indexed + 1;
199
+ return {
200
+ success: true,
201
+ message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
202
+ content: selectedLines,
203
+ totalLines
204
+ };
205
+ } catch (error) {
206
+ if (error?.code === "EISDIR") {
207
+ return {
208
+ success: false,
209
+ message: `Path is not a file: ${relative_file_path}`,
210
+ error: "NOT_A_FILE"
211
+ };
212
+ }
213
+ return {
214
+ success: false,
215
+ message: `Failed to read file: ${relative_file_path}`,
216
+ error: "READ_ERROR"
217
+ };
218
+ }
219
+ }
166
220
  var read_file = async function(input, projectCwd) {
167
221
  const { relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed } = input;
168
222
  try {
@@ -203,6 +257,7 @@ var read_file = async function(input, projectCwd) {
203
257
  };
204
258
  }
205
259
  }
260
+ let absolute_file_path;
206
261
  if (projectCwd) {
207
262
  const validation = validatePath(relative_file_path, projectCwd);
208
263
  if (!validation.valid) {
@@ -212,128 +267,17 @@ var read_file = async function(input, projectCwd) {
212
267
  error: "ACCESS_DENIED"
213
268
  };
214
269
  }
215
- const absolute_file_path = validation.resolvedPath;
216
- try {
217
- const fileStats = await fs5.stat(absolute_file_path);
218
- if (!fileStats.isFile()) {
219
- return {
220
- success: false,
221
- message: `Path is not a file: ${relative_file_path}`,
222
- error: "NOT_A_FILE"
223
- };
224
- }
225
- } catch (error) {
226
- if (error?.code === "ENOENT") {
227
- return {
228
- success: false,
229
- message: `File not found: ${relative_file_path}`,
230
- error: "FILE_NOT_FOUND"
231
- };
232
- }
233
- return {
234
- success: false,
235
- message: `Failed to access file: ${relative_file_path}`,
236
- error: "READ_ERROR"
237
- };
238
- }
239
- try {
240
- const fileContent = await fs5.readFile(absolute_file_path, "utf-8");
241
- const lines = fileContent.split(/\r?\n/);
242
- const totalLines = lines.length;
243
- if (should_read_entire_file) {
244
- return {
245
- success: true,
246
- message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
247
- content: fileContent,
248
- totalLines
249
- };
250
- }
251
- const startIndex = start_line_one_indexed - 1;
252
- if (startIndex >= totalLines) {
253
- return {
254
- success: false,
255
- message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
256
- error: "INVALID_LINE_RANGE"
257
- };
258
- }
259
- const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
260
- const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
261
- const linesRead = normalizedEnd - start_line_one_indexed + 1;
262
- return {
263
- success: true,
264
- message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
265
- content: selectedLines,
266
- totalLines
267
- };
268
- } catch {
269
- return {
270
- success: false,
271
- message: `Failed to read file: ${relative_file_path}`,
272
- error: "READ_ERROR"
273
- };
274
- }
270
+ absolute_file_path = validation.resolvedPath;
275
271
  } else {
276
- const absolute_file_path = path10__default.default.resolve(relative_file_path);
277
- try {
278
- const fileStats = await fs5.stat(absolute_file_path);
279
- if (!fileStats.isFile()) {
280
- return {
281
- success: false,
282
- message: `Path is not a file: ${relative_file_path}`,
283
- error: "NOT_A_FILE"
284
- };
285
- }
286
- } catch (error) {
287
- if (error?.code === "ENOENT") {
288
- return {
289
- success: false,
290
- message: `File not found: ${relative_file_path}`,
291
- error: "FILE_NOT_FOUND"
292
- };
293
- }
294
- return {
295
- success: false,
296
- message: `Failed to access file: ${relative_file_path}`,
297
- error: "READ_ERROR"
298
- };
299
- }
300
- try {
301
- const fileContent = await fs5.readFile(absolute_file_path, "utf-8");
302
- const lines = fileContent.split(/\r?\n/);
303
- const totalLines = lines.length;
304
- if (should_read_entire_file) {
305
- return {
306
- success: true,
307
- message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
308
- content: fileContent,
309
- totalLines
310
- };
311
- }
312
- const startIndex = start_line_one_indexed - 1;
313
- if (startIndex >= totalLines) {
314
- return {
315
- success: false,
316
- message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
317
- error: "INVALID_LINE_RANGE"
318
- };
319
- }
320
- const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
321
- const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
322
- const linesRead = normalizedEnd - start_line_one_indexed + 1;
323
- return {
324
- success: true,
325
- message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
326
- content: selectedLines,
327
- totalLines
328
- };
329
- } catch {
330
- return {
331
- success: false,
332
- message: `Failed to read file: ${relative_file_path}`,
333
- error: "READ_ERROR"
334
- };
335
- }
336
- }
272
+ absolute_file_path = path10__default.default.resolve(relative_file_path);
273
+ }
274
+ return await readFileContent(
275
+ absolute_file_path,
276
+ relative_file_path,
277
+ should_read_entire_file,
278
+ start_line_one_indexed,
279
+ end_line_one_indexed
280
+ );
337
281
  } catch {
338
282
  return {
339
283
  success: false,
@@ -424,13 +368,13 @@ var Diff = class {
424
368
  editLength++;
425
369
  };
426
370
  if (callback) {
427
- (function exec4() {
371
+ (function exec3() {
428
372
  setTimeout(function() {
429
373
  if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
430
374
  return callback(void 0);
431
375
  }
432
376
  if (!execEditLength()) {
433
- exec4();
377
+ exec3();
434
378
  }
435
379
  }, 0);
436
380
  })();
@@ -657,17 +601,19 @@ var apply_patch = async function(input, projectCwd) {
657
601
  }
658
602
  const basePath = projectCwd || process.cwd();
659
603
  const absolute_file_path = resolveProjectPath(file_path, basePath);
604
+ const file = Bun.file(absolute_file_path);
605
+ const exists = await file.exists();
606
+ if (!exists) {
607
+ return {
608
+ success: false,
609
+ message: `File not found: ${file_path}`,
610
+ error: "FILE_NOT_FOUND"
611
+ };
612
+ }
660
613
  let fileContent;
661
614
  try {
662
- fileContent = await fs5.readFile(absolute_file_path, "utf-8");
615
+ fileContent = await file.text();
663
616
  } catch (error) {
664
- if (error?.code === "ENOENT") {
665
- return {
666
- success: false,
667
- message: `File not found: ${file_path}`,
668
- error: "FILE_NOT_FOUND"
669
- };
670
- }
671
617
  return {
672
618
  success: false,
673
619
  message: `Failed to read file: ${file_path}`,
@@ -691,7 +637,7 @@ var apply_patch = async function(input, projectCwd) {
691
637
  }
692
638
  const newContent = fileContent.replace(old_string, new_string);
693
639
  try {
694
- await fs5.writeFile(absolute_file_path, newContent, "utf-8");
640
+ await Bun.write(absolute_file_path, newContent);
695
641
  const diffStats = calculateDiffStats(fileContent, newContent);
696
642
  return {
697
643
  success: true,
@@ -737,28 +683,27 @@ var editFiles = async function(input, projectCwd) {
737
683
  const basePath = projectCwd || process.cwd();
738
684
  const filePath = resolveProjectPath(target_file, basePath);
739
685
  const dirPath = path10__default.default.dirname(filePath);
740
- await fs5.mkdir(dirPath, { recursive: true });
686
+ await fs7.mkdir(dirPath, { recursive: true });
741
687
  let isNewFile = providedNewFile;
742
688
  let existingContent = "";
689
+ const file = Bun.file(filePath);
743
690
  if (isNewFile === void 0) {
744
- try {
745
- existingContent = await fs6__default.default.promises.readFile(filePath, "utf-8");
691
+ const exists = await file.exists();
692
+ if (exists) {
693
+ existingContent = await file.text();
746
694
  isNewFile = false;
747
- } catch (error) {
695
+ } else {
748
696
  isNewFile = true;
749
697
  }
750
698
  } else if (!isNewFile) {
751
- try {
752
- existingContent = await fs6__default.default.promises.readFile(filePath, "utf-8");
753
- } catch (error) {
699
+ const exists = await file.exists();
700
+ if (exists) {
701
+ existingContent = await file.text();
702
+ } else {
754
703
  isNewFile = true;
755
704
  }
756
705
  }
757
- try {
758
- await fs6__default.default.promises.writeFile(filePath, content);
759
- } catch (writeError) {
760
- throw writeError;
761
- }
706
+ await Bun.write(filePath, content);
762
707
  const diffStats = calculateDiffStats(existingContent, content);
763
708
  if (isNewFile) {
764
709
  return {
@@ -821,25 +766,31 @@ var deleteFile = async function(input, projectCwd) {
821
766
  error: "INVALID_FILE_PATH"
822
767
  };
823
768
  }
824
- const originalContent = await fs5.readFile(absolute_file_path);
825
- if (originalContent === void 0) {
769
+ const file = Bun.file(absolute_file_path);
770
+ const exists = await file.exists();
771
+ if (!exists) {
826
772
  return {
827
773
  success: false,
828
- message: `Failed to read file before deletion: ${realPath}`,
829
- error: "READ_ERROR"
774
+ message: `File not found: ${realPath}`,
775
+ error: "FILE_NOT_FOUND"
830
776
  };
831
777
  }
832
- const deleteResult = await fs5.unlink(absolute_file_path).catch(() => {
778
+ let originalContent;
779
+ try {
780
+ originalContent = await file.text();
781
+ } catch {
833
782
  return {
834
783
  success: false,
835
784
  message: `Failed to read file before deletion: ${realPath}`,
836
- error: "DELETE_ERROR"
785
+ error: "READ_ERROR"
837
786
  };
838
- });
839
- if (!deleteResult?.success) {
787
+ }
788
+ try {
789
+ await fs7.unlink(absolute_file_path);
790
+ } catch {
840
791
  return {
841
792
  success: false,
842
- message: `Failed to delete file before deletion: ${realPath}`,
793
+ message: `Failed to delete file: ${realPath}`,
843
794
  error: "DELETE_ERROR"
844
795
  };
845
796
  }
@@ -856,72 +807,218 @@ var deleteFile = async function(input, projectCwd) {
856
807
  };
857
808
  }
858
809
  };
810
+ var GREP_LIMITS = {
811
+ DEFAULT_MAX_MATCHES: 200,
812
+ MAX_LINE_LENGTH: 500,
813
+ MAX_TOTAL_OUTPUT_SIZE: 1 * 1024 * 1024,
814
+ TRUNCATION_MESSAGE: "\n[Results truncated due to size limits. Use more specific patterns or file filters to narrow your search.]"
815
+ };
859
816
  zod.z.object({
860
817
  query: zod.z.string().describe("The regex pattern to search for"),
861
818
  options: zod.z.object({
862
819
  includePattern: zod.z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts")'),
863
820
  excludePattern: zod.z.string().optional().describe("Glob pattern for files to exclude"),
864
- caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive")
865
- })
821
+ caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive"),
822
+ path: zod.z.string().optional().describe("Subdirectory to search in")
823
+ }).optional()
866
824
  });
867
- var execAsync = util.promisify(child_process.exec);
825
+ async function getRipgrepPath() {
826
+ const paths = [
827
+ "/opt/homebrew/bin/rg",
828
+ "/usr/local/bin/rg",
829
+ "/usr/bin/rg",
830
+ "rg"
831
+ // Fallback to PATH
832
+ ];
833
+ for (const rgPath of paths) {
834
+ try {
835
+ const proc = Bun.spawn(["which", rgPath], { stdout: "pipe", stderr: "pipe" });
836
+ await proc.exited;
837
+ if (proc.exitCode === 0) {
838
+ return rgPath;
839
+ }
840
+ } catch {
841
+ continue;
842
+ }
843
+ }
844
+ return "rg";
845
+ }
846
+ async function getMtimesBatched(files) {
847
+ const mtimeMap = /* @__PURE__ */ new Map();
848
+ const BATCH_SIZE = 50;
849
+ for (let i = 0; i < files.length; i += BATCH_SIZE) {
850
+ const batch = files.slice(i, i + BATCH_SIZE);
851
+ const results = await Promise.all(
852
+ batch.map(async (filePath) => {
853
+ const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
854
+ return { path: filePath, mtime };
855
+ })
856
+ );
857
+ results.forEach(({ path: path14, mtime }) => mtimeMap.set(path14, mtime));
858
+ }
859
+ return mtimeMap;
860
+ }
868
861
  var grepTool = async function(input, projectCwd) {
869
862
  const { query, options } = input;
863
+ if (!query || query.trim() === "") {
864
+ return {
865
+ success: false,
866
+ message: "Missing required parameter: query",
867
+ error: "MISSING_QUERY"
868
+ };
869
+ }
870
870
  try {
871
- const { includePattern, excludePattern: excludePattern2, caseSensitive } = options || {};
872
- const searchDir = projectCwd || process.cwd();
873
- if (projectCwd && !path10__default.default.isAbsolute(projectCwd)) {
871
+ const { includePattern, excludePattern, caseSensitive, path: subPath } = options || {};
872
+ let searchDir = projectCwd || process.cwd();
873
+ if (subPath) {
874
+ searchDir = path10__default.default.isAbsolute(subPath) ? subPath : path10__default.default.resolve(searchDir, subPath);
875
+ if (projectCwd) {
876
+ const validation = validatePath(subPath, projectCwd);
877
+ if (!validation.valid) {
878
+ return {
879
+ success: false,
880
+ message: validation.error || "Path validation failed",
881
+ error: "ACCESS_DENIED"
882
+ };
883
+ }
884
+ }
885
+ }
886
+ if (!fs8__default.default.existsSync(searchDir)) {
874
887
  return {
875
888
  success: false,
876
- message: "Invalid project directory",
877
- error: "INVALID_PROJECT_DIR"
889
+ message: `Directory not found: ${searchDir}`,
890
+ error: "DIR_NOT_FOUND"
878
891
  };
879
892
  }
880
- let command = `rg -n --with-filename "${query}"`;
881
- if (caseSensitive) {
882
- command += " -i";
893
+ const rgPath = await getRipgrepPath();
894
+ const args = [
895
+ "-n",
896
+ // Line numbers
897
+ "--with-filename",
898
+ // Always show filename
899
+ "--no-heading",
900
+ // Don't group by file
901
+ "--color=never",
902
+ // No ANSI colors
903
+ "--max-count=100",
904
+ // Max matches per file
905
+ "--max-columns=1000"
906
+ // Truncate long lines
907
+ ];
908
+ if (!caseSensitive) {
909
+ args.push("-i");
883
910
  }
884
911
  if (includePattern) {
885
- command += ` --glob "${includePattern}"`;
886
- }
887
- if (excludePattern2) {
888
- command += ` --glob "!${excludePattern2}"`;
889
- }
890
- command += ` --max-count 50`;
891
- command += ` "${searchDir}"`;
892
- const { stdout } = await execAsync(command);
893
- const rawMatches = stdout.trim().split("\n").filter((line) => line.length > 0);
894
- const detailedMatches = [];
895
- const matches = [];
896
- for (const rawMatch of rawMatches) {
897
- const colonIndex = rawMatch.indexOf(":");
898
- const secondColonIndex = rawMatch.indexOf(":", colonIndex + 1);
899
- if (colonIndex > 0 && secondColonIndex > colonIndex) {
900
- const file = rawMatch.substring(0, colonIndex);
901
- const lineNumber = parseInt(rawMatch.substring(colonIndex + 1, secondColonIndex), 10);
902
- let content = rawMatch.substring(secondColonIndex + 1);
903
- if (content.length > 250) {
904
- content = content.substring(0, 250) + "...";
912
+ args.push("--glob", includePattern);
913
+ }
914
+ if (excludePattern) {
915
+ args.push("--glob", `!${excludePattern}`);
916
+ }
917
+ args.push("--glob", "!node_modules/**");
918
+ args.push("--glob", "!.git/**");
919
+ args.push("--glob", "!dist/**");
920
+ args.push("--glob", "!build/**");
921
+ args.push("--glob", "!*.min.js");
922
+ args.push("--glob", "!*.min.css");
923
+ args.push("--glob", "!package-lock.json");
924
+ args.push("--glob", "!yarn.lock");
925
+ args.push("--glob", "!bun.lockb");
926
+ args.push("--regexp", query);
927
+ args.push(searchDir);
928
+ const proc = Bun.spawn([rgPath, ...args], {
929
+ stdout: "pipe",
930
+ stderr: "pipe"
931
+ });
932
+ const stdout = await new Response(proc.stdout).text();
933
+ const stderr = await new Response(proc.stderr).text();
934
+ const exitCode = await proc.exited;
935
+ if (exitCode === 1) {
936
+ return {
937
+ success: true,
938
+ matches: [],
939
+ detailedMatches: [],
940
+ query,
941
+ matchCount: 0,
942
+ message: `No matches found for pattern: ${query}`
943
+ };
944
+ }
945
+ if (exitCode !== 0) {
946
+ return {
947
+ success: false,
948
+ message: `Ripgrep error: ${stderr || "Unknown error"}`,
949
+ error: "GREP_EXEC_ERROR"
950
+ };
951
+ }
952
+ const lines = stdout.trim().split("\n").filter((line) => line.length > 0);
953
+ const rawMatches = [];
954
+ const uniqueFiles = /* @__PURE__ */ new Set();
955
+ for (const line of lines) {
956
+ const firstColon = line.indexOf(":");
957
+ const secondColon = line.indexOf(":", firstColon + 1);
958
+ if (firstColon > 0 && secondColon > firstColon) {
959
+ const file = line.substring(0, firstColon);
960
+ const lineNumber = parseInt(line.substring(firstColon + 1, secondColon), 10);
961
+ let content = line.substring(secondColon + 1);
962
+ if (content.length > GREP_LIMITS.MAX_LINE_LENGTH) {
963
+ content = content.substring(0, GREP_LIMITS.MAX_LINE_LENGTH) + "...";
905
964
  }
906
- detailedMatches.push({
965
+ rawMatches.push({
907
966
  file,
908
967
  lineNumber,
909
- content
968
+ content: content.trim(),
969
+ mtime: 0
910
970
  });
911
- matches.push(`${file}:${lineNumber}:${content}`);
912
- } else {
913
- matches.push(rawMatch);
971
+ uniqueFiles.add(file);
914
972
  }
915
973
  }
974
+ const mtimeMap = await getMtimesBatched(Array.from(uniqueFiles));
975
+ for (const match of rawMatches) {
976
+ match.mtime = mtimeMap.get(match.file) || 0;
977
+ }
978
+ rawMatches.sort((a, b) => {
979
+ if (b.mtime !== a.mtime) {
980
+ return b.mtime - a.mtime;
981
+ }
982
+ return a.file.localeCompare(b.file);
983
+ });
984
+ const truncated = rawMatches.length > GREP_LIMITS.DEFAULT_MAX_MATCHES;
985
+ const finalMatches = truncated ? rawMatches.slice(0, GREP_LIMITS.DEFAULT_MAX_MATCHES) : rawMatches;
986
+ const detailedMatches = finalMatches.map((m) => ({
987
+ file: m.file,
988
+ lineNumber: m.lineNumber,
989
+ content: m.content
990
+ }));
991
+ const matches = finalMatches.map(
992
+ (m) => `${m.file}:${m.lineNumber}:${m.content}`
993
+ );
994
+ const groupedOutput = [`Found ${finalMatches.length} matches`];
995
+ let currentFile = "";
996
+ for (const match of finalMatches) {
997
+ if (currentFile !== match.file) {
998
+ if (currentFile !== "") {
999
+ groupedOutput.push("");
1000
+ }
1001
+ currentFile = match.file;
1002
+ groupedOutput.push(`${match.file}:`);
1003
+ }
1004
+ groupedOutput.push(` Line ${match.lineNumber}: ${match.content}`);
1005
+ }
1006
+ if (truncated) {
1007
+ groupedOutput.push("");
1008
+ groupedOutput.push(GREP_LIMITS.TRUNCATION_MESSAGE);
1009
+ }
916
1010
  return {
917
1011
  success: true,
918
1012
  matches,
919
1013
  detailedMatches,
920
1014
  query,
921
- matchCount: matches.length,
922
- message: `Found ${matches.length} matches for pattern: ${query}`
1015
+ matchCount: finalMatches.length,
1016
+ truncated,
1017
+ message: `Found ${finalMatches.length} matches for pattern: ${query}`,
1018
+ content: groupedOutput.join("\n")
923
1019
  };
924
1020
  } catch (error) {
1021
+ console.error("[grep] error:", error);
925
1022
  return {
926
1023
  success: false,
927
1024
  message: error?.message || String(error),
@@ -930,9 +1027,25 @@ var grepTool = async function(input, projectCwd) {
930
1027
  }
931
1028
  };
932
1029
  zod.z.object({
933
- pattern: zod.z.string().describe('Glob pattern (e.g., "**/*.js")'),
934
- path: zod.z.string().optional().describe("Relative directory path to search in")
1030
+ pattern: zod.z.string().describe('Glob pattern to match files (e.g., "**/*.js", "src/**/*.ts", "*.json"). Supports standard glob syntax with *, **, and ? wildcards'),
1031
+ 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")
935
1032
  });
1033
+ var RESULT_LIMIT = 100;
1034
+ var MTIME_BATCH_SIZE = 50;
1035
+ async function getMtimesBatched2(files) {
1036
+ const results = [];
1037
+ for (let i = 0; i < files.length; i += MTIME_BATCH_SIZE) {
1038
+ const batch = files.slice(i, i + MTIME_BATCH_SIZE);
1039
+ const batchResults = await Promise.all(
1040
+ batch.map(async (filePath) => {
1041
+ const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
1042
+ return { path: filePath, mtime };
1043
+ })
1044
+ );
1045
+ results.push(...batchResults);
1046
+ }
1047
+ return results;
1048
+ }
936
1049
  var globTool = async function(input, projectCwd) {
937
1050
  const { pattern, path: inputPath } = input;
938
1051
  if (!pattern) {
@@ -945,6 +1058,13 @@ var globTool = async function(input, projectCwd) {
945
1058
  try {
946
1059
  const basePath = projectCwd || process.cwd();
947
1060
  const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
1061
+ if (!fs8__default.default.existsSync(searchPath)) {
1062
+ return {
1063
+ success: false,
1064
+ message: `Directory not found: ${searchPath}`,
1065
+ error: "DIR_NOT_FOUND"
1066
+ };
1067
+ }
948
1068
  if (projectCwd && inputPath) {
949
1069
  const validation = validatePath(inputPath, projectCwd);
950
1070
  if (!validation.valid) {
@@ -955,21 +1075,49 @@ var globTool = async function(input, projectCwd) {
955
1075
  };
956
1076
  }
957
1077
  }
958
- const filesGenerator = fs5.glob(pattern, {
959
- cwd: searchPath
960
- });
1078
+ const glob = new Bun.Glob(pattern);
961
1079
  const files = [];
962
- for await (const file of filesGenerator) {
963
- files.push(file);
1080
+ let truncated = false;
1081
+ for await (const match of glob.scan({
1082
+ cwd: searchPath,
1083
+ absolute: true,
1084
+ onlyFiles: true,
1085
+ followSymlinks: false
1086
+ })) {
1087
+ if (match.includes("/node_modules/") || match.includes("/.git/")) {
1088
+ continue;
1089
+ }
1090
+ if (files.length >= RESULT_LIMIT) {
1091
+ truncated = true;
1092
+ break;
1093
+ }
1094
+ files.push(match);
1095
+ }
1096
+ const filesWithMtime = await getMtimesBatched2(files);
1097
+ filesWithMtime.sort((a, b) => b.mtime - a.mtime);
1098
+ const output = [];
1099
+ if (filesWithMtime.length === 0) {
1100
+ output.push("No files found");
1101
+ } else {
1102
+ output.push(...filesWithMtime.map((f) => f.path));
1103
+ if (truncated) {
1104
+ output.push("");
1105
+ output.push("(Results are truncated. Consider using a more specific path or pattern.)");
1106
+ }
964
1107
  }
965
1108
  const searchLocation = inputPath ? ` in "${inputPath}"` : " in current directory";
966
- const message = `Found ${files.length} matches for pattern "${pattern}"${searchLocation}`;
1109
+ const message = `Found ${filesWithMtime.length} matches for pattern "${pattern}"${searchLocation}`;
967
1110
  return {
968
1111
  success: true,
969
1112
  message,
970
- content: files
1113
+ metadata: {
1114
+ count: filesWithMtime.length,
1115
+ truncated
1116
+ },
1117
+ content: output.join("\n")
971
1118
  };
972
1119
  } catch (error) {
1120
+ console.error("[glob] error:", error);
973
1121
  return {
974
1122
  success: false,
975
1123
  message: `Failed to find files matching pattern: ${pattern}`,
@@ -977,59 +1125,120 @@ var globTool = async function(input, projectCwd) {
977
1125
  };
978
1126
  }
979
1127
  };
980
- var excludePatterns = [
981
- "node_modules/",
982
- "__pycache__/",
983
- ".git/",
984
- "dist/",
985
- "build/",
986
- "target/",
987
- "vendor/",
988
- "bin/",
989
- "obj/",
990
- ".idea/",
991
- ".vscode/",
992
- ".zig-cache/",
1128
+ var IGNORE_PATTERNS = [
1129
+ "node_modules",
1130
+ "__pycache__",
1131
+ ".git",
1132
+ "dist",
1133
+ "build",
1134
+ "target",
1135
+ "vendor",
1136
+ "bin",
1137
+ "obj",
1138
+ ".idea",
1139
+ ".vscode",
1140
+ ".zig-cache",
993
1141
  "zig-out",
994
1142
  ".coverage",
995
- "coverage/",
996
- "vendor/",
997
- "tmp/",
998
- "temp/",
999
- ".cache/",
1000
- "cache/",
1001
- "logs/",
1002
- ".venv/",
1003
- "venv/",
1004
- "env/"
1143
+ "coverage",
1144
+ "tmp",
1145
+ "temp",
1146
+ ".cache",
1147
+ "cache",
1148
+ "logs",
1149
+ ".venv",
1150
+ "venv",
1151
+ "env",
1152
+ ".next",
1153
+ ".turbo",
1154
+ ".vercel",
1155
+ ".output"
1005
1156
  ];
1006
- var excludePattern = excludePatterns.join("|");
1157
+ var RESULT_LIMIT2 = 500;
1158
+ var MTIME_BATCH_SIZE2 = 50;
1007
1159
  zod.z.object({
1008
- path: zod.z.string().optional(),
1009
- recursive: zod.z.boolean().optional().describe("Whether to list files recursively"),
1010
- maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: unlimited)"),
1160
+ path: zod.z.string().optional().describe("Relative path to the directory to list"),
1161
+ recursive: zod.z.boolean().optional().describe("Whether to list files recursively (default: true)"),
1162
+ maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: 3)"),
1011
1163
  pattern: zod.z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
1012
- includeDirectories: zod.z.boolean().optional().describe("Whether to include directories in results (default: true)"),
1013
- includeFiles: zod.z.boolean().optional().describe("Whether to include files in results (default: true)")
1164
+ showHidden: zod.z.boolean().optional().describe("Whether to show hidden files (default: false)")
1014
1165
  });
1015
- var list = async function(input, projectCwd) {
1016
- const { path: relativePath, recursive, maxDepth, pattern, includeDirectories, includeFiles } = input;
1017
- if (maxDepth !== void 0) {
1018
- if (!Number.isInteger(maxDepth) || maxDepth < 0) {
1019
- return {
1020
- success: false,
1021
- message: "maxDepth must be a non-negative integer",
1022
- error: "INVALID_MAX_DEPTH"
1023
- };
1166
+ function shouldIgnore(name, showHidden) {
1167
+ if (!showHidden && name.startsWith(".") && name !== ".") {
1168
+ return true;
1169
+ }
1170
+ return IGNORE_PATTERNS.includes(name);
1171
+ }
1172
+ function matchPattern(name, pattern) {
1173
+ if (!pattern) return true;
1174
+ if (pattern.startsWith(".") && !pattern.includes("*")) {
1175
+ return name.endsWith(pattern);
1176
+ }
1177
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1178
+ const regex = new RegExp(`^${escaped}$`, "i");
1179
+ return regex.test(name);
1180
+ }
1181
+ async function getMtimesBatched3(entries) {
1182
+ for (let i = 0; i < entries.length; i += MTIME_BATCH_SIZE2) {
1183
+ const batch = entries.slice(i, i + MTIME_BATCH_SIZE2);
1184
+ await Promise.all(
1185
+ batch.map(async (entry) => {
1186
+ entry.mtime = await Bun.file(entry.absolutePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
1187
+ })
1188
+ );
1189
+ }
1190
+ }
1191
+ function buildTreeOutput(entries, basePath) {
1192
+ const tree = /* @__PURE__ */ new Map();
1193
+ for (const entry of entries) {
1194
+ const dir = path10__default.default.dirname(entry.relativePath);
1195
+ const dirKey = dir === "." ? "" : dir;
1196
+ if (!tree.has(dirKey)) {
1197
+ tree.set(dirKey, []);
1198
+ }
1199
+ tree.get(dirKey).push(entry);
1200
+ }
1201
+ for (const [, items] of tree) {
1202
+ items.sort((a, b) => {
1203
+ if (a.type !== b.type) {
1204
+ return a.type === "directory" ? -1 : 1;
1205
+ }
1206
+ return a.name.localeCompare(b.name);
1207
+ });
1208
+ }
1209
+ const lines = [`${basePath}/`];
1210
+ function renderLevel(dirPath, indent) {
1211
+ const items = tree.get(dirPath) || [];
1212
+ for (let i = 0; i < items.length; i++) {
1213
+ const item = items[i];
1214
+ const isLast = i === items.length - 1;
1215
+ const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1216
+ const childIndent = indent + (isLast ? " " : "\u2502 ");
1217
+ if (item.type === "directory") {
1218
+ lines.push(`${indent}${prefix}${item.name}/`);
1219
+ const childPath = dirPath ? `${dirPath}/${item.name}` : item.name;
1220
+ renderLevel(childPath, childIndent);
1221
+ } else {
1222
+ lines.push(`${indent}${prefix}${item.name}`);
1223
+ }
1024
1224
  }
1025
1225
  }
1026
- const includeFilesNormalized = includeFiles ?? true;
1027
- const includeDirectoriesNormalized = includeDirectories ?? true;
1028
- if (!includeFilesNormalized && !includeDirectoriesNormalized) {
1226
+ renderLevel("", "");
1227
+ return lines.join("\n");
1228
+ }
1229
+ var list = async function(input, projectCwd) {
1230
+ const {
1231
+ path: relativePath,
1232
+ recursive = true,
1233
+ maxDepth = 3,
1234
+ pattern,
1235
+ showHidden = false
1236
+ } = input;
1237
+ if (maxDepth !== void 0 && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
1029
1238
  return {
1030
1239
  success: false,
1031
- message: "At least one of includeFiles or includeDirectories must be true",
1032
- error: "INVALID_INCLUDE_OPTIONS"
1240
+ message: "maxDepth must be a non-negative integer",
1241
+ error: "INVALID_MAX_DEPTH"
1033
1242
  };
1034
1243
  }
1035
1244
  try {
@@ -1045,88 +1254,119 @@ var list = async function(input, projectCwd) {
1045
1254
  };
1046
1255
  }
1047
1256
  }
1048
- try {
1049
- await fs5.access(absolutePath);
1050
- } catch {
1257
+ if (!fs8__default.default.existsSync(absolutePath)) {
1051
1258
  return {
1052
1259
  success: false,
1053
- message: `File does not exist: ${absolutePath}`,
1054
- error: "FILE_DOES_NOT_EXIST"
1260
+ message: `Directory not found: ${absolutePath}`,
1261
+ error: "DIR_NOT_FOUND"
1055
1262
  };
1056
1263
  }
1057
- const isDir = (await fs5.stat(absolutePath)).isDirectory();
1058
- if (!isDir) {
1264
+ const stats = fs8__default.default.statSync(absolutePath);
1265
+ if (!stats.isDirectory()) {
1059
1266
  return {
1060
1267
  success: false,
1061
- message: `File is not a directory: ${absolutePath}`,
1062
- error: "FILE_IS_NOT_A_DIRECTORY"
1268
+ message: `Path is not a directory: ${absolutePath}`,
1269
+ error: "NOT_A_DIRECTORY"
1063
1270
  };
1064
1271
  }
1065
1272
  const collected = [];
1066
- const patternMatcher = (() => {
1067
- if (!pattern) return null;
1068
- if (pattern.startsWith(".") && !pattern.includes("*") && !pattern.includes("?")) {
1069
- return (entryName) => entryName.endsWith(pattern);
1070
- }
1071
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1072
- const regex = new RegExp(`^${escaped}$`);
1073
- return (entryName) => regex.test(entryName);
1074
- })();
1075
- const matchPattern = (entryName) => {
1076
- if (!patternMatcher) return true;
1077
- return patternMatcher(entryName);
1078
- };
1079
- const maxDepthNormalized = recursive ? maxDepth ?? Infinity : 0;
1273
+ let truncated = false;
1080
1274
  const walk = async (currentDir, depth) => {
1081
- const entries = await fs5.readdir(currentDir, { withFileTypes: true });
1275
+ if (collected.length >= RESULT_LIMIT2) {
1276
+ truncated = true;
1277
+ return;
1278
+ }
1279
+ let entries;
1280
+ try {
1281
+ entries = fs8__default.default.readdirSync(currentDir, { withFileTypes: true });
1282
+ } catch {
1283
+ return;
1284
+ }
1285
+ entries.sort((a, b) => {
1286
+ if (a.isDirectory() !== b.isDirectory()) {
1287
+ return a.isDirectory() ? -1 : 1;
1288
+ }
1289
+ return a.name.localeCompare(b.name);
1290
+ });
1082
1291
  for (const entry of entries) {
1292
+ if (collected.length >= RESULT_LIMIT2) {
1293
+ truncated = true;
1294
+ break;
1295
+ }
1296
+ if (shouldIgnore(entry.name, showHidden)) {
1297
+ continue;
1298
+ }
1083
1299
  const entryAbsolutePath = path10__default.default.join(currentDir, entry.name);
1084
- const entryRelativePath = path10__default.default.relative(absolutePath, entryAbsolutePath) || ".";
1300
+ const entryRelativePath = path10__default.default.relative(absolutePath, entryAbsolutePath);
1085
1301
  if (entry.isDirectory()) {
1086
- const isExcluded = entry.name.match(excludePattern);
1087
- if (includeDirectoriesNormalized && matchPattern(entry.name) && !isExcluded) {
1088
- collected.push({
1089
- name: entry.name,
1090
- absolutePath: entryAbsolutePath,
1091
- relativePath: entryRelativePath,
1092
- type: "directory"
1093
- });
1094
- }
1095
- if (recursive && depth < maxDepthNormalized && !isExcluded) {
1302
+ collected.push({
1303
+ name: entry.name,
1304
+ absolutePath: entryAbsolutePath,
1305
+ relativePath: entryRelativePath,
1306
+ type: "directory",
1307
+ mtime: 0,
1308
+ depth
1309
+ });
1310
+ if (recursive && depth < maxDepth) {
1096
1311
  await walk(entryAbsolutePath, depth + 1);
1097
1312
  }
1098
1313
  } else if (entry.isFile()) {
1099
- if (includeFilesNormalized && matchPattern(entry.name) && !entry.name.match(excludePattern)) {
1314
+ if (matchPattern(entry.name, pattern)) {
1100
1315
  collected.push({
1101
1316
  name: entry.name,
1102
1317
  absolutePath: entryAbsolutePath,
1103
1318
  relativePath: entryRelativePath,
1104
- type: "file"
1319
+ type: "file",
1320
+ mtime: 0,
1321
+ depth
1105
1322
  });
1106
1323
  }
1107
1324
  }
1108
1325
  }
1109
1326
  };
1110
1327
  await walk(absolutePath, 0);
1328
+ await getMtimesBatched3(collected);
1111
1329
  const totalFiles = collected.filter((item) => item.type === "file").length;
1112
1330
  const totalDirectories = collected.filter((item) => item.type === "directory").length;
1113
- let message = `Successfully listed ${collected.length} items in: ${relativePath ?? absolutePath}`;
1331
+ const treeOutput = buildTreeOutput(collected, relativePath || path10__default.default.basename(absolutePath));
1332
+ let message = `Listed ${collected.length} items`;
1333
+ if (relativePath) {
1334
+ message += ` in "${relativePath}"`;
1335
+ }
1336
+ message += ` (${totalFiles} files, ${totalDirectories} directories)`;
1114
1337
  if (recursive) {
1115
- message += ` (recursive${maxDepth !== void 0 ? `, max depth ${maxDepth}` : ""})`;
1338
+ message += ` [depth: ${maxDepth}]`;
1116
1339
  }
1117
1340
  if (pattern) {
1118
- message += ` (filtered by pattern: ${pattern})`;
1341
+ message += ` [filter: ${pattern}]`;
1119
1342
  }
1120
- message += ` - ${totalFiles} files, ${totalDirectories} directories`;
1343
+ if (truncated) {
1344
+ message += ` [TRUNCATED at ${RESULT_LIMIT2} items]`;
1345
+ }
1346
+ const files = collected.map((item) => ({
1347
+ name: item.name,
1348
+ path: item.relativePath,
1349
+ type: item.type
1350
+ }));
1121
1351
  return {
1122
1352
  success: true,
1123
1353
  message,
1124
- files: collected
1354
+ metadata: {
1355
+ totalFiles,
1356
+ totalDirectories,
1357
+ totalItems: collected.length,
1358
+ truncated,
1359
+ maxDepth,
1360
+ recursive
1361
+ },
1362
+ files,
1363
+ content: treeOutput
1125
1364
  };
1126
1365
  } catch (error) {
1366
+ console.error("[list] error:", error);
1127
1367
  return {
1128
1368
  success: false,
1129
- message: `Failed to list files: ${error}`,
1369
+ message: `Failed to list directory: ${error}`,
1130
1370
  error: "LIST_ERROR"
1131
1371
  };
1132
1372
  }
@@ -1142,19 +1382,19 @@ var startHttpServer = () => {
1142
1382
  var CREDENTIALS_DIR = path10__default.default.join(os3__default.default.homedir(), ".amai");
1143
1383
  var CREDENTIALS_PATH = path10__default.default.join(CREDENTIALS_DIR, "credentials.json");
1144
1384
  function getTokens() {
1145
- if (!fs6__default.default.existsSync(CREDENTIALS_PATH)) {
1385
+ if (!fs8__default.default.existsSync(CREDENTIALS_PATH)) {
1146
1386
  return null;
1147
1387
  }
1148
- const raw = fs6__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1388
+ const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1149
1389
  const data = JSON.parse(raw);
1150
1390
  return data;
1151
1391
  }
1152
1392
  var getUserId = () => {
1153
1393
  try {
1154
- if (!fs6__default.default.existsSync(CREDENTIALS_PATH)) {
1394
+ if (!fs8__default.default.existsSync(CREDENTIALS_PATH)) {
1155
1395
  return;
1156
1396
  }
1157
- const raw = fs6__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1397
+ const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1158
1398
  const data = JSON.parse(raw);
1159
1399
  return {
1160
1400
  userId: data.user.id
@@ -1203,7 +1443,7 @@ zod.z.object({
1203
1443
  command: zod.z.string().describe("The terminal command to execute (e.g., 'ls -la', 'pwd', 'echo $HOME')"),
1204
1444
  is_background: zod.z.boolean().describe("Whether the command should be run in the background")
1205
1445
  }).merge(ExplanationSchema);
1206
- var runSecureTerminalCommand = async (command, timeout) => {
1446
+ var runSecureTerminalCommand = async (command, timeout, cwd) => {
1207
1447
  try {
1208
1448
  if (isHarmfulCommand(command)) {
1209
1449
  console.log(`[CLI] Harmful command detected: ${command}`);
@@ -1213,42 +1453,44 @@ var runSecureTerminalCommand = async (command, timeout) => {
1213
1453
  error: "HARMFUL_COMMAND_DETECTED"
1214
1454
  };
1215
1455
  }
1216
- return new Promise((resolve, reject) => {
1217
- const child = child_process.spawn(command, {
1218
- cwd: process.cwd(),
1219
- stdio: ["pipe", "pipe", "pipe"],
1220
- shell: true
1221
- });
1222
- let stdout = "";
1223
- let stderr = "";
1224
- let timeoutId = null;
1225
- if (timeoutId > 0) {
1226
- timeoutId = setTimeout(() => {
1227
- child.kill("SIGKILL");
1228
- reject(new Error(`Command timed out after ${timeout}ms`));
1229
- }, timeout);
1230
- }
1231
- child.stdout?.on("data", (data) => {
1232
- stdout += data.toString();
1233
- });
1234
- child.stderr?.on("data", (data) => {
1235
- stderr += data.toString();
1236
- });
1237
- child.stdout.on("close", (code) => {
1238
- if (timeoutId) {
1239
- clearTimeout(timeoutId);
1240
- }
1241
- resolve({ stdout, stderr, exitCode: code || 0 });
1242
- });
1243
- child.stderr.on("error", (error) => {
1244
- if (timeoutId) {
1245
- clearTimeout(timeoutId);
1246
- }
1247
- reject(error);
1248
- });
1456
+ const proc = Bun.spawn(["sh", "-c", command], {
1457
+ cwd: cwd || process.cwd(),
1458
+ stdout: "pipe",
1459
+ stderr: "pipe"
1249
1460
  });
1250
- } catch {
1251
- console.error("Error while ecexuting the securedShell command");
1461
+ let timedOut = false;
1462
+ let timeoutId = null;
1463
+ if (timeout > 0) {
1464
+ timeoutId = setTimeout(() => {
1465
+ timedOut = true;
1466
+ proc.kill();
1467
+ }, timeout);
1468
+ }
1469
+ const [stdout, stderr, exitCode] = await Promise.all([
1470
+ new Response(proc.stdout).text(),
1471
+ new Response(proc.stderr).text(),
1472
+ proc.exited
1473
+ ]);
1474
+ if (timeoutId) {
1475
+ clearTimeout(timeoutId);
1476
+ }
1477
+ if (timedOut) {
1478
+ return {
1479
+ success: false,
1480
+ message: `Command timed out after ${timeout}ms`,
1481
+ error: "TIMEOUT",
1482
+ stdout,
1483
+ stderr
1484
+ };
1485
+ }
1486
+ return { stdout, stderr, exitCode };
1487
+ } catch (error) {
1488
+ console.error("Error while executing the securedShell command", error);
1489
+ return {
1490
+ success: false,
1491
+ message: "Error while executing the securedShell command",
1492
+ error: error.message
1493
+ };
1252
1494
  }
1253
1495
  };
1254
1496
  var runTerminalCommand = async (input, projectCwd) => {
@@ -1262,13 +1504,12 @@ var runTerminalCommand = async (input, projectCwd) => {
1262
1504
  error: "HARMFUL_COMMAND_DETECTED"
1263
1505
  };
1264
1506
  }
1265
- const child = child_process.spawn(input.command, {
1266
- cwd: projectCwd,
1267
- detached: true,
1268
- stdio: "ignore",
1269
- shell: true
1507
+ const proc = Bun.spawn(["sh", "-c", input.command], {
1508
+ cwd: projectCwd || process.cwd(),
1509
+ stdout: "ignore",
1510
+ stderr: "ignore"
1270
1511
  });
1271
- child.unref();
1512
+ proc.unref();
1272
1513
  console.log(`[LOCAL] Background command started: ${input.command}`);
1273
1514
  return {
1274
1515
  success: true,
@@ -1278,9 +1519,13 @@ var runTerminalCommand = async (input, projectCwd) => {
1278
1519
  } else {
1279
1520
  const result = await runSecureTerminalCommand(
1280
1521
  input.command,
1281
- 3e4
1522
+ 3e4,
1282
1523
  // 30 second timeout
1524
+ projectCwd
1283
1525
  );
1526
+ if (result?.error && !result?.exitCode) {
1527
+ return result;
1528
+ }
1284
1529
  const success = result?.exitCode === 0;
1285
1530
  return {
1286
1531
  success,
@@ -1299,9 +1544,9 @@ var runTerminalCommand = async (input, projectCwd) => {
1299
1544
  };
1300
1545
  }
1301
1546
  };
1302
- var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local"];
1547
+ 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"];
1303
1548
  var getContext = (dir, base = dir, allFiles = []) => {
1304
- const filePath = fs6.readdirSync(dir, { withFileTypes: true });
1549
+ const filePath = fs8.readdirSync(dir, { withFileTypes: true });
1305
1550
  for (const file of filePath) {
1306
1551
  if (ignoreFiles.includes(file.name)) continue;
1307
1552
  const fullPath = path10__default.default.join(dir, file.name);
@@ -1321,28 +1566,34 @@ var IDE_PROJECTS_PATHS = {
1321
1566
  };
1322
1567
  function getWorkspaceStoragePath(ide) {
1323
1568
  const platform = os3__default.default.platform();
1324
- const appName = "Cursor" ;
1569
+ const appName = ide === "cursor" ? "Cursor" : "Code";
1570
+ const appNameLower = appName.toLowerCase();
1325
1571
  if (platform === "darwin") {
1326
1572
  return path10__default.default.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
1327
1573
  } else if (platform === "win32") {
1328
1574
  return path10__default.default.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
1329
1575
  } else {
1330
- return path10__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
1576
+ const capitalizedPath = path10__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
1577
+ const lowercasePath = path10__default.default.join(HOME, ".config", appNameLower, "User", "workspaceStorage");
1578
+ if (fs8__default.default.existsSync(capitalizedPath)) {
1579
+ return capitalizedPath;
1580
+ }
1581
+ return lowercasePath;
1331
1582
  }
1332
1583
  }
1333
1584
  function scanWorkspaceStorage(ide) {
1334
1585
  const projects = [];
1335
- const storagePath = getWorkspaceStoragePath();
1336
- if (!fs6__default.default.existsSync(storagePath)) {
1586
+ const storagePath = getWorkspaceStoragePath(ide);
1587
+ if (!fs8__default.default.existsSync(storagePath)) {
1337
1588
  return projects;
1338
1589
  }
1339
1590
  try {
1340
- const workspaces = fs6__default.default.readdirSync(storagePath);
1591
+ const workspaces = fs8__default.default.readdirSync(storagePath);
1341
1592
  for (const workspace of workspaces) {
1342
1593
  const workspaceJsonPath = path10__default.default.join(storagePath, workspace, "workspace.json");
1343
- if (fs6__default.default.existsSync(workspaceJsonPath)) {
1594
+ if (fs8__default.default.existsSync(workspaceJsonPath)) {
1344
1595
  try {
1345
- const content = fs6__default.default.readFileSync(workspaceJsonPath, "utf-8");
1596
+ const content = fs8__default.default.readFileSync(workspaceJsonPath, "utf-8");
1346
1597
  const data = JSON.parse(content);
1347
1598
  if (data.folder && typeof data.folder === "string") {
1348
1599
  let projectPath = data.folder;
@@ -1350,7 +1601,7 @@ function scanWorkspaceStorage(ide) {
1350
1601
  projectPath = projectPath.replace("file://", "");
1351
1602
  projectPath = decodeURIComponent(projectPath);
1352
1603
  }
1353
- if (fs6__default.default.existsSync(projectPath) && fs6__default.default.statSync(projectPath).isDirectory()) {
1604
+ if (fs8__default.default.existsSync(projectPath) && fs8__default.default.statSync(projectPath).isDirectory()) {
1354
1605
  projects.push({
1355
1606
  name: path10__default.default.basename(projectPath),
1356
1607
  path: projectPath,
@@ -1374,11 +1625,11 @@ var scanIdeProjects = async () => {
1374
1625
  const seenPaths = /* @__PURE__ */ new Set();
1375
1626
  const addProject = (projectPath, ide) => {
1376
1627
  try {
1377
- const resolvedPath = fs6__default.default.realpathSync(projectPath);
1378
- if (fs6__default.default.existsSync(resolvedPath) && fs6__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1628
+ const resolvedPath = fs8__default.default.realpathSync(projectPath);
1629
+ if (fs8__default.default.existsSync(resolvedPath) && fs8__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1379
1630
  const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
1380
1631
  try {
1381
- return fs6__default.default.realpathSync(ideDir) === resolvedPath;
1632
+ return fs8__default.default.realpathSync(ideDir) === resolvedPath;
1382
1633
  } catch {
1383
1634
  return false;
1384
1635
  }
@@ -1399,32 +1650,36 @@ var scanIdeProjects = async () => {
1399
1650
  for (const project of cursorProjects) {
1400
1651
  addProject(project.path, "cursor");
1401
1652
  }
1653
+ const vscodeProjects = scanWorkspaceStorage("vscode");
1654
+ for (const project of vscodeProjects) {
1655
+ addProject(project.path, "vscode");
1656
+ }
1402
1657
  for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
1403
- if (ide === "cursor") continue;
1404
- if (fs6__default.default.existsSync(dirPath)) {
1405
- const projects = fs6__default.default.readdirSync(dirPath);
1658
+ if (ide === "cursor" || ide === "vscode") continue;
1659
+ if (fs8__default.default.existsSync(dirPath)) {
1660
+ const projects = fs8__default.default.readdirSync(dirPath);
1406
1661
  projects.forEach((project) => {
1407
1662
  const projectPath = path10__default.default.join(dirPath, project);
1408
1663
  try {
1409
- const stats = fs6__default.default.lstatSync(projectPath);
1664
+ const stats = fs8__default.default.lstatSync(projectPath);
1410
1665
  let actualPath = null;
1411
1666
  if (stats.isSymbolicLink()) {
1412
- actualPath = fs6__default.default.realpathSync(projectPath);
1667
+ actualPath = fs8__default.default.realpathSync(projectPath);
1413
1668
  } else if (stats.isFile()) {
1414
1669
  try {
1415
- let content = fs6__default.default.readFileSync(projectPath, "utf-8").trim();
1670
+ let content = fs8__default.default.readFileSync(projectPath, "utf-8").trim();
1416
1671
  if (content.startsWith("~/") || content === "~") {
1417
1672
  content = content.replace(/^~/, HOME);
1418
1673
  }
1419
1674
  const resolvedContent = path10__default.default.isAbsolute(content) ? content : path10__default.default.resolve(path10__default.default.dirname(projectPath), content);
1420
- if (fs6__default.default.existsSync(resolvedContent) && fs6__default.default.statSync(resolvedContent).isDirectory()) {
1421
- actualPath = fs6__default.default.realpathSync(resolvedContent);
1675
+ if (fs8__default.default.existsSync(resolvedContent) && fs8__default.default.statSync(resolvedContent).isDirectory()) {
1676
+ actualPath = fs8__default.default.realpathSync(resolvedContent);
1422
1677
  }
1423
1678
  } catch {
1424
1679
  return;
1425
1680
  }
1426
1681
  } else if (stats.isDirectory()) {
1427
- actualPath = fs6__default.default.realpathSync(projectPath);
1682
+ actualPath = fs8__default.default.realpathSync(projectPath);
1428
1683
  }
1429
1684
  if (actualPath) {
1430
1685
  addProject(actualPath, ide);
@@ -1448,7 +1703,7 @@ var Global;
1448
1703
  })(Global || (Global = {}));
1449
1704
 
1450
1705
  // src/snapshot/snapshot.ts
1451
- var execAsync2 = util.promisify(child_process.exec);
1706
+ var execAsync = util.promisify(child_process.exec);
1452
1707
  var Snapshot;
1453
1708
  ((Snapshot2) => {
1454
1709
  const log = {
@@ -1458,7 +1713,7 @@ var Snapshot;
1458
1713
  };
1459
1714
  async function runGit(command, options = {}) {
1460
1715
  try {
1461
- const { stdout, stderr } = await execAsync2(command, {
1716
+ const { stdout, stderr } = await execAsync(command, {
1462
1717
  cwd: options.cwd,
1463
1718
  env: { ...process.env, ...options.env },
1464
1719
  encoding: "utf-8",
@@ -1482,8 +1737,8 @@ var Snapshot;
1482
1737
  const worktree = project.cwd;
1483
1738
  const git = gitdir(projectId);
1484
1739
  try {
1485
- await fs5__default.default.mkdir(git, { recursive: true });
1486
- const gitExists = await fs5__default.default.access(path10__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
1740
+ await fs7__default.default.mkdir(git, { recursive: true });
1741
+ const gitExists = await fs7__default.default.access(path10__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
1487
1742
  if (!gitExists) {
1488
1743
  await runGit(`git init`, {
1489
1744
  env: { GIT_DIR: git, GIT_WORK_TREE: worktree }
@@ -1568,7 +1823,7 @@ var Snapshot;
1568
1823
  for (const file of newFiles) {
1569
1824
  const fullPath = path10__default.default.join(worktree, file);
1570
1825
  try {
1571
- await fs5__default.default.unlink(fullPath);
1826
+ await fs7__default.default.unlink(fullPath);
1572
1827
  log.info("deleted newly created file", { file: fullPath });
1573
1828
  } catch {
1574
1829
  }
@@ -1605,7 +1860,7 @@ var Snapshot;
1605
1860
  log.info("file existed in snapshot but checkout failed, keeping", { file });
1606
1861
  } else {
1607
1862
  log.info("file did not exist in snapshot, deleting", { file });
1608
- await fs5__default.default.unlink(file).catch(() => {
1863
+ await fs7__default.default.unlink(file).catch(() => {
1609
1864
  });
1610
1865
  }
1611
1866
  }
@@ -2054,7 +2309,7 @@ async function main() {
2054
2309
  await connectToUserStreams(serverUrl);
2055
2310
  startHttpServer();
2056
2311
  }
2057
- var execAsync3 = util.promisify(child_process.exec);
2312
+ var execAsync2 = util.promisify(child_process.exec);
2058
2313
  var CODE_SERVER_VERSION = "4.96.4";
2059
2314
  function getPlatformInfo() {
2060
2315
  const platform = process.platform;
@@ -2088,50 +2343,50 @@ function getCodeServerBin() {
2088
2343
  }
2089
2344
  function isCodeServerInstalled() {
2090
2345
  const binPath = getCodeServerBin();
2091
- return fs6__default.default.existsSync(binPath);
2346
+ return fs8__default.default.existsSync(binPath);
2092
2347
  }
2093
2348
  async function installCodeServer() {
2094
2349
  const { ext } = getPlatformInfo();
2095
2350
  const downloadUrl = getDownloadUrl();
2096
2351
  const tarballPath = path10__default.default.join(AMA_DIR, `code-server.${ext}`);
2097
- if (!fs6__default.default.existsSync(AMA_DIR)) {
2098
- fs6__default.default.mkdirSync(AMA_DIR, { recursive: true });
2352
+ if (!fs8__default.default.existsSync(AMA_DIR)) {
2353
+ fs8__default.default.mkdirSync(AMA_DIR, { recursive: true });
2099
2354
  }
2100
- if (!fs6__default.default.existsSync(CODE_DIR)) {
2101
- fs6__default.default.mkdirSync(CODE_DIR, { recursive: true });
2355
+ if (!fs8__default.default.existsSync(CODE_DIR)) {
2356
+ fs8__default.default.mkdirSync(CODE_DIR, { recursive: true });
2102
2357
  }
2103
- if (!fs6__default.default.existsSync(STORAGE_DIR)) {
2104
- fs6__default.default.mkdirSync(STORAGE_DIR, { recursive: true });
2358
+ if (!fs8__default.default.existsSync(STORAGE_DIR)) {
2359
+ fs8__default.default.mkdirSync(STORAGE_DIR, { recursive: true });
2105
2360
  }
2106
- console.log(pc4__default.default.cyan(`Downloading code-server v${CODE_SERVER_VERSION}...`));
2361
+ console.log(pc4__default.default.cyan(`downloading code-server v${CODE_SERVER_VERSION}...`));
2107
2362
  console.log(pc4__default.default.gray(downloadUrl));
2108
2363
  const response = await fetch(downloadUrl);
2109
2364
  if (!response.ok) {
2110
2365
  throw new Error(`Failed to download code-server: ${response.statusText}`);
2111
2366
  }
2112
2367
  const buffer = await response.arrayBuffer();
2113
- await fs6__default.default.promises.writeFile(tarballPath, Buffer.from(buffer));
2368
+ await fs8__default.default.promises.writeFile(tarballPath, Buffer.from(buffer));
2114
2369
  console.log(pc4__default.default.cyan("Extracting code-server..."));
2115
- await execAsync3(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
2116
- await fs6__default.default.promises.unlink(tarballPath);
2370
+ await execAsync2(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
2371
+ await fs8__default.default.promises.unlink(tarballPath);
2117
2372
  const binPath = getCodeServerBin();
2118
- if (fs6__default.default.existsSync(binPath)) {
2119
- await fs6__default.default.promises.chmod(binPath, 493);
2373
+ if (fs8__default.default.existsSync(binPath)) {
2374
+ await fs8__default.default.promises.chmod(binPath, 493);
2120
2375
  }
2121
- console.log(pc4__default.default.green("\u2713 code-server installed successfully"));
2376
+ console.log(pc4__default.default.green("code-server installed successfully"));
2122
2377
  }
2123
2378
  async function killExistingCodeServer() {
2124
2379
  try {
2125
2380
  if (process.platform === "win32") {
2126
- await execAsync3("netstat -ano | findstr :8081 | findstr LISTENING").then(async ({ stdout }) => {
2381
+ await execAsync2("netstat -ano | findstr :8081 | findstr LISTENING").then(async ({ stdout }) => {
2127
2382
  const pid = stdout.trim().split(/\s+/).pop();
2128
- if (pid) await execAsync3(`taskkill /PID ${pid} /F`);
2383
+ if (pid) await execAsync2(`taskkill /PID ${pid} /F`);
2129
2384
  }).catch(() => {
2130
2385
  });
2131
2386
  } else {
2132
- await execAsync3("lsof -ti:8081").then(async ({ stdout }) => {
2387
+ await execAsync2("lsof -ti:8081").then(async ({ stdout }) => {
2133
2388
  const pid = stdout.trim();
2134
- if (pid) await execAsync3(`kill -9 ${pid}`);
2389
+ if (pid) await execAsync2(`kill -9 ${pid}`);
2135
2390
  }).catch(() => {
2136
2391
  });
2137
2392
  }
@@ -2141,8 +2396,8 @@ async function killExistingCodeServer() {
2141
2396
  async function setupDefaultSettings() {
2142
2397
  const userDir = path10__default.default.join(STORAGE_DIR, "User");
2143
2398
  const settingsPath = path10__default.default.join(userDir, "settings.json");
2144
- if (!fs6__default.default.existsSync(userDir)) {
2145
- fs6__default.default.mkdirSync(userDir, { recursive: true });
2399
+ if (!fs8__default.default.existsSync(userDir)) {
2400
+ fs8__default.default.mkdirSync(userDir, { recursive: true });
2146
2401
  }
2147
2402
  const defaultSettings = {
2148
2403
  // Disable signature verification for Open VSX extensions
@@ -2160,9 +2415,9 @@ async function setupDefaultSettings() {
2160
2415
  "workbench.activityBar.location": "top"
2161
2416
  };
2162
2417
  let existingSettings = {};
2163
- if (fs6__default.default.existsSync(settingsPath)) {
2418
+ if (fs8__default.default.existsSync(settingsPath)) {
2164
2419
  try {
2165
- const content = await fs6__default.default.promises.readFile(settingsPath, "utf-8");
2420
+ const content = await fs8__default.default.promises.readFile(settingsPath, "utf-8");
2166
2421
  existingSettings = JSON.parse(content);
2167
2422
  } catch {
2168
2423
  }
@@ -2170,7 +2425,7 @@ async function setupDefaultSettings() {
2170
2425
  const mergedSettings = { ...defaultSettings, ...existingSettings };
2171
2426
  mergedSettings["workbench.colorTheme"] = "Min Dark";
2172
2427
  mergedSettings["extensions.verifySignature"] = false;
2173
- await fs6__default.default.promises.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
2428
+ await fs8__default.default.promises.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
2174
2429
  console.log(pc4__default.default.green("ama code-server settings configured"));
2175
2430
  }
2176
2431
  async function installExtensions() {
@@ -2181,7 +2436,7 @@ async function installExtensions() {
2181
2436
  for (const ext of extensions) {
2182
2437
  try {
2183
2438
  console.log(pc4__default.default.cyan(`ama installing extension: ${ext}...`));
2184
- await execAsync3(`"${binPath}" --user-data-dir "${STORAGE_DIR}" --install-extension ${ext}`);
2439
+ await execAsync2(`"${binPath}" --user-data-dir "${STORAGE_DIR}" --install-extension ${ext}`);
2185
2440
  console.log(pc4__default.default.green(`ama extension ${ext} installed`));
2186
2441
  } catch (error) {
2187
2442
  console.log(pc4__default.default.yellow(`ama failed to install extension ${ext}`), error);
@@ -2191,7 +2446,7 @@ async function installExtensions() {
2191
2446
  async function startCodeServer(cwd) {
2192
2447
  const binPath = getCodeServerBin();
2193
2448
  const workDir = cwd || process.cwd();
2194
- if (!fs6__default.default.existsSync(binPath)) {
2449
+ if (!fs8__default.default.existsSync(binPath)) {
2195
2450
  throw new Error("ama code-server is not installed. Run installCodeServer() first.");
2196
2451
  }
2197
2452
  await killExistingCodeServer();
@@ -2199,12 +2454,12 @@ async function startCodeServer(cwd) {
2199
2454
  await installExtensions();
2200
2455
  const workspaceStoragePath = path10__default.default.join(STORAGE_DIR, "User", "workspaceStorage");
2201
2456
  try {
2202
- if (fs6__default.default.existsSync(workspaceStoragePath)) {
2203
- await fs6__default.default.promises.rm(workspaceStoragePath, { recursive: true, force: true });
2457
+ if (fs8__default.default.existsSync(workspaceStoragePath)) {
2458
+ await fs8__default.default.promises.rm(workspaceStoragePath, { recursive: true, force: true });
2204
2459
  }
2205
2460
  const stateDbPath = path10__default.default.join(STORAGE_DIR, "User", "globalStorage", "state.vscdb");
2206
- if (fs6__default.default.existsSync(stateDbPath)) {
2207
- await fs6__default.default.promises.unlink(stateDbPath);
2461
+ if (fs8__default.default.existsSync(stateDbPath)) {
2462
+ await fs8__default.default.promises.unlink(stateDbPath);
2208
2463
  }
2209
2464
  } catch {
2210
2465
  }