amai 0.0.9 → 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.
package/dist/cli.cjs CHANGED
@@ -1,18 +1,18 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  'use strict';
3
3
 
4
4
  var pc5 = require('picocolors');
5
5
  var WebSocket = require('ws');
6
6
  var zod = require('zod');
7
- var fs5 = require('fs/promises');
8
7
  var path10 = require('path');
9
- var fs6 = require('fs');
8
+ var fs8 = require('fs');
10
9
  var os3 = require('os');
11
- var child_process = require('child_process');
12
- var util = require('util');
10
+ var fs7 = require('fs/promises');
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
  var url = require('url');
17
17
  var readline = require('readline');
18
18
 
@@ -21,10 +21,10 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
21
 
22
22
  var pc5__default = /*#__PURE__*/_interopDefault(pc5);
23
23
  var WebSocket__default = /*#__PURE__*/_interopDefault(WebSocket);
24
- var fs5__default = /*#__PURE__*/_interopDefault(fs5);
25
24
  var path10__default = /*#__PURE__*/_interopDefault(path10);
26
- var fs6__default = /*#__PURE__*/_interopDefault(fs6);
25
+ var fs8__default = /*#__PURE__*/_interopDefault(fs8);
27
26
  var os3__default = /*#__PURE__*/_interopDefault(os3);
27
+ var fs7__default = /*#__PURE__*/_interopDefault(fs7);
28
28
  var readline__default = /*#__PURE__*/_interopDefault(readline);
29
29
 
30
30
  var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
@@ -42,14 +42,14 @@ var ProjectRegistry = class {
42
42
  }
