amai 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +688 -433
- package/dist/cli.js +684 -429
- package/dist/lib/daemon-entry.cjs +663 -408
- package/dist/lib/daemon-entry.js +659 -404
- package/dist/server.cjs +633 -378
- package/dist/server.js +629 -374
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
import WebSocket2 from 'ws';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import fs5, { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
5
4
|
import path10 from 'path';
|
|
6
|
-
import
|
|
5
|
+
import fs6, { readdirSync } from 'fs';
|
|
7
6
|
import os3 from 'os';
|
|
8
|
-
import {
|
|
9
|
-
import { promisify } from 'util';
|
|
7
|
+
import fs7, { unlink, mkdir } from 'fs/promises';
|
|
10
8
|
import pc3 from 'picocolors';
|
|
11
9
|
import { Hono } from 'hono';
|
|
12
10
|
import { serve } from '@hono/node-server';
|
|
13
11
|
import { cors } from 'hono/cors';
|
|
12
|
+
import { exec } from 'child_process';
|
|
13
|
+
import { promisify } from 'util';
|
|
14
14
|
|
|
15
15
|
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
16
16
|
var AMA_DIR = path10.join(os3.homedir(), ".amai");
|
|
@@ -26,14 +26,14 @@ var ProjectRegistry = class {
|
|
|
26
26
|
}
|
|
27
27
|
load() {
|
|
28
28
|
try {
|
|
29
|
-
if (
|
|
30
|
-
const data =
|
|
29
|
+
if (fs6.existsSync(REGISTRY_FILE)) {
|
|
30
|
+
const data = fs6.readFileSync(REGISTRY_FILE, "utf8");
|
|
31
31
|
const parsed = JSON.parse(data);
|
|
32
32
|
if (!Array.isArray(parsed)) {
|
|
33
33
|
console.error("Invalid project registry format: expected array, got", typeof parsed);
|
|
34
34
|
const backupFile = REGISTRY_FILE + ".backup." + Date.now();
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
fs6.copyFileSync(REGISTRY_FILE, backupFile);
|
|
36
|
+
fs6.unlinkSync(REGISTRY_FILE);
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
const projects = parsed;
|
|
@@ -46,11 +46,11 @@ var ProjectRegistry = class {
|
|
|
46
46
|
}
|
|
47
47
|
} catch (error) {
|
|
48
48
|
console.error("Failed to load project registry:", error);
|
|
49
|
-
if (
|
|
49
|
+
if (fs6.existsSync(REGISTRY_FILE)) {
|
|
50
50
|
try {
|
|
51
51
|
const backupFile = REGISTRY_FILE + ".backup." + Date.now();
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
fs6.copyFileSync(REGISTRY_FILE, backupFile);
|
|
53
|
+
fs6.unlinkSync(REGISTRY_FILE);
|
|
54
54
|
console.log("Corrupted registry file backed up and removed. Starting fresh.");
|
|
55
55
|
} catch (backupError) {
|
|
56
56
|
}
|
|
@@ -59,11 +59,11 @@ var ProjectRegistry = class {
|
|
|
59
59
|
}
|
|
60
60
|
save() {
|
|
61
61
|
try {
|
|
62
|
-
if (!
|
|
63
|
-
|
|
62
|
+
if (!fs6.existsSync(AMA_DIR)) {
|
|
63
|
+
fs6.mkdirSync(AMA_DIR, { recursive: true });
|
|
64
64
|
}
|
|
65
65
|
const projects = Array.from(this.projects.values());
|
|
66
|
-
|
|
66
|
+
fs6.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
|
|
67
67
|
} catch (error) {
|
|
68
68
|
console.error("Failed to save project registry:", error);
|
|
69
69
|
}
|
|
@@ -152,6 +152,60 @@ z.object({
|
|
|
152
152
|
),
|
|
153
153
|
end_line_one_indexed: z.number().optional().describe("The one-indexed line number to end reading at (inclusive).")
|
|
154
154
|
});
|
|
155
|
+
async function readFileContent(absolute_file_path, relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed) {
|
|
156
|
+
const file = Bun.file(absolute_file_path);
|
|
157
|
+
const exists = await file.exists();
|
|
158
|
+
if (!exists) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
message: `File not found: ${relative_file_path}`,
|
|
162
|
+
error: "FILE_NOT_FOUND"
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const fileContent = await file.text();
|
|
167
|
+
const lines = fileContent.split(/\r?\n/);
|
|
168
|
+
const totalLines = lines.length;
|
|
169
|
+
if (should_read_entire_file) {
|
|
170
|
+
return {
|
|
171
|
+
success: true,
|
|
172
|
+
message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
|
|
173
|
+
content: fileContent,
|
|
174
|
+
totalLines
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const startIndex = start_line_one_indexed - 1;
|
|
178
|
+
if (startIndex >= totalLines) {
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
|
|
182
|
+
error: "INVALID_LINE_RANGE"
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
|
|
186
|
+
const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
|
|
187
|
+
const linesRead = normalizedEnd - start_line_one_indexed + 1;
|
|
188
|
+
return {
|
|
189
|
+
success: true,
|
|
190
|
+
message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
|
|
191
|
+
content: selectedLines,
|
|
192
|
+
totalLines
|
|
193
|
+
};
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (error?.code === "EISDIR") {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
message: `Path is not a file: ${relative_file_path}`,
|
|
199
|
+
error: "NOT_A_FILE"
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
success: false,
|
|
204
|
+
message: `Failed to read file: ${relative_file_path}`,
|
|
205
|
+
error: "READ_ERROR"
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
155
209
|
var read_file = async function(input, projectCwd) {
|
|
156
210
|
const { relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed } = input;
|
|
157
211
|
try {
|
|
@@ -192,6 +246,7 @@ var read_file = async function(input, projectCwd) {
|
|
|
192
246
|
};
|
|
193
247
|
}
|
|
194
248
|
}
|
|
249
|
+
let absolute_file_path;
|
|
195
250
|
if (projectCwd) {
|
|
196
251
|
const validation = validatePath(relative_file_path, projectCwd);
|
|
197
252
|
if (!validation.valid) {
|
|
@@ -201,128 +256,17 @@ var read_file = async function(input, projectCwd) {
|
|
|
201
256
|
error: "ACCESS_DENIED"
|
|
202
257
|
};
|
|
203
258
|
}
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
const fileStats = await stat(absolute_file_path);
|
|
207
|
-
if (!fileStats.isFile()) {
|
|
208
|
-
return {
|
|
209
|
-
success: false,
|
|
210
|
-
message: `Path is not a file: ${relative_file_path}`,
|
|
211
|
-
error: "NOT_A_FILE"
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
} catch (error) {
|
|
215
|
-
if (error?.code === "ENOENT") {
|
|
216
|
-
return {
|
|
217
|
-
success: false,
|
|
218
|
-
message: `File not found: ${relative_file_path}`,
|
|
219
|
-
error: "FILE_NOT_FOUND"
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
success: false,
|
|
224
|
-
message: `Failed to access file: ${relative_file_path}`,
|
|
225
|
-
error: "READ_ERROR"
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
try {
|
|
229
|
-
const fileContent = await readFile(absolute_file_path, "utf-8");
|
|
230
|
-
const lines = fileContent.split(/\r?\n/);
|
|
231
|
-
const totalLines = lines.length;
|
|
232
|
-
if (should_read_entire_file) {
|
|
233
|
-
return {
|
|
234
|
-
success: true,
|
|
235
|
-
message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
|
|
236
|
-
content: fileContent,
|
|
237
|
-
totalLines
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
const startIndex = start_line_one_indexed - 1;
|
|
241
|
-
if (startIndex >= totalLines) {
|
|
242
|
-
return {
|
|
243
|
-
success: false,
|
|
244
|
-
message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
|
|
245
|
-
error: "INVALID_LINE_RANGE"
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
|
|
249
|
-
const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
|
|
250
|
-
const linesRead = normalizedEnd - start_line_one_indexed + 1;
|
|
251
|
-
return {
|
|
252
|
-
success: true,
|
|
253
|
-
message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
|
|
254
|
-
content: selectedLines,
|
|
255
|
-
totalLines
|
|
256
|
-
};
|
|
257
|
-
} catch {
|
|
258
|
-
return {
|
|
259
|
-
success: false,
|
|
260
|
-
message: `Failed to read file: ${relative_file_path}`,
|
|
261
|
-
error: "READ_ERROR"
|
|
262
|
-
};
|
|
263
|
-
}
|
|
259
|
+
absolute_file_path = validation.resolvedPath;
|
|
264
260
|
} else {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
} catch (error) {
|
|
276
|
-
if (error?.code === "ENOENT") {
|
|
277
|
-
return {
|
|
278
|
-
success: false,
|
|
279
|
-
message: `File not found: ${relative_file_path}`,
|
|
280
|
-
error: "FILE_NOT_FOUND"
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
return {
|
|
284
|
-
success: false,
|
|
285
|
-
message: `Failed to access file: ${relative_file_path}`,
|
|
286
|
-
error: "READ_ERROR"
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
try {
|
|
290
|
-
const fileContent = await readFile(absolute_file_path, "utf-8");
|
|
291
|
-
const lines = fileContent.split(/\r?\n/);
|
|
292
|
-
const totalLines = lines.length;
|
|
293
|
-
if (should_read_entire_file) {
|
|
294
|
-
return {
|
|
295
|
-
success: true,
|
|
296
|
-
message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
|
|
297
|
-
content: fileContent,
|
|
298
|
-
totalLines
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
const startIndex = start_line_one_indexed - 1;
|
|
302
|
-
if (startIndex >= totalLines) {
|
|
303
|
-
return {
|
|
304
|
-
success: false,
|
|
305
|
-
message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
|
|
306
|
-
error: "INVALID_LINE_RANGE"
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
|
|
310
|
-
const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
|
|
311
|
-
const linesRead = normalizedEnd - start_line_one_indexed + 1;
|
|
312
|
-
return {
|
|
313
|
-
success: true,
|
|
314
|
-
message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
|
|
315
|
-
content: selectedLines,
|
|
316
|
-
totalLines
|
|
317
|
-
};
|
|
318
|
-
} catch {
|
|
319
|
-
return {
|
|
320
|
-
success: false,
|
|
321
|
-
message: `Failed to read file: ${relative_file_path}`,
|
|
322
|
-
error: "READ_ERROR"
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
}
|
|
261
|
+
absolute_file_path = path10.resolve(relative_file_path);
|
|
262
|
+
}
|
|
263
|
+
return await readFileContent(
|
|
264
|
+
absolute_file_path,
|
|
265
|
+
relative_file_path,
|
|
266
|
+
should_read_entire_file,
|
|
267
|
+
start_line_one_indexed,
|
|
268
|
+
end_line_one_indexed
|
|
269
|
+
);
|
|
326
270
|
} catch {
|
|
327
271
|
return {
|
|
328
272
|
success: false,
|
|
@@ -413,13 +357,13 @@ var Diff = class {
|
|
|
413
357
|
editLength++;
|
|
414
358
|
};
|
|
415
359
|
if (callback) {
|
|
416
|
-
(function
|
|
360
|
+
(function exec2() {
|
|
417
361
|
setTimeout(function() {
|
|
418
362
|
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
419
363
|
return callback(void 0);
|
|
420
364
|
}
|
|
421
365
|
if (!execEditLength()) {
|
|
422
|
-
|
|
366
|
+
exec2();
|
|
423
367
|
}
|
|
424
368
|
}, 0);
|
|
425
369
|
})();
|
|
@@ -646,17 +590,19 @@ var apply_patch = async function(input, projectCwd) {
|
|
|
646
590
|
}
|
|
647
591
|
const basePath = projectCwd || process.cwd();
|
|
648
592
|
const absolute_file_path = resolveProjectPath(file_path, basePath);
|
|
593
|
+
const file = Bun.file(absolute_file_path);
|
|
594
|
+
const exists = await file.exists();
|
|
595
|
+
if (!exists) {
|
|
596
|
+
return {
|
|
597
|
+
success: false,
|
|
598
|
+
message: `File not found: ${file_path}`,
|
|
599
|
+
error: "FILE_NOT_FOUND"
|
|
600
|
+
};
|
|
601
|
+
}
|
|
649
602
|
let fileContent;
|
|
650
603
|
try {
|
|
651
|
-
fileContent = await
|
|
604
|
+
fileContent = await file.text();
|
|
652
605
|
} catch (error) {
|
|
653
|
-
if (error?.code === "ENOENT") {
|
|
654
|
-
return {
|
|
655
|
-
success: false,
|
|
656
|
-
message: `File not found: ${file_path}`,
|
|
657
|
-
error: "FILE_NOT_FOUND"
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
606
|
return {
|
|
661
607
|
success: false,
|
|
662
608
|
message: `Failed to read file: ${file_path}`,
|
|
@@ -680,7 +626,7 @@ var apply_patch = async function(input, projectCwd) {
|
|
|
680
626
|
}
|
|
681
627
|
const newContent = fileContent.replace(old_string, new_string);
|
|
682
628
|
try {
|
|
683
|
-
await
|
|
629
|
+
await Bun.write(absolute_file_path, newContent);
|
|
684
630
|
const diffStats = calculateDiffStats(fileContent, newContent);
|
|
685
631
|
return {
|
|
686
632
|
success: true,
|
|
@@ -729,25 +675,24 @@ var editFiles = async function(input, projectCwd) {
|
|
|
729
675
|
await mkdir(dirPath, { recursive: true });
|
|
730
676
|
let isNewFile = providedNewFile;
|
|
731
677
|
let existingContent = "";
|
|
678
|
+
const file = Bun.file(filePath);
|
|
732
679
|
if (isNewFile === void 0) {
|
|
733
|
-
|
|
734
|
-
|
|
680
|
+
const exists = await file.exists();
|
|
681
|
+
if (exists) {
|
|
682
|
+
existingContent = await file.text();
|
|
735
683
|
isNewFile = false;
|
|
736
|
-
}
|
|
684
|
+
} else {
|
|
737
685
|
isNewFile = true;
|
|
738
686
|
}
|
|
739
687
|
} else if (!isNewFile) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
688
|
+
const exists = await file.exists();
|
|
689
|
+
if (exists) {
|
|
690
|
+
existingContent = await file.text();
|
|
691
|
+
} else {
|
|
743
692
|
isNewFile = true;
|
|
744
693
|
}
|
|
745
694
|
}
|
|
746
|
-
|
|
747
|
-
await fs4.promises.writeFile(filePath, content);
|
|
748
|
-
} catch (writeError) {
|
|
749
|
-
throw writeError;
|
|
750
|
-
}
|
|
695
|
+
await Bun.write(filePath, content);
|
|
751
696
|
const diffStats = calculateDiffStats(existingContent, content);
|
|
752
697
|
if (isNewFile) {
|
|
753
698
|
return {
|
|
@@ -810,25 +755,31 @@ var deleteFile = async function(input, projectCwd) {
|
|
|
810
755
|
error: "INVALID_FILE_PATH"
|
|
811
756
|
};
|
|
812
757
|
}
|
|
813
|
-
const
|
|
814
|
-
|
|
758
|
+
const file = Bun.file(absolute_file_path);
|
|
759
|
+
const exists = await file.exists();
|
|
760
|
+
if (!exists) {
|
|
815
761
|
return {
|
|
816
762
|
success: false,
|
|
817
|
-
message: `
|
|
818
|
-
error: "
|
|
763
|
+
message: `File not found: ${realPath}`,
|
|
764
|
+
error: "FILE_NOT_FOUND"
|
|
819
765
|
};
|
|
820
766
|
}
|
|
821
|
-
|
|
767
|
+
let originalContent;
|
|
768
|
+
try {
|
|
769
|
+
originalContent = await file.text();
|
|
770
|
+
} catch {
|
|
822
771
|
return {
|
|
823
772
|
success: false,
|
|
824
773
|
message: `Failed to read file before deletion: ${realPath}`,
|
|
825
|
-
error: "
|
|
774
|
+
error: "READ_ERROR"
|
|
826
775
|
};
|
|
827
|
-
}
|
|
828
|
-
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
await unlink(absolute_file_path);
|
|
779
|
+
} catch {
|
|
829
780
|
return {
|
|
830
781
|
success: false,
|
|
831
|
-
message: `Failed to delete file
|
|
782
|
+
message: `Failed to delete file: ${realPath}`,
|
|
832
783
|
error: "DELETE_ERROR"
|
|
833
784
|
};
|
|
834
785
|
}
|
|
@@ -845,72 +796,218 @@ var deleteFile = async function(input, projectCwd) {
|
|
|
845
796
|
};
|
|
846
797
|
}
|
|
847
798
|
};
|
|
799
|
+
var GREP_LIMITS = {
|
|
800
|
+
DEFAULT_MAX_MATCHES: 200,
|
|
801
|
+
MAX_LINE_LENGTH: 500,
|
|
802
|
+
MAX_TOTAL_OUTPUT_SIZE: 1 * 1024 * 1024,
|
|
803
|
+
TRUNCATION_MESSAGE: "\n[Results truncated due to size limits. Use more specific patterns or file filters to narrow your search.]"
|
|
804
|
+
};
|
|
848
805
|
z.object({
|
|
849
806
|
query: z.string().describe("The regex pattern to search for"),
|
|
850
807
|
options: z.object({
|
|
851
808
|
includePattern: z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts")'),
|
|
852
809
|
excludePattern: z.string().optional().describe("Glob pattern for files to exclude"),
|
|
853
|
-
caseSensitive: z.boolean().optional().describe("Whether the search should be case sensitive")
|
|
854
|
-
|
|
810
|
+
caseSensitive: z.boolean().optional().describe("Whether the search should be case sensitive"),
|
|
811
|
+
path: z.string().optional().describe("Subdirectory to search in")
|
|
812
|
+
}).optional()
|
|
855
813
|
});
|
|
856
|
-
|
|
814
|
+
async function getRipgrepPath() {
|
|
815
|
+
const paths = [
|
|
816
|
+
"/opt/homebrew/bin/rg",
|
|
817
|
+
"/usr/local/bin/rg",
|
|
818
|
+
"/usr/bin/rg",
|
|
819
|
+
"rg"
|
|
820
|
+
// Fallback to PATH
|
|
821
|
+
];
|
|
822
|
+
for (const rgPath of paths) {
|
|
823
|
+
try {
|
|
824
|
+
const proc = Bun.spawn(["which", rgPath], { stdout: "pipe", stderr: "pipe" });
|
|
825
|
+
await proc.exited;
|
|
826
|
+
if (proc.exitCode === 0) {
|
|
827
|
+
return rgPath;
|
|
828
|
+
}
|
|
829
|
+
} catch {
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return "rg";
|
|
834
|
+
}
|
|
835
|
+
async function getMtimesBatched(files) {
|
|
836
|
+
const mtimeMap = /* @__PURE__ */ new Map();
|
|
837
|
+
const BATCH_SIZE = 50;
|
|
838
|
+
for (let i = 0; i < files.length; i += BATCH_SIZE) {
|
|
839
|
+
const batch = files.slice(i, i + BATCH_SIZE);
|
|
840
|
+
const results = await Promise.all(
|
|
841
|
+
batch.map(async (filePath) => {
|
|
842
|
+
const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
|
|
843
|
+
return { path: filePath, mtime };
|
|
844
|
+
})
|
|
845
|
+
);
|
|
846
|
+
results.forEach(({ path: path13, mtime }) => mtimeMap.set(path13, mtime));
|
|
847
|
+
}
|
|
848
|
+
return mtimeMap;
|
|
849
|
+
}
|
|
857
850
|
var grepTool = async function(input, projectCwd) {
|
|
858
851
|
const { query, options } = input;
|
|
852
|
+
if (!query || query.trim() === "") {
|
|
853
|
+
return {
|
|
854
|
+
success: false,
|
|
855
|
+
message: "Missing required parameter: query",
|
|
856
|
+
error: "MISSING_QUERY"
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
859
|
try {
|
|
860
|
-
const { includePattern, excludePattern
|
|
861
|
-
|
|
862
|
-
if (
|
|
860
|
+
const { includePattern, excludePattern, caseSensitive, path: subPath } = options || {};
|
|
861
|
+
let searchDir = projectCwd || process.cwd();
|
|
862
|
+
if (subPath) {
|
|
863
|
+
searchDir = path10.isAbsolute(subPath) ? subPath : path10.resolve(searchDir, subPath);
|
|
864
|
+
if (projectCwd) {
|
|
865
|
+
const validation = validatePath(subPath, projectCwd);
|
|
866
|
+
if (!validation.valid) {
|
|
867
|
+
return {
|
|
868
|
+
success: false,
|
|
869
|
+
message: validation.error || "Path validation failed",
|
|
870
|
+
error: "ACCESS_DENIED"
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
if (!fs6.existsSync(searchDir)) {
|
|
863
876
|
return {
|
|
864
877
|
success: false,
|
|
865
|
-
message:
|
|
866
|
-
error: "
|
|
878
|
+
message: `Directory not found: ${searchDir}`,
|
|
879
|
+
error: "DIR_NOT_FOUND"
|
|
867
880
|
};
|
|
868
881
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
882
|
+
const rgPath = await getRipgrepPath();
|
|
883
|
+
const args = [
|
|
884
|
+
"-n",
|
|
885
|
+
// Line numbers
|
|
886
|
+
"--with-filename",
|
|
887
|
+
// Always show filename
|
|
888
|
+
"--no-heading",
|
|
889
|
+
// Don't group by file
|
|
890
|
+
"--color=never",
|
|
891
|
+
// No ANSI colors
|
|
892
|
+
"--max-count=100",
|
|
893
|
+
// Max matches per file
|
|
894
|
+
"--max-columns=1000"
|
|
895
|
+
// Truncate long lines
|
|
896
|
+
];
|
|
897
|
+
if (!caseSensitive) {
|
|
898
|
+
args.push("-i");
|
|
872
899
|
}
|
|
873
900
|
if (includePattern) {
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
if (
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
901
|
+
args.push("--glob", includePattern);
|
|
902
|
+
}
|
|
903
|
+
if (excludePattern) {
|
|
904
|
+
args.push("--glob", `!${excludePattern}`);
|
|
905
|
+
}
|
|
906
|
+
args.push("--glob", "!node_modules/**");
|
|
907
|
+
args.push("--glob", "!.git/**");
|
|
908
|
+
args.push("--glob", "!dist/**");
|
|
909
|
+
args.push("--glob", "!build/**");
|
|
910
|
+
args.push("--glob", "!*.min.js");
|
|
911
|
+
args.push("--glob", "!*.min.css");
|
|
912
|
+
args.push("--glob", "!package-lock.json");
|
|
913
|
+
args.push("--glob", "!yarn.lock");
|
|
914
|
+
args.push("--glob", "!bun.lockb");
|
|
915
|
+
args.push("--regexp", query);
|
|
916
|
+
args.push(searchDir);
|
|
917
|
+
const proc = Bun.spawn([rgPath, ...args], {
|
|
918
|
+
stdout: "pipe",
|
|
919
|
+
stderr: "pipe"
|
|
920
|
+
});
|
|
921
|
+
const stdout = await new Response(proc.stdout).text();
|
|
922
|
+
const stderr = await new Response(proc.stderr).text();
|
|
923
|
+
const exitCode = await proc.exited;
|
|
924
|
+
if (exitCode === 1) {
|
|
925
|
+
return {
|
|
926
|
+
success: true,
|
|
927
|
+
matches: [],
|
|
928
|
+
detailedMatches: [],
|
|
929
|
+
query,
|
|
930
|
+
matchCount: 0,
|
|
931
|
+
message: `No matches found for pattern: ${query}`
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
if (exitCode !== 0) {
|
|
935
|
+
return {
|
|
936
|
+
success: false,
|
|
937
|
+
message: `Ripgrep error: ${stderr || "Unknown error"}`,
|
|
938
|
+
error: "GREP_EXEC_ERROR"
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
const lines = stdout.trim().split("\n").filter((line) => line.length > 0);
|
|
942
|
+
const rawMatches = [];
|
|
943
|
+
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
944
|
+
for (const line of lines) {
|
|
945
|
+
const firstColon = line.indexOf(":");
|
|
946
|
+
const secondColon = line.indexOf(":", firstColon + 1);
|
|
947
|
+
if (firstColon > 0 && secondColon > firstColon) {
|
|
948
|
+
const file = line.substring(0, firstColon);
|
|
949
|
+
const lineNumber = parseInt(line.substring(firstColon + 1, secondColon), 10);
|
|
950
|
+
let content = line.substring(secondColon + 1);
|
|
951
|
+
if (content.length > GREP_LIMITS.MAX_LINE_LENGTH) {
|
|
952
|
+
content = content.substring(0, GREP_LIMITS.MAX_LINE_LENGTH) + "...";
|
|
894
953
|
}
|
|
895
|
-
|
|
954
|
+
rawMatches.push({
|
|
896
955
|
file,
|
|
897
956
|
lineNumber,
|
|
898
|
-
content
|
|
957
|
+
content: content.trim(),
|
|
958
|
+
mtime: 0
|
|
899
959
|
});
|
|
900
|
-
|
|
901
|
-
} else {
|
|
902
|
-
matches.push(rawMatch);
|
|
960
|
+
uniqueFiles.add(file);
|
|
903
961
|
}
|
|
904
962
|
}
|
|
963
|
+
const mtimeMap = await getMtimesBatched(Array.from(uniqueFiles));
|
|
964
|
+
for (const match of rawMatches) {
|
|
965
|
+
match.mtime = mtimeMap.get(match.file) || 0;
|
|
966
|
+
}
|
|
967
|
+
rawMatches.sort((a, b) => {
|
|
968
|
+
if (b.mtime !== a.mtime) {
|
|
969
|
+
return b.mtime - a.mtime;
|
|
970
|
+
}
|
|
971
|
+
return a.file.localeCompare(b.file);
|
|
972
|
+
});
|
|
973
|
+
const truncated = rawMatches.length > GREP_LIMITS.DEFAULT_MAX_MATCHES;
|
|
974
|
+
const finalMatches = truncated ? rawMatches.slice(0, GREP_LIMITS.DEFAULT_MAX_MATCHES) : rawMatches;
|
|
975
|
+
const detailedMatches = finalMatches.map((m) => ({
|
|
976
|
+
file: m.file,
|
|
977
|
+
lineNumber: m.lineNumber,
|
|
978
|
+
content: m.content
|
|
979
|
+
}));
|
|
980
|
+
const matches = finalMatches.map(
|
|
981
|
+
(m) => `${m.file}:${m.lineNumber}:${m.content}`
|
|
982
|
+
);
|
|
983
|
+
const groupedOutput = [`Found ${finalMatches.length} matches`];
|
|
984
|
+
let currentFile = "";
|
|
985
|
+
for (const match of finalMatches) {
|
|
986
|
+
if (currentFile !== match.file) {
|
|
987
|
+
if (currentFile !== "") {
|
|
988
|
+
groupedOutput.push("");
|
|
989
|
+
}
|
|
990
|
+
currentFile = match.file;
|
|
991
|
+
groupedOutput.push(`${match.file}:`);
|
|
992
|
+
}
|
|
993
|
+
groupedOutput.push(` Line ${match.lineNumber}: ${match.content}`);
|
|
994
|
+
}
|
|
995
|
+
if (truncated) {
|
|
996
|
+
groupedOutput.push("");
|
|
997
|
+
groupedOutput.push(GREP_LIMITS.TRUNCATION_MESSAGE);
|
|
998
|
+
}
|
|
905
999
|
return {
|
|
906
1000
|
success: true,
|
|
907
1001
|
matches,
|
|
908
1002
|
detailedMatches,
|
|
909
1003
|
query,
|
|
910
|
-
matchCount:
|
|
911
|
-
|
|
1004
|
+
matchCount: finalMatches.length,
|
|
1005
|
+
truncated,
|
|
1006
|
+
message: `Found ${finalMatches.length} matches for pattern: ${query}`,
|
|
1007
|
+
content: groupedOutput.join("\n")
|
|
912
1008
|
};
|
|
913
1009
|
} catch (error) {
|
|
1010
|
+
console.error("[grep] error:", error);
|
|
914
1011
|
return {
|
|
915
1012
|
success: false,
|
|
916
1013
|
message: error?.message || String(error),
|
|
@@ -919,9 +1016,25 @@ var grepTool = async function(input, projectCwd) {
|
|
|
919
1016
|
}
|
|
920
1017
|
};
|
|
921
1018
|
z.object({
|
|
922
|
-
pattern: z.string().describe('Glob pattern (e.g., "**/*.js")'),
|
|
923
|
-
path: z.string().optional().describe("
|
|
1019
|
+
pattern: z.string().describe('Glob pattern to match files (e.g., "**/*.js", "src/**/*.ts", "*.json"). Supports standard glob syntax with *, **, and ? wildcards'),
|
|
1020
|
+
path: z.string().optional().describe("Optional relative directory path within the project to limit the search scope. If not provided, searches from the project root")
|
|
924
1021
|
});
|
|
1022
|
+
var RESULT_LIMIT = 100;
|
|
1023
|
+
var MTIME_BATCH_SIZE = 50;
|
|
1024
|
+
async function getMtimesBatched2(files) {
|
|
1025
|
+
const results = [];
|
|
1026
|
+
for (let i = 0; i < files.length; i += MTIME_BATCH_SIZE) {
|
|
1027
|
+
const batch = files.slice(i, i + MTIME_BATCH_SIZE);
|
|
1028
|
+
const batchResults = await Promise.all(
|
|
1029
|
+
batch.map(async (filePath) => {
|
|
1030
|
+
const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
|
|
1031
|
+
return { path: filePath, mtime };
|
|
1032
|
+
})
|
|
1033
|
+
);
|
|
1034
|
+
results.push(...batchResults);
|
|
1035
|
+
}
|
|
1036
|
+
return results;
|
|
1037
|
+
}
|
|
925
1038
|
var globTool = async function(input, projectCwd) {
|
|
926
1039
|
const { pattern, path: inputPath } = input;
|
|
927
1040
|
if (!pattern) {
|
|
@@ -934,6 +1047,13 @@ var globTool = async function(input, projectCwd) {
|
|
|
934
1047
|
try {
|
|
935
1048
|
const basePath = projectCwd || process.cwd();
|
|
936
1049
|
const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
|
|
1050
|
+
if (!fs6.existsSync(searchPath)) {
|
|
1051
|
+
return {
|
|
1052
|
+
success: false,
|
|
1053
|
+
message: `Directory not found: ${searchPath}`,
|
|
1054
|
+
error: "DIR_NOT_FOUND"
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
937
1057
|
if (projectCwd && inputPath) {
|
|
938
1058
|
const validation = validatePath(inputPath, projectCwd);
|
|
939
1059
|
if (!validation.valid) {
|
|
@@ -944,21 +1064,49 @@ var globTool = async function(input, projectCwd) {
|
|
|
944
1064
|
};
|
|
945
1065
|
}
|
|
946
1066
|
}
|
|
947
|
-
const
|
|
948
|
-
cwd: searchPath
|
|
949
|
-
});
|
|
1067
|
+
const glob = new Bun.Glob(pattern);
|
|
950
1068
|
const files = [];
|
|
951
|
-
|
|
952
|
-
|
|
1069
|
+
let truncated = false;
|
|
1070
|
+
for await (const match of glob.scan({
|
|
1071
|
+
cwd: searchPath,
|
|
1072
|
+
absolute: true,
|
|
1073
|
+
onlyFiles: true,
|
|
1074
|
+
followSymlinks: false
|
|
1075
|
+
})) {
|
|
1076
|
+
if (match.includes("/node_modules/") || match.includes("/.git/")) {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (files.length >= RESULT_LIMIT) {
|
|
1080
|
+
truncated = true;
|
|
1081
|
+
break;
|
|
1082
|
+
}
|
|
1083
|
+
files.push(match);
|
|
1084
|
+
}
|
|
1085
|
+
const filesWithMtime = await getMtimesBatched2(files);
|
|
1086
|
+
filesWithMtime.sort((a, b) => b.mtime - a.mtime);
|
|
1087
|
+
const output = [];
|
|
1088
|
+
if (filesWithMtime.length === 0) {
|
|
1089
|
+
output.push("No files found");
|
|
1090
|
+
} else {
|
|
1091
|
+
output.push(...filesWithMtime.map((f) => f.path));
|
|
1092
|
+
if (truncated) {
|
|
1093
|
+
output.push("");
|
|
1094
|
+
output.push("(Results are truncated. Consider using a more specific path or pattern.)");
|
|
1095
|
+
}
|
|
953
1096
|
}
|
|
954
1097
|
const searchLocation = inputPath ? ` in "${inputPath}"` : " in current directory";
|
|
955
|
-
const message = `Found ${
|
|
1098
|
+
const message = `Found ${filesWithMtime.length} matches for pattern "${pattern}"${searchLocation}`;
|
|
956
1099
|
return {
|
|
957
1100
|
success: true,
|
|
958
1101
|
message,
|
|
959
|
-
|
|
1102
|
+
metadata: {
|
|
1103
|
+
count: filesWithMtime.length,
|
|
1104
|
+
truncated
|
|
1105
|
+
},
|
|
1106
|
+
content: output.join("\n")
|
|
960
1107
|
};
|
|
961
1108
|
} catch (error) {
|
|
1109
|
+
console.error("[glob] error:", error);
|
|
962
1110
|
return {
|
|
963
1111
|
success: false,
|
|
964
1112
|
message: `Failed to find files matching pattern: ${pattern}`,
|
|
@@ -966,59 +1114,120 @@ var globTool = async function(input, projectCwd) {
|
|
|
966
1114
|
};
|
|
967
1115
|
}
|
|
968
1116
|
};
|
|
969
|
-
var
|
|
970
|
-
"node_modules
|
|
971
|
-
"__pycache__
|
|
972
|
-
".git
|
|
973
|
-
"dist
|
|
974
|
-
"build
|
|
975
|
-
"target
|
|
976
|
-
"vendor
|
|
977
|
-
"bin
|
|
978
|
-
"obj
|
|
979
|
-
".idea
|
|
980
|
-
".vscode
|
|
981
|
-
".zig-cache
|
|
1117
|
+
var IGNORE_PATTERNS = [
|
|
1118
|
+
"node_modules",
|
|
1119
|
+
"__pycache__",
|
|
1120
|
+
".git",
|
|
1121
|
+
"dist",
|
|
1122
|
+
"build",
|
|
1123
|
+
"target",
|
|
1124
|
+
"vendor",
|
|
1125
|
+
"bin",
|
|
1126
|
+
"obj",
|
|
1127
|
+
".idea",
|
|
1128
|
+
".vscode",
|
|
1129
|
+
".zig-cache",
|
|
982
1130
|
"zig-out",
|
|
983
1131
|
".coverage",
|
|
984
|
-
"coverage
|
|
985
|
-
"
|
|
986
|
-
"
|
|
987
|
-
"
|
|
988
|
-
"
|
|
989
|
-
"
|
|
990
|
-
"
|
|
991
|
-
"
|
|
992
|
-
"
|
|
993
|
-
"
|
|
1132
|
+
"coverage",
|
|
1133
|
+
"tmp",
|
|
1134
|
+
"temp",
|
|
1135
|
+
".cache",
|
|
1136
|
+
"cache",
|
|
1137
|
+
"logs",
|
|
1138
|
+
".venv",
|
|
1139
|
+
"venv",
|
|
1140
|
+
"env",
|
|
1141
|
+
".next",
|
|
1142
|
+
".turbo",
|
|
1143
|
+
".vercel",
|
|
1144
|
+
".output"
|
|
994
1145
|
];
|
|
995
|
-
var
|
|
1146
|
+
var RESULT_LIMIT2 = 500;
|
|
1147
|
+
var MTIME_BATCH_SIZE2 = 50;
|
|
996
1148
|
z.object({
|
|
997
|
-
path: z.string().optional(),
|
|
998
|
-
recursive: z.boolean().optional().describe("Whether to list files recursively"),
|
|
999
|
-
maxDepth: z.number().optional().describe("Maximum recursion depth (default:
|
|
1149
|
+
path: z.string().optional().describe("Relative path to the directory to list"),
|
|
1150
|
+
recursive: z.boolean().optional().describe("Whether to list files recursively (default: true)"),
|
|
1151
|
+
maxDepth: z.number().optional().describe("Maximum recursion depth (default: 3)"),
|
|
1000
1152
|
pattern: z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
|
|
1001
|
-
|
|
1002
|
-
includeFiles: z.boolean().optional().describe("Whether to include files in results (default: true)")
|
|
1153
|
+
showHidden: z.boolean().optional().describe("Whether to show hidden files (default: false)")
|
|
1003
1154
|
});
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1155
|
+
function shouldIgnore(name, showHidden) {
|
|
1156
|
+
if (!showHidden && name.startsWith(".") && name !== ".") {
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
return IGNORE_PATTERNS.includes(name);
|
|
1160
|
+
}
|
|
1161
|
+
function matchPattern(name, pattern) {
|
|
1162
|
+
if (!pattern) return true;
|
|
1163
|
+
if (pattern.startsWith(".") && !pattern.includes("*")) {
|
|
1164
|
+
return name.endsWith(pattern);
|
|
1165
|
+
}
|
|
1166
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1167
|
+
const regex = new RegExp(`^${escaped}$`, "i");
|
|
1168
|
+
return regex.test(name);
|
|
1169
|
+
}
|
|
1170
|
+
async function getMtimesBatched3(entries) {
|
|
1171
|
+
for (let i = 0; i < entries.length; i += MTIME_BATCH_SIZE2) {
|
|
1172
|
+
const batch = entries.slice(i, i + MTIME_BATCH_SIZE2);
|
|
1173
|
+
await Promise.all(
|
|
1174
|
+
batch.map(async (entry) => {
|
|
1175
|
+
entry.mtime = await Bun.file(entry.absolutePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
|
|
1176
|
+
})
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
function buildTreeOutput(entries, basePath) {
|
|
1181
|
+
const tree = /* @__PURE__ */ new Map();
|
|
1182
|
+
for (const entry of entries) {
|
|
1183
|
+
const dir = path10.dirname(entry.relativePath);
|
|
1184
|
+
const dirKey = dir === "." ? "" : dir;
|
|
1185
|
+
if (!tree.has(dirKey)) {
|
|
1186
|
+
tree.set(dirKey, []);
|
|
1187
|
+
}
|
|
1188
|
+
tree.get(dirKey).push(entry);
|
|
1189
|
+
}
|
|
1190
|
+
for (const [, items] of tree) {
|
|
1191
|
+
items.sort((a, b) => {
|
|
1192
|
+
if (a.type !== b.type) {
|
|
1193
|
+
return a.type === "directory" ? -1 : 1;
|
|
1194
|
+
}
|
|
1195
|
+
return a.name.localeCompare(b.name);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
const lines = [`${basePath}/`];
|
|
1199
|
+
function renderLevel(dirPath, indent) {
|
|
1200
|
+
const items = tree.get(dirPath) || [];
|
|
1201
|
+
for (let i = 0; i < items.length; i++) {
|
|
1202
|
+
const item = items[i];
|
|
1203
|
+
const isLast = i === items.length - 1;
|
|
1204
|
+
const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
1205
|
+
const childIndent = indent + (isLast ? " " : "\u2502 ");
|
|
1206
|
+
if (item.type === "directory") {
|
|
1207
|
+
lines.push(`${indent}${prefix}${item.name}/`);
|
|
1208
|
+
const childPath = dirPath ? `${dirPath}/${item.name}` : item.name;
|
|
1209
|
+
renderLevel(childPath, childIndent);
|
|
1210
|
+
} else {
|
|
1211
|
+
lines.push(`${indent}${prefix}${item.name}`);
|
|
1212
|
+
}
|
|
1013
1213
|
}
|
|
1014
1214
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1215
|
+
renderLevel("", "");
|
|
1216
|
+
return lines.join("\n");
|
|
1217
|
+
}
|
|
1218
|
+
var list = async function(input, projectCwd) {
|
|
1219
|
+
const {
|
|
1220
|
+
path: relativePath,
|
|
1221
|
+
recursive = true,
|
|
1222
|
+
maxDepth = 3,
|
|
1223
|
+
pattern,
|
|
1224
|
+
showHidden = false
|
|
1225
|
+
} = input;
|
|
1226
|
+
if (maxDepth !== void 0 && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
|
|
1018
1227
|
return {
|
|
1019
1228
|
success: false,
|
|
1020
|
-
message: "
|
|
1021
|
-
error: "
|
|
1229
|
+
message: "maxDepth must be a non-negative integer",
|
|
1230
|
+
error: "INVALID_MAX_DEPTH"
|
|
1022
1231
|
};
|
|
1023
1232
|
}
|
|
1024
1233
|
try {
|
|
@@ -1034,88 +1243,119 @@ var list = async function(input, projectCwd) {
|
|
|
1034
1243
|
};
|
|
1035
1244
|
}
|
|
1036
1245
|
}
|
|
1037
|
-
|
|
1038
|
-
await access(absolutePath);
|
|
1039
|
-
} catch {
|
|
1246
|
+
if (!fs6.existsSync(absolutePath)) {
|
|
1040
1247
|
return {
|
|
1041
1248
|
success: false,
|
|
1042
|
-
message: `
|
|
1043
|
-
error: "
|
|
1249
|
+
message: `Directory not found: ${absolutePath}`,
|
|
1250
|
+
error: "DIR_NOT_FOUND"
|
|
1044
1251
|
};
|
|
1045
1252
|
}
|
|
1046
|
-
const
|
|
1047
|
-
if (!
|
|
1253
|
+
const stats = fs6.statSync(absolutePath);
|
|
1254
|
+
if (!stats.isDirectory()) {
|
|
1048
1255
|
return {
|
|
1049
1256
|
success: false,
|
|
1050
|
-
message: `
|
|
1051
|
-
error: "
|
|
1257
|
+
message: `Path is not a directory: ${absolutePath}`,
|
|
1258
|
+
error: "NOT_A_DIRECTORY"
|
|
1052
1259
|
};
|
|
1053
1260
|
}
|
|
1054
1261
|
const collected = [];
|
|
1055
|
-
|
|
1056
|
-
if (!pattern) return null;
|
|
1057
|
-
if (pattern.startsWith(".") && !pattern.includes("*") && !pattern.includes("?")) {
|
|
1058
|
-
return (entryName) => entryName.endsWith(pattern);
|
|
1059
|
-
}
|
|
1060
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1061
|
-
const regex = new RegExp(`^${escaped}$`);
|
|
1062
|
-
return (entryName) => regex.test(entryName);
|
|
1063
|
-
})();
|
|
1064
|
-
const matchPattern = (entryName) => {
|
|
1065
|
-
if (!patternMatcher) return true;
|
|
1066
|
-
return patternMatcher(entryName);
|
|
1067
|
-
};
|
|
1068
|
-
const maxDepthNormalized = recursive ? maxDepth ?? Infinity : 0;
|
|
1262
|
+
let truncated = false;
|
|
1069
1263
|
const walk = async (currentDir, depth) => {
|
|
1070
|
-
|
|
1264
|
+
if (collected.length >= RESULT_LIMIT2) {
|
|
1265
|
+
truncated = true;
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
let entries;
|
|
1269
|
+
try {
|
|
1270
|
+
entries = fs6.readdirSync(currentDir, { withFileTypes: true });
|
|
1271
|
+
} catch {
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
entries.sort((a, b) => {
|
|
1275
|
+
if (a.isDirectory() !== b.isDirectory()) {
|
|
1276
|
+
return a.isDirectory() ? -1 : 1;
|
|
1277
|
+
}
|
|
1278
|
+
return a.name.localeCompare(b.name);
|
|
1279
|
+
});
|
|
1071
1280
|
for (const entry of entries) {
|
|
1281
|
+
if (collected.length >= RESULT_LIMIT2) {
|
|
1282
|
+
truncated = true;
|
|
1283
|
+
break;
|
|
1284
|
+
}
|
|
1285
|
+
if (shouldIgnore(entry.name, showHidden)) {
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1072
1288
|
const entryAbsolutePath = path10.join(currentDir, entry.name);
|
|
1073
|
-
const entryRelativePath = path10.relative(absolutePath, entryAbsolutePath)
|
|
1289
|
+
const entryRelativePath = path10.relative(absolutePath, entryAbsolutePath);
|
|
1074
1290
|
if (entry.isDirectory()) {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
if (recursive && depth < maxDepthNormalized && !isExcluded) {
|
|
1291
|
+
collected.push({
|
|
1292
|
+
name: entry.name,
|
|
1293
|
+
absolutePath: entryAbsolutePath,
|
|
1294
|
+
relativePath: entryRelativePath,
|
|
1295
|
+
type: "directory",
|
|
1296
|
+
mtime: 0,
|
|
1297
|
+
depth
|
|
1298
|
+
});
|
|
1299
|
+
if (recursive && depth < maxDepth) {
|
|
1085
1300
|
await walk(entryAbsolutePath, depth + 1);
|
|
1086
1301
|
}
|
|
1087
1302
|
} else if (entry.isFile()) {
|
|
1088
|
-
if (
|
|
1303
|
+
if (matchPattern(entry.name, pattern)) {
|
|
1089
1304
|
collected.push({
|
|
1090
1305
|
name: entry.name,
|
|
1091
1306
|
absolutePath: entryAbsolutePath,
|
|
1092
1307
|
relativePath: entryRelativePath,
|
|
1093
|
-
type: "file"
|
|
1308
|
+
type: "file",
|
|
1309
|
+
mtime: 0,
|
|
1310
|
+
depth
|
|
1094
1311
|
});
|
|
1095
1312
|
}
|
|
1096
1313
|
}
|
|
1097
1314
|
}
|
|
1098
1315
|
};
|
|
1099
1316
|
await walk(absolutePath, 0);
|
|
1317
|
+
await getMtimesBatched3(collected);
|
|
1100
1318
|
const totalFiles = collected.filter((item) => item.type === "file").length;
|
|
1101
1319
|
const totalDirectories = collected.filter((item) => item.type === "directory").length;
|
|
1102
|
-
|
|
1320
|
+
const treeOutput = buildTreeOutput(collected, relativePath || path10.basename(absolutePath));
|
|
1321
|
+
let message = `Listed ${collected.length} items`;
|
|
1322
|
+
if (relativePath) {
|
|
1323
|
+
message += ` in "${relativePath}"`;
|
|
1324
|
+
}
|
|
1325
|
+
message += ` (${totalFiles} files, ${totalDirectories} directories)`;
|
|
1103
1326
|
if (recursive) {
|
|
1104
|
-
message += `
|
|
1327
|
+
message += ` [depth: ${maxDepth}]`;
|
|
1105
1328
|
}
|
|
1106
1329
|
if (pattern) {
|
|
1107
|
-
message += `
|
|
1330
|
+
message += ` [filter: ${pattern}]`;
|
|
1108
1331
|
}
|
|
1109
|
-
|
|
1332
|
+
if (truncated) {
|
|
1333
|
+
message += ` [TRUNCATED at ${RESULT_LIMIT2} items]`;
|
|
1334
|
+
}
|
|
1335
|
+
const files = collected.map((item) => ({
|
|
1336
|
+
name: item.name,
|
|
1337
|
+
path: item.relativePath,
|
|
1338
|
+
type: item.type
|
|
1339
|
+
}));
|
|
1110
1340
|
return {
|
|
1111
1341
|
success: true,
|
|
1112
1342
|
message,
|
|
1113
|
-
|
|
1343
|
+
metadata: {
|
|
1344
|
+
totalFiles,
|
|
1345
|
+
totalDirectories,
|
|
1346
|
+
totalItems: collected.length,
|
|
1347
|
+
truncated,
|
|
1348
|
+
maxDepth,
|
|
1349
|
+
recursive
|
|
1350
|
+
},
|
|
1351
|
+
files,
|
|
1352
|
+
content: treeOutput
|
|
1114
1353
|
};
|
|
1115
1354
|
} catch (error) {
|
|
1355
|
+
console.error("[list] error:", error);
|
|
1116
1356
|
return {
|
|
1117
1357
|
success: false,
|
|
1118
|
-
message: `Failed to list
|
|
1358
|
+
message: `Failed to list directory: ${error}`,
|
|
1119
1359
|
error: "LIST_ERROR"
|
|
1120
1360
|
};
|
|
1121
1361
|
}
|
|
@@ -1131,19 +1371,19 @@ var startHttpServer = () => {
|
|
|
1131
1371
|
var CREDENTIALS_DIR = path10.join(os3.homedir(), ".amai");
|
|
1132
1372
|
var CREDENTIALS_PATH = path10.join(CREDENTIALS_DIR, "credentials.json");
|
|
1133
1373
|
function getTokens() {
|
|
1134
|
-
if (!
|
|
1374
|
+
if (!fs6.existsSync(CREDENTIALS_PATH)) {
|
|
1135
1375
|
return null;
|
|
1136
1376
|
}
|
|
1137
|
-
const raw =
|
|
1377
|
+
const raw = fs6.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1138
1378
|
const data = JSON.parse(raw);
|
|
1139
1379
|
return data;
|
|
1140
1380
|
}
|
|
1141
1381
|
var getUserId = () => {
|
|
1142
1382
|
try {
|
|
1143
|
-
if (!
|
|
1383
|
+
if (!fs6.existsSync(CREDENTIALS_PATH)) {
|
|
1144
1384
|
return;
|
|
1145
1385
|
}
|
|
1146
|
-
const raw =
|
|
1386
|
+
const raw = fs6.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1147
1387
|
const data = JSON.parse(raw);
|
|
1148
1388
|
return {
|
|
1149
1389
|
userId: data.user.id
|
|
@@ -1192,7 +1432,7 @@ z.object({
|
|
|
1192
1432
|
command: z.string().describe("The terminal command to execute (e.g., 'ls -la', 'pwd', 'echo $HOME')"),
|
|
1193
1433
|
is_background: z.boolean().describe("Whether the command should be run in the background")
|
|
1194
1434
|
}).merge(ExplanationSchema);
|
|
1195
|
-
var runSecureTerminalCommand = async (command, timeout) => {
|
|
1435
|
+
var runSecureTerminalCommand = async (command, timeout, cwd) => {
|
|
1196
1436
|
try {
|
|
1197
1437
|
if (isHarmfulCommand(command)) {
|
|
1198
1438
|
console.log(`[CLI] Harmful command detected: ${command}`);
|
|
@@ -1202,42 +1442,44 @@ var runSecureTerminalCommand = async (command, timeout) => {
|
|
|
1202
1442
|
error: "HARMFUL_COMMAND_DETECTED"
|
|
1203
1443
|
};
|
|
1204
1444
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
shell: true
|
|
1210
|
-
});
|
|
1211
|
-
let stdout = "";
|
|
1212
|
-
let stderr = "";
|
|
1213
|
-
let timeoutId = null;
|
|
1214
|
-
if (timeoutId > 0) {
|
|
1215
|
-
timeoutId = setTimeout(() => {
|
|
1216
|
-
child.kill("SIGKILL");
|
|
1217
|
-
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1218
|
-
}, timeout);
|
|
1219
|
-
}
|
|
1220
|
-
child.stdout?.on("data", (data) => {
|
|
1221
|
-
stdout += data.toString();
|
|
1222
|
-
});
|
|
1223
|
-
child.stderr?.on("data", (data) => {
|
|
1224
|
-
stderr += data.toString();
|
|
1225
|
-
});
|
|
1226
|
-
child.stdout.on("close", (code) => {
|
|
1227
|
-
if (timeoutId) {
|
|
1228
|
-
clearTimeout(timeoutId);
|
|
1229
|
-
}
|
|
1230
|
-
resolve({ stdout, stderr, exitCode: code || 0 });
|
|
1231
|
-
});
|
|
1232
|
-
child.stderr.on("error", (error) => {
|
|
1233
|
-
if (timeoutId) {
|
|
1234
|
-
clearTimeout(timeoutId);
|
|
1235
|
-
}
|
|
1236
|
-
reject(error);
|
|
1237
|
-
});
|
|
1445
|
+
const proc = Bun.spawn(["sh", "-c", command], {
|
|
1446
|
+
cwd: cwd || process.cwd(),
|
|
1447
|
+
stdout: "pipe",
|
|
1448
|
+
stderr: "pipe"
|
|
1238
1449
|
});
|
|
1239
|
-
|
|
1240
|
-
|
|
1450
|
+
let timedOut = false;
|
|
1451
|
+
let timeoutId = null;
|
|
1452
|
+
if (timeout > 0) {
|
|
1453
|
+
timeoutId = setTimeout(() => {
|
|
1454
|
+
timedOut = true;
|
|
1455
|
+
proc.kill();
|
|
1456
|
+
}, timeout);
|
|
1457
|
+
}
|
|
1458
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
1459
|
+
new Response(proc.stdout).text(),
|
|
1460
|
+
new Response(proc.stderr).text(),
|
|
1461
|
+
proc.exited
|
|
1462
|
+
]);
|
|
1463
|
+
if (timeoutId) {
|
|
1464
|
+
clearTimeout(timeoutId);
|
|
1465
|
+
}
|
|
1466
|
+
if (timedOut) {
|
|
1467
|
+
return {
|
|
1468
|
+
success: false,
|
|
1469
|
+
message: `Command timed out after ${timeout}ms`,
|
|
1470
|
+
error: "TIMEOUT",
|
|
1471
|
+
stdout,
|
|
1472
|
+
stderr
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
return { stdout, stderr, exitCode };
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
console.error("Error while executing the securedShell command", error);
|
|
1478
|
+
return {
|
|
1479
|
+
success: false,
|
|
1480
|
+
message: "Error while executing the securedShell command",
|
|
1481
|
+
error: error.message
|
|
1482
|
+
};
|
|
1241
1483
|
}
|
|
1242
1484
|
};
|
|
1243
1485
|
var runTerminalCommand = async (input, projectCwd) => {
|
|
@@ -1251,13 +1493,12 @@ var runTerminalCommand = async (input, projectCwd) => {
|
|
|
1251
1493
|
error: "HARMFUL_COMMAND_DETECTED"
|
|
1252
1494
|
};
|
|
1253
1495
|
}
|
|
1254
|
-
const
|
|
1255
|
-
cwd: projectCwd,
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
shell: true
|
|
1496
|
+
const proc = Bun.spawn(["sh", "-c", input.command], {
|
|
1497
|
+
cwd: projectCwd || process.cwd(),
|
|
1498
|
+
stdout: "ignore",
|
|
1499
|
+
stderr: "ignore"
|
|
1259
1500
|
});
|
|
1260
|
-
|
|
1501
|
+
proc.unref();
|
|
1261
1502
|
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
1262
1503
|
return {
|
|
1263
1504
|
success: true,
|
|
@@ -1267,9 +1508,13 @@ var runTerminalCommand = async (input, projectCwd) => {
|
|
|
1267
1508
|
} else {
|
|
1268
1509
|
const result = await runSecureTerminalCommand(
|
|
1269
1510
|
input.command,
|
|
1270
|
-
3e4
|
|
1511
|
+
3e4,
|
|
1271
1512
|
// 30 second timeout
|
|
1513
|
+
projectCwd
|
|
1272
1514
|
);
|
|
1515
|
+
if (result?.error && !result?.exitCode) {
|
|
1516
|
+
return result;
|
|
1517
|
+
}
|
|
1273
1518
|
const success = result?.exitCode === 0;
|
|
1274
1519
|
return {
|
|
1275
1520
|
success,
|
|
@@ -1288,7 +1533,7 @@ var runTerminalCommand = async (input, projectCwd) => {
|
|
|
1288
1533
|
};
|
|
1289
1534
|
}
|
|
1290
1535
|
};
|
|
1291
|
-
var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local"];
|
|
1536
|
+
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"];
|
|
1292
1537
|
var getContext = (dir, base = dir, allFiles = []) => {
|
|
1293
1538
|
const filePath = readdirSync(dir, { withFileTypes: true });
|
|
1294
1539
|
for (const file of filePath) {
|
|
@@ -1310,28 +1555,34 @@ var IDE_PROJECTS_PATHS = {
|
|
|
1310
1555
|
};
|
|
1311
1556
|
function getWorkspaceStoragePath(ide) {
|
|
1312
1557
|
const platform = os3.platform();
|
|
1313
|
-
const appName = "Cursor" ;
|
|
1558
|
+
const appName = ide === "cursor" ? "Cursor" : "Code";
|
|
1559
|
+
const appNameLower = appName.toLowerCase();
|
|
1314
1560
|
if (platform === "darwin") {
|
|
1315
1561
|
return path10.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
|
|
1316
1562
|
} else if (platform === "win32") {
|
|
1317
1563
|
return path10.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
|
|
1318
1564
|
} else {
|
|
1319
|
-
|
|
1565
|
+
const capitalizedPath = path10.join(HOME, ".config", appName, "User", "workspaceStorage");
|
|
1566
|
+
const lowercasePath = path10.join(HOME, ".config", appNameLower, "User", "workspaceStorage");
|
|
1567
|
+
if (fs6.existsSync(capitalizedPath)) {
|
|
1568
|
+
return capitalizedPath;
|
|
1569
|
+
}
|
|
1570
|
+
return lowercasePath;
|
|
1320
1571
|
}
|
|
1321
1572
|
}
|
|
1322
1573
|
function scanWorkspaceStorage(ide) {
|
|
1323
1574
|
const projects = [];
|
|
1324
|
-
const storagePath = getWorkspaceStoragePath();
|
|
1325
|
-
if (!
|
|
1575
|
+
const storagePath = getWorkspaceStoragePath(ide);
|
|
1576
|
+
if (!fs6.existsSync(storagePath)) {
|
|
1326
1577
|
return projects;
|
|
1327
1578
|
}
|
|
1328
1579
|
try {
|
|
1329
|
-
const workspaces =
|
|
1580
|
+
const workspaces = fs6.readdirSync(storagePath);
|
|
1330
1581
|
for (const workspace of workspaces) {
|
|
1331
1582
|
const workspaceJsonPath = path10.join(storagePath, workspace, "workspace.json");
|
|
1332
|
-
if (
|
|
1583
|
+
if (fs6.existsSync(workspaceJsonPath)) {
|
|
1333
1584
|
try {
|
|
1334
|
-
const content =
|
|
1585
|
+
const content = fs6.readFileSync(workspaceJsonPath, "utf-8");
|
|
1335
1586
|
const data = JSON.parse(content);
|
|
1336
1587
|
if (data.folder && typeof data.folder === "string") {
|
|
1337
1588
|
let projectPath = data.folder;
|
|
@@ -1339,7 +1590,7 @@ function scanWorkspaceStorage(ide) {
|
|
|
1339
1590
|
projectPath = projectPath.replace("file://", "");
|
|
1340
1591
|
projectPath = decodeURIComponent(projectPath);
|
|
1341
1592
|
}
|
|
1342
|
-
if (
|
|
1593
|
+
if (fs6.existsSync(projectPath) && fs6.statSync(projectPath).isDirectory()) {
|
|
1343
1594
|
projects.push({
|
|
1344
1595
|
name: path10.basename(projectPath),
|
|
1345
1596
|
path: projectPath,
|
|
@@ -1363,11 +1614,11 @@ var scanIdeProjects = async () => {
|
|
|
1363
1614
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
1364
1615
|
const addProject = (projectPath, ide) => {
|
|
1365
1616
|
try {
|
|
1366
|
-
const resolvedPath =
|
|
1367
|
-
if (
|
|
1617
|
+
const resolvedPath = fs6.realpathSync(projectPath);
|
|
1618
|
+
if (fs6.existsSync(resolvedPath) && fs6.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
|
|
1368
1619
|
const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
|
|
1369
1620
|
try {
|
|
1370
|
-
return
|
|
1621
|
+
return fs6.realpathSync(ideDir) === resolvedPath;
|
|
1371
1622
|
} catch {
|
|
1372
1623
|
return false;
|
|
1373
1624
|
}
|
|
@@ -1388,32 +1639,36 @@ var scanIdeProjects = async () => {
|
|
|
1388
1639
|
for (const project of cursorProjects) {
|
|
1389
1640
|
addProject(project.path, "cursor");
|
|
1390
1641
|
}
|
|
1642
|
+
const vscodeProjects = scanWorkspaceStorage("vscode");
|
|
1643
|
+
for (const project of vscodeProjects) {
|
|
1644
|
+
addProject(project.path, "vscode");
|
|
1645
|
+
}
|
|
1391
1646
|
for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
|
|
1392
|
-
if (ide === "cursor") continue;
|
|
1393
|
-
if (
|
|
1394
|
-
const projects =
|
|
1647
|
+
if (ide === "cursor" || ide === "vscode") continue;
|
|
1648
|
+
if (fs6.existsSync(dirPath)) {
|
|
1649
|
+
const projects = fs6.readdirSync(dirPath);
|
|
1395
1650
|
projects.forEach((project) => {
|
|
1396
1651
|
const projectPath = path10.join(dirPath, project);
|
|
1397
1652
|
try {
|
|
1398
|
-
const stats =
|
|
1653
|
+
const stats = fs6.lstatSync(projectPath);
|
|
1399
1654
|
let actualPath = null;
|
|
1400
1655
|
if (stats.isSymbolicLink()) {
|
|
1401
|
-
actualPath =
|
|
1656
|
+
actualPath = fs6.realpathSync(projectPath);
|
|
1402
1657
|
} else if (stats.isFile()) {
|
|
1403
1658
|
try {
|
|
1404
|
-
let content =
|
|
1659
|
+
let content = fs6.readFileSync(projectPath, "utf-8").trim();
|
|
1405
1660
|
if (content.startsWith("~/") || content === "~") {
|
|
1406
1661
|
content = content.replace(/^~/, HOME);
|
|
1407
1662
|
}
|
|
1408
1663
|
const resolvedContent = path10.isAbsolute(content) ? content : path10.resolve(path10.dirname(projectPath), content);
|
|
1409
|
-
if (
|
|
1410
|
-
actualPath =
|
|
1664
|
+
if (fs6.existsSync(resolvedContent) && fs6.statSync(resolvedContent).isDirectory()) {
|
|
1665
|
+
actualPath = fs6.realpathSync(resolvedContent);
|
|
1411
1666
|
}
|
|
1412
1667
|
} catch {
|
|
1413
1668
|
return;
|
|
1414
1669
|
}
|
|
1415
1670
|
} else if (stats.isDirectory()) {
|
|
1416
|
-
actualPath =
|
|
1671
|
+
actualPath = fs6.realpathSync(projectPath);
|
|
1417
1672
|
}
|
|
1418
1673
|
if (actualPath) {
|
|
1419
1674
|
addProject(actualPath, ide);
|
|
@@ -1437,7 +1692,7 @@ var Global;
|
|
|
1437
1692
|
})(Global || (Global = {}));
|
|
1438
1693
|
|
|
1439
1694
|
// src/snapshot/snapshot.ts
|
|
1440
|
-
var
|
|
1695
|
+
var execAsync = promisify(exec);
|
|
1441
1696
|
var Snapshot;
|
|
1442
1697
|
((Snapshot2) => {
|
|
1443
1698
|
const log = {
|
|
@@ -1447,7 +1702,7 @@ var Snapshot;
|
|
|
1447
1702
|
};
|
|
1448
1703
|
async function runGit(command, options = {}) {
|
|
1449
1704
|
try {
|
|
1450
|
-
const { stdout, stderr } = await
|
|
1705
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
1451
1706
|
cwd: options.cwd,
|
|
1452
1707
|
env: { ...process.env, ...options.env },
|
|
1453
1708
|
encoding: "utf-8",
|
|
@@ -1471,8 +1726,8 @@ var Snapshot;
|
|
|
1471
1726
|
const worktree = project.cwd;
|
|
1472
1727
|
const git = gitdir(projectId);
|
|
1473
1728
|
try {
|
|
1474
|
-
await
|
|
1475
|
-
const gitExists = await
|
|
1729
|
+
await fs7.mkdir(git, { recursive: true });
|
|
1730
|
+
const gitExists = await fs7.access(path10.join(git, "HEAD")).then(() => true).catch(() => false);
|
|
1476
1731
|
if (!gitExists) {
|
|
1477
1732
|
await runGit(`git init`, {
|
|
1478
1733
|
env: { GIT_DIR: git, GIT_WORK_TREE: worktree }
|
|
@@ -1557,7 +1812,7 @@ var Snapshot;
|
|
|
1557
1812
|
for (const file of newFiles) {
|
|
1558
1813
|
const fullPath = path10.join(worktree, file);
|
|
1559
1814
|
try {
|
|
1560
|
-
await
|
|
1815
|
+
await fs7.unlink(fullPath);
|
|
1561
1816
|
log.info("deleted newly created file", { file: fullPath });
|
|
1562
1817
|
} catch {
|
|
1563
1818
|
}
|
|
@@ -1594,7 +1849,7 @@ var Snapshot;
|
|
|
1594
1849
|
log.info("file existed in snapshot but checkout failed, keeping", { file });
|
|
1595
1850
|
} else {
|
|
1596
1851
|
log.info("file did not exist in snapshot, deleting", { file });
|
|
1597
|
-
await
|
|
1852
|
+
await fs7.unlink(file).catch(() => {
|
|
1598
1853
|
});
|
|
1599
1854
|
}
|
|
1600
1855
|
}
|