amai 0.0.21 → 0.0.22

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/server.cjs CHANGED
@@ -1,1884 +1,34 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
4
  var WebSocket2 = require('ws');
5
- var zod = require('zod');
6
- var path10 = require('path');
7
- var fs8 = require('fs');
8
- var fsp = require('fs/promises');
9
- var os3 = require('os');
10
- var pc3 = require('picocolors');
11
- var hono = require('hono');
12
- var nodeServer = require('@hono/node-server');
13
- var cors = require('hono/cors');
14
- var child_process = require('child_process');
15
- var util = require('util');
16
-
17
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
-
19
- function _interopNamespace(e) {
20
- if (e && e.__esModule) return e;
21
- var n = Object.create(null);
22
- if (e) {
23
- Object.keys(e).forEach(function (k) {
24
- if (k !== 'default') {
25
- var d = Object.getOwnPropertyDescriptor(e, k);
26
- Object.defineProperty(n, k, d.get ? d : {
27
- enumerable: true,
28
- get: function () { return e[k]; }
29
- });
30
- }
31
- });
32
- }
33
- n.default = e;
34
- return Object.freeze(n);
35
- }
36
-
37
- var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
38
- var path10__default = /*#__PURE__*/_interopDefault(path10);
39
- var fs8__default = /*#__PURE__*/_interopDefault(fs8);
40
- var fsp__namespace = /*#__PURE__*/_interopNamespace(fsp);
41
- var os3__default = /*#__PURE__*/_interopDefault(os3);
42
- var pc3__default = /*#__PURE__*/_interopDefault(pc3);
43
-
44
- var MUTATING_TOOLS = /* @__PURE__ */ new Set([
45
- "editFile",
46
- "deleteFile",
47
- "stringReplace",
48
- "bash"
49
- ]);
50
- function isMutatingTool(toolName) {
51
- return MUTATING_TOOLS.has(toolName);
52
- }
53
- function isPathWithinProject(filePath, projectCwd) {
54
- try {
55
- const resolvedCwd = safeRealpath(projectCwd);
56
- const resolved = path10__default.default.resolve(resolvedCwd, filePath);
57
- const resolvedTarget = safeRealpath(resolved);
58
- const rel = path10__default.default.relative(resolvedCwd, resolvedTarget);
59
- if (rel.startsWith("..") || path10__default.default.isAbsolute(rel)) {
60
- return false;
61
- }
62
- return true;
63
- } catch {
64
- return false;
65
- }
66
- }
67
- function safeRealpath(p) {
68
- try {
69
- return fs8__default.default.realpathSync(p);
70
- } catch {
71
- const parent = path10__default.default.dirname(p);
72
- try {
73
- const realParent = fs8__default.default.realpathSync(parent);
74
- return path10__default.default.join(realParent, path10__default.default.basename(p));
75
- } catch {
76
- return path10__default.default.resolve(p);
77
- }
78
- }
79
- }
80
- function validatePath(filePath, projectCwd) {
81
- if (!projectCwd) {
82
- return {
83
- valid: false,
84
- error: "ACCESS_DENIED: No project context provided"
85
- };
86
- }
87
- try {
88
- if (!isPathWithinProject(filePath, projectCwd)) {
89
- return {
90
- valid: false,
91
- error: `ACCESS_DENIED: Path "${filePath}" is outside project directory "${projectCwd}"`
92
- };
93
- }
94
- const resolvedCwd = safeRealpath(projectCwd);
95
- const resolvedPath = path10__default.default.resolve(resolvedCwd, filePath);
96
- return {
97
- valid: true,
98
- resolvedPath
99
- };
100
- } catch (error) {
101
- return {
102
- valid: false,
103
- error: `ACCESS_DENIED: Invalid path "${filePath}"`
104
- };
105
- }
106
- }
107
- function resolveProjectPath(filePath, projectCwd) {
108
- return path10__default.default.resolve(projectCwd, filePath);
109
- }
110
- function requireProjectCwd(toolName, projectCwd) {
111
- if (!projectCwd && isMutatingTool(toolName)) {
112
- return {
113
- allowed: false,
114
- error: `ACCESS_DENIED: Tool "${toolName}" requires a project context (projectCwd) but none was provided`
115
- };
116
- }
117
- return { allowed: true };
118
- }
119
-
120
- // src/tools/read-file.ts
121
- var MAX_FILE_SIZE = 10 * 1024 * 1024;
122
- var MAX_LINES_RETURNED = 2e3;
123
- var MAX_LINE_LENGTH = 2e3;
124
- var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
125
- var MAX_BYTES = 50 * 1024;
126
- var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
127
- ".zip",
128
- ".tar",
129
- ".gz",
130
- ".exe",
131
- ".dll",
132
- ".so",
133
- ".class",
134
- ".jar",
135
- ".war",
136
- ".7z",
137
- ".doc",
138
- ".docx",
139
- ".xls",
140
- ".xlsx",
141
- ".ppt",
142
- ".pptx",
143
- ".odt",
144
- ".ods",
145
- ".odp",
146
- ".bin",
147
- ".dat",
148
- ".obj",
149
- ".o",
150
- ".a",
151
- ".lib",
152
- ".wasm",
153
- ".pyc",
154
- ".pyo",
155
- ".ico",
156
- ".bmp",
157
- ".ttf",
158
- ".woff",
159
- ".woff2",
160
- ".eot",
161
- ".mp3",
162
- ".mp4",
163
- ".avi",
164
- ".mov",
165
- ".flv"
166
- ]);
167
- async function isBinaryFile(filepath, fileSize) {
168
- const ext = path10__default.default.extname(filepath).toLowerCase();
169
- if (BINARY_EXTENSIONS.has(ext)) return true;
170
- if (fileSize === 0) return false;
171
- try {
172
- const fh = await fsp__namespace.open(filepath, "r");
173
- try {
174
- const sampleSize = Math.min(4096, fileSize);
175
- const bytes = Buffer.alloc(sampleSize);
176
- const result = await fh.read(bytes, 0, sampleSize, 0);
177
- if (result.bytesRead === 0) return false;
178
- let nonPrintableCount = 0;
179
- for (let i = 0; i < result.bytesRead; i++) {
180
- if (bytes[i] === 0) return true;
181
- if (bytes[i] < 9 || bytes[i] > 13 && bytes[i] < 32) {
182
- nonPrintableCount++;
183
- }
184
- }
185
- return nonPrintableCount / result.bytesRead > 0.3;
186
- } finally {
187
- await fh.close();
188
- }
189
- } catch {
190
- return false;
191
- }
192
- }
193
- async function findSimilarFiles(filepath) {
194
- const dir = path10__default.default.dirname(filepath);
195
- const base = path10__default.default.basename(filepath).toLowerCase();
196
- try {
197
- const entries = await fsp__namespace.readdir(dir);
198
- return entries.filter(
199
- (entry) => entry.toLowerCase().includes(base) || base.includes(entry.toLowerCase())
200
- ).map((entry) => path10__default.default.join(dir, entry)).slice(0, 3);
201
- } catch {
202
- return [];
203
- }
204
- }
205
- zod.z.object({
206
- relative_file_path: zod.z.string().describe("The path to the file or directory to read."),
207
- should_read_entire_file: zod.z.boolean().describe("Whether to read the entire file.").optional().default(true),
208
- start_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to start reading from (inclusive). Alias: offset."),
209
- end_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to end reading at (inclusive). Alias: offset + limit.")
210
- });
211
- async function readFileContent(absolute_file_path, relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed) {
212
- let stat2;
213
- try {
214
- stat2 = fs8__default.default.statSync(absolute_file_path);
215
- } catch {
216
- const suggestions = await findSimilarFiles(absolute_file_path);
217
- let message = `File not found: ${relative_file_path}`;
218
- if (suggestions.length > 0) {
219
- message += `
220
-
221
- Did you mean one of these?
222
- ${suggestions.join("\n")}`;
223
- }
224
- return {
225
- success: false,
226
- message,
227
- error: "FILE_NOT_FOUND"
228
- };
229
- }
230
- if (stat2.isDirectory()) {
231
- try {
232
- const dirents = await fsp__namespace.readdir(absolute_file_path, { withFileTypes: true });
233
- const entries = await Promise.all(
234
- dirents.map(async (dirent) => {
235
- if (dirent.isDirectory()) return dirent.name + "/";
236
- if (dirent.isSymbolicLink()) {
237
- const target = await fsp__namespace.stat(path10__default.default.join(absolute_file_path, dirent.name)).catch(() => void 0);
238
- if (target?.isDirectory()) return dirent.name + "/";
239
- }
240
- return dirent.name;
241
- })
242
- );
243
- entries.sort((a, b) => a.localeCompare(b));
244
- const truncated = entries.length > MAX_LINES_RETURNED;
245
- const sliced = entries.slice(0, MAX_LINES_RETURNED);
246
- const output = [
247
- `<path>${absolute_file_path}</path>`,
248
- `<type>directory</type>`,
249
- `<entries>`,
250
- sliced.join("\n"),
251
- truncated ? `
252
- (Showing ${sliced.length} of ${entries.length} entries)` : `
253
- (${entries.length} entries)`,
254
- `</entries>`
255
- ].join("\n");
256
- return {
257
- success: true,
258
- message: `Listed directory: ${relative_file_path} (${entries.length} entries)`,
259
- content: output,
260
- totalLines: entries.length,
261
- truncated
262
- };
263
- } catch (err) {
264
- return {
265
- success: false,
266
- message: `Failed to list directory: ${relative_file_path}`,
267
- error: "READ_ERROR"
268
- };
269
- }
270
- }
271
- try {
272
- if (stat2.size > MAX_FILE_SIZE) {
273
- return {
274
- success: false,
275
- message: `File too large (${Math.round(stat2.size / 1024 / 1024)}MB). Maximum is ${MAX_FILE_SIZE / 1024 / 1024}MB. Use line ranges to read portions.`,
276
- error: "FILE_TOO_LARGE"
277
- };
278
- }
279
- const binary = await isBinaryFile(absolute_file_path, stat2.size);
280
- if (binary) {
281
- return {
282
- success: false,
283
- message: `Cannot read binary file: ${relative_file_path}`,
284
- error: "BINARY_FILE"
285
- };
286
- }
287
- const fileContent = await Bun.file(absolute_file_path).text();
288
- const lines = fileContent.split(/\r?\n/);
289
- const totalLines = lines.length;
290
- const start = should_read_entire_file ? 0 : (start_line_one_indexed ?? 1) - 1;
291
- const end = should_read_entire_file ? Math.min(totalLines, MAX_LINES_RETURNED) : Math.min(end_line_one_indexed ?? totalLines, totalLines);
292
- if (start >= totalLines && !(totalLines === 0 && start === 0)) {
293
- return {
294
- success: false,
295
- message: `Offset ${start + 1} is out of range for this file (${totalLines} lines)`,
296
- error: "INVALID_LINE_RANGE"
297
- };
298
- }
299
- const outputLines = [];
300
- let bytes = 0;
301
- let truncatedByBytes = false;
302
- let actualEnd = start;
303
- for (let i = start; i < end; i++) {
304
- let line = lines[i];
305
- if (line.length > MAX_LINE_LENGTH) {
306
- line = line.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX;
307
- }
308
- const numberedLine = `${i + 1}: ${line}`;
309
- const lineBytes = Buffer.byteLength(numberedLine, "utf-8") + (outputLines.length > 0 ? 1 : 0);
310
- if (bytes + lineBytes > MAX_BYTES && outputLines.length > 0) {
311
- truncatedByBytes = true;
312
- break;
313
- }
314
- outputLines.push(numberedLine);
315
- bytes += lineBytes;
316
- actualEnd = i + 1;
317
- }
318
- const hasMoreLines = actualEnd < totalLines;
319
- const truncated = truncatedByBytes || hasMoreLines || should_read_entire_file && totalLines > MAX_LINES_RETURNED;
320
- let output = `<path>${absolute_file_path}</path>
321
- <type>file</type>
322
- <content>
323
- `;
324
- output += outputLines.join("\n");
325
- if (truncatedByBytes) {
326
- output += `
327
-
328
- (Output capped at ${MAX_BYTES / 1024} KB. Showing lines ${start + 1}-${actualEnd}. Use start_line_one_indexed=${actualEnd + 1} to continue.)`;
329
- } else if (hasMoreLines && !should_read_entire_file) {
330
- output += `
331
-
332
- (Showing lines ${start + 1}-${actualEnd} of ${totalLines}. Use start_line_one_indexed=${actualEnd + 1} to continue.)`;
333
- } else {
334
- output += `
335
-
336
- (End of file - total ${totalLines} lines)`;
337
- }
338
- output += "\n</content>";
339
- return {
340
- success: true,
341
- message: truncated ? `Read lines ${start + 1}-${actualEnd} of ${totalLines} from: ${relative_file_path} (truncated)` : `Successfully read file: ${relative_file_path} (${totalLines} lines)`,
342
- content: output,
343
- totalLines,
344
- truncated
345
- };
346
- } catch (error) {
347
- return {
348
- success: false,
349
- message: `Failed to read file: ${relative_file_path}`,
350
- error: "READ_ERROR"
351
- };
352
- }
353
- }
354
- var read_file = async function(input, projectCwd) {
355
- const { relative_file_path, should_read_entire_file = true, start_line_one_indexed, end_line_one_indexed } = input;
356
- try {
357
- if (!relative_file_path) {
358
- return {
359
- success: false,
360
- message: "Missing required parameter: relative_file_path",
361
- error: "MISSING_TARGET_FILE"
362
- };
363
- }
364
- if (!should_read_entire_file) {
365
- if (start_line_one_indexed === void 0 || end_line_one_indexed === void 0) {
366
- return {
367
- success: false,
368
- message: "start_line_one_indexed and end_line_one_indexed are required when should_read_entire_file is false",
369
- error: "MISSING_LINE_RANGE"
370
- };
371
- }
372
- if (!Number.isInteger(start_line_one_indexed) || start_line_one_indexed < 1) {
373
- return {
374
- success: false,
375
- message: "start_line_one_indexed must be a positive integer (1-indexed)",
376
- error: "INVALID_START_LINE"
377
- };
378
- }
379
- if (!Number.isInteger(end_line_one_indexed) || end_line_one_indexed < 1) {
380
- return {
381
- success: false,
382
- message: "end_line_one_indexed must be a positive integer (1-indexed)",
383
- error: "INVALID_END_LINE"
384
- };
385
- }
386
- if (end_line_one_indexed < start_line_one_indexed) {
387
- return {
388
- success: false,
389
- message: "end_line_one_indexed must be greater than or equal to start_line_one_indexed",
390
- error: "INVALID_LINE_RANGE"
391
- };
392
- }
393
- }
394
- let absolute_file_path;
395
- if (projectCwd) {
396
- const validation = validatePath(relative_file_path, projectCwd);
397
- if (!validation.valid) {
398
- return {
399
- success: false,
400
- message: validation.error || "Path validation failed",
401
- error: "ACCESS_DENIED"
402
- };
403
- }
404
- absolute_file_path = validation.resolvedPath;
405
- } else {
406
- absolute_file_path = path10__default.default.resolve(relative_file_path);
407
- }
408
- return await readFileContent(
409
- absolute_file_path,
410
- relative_file_path,
411
- should_read_entire_file,
412
- start_line_one_indexed,
413
- end_line_one_indexed
414
- );
415
- } catch {
416
- return {
417
- success: false,
418
- message: `Failed to read file: ${relative_file_path}`,
419
- error: "READ_ERROR"
420
- };
421
- }
422
- };
423
-
424
- // ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/base.js
425
- var Diff = class {
426
- diff(oldStr, newStr, options = {}) {
427
- let callback;
428
- if (typeof options === "function") {
429
- callback = options;
430
- options = {};
431
- } else if ("callback" in options) {
432
- callback = options.callback;
433
- }
434
- const oldString = this.castInput(oldStr, options);
435
- const newString = this.castInput(newStr, options);
436
- const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
437
- const newTokens = this.removeEmpty(this.tokenize(newString, options));
438
- return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
439
- }
440
- diffWithOptionsObj(oldTokens, newTokens, options, callback) {
441
- var _a;
442
- const done = (value) => {
443
- value = this.postProcess(value, options);
444
- if (callback) {
445
- setTimeout(function() {
446
- callback(value);
447
- }, 0);
448
- return void 0;
449
- } else {
450
- return value;
451
- }
452
- };
453
- const newLen = newTokens.length, oldLen = oldTokens.length;
454
- let editLength = 1;
455
- let maxEditLength = newLen + oldLen;
456
- if (options.maxEditLength != null) {
457
- maxEditLength = Math.min(maxEditLength, options.maxEditLength);
458
- }
459
- const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
460
- const abortAfterTimestamp = Date.now() + maxExecutionTime;
461
- const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
462
- let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
463
- if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
464
- return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
465
- }
466
- let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
467
- const execEditLength = () => {
468
- for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
469
- let basePath;
470
- const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
471
- if (removePath) {
472
- bestPath[diagonalPath - 1] = void 0;
473
- }
474
- let canAdd = false;
475
- if (addPath) {
476
- const addPathNewPos = addPath.oldPos - diagonalPath;
477
- canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
478
- }
479
- const canRemove = removePath && removePath.oldPos + 1 < oldLen;
480
- if (!canAdd && !canRemove) {
481
- bestPath[diagonalPath] = void 0;
482
- continue;
483
- }
484
- if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
485
- basePath = this.addToPath(addPath, true, false, 0, options);
486
- } else {
487
- basePath = this.addToPath(removePath, false, true, 1, options);
488
- }
489
- newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
490
- if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
491
- return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
492
- } else {
493
- bestPath[diagonalPath] = basePath;
494
- if (basePath.oldPos + 1 >= oldLen) {
495
- maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
496
- }
497
- if (newPos + 1 >= newLen) {
498
- minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
499
- }
500
- }
501
- }
502
- editLength++;
503
- };
504
- if (callback) {
505
- (function exec2() {
506
- setTimeout(function() {
507
- if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
508
- return callback(void 0);
509
- }
510
- if (!execEditLength()) {
511
- exec2();
512
- }
513
- }, 0);
514
- })();
515
- } else {
516
- while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
517
- const ret = execEditLength();
518
- if (ret) {
519
- return ret;
520
- }
521
- }
522
- }
523
- }
524
- addToPath(path14, added, removed, oldPosInc, options) {
525
- const last = path14.lastComponent;
526
- if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
527
- return {
528
- oldPos: path14.oldPos + oldPosInc,
529
- lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
530
- };
531
- } else {
532
- return {
533
- oldPos: path14.oldPos + oldPosInc,
534
- lastComponent: { count: 1, added, removed, previousComponent: last }
535
- };
536
- }
537
- }
538
- extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
539
- const newLen = newTokens.length, oldLen = oldTokens.length;
540
- let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
541
- while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
542
- newPos++;
543
- oldPos++;
544
- commonCount++;
545
- if (options.oneChangePerToken) {
546
- basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
547
- }
548
- }
549
- if (commonCount && !options.oneChangePerToken) {
550
- basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
551
- }
552
- basePath.oldPos = oldPos;
553
- return newPos;
554
- }
555
- equals(left, right, options) {
556
- if (options.comparator) {
557
- return options.comparator(left, right);
558
- } else {
559
- return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
560
- }
561
- }
562
- removeEmpty(array) {
563
- const ret = [];
564
- for (let i = 0; i < array.length; i++) {
565
- if (array[i]) {
566
- ret.push(array[i]);
567
- }
568
- }
569
- return ret;
570
- }
571
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
572
- castInput(value, options) {
573
- return value;
574
- }
575
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
576
- tokenize(value, options) {
577
- return Array.from(value);
578
- }
579
- join(chars) {
580
- return chars.join("");
581
- }
582
- postProcess(changeObjects, options) {
583
- return changeObjects;
584
- }
585
- get useLongestToken() {
586
- return false;
587
- }
588
- buildValues(lastComponent, newTokens, oldTokens) {
589
- const components = [];
590
- let nextComponent;
591
- while (lastComponent) {
592
- components.push(lastComponent);
593
- nextComponent = lastComponent.previousComponent;
594
- delete lastComponent.previousComponent;
595
- lastComponent = nextComponent;
596
- }
597
- components.reverse();
598
- const componentLen = components.length;
599
- let componentPos = 0, newPos = 0, oldPos = 0;
600
- for (; componentPos < componentLen; componentPos++) {
601
- const component = components[componentPos];
602
- if (!component.removed) {
603
- if (!component.added && this.useLongestToken) {
604
- let value = newTokens.slice(newPos, newPos + component.count);
605
- value = value.map(function(value2, i) {
606
- const oldValue = oldTokens[oldPos + i];
607
- return oldValue.length > value2.length ? oldValue : value2;
608
- });
609
- component.value = this.join(value);
610
- } else {
611
- component.value = this.join(newTokens.slice(newPos, newPos + component.count));
612
- }
613
- newPos += component.count;
614
- if (!component.added) {
615
- oldPos += component.count;
616
- }
617
- } else {
618
- component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
619
- oldPos += component.count;
620
- }
621
- }
622
- return components;
623
- }
624
- };
625
-
626
- // ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/line.js
627
- var LineDiff = class extends Diff {
628
- constructor() {
629
- super(...arguments);
630
- this.tokenize = tokenize;
631
- }
632
- equals(left, right, options) {
633
- if (options.ignoreWhitespace) {
634
- if (!options.newlineIsToken || !left.includes("\n")) {
635
- left = left.trim();
636
- }
637
- if (!options.newlineIsToken || !right.includes("\n")) {
638
- right = right.trim();
639
- }
640
- } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
641
- if (left.endsWith("\n")) {
642
- left = left.slice(0, -1);
643
- }
644
- if (right.endsWith("\n")) {
645
- right = right.slice(0, -1);
646
- }
647
- }
648
- return super.equals(left, right, options);
649
- }
650
- };
651
- var lineDiff = new LineDiff();
652
- function diffLines(oldStr, newStr, options) {
653
- return lineDiff.diff(oldStr, newStr, options);
654
- }
655
- function tokenize(value, options) {
656
- if (options.stripTrailingCr) {
657
- value = value.replace(/\r\n/g, "\n");
658
- }
659
- const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
660
- if (!linesAndNewlines[linesAndNewlines.length - 1]) {
661
- linesAndNewlines.pop();
662
- }
663
- for (let i = 0; i < linesAndNewlines.length; i++) {
664
- const line = linesAndNewlines[i];
665
- if (i % 2 && !options.newlineIsToken) {
666
- retLines[retLines.length - 1] += line;
667
- } else {
668
- retLines.push(line);
669
- }
670
- }
671
- return retLines;
672
- }
673
-
674
- // src/lib/diff.ts
675
- function calculateDiffStats(oldContent, newContent) {
676
- const changes = diffLines(oldContent, newContent);
677
- let linesAdded = 0;
678
- let linesRemoved = 0;
679
- for (const change of changes) {
680
- if (change.added) {
681
- linesAdded += change.count || 0;
682
- } else if (change.removed) {
683
- linesRemoved += change.count || 0;
684
- }
685
- }
686
- return { linesAdded, linesRemoved };
687
- }
688
-
689
- // src/tools/stringReplace.ts
690
- zod.z.object({
691
- file_path: zod.z.string().describe("The path to the file you want to search and replace in. You can use either a relative path in the workspace or an absolute path. If an absolute path is provided, it will be preserved as is"),
692
- new_string: zod.z.string().describe("The edited text to replace the old_string (must be different from the old_string)"),
693
- old_string: zod.z.string().describe("The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)"),
694
- replaceAll: zod.z.boolean().optional().describe("Replace all occurrences of old_string (default false)")
695
- });
696
- function levenshtein(a, b) {
697
- if (a === "" || b === "") return Math.max(a.length, b.length);
698
- const matrix = Array.from(
699
- { length: a.length + 1 },
700
- (_, i) => Array.from({ length: b.length + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
701
- );
702
- for (let i = 1; i <= a.length; i++) {
703
- for (let j = 1; j <= b.length; j++) {
704
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
705
- matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
706
- }
707
- }
708
- return matrix[a.length][b.length];
709
- }
710
- var SINGLE_CANDIDATE_SIMILARITY_THRESHOLD = 0;
711
- var MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD = 0.3;
712
- var SimpleReplacer = function* (_content, find) {
713
- yield find;
714
- };
715
- var LineTrimmedReplacer = function* (content, find) {
716
- const originalLines = content.split("\n");
717
- const searchLines = find.split("\n");
718
- if (searchLines[searchLines.length - 1] === "") searchLines.pop();
719
- for (let i = 0; i <= originalLines.length - searchLines.length; i++) {
720
- let matches = true;
721
- for (let j = 0; j < searchLines.length; j++) {
722
- if (originalLines[i + j].trim() !== searchLines[j].trim()) {
723
- matches = false;
724
- break;
725
- }
726
- }
727
- if (matches) {
728
- let matchStartIndex = 0;
729
- for (let k = 0; k < i; k++) matchStartIndex += originalLines[k].length + 1;
730
- let matchEndIndex = matchStartIndex;
731
- for (let k = 0; k < searchLines.length; k++) {
732
- matchEndIndex += originalLines[i + k].length;
733
- if (k < searchLines.length - 1) matchEndIndex += 1;
734
- }
735
- yield content.substring(matchStartIndex, matchEndIndex);
736
- }
737
- }
738
- };
739
- var BlockAnchorReplacer = function* (content, find) {
740
- const originalLines = content.split("\n");
741
- const searchLines = find.split("\n");
742
- if (searchLines.length < 3) return;
743
- if (searchLines[searchLines.length - 1] === "") searchLines.pop();
744
- const firstLineSearch = searchLines[0].trim();
745
- const lastLineSearch = searchLines[searchLines.length - 1].trim();
746
- const searchBlockSize = searchLines.length;
747
- const candidates = [];
748
- for (let i = 0; i < originalLines.length; i++) {
749
- if (originalLines[i].trim() !== firstLineSearch) continue;
750
- for (let j = i + 2; j < originalLines.length; j++) {
751
- if (originalLines[j].trim() === lastLineSearch) {
752
- candidates.push({ startLine: i, endLine: j });
753
- break;
754
- }
755
- }
756
- }
757
- if (candidates.length === 0) return;
758
- const computeSimilarity = (startLine, endLine) => {
759
- const actualBlockSize = endLine - startLine + 1;
760
- const linesToCheck = Math.min(searchBlockSize - 2, actualBlockSize - 2);
761
- if (linesToCheck <= 0) return 1;
762
- let similarity = 0;
763
- for (let j = 1; j < searchBlockSize - 1 && j < actualBlockSize - 1; j++) {
764
- const originalLine = originalLines[startLine + j].trim();
765
- const searchLine = searchLines[j].trim();
766
- const maxLen = Math.max(originalLine.length, searchLine.length);
767
- if (maxLen === 0) continue;
768
- const distance = levenshtein(originalLine, searchLine);
769
- similarity += (1 - distance / maxLen) / linesToCheck;
770
- }
771
- return similarity;
772
- };
773
- const extractBlock = (startLine, endLine) => {
774
- let matchStartIndex = 0;
775
- for (let k = 0; k < startLine; k++) matchStartIndex += originalLines[k].length + 1;
776
- let matchEndIndex = matchStartIndex;
777
- for (let k = startLine; k <= endLine; k++) {
778
- matchEndIndex += originalLines[k].length;
779
- if (k < endLine) matchEndIndex += 1;
780
- }
781
- return content.substring(matchStartIndex, matchEndIndex);
782
- };
783
- if (candidates.length === 1) {
784
- const { startLine, endLine } = candidates[0];
785
- if (computeSimilarity(startLine, endLine) >= SINGLE_CANDIDATE_SIMILARITY_THRESHOLD) {
786
- yield extractBlock(startLine, endLine);
787
- }
788
- return;
789
- }
790
- let bestMatch = null;
791
- let maxSimilarity = -1;
792
- for (const candidate of candidates) {
793
- const similarity = computeSimilarity(candidate.startLine, candidate.endLine);
794
- if (similarity > maxSimilarity) {
795
- maxSimilarity = similarity;
796
- bestMatch = candidate;
797
- }
798
- }
799
- if (maxSimilarity >= MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD && bestMatch) {
800
- yield extractBlock(bestMatch.startLine, bestMatch.endLine);
801
- }
802
- };
803
- var WhitespaceNormalizedReplacer = function* (content, find) {
804
- const normalizeWhitespace = (text) => text.replace(/\s+/g, " ").trim();
805
- const normalizedFind = normalizeWhitespace(find);
806
- const lines = content.split("\n");
807
- for (let i = 0; i < lines.length; i++) {
808
- const line = lines[i];
809
- if (normalizeWhitespace(line) === normalizedFind) {
810
- yield line;
811
- } else {
812
- const normalizedLine = normalizeWhitespace(line);
813
- if (normalizedLine.includes(normalizedFind)) {
814
- const words = find.trim().split(/\s+/);
815
- if (words.length > 0) {
816
- const pattern = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+");
817
- try {
818
- const regex = new RegExp(pattern);
819
- const match = line.match(regex);
820
- if (match) yield match[0];
821
- } catch {
822
- }
823
- }
824
- }
825
- }
826
- }
827
- const findLines = find.split("\n");
828
- if (findLines.length > 1) {
829
- for (let i = 0; i <= lines.length - findLines.length; i++) {
830
- const block = lines.slice(i, i + findLines.length);
831
- if (normalizeWhitespace(block.join("\n")) === normalizedFind) {
832
- yield block.join("\n");
833
- }
834
- }
835
- }
836
- };
837
- var IndentationFlexibleReplacer = function* (content, find) {
838
- const removeIndentation = (text) => {
839
- const lines = text.split("\n");
840
- const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
841
- if (nonEmptyLines.length === 0) return text;
842
- const minIndent = Math.min(
843
- ...nonEmptyLines.map((line) => {
844
- const match = line.match(/^(\s*)/);
845
- return match ? match[1].length : 0;
846
- })
847
- );
848
- return lines.map((line) => line.trim().length === 0 ? line : line.slice(minIndent)).join("\n");
849
- };
850
- const normalizedFind = removeIndentation(find);
851
- const contentLines = content.split("\n");
852
- const findLines = find.split("\n");
853
- for (let i = 0; i <= contentLines.length - findLines.length; i++) {
854
- const block = contentLines.slice(i, i + findLines.length).join("\n");
855
- if (removeIndentation(block) === normalizedFind) {
856
- yield block;
857
- }
858
- }
859
- };
860
- var EscapeNormalizedReplacer = function* (content, find) {
861
- const unescapeString = (str) => {
862
- return str.replace(/\\(n|t|r|'|"|`|\\|\n|\$)/g, (match, capturedChar) => {
863
- switch (capturedChar) {
864
- case "n":
865
- return "\n";
866
- case "t":
867
- return " ";
868
- case "r":
869
- return "\r";
870
- case "'":
871
- return "'";
872
- case '"':
873
- return '"';
874
- case "`":
875
- return "`";
876
- case "\\":
877
- return "\\";
878
- case "\n":
879
- return "\n";
880
- case "$":
881
- return "$";
882
- default:
883
- return match;
884
- }
885
- });
886
- };
887
- const unescapedFind = unescapeString(find);
888
- if (content.includes(unescapedFind)) {
889
- yield unescapedFind;
890
- }
891
- const lines = content.split("\n");
892
- const findLines = unescapedFind.split("\n");
893
- for (let i = 0; i <= lines.length - findLines.length; i++) {
894
- const block = lines.slice(i, i + findLines.length).join("\n");
895
- if (unescapeString(block) === unescapedFind) {
896
- yield block;
897
- }
898
- }
899
- };
900
- var TrimmedBoundaryReplacer = function* (content, find) {
901
- const trimmedFind = find.trim();
902
- if (trimmedFind === find) return;
903
- if (content.includes(trimmedFind)) {
904
- yield trimmedFind;
905
- }
906
- const lines = content.split("\n");
907
- const findLines = find.split("\n");
908
- for (let i = 0; i <= lines.length - findLines.length; i++) {
909
- const block = lines.slice(i, i + findLines.length).join("\n");
910
- if (block.trim() === trimmedFind) {
911
- yield block;
912
- }
913
- }
914
- };
915
- var ContextAwareReplacer = function* (content, find) {
916
- const findLines = find.split("\n");
917
- if (findLines.length < 3) return;
918
- if (findLines[findLines.length - 1] === "") findLines.pop();
919
- const contentLines = content.split("\n");
920
- const firstLine = findLines[0].trim();
921
- const lastLine = findLines[findLines.length - 1].trim();
922
- for (let i = 0; i < contentLines.length; i++) {
923
- if (contentLines[i].trim() !== firstLine) continue;
924
- for (let j = i + 2; j < contentLines.length; j++) {
925
- if (contentLines[j].trim() === lastLine) {
926
- const blockLines = contentLines.slice(i, j + 1);
927
- if (blockLines.length === findLines.length) {
928
- let matchingLines = 0;
929
- let totalNonEmptyLines = 0;
930
- for (let k = 1; k < blockLines.length - 1; k++) {
931
- const blockLine = blockLines[k].trim();
932
- const findLine = findLines[k].trim();
933
- if (blockLine.length > 0 || findLine.length > 0) {
934
- totalNonEmptyLines++;
935
- if (blockLine === findLine) matchingLines++;
936
- }
937
- }
938
- if (totalNonEmptyLines === 0 || matchingLines / totalNonEmptyLines >= 0.5) {
939
- yield blockLines.join("\n");
940
- break;
941
- }
942
- }
943
- break;
944
- }
945
- }
946
- }
947
- };
948
- var MultiOccurrenceReplacer = function* (content, find) {
949
- let startIndex = 0;
950
- while (true) {
951
- const index = content.indexOf(find, startIndex);
952
- if (index === -1) break;
953
- yield find;
954
- startIndex = index + find.length;
955
- }
956
- };
957
- var REPLACERS = [
958
- SimpleReplacer,
959
- LineTrimmedReplacer,
960
- BlockAnchorReplacer,
961
- WhitespaceNormalizedReplacer,
962
- IndentationFlexibleReplacer,
963
- EscapeNormalizedReplacer,
964
- TrimmedBoundaryReplacer,
965
- ContextAwareReplacer,
966
- MultiOccurrenceReplacer
967
- ];
968
- function smartReplace(content, oldString, newString, replaceAll = false) {
969
- if (oldString === newString) {
970
- throw new Error("No changes to apply: oldString and newString are identical.");
971
- }
972
- let notFound = true;
973
- for (const replacer of REPLACERS) {
974
- for (const search of replacer(content, oldString)) {
975
- const index = content.indexOf(search);
976
- if (index === -1) continue;
977
- notFound = false;
978
- if (replaceAll) {
979
- return content.replaceAll(search, newString);
980
- }
981
- const lastIndex = content.lastIndexOf(search);
982
- if (index !== lastIndex) continue;
983
- return content.substring(0, index) + newString + content.substring(index + search.length);
984
- }
985
- }
986
- if (notFound) {
987
- throw new Error(
988
- "oldString not found in content. It must match the file contents exactly, including whitespace, indentation, and line endings."
989
- );
990
- }
991
- throw new Error(
992
- "Found multiple matches for oldString. Provide more surrounding lines in oldString to identify the correct match."
993
- );
994
- }
995
- var apply_patch = async function(input, projectCwd) {
996
- const { file_path, new_string, old_string, replaceAll: shouldReplaceAll = false } = input;
997
- try {
998
- if (!file_path) {
999
- return {
1000
- success: false,
1001
- message: "Missing required parameter: file_path",
1002
- error: "MISSING_FILE_PATH"
1003
- };
1004
- }
1005
- if (old_string === void 0 || old_string === null) {
1006
- return {
1007
- success: false,
1008
- message: "Missing required parameter: old_string",
1009
- error: "MISSING_OLD_STRING"
1010
- };
1011
- }
1012
- if (new_string === void 0 || new_string === null) {
1013
- return {
1014
- success: false,
1015
- message: "Missing required parameter: new_string",
1016
- error: "MISSING_NEW_STRING"
1017
- };
1018
- }
1019
- if (old_string === new_string) {
1020
- return {
1021
- success: false,
1022
- message: "old_string and new_string must be different",
1023
- error: "STRINGS_IDENTICAL"
1024
- };
1025
- }
1026
- if (projectCwd) {
1027
- const validation = validatePath(file_path, projectCwd);
1028
- if (!validation.valid) {
1029
- return {
1030
- success: false,
1031
- message: validation.error || "Path validation failed",
1032
- error: "ACCESS_DENIED"
1033
- };
1034
- }
1035
- }
1036
- const basePath = projectCwd || process.cwd();
1037
- const absolute_file_path = resolveProjectPath(file_path, basePath);
1038
- const file = Bun.file(absolute_file_path);
1039
- const exists = await file.exists();
1040
- if (!exists) {
1041
- if (old_string === "") {
1042
- const { mkdir: mkdir2 } = await import('fs/promises');
1043
- const path14 = await import('path');
1044
- await mkdir2(path14.dirname(absolute_file_path), { recursive: true });
1045
- await Bun.write(absolute_file_path, new_string);
1046
- const diffStats = calculateDiffStats("", new_string);
1047
- return {
1048
- success: true,
1049
- isNewFile: true,
1050
- old_string: "",
1051
- new_string,
1052
- linesAdded: diffStats.linesAdded,
1053
- linesRemoved: diffStats.linesRemoved,
1054
- message: `Created new file: ${file_path}`
1055
- };
1056
- }
1057
- return {
1058
- success: false,
1059
- message: `File not found: ${file_path}`,
1060
- error: "FILE_NOT_FOUND"
1061
- };
1062
- }
1063
- let fileContent;
1064
- try {
1065
- fileContent = await file.text();
1066
- } catch (error) {
1067
- return {
1068
- success: false,
1069
- message: `Failed to read file: ${file_path}`,
1070
- error: "READ_ERROR"
1071
- };
1072
- }
1073
- let newContent;
1074
- try {
1075
- newContent = smartReplace(fileContent, old_string, new_string, shouldReplaceAll);
1076
- } catch (err) {
1077
- if (err.message.includes("not found")) {
1078
- return {
1079
- success: false,
1080
- message: `old_string not found in file: ${file_path}. Ensure it matches the file contents exactly, including whitespace and indentation.`,
1081
- error: "STRING_NOT_FOUND"
1082
- };
1083
- }
1084
- if (err.message.includes("multiple matches")) {
1085
- const occurrences = fileContent.split(old_string).length - 1;
1086
- return {
1087
- success: false,
1088
- message: `old_string appears ${occurrences > 1 ? occurrences + " times" : "multiple times (via fuzzy match)"} in the file. Provide more surrounding context to make it unique, or set replaceAll to true.`,
1089
- error: "STRING_NOT_UNIQUE"
1090
- };
1091
- }
1092
- return {
1093
- success: false,
1094
- message: err.message,
1095
- error: "REPLACE_ERROR"
1096
- };
1097
- }
1098
- try {
1099
- await Bun.write(absolute_file_path, newContent);
1100
- const diffStats = calculateDiffStats(fileContent, newContent);
1101
- return {
1102
- success: true,
1103
- old_string,
1104
- new_string,
1105
- linesAdded: diffStats.linesAdded,
1106
- linesRemoved: diffStats.linesRemoved,
1107
- message: `Successfully replaced string in file: ${file_path}`
1108
- };
1109
- } catch (error) {
1110
- return {
1111
- success: false,
1112
- message: `Failed to write to file: ${file_path}`,
1113
- error: "WRITE_ERROR"
1114
- };
1115
- }
1116
- } catch (error) {
1117
- return {
1118
- success: false,
1119
- message: `Unexpected error: ${error.message}`,
1120
- error: "UNEXPECTED_ERROR"
1121
- };
1122
- }
1123
- };
1124
- var DEFAULT_SERVER_URL = "wss://bridge.ama.shujan.xyz";
1125
- var AMA_DIR = path10__default.default.join(os3__default.default.homedir(), ".amai");
1126
- path10__default.default.join(AMA_DIR, "code");
1127
- path10__default.default.join(AMA_DIR, "storage");
1128
- zod.z.object({
1129
- target_file: zod.z.string().describe("The relative path to the file to modify. The tool will create any directories in the path that don't exist"),
1130
- content: zod.z.string().describe("The full content to write to the file"),
1131
- providedNewFile: zod.z.boolean().describe("Whether this is a new file (true) or an edit to an existing file (false). Auto-detected if omitted.").optional()
1132
- });
1133
- var editFiles = async function(input, projectCwd) {
1134
- const { target_file, content, providedNewFile } = input;
1135
- if (!target_file) {
1136
- return {
1137
- success: false,
1138
- error: "Missing required parameter: target_file",
1139
- message: "target_file is required"
1140
- };
1141
- }
1142
- try {
1143
- if (projectCwd) {
1144
- const validation = validatePath(target_file, projectCwd);
1145
- if (!validation.valid) {
1146
- return {
1147
- success: false,
1148
- error: validation.error || "Path validation failed",
1149
- message: `Failed to edit file: ${target_file}`
1150
- };
1151
- }
1152
- }
1153
- const basePath = projectCwd || process.cwd();
1154
- const filePath = resolveProjectPath(target_file, basePath);
1155
- const dirPath = path10__default.default.dirname(filePath);
1156
- await fsp.mkdir(dirPath, { recursive: true });
1157
- let isNewFile = providedNewFile;
1158
- let existingContent = "";
1159
- const file = Bun.file(filePath);
1160
- if (isNewFile === void 0) {
1161
- const exists = await file.exists();
1162
- if (exists) {
1163
- existingContent = await file.text();
1164
- isNewFile = false;
1165
- } else {
1166
- isNewFile = true;
1167
- }
1168
- } else if (!isNewFile) {
1169
- const exists = await file.exists();
1170
- if (exists) {
1171
- existingContent = await file.text();
1172
- } else {
1173
- isNewFile = true;
1174
- }
1175
- }
1176
- if (!isNewFile && existingContent === content) {
1177
- return {
1178
- success: true,
1179
- isNewFile: false,
1180
- message: `No changes needed: ${target_file} (content identical)`,
1181
- linesAdded: 0,
1182
- linesRemoved: 0
1183
- };
1184
- }
1185
- await Bun.write(filePath, content);
1186
- const diffStats = calculateDiffStats(existingContent, content);
1187
- if (isNewFile) {
1188
- return {
1189
- success: true,
1190
- isNewFile: true,
1191
- old_string: "",
1192
- new_string: content,
1193
- message: `Created new file: ${target_file} (+${diffStats.linesAdded} lines)`,
1194
- linesAdded: diffStats.linesAdded,
1195
- linesRemoved: diffStats.linesRemoved
1196
- };
1197
- } else {
1198
- return {
1199
- success: true,
1200
- isNewFile: false,
1201
- old_string: existingContent,
1202
- new_string: content,
1203
- message: `Modified file: ${target_file} (+${diffStats.linesAdded} -${diffStats.linesRemoved} lines)`,
1204
- linesAdded: diffStats.linesAdded,
1205
- linesRemoved: diffStats.linesRemoved
1206
- };
1207
- }
1208
- } catch (error) {
1209
- return {
1210
- success: false,
1211
- error: error instanceof Error ? error.message : "Unknown error",
1212
- message: `Failed to edit file: ${target_file}`
1213
- };
1214
- }
1215
- };
1216
- zod.z.object({
1217
- path: zod.z.string().describe("Relative file path to delete")
1218
- });
1219
- var deleteFile = async function(input, projectCwd) {
1220
- const { path: realPath } = input;
1221
- if (!realPath) {
1222
- return {
1223
- success: false,
1224
- message: "Missing required parameter: path",
1225
- error: "MISSING_PATH"
1226
- };
1227
- }
1228
- if (projectCwd) {
1229
- const validation = validatePath(realPath, projectCwd);
1230
- if (!validation.valid) {
1231
- return {
1232
- success: false,
1233
- message: validation.error || "Path validation failed",
1234
- error: "ACCESS_DENIED"
1235
- };
1236
- }
1237
- }
1238
- try {
1239
- const basePath = projectCwd || process.cwd();
1240
- const absolute_file_path = resolveProjectPath(realPath, basePath);
1241
- if (!absolute_file_path) {
1242
- return {
1243
- success: false,
1244
- message: "Invalid file path",
1245
- error: "INVALID_FILE_PATH"
1246
- };
1247
- }
1248
- const file = Bun.file(absolute_file_path);
1249
- const exists = await file.exists();
1250
- if (!exists) {
1251
- return {
1252
- success: false,
1253
- message: `File not found: ${realPath}`,
1254
- error: "FILE_NOT_FOUND"
1255
- };
1256
- }
1257
- let originalContent;
1258
- try {
1259
- originalContent = await file.text();
1260
- } catch {
1261
- return {
1262
- success: false,
1263
- message: `Failed to read file before deletion: ${realPath}`,
1264
- error: "READ_ERROR"
1265
- };
1266
- }
1267
- try {
1268
- await fsp.unlink(absolute_file_path);
1269
- } catch {
1270
- return {
1271
- success: false,
1272
- message: `Failed to delete file: ${realPath}`,
1273
- error: "DELETE_ERROR"
1274
- };
1275
- }
1276
- return {
1277
- success: true,
1278
- message: `Successfully deleted file: ${realPath}`,
1279
- content: originalContent
1280
- };
1281
- } catch (error) {
1282
- return {
1283
- success: false,
1284
- message: `Failed to delete file: ${realPath}`,
1285
- error: "DELETE_ERROR"
1286
- };
1287
- }
1288
- };
1289
- var GREP_LIMITS = {
1290
- DEFAULT_MAX_MATCHES: 200,
1291
- MAX_LINE_LENGTH: 2e3,
1292
- // aligned with OpenCode's 2000-char truncation
1293
- MAX_TOTAL_OUTPUT_SIZE: 1 * 1024 * 1024,
1294
- EXECUTION_TIMEOUT_MS: 15e3,
1295
- TRUNCATION_MESSAGE: "\n[Results truncated due to size limits. Use more specific patterns or file filters to narrow your search.]"
1296
- };
1297
- zod.z.object({
1298
- query: zod.z.string().describe("The regex pattern to search for"),
1299
- options: zod.z.object({
1300
- includePattern: zod.z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts", "*.{ts,tsx}")'),
1301
- excludePattern: zod.z.string().optional().describe("Glob pattern for files to exclude"),
1302
- caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive"),
1303
- path: zod.z.string().optional().describe("Subdirectory to search in"),
1304
- sortByMtime: zod.z.boolean().optional().describe("Sort results by file modification time (default: true)")
1305
- }).optional()
1306
- });
1307
- var _cachedRgPath = null;
1308
- async function getRipgrepPath() {
1309
- if (_cachedRgPath) return _cachedRgPath;
1310
- const paths = [
1311
- "/opt/homebrew/bin/rg",
1312
- "/usr/local/bin/rg",
1313
- "/usr/bin/rg"
1314
- ];
1315
- for (const rgPath of paths) {
1316
- if (fs8__default.default.existsSync(rgPath)) {
1317
- _cachedRgPath = rgPath;
1318
- return rgPath;
1319
- }
1320
- }
1321
- _cachedRgPath = "rg";
1322
- return "rg";
1323
- }
1324
- getRipgrepPath();
1325
- async function getMtimesBatched(files) {
1326
- const mtimeMap = /* @__PURE__ */ new Map();
1327
- const BATCH_SIZE = 50;
1328
- for (let i = 0; i < files.length; i += BATCH_SIZE) {
1329
- const batch = files.slice(i, i + BATCH_SIZE);
1330
- const results = await Promise.all(
1331
- batch.map(async (filePath) => {
1332
- const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
1333
- return { path: filePath, mtime };
1334
- })
1335
- );
1336
- results.forEach(({ path: path14, mtime }) => mtimeMap.set(path14, mtime));
1337
- }
1338
- return mtimeMap;
1339
- }
1340
- var grepTool = async function(input, projectCwd) {
1341
- const { query, options } = input;
1342
- if (!query || query.trim() === "") {
1343
- return {
1344
- success: false,
1345
- message: "Missing required parameter: query",
1346
- error: "MISSING_QUERY"
1347
- };
1348
- }
1349
- try {
1350
- const { includePattern, excludePattern, caseSensitive, path: subPath, sortByMtime = true } = options || {};
1351
- let searchDir = projectCwd || process.cwd();
1352
- if (subPath) {
1353
- searchDir = path10__default.default.isAbsolute(subPath) ? subPath : path10__default.default.resolve(searchDir, subPath);
1354
- if (projectCwd) {
1355
- const validation = validatePath(subPath, projectCwd);
1356
- if (!validation.valid) {
1357
- return {
1358
- success: false,
1359
- message: validation.error || "Path validation failed",
1360
- error: "ACCESS_DENIED"
1361
- };
1362
- }
1363
- }
1364
- }
1365
- if (!fs8__default.default.existsSync(searchDir)) {
1366
- return {
1367
- success: false,
1368
- message: `Directory not found: ${searchDir}`,
1369
- error: "DIR_NOT_FOUND"
1370
- };
1371
- }
1372
- const rgPath = await getRipgrepPath();
1373
- const args = [
1374
- "-nH",
1375
- // line numbers + filename (compact form, matching OpenCode)
1376
- "--hidden",
1377
- // search hidden files (aligned with OpenCode)
1378
- "--no-messages",
1379
- // suppress error messages for unreadable files
1380
- "--color=never",
1381
- "--max-count=100",
1382
- "--max-columns=2000"
1383
- ];
1384
- if (!caseSensitive) {
1385
- args.push("-i");
1386
- }
1387
- if (includePattern) {
1388
- args.push("--glob", includePattern);
1389
- }
1390
- if (excludePattern) {
1391
- args.push("--glob", `!${excludePattern}`);
1392
- }
1393
- args.push("--glob", "!node_modules/**");
1394
- args.push("--glob", "!.git/**");
1395
- args.push("--glob", "!dist/**");
1396
- args.push("--glob", "!build/**");
1397
- args.push("--glob", "!*.min.js");
1398
- args.push("--glob", "!*.min.css");
1399
- args.push("--glob", "!package-lock.json");
1400
- args.push("--glob", "!yarn.lock");
1401
- args.push("--glob", "!bun.lockb");
1402
- args.push("--glob", "!pnpm-lock.yaml");
1403
- args.push("--regexp", query);
1404
- args.push(searchDir);
1405
- const proc = Bun.spawn([rgPath, ...args], {
1406
- stdout: "pipe",
1407
- stderr: "pipe"
1408
- });
1409
- let timedOut = false;
1410
- const timeoutId = setTimeout(() => {
1411
- timedOut = true;
1412
- proc.kill();
1413
- }, GREP_LIMITS.EXECUTION_TIMEOUT_MS);
1414
- const stdout = await new Response(proc.stdout).text();
1415
- const stderr = await new Response(proc.stderr).text();
1416
- const exitCode = await proc.exited;
1417
- clearTimeout(timeoutId);
1418
- if (timedOut) {
1419
- return {
1420
- success: false,
1421
- message: `Search timed out after ${GREP_LIMITS.EXECUTION_TIMEOUT_MS}ms. Use more specific patterns.`,
1422
- error: "GREP_TIMEOUT"
1423
- };
1424
- }
1425
- if (exitCode === 1) {
1426
- return {
1427
- success: true,
1428
- matches: [],
1429
- detailedMatches: [],
1430
- query,
1431
- matchCount: 0,
1432
- message: `No matches found for pattern: ${query}`
1433
- };
1434
- }
1435
- if (exitCode !== 0 && exitCode !== 2) {
1436
- return {
1437
- success: false,
1438
- message: `Ripgrep error: ${stderr || "Unknown error"}`,
1439
- error: "GREP_EXEC_ERROR"
1440
- };
1441
- }
1442
- const lines = stdout.trim().split("\n").filter((line) => line.length > 0);
1443
- const rawMatches = [];
1444
- const uniqueFiles = /* @__PURE__ */ new Set();
1445
- for (const line of lines) {
1446
- const firstColon = line.indexOf(":");
1447
- const secondColon = line.indexOf(":", firstColon + 1);
1448
- if (firstColon > 0 && secondColon > firstColon) {
1449
- const file = line.substring(0, firstColon);
1450
- const lineNumber = parseInt(line.substring(firstColon + 1, secondColon), 10);
1451
- let content = line.substring(secondColon + 1);
1452
- if (content.length > GREP_LIMITS.MAX_LINE_LENGTH) {
1453
- content = content.substring(0, GREP_LIMITS.MAX_LINE_LENGTH) + "...";
1454
- }
1455
- rawMatches.push({
1456
- file,
1457
- lineNumber,
1458
- content: content.trim(),
1459
- mtime: 0
1460
- });
1461
- uniqueFiles.add(file);
1462
- }
1463
- }
1464
- if (sortByMtime && uniqueFiles.size > 0) {
1465
- const mtimeMap = await getMtimesBatched(Array.from(uniqueFiles));
1466
- for (const match of rawMatches) {
1467
- match.mtime = mtimeMap.get(match.file) || 0;
1468
- }
1469
- rawMatches.sort((a, b) => {
1470
- if (b.mtime !== a.mtime) return b.mtime - a.mtime;
1471
- return a.file.localeCompare(b.file);
1472
- });
1473
- }
1474
- const truncated = rawMatches.length > GREP_LIMITS.DEFAULT_MAX_MATCHES;
1475
- const finalMatches = truncated ? rawMatches.slice(0, GREP_LIMITS.DEFAULT_MAX_MATCHES) : rawMatches;
1476
- const detailedMatches = finalMatches.map((m) => ({
1477
- file: m.file,
1478
- lineNumber: m.lineNumber,
1479
- content: m.content
1480
- }));
1481
- const matches = finalMatches.map(
1482
- (m) => `${m.file}:${m.lineNumber}:${m.content}`
1483
- );
1484
- const groupedOutput = [`Found ${finalMatches.length} matches`];
1485
- let currentFile = "";
1486
- for (const match of finalMatches) {
1487
- if (currentFile !== match.file) {
1488
- if (currentFile !== "") {
1489
- groupedOutput.push("");
1490
- }
1491
- currentFile = match.file;
1492
- groupedOutput.push(`${match.file}:`);
1493
- }
1494
- groupedOutput.push(` Line ${match.lineNumber}: ${match.content}`);
1495
- }
1496
- if (truncated) {
1497
- groupedOutput.push("");
1498
- groupedOutput.push(GREP_LIMITS.TRUNCATION_MESSAGE);
1499
- }
1500
- return {
1501
- success: true,
1502
- matches,
1503
- detailedMatches,
1504
- query,
1505
- matchCount: finalMatches.length,
1506
- truncated,
1507
- message: `Found ${finalMatches.length} matches for pattern: ${query}`,
1508
- content: groupedOutput.join("\n")
1509
- };
1510
- } catch (error) {
1511
- console.error("[grep] error:", error);
1512
- return {
1513
- success: false,
1514
- message: error?.message || String(error),
1515
- error: "GREP_EXEC_ERROR"
1516
- };
1517
- }
1518
- };
1519
- zod.z.object({
1520
- pattern: zod.z.string().describe('Glob pattern to match files (e.g., "**/*.js", "src/**/*.ts", "*.json"). Supports standard glob syntax with *, **, and ? wildcards'),
1521
- path: zod.z.string().optional().describe("Optional directory path to limit the search scope. If not provided, searches from the project root")
1522
- });
1523
- var RESULT_LIMIT = 100;
1524
- var MTIME_BATCH_SIZE = 50;
1525
- async function getMtimesBatched2(files) {
1526
- const results = [];
1527
- for (let i = 0; i < files.length; i += MTIME_BATCH_SIZE) {
1528
- const batch = files.slice(i, i + MTIME_BATCH_SIZE);
1529
- const batchResults = await Promise.all(
1530
- batch.map(async (filePath) => {
1531
- const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
1532
- return { path: filePath, mtime };
1533
- })
1534
- );
1535
- results.push(...batchResults);
1536
- }
1537
- return results;
1538
- }
1539
- var globTool = async function(input, projectCwd) {
1540
- const { pattern, path: inputPath } = input;
1541
- if (!pattern) {
1542
- return {
1543
- success: false,
1544
- message: "Missing required parameter: pattern",
1545
- error: "MISSING_PATTERN"
1546
- };
1547
- }
1548
- try {
1549
- const basePath = projectCwd || process.cwd();
1550
- const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
1551
- if (!fs8__default.default.existsSync(searchPath)) {
1552
- return {
1553
- success: false,
1554
- message: `Directory not found: ${searchPath}`,
1555
- error: "DIR_NOT_FOUND"
1556
- };
1557
- }
1558
- if (projectCwd && inputPath) {
1559
- const validation = validatePath(inputPath, projectCwd);
1560
- if (!validation.valid) {
1561
- return {
1562
- success: false,
1563
- message: validation.error || "Path validation failed",
1564
- error: "ACCESS_DENIED"
1565
- };
1566
- }
1567
- }
1568
- const glob = new Bun.Glob(pattern);
1569
- const files = [];
1570
- let truncated = false;
1571
- for await (const match of glob.scan({
1572
- cwd: searchPath,
1573
- absolute: true,
1574
- onlyFiles: true,
1575
- followSymlinks: false
1576
- })) {
1577
- if (match.includes("/node_modules/") || match.includes("/.git/")) {
1578
- continue;
1579
- }
1580
- if (files.length >= RESULT_LIMIT) {
1581
- truncated = true;
1582
- break;
1583
- }
1584
- files.push(match);
1585
- }
1586
- let sortedFiles;
1587
- if (files.length > 0) {
1588
- const filesWithMtime = await getMtimesBatched2(files);
1589
- filesWithMtime.sort((a, b) => b.mtime - a.mtime);
1590
- sortedFiles = filesWithMtime.map((f) => f.path);
1591
- } else {
1592
- sortedFiles = files;
1593
- }
1594
- const output = [];
1595
- if (sortedFiles.length === 0) {
1596
- output.push("No files found");
1597
- } else {
1598
- output.push(...sortedFiles);
1599
- if (truncated) {
1600
- output.push("");
1601
- output.push("(Results are truncated. Consider using a more specific path or pattern.)");
1602
- }
1603
- }
1604
- const searchLocation = inputPath ? ` in "${inputPath}"` : " in current directory";
1605
- const message = `Found ${sortedFiles.length} matches for pattern "${pattern}"${searchLocation}`;
1606
- return {
1607
- success: true,
1608
- message,
1609
- metadata: {
1610
- count: sortedFiles.length,
1611
- truncated
1612
- },
1613
- content: output.join("\n")
1614
- };
1615
- } catch (error) {
1616
- console.error("[glob] error:", error);
1617
- return {
1618
- success: false,
1619
- message: `Failed to find files matching pattern: ${pattern}`,
1620
- error: "GLOB_ERROR"
1621
- };
1622
- }
1623
- };
1624
- var DEFAULT_IGNORE_PATTERNS = [
1625
- "node_modules",
1626
- "__pycache__",
1627
- ".git",
1628
- "dist",
1629
- "build",
1630
- "target",
1631
- "vendor",
1632
- "bin",
1633
- "obj",
1634
- ".idea",
1635
- ".vscode",
1636
- ".zig-cache",
1637
- "zig-out",
1638
- ".coverage",
1639
- "coverage",
1640
- "tmp",
1641
- "temp",
1642
- ".cache",
1643
- "cache",
1644
- "logs",
1645
- ".venv",
1646
- "venv",
1647
- "env",
1648
- ".next",
1649
- ".turbo",
1650
- ".vercel",
1651
- ".output"
1652
- ];
1653
- var RESULT_LIMIT2 = 500;
1654
- var MTIME_BATCH_SIZE2 = 50;
1655
- zod.z.object({
1656
- path: zod.z.string().optional().describe("Path to the directory to list"),
1657
- recursive: zod.z.boolean().optional().describe("Whether to list files recursively (default: true)"),
1658
- maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: 3)"),
1659
- pattern: zod.z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
1660
- showHidden: zod.z.boolean().optional().describe("Whether to show hidden files (default: false)"),
1661
- includeMetadata: zod.z.boolean().optional().describe("Whether to fetch file metadata like mtime (default: false -- faster without I/O)"),
1662
- ignore: zod.z.array(zod.z.string()).optional().describe("Additional glob patterns to ignore (added to default ignore list)")
1663
- });
1664
- function shouldIgnore(name, showHidden, ignoreSet) {
1665
- if (!showHidden && name.startsWith(".") && name !== ".") {
1666
- return true;
1667
- }
1668
- return ignoreSet.has(name);
1669
- }
1670
- function matchPattern(name, pattern) {
1671
- if (!pattern) return true;
1672
- if (pattern.startsWith(".") && !pattern.includes("*")) {
1673
- return name.endsWith(pattern);
1674
- }
1675
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1676
- const regex = new RegExp(`^${escaped}$`, "i");
1677
- return regex.test(name);
1678
- }
1679
- async function getMtimesBatched3(entries) {
1680
- for (let i = 0; i < entries.length; i += MTIME_BATCH_SIZE2) {
1681
- const batch = entries.slice(i, i + MTIME_BATCH_SIZE2);
1682
- await Promise.all(
1683
- batch.map(async (entry) => {
1684
- entry.mtime = await Bun.file(entry.absolutePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
1685
- })
1686
- );
1687
- }
1688
- }
1689
- function buildTreeOutput(entries, basePath) {
1690
- const tree = /* @__PURE__ */ new Map();
1691
- for (const entry of entries) {
1692
- const dir = path10__default.default.dirname(entry.relativePath);
1693
- const dirKey = dir === "." ? "" : dir;
1694
- if (!tree.has(dirKey)) {
1695
- tree.set(dirKey, []);
1696
- }
1697
- tree.get(dirKey).push(entry);
1698
- }
1699
- for (const [, items] of tree) {
1700
- items.sort((a, b) => {
1701
- if (a.type !== b.type) {
1702
- return a.type === "directory" ? -1 : 1;
1703
- }
1704
- return a.name.localeCompare(b.name);
1705
- });
1706
- }
1707
- const lines = [`${basePath}/`];
1708
- function renderLevel(dirPath, indent) {
1709
- const items = tree.get(dirPath) || [];
1710
- for (let i = 0; i < items.length; i++) {
1711
- const item = items[i];
1712
- const isLast = i === items.length - 1;
1713
- const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1714
- const childIndent = indent + (isLast ? " " : "\u2502 ");
1715
- if (item.type === "directory") {
1716
- lines.push(`${indent}${prefix}${item.name}/`);
1717
- const childPath = dirPath ? `${dirPath}/${item.name}` : item.name;
1718
- renderLevel(childPath, childIndent);
1719
- } else {
1720
- lines.push(`${indent}${prefix}${item.name}`);
1721
- }
1722
- }
1723
- }
1724
- renderLevel("", "");
1725
- return lines.join("\n");
1726
- }
1727
- var list = async function(input, projectCwd) {
1728
- const {
1729
- path: relativePath,
1730
- recursive = true,
1731
- maxDepth = 3,
1732
- pattern,
1733
- showHidden = false,
1734
- includeMetadata = false,
1735
- ignore: extraIgnore
1736
- } = input;
1737
- if (maxDepth !== void 0 && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
1738
- return {
1739
- success: false,
1740
- message: "maxDepth must be a non-negative integer",
1741
- error: "INVALID_MAX_DEPTH"
1742
- };
1743
- }
1744
- try {
1745
- const basePath = projectCwd || process.cwd();
1746
- const absolutePath = relativePath ? resolveProjectPath(relativePath, basePath) : basePath;
1747
- if (projectCwd && relativePath) {
1748
- const validation = validatePath(relativePath, projectCwd);
1749
- if (!validation.valid) {
1750
- return {
1751
- success: false,
1752
- message: validation.error || "Path validation failed",
1753
- error: "ACCESS_DENIED"
1754
- };
1755
- }
1756
- }
1757
- if (!fs8__default.default.existsSync(absolutePath)) {
1758
- return {
1759
- success: false,
1760
- message: `Directory not found: ${absolutePath}`,
1761
- error: "DIR_NOT_FOUND"
1762
- };
1763
- }
1764
- const stats = fs8__default.default.statSync(absolutePath);
1765
- if (!stats.isDirectory()) {
1766
- return {
1767
- success: false,
1768
- message: `Path is not a directory: ${absolutePath}`,
1769
- error: "NOT_A_DIRECTORY"
1770
- };
1771
- }
1772
- const ignoreSet = new Set(DEFAULT_IGNORE_PATTERNS);
1773
- if (extraIgnore && extraIgnore.length > 0) {
1774
- for (const pat of extraIgnore) {
1775
- ignoreSet.add(pat);
1776
- }
1777
- }
1778
- const collected = [];
1779
- let truncated = false;
1780
- const walk = async (currentDir, depth) => {
1781
- if (collected.length >= RESULT_LIMIT2) {
1782
- truncated = true;
1783
- return;
1784
- }
1785
- let entries;
1786
- try {
1787
- entries = fs8__default.default.readdirSync(currentDir, { withFileTypes: true });
1788
- } catch {
1789
- return;
1790
- }
1791
- entries.sort((a, b) => {
1792
- if (a.isDirectory() !== b.isDirectory()) {
1793
- return a.isDirectory() ? -1 : 1;
1794
- }
1795
- return a.name.localeCompare(b.name);
1796
- });
1797
- for (const entry of entries) {
1798
- if (collected.length >= RESULT_LIMIT2) {
1799
- truncated = true;
1800
- break;
1801
- }
1802
- if (shouldIgnore(entry.name, showHidden, ignoreSet)) {
1803
- continue;
1804
- }
1805
- const entryAbsolutePath = path10__default.default.join(currentDir, entry.name);
1806
- const entryRelativePath = path10__default.default.relative(absolutePath, entryAbsolutePath);
1807
- if (entry.isDirectory()) {
1808
- collected.push({
1809
- name: entry.name,
1810
- absolutePath: entryAbsolutePath,
1811
- relativePath: entryRelativePath,
1812
- type: "directory",
1813
- mtime: 0,
1814
- depth
1815
- });
1816
- if (recursive && depth < maxDepth) {
1817
- await walk(entryAbsolutePath, depth + 1);
1818
- }
1819
- } else if (entry.isFile()) {
1820
- if (matchPattern(entry.name, pattern)) {
1821
- collected.push({
1822
- name: entry.name,
1823
- absolutePath: entryAbsolutePath,
1824
- relativePath: entryRelativePath,
1825
- type: "file",
1826
- mtime: 0,
1827
- depth
1828
- });
1829
- }
1830
- }
1831
- }
1832
- };
1833
- await walk(absolutePath, 0);
1834
- if (includeMetadata) {
1835
- await getMtimesBatched3(collected);
1836
- }
1837
- const totalFiles = collected.filter((item) => item.type === "file").length;
1838
- const totalDirectories = collected.filter((item) => item.type === "directory").length;
1839
- const treeOutput = buildTreeOutput(collected, relativePath || path10__default.default.basename(absolutePath));
1840
- let message = `Listed ${collected.length} items`;
1841
- if (relativePath) {
1842
- message += ` in "${relativePath}"`;
1843
- }
1844
- message += ` (${totalFiles} files, ${totalDirectories} directories)`;
1845
- if (recursive) {
1846
- message += ` [depth: ${maxDepth}]`;
1847
- }
1848
- if (pattern) {
1849
- message += ` [filter: ${pattern}]`;
1850
- }
1851
- if (truncated) {
1852
- message += ` [TRUNCATED at ${RESULT_LIMIT2} items]`;
1853
- }
1854
- const files = collected.map((item) => ({
1855
- name: item.name,
1856
- path: item.relativePath,
1857
- type: item.type
1858
- }));
1859
- return {
1860
- success: true,
1861
- message,
1862
- metadata: {
1863
- totalFiles,
1864
- totalDirectories,
1865
- totalItems: collected.length,
1866
- truncated,
1867
- maxDepth,
1868
- recursive
1869
- },
1870
- files,
1871
- content: treeOutput
1872
- };
1873
- } catch (error) {
1874
- console.error("[list] error:", error);
1875
- return {
1876
- success: false,
1877
- message: `Failed to list directory: ${error}`,
1878
- error: "LIST_ERROR"
1879
- };
1880
- }
1881
- };
5
+ var path6 = require('path');
6
+ var os3 = require('os');
7
+ var pc3 = require('picocolors');
8
+ var hono = require('hono');
9
+ var nodeServer = require('@hono/node-server');
10
+ var cors = require('hono/cors');
11
+ var fs4 = require('fs');
12
+ var child_process = require('child_process');
13
+ var util = require('util');
14
+ var fs5 = require('fs/promises');
15
+ var zod = require('zod');
16
+ var http = require('http');
17
+ var agent = require('@ama/agent');
18
+
19
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
+
21
+ var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
22
+ var path6__default = /*#__PURE__*/_interopDefault(path6);
23
+ var os3__default = /*#__PURE__*/_interopDefault(os3);
24
+ var pc3__default = /*#__PURE__*/_interopDefault(pc3);
25
+ var fs4__default = /*#__PURE__*/_interopDefault(fs4);
26
+ var fs5__default = /*#__PURE__*/_interopDefault(fs5);
27
+
28
+ var DEFAULT_SERVER_URL = "wss://bridge.ama.shujan.xyz";
29
+ var AMA_DIR = path6__default.default.join(os3__default.default.homedir(), ".amai");
30
+ path6__default.default.join(AMA_DIR, "code");
31
+ path6__default.default.join(AMA_DIR, "storage");
1882
32
  var startHttpServer = () => {
1883
33
  const app = new hono.Hono();
1884
34
  app.use(cors.cors());
@@ -1894,22 +44,22 @@ var startHttpServer = () => {
1894
44
  }
1895
45
  });
1896
46
  };