43
43
  load() {
44
44
  try {
45
- if (fs6__default.default.existsSync(REGISTRY_FILE)) {
46
- const data = fs6__default.default.readFileSync(REGISTRY_FILE, "utf8");
45
+ if (fs8__default.default.existsSync(REGISTRY_FILE)) {
46
+ const data = fs8__default.default.readFileSync(REGISTRY_FILE, "utf8");
47
47
  const parsed = JSON.parse(data);
48
48
  if (!Array.isArray(parsed)) {
49
49
  console.error("Invalid project registry format: expected array, got", typeof parsed);
50
50
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
51
- fs6__default.default.copyFileSync(REGISTRY_FILE, backupFile);
52
- fs6__default.default.unlinkSync(REGISTRY_FILE);
51
+ fs8__default.default.copyFileSync(REGISTRY_FILE, backupFile);
52
+ fs8__default.default.unlinkSync(REGISTRY_FILE);
53
53
  return;
54
54
  }
55
55
  const projects = parsed;
@@ -62,11 +62,11 @@ var ProjectRegistry = class {
62
62
  }
63
63
  } catch (error) {
64
64
  console.error("Failed to load project registry:", error);
65
- if (fs6__default.default.existsSync(REGISTRY_FILE)) {
65
+ if (fs8__default.default.existsSync(REGISTRY_FILE)) {
66
66
  try {
67
67
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
68
- fs6__default.default.copyFileSync(REGISTRY_FILE, backupFile);
69
- fs6__default.default.unlinkSync(REGISTRY_FILE);
68
+ fs8__default.default.copyFileSync(REGISTRY_FILE, backupFile);
69
+ fs8__default.default.unlinkSync(REGISTRY_FILE);
70
70
  console.log("Corrupted registry file backed up and removed. Starting fresh.");
71
71
  } catch (backupError) {
72
72
  }
@@ -75,11 +75,11 @@ var ProjectRegistry = class {
75
75
  }
76
76
  save() {
77
77
  try {
78
- if (!fs6__default.default.existsSync(AMA_DIR)) {
79
- fs6__default.default.mkdirSync(AMA_DIR, { recursive: true });
78
+ if (!fs8__default.default.existsSync(AMA_DIR)) {
79
+ fs8__default.default.mkdirSync(AMA_DIR, { recursive: true });
80
80
  }
81
81
  const projects = Array.from(this.projects.values());
82
- fs6__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
82
+ fs8__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
83
83
  } catch (error) {
84
84
  console.error("Failed to save project registry:", error);
85
85
  }
@@ -168,6 +168,60 @@ zod.z.object({
168
168
  ),
169
169
  end_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to end reading at (inclusive).")
170
170
  });
171
+ async function readFileContent(absolute_file_path, relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed) {
172
+ const file = Bun.file(absolute_file_path);
173
+ const exists = await file.exists();
174
+ if (!exists) {
175
+ return {
176
+ success: false,
177
+ message: `File not found: ${relative_file_path}`,
178
+ error: "FILE_NOT_FOUND"
179
+ };
180
+ }
181
+ try {
182
+ const fileContent = await file.text();
183
+ const lines = fileContent.split(/\r?\n/);
184
+ const totalLines = lines.length;
185
+ if (should_read_entire_file) {
186
+ return {
187
+ success: true,
188
+ message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
189
+ content: fileContent,
190
+ totalLines
191
+ };
192
+ }
193
+ const startIndex = start_line_one_indexed - 1;
194
+ if (startIndex >= totalLines) {
195
+ return {
196
+ success: false,
197
+ message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
198
+ error: "INVALID_LINE_RANGE"
199
+ };
200
+ }
201
+ const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
202
+ const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
203
+ const linesRead = normalizedEnd - start_line_one_indexed + 1;
204
+ return {
205
+ success: true,
206
+ message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
207
+ content: selectedLines,
208
+ totalLines
209
+ };
210
+ } catch (error) {
211
+ if (error?.code === "EISDIR") {
212
+ return {
213
+ success: false,
214
+ message: `Path is not a file: ${relative_file_path}`,
215
+ error: "NOT_A_FILE"
216
+ };
217
+ }
218
+ return {
219
+ success: false,
220
+ message: `Failed to read file: ${relative_file_path}`,
221
+ error: "READ_ERROR"
222
+ };
223
+ }
224
+ }
171
225
  var read_file = async function(input, projectCwd) {
172
226
  const { relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed } = input;
173
227
  try {
@@ -208,6 +262,7 @@ var read_file = async function(input, projectCwd) {
208
262
  };
209
263
  }
210
264
  }
265
+ let absolute_file_path;
211
266
  if (projectCwd) {
212
267
  const validation = validatePath(relative_file_path, projectCwd);
213
268
  if (!validation.valid) {
@@ -217,128 +272,17 @@ var read_file = async function(input, projectCwd) {
217
272
  error: "ACCESS_DENIED"
218
273
  };
219
274
  }
220
- const absolute_file_path = validation.resolvedPath;
221
- try {
222
- const fileStats = await fs5.stat(absolute_file_path);
223
- if (!fileStats.isFile()) {
224
- return {
225
- success: false,
226
- message: `Path is not a file: ${relative_file_path}`,
227
- error: "NOT_A_FILE"
228
- };
229
- }
230
- } catch (error) {
231
- if (error?.code === "ENOENT") {
232
- return {
233
- success: false,
234
- message: `File not found: ${relative_file_path}`,
235
- error: "FILE_NOT_FOUND"
236
- };
237
- }
238
- return {
239
- success: false,
240
- message: `Failed to access file: ${relative_file_path}`,
241
- error: "READ_ERROR"
242
- };
243
- }
244
- try {
245
- const fileContent = await fs5.readFile(absolute_file_path, "utf-8");
246
- const lines = fileContent.split(/\r?\n/);
247
- const totalLines = lines.length;
248
- if (should_read_entire_file) {
249
- return {
250
- success: true,
251
- message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
252
- content: fileContent,
253
- totalLines
254
- };
255
- }
256
- const startIndex = start_line_one_indexed - 1;
257
- if (startIndex >= totalLines) {
258
- return {
259
- success: false,
260
- message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
261
- error: "INVALID_LINE_RANGE"
262
- };
263
- }
264
- const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
265
- const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
266
- const linesRead = normalizedEnd - start_line_one_indexed + 1;
267
- return {
268
- success: true,
269
- message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
270
- content: selectedLines,
271
- totalLines
272
- };
273
- } catch {
274
- return {
275
- success: false,
276
- message: `Failed to read file: ${relative_file_path}`,
277
- error: "READ_ERROR"
278
- };
279
- }
275
+ absolute_file_path = validation.resolvedPath;
280
276
  } else {
281
- const absolute_file_path = path10__default.default.resolve(relative_file_path);
282
- try {
283
- const fileStats = await fs5.stat(absolute_file_path);
284
- if (!fileStats.isFile()) {
285
- return {
286
- success: false,
287
- message: `Path is not a file: ${relative_file_path}`,
288
- error: "NOT_A_FILE"
289
- };
290
- }
291
- } catch (error) {
292
- if (error?.code === "ENOENT") {
293
- return {
294
- success: false,
295
- message: `File not found: ${relative_file_path}`,
296
- error: "FILE_NOT_FOUND"
297
- };
298
- }
299
- return {
300
- success: false,
301
- message: `Failed to access file: ${relative_file_path}`,
302
- error: "READ_ERROR"
303
- };
304
- }
305
- try {
306
- const fileContent = await fs5.readFile(absolute_file_path, "utf-8");
307
- const lines = fileContent.split(/\r?\n/);
308
- const totalLines = lines.length;
309
- if (should_read_entire_file) {
310
- return {
311
- success: true,
312
- message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
313
- content: fileContent,
314
- totalLines
315
- };
316
- }
317
- const startIndex = start_line_one_indexed - 1;
318
- if (startIndex >= totalLines) {
319
- return {
320
- success: false,
321
- message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
322
- error: "INVALID_LINE_RANGE"
323
- };
324
- }
325
- const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
326
- const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
327
- const linesRead = normalizedEnd - start_line_one_indexed + 1;
328
- return {
329
- success: true,
330
- message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
331
- content: selectedLines,
332
- totalLines
333
- };
334
- } catch {
335
- return {
336
- success: false,
337
- message: `Failed to read file: ${relative_file_path}`,
338
- error: "READ_ERROR"
339
- };
340
- }
341
- }
277
+ absolute_file_path = path10__default.default.resolve(relative_file_path);
278
+ }
279
+ return await readFileContent(
280
+ absolute_file_path,
281
+ relative_file_path,
282
+ should_read_entire_file,
283
+ start_line_one_indexed,
284
+ end_line_one_indexed
285
+ );
342
286
  } catch {
343
287
  return {
344
288
  success: false,
@@ -429,13 +373,13 @@ var Diff = class {
429
373
  editLength++;
430
374
  };
431
375
  if (callback) {
432
- (function exec4() {
376
+ (function exec3() {
433
377
  setTimeout(function() {
434
378
  if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
435
379
  return callback(void 0);
436
380
  }
437
381
  if (!execEditLength()) {
438
- exec4();
382
+ exec3();
439
383
  }
440
384
  }, 0);
441
385
  })();
@@ -662,17 +606,19 @@ var apply_patch = async function(input, projectCwd) {
662
606
  }
663
607
  const basePath = projectCwd || process.cwd();
664
608
  const absolute_file_path = resolveProjectPath(file_path, basePath);
609
+ const file = Bun.file(absolute_file_path);
610
+ const exists = await file.exists();
611
+ if (!exists) {
612
+ return {
613
+ success: false,
614
+ message: `File not found: ${file_path}`,
615
+ error: "FILE_NOT_FOUND"
616
+ };
617
+ }
665
618
  let fileContent;
666
619
  try {
667
- fileContent = await fs5.readFile(absolute_file_path, "utf-8");
620
+ fileContent = await file.text();
668
621
  } catch (error) {
669
- if (error?.code === "ENOENT") {
670
- return {
671
- success: false,
672
- message: `File not found: ${file_path}`,
673
- error: "FILE_NOT_FOUND"
674
- };
675
- }
676
622
  return {
677
623
  success: false,
678
624
  message: `Failed to read file: ${file_path}`,
@@ -696,7 +642,7 @@ var apply_patch = async function(input, projectCwd) {
696
642
  }
697
643
  const newContent = fileContent.replace(old_string, new_string);
698
644
  try {
699
- await fs5.writeFile(absolute_file_path, newContent, "utf-8");
645
+ await Bun.write(absolute_file_path, newContent);
700
646
  const diffStats = calculateDiffStats(fileContent, newContent);
701
647
  return {
702
648
  success: true,
@@ -742,28 +688,27 @@ var editFiles = async function(input, projectCwd) {
742
688
  const basePath = projectCwd || process.cwd();
743
689
  const filePath = resolveProjectPath(target_file, basePath);
744
690
  const dirPath = path10__default.default.dirname(filePath);
745
- await fs5.mkdir(dirPath, { recursive: true });
691
+ await fs7.mkdir(dirPath, { recursive: true });
746
692
  let isNewFile = providedNewFile;
747
693
  let existingContent = "";
694
+ const file = Bun.file(filePath);
748
695
  if (isNewFile === void 0) {
749
- try {
750
- existingContent = await fs6__default.default.promises.readFile(filePath, "utf-8");
696
+ const exists = await file.exists();
697
+ if (exists) {
698
+ existingContent = await file.text();
751
699
  isNewFile = false;
752
- } catch (error) {
700
+ } else {
753
701
  isNewFile = true;
754
702
  }
755
703
  } else if (!isNewFile) {
756
- try {
757
- existingContent = await fs6__default.default.promises.readFile(filePath, "utf-8");
758
- } catch (error) {
704
+ const exists = await file.exists();
705
+ if (exists) {
706
+ existingContent = await file.text();
707
+ } else {
759
708
  isNewFile = true;
760
709
  }
761
710
  }
762
- try {
763
- await fs6__default.default.promises.writeFile(filePath, content);
764
- } catch (writeError) {
765
- throw writeError;
766
- }
711
+ await Bun.write(filePath, content);
767
712
  const diffStats = calculateDiffStats(existingContent, content);
768
713
  if (isNewFile) {
769
714
  return {
@@ -826,25 +771,31 @@ var deleteFile = async function(input, projectCwd) {
826
771
  error: "INVALID_FILE_PATH"
827
772
  };
828
773
  }
829
- const originalContent = await fs5.readFile(absolute_file_path);
830
- if (originalContent === void 0) {
774
+ const file = Bun.file(absolute_file_path);
775
+ const exists = await file.exists();
776
+ if (!exists) {
831
777
  return {
832
778
  success: false,
833
- message: `Failed to read file before deletion: ${realPath}`,
834
- error: "READ_ERROR"
779
+ message: `File not found: ${realPath}`,
780
+ error: "FILE_NOT_FOUND"
835
781
  };
836
782
  }
837
- const deleteResult = await fs5.unlink(absolute_file_path).catch(() => {
783
+ let originalContent;
784
+ try {
785
+ originalContent = await file.text();
786
+ } catch {
838
787
  return {
839
788
  success: false,
840
789
  message: `Failed to read file before deletion: ${realPath}`,
841
- error: "DELETE_ERROR"
790
+ error: "READ_ERROR"
842
791
  };
843
- });
844
- if (!deleteResult?.success) {
792
+ }
793
+ try {
794
+ await fs7.unlink(absolute_file_path);
795
+ } catch {
845
796
  return {
846
797
  success: false,
847
- message: `Failed to delete file before deletion: ${realPath}`,
798
+ message: `Failed to delete file: ${realPath}`,
848
799
  error: "DELETE_ERROR"
849
800
  };
850
801
  }
@@ -861,72 +812,218 @@ var deleteFile = async function(input, projectCwd) {
861
812
  };
862
813
  }
863
814
  };
815
+ var GREP_LIMITS = {
816
+ DEFAULT_MAX_MATCHES: 200,
817
+ MAX_LINE_LENGTH: 500,
818
+ MAX_TOTAL_OUTPUT_SIZE: 1 * 1024 * 1024,
819
+ TRUNCATION_MESSAGE: "\n[Results truncated due to size limits. Use more specific patterns or file filters to narrow your search.]"
820
+ };
864
821
  zod.z.object({
865
822
  query: zod.z.string().describe("The regex pattern to search for"),
866
823
  options: zod.z.object({
867
824
  includePattern: zod.z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts")'),
868
825
  excludePattern: zod.z.string().optional().describe("Glob pattern for files to exclude"),
869
- caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive")
870
- })
826
+ caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive"),
827
+ path: zod.z.string().optional().describe("Subdirectory to search in")
828
+ }).optional()
871
829
  });
872
- var execAsync = util.promisify(child_process.exec);
830
+ async function getRipgrepPath() {
831
+ const paths = [
832
+ "/opt/homebrew/bin/rg",
833
+ "/usr/local/bin/rg",
834
+ "/usr/bin/rg",
835
+ "rg"
836
+ // Fallback to PATH
837
+ ];
838
+ for (const rgPath of paths) {
839
+ try {
840
+ const proc = Bun.spawn(["which", rgPath], { stdout: "pipe", stderr: "pipe" });
841
+ await proc.exited;
842
+ if (proc.exitCode === 0) {
843
+ return rgPath;
844
+ }
845
+ } catch {
846
+ continue;
847
+ }
848
+ }
849
+ return "rg";
850
+ }
851
+ async function getMtimesBatched(files) {
852
+ const mtimeMap = /* @__PURE__ */ new Map();
853
+ const BATCH_SIZE = 50;
854
+ for (let i = 0; i < files.length; i += BATCH_SIZE) {
855
+ const batch = files.slice(i, i + BATCH_SIZE);
856
+ const results = await Promise.all(
857
+ batch.map(async (filePath) => {
858
+ const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
859
+ return { path: filePath, mtime };
860
+ })
861
+ );
862
+ results.forEach(({ path: path16, mtime }) => mtimeMap.set(path16, mtime));
863
+ }
864
+ return mtimeMap;
865
+ }
873
866
  var grepTool = async function(input, projectCwd) {
874
867
  const { query, options } = input;
868
+ if (!query || query.trim() === "") {
869
+ return {
870
+ success: false,
871
+ message: "Missing required parameter: query",
872
+ error: "MISSING_QUERY"
873
+ };
874
+ }
875
875
  try {
876
- const { includePattern, excludePattern: excludePattern2, caseSensitive } = options || {};
877
- const searchDir = projectCwd || process.cwd();
878
- if (projectCwd && !path10__default.default.isAbsolute(projectCwd)) {
876
+ const { includePattern, excludePattern, caseSensitive, path: subPath } = options || {};
877
+ let searchDir = projectCwd || process.cwd();
878
+ if (subPath) {
879
+ searchDir = path10__default.default.isAbsolute(subPath) ? subPath : path10__default.default.resolve(searchDir, subPath);
880
+ if (projectCwd) {
881
+ const validation = validatePath(subPath, projectCwd);
882
+ if (!validation.valid) {
883
+ return {
884
+ success: false,
885
+ message: validation.error || "Path validation failed",
886
+ error: "ACCESS_DENIED"
887
+ };
888
+ }
889
+ }
890
+ }
891
+ if (!fs8__default.default.existsSync(searchDir)) {
879
892
  return {
880
893
  success: false,
881
- message: "Invalid project directory",
882
- error: "INVALID_PROJECT_DIR"
894
+ message: `Directory not found: ${searchDir}`,
895
+ error: "DIR_NOT_FOUND"
883
896
  };
884
897
  }
885
- let command = `rg -n --with-filename "${query}"`;
886
- if (caseSensitive) {
887
- command += " -i";
898
+ const rgPath = await getRipgrepPath();
899
+ const args2 = [
900
+ "-n",
901
+ // Line numbers
902
+ "--with-filename",
903
+ // Always show filename
904
+ "--no-heading",
905
+ // Don't group by file
906
+ "--color=never",
907
+ // No ANSI colors
908
+ "--max-count=100",
909
+ // Max matches per file
910
+ "--max-columns=1000"
911
+ // Truncate long lines
912
+ ];
913
+ if (!caseSensitive) {
914
+ args2.push("-i");
888
915
  }
889
916
  if (includePattern) {
890
- command += ` --glob "${includePattern}"`;
891
- }
892
- if (excludePattern2) {
893
- command += ` --glob "!${excludePattern2}"`;
894
- }
895
- command += ` --max-count 50`;
896
- command += ` "${searchDir}"`;
897
- const { stdout } = await execAsync(command);
898
- const rawMatches = stdout.trim().split("\n").filter((line) => line.length > 0);
899
- const detailedMatches = [];
900
- const matches = [];
901
- for (const rawMatch of rawMatches) {
902
- const colonIndex = rawMatch.indexOf(":");
903
- const secondColonIndex = rawMatch.indexOf(":", colonIndex + 1);
904
- if (colonIndex > 0 && secondColonIndex > colonIndex) {
905
- const file = rawMatch.substring(0, colonIndex);
906
- const lineNumber = parseInt(rawMatch.substring(colonIndex + 1, secondColonIndex), 10);
907
- let content = rawMatch.substring(secondColonIndex + 1);
908
- if (content.length > 250) {
909
- content = content.substring(0, 250) + "...";
917
+ args2.push("--glob", includePattern);
918
+ }
919
+ if (excludePattern) {
920
+ args2.push("--glob", `!${excludePattern}`);
921
+ }
922
+ args2.push("--glob", "!node_modules/**");
923
+ args2.push("--glob", "!.git/**");
924
+ args2.push("--glob", "!dist/**");
925
+ args2.push("--glob", "!build/**");
926
+ args2.push("--glob", "!*.min.js");
927
+ args2.push("--glob", "!*.min.css");
928
+ args2.push("--glob", "!package-lock.json");
929
+ args2.push("--glob", "!yarn.lock");
930
+ args2.push("--glob", "!bun.lockb");
931
+ args2.push("--regexp", query);
932
+ args2.push(searchDir);
933
+ const proc = Bun.spawn([rgPath, ...args2], {
934
+ stdout: "pipe",
935
+ stderr: "pipe"
936
+ });
937
+ const stdout = await new Response(proc.stdout).text();
938
+ const stderr = await new Response(proc.stderr).text();
939
+ const exitCode = await proc.exited;
940
+ if (exitCode === 1) {
941
+ return {
942
+ success: true,
943
+ matches: [],
944
+ detailedMatches: [],
945
+ query,
946
+ matchCount: 0,
947
+ message: `No matches found for pattern: ${query}`
948
+ };
949
+ }
950
+ if (exitCode !== 0) {
951
+ return {
952
+ success: false,
953
+ message: `Ripgrep error: ${stderr || "Unknown error"}`,
954
+ error: "GREP_EXEC_ERROR"
955
+ };
956
+ }
957
+ const lines = stdout.trim().split("\n").filter((line) => line.length > 0);
958
+ const rawMatches = [];
959
+ const uniqueFiles = /* @__PURE__ */ new Set();
960
+ for (const line of lines) {
961
+ const firstColon = line.indexOf(":");
962
+ const secondColon = line.indexOf(":", firstColon + 1);
963
+ if (firstColon > 0 && secondColon > firstColon) {
964
+ const file = line.substring(0, firstColon);
965
+ const lineNumber = parseInt(line.substring(firstColon + 1, secondColon), 10);
966
+ let content = line.substring(secondColon + 1);
967
+ if (content.length > GREP_LIMITS.MAX_LINE_LENGTH) {
968
+ content = content.substring(0, GREP_LIMITS.MAX_LINE_LENGTH) + "...";
910
969
  }
911
- detailedMatches.push({
970
+ rawMatches.push({
912
971
  file,
913
972
  lineNumber,
914
- content
973
+ content: content.trim(),
974
+ mtime: 0
915
975
  });
916
- matches.push(`${file}:${lineNumber}:${content}`);
917
- } else {
918
- matches.push(rawMatch);
976
+ uniqueFiles.add(file);
977
+ }
978
+ }
979
+ const mtimeMap = await getMtimesBatched(Array.from(uniqueFiles));
980
+ for (const match of rawMatches) {
981
+ match.mtime = mtimeMap.get(match.file) || 0;
982
+ }
983
+ rawMatches.sort((a, b) => {
984
+ if (b.mtime !== a.mtime) {
985
+ return b.mtime - a.mtime;
986
+ }
987
+ return a.file.localeCompare(b.file);
988
+ });
989
+ const truncated = rawMatches.length > GREP_LIMITS.DEFAULT_MAX_MATCHES;
990
+ const finalMatches = truncated ? rawMatches.slice(0, GREP_LIMITS.DEFAULT_MAX_MATCHES) : rawMatches;
991
+ const detailedMatches = finalMatches.map((m) => ({
992
+ file: m.file,
993
+ lineNumber: m.lineNumber,
994
+ content: m.content
995
+ }));
996
+ const matches = finalMatches.map(
997
+ (m) => `${m.file}:${m.lineNumber}:${m.content}`
998
+ );
999
+ const groupedOutput = [`Found ${finalMatches.length} matches`];
1000
+ let currentFile = "";
1001
+ for (const match of finalMatches) {
1002
+ if (currentFile !== match.file) {
1003
+ if (currentFile !== "") {
1004
+ groupedOutput.push("");
1005
+ }
1006
+ currentFile = match.file;
1007
+ groupedOutput.push(`${match.file}:`);
919
1008
  }
1009
+ groupedOutput.push(` Line ${match.lineNumber}: ${match.content}`);
1010
+ }
1011
+ if (truncated) {
1012
+ groupedOutput.push("");
1013
+ groupedOutput.push(GREP_LIMITS.TRUNCATION_MESSAGE);
920
1014
  }
921
1015
  return {
922
1016
  success: true,
923
1017
  matches,
924
1018
  detailedMatches,
925
1019
  query,
926
- matchCount: matches.length,
927
- message: `Found ${matches.length} matches for pattern: ${query}`
1020
+ matchCount: finalMatches.length,
1021
+ truncated,
1022
+ message: `Found ${finalMatches.length} matches for pattern: ${query}`,
1023
+ content: groupedOutput.join("\n")
928
1024
  };
929
1025
  } catch (error) {
1026
+ console.error("[grep] error:", error);
930
1027
  return {
931
1028
  success: false,
932
1029
  message: error?.message || String(error),
@@ -935,9 +1032,25 @@ var grepTool = async function(input, projectCwd) {
935
1032
  }
936
1033
  };
937
1034
  zod.z.object({
938
- pattern: zod.z.string().describe('Glob pattern (e.g., "**/*.js")'),
939
- path: zod.z.string().optional().describe("Relative directory path to search in")
1035
+ pattern: zod.z.string().describe('Glob pattern to match files (e.g., "**/*.js", "src/**/*.ts", "*.json"). Supports standard glob syntax with *, **, and ? wildcards'),
1036
+ 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")
940
1037
  });
1038
+ var RESULT_LIMIT = 100;
1039
+ var MTIME_BATCH_SIZE = 50;
1040
+ async function getMtimesBatched2(files) {
1041
+ const results = [];
1042
+ for (let i = 0; i < files.length; i += MTIME_BATCH_SIZE) {
1043
+ const batch = files.slice(i, i + MTIME_BATCH_SIZE);
1044
+ const batchResults = await Promise.all(
1045
+ batch.map(async (filePath) => {
1046
+ const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
1047
+ return { path: filePath, mtime };
1048
+ })
1049
+ );
1050
+ results.push(...batchResults);
1051
+ }
1052
+ return results;
1053
+ }
941
1054
  var globTool = async function(input, projectCwd) {
942
1055
  const { pattern, path: inputPath } = input;
943
1056
  if (!pattern) {
@@ -950,6 +1063,13 @@ var globTool = async function(input, projectCwd) {
950
1063
  try {
951
1064
  const basePath = projectCwd || process.cwd();
952
1065
  const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
1066
+ if (!fs8__default.default.existsSync(searchPath)) {
1067
+ return {
1068
+ success: false,
1069
+ message: `Directory not found: ${searchPath}`,
1070
+ error: "DIR_NOT_FOUND"
1071
+ };
1072
+ }
953
1073
  if (projectCwd && inputPath) {
954
1074
  const validation = validatePath(inputPath, projectCwd);
955
1075
  if (!validation.valid) {
@@ -960,21 +1080,49 @@ var globTool = async function(input, projectCwd) {
960
1080
  };
961
1081
  }
962
1082
  }
963
- const filesGenerator = fs5.glob(pattern, {
964
- cwd: searchPath
965
- });
1083
+ const glob = new Bun.Glob(pattern);
966
1084
  const files = [];
967
- for await (const file of filesGenerator) {
968
- files.push(file);
1085
+ let truncated = false;
1086
+ for await (const match of glob.scan({
1087
+ cwd: searchPath,
1088
+ absolute: true,
1089
+ onlyFiles: true,
1090
+ followSymlinks: false
1091
+ })) {
1092
+ if (match.includes("/node_modules/") || match.includes("/.git/")) {
1093
+ continue;
1094
+ }
1095
+ if (files.length >= RESULT_LIMIT) {
1096
+ truncated = true;
1097
+ break;
1098
+ }
1099
+ files.push(match);
1100
+ }
1101
+ const filesWithMtime = await getMtimesBatched2(files);
1102
+ filesWithMtime.sort((a, b) => b.mtime - a.mtime);
1103
+ const output = [];
1104
+ if (filesWithMtime.length === 0) {
1105
+ output.push("No files found");
1106
+ } else {
1107
+ output.push(...filesWithMtime.map((f) => f.path));
1108
+ if (truncated) {
1109
+ output.push("");
1110
+ output.push("(Results are truncated. Consider using a more specific path or pattern.)");
1111
+ }
969
1112
  }
970
1113
  const searchLocation = inputPath ? ` in "${inputPath}"` : " in current directory";
971
- const message = `Found ${files.length} matches for pattern "${pattern}"${searchLocation}`;
1114
+ const message = `Found ${filesWithMtime.length} matches for pattern "${pattern}"${searchLocation}`;
972
1115
  return {
973
1116
  success: true,
974
1117
  message,
975
- content: files
1118
+ metadata: {
1119
+ count: filesWithMtime.length,
1120
+ truncated
1121
+ },
1122
+ content: output.join("\n")
976
1123
  };
977
1124
  } catch (error) {
1125
+ console.error("[glob] error:", error);
978
1126
  return {
979
1127
  success: false,
980
1128
  message: `Failed to find files matching pattern: ${pattern}`,
@@ -982,59 +1130,120 @@ var globTool = async function(input, projectCwd) {
982
1130
  };
983
1131
  }
984
1132
  };
985
- var excludePatterns = [
986
- "node_modules/",
987
- "__pycache__/",
988
- ".git/",
989
- "dist/",
990
- "build/",
991
- "target/",
992
- "vendor/",
993
- "bin/",
994
- "obj/",
995
- ".idea/",
996
- ".vscode/",
997
- ".zig-cache/",
1133
+ var IGNORE_PATTERNS = [
1134
+ "node_modules",
1135
+ "__pycache__",
1136
+ ".git",
1137
+ "dist",
1138
+ "build",
1139
+ "target",
1140
+ "vendor",
1141
+ "bin",
1142
+ "obj",
1143
+ ".idea",
1144
+ ".vscode",
1145
+ ".zig-cache",
998
1146
  "zig-out",
999
1147
  ".coverage",
1000
- "coverage/",
1001
- "vendor/",
1002
- "tmp/",
1003
- "temp/",
1004
- ".cache/",
1005
- "cache/",
1006
- "logs/",
1007
- ".venv/",
1008
- "venv/",
1009
- "env/"
1148
+ "coverage",
1149
+ "tmp",
1150
+ "temp",
1151
+ ".cache",
1152
+ "cache",
1153
+ "logs",
1154
+ ".venv",
1155
+ "venv",
1156
+ "env",
1157
+ ".next",
1158
+ ".turbo",
1159
+ ".vercel",
1160
+ ".output"
1010
1161
  ];
1011
- var excludePattern = excludePatterns.join("|");
1162
+ var RESULT_LIMIT2 = 500;
1163
+ var MTIME_BATCH_SIZE2 = 50;
1012
1164
  zod.z.object({
1013
- path: zod.z.string().optional(),
1014
- recursive: zod.z.boolean().optional().describe("Whether to list files recursively"),
1015
- maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: unlimited)"),
1165
+ path: zod.z.string().optional().describe("Relative path to the directory to list"),
1166
+ recursive: zod.z.boolean().optional().describe("Whether to list files recursively (default: true)"),
1167
+ maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: 3)"),
1016
1168
  pattern: zod.z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
1017
- includeDirectories: zod.z.boolean().optional().describe("Whether to include directories in results (default: true)"),
1018
- includeFiles: zod.z.boolean().optional().describe("Whether to include files in results (default: true)")
1169
+ showHidden: zod.z.boolean().optional().describe("Whether to show hidden files (default: false)")
1019
1170
  });
1020
- var list = async function(input, projectCwd) {
1021
- const { path: relativePath, recursive, maxDepth, pattern, includeDirectories, includeFiles } = input;
1022
- if (maxDepth !== void 0) {
1023
- if (!Number.isInteger(maxDepth) || maxDepth < 0) {
1024
- return {
1025
- success: false,
1026
- message: "maxDepth must be a non-negative integer",
1027
- error: "INVALID_MAX_DEPTH"
1028
- };
1171
+ function shouldIgnore(name, showHidden) {
1172
+ if (!showHidden && name.startsWith(".") && name !== ".") {
1173
+ return true;
1174
+ }
1175
+ return IGNORE_PATTERNS.includes(name);
1176
+ }
1177
+ function matchPattern(name, pattern) {
1178
+ if (!pattern) return true;
1179
+ if (pattern.startsWith(".") && !pattern.includes("*")) {
1180
+ return name.endsWith(pattern);
1181
+ }
1182
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1183
+ const regex = new RegExp(`^${escaped}$`, "i");
1184
+ return regex.test(name);
1185
+ }
1186
+ async function getMtimesBatched3(entries) {
1187
+ for (let i = 0; i < entries.length; i += MTIME_BATCH_SIZE2) {
1188
+ const batch = entries.slice(i, i + MTIME_BATCH_SIZE2);
1189
+ await Promise.all(
1190
+ batch.map(async (entry) => {
1191
+ entry.mtime = await Bun.file(entry.absolutePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
1192
+ })
1193
+ );
1194
+ }
1195
+ }
1196
+ function buildTreeOutput(entries, basePath) {
1197
+ const tree = /* @__PURE__ */ new Map();
1198
+ for (const entry of entries) {
1199
+ const dir = path10__default.default.dirname(entry.relativePath);
1200
+ const dirKey = dir === "." ? "" : dir;
1201
+ if (!tree.has(dirKey)) {
1202
+ tree.set(dirKey, []);
1203
+ }
1204
+ tree.get(dirKey).push(entry);
1205
+ }
1206
+ for (const [, items] of tree) {
1207
+ items.sort((a, b) => {
1208
+ if (a.type !== b.type) {
1209
+ return a.type === "directory" ? -1 : 1;
1210
+ }
1211
+ return a.name.localeCompare(b.name);
1212
+ });
1213
+ }
1214
+ const lines = [`${basePath}/`];
1215
+ function renderLevel(dirPath, indent) {
1216
+ const items = tree.get(dirPath) || [];
1217
+ for (let i = 0; i < items.length; i++) {
1218
+ const item = items[i];
1219
+ const isLast = i === items.length - 1;
1220
+ const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1221
+ const childIndent = indent + (isLast ? " " : "\u2502 ");
1222
+ if (item.type === "directory") {
1223
+ lines.push(`${indent}${prefix}${item.name}/`);
1224
+ const childPath = dirPath ? `${dirPath}/${item.name}` : item.name;
1225
+ renderLevel(childPath, childIndent);
1226
+ } else {
1227
+ lines.push(`${indent}${prefix}${item.name}`);
1228
+ }
1029
1229
  }
1030
1230
  }
1031
- const includeFilesNormalized = includeFiles ?? true;
1032
- const includeDirectoriesNormalized = includeDirectories ?? true;
1033
- if (!includeFilesNormalized && !includeDirectoriesNormalized) {
1231
+ renderLevel("", "");
1232
+ return lines.join("\n");
1233
+ }
1234
+ var list = async function(input, projectCwd) {
1235
+ const {
1236
+ path: relativePath,
1237
+ recursive = true,
1238
+ maxDepth = 3,
1239
+ pattern,
1240
+ showHidden = false
1241
+ } = input;
1242
+ if (maxDepth !== void 0 && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
1034
1243
  return {
1035
1244
  success: false,
1036
- message: "At least one of includeFiles or includeDirectories must be true",
1037
- error: "INVALID_INCLUDE_OPTIONS"
1245
+ message: "maxDepth must be a non-negative integer",
1246
+ error: "INVALID_MAX_DEPTH"
1038
1247
  };
1039
1248
  }
1040
1249
  try {
@@ -1050,88 +1259,119 @@ var list = async function(input, projectCwd) {
1050
1259
  };
1051
1260
  }
1052
1261
  }
1053
- try {
1054
- await fs5.access(absolutePath);
1055
- } catch {
1262
+ if (!fs8__default.default.existsSync(absolutePath)) {
1056
1263
  return {
1057
1264
  success: false,
1058
- message: `File does not exist: ${absolutePath}`,
1059
- error: "FILE_DOES_NOT_EXIST"
1265
+ message: `Directory not found: ${absolutePath}`,
1266
+ error: "DIR_NOT_FOUND"
1060
1267
  };
1061
1268
  }
1062
- const isDir = (await fs5.stat(absolutePath)).isDirectory();
1063
- if (!isDir) {
1269
+ const stats = fs8__default.default.statSync(absolutePath);
1270
+ if (!stats.isDirectory()) {
1064
1271
  return {
1065
1272
  success: false,
1066
- message: `File is not a directory: ${absolutePath}`,
1067
- error: "FILE_IS_NOT_A_DIRECTORY"
1273
+ message: `Path is not a directory: ${absolutePath}`,
1274
+ error: "NOT_A_DIRECTORY"
1068
1275
  };
1069
1276
  }
1070
1277
  const collected = [];
1071
- const patternMatcher = (() => {
1072
- if (!pattern) return null;
1073
- if (pattern.startsWith(".") && !pattern.includes("*") && !pattern.includes("?")) {
1074
- return (entryName) => entryName.endsWith(pattern);
1075
- }
1076
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1077
- const regex = new RegExp(`^${escaped}$`);
1078
- return (entryName) => regex.test(entryName);
1079
- })();
1080
- const matchPattern = (entryName) => {
1081
- if (!patternMatcher) return true;
1082
- return patternMatcher(entryName);
1083
- };
1084
- const maxDepthNormalized = recursive ? maxDepth ?? Infinity : 0;
1278
+ let truncated = false;
1085
1279
  const walk = async (currentDir, depth) => {
1086
- const entries = await fs5.readdir(currentDir, { withFileTypes: true });
1280
+ if (collected.length >= RESULT_LIMIT2) {
1281
+ truncated = true;
1282
+ return;
1283
+ }
1284
+ let entries;
1285
+ try {
1286
+ entries = fs8__default.default.readdirSync(currentDir, { withFileTypes: true });
1287
+ } catch {
1288
+ return;
1289
+ }
1290
+ entries.sort((a, b) => {
1291
+ if (a.isDirectory() !== b.isDirectory()) {
1292
+ return a.isDirectory() ? -1 : 1;
1293
+ }
1294
+ return a.name.localeCompare(b.name);
1295
+ });
1087
1296
  for (const entry of entries) {
1297
+ if (collected.length >= RESULT_LIMIT2) {
1298
+ truncated = true;
1299
+ break;
1300
+ }
1301
+ if (shouldIgnore(entry.name, showHidden)) {
1302
+ continue;
1303
+ }
1088
1304
  const entryAbsolutePath = path10__default.default.join(currentDir, entry.name);
1089
- const entryRelativePath = path10__default.default.relative(absolutePath, entryAbsolutePath) || ".";
1305
+ const entryRelativePath = path10__default.default.relative(absolutePath, entryAbsolutePath);
1090
1306
  if (entry.isDirectory()) {
1091
- const isExcluded = entry.name.match(excludePattern);
1092
- if (includeDirectoriesNormalized && matchPattern(entry.name) && !isExcluded) {
1093
- collected.push({
1094
- name: entry.name,
1095
- absolutePath: entryAbsolutePath,
1096
- relativePath: entryRelativePath,
1097
- type: "directory"
1098
- });
1099
- }
1100
- if (recursive && depth < maxDepthNormalized && !isExcluded) {
1307
+ collected.push({
1308
+ name: entry.name,
1309
+ absolutePath: entryAbsolutePath,
1310
+ relativePath: entryRelativePath,
1311
+ type: "directory",
1312
+ mtime: 0,
1313
+ depth
1314
+ });
1315
+ if (recursive && depth < maxDepth) {
1101
1316
  await walk(entryAbsolutePath, depth + 1);
1102
1317
  }
1103
1318
  } else if (entry.isFile()) {
1104
- if (includeFilesNormalized && matchPattern(entry.name) && !entry.name.match(excludePattern)) {
1319
+ if (matchPattern(entry.name, pattern)) {
1105
1320
  collected.push({
1106
1321
  name: entry.name,
1107
1322
  absolutePath: entryAbsolutePath,
1108
1323
  relativePath: entryRelativePath,
1109
- type: "file"
1324
+ type: "file",
1325
+ mtime: 0,
1326
+ depth
1110
1327
  });
1111
1328
  }
1112
1329
  }
1113
1330
  }
1114
1331
  };
1115
1332
  await walk(absolutePath, 0);
1333
+ await getMtimesBatched3(collected);
1116
1334
  const totalFiles = collected.filter((item) => item.type === "file").length;
1117
1335
  const totalDirectories = collected.filter((item) => item.type === "directory").length;
1118
- let message = `Successfully listed ${collected.length} items in: ${relativePath ?? absolutePath}`;
1336
+ const treeOutput = buildTreeOutput(collected, relativePath || path10__default.default.basename(absolutePath));
1337
+ let message = `Listed ${collected.length} items`;
1338
+ if (relativePath) {
1339
+ message += ` in "${relativePath}"`;
1340
+ }
1341
+ message += ` (${totalFiles} files, ${totalDirectories} directories)`;
1119
1342
  if (recursive) {
1120
- message += ` (recursive${maxDepth !== void 0 ? `, max depth ${maxDepth}` : ""})`;
1343
+ message += ` [depth: ${maxDepth}]`;
1121
1344
  }
1122
1345
  if (pattern) {
1123
- message += ` (filtered by pattern: ${pattern})`;
1346
+ message += ` [filter: ${pattern}]`;
1347
+ }
1348
+ if (truncated) {
1349
+ message += ` [TRUNCATED at ${RESULT_LIMIT2} items]`;
1124
1350
  }
1125
- message += ` - ${totalFiles} files, ${totalDirectories} directories`;
1351
+ const files = collected.map((item) => ({
1352
+ name: item.name,
1353
+ path: item.relativePath,
1354
+ type: item.type
1355
+ }));
1126
1356
  return {
1127
1357
  success: true,
1128
1358
  message,
1129
- files: collected
1359
+ metadata: {
1360
+ totalFiles,
1361
+ totalDirectories,
1362
+ totalItems: collected.length,
1363
+ truncated,
1364
+ maxDepth,
1365
+ recursive
1366
+ },
1367
+ files,
1368
+ content: treeOutput
1130
1369
  };
1131
1370
  } catch (error) {
1371
+ console.error("[list] error:", error);
1132
1372
  return {
1133
1373
  success: false,
1134
- message: `Failed to list files: ${error}`,
1374
+ message: `Failed to list directory: ${error}`,
1135
1375
  error: "LIST_ERROR"
1136
1376
  };
1137
1377
  }
@@ -1149,10 +1389,10 @@ var CREDENTIALS_DIR = path10__default.default.join(os3__default.default.homedir(
1149
1389
  var CREDENTIALS_PATH = path10__default.default.join(CREDENTIALS_DIR, "credentials.json");
1150
1390
  function isAuthenticated() {
1151
1391
  try {
1152
- if (!fs6__default.default.existsSync(CREDENTIALS_PATH)) {
1392
+ if (!fs8__default.default.existsSync(CREDENTIALS_PATH)) {
1153
1393
  return false;
1154
1394
  }
1155
- const raw = fs6__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1395
+ const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1156
1396
  const data = JSON.parse(raw);
1157
1397
  return Boolean(data && data.access_token);
1158
1398
  } catch {
@@ -1161,10 +1401,10 @@ function isAuthenticated() {
1161
1401
  }
1162
1402
  function saveTokens(tokens) {
1163
1403
  try {
1164
- if (!fs6__default.default.existsSync(CREDENTIALS_DIR)) {
1165
- fs6__default.default.mkdirSync(CREDENTIALS_DIR, { recursive: true });
1404
+ if (!fs8__default.default.existsSync(CREDENTIALS_DIR)) {
1405
+ fs8__default.default.mkdirSync(CREDENTIALS_DIR, { recursive: true });
1166
1406
  }
1167
- fs6__default.default.writeFileSync(
1407
+ fs8__default.default.writeFileSync(
1168
1408
  CREDENTIALS_PATH,
1169
1409
  JSON.stringify(tokens, null, 2),
1170
1410
  "utf8"
@@ -1175,18 +1415,18 @@ function saveTokens(tokens) {
1175
1415
  }
1176
1416
  function logout() {
1177
1417
  try {
1178
- if (fs6__default.default.existsSync(CREDENTIALS_PATH)) {
1179
- fs6__default.default.unlinkSync(CREDENTIALS_PATH);
1418
+ if (fs8__default.default.existsSync(CREDENTIALS_PATH)) {
1419
+ fs8__default.default.unlinkSync(CREDENTIALS_PATH);
1180
1420
  }
1181
1421
  } catch (error) {
1182
1422
  console.error(pc5__default.default.red("Failed to logout"), error);
1183
1423
  }
1184
1424
  }
1185
1425
  function getTokens() {
1186
- if (!fs6__default.default.existsSync(CREDENTIALS_PATH)) {
1426
+ if (!fs8__default.default.existsSync(CREDENTIALS_PATH)) {
1187
1427
  return null;
1188
1428
  }
1189
- const raw = fs6__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1429
+ const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1190
1430
  const data = JSON.parse(raw);
1191
1431
  return data;
1192
1432
  }
@@ -1254,30 +1494,19 @@ async function pollForTokens({
1254
1494
  async function login() {
1255
1495
  try {
1256
1496
  const device = await authorizeDevice();
1257
- console.log(pc5__default.default.bold("To sign in, follow these steps:"));
1497
+ console.log("");
1258
1498
  if (device.verification_uri_complete) {
1259
- console.log(
1260
- ` 1. Open this URL in your browser: ${pc5__default.default.cyan(
1261
- device.verification_uri_complete
1262
- )}`
1263
- );
1499
+ console.log(pc5__default.default.cyan(`open: ${device.verification_uri_complete}`));
1264
1500
  } else {
1265
- console.log(
1266
- ` 1. Open this URL in your browser: ${pc5__default.default.cyan(
1267
- device.verification_uri
1268
- )}`
1269
- );
1270
- console.log(
1271
- ` 2. Enter this code when prompted: ${pc5__default.default.bold(device.user_code)}`
1272
- );
1501
+ console.log(pc5__default.default.cyan(`open: ${device.verification_uri}`));
1502
+ console.log(pc5__default.default.gray(`code: ${device.user_code}`));
1273
1503
  }
1274
- console.log(" 3. Come back here; we will detect when you finish logging in.");
1275
- console.log();
1504
+ console.log("");
1276
1505
  console.log(
1277
1506
  pc5__default.default.gray(
1278
- `Waiting for authorization (expires in ~${Math.round(
1507
+ `waiting for authorization (~${Math.round(
1279
1508
  device.expires_in / 60
1280
- )} minutes)...`
1509
+ )} min)...`
1281
1510
  )
1282
1511
  );
1283
1512
  const tokens = await pollForTokens({
@@ -1286,20 +1515,20 @@ async function login() {
1286
1515
  expiresIn: device.expires_in,
1287
1516
  interval: device.interval
1288
1517
  });
1289
- console.log(pc5__default.default.green("Successfully authenticated!"));
1518
+ console.log(pc5__default.default.cyan("authenticated"));
1290
1519
  saveTokens(tokens);
1291
1520
  return tokens;
1292
1521
  } catch (error) {
1293
- console.error(pc5__default.default.red(error.message || "Login failed"));
1522
+ console.error(pc5__default.default.red(error.message || "login failed"));
1294
1523
  throw error;
1295
1524
  }
1296
1525
  }
1297
1526
  var getUserId = () => {
1298
1527
  try {
1299
- if (!fs6__default.default.existsSync(CREDENTIALS_PATH)) {
1528
+ if (!fs8__default.default.existsSync(CREDENTIALS_PATH)) {
1300
1529
  return;
1301
1530
  }
1302
- const raw = fs6__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1531
+ const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1303
1532
  const data = JSON.parse(raw);
1304
1533
  return {
1305
1534
  userId: data.user.id
@@ -1348,7 +1577,7 @@ zod.z.object({
1348
1577
  command: zod.z.string().describe("The terminal command to execute (e.g., 'ls -la', 'pwd', 'echo $HOME')"),
1349
1578
  is_background: zod.z.boolean().describe("Whether the command should be run in the background")
1350
1579
  }).merge(ExplanationSchema);
1351
- var runSecureTerminalCommand = async (command, timeout) => {
1580
+ var runSecureTerminalCommand = async (command, timeout, cwd) => {
1352
1581
  try {
1353
1582
  if (isHarmfulCommand(command)) {
1354
1583
  console.log(`[CLI] Harmful command detected: ${command}`);
@@ -1358,42 +1587,44 @@ var runSecureTerminalCommand = async (command, timeout) => {
1358
1587
  error: "HARMFUL_COMMAND_DETECTED"
1359
1588
  };
1360
1589
  }
1361
- return new Promise((resolve, reject) => {
1362
- const child = child_process.spawn(command, {
1363
- cwd: process.cwd(),
1364
- stdio: ["pipe", "pipe", "pipe"],
1365
- shell: true
1366
- });
1367
- let stdout = "";
1368
- let stderr = "";
1369
- let timeoutId = null;
1370
- if (timeoutId > 0) {
1371
- timeoutId = setTimeout(() => {
1372
- child.kill("SIGKILL");
1373
- reject(new Error(`Command timed out after ${timeout}ms`));
1374
- }, timeout);
1375
- }
1376
- child.stdout?.on("data", (data) => {
1377
- stdout += data.toString();
1378
- });
1379
- child.stderr?.on("data", (data) => {
1380
- stderr += data.toString();
1381
- });
1382
- child.stdout.on("close", (code) => {
1383
- if (timeoutId) {
1384
- clearTimeout(timeoutId);
1385
- }
1386
- resolve({ stdout, stderr, exitCode: code || 0 });
1387
- });
1388
- child.stderr.on("error", (error) => {
1389
- if (timeoutId) {
1390
- clearTimeout(timeoutId);
1391
- }
1392
- reject(error);
1393
- });
1590
+ const proc = Bun.spawn(["sh", "-c", command], {
1591
+ cwd: cwd || process.cwd(),
1592
+ stdout: "pipe",
1593
+ stderr: "pipe"
1394
1594
  });
1395
- } catch {
1396
- console.error("Error while ecexuting the securedShell command");
1595
+ let timedOut = false;
1596
+ let timeoutId = null;
1597
+ if (timeout > 0) {
1598
+ timeoutId = setTimeout(() => {
1599
+ timedOut = true;
1600
+ proc.kill();
1601
+ }, timeout);
1602
+ }
1603
+ const [stdout, stderr, exitCode] = await Promise.all([
1604
+ new Response(proc.stdout).text(),
1605
+ new Response(proc.stderr).text(),
1606
+ proc.exited
1607
+ ]);
1608
+ if (timeoutId) {
1609
+ clearTimeout(timeoutId);
1610
+ }
1611
+ if (timedOut) {
1612
+ return {
1613
+ success: false,
1614
+ message: `Command timed out after ${timeout}ms`,
1615
+ error: "TIMEOUT",
1616
+ stdout,
1617
+ stderr
1618
+ };
1619
+ }
1620
+ return { stdout, stderr, exitCode };
1621
+ } catch (error) {
1622
+ console.error("Error while executing the securedShell command", error);
1623
+ return {
1624
+ success: false,
1625
+ message: "Error while executing the securedShell command",
1626
+ error: error.message
1627
+ };
1397
1628
  }
1398
1629
  };
1399
1630
  var runTerminalCommand = async (input, projectCwd) => {
@@ -1407,13 +1638,12 @@ var runTerminalCommand = async (input, projectCwd) => {
1407
1638
  error: "HARMFUL_COMMAND_DETECTED"
1408
1639
  };
1409
1640
  }
1410
- const child = child_process.spawn(input.command, {
1411
- cwd: projectCwd,
1412
- detached: true,
1413
- stdio: "ignore",
1414
- shell: true
1641
+ const proc = Bun.spawn(["sh", "-c", input.command], {
1642
+ cwd: projectCwd || process.cwd(),
1643
+ stdout: "ignore",
1644
+ stderr: "ignore"
1415
1645
  });
1416
- child.unref();
1646
+ proc.unref();
1417
1647
  console.log(`[LOCAL] Background command started: ${input.command}`);
1418
1648
  return {
1419
1649
  success: true,
@@ -1423,9 +1653,13 @@ var runTerminalCommand = async (input, projectCwd) => {
1423
1653
  } else {
1424
1654
  const result = await runSecureTerminalCommand(
1425
1655
  input.command,
1426
- 3e4
1656
+ 3e4,
1427
1657
  // 30 second timeout
1658
+ projectCwd
1428
1659
  );
1660
+ if (result?.error && !result?.exitCode) {
1661
+ return result;
1662
+ }
1429
1663
  const success = result?.exitCode === 0;
1430
1664
  return {
1431
1665
  success,
@@ -1444,9 +1678,9 @@ var runTerminalCommand = async (input, projectCwd) => {
1444
1678
  };
1445
1679
  }
1446
1680
  };
1447
- var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local"];
1681
+ 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"];
1448
1682
  var getContext = (dir, base = dir, allFiles = []) => {
1449
- const filePath = fs6.readdirSync(dir, { withFileTypes: true });
1683
+ const filePath = fs8.readdirSync(dir, { withFileTypes: true });
1450
1684
  for (const file of filePath) {
1451
1685
  if (ignoreFiles.includes(file.name)) continue;
1452
1686
  const fullPath = path10__default.default.join(dir, file.name);
@@ -1466,28 +1700,34 @@ var IDE_PROJECTS_PATHS = {
1466
1700
  };
1467
1701
  function getWorkspaceStoragePath(ide) {
1468
1702
  const platform = os3__default.default.platform();
1469
- const appName = "Cursor" ;
1703
+ const appName = ide === "cursor" ? "Cursor" : "Code";
1704
+ const appNameLower = appName.toLowerCase();
1470
1705
  if (platform === "darwin") {
1471
1706
  return path10__default.default.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
1472
1707
  } else if (platform === "win32") {
1473
1708
  return path10__default.default.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
1474
1709
  } else {
1475
- return path10__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
1710
+ const capitalizedPath = path10__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
1711
+ const lowercasePath = path10__default.default.join(HOME, ".config", appNameLower, "User", "workspaceStorage");
1712
+ if (fs8__default.default.existsSync(capitalizedPath)) {
1713
+ return capitalizedPath;
1714
+ }
1715
+ return lowercasePath;
1476
1716
  }
1477
1717
  }
1478
1718
  function scanWorkspaceStorage(ide) {
1479
1719
  const projects = [];
1480
- const storagePath = getWorkspaceStoragePath();
1481
- if (!fs6__default.default.existsSync(storagePath)) {
1720
+ const storagePath = getWorkspaceStoragePath(ide);
1721
+ if (!fs8__default.default.existsSync(storagePath)) {
1482
1722
  return projects;
1483
1723
  }
1484
1724
  try {
1485
- const workspaces = fs6__default.default.readdirSync(storagePath);
1725
+ const workspaces = fs8__default.default.readdirSync(storagePath);
1486
1726
  for (const workspace of workspaces) {
1487
1727
  const workspaceJsonPath = path10__default.default.join(storagePath, workspace, "workspace.json");
1488
- if (fs6__default.default.existsSync(workspaceJsonPath)) {
1728
+ if (fs8__default.default.existsSync(workspaceJsonPath)) {
1489
1729
  try {
1490
- const content = fs6__default.default.readFileSync(workspaceJsonPath, "utf-8");
1730
+ const content = fs8__default.default.readFileSync(workspaceJsonPath, "utf-8");
1491
1731
  const data = JSON.parse(content);
1492
1732
  if (data.folder && typeof data.folder === "string") {
1493
1733
  let projectPath = data.folder;
@@ -1495,7 +1735,7 @@ function scanWorkspaceStorage(ide) {
1495
1735
  projectPath = projectPath.replace("file://", "");
1496
1736
  projectPath = decodeURIComponent(projectPath);
1497
1737
  }
1498
- if (fs6__default.default.existsSync(projectPath) && fs6__default.default.statSync(projectPath).isDirectory()) {
1738
+ if (fs8__default.default.existsSync(projectPath) && fs8__default.default.statSync(projectPath).isDirectory()) {
1499
1739
  projects.push({
1500
1740
  name: path10__default.default.basename(projectPath),
1501
1741
  path: projectPath,
@@ -1519,11 +1759,11 @@ var scanIdeProjects = async () => {
1519
1759
  const seenPaths = /* @__PURE__ */ new Set();
1520
1760
  const addProject = (projectPath, ide) => {
1521
1761
  try {
1522
- const resolvedPath = fs6__default.default.realpathSync(projectPath);
1523
- if (fs6__default.default.existsSync(resolvedPath) && fs6__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1762
+ const resolvedPath = fs8__default.default.realpathSync(projectPath);
1763
+ if (fs8__default.default.existsSync(resolvedPath) && fs8__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1524
1764
  const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
1525
1765
  try {
1526
- return fs6__default.default.realpathSync(ideDir) === resolvedPath;
1766
+ return fs8__default.default.realpathSync(ideDir) === resolvedPath;
1527
1767
  } catch {
1528
1768
  return false;
1529
1769
  }
@@ -1544,32 +1784,36 @@ var scanIdeProjects = async () => {
1544
1784
  for (const project of cursorProjects) {
1545
1785
  addProject(project.path, "cursor");
1546
1786
  }
1787
+ const vscodeProjects = scanWorkspaceStorage("vscode");
1788
+ for (const project of vscodeProjects) {
1789
+ addProject(project.path, "vscode");
1790
+ }
1547
1791
  for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
1548
- if (ide === "cursor") continue;
1549
- if (fs6__default.default.existsSync(dirPath)) {
1550
- const projects = fs6__default.default.readdirSync(dirPath);
1792
+ if (ide === "cursor" || ide === "vscode") continue;
1793
+ if (fs8__default.default.existsSync(dirPath)) {
1794
+ const projects = fs8__default.default.readdirSync(dirPath);
1551
1795
  projects.forEach((project) => {
1552
1796
  const projectPath = path10__default.default.join(dirPath, project);
1553
1797
  try {
1554
- const stats = fs6__default.default.lstatSync(projectPath);
1798
+ const stats = fs8__default.default.lstatSync(projectPath);
1555
1799
  let actualPath = null;
1556
1800
  if (stats.isSymbolicLink()) {
1557
- actualPath = fs6__default.default.realpathSync(projectPath);
1801
+ actualPath = fs8__default.default.realpathSync(projectPath);
1558
1802
  } else if (stats.isFile()) {
1559
1803
  try {
1560
- let content = fs6__default.default.readFileSync(projectPath, "utf-8").trim();
1804
+ let content = fs8__default.default.readFileSync(projectPath, "utf-8").trim();
1561
1805
  if (content.startsWith("~/") || content === "~") {
1562
1806
  content = content.replace(/^~/, HOME);
1563
1807
  }
1564
1808
  const resolvedContent = path10__default.default.isAbsolute(content) ? content : path10__default.default.resolve(path10__default.default.dirname(projectPath), content);
1565
- if (fs6__default.default.existsSync(resolvedContent) && fs6__default.default.statSync(resolvedContent).isDirectory()) {
1566
- actualPath = fs6__default.default.realpathSync(resolvedContent);
1809
+ if (fs8__default.default.existsSync(resolvedContent) && fs8__default.default.statSync(resolvedContent).isDirectory()) {
1810
+ actualPath = fs8__default.default.realpathSync(resolvedContent);
1567
1811
  }
1568
1812
  } catch {
1569
1813
  return;
1570
1814
  }
1571
1815
  } else if (stats.isDirectory()) {
1572
- actualPath = fs6__default.default.realpathSync(projectPath);
1816
+ actualPath = fs8__default.default.realpathSync(projectPath);
1573
1817
  }
1574
1818
  if (actualPath) {
1575
1819
  addProject(actualPath, ide);
@@ -1593,7 +1837,7 @@ var Global;
1593
1837
  })(Global || (Global = {}));
1594
1838
 
1595
1839
  // src/snapshot/snapshot.ts
1596
- var execAsync2 = util.promisify(child_process.exec);
1840
+ var execAsync = util.promisify(child_process.exec);
1597
1841
  var Snapshot;
1598
1842
  ((Snapshot2) => {
1599
1843
  const log = {
@@ -1603,7 +1847,7 @@ var Snapshot;
1603
1847
  };
1604
1848
  async function runGit(command, options = {}) {
1605
1849
  try {
1606
- const { stdout, stderr } = await execAsync2(command, {
1850
+ const { stdout, stderr } = await execAsync(command, {
1607
1851
  cwd: options.cwd,
1608
1852
  env: { ...process.env, ...options.env },
1609
1853
  encoding: "utf-8",
@@ -1627,8 +1871,8 @@ var Snapshot;
1627
1871
  const worktree = project.cwd;
1628
1872
  const git = gitdir(projectId);
1629
1873
  try {
1630
- await fs5__default.default.mkdir(git, { recursive: true });
1631
- const gitExists = await fs5__default.default.access(path10__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
1874
+ await fs7__default.default.mkdir(git, { recursive: true });
1875
+ const gitExists = await fs7__default.default.access(path10__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
1632
1876
  if (!gitExists) {
1633
1877
  await runGit(`git init`, {
1634
1878
  env: { GIT_DIR: git, GIT_WORK_TREE: worktree }
@@ -1713,7 +1957,7 @@ var Snapshot;
1713
1957
  for (const file of newFiles) {
1714
1958
  const fullPath = path10__default.default.join(worktree, file);
1715
1959
  try {
1716
- await fs5__default.default.unlink(fullPath);
1960
+ await fs7__default.default.unlink(fullPath);
1717
1961
  log.info("deleted newly created file", { file: fullPath });
1718
1962
  } catch {
1719
1963
  }
@@ -1750,7 +1994,7 @@ var Snapshot;
1750
1994
  log.info("file existed in snapshot but checkout failed, keeping", { file });
1751
1995
  } else {
1752
1996
  log.info("file did not exist in snapshot, deleting", { file });
1753
- await fs5__default.default.unlink(file).catch(() => {
1997
+ await fs7__default.default.unlink(file).catch(() => {
1754
1998
  });
1755
1999
  }
1756
2000
  }
@@ -1984,7 +2228,19 @@ var rpcHandlers = {
1984
2228
  return { success: true, diff };
1985
2229
  }
1986
2230
  };
2231
+ var INITIAL_RECONNECT_DELAY = 1e3;
2232
+ var MAX_RECONNECT_DELAY = 6e4;
2233
+ var BACKOFF_MULTIPLIER = 2;
1987
2234
  var reconnectTimeout = null;
2235
+ var reconnectAttempts = 0;
2236
+ function getReconnectDelay() {
2237
+ const delay = Math.min(
2238
+ INITIAL_RECONNECT_DELAY * Math.pow(BACKOFF_MULTIPLIER, reconnectAttempts),
2239
+ MAX_RECONNECT_DELAY
2240
+ );
2241
+ const jitter = delay * 0.25 * (Math.random() * 2 - 1);
2242
+ return Math.floor(delay + jitter);
2243
+ }
1988
2244
  var connectToUserStreams = async (serverUrl) => {
1989
2245
  const userId = getUserId();
1990
2246
  if (!userId?.userId) {
@@ -2005,7 +2261,8 @@ var connectToUserStreams = async (serverUrl) => {
2005
2261
  }
2006
2262
  });
2007
2263
  ws.on("open", () => {
2008
- console.log(pc5__default.default.green("CLI connected to user-streams"));
2264
+ reconnectAttempts = 0;
2265
+ console.log(pc5__default.default.cyan("connected to user streams"));
2009
2266
  if (reconnectTimeout) {
2010
2267
  clearTimeout(reconnectTimeout);
2011
2268
  reconnectTimeout = null;
@@ -2016,7 +2273,7 @@ var connectToUserStreams = async (serverUrl) => {
2016
2273
  const message = JSON.parse(event.toString());
2017
2274
  if (message._tag === "rpc_call") {
2018
2275
  const { requestId, method, input } = message;
2019
- console.log(pc5__default.default.gray(`RPC call: ${method}`));
2276
+ console.log(pc5__default.default.gray(`> ${method}`));
2020
2277
  const handler = rpcHandlers[method];
2021
2278
  if (!handler) {
2022
2279
  ws.send(JSON.stringify({
@@ -2027,7 +2284,6 @@ var connectToUserStreams = async (serverUrl) => {
2027
2284
  message: `Unknown RPC method: ${method}`
2028
2285
  }
2029
2286
  }));
2030
- console.log(pc5__default.default.yellow(`Unknown RPC method: ${method}`));
2031
2287
  return;
2032
2288
  }
2033
2289
  try {
@@ -2037,7 +2293,6 @@ var connectToUserStreams = async (serverUrl) => {
2037
2293
  requestId,
2038
2294
  data: result
2039
2295
  }));
2040
- console.log(pc5__default.default.green(`RPC completed: ${method}`));
2041
2296
  } catch (error) {
2042
2297
  const rpcError = error._tag ? error : {
2043
2298
  _tag: "RpcError",
@@ -2048,7 +2303,7 @@ var connectToUserStreams = async (serverUrl) => {
2048
2303
  requestId,
2049
2304
  data: rpcError
2050
2305
  }));
2051
- console.log(pc5__default.default.red(`RPC failed: ${method} - ${rpcError.message}`));
2306
+ console.log(pc5__default.default.red(` ${method} failed`));
2052
2307
  }
2053
2308
  return;
2054
2309
  }
@@ -2067,25 +2322,38 @@ var connectToUserStreams = async (serverUrl) => {
2067
2322
  }
2068
2323
  }
2069
2324
  } catch (parseError) {
2070
- console.error(pc5__default.default.red(`Failed to parse message: ${parseError}`));
2325
+ console.error(pc5__default.default.red(`parse error`));
2071
2326
  }
2072
2327
  });
2073
2328
  ws.on("close", (code, reason) => {
2074
- console.log(pc5__default.default.yellow(`CLI disconnected from user-streams (code: ${code})`));
2075
- console.log(pc5__default.default.gray("Reconnecting in 5 seconds..."));
2329
+ const delay = getReconnectDelay();
2330
+ reconnectAttempts++;
2331
+ console.log(pc5__default.default.gray(`user streams disconnected, reconnecting in ${Math.round(delay / 1e3)}s...`));
2076
2332
  reconnectTimeout = setTimeout(() => {
2077
2333
  connectToUserStreams(serverUrl).catch((err) => {
2078
- console.error(pc5__default.default.red(`Reconnection failed: ${err.message}`));
2334
+ console.error(pc5__default.default.red(`reconnection failed`));
2079
2335
  });
2080
- }, 5e3);
2336
+ }, delay);
2081
2337
  });
2082
2338
  ws.on("error", (error) => {
2083
- console.error(pc5__default.default.red(`User streams WebSocket error: ${error.message}`));
2339
+ console.error(pc5__default.default.red(`stream error: ${error.message}`));
2084
2340
  });
2085
2341
  return ws;
2086
2342
  };
2087
2343
 
2088
2344
  // src/server.ts
2345
+ var INITIAL_RECONNECT_DELAY2 = 1e3;
2346
+ var MAX_RECONNECT_DELAY2 = 6e4;
2347
+ var BACKOFF_MULTIPLIER2 = 2;
2348
+ var reconnectAttempts2 = 0;
2349
+ function getReconnectDelay2() {
2350
+ const delay = Math.min(
2351
+ INITIAL_RECONNECT_DELAY2 * Math.pow(BACKOFF_MULTIPLIER2, reconnectAttempts2),
2352
+ MAX_RECONNECT_DELAY2
2353
+ );
2354
+ const jitter = delay * 0.25 * (Math.random() * 2 - 1);
2355
+ return Math.floor(delay + jitter);
2356
+ }
2089
2357
  var toolExecutors = {
2090
2358
  editFile: editFiles,
2091
2359
  deleteFile,
@@ -2108,12 +2376,13 @@ function connectToServer(serverUrl = DEFAULT_SERVER_URL) {
2108
2376
  }
2109
2377
  });
2110
2378
  ws.on("open", () => {
2111
- console.log(pc5__default.default.green("Connected to server agent streams"));
2379
+ reconnectAttempts2 = 0;
2380
+ console.log(pc5__default.default.cyan("connected to server"));
2112
2381
  });
2113
2382
  ws.on("message", async (data) => {
2114
2383
  const message = JSON.parse(data.toString());
2115
2384
  if (message.type === "tool_call") {
2116
- console.log(`tool call: ${message.tool}${message.projectCwd ? ` (project: ${message.projectCwd})` : ""}`);
2385
+ console.log(pc5__default.default.gray(`> ${message.tool}`));
2117
2386
  try {
2118
2387
  const executor = toolExecutors[message.tool];
2119
2388
  if (!executor) {
@@ -2125,17 +2394,16 @@ function connectToServer(serverUrl = DEFAULT_SERVER_URL) {
2125
2394
  id: message.id,
2126
2395
  result
2127
2396
  }));
2128
- console.log(pc5__default.default.green(`tool call completed: ${message.tool}`));
2129
2397
  } catch (error) {
2130
2398
  ws.send(JSON.stringify({
2131
2399
  type: "tool_result",
2132
2400
  id: message.id,
2133
2401
  error: error.message
2134
2402
  }));
2135
- console.error(pc5__default.default.red(`tool call failed: ${message.tool} ${error.message}`));
2403
+ console.error(pc5__default.default.red(` ${message.tool} failed: ${error.message}`));
2136
2404
  }
2137
2405
  } else if (message.type === "rpc_call") {
2138
- console.log(`rpc call: ${message.method}`);
2406
+ console.log(pc5__default.default.gray(`> rpc: ${message.method}`));
2139
2407
  try {
2140
2408
  const handler = rpcHandlers[message.method];
2141
2409
  if (!handler) {
@@ -2147,34 +2415,35 @@ function connectToServer(serverUrl = DEFAULT_SERVER_URL) {
2147
2415
  id: message.id,
2148
2416
  result
2149
2417
  }));
2150
- console.log(pc5__default.default.green(`rpc call completed: ${message.method}`));
2151
2418
  } catch (error) {
2152
2419
  ws.send(JSON.stringify({
2153
2420
  type: "tool_result",
2154
2421
  id: message.id,
2155
2422
  error: error.message
2156
2423
  }));
2157
- console.error(pc5__default.default.red(`rpc call failed: ${message.method} ${error.message}`));
2424
+ console.error(pc5__default.default.red(` rpc failed: ${message.method}`));
2158
2425
  }
2159
2426
  }
2160
2427
  });
2161
2428
  ws.on("close", () => {
2162
- console.log(pc5__default.default.red("disconnected from server. reconnecting in 5s..."));
2163
- setTimeout(() => connectToServer(serverUrl), 5e3);
2429
+ const delay = getReconnectDelay2();
2430
+ reconnectAttempts2++;
2431
+ console.log(pc5__default.default.gray(`disconnected, reconnecting in ${Math.round(delay / 1e3)}s...`));
2432
+ setTimeout(() => connectToServer(serverUrl), delay);
2164
2433
  });
2165
2434
  ws.on("error", (error) => {
2166
- console.error(pc5__default.default.red(`web socket error: ${error.message}`));
2435
+ console.error(pc5__default.default.red(`connection error: ${error.message}`));
2167
2436
  });
2168
2437
  return ws;
2169
2438
  }
2170
2439
  async function main() {
2171
2440
  const serverUrl = DEFAULT_SERVER_URL;
2172
- console.log(pc5__default.default.green("starting local amai..."));
2441
+ console.log(pc5__default.default.gray("starting ama..."));
2173
2442
  connectToServer(serverUrl);
2174
2443
  await connectToUserStreams(serverUrl);
2175
2444
  startHttpServer();
2176
2445
  }
2177
- var execAsync3 = util.promisify(child_process.exec);
2446
+ var execAsync2 = util.promisify(child_process.exec);
2178
2447
  var CODE_SERVER_VERSION = "4.96.4";
2179
2448
  function getPlatformInfo() {
2180
2449
  const platform = process.platform;
@@ -2208,50 +2477,50 @@ function getCodeServerBin() {
2208
2477
  }
2209
2478
  function isCodeServerInstalled() {
2210
2479
  const binPath = getCodeServerBin();
2211
- return fs6__default.default.existsSync(binPath);
2480
+ return fs8__default.default.existsSync(binPath);
2212
2481
  }
2213
2482
  async function installCodeServer() {
2214
2483
  const { ext } = getPlatformInfo();
2215
2484
  const downloadUrl = getDownloadUrl();
2216
2485
  const tarballPath = path10__default.default.join(AMA_DIR, `code-server.${ext}`);
2217
- if (!fs6__default.default.existsSync(AMA_DIR)) {
2218
- fs6__default.default.mkdirSync(AMA_DIR, { recursive: true });
2486
+ if (!fs8__default.default.existsSync(AMA_DIR)) {
2487
+ fs8__default.default.mkdirSync(AMA_DIR, { recursive: true });
2219
2488
  }
2220
- if (!fs6__default.default.existsSync(CODE_DIR)) {
2221
- fs6__default.default.mkdirSync(CODE_DIR, { recursive: true });
2489
+ if (!fs8__default.default.existsSync(CODE_DIR)) {
2490
+ fs8__default.default.mkdirSync(CODE_DIR, { recursive: true });
2222
2491
  }
2223
- if (!fs6__default.default.existsSync(STORAGE_DIR)) {
2224
- fs6__default.default.mkdirSync(STORAGE_DIR, { recursive: true });
2492
+ if (!fs8__default.default.existsSync(STORAGE_DIR)) {
2493
+ fs8__default.default.mkdirSync(STORAGE_DIR, { recursive: true });
2225
2494
  }
2226
- console.log(pc5__default.default.cyan(`Downloading code-server v${CODE_SERVER_VERSION}...`));
2495
+ console.log(pc5__default.default.cyan(`downloading code-server v${CODE_SERVER_VERSION}...`));
2227
2496
  console.log(pc5__default.default.gray(downloadUrl));
2228
2497
  const response = await fetch(downloadUrl);
2229
2498
  if (!response.ok) {
2230
2499
  throw new Error(`Failed to download code-server: ${response.statusText}`);
2231
2500
  }
2232
2501
  const buffer = await response.arrayBuffer();
2233
- await fs6__default.default.promises.writeFile(tarballPath, Buffer.from(buffer));
2502
+ await fs8__default.default.promises.writeFile(tarballPath, Buffer.from(buffer));
2234
2503
  console.log(pc5__default.default.cyan("Extracting code-server..."));
2235
- await execAsync3(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
2236
- await fs6__default.default.promises.unlink(tarballPath);
2504
+ await execAsync2(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
2505
+ await fs8__default.default.promises.unlink(tarballPath);
2237
2506
  const binPath = getCodeServerBin();
2238
- if (fs6__default.default.existsSync(binPath)) {
2239
- await fs6__default.default.promises.chmod(binPath, 493);
2507
+ if (fs8__default.default.existsSync(binPath)) {
2508
+ await fs8__default.default.promises.chmod(binPath, 493);
2240
2509
  }
2241
- console.log(pc5__default.default.green("\u2713 code-server installed successfully"));
2510
+ console.log(pc5__default.default.green("code-server installed successfully"));
2242
2511
  }
2243
2512
  async function killExistingCodeServer() {
2244
2513
  try {
2245
2514
  if (process.platform === "win32") {
2246
- await execAsync3("netstat -ano | findstr :8081 | findstr LISTENING").then(async ({ stdout }) => {
2515
+ await execAsync2("netstat -ano | findstr :8081 | findstr LISTENING").then(async ({ stdout }) => {
2247
2516
  const pid = stdout.trim().split(/\s+/).pop();
2248
- if (pid) await execAsync3(`taskkill /PID ${pid} /F`);
2517
+ if (pid) await execAsync2(`taskkill /PID ${pid} /F`);
2249
2518
  }).catch(() => {
2250
2519
  });
2251
2520
  } else {
2252
- await execAsync3("lsof -ti:8081").then(async ({ stdout }) => {
2521
+ await execAsync2("lsof -ti:8081").then(async ({ stdout }) => {
2253
2522
  const pid = stdout.trim();
2254
- if (pid) await execAsync3(`kill -9 ${pid}`);
2523
+ if (pid) await execAsync2(`kill -9 ${pid}`);
2255
2524
  }).catch(() => {
2256
2525
  });
2257
2526
  }
@@ -2261,8 +2530,8 @@ async function killExistingCodeServer() {
2261
2530
  async function setupDefaultSettings() {
2262
2531
  const userDir = path10__default.default.join(STORAGE_DIR, "User");
2263
2532
  const settingsPath = path10__default.default.join(userDir, "settings.json");
2264
- if (!fs6__default.default.existsSync(userDir)) {
2265
- fs6__default.default.mkdirSync(userDir, { recursive: true });
2533
+ if (!fs8__default.default.existsSync(userDir)) {
2534
+ fs8__default.default.mkdirSync(userDir, { recursive: true });
2266
2535
  }
2267
2536
  const defaultSettings = {
2268
2537
  // Disable signature verification for Open VSX extensions
@@ -2280,9 +2549,9 @@ async function setupDefaultSettings() {
2280
2549
  "workbench.activityBar.location": "top"
2281
2550
  };
2282
2551
  let existingSettings = {};
2283
- if (fs6__default.default.existsSync(settingsPath)) {
2552
+ if (fs8__default.default.existsSync(settingsPath)) {
2284
2553
  try {
2285
- const content = await fs6__default.default.promises.readFile(settingsPath, "utf-8");
2554
+ const content = await fs8__default.default.promises.readFile(settingsPath, "utf-8");
2286
2555
  existingSettings = JSON.parse(content);
2287
2556
  } catch {
2288
2557
  }
@@ -2290,7 +2559,7 @@ async function setupDefaultSettings() {
2290
2559
  const mergedSettings = { ...defaultSettings, ...existingSettings };
2291
2560
  mergedSettings["workbench.colorTheme"] = "Min Dark";
2292
2561
  mergedSettings["extensions.verifySignature"] = false;
2293
- await fs6__default.default.promises.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
2562
+ await fs8__default.default.promises.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
2294
2563
  console.log(pc5__default.default.green("ama code-server settings configured"));
2295
2564
  }
2296
2565
  async function installExtensions() {
@@ -2301,7 +2570,7 @@ async function installExtensions() {
2301
2570
  for (const ext of extensions) {
2302
2571
  try {
2303
2572
  console.log(pc5__default.default.cyan(`ama installing extension: ${ext}...`));
2304
- await execAsync3(`"${binPath}" --user-data-dir "${STORAGE_DIR}" --install-extension ${ext}`);
2573
+ await execAsync2(`"${binPath}" --user-data-dir "${STORAGE_DIR}" --install-extension ${ext}`);
2305
2574
  console.log(pc5__default.default.green(`ama extension ${ext} installed`));
2306
2575
  } catch (error) {
2307
2576
  console.log(pc5__default.default.yellow(`ama failed to install extension ${ext}`), error);
@@ -2311,7 +2580,7 @@ async function installExtensions() {
2311
2580
  async function startCodeServer(cwd) {
2312
2581
  const binPath = getCodeServerBin();
2313
2582
  const workDir = cwd || process.cwd();
2314
- if (!fs6__default.default.existsSync(binPath)) {
2583
+ if (!fs8__default.default.existsSync(binPath)) {
2315
2584
  throw new Error("ama code-server is not installed. Run installCodeServer() first.");
2316
2585
  }
2317
2586
  await killExistingCodeServer();
@@ -2319,12 +2588,12 @@ async function startCodeServer(cwd) {
2319
2588
  await installExtensions();
2320
2589
  const workspaceStoragePath = path10__default.default.join(STORAGE_DIR, "User", "workspaceStorage");
2321
2590
  try {
2322
- if (fs6__default.default.existsSync(workspaceStoragePath)) {
2323
- await fs6__default.default.promises.rm(workspaceStoragePath, { recursive: true, force: true });
2591
+ if (fs8__default.default.existsSync(workspaceStoragePath)) {
2592
+ await fs8__default.default.promises.rm(workspaceStoragePath, { recursive: true, force: true });
2324
2593
  }
2325
2594
  const stateDbPath = path10__default.default.join(STORAGE_DIR, "User", "globalStorage", "state.vscdb");
2326
- if (fs6__default.default.existsSync(stateDbPath)) {
2327
- await fs6__default.default.promises.unlink(stateDbPath);
2595
+ if (fs8__default.default.existsSync(stateDbPath)) {
2596
+ await fs8__default.default.promises.unlink(stateDbPath);
2328
2597
  }
2329
2598
  } catch {
2330
2599
  }
@@ -2355,11 +2624,11 @@ var __dirname$1 = path10.dirname(__filename$1);
2355
2624
  var DAEMON_PID_FILE = path10__default.default.join(AMA_DIR, "daemon.pid");
2356
2625
  var DAEMON_LOG_FILE = path10__default.default.join(AMA_DIR, "daemon.log");
2357
2626
  function isDaemonRunning() {
2358
- if (!fs6__default.default.existsSync(DAEMON_PID_FILE)) {
2627
+ if (!fs8__default.default.existsSync(DAEMON_PID_FILE)) {
2359
2628
  return false;
2360
2629
  }
2361
2630
  try {
2362
- const pid = Number(fs6__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
2631
+ const pid = Number(fs8__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
2363
2632
  process.kill(pid, 0);
2364
2633
  return true;
2365
2634
  } catch {
@@ -2367,30 +2636,30 @@ function isDaemonRunning() {
2367
2636
  }
2368
2637
  }
2369
2638
  function stopDaemon() {
2370
- if (!fs6__default.default.existsSync(DAEMON_PID_FILE)) {
2639
+ if (!fs8__default.default.existsSync(DAEMON_PID_FILE)) {
2371
2640
  return false;
2372
2641
  }
2373
2642
  try {
2374
- const pid = Number(fs6__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
2643
+ const pid = Number(fs8__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
2375
2644
  process.kill(pid, "SIGTERM");
2376
- fs6__default.default.unlinkSync(DAEMON_PID_FILE);
2645
+ fs8__default.default.unlinkSync(DAEMON_PID_FILE);
2377
2646
  return true;
2378
2647
  } catch (error) {
2379
2648
  return false;
2380
2649
  }
2381
2650
  }
2382
2651
  function startDaemon() {
2383
- if (!fs6__default.default.existsSync(AMA_DIR)) {
2384
- fs6__default.default.mkdirSync(AMA_DIR, { recursive: true });
2652
+ if (!fs8__default.default.existsSync(AMA_DIR)) {
2653
+ fs8__default.default.mkdirSync(AMA_DIR, { recursive: true });
2385
2654
  }
2386
2655
  if (isDaemonRunning()) {
2387
2656
  stopDaemon();
2388
2657
  }
2389
2658
  const daemonScript = path10__default.default.join(__dirname$1, "lib", "daemon-entry.js");
2390
- if (!fs6__default.default.existsSync(daemonScript)) {
2659
+ if (!fs8__default.default.existsSync(daemonScript)) {
2391
2660
  throw new Error(`Daemon entry script not found at: ${daemonScript}. Please rebuild the project.`);
2392
2661
  }
2393
- const logFd = fs6__default.default.openSync(DAEMON_LOG_FILE, "a");
2662
+ const logFd = fs8__default.default.openSync(DAEMON_LOG_FILE, "a");
2394
2663
  const daemon = child_process.spawn(process.execPath, [daemonScript], {
2395
2664
  detached: true,
2396
2665
  stdio: ["ignore", logFd, logFd],
@@ -2398,21 +2667,27 @@ function startDaemon() {
2398
2667
  cwd: process.cwd()
2399
2668
  });
2400
2669
  daemon.unref();
2401
- fs6__default.default.writeFileSync(DAEMON_PID_FILE, String(daemon.pid));
2402
- fs6__default.default.closeSync(logFd);
2670
+ fs8__default.default.writeFileSync(DAEMON_PID_FILE, String(daemon.pid));
2671
+ fs8__default.default.closeSync(logFd);
2403
2672
  }
2404
2673
  function getDaemonPid() {
2405
- if (!fs6__default.default.existsSync(DAEMON_PID_FILE)) {
2674
+ if (!fs8__default.default.existsSync(DAEMON_PID_FILE)) {
2406
2675
  return null;
2407
2676
  }
2408
2677
  try {
2409
- return Number(fs6__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
2678
+ return Number(fs8__default.default.readFileSync(DAEMON_PID_FILE, "utf8"));
2410
2679
  } catch {
2411
2680
  return null;
2412
2681
  }
2413
2682
  }
2414
- var VERSION = "0.0.9";
2683
+ var VERSION = "0.0.11";
2415
2684
  var PROJECT_DIR = process.cwd();
2685
+ var LOGO = `
2686
+ __ _ _ __ ___ __ _
2687
+ / _\` | '_ \` _ \\ / _\` |
2688
+ | (_| | | | | | | (_| |
2689
+ \\__,_|_| |_| |_|\\__,_|
2690
+ `;
2416
2691
  function promptUser(question) {
2417
2692
  const rl = readline__default.default.createInterface({
2418
2693
  input: process.stdin,
@@ -2427,168 +2702,249 @@ function promptUser(question) {
2427
2702
  }
2428
2703
  async function startWithCodeServer() {
2429
2704
  if (!isCodeServerInstalled()) {
2430
- console.log(pc5__default.default.cyan("First run detected. Setting up code-server..."));
2705
+ console.log(pc5__default.default.gray("setting up code-server..."));
2431
2706
  try {
2432
2707
  await installCodeServer();
2433
2708
  } catch (error) {
2434
- console.error(pc5__default.default.red(`Failed to install code-server: ${error.message}`));
2435
- console.log(pc5__default.default.yellow("Continuing without code-server..."));
2709
+ console.error(pc5__default.default.red(`failed to install code-server: ${error.message}`));
2710
+ console.log(pc5__default.default.gray("continuing without code-server..."));
2436
2711
  }
2437
2712
  }
2438
2713
  if (isCodeServerInstalled()) {
2439
2714
  try {
2440
2715
  await startCodeServer(PROJECT_DIR);
2441
2716
  } catch (error) {
2442
- console.error(pc5__default.default.red(`Failed to start code-server: ${error.message}`));
2717
+ console.error(pc5__default.default.red(`failed to start code-server: ${error.message}`));
2443
2718
  }
2444
2719
  }
2445
2720
  main();
2446
2721
  }
2722
+ async function checkForUpdates() {
2723
+ try {
2724
+ const response = await fetch("https://registry.npmjs.org/amai/latest");
2725
+ const data = await response.json();
2726
+ const latestVersion = data.version;
2727
+ return {
2728
+ current: VERSION,
2729
+ latest: latestVersion,
2730
+ hasUpdate: latestVersion !== VERSION
2731
+ };
2732
+ } catch {
2733
+ return { current: VERSION, latest: VERSION, hasUpdate: false };
2734
+ }
2735
+ }
2736
+ function runNpmInstall() {
2737
+ return new Promise((resolve, reject) => {
2738
+ const child = child_process.spawn("bun", ["add", "-g", "amai@latest"], {
2739
+ stdio: "inherit",
2740
+ shell: true
2741
+ });
2742
+ child.on("close", (code) => {
2743
+ if (code === 0) resolve();
2744
+ else reject(new Error(`bun add exited with code ${code}`));
2745
+ });
2746
+ child.on("error", reject);
2747
+ });
2748
+ }
2447
2749
  var args = process.argv.slice(2);
2750
+ if (args[0] === "--version" || args[0] === "-v") {
2751
+ console.log(pc5__default.default.gray(`amai ${VERSION}`));
2752
+ process.exit(0);
2753
+ }
2448
2754
  if (args[0] === "--help" || args[0] === "-h") {
2449
- console.log(`
2450
- ${pc5__default.default.bold("amai cli")} ${pc5__default.default.gray(VERSION)}
2451
-
2452
- Usage: amai [command] [options]
2453
-
2454
- Commands:
2455
- login Authorize device
2456
- logout Log out and remove credentials
2457
- start Start background daemon (background mode is highly recommended for better performance and stability)
2458
- stop Stop background daemon
2459
- status Check daemon status
2460
- project add <path> Register a project directory
2461
- project list List registered projects
2462
- Options:
2463
- --help, -h Show this help message
2464
- --logout, -l Log out and remove credentials
2465
- Environment Variables:
2466
- SERVER_URL Server URL to connect to
2467
-
2468
- Example:
2469
- amai login
2470
- amai start
2471
- amai project add /path/to/project
2472
- amai Start the agent (will prompt for background mode)
2473
- `);
2755
+ console.log(pc5__default.default.cyan(LOGO));
2756
+ console.log(pc5__default.default.gray(` v${VERSION}`));
2757
+ console.log("");
2758
+ console.log(pc5__default.default.cyan(" usage"));
2759
+ console.log(pc5__default.default.gray(" amai [command]"));
2760
+ console.log("");
2761
+ console.log(pc5__default.default.cyan(" commands"));
2762
+ console.log(pc5__default.default.gray(" login authenticate with amai"));
2763
+ console.log(pc5__default.default.gray(" logout remove credentials"));
2764
+ console.log(pc5__default.default.gray(" start start background daemon"));
2765
+ console.log(pc5__default.default.gray(" stop stop background daemon"));
2766
+ console.log(pc5__default.default.gray(" status check daemon status"));
2767
+ console.log(pc5__default.default.gray(" update update to latest version"));
2768
+ console.log(pc5__default.default.gray(" project add register a project"));
2769
+ console.log(pc5__default.default.gray(" project list list projects"));
2770
+ console.log("");
2771
+ console.log(pc5__default.default.cyan(" options"));
2772
+ console.log(pc5__default.default.gray(" -h, --help show help"));
2773
+ console.log(pc5__default.default.gray(" -v, --version show version"));
2774
+ console.log("");
2474
2775
  process.exit(0);
2475
2776
  }
2476
- if (args[0] === "start") {
2477
- if (isDaemonRunning()) {
2478
- console.log(pc5__default.default.yellow("ama is already running"));
2777
+ if (args[0] === "update") {
2778
+ (async () => {
2779
+ console.log(pc5__default.default.gray("checking for updates..."));
2780
+ const { current, latest, hasUpdate } = await checkForUpdates();
2781
+ if (!hasUpdate) {
2782
+ console.log(pc5__default.default.cyan(`already on latest version (${current})`));
2783
+ process.exit(0);
2784
+ }
2785
+ console.log(pc5__default.default.cyan(`update available: ${current} -> ${latest}`));
2786
+ const answer = await promptUser(pc5__default.default.gray("install update? (Y/n): "));
2787
+ if (answer === "" || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
2788
+ console.log(pc5__default.default.gray("updating..."));
2789
+ try {
2790
+ await runNpmInstall();
2791
+ console.log(pc5__default.default.cyan(`updated to ${latest}`));
2792
+ } catch (error) {
2793
+ console.error(pc5__default.default.red(`update failed: ${error.message}`));
2794
+ process.exit(1);
2795
+ }
2796
+ }
2479
2797
  process.exit(0);
2480
- }
2481
- if (!isAuthenticated()) {
2482
- console.log(pc5__default.default.yellow("Not authenticated. Please log in first."));
2483
- login().then(() => {
2484
- console.log(pc5__default.default.green("starting ama in background mode..."));
2485
- startDaemon();
2486
- console.log(pc5__default.default.green("ama started in background mode successfully"));
2798
+ })();
2799
+ } else if (args[0] === "start") {
2800
+ (async () => {
2801
+ if (isDaemonRunning()) {
2802
+ console.log(pc5__default.default.gray("amai is already running"));
2487
2803
  process.exit(0);
2488
- }).catch(() => {
2489
- console.error(pc5__default.default.red("Login failed. Cannot start ama in background mode."));
2490
- process.exit(1);
2491
- });
2492
- } else {
2804
+ }
2805
+ if (!isAuthenticated()) {
2806
+ console.log(pc5__default.default.gray("not authenticated"));
2807
+ try {
2808
+ await login();
2809
+ } catch {
2810
+ console.error(pc5__default.default.red("login failed"));
2811
+ process.exit(1);
2812
+ }
2813
+ }
2493
2814
  startDaemon();
2494
- console.log(pc5__default.default.green(pc5__default.default.bold("amai started in background mode")));
2495
- console.log(pc5__default.default.gray(`Tip: You can check status any time with ${pc5__default.default.bold("amai status")}`));
2815
+ console.log(pc5__default.default.cyan("amai started"));
2816
+ console.log(pc5__default.default.gray(`check status: amai status`));
2496
2817
  process.exit(0);
2497
- }
2498
- }
2499
- if (args[0] === "stop") {
2818
+ })();
2819
+ } else if (args[0] === "stop") {
2500
2820
  if (stopDaemon()) {
2501
- console.log(pc5__default.default.green("Daemon stopped successfully"));
2821
+ console.log(pc5__default.default.cyan("daemon stopped"));
2502
2822
  } else {
2503
- console.log(pc5__default.default.yellow("Daemon was not running"));
2823
+ console.log(pc5__default.default.gray("daemon was not running"));
2504
2824
  }
2505
2825
  process.exit(0);
2506
- }
2507
- if (args[0] === "status") {
2826
+ } else if (args[0] === "status") {
2508
2827
  const running = isDaemonRunning();
2509
2828
  const pid = getDaemonPid();
2829
+ console.log("");
2830
+ console.log(pc5__default.default.cyan(" amai status"));
2831
+ console.log("");
2510
2832
  if (running && pid) {
2511
- console.log(pc5__default.default.green(`Daemon is running (PID: ${pid})`));
2833
+ console.log(pc5__default.default.gray(` status running`));
2834
+ console.log(pc5__default.default.gray(` pid ${pid}`));
2512
2835
  } else {
2513
- console.log(pc5__default.default.yellow("Daemon is not running"));
2836
+ console.log(pc5__default.default.gray(` status stopped`));
2514
2837
  }
2838
+ console.log(pc5__default.default.gray(` version ${VERSION}`));
2839
+ console.log("");
2515
2840
  process.exit(0);
2516
- }
2517
- if (args[0] === "project") {
2841
+ } else if (args[0] === "project") {
2518
2842
  if (args[1] === "add") {
2519
2843
  const projectPath = args[2];
2520
2844
  if (!projectPath) {
2521
- console.error(pc5__default.default.red("Please provide a project path"));
2522
- console.log("Usage: amai project add <path>");
2845
+ console.error(pc5__default.default.red("please provide a project path"));
2846
+ console.log(pc5__default.default.gray("usage: amai project add <path>"));
2523
2847
  process.exit(1);
2524
2848
  }
2525
2849
  const resolvedPath = path10__default.default.resolve(projectPath);
2526
- if (!fs6__default.default.existsSync(resolvedPath)) {
2527
- console.error(pc5__default.default.red(`Path does not exist: ${resolvedPath}`));
2850
+ if (!fs8__default.default.existsSync(resolvedPath)) {
2851
+ console.error(pc5__default.default.red(`path does not exist: ${resolvedPath}`));
2528
2852
  process.exit(1);
2529
2853
  }
2530
- if (!fs6__default.default.statSync(resolvedPath).isDirectory()) {
2531
- console.error(pc5__default.default.red(`Path is not a directory: ${resolvedPath}`));
2854
+ if (!fs8__default.default.statSync(resolvedPath).isDirectory()) {
2855
+ console.error(pc5__default.default.red(`path is not a directory: ${resolvedPath}`));
2532
2856
  process.exit(1);
2533
2857
  }
2534
2858
  const projectId = path10__default.default.basename(resolvedPath);
2535
2859
  projectRegistry.register(projectId, resolvedPath);
2536
- console.log(pc5__default.default.green(`Project registered: ${projectId} -> ${resolvedPath}`));
2860
+ console.log(pc5__default.default.cyan(`project registered: ${projectId}`));
2861
+ console.log(pc5__default.default.gray(` ${resolvedPath}`));
2537
2862
  process.exit(0);
2538
2863
  } else if (args[1] === "list") {
2539
2864
  const projects = projectRegistry.list();
2865
+ console.log("");
2866
+ console.log(pc5__default.default.cyan(" projects"));
2867
+ console.log("");
2540
2868
  if (projects.length === 0) {
2541
- console.log(pc5__default.default.yellow("No projects registered"));
2869
+ console.log(pc5__default.default.gray(" no projects registered"));
2542
2870
  } else {
2543
- console.log(pc5__default.default.bold("Registered projects:"));
2544
2871
  projects.forEach((project) => {
2545
- console.log(` ${pc5__default.default.cyan(project.id)}: ${project.cwd} ${project.active ? pc5__default.default.green("(active)") : ""}`);
2872
+ const status = project.active ? pc5__default.default.cyan("active") : pc5__default.default.gray("inactive");
2873
+ console.log(pc5__default.default.gray(` ${project.id} [${status}]`));
2874
+ console.log(pc5__default.default.gray(` ${project.cwd}`));
2546
2875
  });
2547
2876
  }
2877
+ console.log("");
2548
2878
  process.exit(0);
2549
2879
  } else {
2550
- console.error(pc5__default.default.red(`Unknown project command: ${args[1]}`));
2551
- console.log('Use "amai project add <path>" or "amai project list"');
2880
+ console.error(pc5__default.default.red(`unknown project command: ${args[1]}`));
2881
+ console.log(pc5__default.default.gray("usage: amai project add <path> | amai project list"));
2552
2882
  process.exit(1);
2553
2883
  }
2554
- }
2555
- if (args[0] === "login" || args[0] === "--login") {
2556
- login().then(() => process.exit(0)).catch(() => process.exit(1));
2884
+ } else if (args[0] === "login" || args[0] === "--login") {
2885
+ (async () => {
2886
+ try {
2887
+ await login();
2888
+ console.log("");
2889
+ if (isDaemonRunning()) {
2890
+ console.log(pc5__default.default.gray("amai is already running"));
2891
+ process.exit(0);
2892
+ }
2893
+ const answer = await promptUser(pc5__default.default.gray("start amai now? (Y/n): "));
2894
+ const shouldStart = answer === "" || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
2895
+ if (shouldStart) {
2896
+ const bgAnswer = await promptUser(pc5__default.default.gray("run in background? (Y/n): "));
2897
+ const runInBackground = bgAnswer === "" || bgAnswer.toLowerCase() === "y" || bgAnswer.toLowerCase() === "yes";
2898
+ if (runInBackground) {
2899
+ console.log(pc5__default.default.gray("starting..."));
2900
+ startDaemon();
2901
+ console.log(pc5__default.default.cyan("amai started"));
2902
+ console.log(pc5__default.default.gray('use "amai status" to check status'));
2903
+ } else {
2904
+ console.log(pc5__default.default.gray("starting in foreground..."));
2905
+ startWithCodeServer();
2906
+ return;
2907
+ }
2908
+ }
2909
+ process.exit(0);
2910
+ } catch {
2911
+ console.error(pc5__default.default.red("login failed"));
2912
+ process.exit(1);
2913
+ }
2914
+ })();
2557
2915
  } else if (args[0] === "logout" || args[0] === "--logout") {
2558
2916
  logout();
2559
- console.log(pc5__default.default.green("Logged out successfully"));
2917
+ console.log(pc5__default.default.cyan("logged out"));
2560
2918
  process.exit(0);
2561
2919
  } else {
2562
2920
  (async () => {
2921
+ console.log(pc5__default.default.cyan(LOGO));
2563
2922
  if (!isAuthenticated()) {
2564
- console.log(pc5__default.default.yellow("Not authenticated. Please log in first."));
2923
+ console.log(pc5__default.default.gray("not authenticated"));
2565
2924
  try {
2566
2925
  await login();
2926
+ console.log("");
2567
2927
  } catch {
2568
- console.error(pc5__default.default.red("Login failed. Cannot start server."));
2928
+ console.error(pc5__default.default.red("login failed"));
2569
2929
  process.exit(1);
2570
2930
  }
2571
2931
  }
2572
2932
  if (isDaemonRunning()) {
2573
- console.log(pc5__default.default.yellow('Daemon is already running. Use "amai status" to check its status.'));
2933
+ console.log(pc5__default.default.gray("amai is already running"));
2934
+ console.log(pc5__default.default.gray('use "amai status" to check status'));
2574
2935
  process.exit(0);
2575
2936
  }
2576
- console.log("");
2577
- console.log(pc5__default.default.bold("How would you like to run amai?"));
2578
- console.log(pc5__default.default.gray("Background mode is highly recommended for better performance and stability."));
2579
- const answer = await promptUser(
2580
- pc5__default.default.cyan("Run in background? (Y/n): ")
2581
- );
2937
+ const answer = await promptUser(pc5__default.default.gray("run in background? (Y/n): "));
2582
2938
  const runInBackground = answer === "" || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
2583
2939
  if (runInBackground) {
2584
- console.log(pc5__default.default.green("Starting daemon in background..."));
2940
+ console.log(pc5__default.default.gray("starting..."));
2585
2941
  startDaemon();
2586
- console.log(pc5__default.default.green("Daemon started successfully!"));
2587
- console.log(pc5__default.default.gray('Use "amai status" to check daemon status.'));
2588
- console.log(pc5__default.default.gray('Use "amai stop" to stop the daemon.'));
2942
+ console.log(pc5__default.default.cyan("amai started"));
2943
+ console.log(pc5__default.default.gray('use "amai status" to check status'));
2944
+ console.log(pc5__default.default.gray('use "amai stop" to stop'));
2589
2945
  process.exit(0);
2590
2946
  } else {
2591
- console.log(pc5__default.default.yellow("Starting in foreground mode..."));
2947
+ console.log(pc5__default.default.gray("starting in foreground..."));
2592
2948
  startWithCodeServer();
2593
2949
  }
2594
2950
  })();