amai 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,1934 @@
1
+ #!/usr/bin/env node
2
+ import pc4 from 'picocolors';
3
+ import WebSocket from 'ws';
4
+ import { z } from 'zod';
5
+ import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
6
+ import path9, { dirname } from 'path';
7
+ import fs3, { readdirSync } from 'fs';
8
+ import os2 from 'os';
9
+ import { exec, spawn } from 'child_process';
10
+ import { promisify } from 'util';
11
+ import { Hono } from 'hono';
12
+ import { serve } from '@hono/node-server';
13
+ import { cors } from 'hono/cors';
14
+ import { fileURLToPath } from 'url';
15
+ import readline from 'readline';
16
+
17
+ var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
18
+ var CLIENT_ID = "client_01K4Y8A67H544Z6J8A47E5GJ9A";
19
+ var AMA_DIR = path9.join(os2.homedir(), ".ama");
20
+ var CODE_DIR = path9.join(AMA_DIR, "code");
21
+ var STORAGE_DIR = path9.join(AMA_DIR, "storage");
22
+
23
+ // src/lib/project-registry.ts
24
+ var REGISTRY_FILE = path9.join(AMA_DIR, "projects.json");
25
+ var ProjectRegistry = class {
26
+ projects = /* @__PURE__ */ new Map();
27
+ constructor() {
28
+ this.load();
29
+ }
30
+ load() {
31
+ try {
32
+ if (fs3.existsSync(REGISTRY_FILE)) {
33
+ const data = fs3.readFileSync(REGISTRY_FILE, "utf8");
34
+ const parsed = JSON.parse(data);
35
+ if (!Array.isArray(parsed)) {
36
+ console.error("Invalid project registry format: expected array, got", typeof parsed);
37
+ const backupFile = REGISTRY_FILE + ".backup." + Date.now();
38
+ fs3.copyFileSync(REGISTRY_FILE, backupFile);
39
+ fs3.unlinkSync(REGISTRY_FILE);
40
+ return;
41
+ }
42
+ const projects = parsed;
43
+ this.projects.clear();
44
+ projects.forEach((project) => {
45
+ if (project && typeof project === "object" && project.id && project.cwd) {
46
+ this.projects.set(project.id, project);
47
+ }
48
+ });
49
+ }
50
+ } catch (error) {
51
+ console.error("Failed to load project registry:", error);
52
+ if (fs3.existsSync(REGISTRY_FILE)) {
53
+ try {
54
+ const backupFile = REGISTRY_FILE + ".backup." + Date.now();
55
+ fs3.copyFileSync(REGISTRY_FILE, backupFile);
56
+ fs3.unlinkSync(REGISTRY_FILE);
57
+ console.log("Corrupted registry file backed up and removed. Starting fresh.");
58
+ } catch (backupError) {
59
+ }
60
+ }
61
+ }
62
+ }
63
+ save() {
64
+ try {
65
+ if (!fs3.existsSync(AMA_DIR)) {
66
+ fs3.mkdirSync(AMA_DIR, { recursive: true });
67
+ }
68
+ const projects = Array.from(this.projects.values());
69
+ fs3.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
70
+ } catch (error) {
71
+ console.error("Failed to save project registry:", error);
72
+ }
73
+ }
74
+ register(projectId, cwd, name) {
75
+ const normalizedCwd = path9.normalize(path9.resolve(cwd));
76
+ this.projects.set(projectId, {
77
+ id: projectId,
78
+ cwd: normalizedCwd,
79
+ name: name || path9.basename(normalizedCwd),
80
+ active: true
81
+ });
82
+ this.save();
83
+ }
84
+ unregister(projectId) {
85
+ this.projects.delete(projectId);
86
+ this.save();
87
+ }
88
+ getProjectCwd(projectId) {
89
+ const project = this.projects.get(projectId);
90
+ return project?.cwd || null;
91
+ }
92
+ isPathAllowed(projectId, targetPath) {
93
+ const projectCwd = this.getProjectCwd(projectId);
94
+ if (!projectCwd) {
95
+ return false;
96
+ }
97
+ return isPathWithinProject(targetPath, projectCwd);
98
+ }
99
+ list() {
100
+ return Array.from(this.projects.values());
101
+ }
102
+ getProject(projectId) {
103
+ return this.projects.get(projectId) || null;
104
+ }
105
+ };
106
+ var projectRegistry = new ProjectRegistry();
107
+ function isPathWithinProject(filePath, projectCwd) {
108
+ try {
109
+ const resolved = path9.resolve(projectCwd, filePath);
110
+ const normalized = path9.normalize(resolved);
111
+ const normalizedCwd = path9.normalize(projectCwd);
112
+ return normalized.startsWith(normalizedCwd);
113
+ } catch {
114
+ return false;
115
+ }
116
+ }
117
+
118
+ // src/lib/sandbox.ts
119
+ function validatePath(filePath, projectCwd) {
120
+ if (!projectCwd) {
121
+ return {
122
+ valid: false,
123
+ error: "ACCESS_DENIED: No project context provided"
124
+ };
125
+ }
126
+ try {
127
+ const resolvedPath = path9.resolve(projectCwd, filePath);
128
+ if (!isPathWithinProject(filePath, projectCwd)) {
129
+ return {
130
+ valid: false,
131
+ error: `ACCESS_DENIED: Path "${filePath}" is outside project directory "${projectCwd}"`
132
+ };
133
+ }
134
+ return {
135
+ valid: true,
136
+ resolvedPath
137
+ };
138
+ } catch (error) {
139
+ return {
140
+ valid: false,
141
+ error: `ACCESS_DENIED: Invalid path "${filePath}"`
142
+ };
143
+ }
144
+ }
145
+ function resolveProjectPath(filePath, projectCwd) {
146
+ return path9.resolve(projectCwd, filePath);
147
+ }
148
+
149
+ // src/tools/read-file.ts
150
+ z.object({
151
+ relative_file_path: z.string().describe("The relative path to the file to read."),
152
+ should_read_entire_file: z.boolean().describe("Whether to read the entire file."),
153
+ start_line_one_indexed: z.number().optional().describe(
154
+ "The one-indexed line number to start reading from (inclusive)."
155
+ ),
156
+ end_line_one_indexed: z.number().optional().describe("The one-indexed line number to end reading at (inclusive).")
157
+ });
158
+ var read_file = async function(input, projectCwd) {
159
+ const { relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed } = input;
160
+ try {
161
+ if (!relative_file_path) {
162
+ return {
163
+ success: false,
164
+ message: "Missing required parameter: target_file",
165
+ error: "MISSING_TARGET_FILE"
166
+ };
167
+ }
168
+ if (!should_read_entire_file) {
169
+ if (start_line_one_indexed === void 0 || end_line_one_indexed === void 0) {
170
+ return {
171
+ success: false,
172
+ message: "start_line_one_indexed and end_line_one_indexed are required when should_read_entire_file is false",
173
+ error: "MISSING_LINE_RANGE"
174
+ };
175
+ }
176
+ if (!Number.isInteger(start_line_one_indexed) || start_line_one_indexed < 1) {
177
+ return {
178
+ success: false,
179
+ message: "start_line_one_indexed must be a positive integer (1-indexed)",
180
+ error: "INVALID_START_LINE"
181
+ };
182
+ }
183
+ if (!Number.isInteger(end_line_one_indexed) || end_line_one_indexed < 1) {
184
+ return {
185
+ success: false,
186
+ message: "end_line_one_indexed must be a positive integer (1-indexed)",
187
+ error: "INVALID_END_LINE"
188
+ };
189
+ }
190
+ if (end_line_one_indexed < start_line_one_indexed) {
191
+ return {
192
+ success: false,
193
+ message: "end_line_one_indexed must be greater than or equal to start_line_one_indexed",
194
+ error: "INVALID_LINE_RANGE"
195
+ };
196
+ }
197
+ }
198
+ if (projectCwd) {
199
+ const validation = validatePath(relative_file_path, projectCwd);
200
+ if (!validation.valid) {
201
+ return {
202
+ success: false,
203
+ message: validation.error || "Path validation failed",
204
+ error: "ACCESS_DENIED"
205
+ };
206
+ }
207
+ const absolute_file_path = validation.resolvedPath;
208
+ try {
209
+ const fileStats = await stat(absolute_file_path);
210
+ if (!fileStats.isFile()) {
211
+ return {
212
+ success: false,
213
+ message: `Path is not a file: ${relative_file_path}`,
214
+ error: "NOT_A_FILE"
215
+ };
216
+ }
217
+ } catch (error) {
218
+ if (error?.code === "ENOENT") {
219
+ return {
220
+ success: false,
221
+ message: `File not found: ${relative_file_path}`,
222
+ error: "FILE_NOT_FOUND"
223
+ };
224
+ }
225
+ return {
226
+ success: false,
227
+ message: `Failed to access file: ${relative_file_path}`,
228
+ error: "READ_ERROR"
229
+ };
230
+ }
231
+ try {
232
+ const fileContent = await readFile(absolute_file_path, "utf-8");
233
+ const lines = fileContent.split(/\r?\n/);
234
+ const totalLines = lines.length;
235
+ if (should_read_entire_file) {
236
+ return {
237
+ success: true,
238
+ message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
239
+ content: fileContent,
240
+ totalLines
241
+ };
242
+ }
243
+ const startIndex = start_line_one_indexed - 1;
244
+ if (startIndex >= totalLines) {
245
+ return {
246
+ success: false,
247
+ message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
248
+ error: "INVALID_LINE_RANGE"
249
+ };
250
+ }
251
+ const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
252
+ const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
253
+ const linesRead = normalizedEnd - start_line_one_indexed + 1;
254
+ return {
255
+ success: true,
256
+ message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
257
+ content: selectedLines,
258
+ totalLines
259
+ };
260
+ } catch {
261
+ return {
262
+ success: false,
263
+ message: `Failed to read file: ${relative_file_path}`,
264
+ error: "READ_ERROR"
265
+ };
266
+ }
267
+ } else {
268
+ const absolute_file_path = path9.resolve(relative_file_path);
269
+ try {
270
+ const fileStats = await stat(absolute_file_path);
271
+ if (!fileStats.isFile()) {
272
+ return {
273
+ success: false,
274
+ message: `Path is not a file: ${relative_file_path}`,
275
+ error: "NOT_A_FILE"
276
+ };
277
+ }
278
+ } catch (error) {
279
+ if (error?.code === "ENOENT") {
280
+ return {
281
+ success: false,
282
+ message: `File not found: ${relative_file_path}`,
283
+ error: "FILE_NOT_FOUND"
284
+ };
285
+ }
286
+ return {
287
+ success: false,
288
+ message: `Failed to access file: ${relative_file_path}`,
289
+ error: "READ_ERROR"
290
+ };
291
+ }
292
+ try {
293
+ const fileContent = await readFile(absolute_file_path, "utf-8");
294
+ const lines = fileContent.split(/\r?\n/);
295
+ const totalLines = lines.length;
296
+ if (should_read_entire_file) {
297
+ return {
298
+ success: true,
299
+ message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
300
+ content: fileContent,
301
+ totalLines
302
+ };
303
+ }
304
+ const startIndex = start_line_one_indexed - 1;
305
+ if (startIndex >= totalLines) {
306
+ return {
307
+ success: false,
308
+ message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
309
+ error: "INVALID_LINE_RANGE"
310
+ };
311
+ }
312
+ const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
313
+ const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
314
+ const linesRead = normalizedEnd - start_line_one_indexed + 1;
315
+ return {
316
+ success: true,
317
+ message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
318
+ content: selectedLines,
319
+ totalLines
320
+ };
321
+ } catch {
322
+ return {
323
+ success: false,
324
+ message: `Failed to read file: ${relative_file_path}`,
325
+ error: "READ_ERROR"
326
+ };
327
+ }
328
+ }
329
+ } catch {
330
+ return {
331
+ success: false,
332
+ message: `Failed to read file: ${relative_file_path}`,
333
+ error: "READ_ERROR"
334
+ };
335
+ }
336
+ };
337
+
338
+ // ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/base.js
339
+ var Diff = class {
340
+ diff(oldStr, newStr, options = {}) {
341
+ let callback;
342
+ if (typeof options === "function") {
343
+ callback = options;
344
+ options = {};
345
+ } else if ("callback" in options) {
346
+ callback = options.callback;
347
+ }
348
+ const oldString = this.castInput(oldStr, options);
349
+ const newString = this.castInput(newStr, options);
350
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
351
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
352
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
353
+ }
354
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
355
+ var _a;
356
+ const done = (value) => {
357
+ value = this.postProcess(value, options);
358
+ if (callback) {
359
+ setTimeout(function() {
360
+ callback(value);
361
+ }, 0);
362
+ return void 0;
363
+ } else {
364
+ return value;
365
+ }
366
+ };
367
+ const newLen = newTokens.length, oldLen = oldTokens.length;
368
+ let editLength = 1;
369
+ let maxEditLength = newLen + oldLen;
370
+ if (options.maxEditLength != null) {
371
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
372
+ }
373
+ const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
374
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
375
+ const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
376
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
377
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
378
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
379
+ }
380
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
381
+ const execEditLength = () => {
382
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
383
+ let basePath;
384
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
385
+ if (removePath) {
386
+ bestPath[diagonalPath - 1] = void 0;
387
+ }
388
+ let canAdd = false;
389
+ if (addPath) {
390
+ const addPathNewPos = addPath.oldPos - diagonalPath;
391
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
392
+ }
393
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
394
+ if (!canAdd && !canRemove) {
395
+ bestPath[diagonalPath] = void 0;
396
+ continue;
397
+ }
398
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
399
+ basePath = this.addToPath(addPath, true, false, 0, options);
400
+ } else {
401
+ basePath = this.addToPath(removePath, false, true, 1, options);
402
+ }
403
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
404
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
405
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
406
+ } else {
407
+ bestPath[diagonalPath] = basePath;
408
+ if (basePath.oldPos + 1 >= oldLen) {
409
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
410
+ }
411
+ if (newPos + 1 >= newLen) {
412
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
413
+ }
414
+ }
415
+ }
416
+ editLength++;
417
+ };
418
+ if (callback) {
419
+ (function exec3() {
420
+ setTimeout(function() {
421
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
422
+ return callback(void 0);
423
+ }
424
+ if (!execEditLength()) {
425
+ exec3();
426
+ }
427
+ }, 0);
428
+ })();
429
+ } else {
430
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
431
+ const ret = execEditLength();
432
+ if (ret) {
433
+ return ret;
434
+ }
435
+ }
436
+ }
437
+ }
438
+ addToPath(path15, added, removed, oldPosInc, options) {
439
+ const last = path15.lastComponent;
440
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
441
+ return {
442
+ oldPos: path15.oldPos + oldPosInc,
443
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
444
+ };
445
+ } else {
446
+ return {
447
+ oldPos: path15.oldPos + oldPosInc,
448
+ lastComponent: { count: 1, added, removed, previousComponent: last }
449
+ };
450
+ }
451
+ }
452
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
453
+ const newLen = newTokens.length, oldLen = oldTokens.length;
454
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
455
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
456
+ newPos++;
457
+ oldPos++;
458
+ commonCount++;
459
+ if (options.oneChangePerToken) {
460
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
461
+ }
462
+ }
463
+ if (commonCount && !options.oneChangePerToken) {
464
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
465
+ }
466
+ basePath.oldPos = oldPos;
467
+ return newPos;
468
+ }
469
+ equals(left, right, options) {
470
+ if (options.comparator) {
471
+ return options.comparator(left, right);
472
+ } else {
473
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
474
+ }
475
+ }
476
+ removeEmpty(array) {
477
+ const ret = [];
478
+ for (let i = 0; i < array.length; i++) {
479
+ if (array[i]) {
480
+ ret.push(array[i]);
481
+ }
482
+ }
483
+ return ret;
484
+ }
485
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
486
+ castInput(value, options) {
487
+ return value;
488
+ }
489
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
490
+ tokenize(value, options) {
491
+ return Array.from(value);
492
+ }
493
+ join(chars) {
494
+ return chars.join("");
495
+ }
496
+ postProcess(changeObjects, options) {
497
+ return changeObjects;
498
+ }
499
+ get useLongestToken() {
500
+ return false;
501
+ }
502
+ buildValues(lastComponent, newTokens, oldTokens) {
503
+ const components = [];
504
+ let nextComponent;
505
+ while (lastComponent) {
506
+ components.push(lastComponent);
507
+ nextComponent = lastComponent.previousComponent;
508
+ delete lastComponent.previousComponent;
509
+ lastComponent = nextComponent;
510
+ }
511
+ components.reverse();
512
+ const componentLen = components.length;
513
+ let componentPos = 0, newPos = 0, oldPos = 0;
514
+ for (; componentPos < componentLen; componentPos++) {
515
+ const component = components[componentPos];
516
+ if (!component.removed) {
517
+ if (!component.added && this.useLongestToken) {
518
+ let value = newTokens.slice(newPos, newPos + component.count);
519
+ value = value.map(function(value2, i) {
520
+ const oldValue = oldTokens[oldPos + i];
521
+ return oldValue.length > value2.length ? oldValue : value2;
522
+ });
523
+ component.value = this.join(value);
524
+ } else {
525
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
526
+ }
527
+ newPos += component.count;
528
+ if (!component.added) {
529
+ oldPos += component.count;
530
+ }
531
+ } else {
532
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
533
+ oldPos += component.count;
534
+ }
535
+ }
536
+ return components;
537
+ }
538
+ };
539
+
540
+ // ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/line.js
541
+ var LineDiff = class extends Diff {
542
+ constructor() {
543
+ super(...arguments);
544
+ this.tokenize = tokenize;
545
+ }
546
+ equals(left, right, options) {
547
+ if (options.ignoreWhitespace) {
548
+ if (!options.newlineIsToken || !left.includes("\n")) {
549
+ left = left.trim();
550
+ }
551
+ if (!options.newlineIsToken || !right.includes("\n")) {
552
+ right = right.trim();
553
+ }
554
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
555
+ if (left.endsWith("\n")) {
556
+ left = left.slice(0, -1);
557
+ }
558
+ if (right.endsWith("\n")) {
559
+ right = right.slice(0, -1);
560
+ }
561
+ }
562
+ return super.equals(left, right, options);
563
+ }
564
+ };
565
+ var lineDiff = new LineDiff();
566
+ function diffLines(oldStr, newStr, options) {
567
+ return lineDiff.diff(oldStr, newStr, options);
568
+ }
569
+ function tokenize(value, options) {
570
+ if (options.stripTrailingCr) {
571
+ value = value.replace(/\r\n/g, "\n");
572
+ }
573
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
574
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
575
+ linesAndNewlines.pop();
576
+ }
577
+ for (let i = 0; i < linesAndNewlines.length; i++) {
578
+ const line = linesAndNewlines[i];
579
+ if (i % 2 && !options.newlineIsToken) {
580
+ retLines[retLines.length - 1] += line;
581
+ } else {
582
+ retLines.push(line);
583
+ }
584
+ }
585
+ return retLines;
586
+ }
587
+
588
+ // src/lib/diff.ts
589
+ function calculateDiffStats(oldContent, newContent) {
590
+ const changes = diffLines(oldContent, newContent);
591
+ let linesAdded = 0;
592
+ let linesRemoved = 0;
593
+ for (const change of changes) {
594
+ if (change.added) {
595
+ linesAdded += change.count || 0;
596
+ } else if (change.removed) {
597
+ linesRemoved += change.count || 0;
598
+ }
599
+ }
600
+ return { linesAdded, linesRemoved };
601
+ }
602
+
603
+ // src/tools/apply-patch.ts
604
+ z.object({
605
+ file_path: 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"),
606
+ new_string: z.string().describe("The edited text to replace the old_string (must be different from the old_string)"),
607
+ old_string: 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)")
608
+ });
609
+ var apply_patch = async function(input, projectCwd) {
610
+ const { file_path, new_string, old_string } = input;
611
+ try {
612
+ if (!file_path) {
613
+ return {
614
+ success: false,
615
+ message: "Missing required parameter: file_path",
616
+ error: "MISSING_FILE_PATH"
617
+ };
618
+ }
619
+ if (old_string === void 0 || old_string === null) {
620
+ return {
621
+ success: false,
622
+ message: "Missing required parameter: old_string",
623
+ error: "MISSING_OLD_STRING"
624
+ };
625
+ }
626
+ if (new_string === void 0 || new_string === null) {
627
+ return {
628
+ success: false,
629
+ message: "Missing required parameter: new_string",
630
+ error: "MISSING_NEW_STRING"
631
+ };
632
+ }
633
+ if (old_string === new_string) {
634
+ return {
635
+ success: false,
636
+ message: "old_string and new_string must be different",
637
+ error: "STRINGS_IDENTICAL"
638
+ };
639
+ }
640
+ if (projectCwd) {
641
+ const validation = validatePath(file_path, projectCwd);
642
+ if (!validation.valid) {
643
+ return {
644
+ success: false,
645
+ message: validation.error || "Path validation failed",
646
+ error: "ACCESS_DENIED"
647
+ };
648
+ }
649
+ }
650
+ const basePath = projectCwd || process.cwd();
651
+ const absolute_file_path = resolveProjectPath(file_path, basePath);
652
+ let fileContent;
653
+ try {
654
+ fileContent = await readFile(absolute_file_path, "utf-8");
655
+ } catch (error) {
656
+ if (error?.code === "ENOENT") {
657
+ return {
658
+ success: false,
659
+ message: `File not found: ${file_path}`,
660
+ error: "FILE_NOT_FOUND"
661
+ };
662
+ }
663
+ return {
664
+ success: false,
665
+ message: `Failed to read file: ${file_path}`,
666
+ error: "READ_ERROR"
667
+ };
668
+ }
669
+ if (!fileContent.includes(old_string)) {
670
+ return {
671
+ success: false,
672
+ message: `old_string not found in file: ${file_path}`,
673
+ error: "STRING_NOT_FOUND"
674
+ };
675
+ }
676
+ const occurrences = fileContent.split(old_string).length - 1;
677
+ if (occurrences > 1) {
678
+ return {
679
+ success: false,
680
+ message: `old_string appears ${occurrences} times in the file. It must be unique. Please include more context to make it unique.`,
681
+ error: "STRING_NOT_UNIQUE"
682
+ };
683
+ }
684
+ const newContent = fileContent.replace(old_string, new_string);
685
+ try {
686
+ await writeFile(absolute_file_path, newContent, "utf-8");
687
+ const diffStats = calculateDiffStats(fileContent, newContent);
688
+ return {
689
+ success: true,
690
+ old_string,
691
+ new_string,
692
+ linesAdded: diffStats.linesAdded,
693
+ linesRemoved: diffStats.linesRemoved,
694
+ message: `Successfully replaced string in file: ${file_path}`
695
+ };
696
+ } catch (error) {
697
+ return {
698
+ success: false,
699
+ message: `Failed to write to file: ${file_path}`,
700
+ error: "WRITE_ERROR"
701
+ };
702
+ }
703
+ } catch (error) {
704
+ return {
705
+ success: false,
706
+ message: `Unexpected error: ${error.message}`,
707
+ error: "UNEXPECTED_ERROR"
708
+ };
709
+ }
710
+ };
711
+ z.object({
712
+ target_file: z.string().describe("The relative path to the file to modify. The tool will create any directories in the path that don't exist"),
713
+ content: z.string().describe("The content to write to the file"),
714
+ providedNewFile: z.boolean().describe("The new file content to write to the file").optional()
715
+ });
716
+ var editFiles = async function(input, projectCwd) {
717
+ const { target_file, content, providedNewFile } = input;
718
+ try {
719
+ if (projectCwd) {
720
+ const validation = validatePath(target_file, projectCwd);
721
+ if (!validation.valid) {
722
+ return {
723
+ success: false,
724
+ error: validation.error || "Path validation failed",
725
+ message: `Failed to edit file: ${target_file}`
726
+ };
727
+ }
728
+ }
729
+ const basePath = projectCwd || process.cwd();
730
+ const filePath = resolveProjectPath(target_file, basePath);
731
+ const dirPath = path9.dirname(filePath);
732
+ await mkdir(dirPath, { recursive: true });
733
+ let isNewFile = providedNewFile;
734
+ let existingContent = "";
735
+ if (isNewFile === void 0) {
736
+ try {
737
+ existingContent = await fs3.promises.readFile(filePath, "utf-8");
738
+ isNewFile = false;
739
+ } catch (error) {
740
+ isNewFile = true;
741
+ }
742
+ } else if (!isNewFile) {
743
+ try {
744
+ existingContent = await fs3.promises.readFile(filePath, "utf-8");
745
+ } catch (error) {
746
+ isNewFile = true;
747
+ }
748
+ }
749
+ await fs3.promises.writeFile(filePath, content);
750
+ const diffStats = calculateDiffStats(existingContent, content);
751
+ if (isNewFile) {
752
+ return {
753
+ success: true,
754
+ isNewFile: true,
755
+ old_string: "",
756
+ new_string: content,
757
+ message: `Created new file: ${target_file}`,
758
+ linesAdded: diffStats.linesAdded,
759
+ linesRemoved: diffStats.linesRemoved
760
+ };
761
+ } else {
762
+ return {
763
+ success: true,
764
+ isNewFile: false,
765
+ old_string: existingContent,
766
+ new_string: content,
767
+ message: `Modified file: ${target_file}`,
768
+ linesAdded: diffStats.linesAdded,
769
+ linesRemoved: diffStats.linesRemoved
770
+ };
771
+ }
772
+ } catch (error) {
773
+ return {
774
+ success: false,
775
+ error: error instanceof Error ? error.message : "Unknown error",
776
+ message: `Failed to edit file: ${target_file}`
777
+ };
778
+ }
779
+ };
780
+ z.object({
781
+ path: z.string().describe("Relative file path to delete")
782
+ });
783
+ var deleteFile = async function(input, projectCwd) {
784
+ const { path: realPath } = input;
785
+ if (!realPath) {
786
+ return {
787
+ success: false,
788
+ message: "Missing required parameter: path",
789
+ error: "MISSING_PATH"
790
+ };
791
+ }
792
+ if (projectCwd) {
793
+ const validation = validatePath(realPath, projectCwd);
794
+ if (!validation.valid) {
795
+ return {
796
+ success: false,
797
+ message: validation.error || "Path validation failed",
798
+ error: "ACCESS_DENIED"
799
+ };
800
+ }
801
+ }
802
+ try {
803
+ const basePath = projectCwd || process.cwd();
804
+ const absolute_file_path = resolveProjectPath(realPath, basePath);
805
+ if (!absolute_file_path) {
806
+ return {
807
+ success: false,
808
+ message: "Invalid file path",
809
+ error: "INVALID_FILE_PATH"
810
+ };
811
+ }
812
+ const originalContent = await readFile(absolute_file_path);
813
+ if (originalContent === void 0) {
814
+ return {
815
+ success: false,
816
+ message: `Failed to read file before deletion: ${realPath}`,
817
+ error: "READ_ERROR"
818
+ };
819
+ }
820
+ const deleteResult = await unlink(absolute_file_path).catch(() => {
821
+ return {
822
+ success: false,
823
+ message: `Failed to read file before deletion: ${realPath}`,
824
+ error: "DELETE_ERROR"
825
+ };
826
+ });
827
+ if (!deleteResult?.success) {
828
+ return {
829
+ success: false,
830
+ message: `Failed to delete file before deletion: ${realPath}`,
831
+ error: "DELETE_ERROR"
832
+ };
833
+ }
834
+ return {
835
+ success: true,
836
+ message: `Successfully deleted file: ${realPath}`,
837
+ content: originalContent
838
+ };
839
+ } catch (error) {
840
+ return {
841
+ success: false,
842
+ message: `Failed to delete file: ${realPath}`,
843
+ error: "DELETE_ERROR"
844
+ };
845
+ }
846
+ };
847
+ z.object({
848
+ query: z.string().describe("The regex pattern to search for"),
849
+ options: z.object({
850
+ includePattern: z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts")'),
851
+ excludePattern: z.string().optional().describe("Glob pattern for files to exclude"),
852
+ caseSensitive: z.boolean().optional().describe("Whether the search should be case sensitive")
853
+ })
854
+ });
855
+ var execAsync = promisify(exec);
856
+ var grepTool = async function(input, projectCwd) {
857
+ const { query, options } = input;
858
+ try {
859
+ const { includePattern, excludePattern: excludePattern2, caseSensitive } = options || {};
860
+ const searchDir = projectCwd || process.cwd();
861
+ if (projectCwd && !path9.isAbsolute(projectCwd)) {
862
+ return {
863
+ success: false,
864
+ message: "Invalid project directory",
865
+ error: "INVALID_PROJECT_DIR"
866
+ };
867
+ }
868
+ let command = `rg -n --with-filename "${query}"`;
869
+ if (caseSensitive) {
870
+ command += " -i";
871
+ }
872
+ if (includePattern) {
873
+ command += ` --glob "${includePattern}"`;
874
+ }
875
+ if (excludePattern2) {
876
+ command += ` --glob "!${excludePattern2}"`;
877
+ }
878
+ command += ` --max-count 50`;
879
+ command += ` "${searchDir}"`;
880
+ const { stdout } = await execAsync(command);
881
+ const rawMatches = stdout.trim().split("\n").filter((line) => line.length > 0);
882
+ const detailedMatches = [];
883
+ const matches = [];
884
+ for (const rawMatch of rawMatches) {
885
+ const colonIndex = rawMatch.indexOf(":");
886
+ const secondColonIndex = rawMatch.indexOf(":", colonIndex + 1);
887
+ if (colonIndex > 0 && secondColonIndex > colonIndex) {
888
+ const file = rawMatch.substring(0, colonIndex);
889
+ const lineNumber = parseInt(rawMatch.substring(colonIndex + 1, secondColonIndex), 10);
890
+ let content = rawMatch.substring(secondColonIndex + 1);
891
+ if (content.length > 250) {
892
+ content = content.substring(0, 250) + "...";
893
+ }
894
+ detailedMatches.push({
895
+ file,
896
+ lineNumber,
897
+ content
898
+ });
899
+ matches.push(`${file}:${lineNumber}:${content}`);
900
+ } else {
901
+ matches.push(rawMatch);
902
+ }
903
+ }
904
+ return {
905
+ success: true,
906
+ matches,
907
+ detailedMatches,
908
+ query,
909
+ matchCount: matches.length,
910
+ message: `Found ${matches.length} matches for pattern: ${query}`
911
+ };
912
+ } catch (error) {
913
+ return {
914
+ success: false,
915
+ message: error?.message || String(error),
916
+ error: "GREP_EXEC_ERROR"
917
+ };
918
+ }
919
+ };
920
+ z.object({
921
+ pattern: z.string().describe('Glob pattern (e.g., "**/*.js")'),
922
+ path: z.string().optional().describe("Relative directory path to search in")
923
+ });
924
+ var globTool = async function(input, projectCwd) {
925
+ const { pattern, path: inputPath } = input;
926
+ if (!pattern) {
927
+ return {
928
+ success: false,
929
+ message: "Missing required parameter: pattern",
930
+ error: "MISSING_PATTERN"
931
+ };
932
+ }
933
+ try {
934
+ const basePath = projectCwd || process.cwd();
935
+ const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
936
+ if (projectCwd && inputPath) {
937
+ const validation = validatePath(inputPath, projectCwd);
938
+ if (!validation.valid) {
939
+ return {
940
+ success: false,
941
+ message: validation.error || "Path validation failed",
942
+ error: "ACCESS_DENIED"
943
+ };
944
+ }
945
+ }
946
+ const filesGenerator = glob(pattern, {
947
+ cwd: searchPath
948
+ });
949
+ const files = [];
950
+ for await (const file of filesGenerator) {
951
+ files.push(file);
952
+ }
953
+ const searchLocation = inputPath ? ` in "${inputPath}"` : " in current directory";
954
+ const message = `Found ${files.length} matches for pattern "${pattern}"${searchLocation}`;
955
+ return {
956
+ success: true,
957
+ message,
958
+ content: files
959
+ };
960
+ } catch (error) {
961
+ return {
962
+ success: false,
963
+ message: `Failed to find files matching pattern: ${pattern}`,
964
+ error: "GLOB_ERROR"
965
+ };
966
+ }
967
+ };
968
+ var excludePatterns = [
969
+ "node_modules",
970
+ "dist",
971
+ "build",
972
+ "coverage",
973
+ "logs",
974
+ "tmp"
975
+ ];
976
+ var excludePattern = excludePatterns.join("|");
977
+ z.object({
978
+ path: z.string().optional(),
979
+ recursive: z.boolean().optional().describe("Whether to list files recursively"),
980
+ maxDepth: z.number().optional().describe("Maximum recursion depth (default: unlimited)"),
981
+ pattern: z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
982
+ includeDirectories: z.boolean().optional().describe("Whether to include directories in results (default: true)"),
983
+ includeFiles: z.boolean().optional().describe("Whether to include files in results (default: true)")
984
+ });
985
+ var list = async function(input, projectCwd) {
986
+ const { path: relativePath, recursive, maxDepth, pattern, includeDirectories, includeFiles } = input;
987
+ if (maxDepth !== void 0) {
988
+ if (!Number.isInteger(maxDepth) || maxDepth < 0) {
989
+ return {
990
+ success: false,
991
+ message: "maxDepth must be a non-negative integer",
992
+ error: "INVALID_MAX_DEPTH"
993
+ };
994
+ }
995
+ }
996
+ const includeFilesNormalized = includeFiles ?? true;
997
+ const includeDirectoriesNormalized = includeDirectories ?? true;
998
+ if (!includeFilesNormalized && !includeDirectoriesNormalized) {
999
+ return {
1000
+ success: false,
1001
+ message: "At least one of includeFiles or includeDirectories must be true",
1002
+ error: "INVALID_INCLUDE_OPTIONS"
1003
+ };
1004
+ }
1005
+ try {
1006
+ const basePath = projectCwd || process.cwd();
1007
+ const absolutePath = relativePath ? resolveProjectPath(relativePath, basePath) : basePath;
1008
+ if (projectCwd && relativePath) {
1009
+ const validation = validatePath(relativePath, projectCwd);
1010
+ if (!validation.valid) {
1011
+ return {
1012
+ success: false,
1013
+ message: validation.error || "Path validation failed",
1014
+ error: "ACCESS_DENIED"
1015
+ };
1016
+ }
1017
+ }
1018
+ try {
1019
+ await access(absolutePath);
1020
+ } catch {
1021
+ return {
1022
+ success: false,
1023
+ message: `File does not exist: ${absolutePath}`,
1024
+ error: "FILE_DOES_NOT_EXIST"
1025
+ };
1026
+ }
1027
+ const isDir = (await stat(absolutePath)).isDirectory();
1028
+ if (!isDir) {
1029
+ return {
1030
+ success: false,
1031
+ message: `File is not a directory: ${absolutePath}`,
1032
+ error: "FILE_IS_NOT_A_DIRECTORY"
1033
+ };
1034
+ }
1035
+ const collected = [];
1036
+ const patternMatcher = (() => {
1037
+ if (!pattern) return null;
1038
+ if (pattern.startsWith(".") && !pattern.includes("*") && !pattern.includes("?")) {
1039
+ return (entryName) => entryName.endsWith(pattern);
1040
+ }
1041
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1042
+ const regex = new RegExp(`^${escaped}$`);
1043
+ return (entryName) => regex.test(entryName);
1044
+ })();
1045
+ const matchPattern = (entryName) => {
1046
+ if (!patternMatcher) return true;
1047
+ return patternMatcher(entryName);
1048
+ };
1049
+ const maxDepthNormalized = recursive ? maxDepth ?? Infinity : 0;
1050
+ const walk = async (currentDir, depth) => {
1051
+ const entries = await readdir(currentDir, { withFileTypes: true });
1052
+ for (const entry of entries) {
1053
+ const entryAbsolutePath = path9.join(currentDir, entry.name);
1054
+ const entryRelativePath = path9.relative(absolutePath, entryAbsolutePath) || ".";
1055
+ if (entry.isDirectory()) {
1056
+ const isExcluded = entry.name.match(excludePattern);
1057
+ if (includeDirectoriesNormalized && matchPattern(entry.name) && !isExcluded) {
1058
+ collected.push({
1059
+ name: entry.name,
1060
+ absolutePath: entryAbsolutePath,
1061
+ relativePath: entryRelativePath,
1062
+ type: "directory"
1063
+ });
1064
+ }
1065
+ if (recursive && depth < maxDepthNormalized && !isExcluded) {
1066
+ await walk(entryAbsolutePath, depth + 1);
1067
+ }
1068
+ } else if (entry.isFile()) {
1069
+ if (includeFilesNormalized && matchPattern(entry.name) && !entry.name.match(excludePattern)) {
1070
+ collected.push({
1071
+ name: entry.name,
1072
+ absolutePath: entryAbsolutePath,
1073
+ relativePath: entryRelativePath,
1074
+ type: "file"
1075
+ });
1076
+ }
1077
+ }
1078
+ }
1079
+ };
1080
+ await walk(absolutePath, 0);
1081
+ const totalFiles = collected.filter((item) => item.type === "file").length;
1082
+ const totalDirectories = collected.filter((item) => item.type === "directory").length;
1083
+ let message = `Successfully listed ${collected.length} items in: ${relativePath ?? absolutePath}`;
1084
+ if (recursive) {
1085
+ message += ` (recursive${maxDepth !== void 0 ? `, max depth ${maxDepth}` : ""})`;
1086
+ }
1087
+ if (pattern) {
1088
+ message += ` (filtered by pattern: ${pattern})`;
1089
+ }
1090
+ message += ` - ${totalFiles} files, ${totalDirectories} directories`;
1091
+ return {
1092
+ success: true,
1093
+ message,
1094
+ files: collected
1095
+ };
1096
+ } catch (error) {
1097
+ return {
1098
+ success: false,
1099
+ message: `Failed to list files: ${error}`,
1100
+ error: "LIST_ERROR"
1101
+ };
1102
+ }
1103
+ };
1104
+ var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local"];
1105
+ var getContext = (dir, base = dir, allFiles = []) => {
1106
+ const filePath = readdirSync(dir, { withFileTypes: true });
1107
+ for (const file of filePath) {
1108
+ if (ignoreFiles.includes(file.name)) continue;
1109
+ const fullPath = path9.join(dir, file.name);
1110
+ if (file.isDirectory()) {
1111
+ getContext(fullPath, base, allFiles);
1112
+ } else {
1113
+ allFiles.push(path9.relative(base, fullPath));
1114
+ }
1115
+ }
1116
+ return allFiles;
1117
+ };
1118
+ var HOME = os2.homedir();
1119
+ var IDE_PROJECTS_PATHS = {
1120
+ vscode: path9.join(HOME, ".vscode", "projects"),
1121
+ cursor: path9.join(HOME, ".cursor", "projects"),
1122
+ claude: path9.join(HOME, ".claude", "projects")
1123
+ };
1124
+ function getWorkspaceStoragePath(ide) {
1125
+ const platform = os2.platform();
1126
+ const appName = "Cursor" ;
1127
+ if (platform === "darwin") {
1128
+ return path9.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
1129
+ } else if (platform === "win32") {
1130
+ return path9.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
1131
+ } else {
1132
+ return path9.join(HOME, ".config", appName, "User", "workspaceStorage");
1133
+ }
1134
+ }
1135
+ function scanWorkspaceStorage(ide) {
1136
+ const projects = [];
1137
+ const storagePath = getWorkspaceStoragePath();
1138
+ if (!fs3.existsSync(storagePath)) {
1139
+ return projects;
1140
+ }
1141
+ try {
1142
+ const workspaces = fs3.readdirSync(storagePath);
1143
+ for (const workspace of workspaces) {
1144
+ const workspaceJsonPath = path9.join(storagePath, workspace, "workspace.json");
1145
+ if (fs3.existsSync(workspaceJsonPath)) {
1146
+ try {
1147
+ const content = fs3.readFileSync(workspaceJsonPath, "utf-8");
1148
+ const data = JSON.parse(content);
1149
+ if (data.folder && typeof data.folder === "string") {
1150
+ let projectPath = data.folder;
1151
+ if (projectPath.startsWith("file://")) {
1152
+ projectPath = projectPath.replace("file://", "");
1153
+ projectPath = decodeURIComponent(projectPath);
1154
+ }
1155
+ if (fs3.existsSync(projectPath) && fs3.statSync(projectPath).isDirectory()) {
1156
+ projects.push({
1157
+ name: path9.basename(projectPath),
1158
+ path: projectPath,
1159
+ type: ide
1160
+ });
1161
+ }
1162
+ }
1163
+ } catch (err) {
1164
+ console.debug(`Error reading workspace.json at ${workspaceJsonPath}: ${err}`);
1165
+ }
1166
+ }
1167
+ }
1168
+ } catch (err) {
1169
+ console.debug(`Error scanning workspaceStorage for ${ide}: ${err}`);
1170
+ }
1171
+ return projects;
1172
+ }
1173
+ var scanIdeProjects = async () => {
1174
+ try {
1175
+ const allProjects = [];
1176
+ const seenPaths = /* @__PURE__ */ new Set();
1177
+ const addProject = (projectPath, ide) => {
1178
+ try {
1179
+ const resolvedPath = fs3.realpathSync(projectPath);
1180
+ if (fs3.existsSync(resolvedPath) && fs3.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1181
+ const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
1182
+ try {
1183
+ return fs3.realpathSync(ideDir) === resolvedPath;
1184
+ } catch {
1185
+ return false;
1186
+ }
1187
+ });
1188
+ if (!isIdeProjectsDir) {
1189
+ seenPaths.add(resolvedPath);
1190
+ allProjects.push({
1191
+ name: path9.basename(resolvedPath),
1192
+ path: resolvedPath,
1193
+ type: ide
1194
+ });
1195
+ }
1196
+ }
1197
+ } catch {
1198
+ }
1199
+ };
1200
+ const cursorProjects = scanWorkspaceStorage("cursor");
1201
+ for (const project of cursorProjects) {
1202
+ addProject(project.path, "cursor");
1203
+ }
1204
+ for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
1205
+ if (ide === "cursor") continue;
1206
+ if (fs3.existsSync(dirPath)) {
1207
+ const projects = fs3.readdirSync(dirPath);
1208
+ projects.forEach((project) => {
1209
+ const projectPath = path9.join(dirPath, project);
1210
+ try {
1211
+ const stats = fs3.lstatSync(projectPath);
1212
+ let actualPath = null;
1213
+ if (stats.isSymbolicLink()) {
1214
+ actualPath = fs3.realpathSync(projectPath);
1215
+ } else if (stats.isFile()) {
1216
+ try {
1217
+ let content = fs3.readFileSync(projectPath, "utf-8").trim();
1218
+ if (content.startsWith("~/") || content === "~") {
1219
+ content = content.replace(/^~/, HOME);
1220
+ }
1221
+ const resolvedContent = path9.isAbsolute(content) ? content : path9.resolve(path9.dirname(projectPath), content);
1222
+ if (fs3.existsSync(resolvedContent) && fs3.statSync(resolvedContent).isDirectory()) {
1223
+ actualPath = fs3.realpathSync(resolvedContent);
1224
+ }
1225
+ } catch {
1226
+ return;
1227
+ }
1228
+ } else if (stats.isDirectory()) {
1229
+ actualPath = fs3.realpathSync(projectPath);
1230
+ }
1231
+ if (actualPath) {
1232
+ addProject(actualPath, ide);
1233
+ }
1234
+ } catch {
1235
+ }
1236
+ });
1237
+ }
1238
+ }
1239
+ return allProjects;
1240
+ } catch (error) {
1241
+ console.debug(`Error scanning IDE projects: ${error}`);
1242
+ return [];
1243
+ }
1244
+ };
1245
+ var wsConnection = null;
1246
+ var startHttpServer = (connection) => {
1247
+ if (connection) {
1248
+ wsConnection = connection;
1249
+ }
1250
+ const app = new Hono();
1251
+ app.use(cors());
1252
+ app.post("/daemon/status/stream", (c) => {
1253
+ const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
1254
+ return c.json({ connected: status === "open" });
1255
+ });
1256
+ app.get("context", async (c) => {
1257
+ const context = getContext(process.cwd());
1258
+ return c.body(JSON.stringify(context));
1259
+ });
1260
+ app.get("/ide-projects", async (c) => {
1261
+ try {
1262
+ const projects = await scanIdeProjects();
1263
+ if (!projects) {
1264
+ return c.json({ error: "No projects found" }, 500);
1265
+ }
1266
+ return c.json({ projects });
1267
+ } catch (error) {
1268
+ return c.json({ error: "Failed to scan IDE projects" }, 500);
1269
+ }
1270
+ });
1271
+ app.post("/projects/register", async (c) => {
1272
+ try {
1273
+ const { projectId, cwd, name } = await c.req.json();
1274
+ if (!projectId || !cwd) {
1275
+ return c.json({ error: "projectId and cwd are required" }, 400);
1276
+ }
1277
+ projectRegistry.register(projectId, cwd, name);
1278
+ return c.json({ success: true, projectId, cwd });
1279
+ } catch (error) {
1280
+ return c.json({ error: error.message || "Failed to register project" }, 500);
1281
+ }
1282
+ });
1283
+ app.post("/revert", async (c) => {
1284
+ try {
1285
+ const { filePath, oldString, newString, projectCwd } = await c.req.json();
1286
+ if (!filePath || oldString === void 0) {
1287
+ return c.json({ error: "filePath and oldString required" }, 400);
1288
+ }
1289
+ let resolved;
1290
+ if (projectCwd) {
1291
+ resolved = path9.isAbsolute(filePath) ? filePath : path9.resolve(projectCwd, filePath);
1292
+ const normalizedResolved = path9.normalize(resolved);
1293
+ const normalizedCwd = path9.normalize(projectCwd);
1294
+ if (!normalizedResolved.startsWith(normalizedCwd)) {
1295
+ return c.json({ error: "Path is outside project directory" }, 403);
1296
+ }
1297
+ } else {
1298
+ resolved = path9.isAbsolute(filePath) ? filePath : path9.join(process.cwd(), filePath);
1299
+ }
1300
+ let currentContent;
1301
+ try {
1302
+ currentContent = await readFile(resolved, "utf-8");
1303
+ } catch (error) {
1304
+ if (error?.code === "ENOENT") {
1305
+ return c.json({ error: `File not found: ${filePath}` }, 404);
1306
+ }
1307
+ return c.json({ error: `Failed to read file: ${error.message}` }, 500);
1308
+ }
1309
+ let finalContent;
1310
+ if (newString && newString !== oldString) {
1311
+ if (!currentContent.includes(newString)) {
1312
+ return c.json({ error: "Cannot revert: the new content is not found in the current file. The file may have been modified." }, 400);
1313
+ }
1314
+ const occurrences = currentContent.split(newString).length - 1;
1315
+ if (occurrences > 1) {
1316
+ return c.json({ error: "Cannot revert: the new content appears multiple times in the file" }, 400);
1317
+ }
1318
+ finalContent = currentContent.replace(newString, oldString);
1319
+ } else {
1320
+ finalContent = oldString;
1321
+ }
1322
+ await writeFile(resolved, finalContent, "utf-8");
1323
+ return c.json({ success: true });
1324
+ } catch (error) {
1325
+ return c.json({ error: error.message }, 500);
1326
+ }
1327
+ });
1328
+ app.get("/projects", (c) => {
1329
+ const projects = projectRegistry.list();
1330
+ return c.json({ projects });
1331
+ });
1332
+ app.get("/projects/:projectId", (c) => {
1333
+ const projectId = c.req.param("projectId");
1334
+ const project = projectRegistry.getProject(projectId);
1335
+ if (!project) {
1336
+ return c.json({ error: "Project not found" }, 404);
1337
+ }
1338
+ return c.json({ project });
1339
+ });
1340
+ app.delete("/projects/:projectId", (c) => {
1341
+ const projectId = c.req.param("projectId");
1342
+ projectRegistry.unregister(projectId);
1343
+ return c.json({ success: true });
1344
+ });
1345
+ serve({ fetch: app.fetch, port: 3456 });
1346
+ };
1347
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1348
+ var CREDENTIALS_DIR = path9.join(os2.homedir(), ".ama");
1349
+ var CREDENTIALS_PATH = path9.join(CREDENTIALS_DIR, "credentials.json");
1350
+ function isAuthenticated() {
1351
+ try {
1352
+ if (!fs3.existsSync(CREDENTIALS_PATH)) {
1353
+ return false;
1354
+ }
1355
+ const raw = fs3.readFileSync(CREDENTIALS_PATH, "utf8");
1356
+ const data = JSON.parse(raw);
1357
+ return Boolean(data && data.access_token);
1358
+ } catch {
1359
+ return false;
1360
+ }
1361
+ }
1362
+ function saveTokens(tokens) {
1363
+ try {
1364
+ if (!fs3.existsSync(CREDENTIALS_DIR)) {
1365
+ fs3.mkdirSync(CREDENTIALS_DIR, { recursive: true });
1366
+ }
1367
+ fs3.writeFileSync(
1368
+ CREDENTIALS_PATH,
1369
+ JSON.stringify(tokens, null, 2),
1370
+ "utf8"
1371
+ );
1372
+ } catch (error) {
1373
+ console.error(pc4.red("Failed to save credentials"), error);
1374
+ }
1375
+ }
1376
+ function logout() {
1377
+ try {
1378
+ if (fs3.existsSync(CREDENTIALS_PATH)) {
1379
+ fs3.unlinkSync(CREDENTIALS_PATH);
1380
+ }
1381
+ } catch (error) {
1382
+ console.error(pc4.red("Failed to logout"), error);
1383
+ }
1384
+ }
1385
+ function getTokens() {
1386
+ if (!fs3.existsSync(CREDENTIALS_PATH)) {
1387
+ return null;
1388
+ }
1389
+ const raw = fs3.readFileSync(CREDENTIALS_PATH, "utf8");
1390
+ const data = JSON.parse(raw);
1391
+ return data;
1392
+ }
1393
+ async function authorizeDevice() {
1394
+ const response = await fetch(
1395
+ "https://api.workos.com/user_management/authorize/device",
1396
+ {
1397
+ method: "POST",
1398
+ headers: {
1399
+ "Content-Type": "application/x-www-form-urlencoded"
1400
+ },
1401
+ body: new URLSearchParams({
1402
+ client_id: CLIENT_ID
1403
+ })
1404
+ }
1405
+ );
1406
+ const data = await response.json();
1407
+ return data;
1408
+ }
1409
+ async function pollForTokens({
1410
+ clientId,
1411
+ deviceCode,
1412
+ expiresIn = 300,
1413
+ interval = 5
1414
+ }) {
1415
+ const start = Date.now();
1416
+ while (true) {
1417
+ if (Date.now() - start > expiresIn * 1e3) {
1418
+ throw new Error("Authorization timed out");
1419
+ }
1420
+ const response = await fetch(
1421
+ "https://api.workos.com/user_management/authenticate",
1422
+ {
1423
+ method: "POST",
1424
+ headers: {
1425
+ "Content-Type": "application/x-www-form-urlencoded"
1426
+ },
1427
+ body: new URLSearchParams({
1428
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1429
+ device_code: deviceCode,
1430
+ client_id: clientId
1431
+ })
1432
+ }
1433
+ );
1434
+ const data = await response.json();
1435
+ if (response.ok) {
1436
+ return data;
1437
+ }
1438
+ switch (data.error) {
1439
+ case "authorization_pending":
1440
+ await sleep(interval * 1e3);
1441
+ break;
1442
+ case "slow_down":
1443
+ interval += 1;
1444
+ await sleep(interval * 1e3);
1445
+ break;
1446
+ case "access_denied":
1447
+ case "expired_token":
1448
+ throw new Error("Authorization failed");
1449
+ default:
1450
+ throw new Error("Authorization failed");
1451
+ }
1452
+ }
1453
+ }
1454
+ async function login() {
1455
+ try {
1456
+ const device = await authorizeDevice();
1457
+ console.log(pc4.bold("To sign in, follow these steps:"));
1458
+ if (device.verification_uri_complete) {
1459
+ console.log(
1460
+ ` 1. Open this URL in your browser: ${pc4.cyan(
1461
+ device.verification_uri_complete
1462
+ )}`
1463
+ );
1464
+ } else {
1465
+ console.log(
1466
+ ` 1. Open this URL in your browser: ${pc4.cyan(
1467
+ device.verification_uri
1468
+ )}`
1469
+ );
1470
+ console.log(
1471
+ ` 2. Enter this code when prompted: ${pc4.bold(device.user_code)}`
1472
+ );
1473
+ }
1474
+ console.log(" 3. Come back here; we will detect when you finish logging in.");
1475
+ console.log();
1476
+ console.log(
1477
+ pc4.gray(
1478
+ `Waiting for authorization (expires in ~${Math.round(
1479
+ device.expires_in / 60
1480
+ )} minutes)...`
1481
+ )
1482
+ );
1483
+ const tokens = await pollForTokens({
1484
+ clientId: CLIENT_ID,
1485
+ deviceCode: device.device_code,
1486
+ expiresIn: device.expires_in,
1487
+ interval: device.interval
1488
+ });
1489
+ console.log(pc4.green("Successfully authenticated!"));
1490
+ saveTokens(tokens);
1491
+ return tokens;
1492
+ } catch (error) {
1493
+ console.error(pc4.red(error.message || "Login failed"));
1494
+ throw error;
1495
+ }
1496
+ }
1497
+
1498
+ // src/server.ts
1499
+ var toolExecutors = {
1500
+ editFile: editFiles,
1501
+ deleteFile,
1502
+ grep: grepTool,
1503
+ glob: globTool,
1504
+ listDirectory: list,
1505
+ readFile: read_file,
1506
+ stringReplace: apply_patch
1507
+ };
1508
+ function getConnectionStatus(ws) {
1509
+ return ws.readyState === WebSocket.CONNECTING ? "connecting" : ws.readyState === WebSocket.OPEN ? "open" : ws.readyState === WebSocket.CLOSING ? "closing" : "closed";
1510
+ }
1511
+ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
1512
+ const tokens = getTokens();
1513
+ if (!tokens) {
1514
+ throw new Error("No tokens found");
1515
+ }
1516
+ const wsUrl = `${serverUrl}/agent-streams`;
1517
+ const ws = new WebSocket(wsUrl, {
1518
+ headers: {
1519
+ "Authorization": `Bearer ${tokens.access_token}`
1520
+ }
1521
+ });
1522
+ ws.on("open", () => {
1523
+ console.log(pc4.green("Connected to server agent streams"));
1524
+ });
1525
+ ws.on("message", async (data) => {
1526
+ const message = JSON.parse(data.toString());
1527
+ if (message.type === "tool_call") {
1528
+ console.log(`Executing tool: ${message.tool}${message.projectCwd ? ` (project: ${message.projectCwd})` : ""}`);
1529
+ try {
1530
+ const executor = toolExecutors[message.tool];
1531
+ if (!executor) {
1532
+ throw new Error(`Unknown tool: ${message.tool}`);
1533
+ }
1534
+ const result = await executor(message.args, message.projectCwd);
1535
+ ws.send(JSON.stringify({
1536
+ type: "tool_result",
1537
+ id: message.id,
1538
+ result
1539
+ }));
1540
+ console.log(pc4.green(`Tool completed: ${message.tool}`));
1541
+ } catch (error) {
1542
+ ws.send(JSON.stringify({
1543
+ type: "tool_result",
1544
+ id: message.id,
1545
+ error: error.message
1546
+ }));
1547
+ console.error(pc4.red(`Tool failed: ${message.tool} ${error.message}`));
1548
+ }
1549
+ }
1550
+ });
1551
+ ws.on("close", () => {
1552
+ console.log(pc4.red("Disconnected from server. Reconnecting in 5s..."));
1553
+ setTimeout(() => connectToServer2(serverUrl), 5e3);
1554
+ });
1555
+ ws.on("error", (error) => {
1556
+ console.error(pc4.red(`WebSocket error: ${error.message}`));
1557
+ });
1558
+ return ws;
1559
+ }
1560
+ async function main() {
1561
+ const serverUrl = DEFAULT_SERVER_URL;
1562
+ console.log(pc4.green("Starting local ama-agent..."));
1563
+ console.log(pc4.gray(`Connecting to server at ${serverUrl}`));
1564
+ const connection = connectToServer2(serverUrl);
1565
+ startHttpServer(connection);
1566
+ }
1567
+ var execAsync2 = promisify(exec);
1568
+ var CODE_SERVER_VERSION = "4.96.4";
1569
+ function getPlatformInfo() {
1570
+ const platform = process.platform;
1571
+ const arch = process.arch;
1572
+ if (platform === "darwin") {
1573
+ return {
1574
+ platform: "macos",
1575
+ arch: arch === "arm64" ? "arm64" : "amd64",
1576
+ ext: "tar.gz"
1577
+ };
1578
+ } else if (platform === "linux") {
1579
+ return {
1580
+ platform: "linux",
1581
+ arch: arch === "arm64" ? "arm64" : "amd64",
1582
+ ext: "tar.gz"
1583
+ };
1584
+ } else {
1585
+ throw new Error(`Unsupported platform: ${platform}`);
1586
+ }
1587
+ }
1588
+ function getDownloadUrl() {
1589
+ const { platform, arch, ext } = getPlatformInfo();
1590
+ return `https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-${platform}-${arch}.${ext}`;
1591
+ }
1592
+ function getCodeServerDir() {
1593
+ const { platform, arch } = getPlatformInfo();
1594
+ return path9.join(CODE_DIR, `code-server-${CODE_SERVER_VERSION}-${platform}-${arch}`);
1595
+ }
1596
+ function getCodeServerBin() {
1597
+ return path9.join(getCodeServerDir(), "bin", "code-server");
1598
+ }
1599
+ function isCodeServerInstalled() {
1600
+ const binPath = getCodeServerBin();
1601
+ return fs3.existsSync(binPath);
1602
+ }
1603
+ async function installCodeServer() {
1604
+ const { ext } = getPlatformInfo();
1605
+ const downloadUrl = getDownloadUrl();
1606
+ const tarballPath = path9.join(AMA_DIR, `code-server.${ext}`);
1607
+ if (!fs3.existsSync(AMA_DIR)) {
1608
+ fs3.mkdirSync(AMA_DIR, { recursive: true });
1609
+ }
1610
+ if (!fs3.existsSync(CODE_DIR)) {
1611
+ fs3.mkdirSync(CODE_DIR, { recursive: true });
1612
+ }
1613
+ if (!fs3.existsSync(STORAGE_DIR)) {
1614
+ fs3.mkdirSync(STORAGE_DIR, { recursive: true });
1615
+ }
1616
+ console.log(pc4.cyan(`Downloading code-server v${CODE_SERVER_VERSION}...`));
1617
+ console.log(pc4.gray(downloadUrl));
1618
+ const response = await fetch(downloadUrl);
1619
+ if (!response.ok) {
1620
+ throw new Error(`Failed to download code-server: ${response.statusText}`);
1621
+ }
1622
+ const buffer = await response.arrayBuffer();
1623
+ await fs3.promises.writeFile(tarballPath, Buffer.from(buffer));
1624
+ console.log(pc4.cyan("Extracting code-server..."));
1625
+ await execAsync2(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
1626
+ await fs3.promises.unlink(tarballPath);
1627
+ const binPath = getCodeServerBin();
1628
+ if (fs3.existsSync(binPath)) {
1629
+ await fs3.promises.chmod(binPath, 493);
1630
+ }
1631
+ console.log(pc4.green("\u2713 code-server installed successfully"));
1632
+ }
1633
+ async function killExistingCodeServer() {
1634
+ try {
1635
+ if (process.platform === "win32") {
1636
+ await execAsync2("netstat -ano | findstr :8081 | findstr LISTENING").then(async ({ stdout }) => {
1637
+ const pid = stdout.trim().split(/\s+/).pop();
1638
+ if (pid) await execAsync2(`taskkill /PID ${pid} /F`);
1639
+ }).catch(() => {
1640
+ });
1641
+ } else {
1642
+ await execAsync2("lsof -ti:8081").then(async ({ stdout }) => {
1643
+ const pid = stdout.trim();
1644
+ if (pid) await execAsync2(`kill -9 ${pid}`);
1645
+ }).catch(() => {
1646
+ });
1647
+ }
1648
+ } catch {
1649
+ }
1650
+ }
1651
+ async function startCodeServer(cwd) {
1652
+ const binPath = getCodeServerBin();
1653
+ const workDir = cwd || process.cwd();
1654
+ if (!fs3.existsSync(binPath)) {
1655
+ throw new Error("code-server is not installed. Run installCodeServer() first.");
1656
+ }
1657
+ await killExistingCodeServer();
1658
+ const workspaceStoragePath = path9.join(STORAGE_DIR, "User", "workspaceStorage");
1659
+ path9.join(STORAGE_DIR, "User", "globalStorage");
1660
+ try {
1661
+ if (fs3.existsSync(workspaceStoragePath)) {
1662
+ await fs3.promises.rm(workspaceStoragePath, { recursive: true, force: true });
1663
+ }
1664
+ const stateDbPath = path9.join(STORAGE_DIR, "User", "globalStorage", "state.vscdb");
1665
+ if (fs3.existsSync(stateDbPath)) {
1666
+ await fs3.promises.unlink(stateDbPath);
1667
+ }
1668
+ } catch {
1669
+ }
1670
+ console.log(pc4.cyan(`Starting code-server in ${workDir}...`));
1671
+ const codeServer = spawn(
1672
+ binPath,
1673
+ [
1674
+ "--port",
1675
+ "8081",
1676
+ "--host",
1677
+ "0.0.0.0",
1678
+ "--auth",
1679
+ "none",
1680
+ "--user-data-dir",
1681
+ STORAGE_DIR,
1682
+ "--disable-workspace-trust",
1683
+ workDir
1684
+ ],
1685
+ {
1686
+ stdio: ["ignore", "pipe", "pipe"]
1687
+ }
1688
+ );
1689
+ console.log(pc4.green(`\u2713 code-server running at http://localhost:8081/?folder=${encodeURIComponent(workDir)}`));
1690
+ return codeServer;
1691
+ }
1692
+ var __filename$1 = fileURLToPath(import.meta.url);
1693
+ var __dirname$1 = dirname(__filename$1);
1694
+ var DAEMON_PID_FILE = path9.join(AMA_DIR, "daemon.pid");
1695
+ var DAEMON_LOG_FILE = path9.join(AMA_DIR, "daemon.log");
1696
+ function isDaemonRunning() {
1697
+ if (!fs3.existsSync(DAEMON_PID_FILE)) {
1698
+ return false;
1699
+ }
1700
+ try {
1701
+ const pid = Number(fs3.readFileSync(DAEMON_PID_FILE, "utf8"));
1702
+ process.kill(pid, 0);
1703
+ return true;
1704
+ } catch {
1705
+ return false;
1706
+ }
1707
+ }
1708
+ function stopDaemon() {
1709
+ if (!fs3.existsSync(DAEMON_PID_FILE)) {
1710
+ return false;
1711
+ }
1712
+ try {
1713
+ const pid = Number(fs3.readFileSync(DAEMON_PID_FILE, "utf8"));
1714
+ process.kill(pid, "SIGTERM");
1715
+ fs3.unlinkSync(DAEMON_PID_FILE);
1716
+ return true;
1717
+ } catch (error) {
1718
+ return false;
1719
+ }
1720
+ }
1721
+ function startDaemon() {
1722
+ if (!fs3.existsSync(AMA_DIR)) {
1723
+ fs3.mkdirSync(AMA_DIR, { recursive: true });
1724
+ }
1725
+ if (isDaemonRunning()) {
1726
+ stopDaemon();
1727
+ }
1728
+ const daemonScript = path9.join(__dirname$1, "lib", "daemon-entry.js");
1729
+ if (!fs3.existsSync(daemonScript)) {
1730
+ throw new Error(`Daemon entry script not found at: ${daemonScript}. Please rebuild the project.`);
1731
+ }
1732
+ const logFd = fs3.openSync(DAEMON_LOG_FILE, "a");
1733
+ const daemon = spawn(process.execPath, [daemonScript], {
1734
+ detached: true,
1735
+ stdio: ["ignore", logFd, logFd],
1736
+ env: { ...process.env, AMA_DAEMON: "1" },
1737
+ cwd: process.cwd()
1738
+ });
1739
+ daemon.unref();
1740
+ fs3.writeFileSync(DAEMON_PID_FILE, String(daemon.pid));
1741
+ fs3.closeSync(logFd);
1742
+ }
1743
+ function getDaemonPid() {
1744
+ if (!fs3.existsSync(DAEMON_PID_FILE)) {
1745
+ return null;
1746
+ }
1747
+ try {
1748
+ return Number(fs3.readFileSync(DAEMON_PID_FILE, "utf8"));
1749
+ } catch {
1750
+ return null;
1751
+ }
1752
+ }
1753
+ var VERSION = "0.0.1";
1754
+ var PROJECT_DIR = process.cwd();
1755
+ function promptUser(question) {
1756
+ const rl = readline.createInterface({
1757
+ input: process.stdin,
1758
+ output: process.stdout
1759
+ });
1760
+ return new Promise((resolve) => {
1761
+ rl.question(question, (answer) => {
1762
+ rl.close();
1763
+ resolve(answer.trim());
1764
+ });
1765
+ });
1766
+ }
1767
+ async function startWithCodeServer() {
1768
+ if (!isCodeServerInstalled()) {
1769
+ console.log(pc4.cyan("First run detected. Setting up code-server..."));
1770
+ try {
1771
+ await installCodeServer();
1772
+ } catch (error) {
1773
+ console.error(pc4.red(`Failed to install code-server: ${error.message}`));
1774
+ console.log(pc4.yellow("Continuing without code-server..."));
1775
+ }
1776
+ }
1777
+ if (isCodeServerInstalled()) {
1778
+ try {
1779
+ await startCodeServer(PROJECT_DIR);
1780
+ } catch (error) {
1781
+ console.error(pc4.red(`Failed to start code-server: ${error.message}`));
1782
+ }
1783
+ }
1784
+ main();
1785
+ }
1786
+ var args = process.argv.slice(2);
1787
+ if (args[0] === "--help" || args[0] === "-h") {
1788
+ console.log(`
1789
+ ${pc4.bold("ama cli")} ${pc4.gray(VERSION)}
1790
+
1791
+ Usage: ama [command] [options]
1792
+
1793
+ Commands:
1794
+ login Authorize device
1795
+ logout Log out and remove credentials
1796
+ start Start background daemon (background mode is highly recommended for better performance and stability)
1797
+ stop Stop background daemon
1798
+ status Check daemon status
1799
+ project add <path> Register a project directory
1800
+ project list List registered projects
1801
+ Options:
1802
+ --help, -h Show this help message
1803
+ --logout, -l Log out and remove credentials
1804
+ Environment Variables:
1805
+ SERVER_URL Server URL to connect to
1806
+
1807
+ Example:
1808
+ ama login
1809
+ ama start
1810
+ ama project add /path/to/project
1811
+ ama Start the agent (will prompt for background mode)
1812
+ `);
1813
+ process.exit(0);
1814
+ }
1815
+ if (args[0] === "start") {
1816
+ if (isDaemonRunning()) {
1817
+ console.log(pc4.yellow("Daemon is already running"));
1818
+ process.exit(0);
1819
+ }
1820
+ if (!isAuthenticated()) {
1821
+ console.log(pc4.yellow("Not authenticated. Please log in first."));
1822
+ login().then(() => {
1823
+ console.log(pc4.green("Starting daemon..."));
1824
+ startDaemon();
1825
+ console.log(pc4.green("Daemon started successfully"));
1826
+ process.exit(0);
1827
+ }).catch(() => {
1828
+ console.error(pc4.red("Login failed. Cannot start daemon."));
1829
+ process.exit(1);
1830
+ });
1831
+ } else {
1832
+ startDaemon();
1833
+ console.log(pc4.green(pc4.bold("ama started in background mode")));
1834
+ console.log(pc4.gray(`Tip: You can check status any time with ${pc4.bold("ama status")}`));
1835
+ process.exit(0);
1836
+ }
1837
+ }
1838
+ if (args[0] === "stop") {
1839
+ if (stopDaemon()) {
1840
+ console.log(pc4.green("Daemon stopped successfully"));
1841
+ } else {
1842
+ console.log(pc4.yellow("Daemon was not running"));
1843
+ }
1844
+ process.exit(0);
1845
+ }
1846
+ if (args[0] === "status") {
1847
+ const running = isDaemonRunning();
1848
+ const pid = getDaemonPid();
1849
+ if (running && pid) {
1850
+ console.log(pc4.green(`Daemon is running (PID: ${pid})`));
1851
+ } else {
1852
+ console.log(pc4.yellow("Daemon is not running"));
1853
+ }
1854
+ process.exit(0);
1855
+ }
1856
+ if (args[0] === "project") {
1857
+ if (args[1] === "add") {
1858
+ const projectPath = args[2];
1859
+ if (!projectPath) {
1860
+ console.error(pc4.red("Please provide a project path"));
1861
+ console.log("Usage: ama project add <path>");
1862
+ process.exit(1);
1863
+ }
1864
+ const resolvedPath = path9.resolve(projectPath);
1865
+ if (!fs3.existsSync(resolvedPath)) {
1866
+ console.error(pc4.red(`Path does not exist: ${resolvedPath}`));
1867
+ process.exit(1);
1868
+ }
1869
+ if (!fs3.statSync(resolvedPath).isDirectory()) {
1870
+ console.error(pc4.red(`Path is not a directory: ${resolvedPath}`));
1871
+ process.exit(1);
1872
+ }
1873
+ const projectId = path9.basename(resolvedPath);
1874
+ projectRegistry.register(projectId, resolvedPath);
1875
+ console.log(pc4.green(`Project registered: ${projectId} -> ${resolvedPath}`));
1876
+ process.exit(0);
1877
+ } else if (args[1] === "list") {
1878
+ const projects = projectRegistry.list();
1879
+ if (projects.length === 0) {
1880
+ console.log(pc4.yellow("No projects registered"));
1881
+ } else {
1882
+ console.log(pc4.bold("Registered projects:"));
1883
+ projects.forEach((project) => {
1884
+ console.log(` ${pc4.cyan(project.id)}: ${project.cwd} ${project.active ? pc4.green("(active)") : ""}`);
1885
+ });
1886
+ }
1887
+ process.exit(0);
1888
+ } else {
1889
+ console.error(pc4.red(`Unknown project command: ${args[1]}`));
1890
+ console.log('Use "ama project add <path>" or "ama project list"');
1891
+ process.exit(1);
1892
+ }
1893
+ }
1894
+ if (args[0] === "login" || args[0] === "--login") {
1895
+ login().then(() => process.exit(0)).catch(() => process.exit(1));
1896
+ } else if (args[0] === "logout" || args[0] === "--logout") {
1897
+ logout();
1898
+ console.log(pc4.green("Logged out successfully"));
1899
+ process.exit(0);
1900
+ } else {
1901
+ (async () => {
1902
+ if (!isAuthenticated()) {
1903
+ console.log(pc4.yellow("Not authenticated. Please log in first."));
1904
+ try {
1905
+ await login();
1906
+ } catch {
1907
+ console.error(pc4.red("Login failed. Cannot start server."));
1908
+ process.exit(1);
1909
+ }
1910
+ }
1911
+ if (isDaemonRunning()) {
1912
+ console.log(pc4.yellow('Daemon is already running. Use "ama status" to check its status.'));
1913
+ process.exit(0);
1914
+ }
1915
+ console.log("");
1916
+ console.log(pc4.bold("How would you like to run ama?"));
1917
+ console.log(pc4.gray("Background mode is highly recommended for better performance and stability."));
1918
+ const answer = await promptUser(
1919
+ pc4.cyan("Run in background? (Y/n): ")
1920
+ );
1921
+ const runInBackground = answer === "" || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
1922
+ if (runInBackground) {
1923
+ console.log(pc4.green("Starting daemon in background..."));
1924
+ startDaemon();
1925
+ console.log(pc4.green("Daemon started successfully!"));
1926
+ console.log(pc4.gray('Use "ama status" to check daemon status.'));
1927
+ console.log(pc4.gray('Use "ama stop" to stop the daemon.'));
1928
+ process.exit(0);
1929
+ } else {
1930
+ console.log(pc4.yellow("Starting in foreground mode..."));
1931
+ startWithCodeServer();
1932
+ }
1933
+ })();
1934
+ }