1897
- var CREDENTIALS_DIR = path10__default.default.join(os3__default.default.homedir(), ".amai");
1898
- var CREDENTIALS_PATH = path10__default.default.join(CREDENTIALS_DIR, "credentials.json");
47
+ var CREDENTIALS_DIR = path6__default.default.join(os3__default.default.homedir(), ".amai");
48
+ var CREDENTIALS_PATH = path6__default.default.join(CREDENTIALS_DIR, "credentials.json");
1899
49
  function getTokens() {
1900
- if (!fs8__default.default.existsSync(CREDENTIALS_PATH)) {
50
+ if (!fs4__default.default.existsSync(CREDENTIALS_PATH)) {
1901
51
  return null;
1902
52
  }
1903
- const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
53
+ const raw = fs4__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1904
54
  const data = JSON.parse(raw);
1905
55
  return data;
1906
56
  }
1907
57
  var getUserId = () => {
1908
58
  try {
1909
- if (!fs8__default.default.existsSync(CREDENTIALS_PATH)) {
59
+ if (!fs4__default.default.existsSync(CREDENTIALS_PATH)) {
1910
60
  return;
1911
61
  }
1912
- const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
62
+ const raw = fs4__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
1913
63
  const data = JSON.parse(raw);
1914
64
  const fromUserObject = data.user?.id;
1915
65
  const fromTopLevel = data.sub ?? data.user_id;
@@ -1934,168 +84,54 @@ var getUserId = () => {
1934
84
  throw new Error("Error while getting userId");
1935
85
  }
1936
86
  };
1937
- var ExplanationSchema = zod.z.object({
1938
- description: zod.z.string().describe(
1939
- "Clear, concise description of what this command does in 5-10 words. Examples:\nInput: ls\nOutput: Lists files in current directory\n\nInput: git status\nOutput: Shows working tree status\n\nInput: npm install\nOutput: Installs package dependencies\n\nInput: mkdir foo\nOutput: Creates directory 'foo'"
1940
- )
1941
- });
1942
- var BLOCKED_PATTERNS = [
1943
- /\brm\s+(-\w+\s+)*(\/ |\/\s*$|~|\/\*|\*)/,
1944
- /\bdd\s+.*of=\/dev\//,
1945
- /\bmkfs\b/,
1946
- /:\(\)\{.*\|.*&\}\s*;?\s*:/,
1947
- /\bchmod\s+.*-R.*\s+\/\s*$/,
1948
- /\bchown\s+.*-R.*\s+\/\s*$/,
1949
- /\b(curl|wget)\s+.*\|\s*(ba)?sh/,
1950
- /\bmv\s+(\/|\*)\s/,
1951
- /\bcat\s+\/dev\/(u?random|zero)\s*>\s*\/dev\//,
1952
- /\bformat\s+[A-Z]:/i,
1953
- /\bdiskpart\b/i,
1954
- /\bcipher\s+\/w:/i
1955
- ];
1956
- var DANGEROUS_FLAGS = [
1957
- /--no-preserve-root/,
1958
- /\bgit\s+push\s+.*--force\b/,
1959
- /\bgit\s+push\s+-f\b/
1960
- ];
1961
- var MAX_OUTPUT_SIZE = 1 * 1024 * 1024;
1962
- var DEFAULT_TIMEOUT = 12e4;
1963
- function evaluateCommandSafety(command) {
1964
- const trimmed = command.trim();
1965
- for (const pattern of BLOCKED_PATTERNS) {
1966
- if (pattern.test(trimmed)) {
1967
- return { safe: false, reason: `Blocked by safety policy: matches destructive pattern` };
1968
- }
1969
- }
1970
- for (const flag of DANGEROUS_FLAGS) {
1971
- if (flag.test(trimmed)) {
1972
- return { safe: false, reason: `Blocked by safety policy: dangerous flag detected` };
1973
- }
1974
- }
1975
- return { safe: true };
87
+ var MUTATING_TOOLS = /* @__PURE__ */ new Set([
88
+ "editFile",
89
+ "deleteFile",
90
+ "stringReplace",
91
+ "bash"
92
+ ]);
93
+ function isMutatingTool(toolName) {
94
+ return MUTATING_TOOLS.has(toolName);
1976
95
  }
1977
- zod.z.object({
1978
- command: zod.z.string().describe("The terminal command to execute"),
1979
- is_background: zod.z.boolean().optional().default(false).describe("Whether the command should be run in the background"),
1980
- timeout: zod.z.number().optional().describe("Optional timeout in milliseconds. If not specified, commands will time out after 120000ms (2 minutes)."),
1981
- workdir: zod.z.string().optional().describe("The working directory to run the command in. Defaults to the project directory. Use this instead of 'cd' commands.")
1982
- }).merge(ExplanationSchema);
1983
- var runSecureTerminalCommand = async (command, timeout, cwd) => {
96
+ function isPathWithinProject(filePath, projectCwd) {
1984
97
  try {
1985
- const safety = evaluateCommandSafety(command);
1986
- if (!safety.safe) {
1987
- console.log(`[CLI] Blocked command: ${command} \u2014 ${safety.reason}`);
1988
- return {
1989
- success: false,
1990
- message: safety.reason,
1991
- error: "BLOCKED_COMMAND"
1992
- };
1993
- }
1994
- const proc = Bun.spawn(["sh", "-c", command], {
1995
- cwd: cwd || process.cwd(),
1996
- stdout: "pipe",
1997
- stderr: "pipe"
1998
- });
1999
- let timedOut = false;
2000
- let timeoutId = null;
2001
- if (timeout > 0) {
2002
- timeoutId = setTimeout(() => {
2003
- timedOut = true;
2004
- proc.kill();
2005
- }, timeout);
2006
- }
2007
- const [stdout, stderr, exitCode] = await Promise.all([
2008
- new Response(proc.stdout).text(),
2009
- new Response(proc.stderr).text(),
2010
- proc.exited
2011
- ]);
2012
- if (timeoutId) {
2013
- clearTimeout(timeoutId);
2014
- }
2015
- if (timedOut) {
2016
- return {
2017
- success: false,
2018
- message: `Command timed out after ${timeout}ms`,
2019
- error: "TIMEOUT",
2020
- stdout: stdout.slice(0, MAX_OUTPUT_SIZE),
2021
- stderr: stderr.slice(0, MAX_OUTPUT_SIZE)
2022
- };
98
+ const resolvedCwd = safeRealpath(projectCwd);
99
+ const resolved = path6__default.default.resolve(resolvedCwd, filePath);
100
+ const resolvedTarget = safeRealpath(resolved);
101
+ const rel = path6__default.default.relative(resolvedCwd, resolvedTarget);
102
+ if (rel.startsWith("..") || path6__default.default.isAbsolute(rel)) {
103
+ return false;
2023
104
  }
2024
- return {
2025
- stdout: stdout.slice(0, MAX_OUTPUT_SIZE),
2026
- stderr: stderr.slice(0, MAX_OUTPUT_SIZE),
2027
- exitCode
2028
- };
2029
- } catch (error) {
2030
- console.error("Error while executing the securedShell command", error);
2031
- return {
2032
- success: false,
2033
- message: "Error while executing the securedShell command",
2034
- error: error.message
2035
- };
105
+ return true;
106
+ } catch {
107
+ return false;
2036
108
  }
2037
- };
2038
- var bashTool = async (input, projectCwd) => {
109
+ }
110
+ function safeRealpath(p) {
2039
111
  try {
2040
- const safety = evaluateCommandSafety(input.command);
2041
- if (!safety.safe) {
2042
- console.log(`[CLI] Blocked command: ${input.command} \u2014 ${safety.reason}`);
2043
- return {
2044
- success: false,
2045
- message: safety.reason,
2046
- error: "BLOCKED_COMMAND"
2047
- };
2048
- }
2049
- if (input.timeout !== void 0 && input.timeout < 0) {
2050
- return {
2051
- success: false,
2052
- message: `Invalid timeout value: ${input.timeout}. Timeout must be a positive number.`,
2053
- error: "INVALID_TIMEOUT"
2054
- };
2055
- }
2056
- const cwd = input.workdir || projectCwd || process.cwd();
2057
- const timeout = input.timeout ?? DEFAULT_TIMEOUT;
2058
- if (input?.is_background) {
2059
- const proc = Bun.spawn(["sh", "-c", input.command], {
2060
- cwd,
2061
- stdout: "ignore",
2062
- stderr: "ignore"
2063
- });
2064
- proc.unref();
2065
- console.log(`[LOCAL] Background command started: ${input.command}`);
2066
- return {
2067
- success: true,
2068
- message: `Background command started: ${input.command}`,
2069
- isBackground: true
2070
- };
2071
- } else {
2072
- const result = await runSecureTerminalCommand(
2073
- input.command,
2074
- timeout,
2075
- cwd
2076
- );
2077
- if (result?.error && !result?.exitCode) {
2078
- return result;
2079
- }
2080
- const success = result?.exitCode === 0;
2081
- return {
2082
- success,
2083
- stdout: result?.stdout?.trim(),
2084
- stderr: result?.stderr?.trim(),
2085
- exitCode: result?.exitCode,
2086
- message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
2087
- };
112
+ return fs4__default.default.realpathSync(p);
113
+ } catch {
114
+ const parent = path6__default.default.dirname(p);
115
+ try {
116
+ const realParent = fs4__default.default.realpathSync(parent);
117
+ return path6__default.default.join(realParent, path6__default.default.basename(p));
118
+ } catch {
119
+ return path6__default.default.resolve(p);
2088
120
  }
2089
- } catch (error) {
2090
- console.error("Error while executing the terminal command", error);
121
+ }
122
+ }
123
+ function requireProjectCwd(toolName, projectCwd) {
124
+ if (!projectCwd && isMutatingTool(toolName)) {
2091
125
  return {
2092
- success: false,
2093
- message: "Error while executing the terminal command",
2094
- error: error.message
126
+ allowed: false,
127
+ error: `ACCESS_DENIED: Tool "${toolName}" requires a project context (projectCwd) but none was provided`
2095
128
  };
2096
129
  }
2097
- };
2098
- var REGISTRY_FILE = path10__default.default.join(AMA_DIR, "projects.json");
130
+ return { allowed: true };
131
+ }
132
+
133
+ // src/lib/project-registry.ts
134
+ var REGISTRY_FILE = path6__default.default.join(AMA_DIR, "projects.json");
2099
135
  var ProjectRegistry = class {
2100
136
  projects = /* @__PURE__ */ new Map();
2101
137
  constructor() {
@@ -2103,14 +139,14 @@ var ProjectRegistry = class {
2103
139
  }
2104
140
  load() {
2105
141
  try {
2106
- if (fs8__default.default.existsSync(REGISTRY_FILE)) {
2107
- const data = fs8__default.default.readFileSync(REGISTRY_FILE, "utf8");
142
+ if (fs4__default.default.existsSync(REGISTRY_FILE)) {
143
+ const data = fs4__default.default.readFileSync(REGISTRY_FILE, "utf8");
2108
144
  const parsed = JSON.parse(data);
2109
145
  if (!Array.isArray(parsed)) {
2110
146
  console.error("Invalid project registry format: expected array, got", typeof parsed);
2111
147
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
2112
- fs8__default.default.copyFileSync(REGISTRY_FILE, backupFile);
2113
- fs8__default.default.unlinkSync(REGISTRY_FILE);
148
+ fs4__default.default.copyFileSync(REGISTRY_FILE, backupFile);
149
+ fs4__default.default.unlinkSync(REGISTRY_FILE);
2114
150
  return;
2115
151
  }
2116
152
  const projects = parsed;
@@ -2123,11 +159,11 @@ var ProjectRegistry = class {
2123
159
  }
2124
160
  } catch (error) {
2125
161
  console.error("Failed to load project registry:", error);
2126
- if (fs8__default.default.existsSync(REGISTRY_FILE)) {
162
+ if (fs4__default.default.existsSync(REGISTRY_FILE)) {
2127
163
  try {
2128
164
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
2129
- fs8__default.default.copyFileSync(REGISTRY_FILE, backupFile);
2130
- fs8__default.default.unlinkSync(REGISTRY_FILE);
165
+ fs4__default.default.copyFileSync(REGISTRY_FILE, backupFile);
166
+ fs4__default.default.unlinkSync(REGISTRY_FILE);
2131
167
  console.log("Corrupted registry file backed up and removed. Starting fresh.");
2132
168
  } catch (backupError) {
2133
169
  }
@@ -2136,21 +172,21 @@ var ProjectRegistry = class {
2136
172
  }
2137
173
  save() {
2138
174
  try {
2139
- if (!fs8__default.default.existsSync(AMA_DIR)) {
2140
- fs8__default.default.mkdirSync(AMA_DIR, { recursive: true });
175
+ if (!fs4__default.default.existsSync(AMA_DIR)) {
176
+ fs4__default.default.mkdirSync(AMA_DIR, { recursive: true });
2141
177
  }
2142
178
  const projects = Array.from(this.projects.values());
2143
- fs8__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
179
+ fs4__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
2144
180
  } catch (error) {
2145
181
  console.error("Failed to save project registry:", error);
2146
182
  }
2147
183
  }
2148
184
  register(projectId, cwd, name) {
2149
- const normalizedCwd = path10__default.default.normalize(path10__default.default.resolve(cwd));
185
+ const normalizedCwd = path6__default.default.normalize(path6__default.default.resolve(cwd));
2150
186
  this.projects.set(projectId, {
2151
187
  id: projectId,
2152
188
  cwd: normalizedCwd,
2153
- name: name || path10__default.default.basename(normalizedCwd),
189
+ name: name || path6__default.default.basename(normalizedCwd),
2154
190
  active: true
2155
191
  });
2156
192
  this.save();
@@ -2180,36 +216,36 @@ var ProjectRegistry = class {
2180
216
  var projectRegistry = new ProjectRegistry();
2181
217
  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"];
2182
218
  var getContext = (dir, base = dir, allFiles = []) => {
2183
- const filePath = fs8.readdirSync(dir, { withFileTypes: true });
219
+ const filePath = fs4.readdirSync(dir, { withFileTypes: true });
2184
220
  for (const file of filePath) {
2185
221
  if (ignoreFiles.includes(file.name)) continue;
2186
- const fullPath = path10__default.default.join(dir, file.name);
222
+ const fullPath = path6__default.default.join(dir, file.name);
2187
223
  if (file.isDirectory()) {
2188
224
  getContext(fullPath, base, allFiles);
2189
225
  } else {
2190
- allFiles.push(path10__default.default.relative(base, fullPath));
226
+ allFiles.push(path6__default.default.relative(base, fullPath));
2191
227
  }
2192
228
  }
2193
229
  return allFiles;
2194
230
  };
2195
231
  var HOME = os3__default.default.homedir();
2196
232
  var IDE_PROJECTS_PATHS = {
2197
- vscode: path10__default.default.join(HOME, ".vscode", "projects"),
2198
- cursor: path10__default.default.join(HOME, ".cursor", "projects"),
2199
- claude: path10__default.default.join(HOME, ".claude", "projects")
233
+ vscode: path6__default.default.join(HOME, ".vscode", "projects"),
234
+ cursor: path6__default.default.join(HOME, ".cursor", "projects"),
235
+ claude: path6__default.default.join(HOME, ".claude", "projects")
2200
236
  };
2201
237
  function getWorkspaceStoragePath(ide) {
2202
238
  const platform = os3__default.default.platform();
2203
239
  const appName = ide === "cursor" ? "Cursor" : "Code";
2204
240
  const appNameLower = appName.toLowerCase();
2205
241
  if (platform === "darwin") {
2206
- return path10__default.default.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
242
+ return path6__default.default.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
2207
243
  } else if (platform === "win32") {
2208
- return path10__default.default.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
244
+ return path6__default.default.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
2209
245
  } else {
2210
- const capitalizedPath = path10__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
2211
- const lowercasePath = path10__default.default.join(HOME, ".config", appNameLower, "User", "workspaceStorage");
2212
- if (fs8__default.default.existsSync(capitalizedPath)) {
246
+ const capitalizedPath = path6__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
247
+ const lowercasePath = path6__default.default.join(HOME, ".config", appNameLower, "User", "workspaceStorage");
248
+ if (fs4__default.default.existsSync(capitalizedPath)) {
2213
249
  return capitalizedPath;
2214
250
  }
2215
251
  return lowercasePath;
@@ -2218,16 +254,16 @@ function getWorkspaceStoragePath(ide) {
2218
254
  function scanWorkspaceStorage(ide) {
2219
255
  const projects = [];
2220
256
  const storagePath = getWorkspaceStoragePath(ide);
2221
- if (!fs8__default.default.existsSync(storagePath)) {
257
+ if (!fs4__default.default.existsSync(storagePath)) {
2222
258
  return projects;
2223
259
  }
2224
260
  try {
2225
- const workspaces = fs8__default.default.readdirSync(storagePath);
261
+ const workspaces = fs4__default.default.readdirSync(storagePath);
2226
262
  for (const workspace of workspaces) {
2227
- const workspaceJsonPath = path10__default.default.join(storagePath, workspace, "workspace.json");
2228
- if (fs8__default.default.existsSync(workspaceJsonPath)) {
263
+ const workspaceJsonPath = path6__default.default.join(storagePath, workspace, "workspace.json");
264
+ if (fs4__default.default.existsSync(workspaceJsonPath)) {
2229
265
  try {
2230
- const content = fs8__default.default.readFileSync(workspaceJsonPath, "utf-8");
266
+ const content = fs4__default.default.readFileSync(workspaceJsonPath, "utf-8");
2231
267
  const data = JSON.parse(content);
2232
268
  if (data.folder && typeof data.folder === "string") {
2233
269
  let projectPath = data.folder;
@@ -2235,9 +271,9 @@ function scanWorkspaceStorage(ide) {
2235
271
  projectPath = projectPath.replace("file://", "");
2236
272
  projectPath = decodeURIComponent(projectPath);
2237
273
  }
2238
- if (fs8__default.default.existsSync(projectPath) && fs8__default.default.statSync(projectPath).isDirectory()) {
274
+ if (fs4__default.default.existsSync(projectPath) && fs4__default.default.statSync(projectPath).isDirectory()) {
2239
275
  projects.push({
2240
- name: path10__default.default.basename(projectPath),
276
+ name: path6__default.default.basename(projectPath),
2241
277
  path: projectPath,
2242
278
  type: ide
2243
279
  });
@@ -2259,11 +295,11 @@ var scanIdeProjects = async () => {
2259
295
  const seenPaths = /* @__PURE__ */ new Set();
2260
296
  const addProject = (projectPath, ide) => {
2261
297
  try {
2262
- const resolvedPath = fs8__default.default.realpathSync(projectPath);
2263
- if (fs8__default.default.existsSync(resolvedPath) && fs8__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
298
+ const resolvedPath = fs4__default.default.realpathSync(projectPath);
299
+ if (fs4__default.default.existsSync(resolvedPath) && fs4__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
2264
300
  const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
2265
301
  try {
2266
- return fs8__default.default.realpathSync(ideDir) === resolvedPath;
302
+ return fs4__default.default.realpathSync(ideDir) === resolvedPath;
2267
303
  } catch {
2268
304
  return false;
2269
305
  }
@@ -2271,7 +307,7 @@ var scanIdeProjects = async () => {
2271
307
  if (!isIdeProjectsDir) {
2272
308
  seenPaths.add(resolvedPath);
2273
309
  allProjects.push({
2274
- name: path10__default.default.basename(resolvedPath),
310
+ name: path6__default.default.basename(resolvedPath),
2275
311
  path: resolvedPath,
2276
312
  type: ide
2277
313
  });
@@ -2290,30 +326,30 @@ var scanIdeProjects = async () => {
2290
326
  }
2291
327
  for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
2292
328
  if (ide === "cursor" || ide === "vscode") continue;
2293
- if (fs8__default.default.existsSync(dirPath)) {
2294
- const projects = fs8__default.default.readdirSync(dirPath);
329
+ if (fs4__default.default.existsSync(dirPath)) {
330
+ const projects = fs4__default.default.readdirSync(dirPath);
2295
331
  projects.forEach((project) => {
2296
- const projectPath = path10__default.default.join(dirPath, project);
332
+ const projectPath = path6__default.default.join(dirPath, project);
2297
333
  try {
2298
- const stats = fs8__default.default.lstatSync(projectPath);
334
+ const stats = fs4__default.default.lstatSync(projectPath);
2299
335
  let actualPath = null;
2300
336
  if (stats.isSymbolicLink()) {
2301
- actualPath = fs8__default.default.realpathSync(projectPath);
337
+ actualPath = fs4__default.default.realpathSync(projectPath);
2302
338
  } else if (stats.isFile()) {
2303
339
  try {
2304
- let content = fs8__default.default.readFileSync(projectPath, "utf-8").trim();
340
+ let content = fs4__default.default.readFileSync(projectPath, "utf-8").trim();
2305
341
  if (content.startsWith("~/") || content === "~") {
2306
342
  content = content.replace(/^~/, HOME);
2307
343
  }
2308
- const resolvedContent = path10__default.default.isAbsolute(content) ? content : path10__default.default.resolve(path10__default.default.dirname(projectPath), content);
2309
- if (fs8__default.default.existsSync(resolvedContent) && fs8__default.default.statSync(resolvedContent).isDirectory()) {
2310
- actualPath = fs8__default.default.realpathSync(resolvedContent);
344
+ const resolvedContent = path6__default.default.isAbsolute(content) ? content : path6__default.default.resolve(path6__default.default.dirname(projectPath), content);
345
+ if (fs4__default.default.existsSync(resolvedContent) && fs4__default.default.statSync(resolvedContent).isDirectory()) {
346
+ actualPath = fs4__default.default.realpathSync(resolvedContent);
2311
347
  }
2312
348
  } catch {
2313
349
  return;
2314
350
  }
2315
351
  } else if (stats.isDirectory()) {
2316
- actualPath = fs8__default.default.realpathSync(projectPath);
352
+ actualPath = fs4__default.default.realpathSync(projectPath);
2317
353
  }
2318
354
  if (actualPath) {
2319
355
  addProject(actualPath, ide);
@@ -2332,7 +368,7 @@ var scanIdeProjects = async () => {
2332
368
  var Global;
2333
369
  ((Global2) => {
2334
370
  ((Path2) => {
2335
- Path2.data = path10__default.default.join(AMA_DIR, "data");
371
+ Path2.data = path6__default.default.join(AMA_DIR, "data");
2336
372
  })(Global2.Path || (Global2.Path = {}));
2337
373
  })(Global || (Global = {}));
2338
374
 
@@ -2371,8 +407,8 @@ var Snapshot;
2371
407
  const worktree = project.cwd;
2372
408
  const git = gitdir(projectId);
2373
409
  try {
2374
- await fsp__namespace.default.mkdir(git, { recursive: true });
2375
- const gitExists = await fsp__namespace.default.access(path10__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
410
+ await fs5__default.default.mkdir(git, { recursive: true });
411
+ const gitExists = await fs5__default.default.access(path6__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
2376
412
  if (!gitExists) {
2377
413
  await runGit(`git init`, {
2378
414
  env: { GIT_DIR: git, GIT_WORK_TREE: worktree }
@@ -2413,7 +449,7 @@ var Snapshot;
2413
449
  const files = result.stdout;
2414
450
  return {
2415
451
  hash,
2416
- files: files.trim().split("\n").map((x) => x.trim()).filter(Boolean).map((x) => path10__default.default.join(worktree, x))
452
+ files: files.trim().split("\n").map((x) => x.trim()).filter(Boolean).map((x) => path6__default.default.join(worktree, x))
2417
453
  };
2418
454
  }
2419
455
  Snapshot2.patch = patch;
@@ -2455,9 +491,9 @@ var Snapshot;
2455
491
  if (diffResult.exitCode === 0 && diffResult.stdout.trim()) {
2456
492
  const newFiles = diffResult.stdout.trim().split("\n").filter(Boolean);
2457
493
  for (const file of newFiles) {
2458
- const fullPath = path10__default.default.join(worktree, file);
494
+ const fullPath = path6__default.default.join(worktree, file);
2459
495
  try {
2460
- await fsp__namespace.default.unlink(fullPath);
496
+ await fs5__default.default.unlink(fullPath);
2461
497
  log.info("deleted newly created file", { file: fullPath });
2462
498
  } catch {
2463
499
  }
@@ -2485,7 +521,7 @@ var Snapshot;
2485
521
  { cwd: worktree }
2486
522
  );
2487
523
  if (result.exitCode !== 0) {
2488
- const relativePath = path10__default.default.relative(worktree, file);
524
+ const relativePath = path6__default.default.relative(worktree, file);
2489
525
  const checkTree = await runGit(
2490
526
  `git --git-dir "${git}" --work-tree "${worktree}" ls-tree ${item.hash} -- "${relativePath}"`,
2491
527
  { cwd: worktree }
@@ -2494,7 +530,7 @@ var Snapshot;
2494
530
  log.info("file existed in snapshot but checkout failed, keeping", { file });
2495
531
  } else {
2496
532
  log.info("file did not exist in snapshot, deleting", { file });
2497
- await fsp__namespace.default.unlink(file).catch(() => {
533
+ await fs5__default.default.unlink(file).catch(() => {
2498
534
  });
2499
535
  }
2500
536
  }
@@ -2548,10 +584,10 @@ var Snapshot;
2548
584
  const lines = numstatResult.stdout.trim().split("\n").filter(Boolean);
2549
585
  for (const line of lines) {
2550
586
  const [additions, deletions, file] = line.split(" ");
2551
- const isBinaryFile2 = additions === "-" && deletions === "-";
587
+ const isBinaryFile = additions === "-" && deletions === "-";
2552
588
  let before = "";
2553
589
  let after = "";
2554
- if (!isBinaryFile2) {
590
+ if (!isBinaryFile) {
2555
591
  const beforeResult = await runGit(
2556
592
  `git -c core.autocrlf=false --git-dir "${git}" --work-tree "${worktree}" show ${from}:${file}`,
2557
593
  { cwd: worktree }
@@ -2575,13 +611,13 @@ var Snapshot;
2575
611
  }
2576
612
  Snapshot2.diffFull = diffFull;
2577
613
  function gitdir(projectId) {
2578
- return path10__default.default.join(Global.Path.data, "snapshot", projectId);
614
+ return path6__default.default.join(Global.Path.data, "snapshot", projectId);
2579
615
  }
2580
616
  })(Snapshot || (Snapshot = {}));
2581
617
  var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
2582
618
  var ISSUER = "https://auth.openai.com";
2583
619
  var OAUTH_PORT = 1455;
2584
- var CREDENTIALS_PATH2 = path10__default.default.join(AMA_DIR, "codex-credentials.json");
620
+ var CREDENTIALS_PATH2 = path6__default.default.join(AMA_DIR, "codex-credentials.json");
2585
621
  var CALLBACK_PATH = "/auth/callback";
2586
622
  var OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
2587
623
  var REFRESH_BUFFER_MS = 60 * 1e3;
@@ -2676,19 +712,19 @@ var HTML_ERROR = (error) => `<!doctype html>
2676
712
  </body>
2677
713
  </html>`;
2678
714
  function ensureCredentialsDir() {
2679
- if (!fs8__default.default.existsSync(AMA_DIR)) {
2680
- fs8__default.default.mkdirSync(AMA_DIR, { recursive: true });
715
+ if (!fs4__default.default.existsSync(AMA_DIR)) {
716
+ fs4__default.default.mkdirSync(AMA_DIR, { recursive: true });
2681
717
  }
2682
718
  }
2683
719
  function saveCredentials(credentials) {
2684
720
  ensureCredentialsDir();
2685
- fs8__default.default.writeFileSync(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2), "utf8");
721
+ fs4__default.default.writeFileSync(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2), "utf8");
2686
722
  }
2687
723
  function readCredentials() {
2688
- if (!fs8__default.default.existsSync(CREDENTIALS_PATH2)) {
724
+ if (!fs4__default.default.existsSync(CREDENTIALS_PATH2)) {
2689
725
  return null;
2690
726
  }
2691
- const raw = fs8__default.default.readFileSync(CREDENTIALS_PATH2, "utf8");
727
+ const raw = fs4__default.default.readFileSync(CREDENTIALS_PATH2, "utf8");
2692
728
  const parsed = JSON.parse(raw);
2693
729
  if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string" || typeof parsed.accountId !== "string" || typeof parsed.expiresAt !== "number") {
2694
730
  return null;
@@ -2796,56 +832,66 @@ async function startOAuthServer() {
2796
832
  if (oauthServer) {
2797
833
  return { redirectUri: `http://localhost:${OAUTH_PORT}${CALLBACK_PATH}` };
2798
834
  }
2799
- oauthServer = Bun.serve({
2800
- port: OAUTH_PORT,
2801
- fetch(request) {
2802
- const url = new URL(request.url);
2803
- if (url.pathname !== CALLBACK_PATH) {
2804
- return new Response("Not found", { status: 404 });
2805
- }
2806
- const code = url.searchParams.get("code");
2807
- const state = url.searchParams.get("state");
2808
- const error = url.searchParams.get("error");
2809
- const errorDescription = url.searchParams.get("error_description");
2810
- if (error) {
2811
- const message = errorDescription || error;
2812
- pendingOAuth?.reject(new Error(message));
2813
- pendingOAuth = void 0;
2814
- return new Response(HTML_ERROR(message), {
2815
- headers: { "Content-Type": "text/html" }
2816
- });
2817
- }
2818
- if (!code) {
2819
- const message = "Missing authorization code";
2820
- pendingOAuth?.reject(new Error(message));
2821
- pendingOAuth = void 0;
2822
- return new Response(HTML_ERROR(message), {
2823
- status: 400,
2824
- headers: { "Content-Type": "text/html" }
2825
- });
2826
- }
2827
- if (!pendingOAuth || state !== pendingOAuth.state) {
2828
- const message = "Invalid state - potential CSRF attack";
2829
- pendingOAuth?.reject(new Error(message));
2830
- pendingOAuth = void 0;
2831
- return new Response(HTML_ERROR(message), {
2832
- status: 400,
2833
- headers: { "Content-Type": "text/html" }
2834
- });
2835
- }
2836
- const current = pendingOAuth;
835
+ oauthServer = http.createServer((req, res) => {
836
+ const requestUrl = new URL(req.url || "/", `http://localhost:${OAUTH_PORT}`);
837
+ if (requestUrl.pathname !== CALLBACK_PATH) {
838
+ res.statusCode = 404;
839
+ res.end("Not found");
840
+ return;
841
+ }
842
+ const code = requestUrl.searchParams.get("code");
843
+ const state = requestUrl.searchParams.get("state");
844
+ const error = requestUrl.searchParams.get("error");
845
+ const errorDescription = requestUrl.searchParams.get("error_description");
846
+ const sendHtml = (html, statusCode = 200) => {
847
+ res.statusCode = statusCode;
848
+ res.setHeader("Content-Type", "text/html");
849
+ res.end(html);
850
+ };
851
+ if (error) {
852
+ const message = errorDescription || error;
853
+ pendingOAuth?.reject(new Error(message));
2837
854
  pendingOAuth = void 0;
2838
- exchangeCodeForTokens(code, `http://localhost:${OAUTH_PORT}${CALLBACK_PATH}`, current.pkce).then((tokens) => current.resolve(tokens)).catch((err) => current.reject(err));
2839
- return new Response(HTML_SUCCESS, {
2840
- headers: { "Content-Type": "text/html" }
2841
- });
855
+ sendHtml(HTML_ERROR(message));
856
+ return;
857
+ }
858
+ if (!code) {
859
+ const message = "Missing authorization code";
860
+ pendingOAuth?.reject(new Error(message));
861
+ pendingOAuth = void 0;
862
+ sendHtml(HTML_ERROR(message), 400);
863
+ return;
2842
864
  }
865
+ if (!pendingOAuth || state !== pendingOAuth.state) {
866
+ const message = "Invalid state - potential CSRF attack";
867
+ pendingOAuth?.reject(new Error(message));
868
+ pendingOAuth = void 0;
869
+ sendHtml(HTML_ERROR(message), 400);
870
+ return;
871
+ }
872
+ const current = pendingOAuth;
873
+ pendingOAuth = void 0;
874
+ exchangeCodeForTokens(code, `http://localhost:${OAUTH_PORT}${CALLBACK_PATH}`, current.pkce).then((tokens) => current.resolve(tokens)).catch((err) => current.reject(err));
875
+ sendHtml(HTML_SUCCESS);
2843
876
  });
877
+ try {
878
+ await new Promise((resolve, reject) => {
879
+ oauthServer?.once("error", reject);
880
+ oauthServer?.listen(OAUTH_PORT, "127.0.0.1", () => {
881
+ oauthServer?.off("error", reject);
882
+ resolve();
883
+ });
884
+ });
885
+ } catch (error) {
886
+ oauthServer?.close();
887
+ oauthServer = void 0;
888
+ throw error;
889
+ }
2844
890
  return { redirectUri: `http://localhost:${OAUTH_PORT}${CALLBACK_PATH}` };
2845
891
  }
2846
892
  function stopOAuthServer() {
2847
893
  if (oauthServer) {
2848
- oauthServer.stop();
894
+ oauthServer.close();
2849
895
  oauthServer = void 0;
2850
896
  }
2851
897
  }
@@ -2964,8 +1010,8 @@ async function getCodexStatus() {
2964
1010
  async function codexLogout() {
2965
1011
  pendingOAuth = void 0;
2966
1012
  stopOAuthServer();
2967
- if (fs8__default.default.existsSync(CREDENTIALS_PATH2)) {
2968
- fs8__default.default.unlinkSync(CREDENTIALS_PATH2);
1013
+ if (fs4__default.default.existsSync(CREDENTIALS_PATH2)) {
1014
+ fs4__default.default.unlinkSync(CREDENTIALS_PATH2);
2969
1015
  }
2970
1016
  }
2971
1017
 
@@ -3248,125 +1294,6 @@ var connectToUserStreams = async (serverUrl) => {
3248
1294
  return ws;
3249
1295
  };
3250
1296
  var toolCallSchema = zod.z.object({
3251
- tool: zod.z.string().describe("The name of the tool to execute"),
3252
- parameters: zod.z.record(zod.z.string(), zod.z.unknown()).describe("Parameters for the tool")
3253
- });
3254
- zod.z.object({
3255
- tool_calls: zod.z.array(toolCallSchema).min(1, "Provide at least one tool call").max(25, "Maximum of 25 tools allowed in batch").describe("Array of tool calls to execute in parallel")
3256
- });
3257
- var DISALLOWED_TOOLS = /* @__PURE__ */ new Set(["batch"]);
3258
- var MAX_CONCURRENCY = 5;
3259
- var PER_CALL_TIMEOUT = 3e4;
3260
- var MAX_BATCH_SIZE = 25;
3261
- var batchableToolExecutors = {
3262
- deleteFile,
3263
- grep: grepTool,
3264
- glob: globTool,
3265
- listDirectory: list,
3266
- readFile: read_file,
3267
- bash: bashTool,
3268
- stringReplace: apply_patch,
3269
- editFile: editFiles
3270
- };
3271
- function withTimeout(promise, ms) {
3272
- return new Promise((resolve, reject) => {
3273
- const timer = setTimeout(() => {
3274
- reject(new Error(`BATCH_CALL_TIMEOUT: exceeded ${ms}ms`));
3275
- }, ms);
3276
- promise.then((v) => {
3277
- clearTimeout(timer);
3278
- resolve(v);
3279
- }).catch((e) => {
3280
- clearTimeout(timer);
3281
- reject(e);
3282
- });
3283
- });
3284
- }
3285
- async function runWithConcurrencyLimit(tasks, limit) {
3286
- const results = new Array(tasks.length);
3287
- let nextIndex = 0;
3288
- async function worker() {
3289
- while (nextIndex < tasks.length) {
3290
- const idx = nextIndex++;
3291
- results[idx] = await tasks[idx]();
3292
- }
3293
- }
3294
- const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => worker());
3295
- await Promise.all(workers);
3296
- return results;
3297
- }
3298
- var batchTool = async function(input, projectCwd) {
3299
- const { tool_calls } = input;
3300
- const callsToExecute = tool_calls.slice(0, MAX_BATCH_SIZE);
3301
- const discardedCalls = tool_calls.slice(MAX_BATCH_SIZE);
3302
- const executeCall = async (call) => {
3303
- const start = performance.now();
3304
- try {
3305
- if (DISALLOWED_TOOLS.has(call.tool)) {
3306
- return {
3307
- tool: call.tool,
3308
- success: false,
3309
- error: `Tool '${call.tool}' is not allowed in batch. Disallowed tools: ${Array.from(DISALLOWED_TOOLS).join(", ")}`,
3310
- durationMs: 0
3311
- };
3312
- }
3313
- const executor = batchableToolExecutors[call.tool];
3314
- if (!executor) {
3315
- const availableTools = Object.keys(batchableToolExecutors).join(", ");
3316
- return {
3317
- tool: call.tool,
3318
- success: false,
3319
- error: `Tool '${call.tool}' not found. Available tools for batching: ${availableTools}`,
3320
- durationMs: 0
3321
- };
3322
- }
3323
- const result = await withTimeout(executor(call.parameters, projectCwd), PER_CALL_TIMEOUT);
3324
- const durationMs = Math.round(performance.now() - start);
3325
- return {
3326
- tool: call.tool,
3327
- success: result.success !== false,
3328
- result,
3329
- durationMs
3330
- };
3331
- } catch (error) {
3332
- const durationMs = Math.round(performance.now() - start);
3333
- const timedOut = error.message?.includes("BATCH_CALL_TIMEOUT");
3334
- return {
3335
- tool: call.tool,
3336
- success: false,
3337
- error: error.message || String(error),
3338
- durationMs,
3339
- timedOut
3340
- };
3341
- }
3342
- };
3343
- const tasks = callsToExecute.map(
3344
- (call) => () => executeCall(call)
3345
- );
3346
- const results = await runWithConcurrencyLimit(tasks, MAX_CONCURRENCY);
3347
- for (const call of discardedCalls) {
3348
- results.push({
3349
- tool: call.tool,
3350
- success: false,
3351
- error: `Maximum of ${MAX_BATCH_SIZE} tools allowed in batch`,
3352
- durationMs: 0
3353
- });
3354
- }
3355
- const successfulCalls = results.filter((r) => r.success).length;
3356
- const failedCalls = results.length - successfulCalls;
3357
- const outputMessage = failedCalls > 0 ? `Executed ${successfulCalls}/${results.length} tools successfully. ${failedCalls} failed.` : `All ${successfulCalls} tools executed successfully.
3358
-
3359
- Keep using the batch tool for optimal performance in your next response!`;
3360
- return {
3361
- success: failedCalls === 0,
3362
- message: outputMessage,
3363
- totalCalls: results.length,
3364
- successful: successfulCalls,
3365
- failed: failedCalls,
3366
- results
3367
- };
3368
- };
3369
- var toolCallSchema2 = zod.z.object({
3370
1297
  type: zod.z.literal("tool_call"),
3371
1298
  id: zod.z.string(),
3372
1299
  tool: zod.z.string(),
@@ -3389,7 +1316,7 @@ var TOOL_TIMEOUTS = {
3389
1316
  function getTimeoutForTool(tool) {
3390
1317
  return TOOL_TIMEOUTS[tool] ?? DEFAULT_TIMEOUT_MS;
3391
1318
  }
3392
- function withTimeout2(promise, ms, tool) {
1319
+ function withTimeout(promise, ms, tool) {
3393
1320
  return new Promise((resolve, reject) => {
3394
1321
  const timer = setTimeout(() => {
3395
1322
  reject(new ToolTimeoutError(tool, ms));
@@ -3439,7 +1366,7 @@ async function executeTool(toolName, args, projectCwd, executors) {
3439
1366
  }
3440
1367
  try {
3441
1368
  const timeoutMs = getTimeoutForTool(toolName);
3442
- const result = await withTimeout2(executor(args, projectCwd), timeoutMs, toolName);
1369
+ const result = await withTimeout(executor(args, projectCwd), timeoutMs, toolName);
3443
1370
  const durationMs = Math.round(performance.now() - start);
3444
1371
  return {
3445
1372
  success: result?.success !== false,
@@ -3466,7 +1393,7 @@ async function executeTool(toolName, args, projectCwd, executors) {
3466
1393
  }
3467
1394
  }
3468
1395
  function parseToolCall(raw) {
3469
- const result = toolCallSchema2.safeParse(raw);
1396
+ const result = toolCallSchema.safeParse(raw);
3470
1397
  if (!result.success) {
3471
1398
  return new ValidationError(
3472
1399
  `Invalid tool_call payload: ${result.error.issues.map((i) => i.message).join(", ")}`
@@ -3489,15 +1416,15 @@ function getReconnectDelay2() {
3489
1416
  return Math.floor(delay + jitter);
3490
1417
  }
3491
1418
  var toolExecutors = {
3492
- editFile: editFiles,
3493
- deleteFile,
3494
- grep: grepTool,
3495
- glob: globTool,
3496
- listDirectory: list,
3497
- readFile: read_file,
3498
- stringReplace: apply_patch,
3499
- bash: bashTool,
3500
- batch: batchTool
1419
+ editFile: agent.editFiles,
1420
+ deleteFile: agent.deleteFile,
1421
+ grep: agent.grepTool,
1422
+ glob: agent.globTool,
1423
+ listDirectory: agent.list,
1424
+ readFile: agent.read_file,
1425
+ stringReplace: agent.apply_patch,
1426
+ bash: agent.bashTool,
1427
+ batch: agent.batchTool
3501
1428
  };
3502
1429
  function getConnectionStatus(ws) {
3503
1430
  return ws.readyState === WebSocket2__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket2__default.default.OPEN ? "open" : ws.readyState === WebSocket2__default.default.CLOSING ? "closing" : "closed";