antri_cli 1.50.0 → 1.53.0

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.
Files changed (37) hide show
  1. package/dist/cli/shortcuts.d.ts.map +1 -1
  2. package/dist/cli/shortcuts.js +57 -0
  3. package/dist/cli/shortcuts.js.map +1 -1
  4. package/dist/cloud/firestore.d.ts +7 -0
  5. package/dist/cloud/firestore.d.ts.map +1 -1
  6. package/dist/cloud/firestore.js +17 -0
  7. package/dist/cloud/firestore.js.map +1 -1
  8. package/dist/core/agent.d.ts +1 -0
  9. package/dist/core/agent.d.ts.map +1 -1
  10. package/dist/core/agent.js +20 -7
  11. package/dist/core/agent.js.map +1 -1
  12. package/dist/core/config.js +1 -1
  13. package/dist/core/history.d.ts +1 -0
  14. package/dist/core/history.d.ts.map +1 -1
  15. package/dist/core/history.js +3 -0
  16. package/dist/core/history.js.map +1 -1
  17. package/dist/core/sessionManager.d.ts +23 -0
  18. package/dist/core/sessionManager.d.ts.map +1 -0
  19. package/dist/core/sessionManager.js +188 -0
  20. package/dist/core/sessionManager.js.map +1 -0
  21. package/dist/core/tools.d.ts.map +1 -1
  22. package/dist/core/tools.js +421 -0
  23. package/dist/core/tools.js.map +1 -1
  24. package/dist/core/updater.d.ts +1 -1
  25. package/dist/core/updater.js +1 -1
  26. package/dist/desktop/public/app.js +150 -2
  27. package/dist/desktop/public/index.html +16 -2
  28. package/dist/desktop/public/style.css +79 -0
  29. package/dist/desktop/server.d.ts.map +1 -1
  30. package/dist/desktop/server.js +48 -0
  31. package/dist/desktop/server.js.map +1 -1
  32. package/dist/profiles/profileManager.d.ts.map +1 -1
  33. package/dist/profiles/profileManager.js +53 -34
  34. package/dist/profiles/profileManager.js.map +1 -1
  35. package/dist/types.d.ts +16 -0
  36. package/dist/types.d.ts.map +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,188 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ const CHATS_DIR = path.join(os.homedir(), '.antri', 'chats');
5
+ const ACTIVE_SESSION_FILE = path.join(CHATS_DIR, '.active_session');
6
+ export class SessionManager {
7
+ chatsDir;
8
+ activeSessionId = '';
9
+ constructor(customDir) {
10
+ this.chatsDir = customDir || CHATS_DIR;
11
+ this.ensureDirectory();
12
+ this.initActiveSession();
13
+ }
14
+ ensureDirectory() {
15
+ if (!fs.existsSync(this.chatsDir)) {
16
+ fs.mkdirSync(this.chatsDir, { recursive: true });
17
+ }
18
+ }
19
+ initActiveSession() {
20
+ try {
21
+ if (fs.existsSync(ACTIVE_SESSION_FILE)) {
22
+ const id = fs.readFileSync(ACTIVE_SESSION_FILE, 'utf-8').trim();
23
+ if (id && fs.existsSync(this.getSessionFilePath(id))) {
24
+ this.activeSessionId = id;
25
+ return;
26
+ }
27
+ }
28
+ }
29
+ catch { }
30
+ // Pick first existing session or create a default session
31
+ const sessions = this.listSessions();
32
+ if (sessions.length > 0) {
33
+ this.activeSessionId = sessions[0].id;
34
+ }
35
+ else {
36
+ const newSession = this.createSession('New Chat');
37
+ this.activeSessionId = newSession.id;
38
+ }
39
+ this.saveActiveSessionId();
40
+ }
41
+ getSessionFilePath(id) {
42
+ const cleanId = id.replace(/[^a-zA-Z0-9_-]/g, '_');
43
+ return path.join(this.chatsDir, `${cleanId}.json`);
44
+ }
45
+ saveActiveSessionId() {
46
+ try {
47
+ this.ensureDirectory();
48
+ fs.writeFileSync(ACTIVE_SESSION_FILE, this.activeSessionId, 'utf-8');
49
+ }
50
+ catch { }
51
+ }
52
+ getActiveSessionId() {
53
+ return this.activeSessionId;
54
+ }
55
+ setActiveSessionId(id) {
56
+ const session = this.getSession(id);
57
+ if (session) {
58
+ this.activeSessionId = id;
59
+ this.saveActiveSessionId();
60
+ return session;
61
+ }
62
+ return null;
63
+ }
64
+ createSession(title = 'New Chat') {
65
+ this.ensureDirectory();
66
+ const id = `chat_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
67
+ const session = {
68
+ id,
69
+ title,
70
+ createdAt: Date.now(),
71
+ updatedAt: Date.now(),
72
+ messages: [],
73
+ };
74
+ this.saveSession(session);
75
+ this.activeSessionId = id;
76
+ this.saveActiveSessionId();
77
+ return session;
78
+ }
79
+ getSession(id) {
80
+ const filePath = this.getSessionFilePath(id);
81
+ if (fs.existsSync(filePath)) {
82
+ try {
83
+ const raw = fs.readFileSync(filePath, 'utf-8');
84
+ return JSON.parse(raw);
85
+ }
86
+ catch { }
87
+ }
88
+ return null;
89
+ }
90
+ getActiveSession() {
91
+ const session = this.getSession(this.activeSessionId);
92
+ if (session)
93
+ return session;
94
+ return this.createSession('New Chat');
95
+ }
96
+ saveSession(session) {
97
+ this.ensureDirectory();
98
+ session.updatedAt = Date.now();
99
+ const filePath = this.getSessionFilePath(session.id);
100
+ fs.writeFileSync(filePath, JSON.stringify(session, null, 2), 'utf-8');
101
+ }
102
+ addMessageToActiveSession(message) {
103
+ const session = this.getActiveSession();
104
+ session.messages.push({
105
+ ...message,
106
+ timestamp: message.timestamp || Date.now(),
107
+ });
108
+ // Auto-generate title on first user message if title is "New Chat"
109
+ if (session.title === 'New Chat' && message.role === 'user') {
110
+ const cleanPrompt = message.content.replace(/\[Attached File:[^\]]+\]/g, '').trim();
111
+ if (cleanPrompt) {
112
+ session.title = cleanPrompt.length > 35 ? `${cleanPrompt.slice(0, 32)}...` : cleanPrompt;
113
+ }
114
+ }
115
+ this.saveSession(session);
116
+ return session;
117
+ }
118
+ listSessions() {
119
+ this.ensureDirectory();
120
+ try {
121
+ const files = fs.readdirSync(this.chatsDir).filter((f) => f.endsWith('.json') && !f.startsWith('.'));
122
+ const summaries = [];
123
+ for (const file of files) {
124
+ try {
125
+ const filePath = path.join(this.chatsDir, file);
126
+ const raw = fs.readFileSync(filePath, 'utf-8');
127
+ const data = JSON.parse(raw);
128
+ if (data && data.id) {
129
+ const lastUserMsg = data.messages?.filter((m) => m.role === 'user').pop();
130
+ summaries.push({
131
+ id: data.id,
132
+ title: data.title || 'Untitled Chat',
133
+ createdAt: data.createdAt || 0,
134
+ updatedAt: data.updatedAt || 0,
135
+ messageCount: data.messages?.length || 0,
136
+ preview: lastUserMsg?.content?.slice(0, 80) || '',
137
+ isActive: data.id === this.activeSessionId,
138
+ });
139
+ }
140
+ }
141
+ catch { }
142
+ }
143
+ return summaries.sort((a, b) => b.updatedAt - a.updatedAt);
144
+ }
145
+ catch {
146
+ return [];
147
+ }
148
+ }
149
+ deleteSession(id) {
150
+ const filePath = this.getSessionFilePath(id);
151
+ try {
152
+ if (fs.existsSync(filePath)) {
153
+ fs.unlinkSync(filePath);
154
+ }
155
+ if (this.activeSessionId === id) {
156
+ const remaining = this.listSessions();
157
+ if (remaining.length > 0) {
158
+ this.activeSessionId = remaining[0].id;
159
+ }
160
+ else {
161
+ const fresh = this.createSession('New Chat');
162
+ this.activeSessionId = fresh.id;
163
+ }
164
+ this.saveActiveSessionId();
165
+ }
166
+ return true;
167
+ }
168
+ catch {
169
+ return false;
170
+ }
171
+ }
172
+ renameSession(id, newTitle) {
173
+ const session = this.getSession(id);
174
+ if (session) {
175
+ session.title = newTitle.trim() || 'Untitled Chat';
176
+ this.saveSession(session);
177
+ return true;
178
+ }
179
+ return false;
180
+ }
181
+ clearActiveSessionMessages() {
182
+ const session = this.getActiveSession();
183
+ session.messages = [];
184
+ this.saveSession(session);
185
+ }
186
+ }
187
+ export const sessionManager = new SessionManager();
188
+ //# sourceMappingURL=sessionManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionManager.js","sourceRoot":"","sources":["../../src/core/sessionManager.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/core/tools.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWzD,eAAO,MAAM,eAAe,aAO1B,CAAC;AAEH,eAAO,MAAM,eAAe,EAAE,cAAc,EA2M3C,CAAC;AAEF,wBAAgB,iBAAiB,IAAI,cAAc,EAAE,CAYpD;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9F,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,uBAAuB,CAAC,CAAoB;gBAExC,UAAU,GAAE,MAAsB;IAIvC,oBAAoB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;WAIhD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAInC,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAmD9E,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAuTvG"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/core/tools.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWzD,eAAO,MAAM,eAAe,aAS1B,CAAC;AAEH,eAAO,MAAM,eAAe,EAAE,cAAc,EAoV3C,CAAC;AAEF,wBAAgB,iBAAiB,IAAI,cAAc,EAAE,CAYpD;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9F,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,uBAAuB,CAAC,CAAoB;gBAExC,UAAU,GAAE,MAAsB;IAIvC,oBAAoB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;WAIhD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAInC,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAmD9E,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CA+lBvG"}
@@ -20,6 +20,8 @@ export const SENSITIVE_TOOLS = new Set([
20
20
  'run_command',
21
21
  'execute_python',
22
22
  'synthesize_skill',
23
+ 'edit_file',
24
+ 'delete_file',
23
25
  ]);
24
26
  export const AVAILABLE_TOOLS = [
25
27
  {
@@ -165,6 +167,143 @@ export const AVAILABLE_TOOLS = [
165
167
  required: ['file_path', 'content'],
166
168
  },
167
169
  },
170
+ {
171
+ name: 'edit_file',
172
+ description: 'Perform targeted, precise surgical edits on an existing file by replacing an exact string or code block with new code, preserving indentation, comments, and structure.',
173
+ parameters: {
174
+ type: 'object',
175
+ properties: {
176
+ file_path: {
177
+ type: 'string',
178
+ description: 'The relative or absolute path of the file to edit.',
179
+ },
180
+ target_content: {
181
+ type: 'string',
182
+ description: 'The exact lines or block of code to be replaced. Must match existing file content exactly.',
183
+ },
184
+ replacement_content: {
185
+ type: 'string',
186
+ description: 'The new code or lines to replace target_content with.',
187
+ },
188
+ allow_multiple: {
189
+ type: 'boolean',
190
+ description: 'Optional. If true, replaces all occurrences of target_content; otherwise errors if multiple are found (default: false).',
191
+ },
192
+ },
193
+ required: ['file_path', 'target_content', 'replacement_content'],
194
+ },
195
+ },
196
+ {
197
+ name: 'create_directory',
198
+ description: 'Recursively create a new directory or folder structure in the workspace.',
199
+ parameters: {
200
+ type: 'object',
201
+ properties: {
202
+ dir_path: {
203
+ type: 'string',
204
+ description: 'The path of the directory to create.',
205
+ },
206
+ },
207
+ required: ['dir_path'],
208
+ },
209
+ },
210
+ {
211
+ name: 'delete_file',
212
+ description: 'Delete a file or directory from the workspace.',
213
+ parameters: {
214
+ type: 'object',
215
+ properties: {
216
+ file_path: {
217
+ type: 'string',
218
+ description: 'The relative or absolute path of the file or directory to delete.',
219
+ },
220
+ },
221
+ required: ['file_path'],
222
+ },
223
+ },
224
+ {
225
+ name: 'find_files',
226
+ description: 'Search for project files matching a glob pattern (e.g. "*.ts", "src/**/*.tsx", "**/*.py") or extension, filtering out node_modules, .git, and build artifacts.',
227
+ parameters: {
228
+ type: 'object',
229
+ properties: {
230
+ pattern: {
231
+ type: 'string',
232
+ description: 'The glob pattern or filename search query (e.g. "*.ts", "package.json", "**/*.css").',
233
+ },
234
+ dir_path: {
235
+ type: 'string',
236
+ description: 'Optional root directory to search within (default: workspace root).',
237
+ },
238
+ max_results: {
239
+ type: 'number',
240
+ description: 'Optional maximum number of matching file paths to return (default: 50).',
241
+ },
242
+ },
243
+ required: ['pattern'],
244
+ },
245
+ },
246
+ {
247
+ name: 'grep_search',
248
+ description: 'Search for exact text or regular expression patterns across workspace files with line numbers and matched line snippets.',
249
+ parameters: {
250
+ type: 'object',
251
+ properties: {
252
+ query: {
253
+ type: 'string',
254
+ description: 'The search term or regular expression pattern to look for.',
255
+ },
256
+ dir_path: {
257
+ type: 'string',
258
+ description: 'Optional directory to search within (default: workspace root).',
259
+ },
260
+ is_regex: {
261
+ type: 'boolean',
262
+ description: 'Optional. If true, treats query as a regular expression (default: false).',
263
+ },
264
+ case_sensitive: {
265
+ type: 'boolean',
266
+ description: 'Optional. If true, performs case-sensitive search (default: false).',
267
+ },
268
+ max_results: {
269
+ type: 'number',
270
+ description: 'Optional maximum number of matching lines to return (default: 50).',
271
+ },
272
+ },
273
+ required: ['query'],
274
+ },
275
+ },
276
+ {
277
+ name: 'file_info',
278
+ description: 'Inspect metadata of a file in the workspace (size, line count, modified time, extension, permissions).',
279
+ parameters: {
280
+ type: 'object',
281
+ properties: {
282
+ file_path: {
283
+ type: 'string',
284
+ description: 'The path of the file to inspect.',
285
+ },
286
+ },
287
+ required: ['file_path'],
288
+ },
289
+ },
290
+ {
291
+ name: 'git_diff',
292
+ description: 'Inspect git status and uncommitted/staged code diffs in the workspace repository.',
293
+ parameters: {
294
+ type: 'object',
295
+ properties: {
296
+ file_path: {
297
+ type: 'string',
298
+ description: 'Optional specific file path to inspect diff for.',
299
+ },
300
+ staged: {
301
+ type: 'boolean',
302
+ description: 'Optional. If true, shows staged changes (--staged); otherwise shows working tree diff (default: false).',
303
+ },
304
+ },
305
+ },
306
+ },
168
307
  {
169
308
  name: 'list_dir',
170
309
  description: 'List the contents of a directory in the workspace.',
@@ -458,6 +597,288 @@ export class ToolExecutor {
458
597
  output: `Successfully wrote ${args.content.length} characters to ${args.file_path}`,
459
598
  };
460
599
  }
600
+ case 'edit_file': {
601
+ let resolvedPath = args.file_path;
602
+ if (resolvedPath.startsWith('~')) {
603
+ resolvedPath = path.join(os.homedir(), resolvedPath.slice(1));
604
+ }
605
+ else {
606
+ resolvedPath = path.resolve(this.workingDir, resolvedPath);
607
+ }
608
+ if (!fs.existsSync(resolvedPath)) {
609
+ return {
610
+ tool_call_id: toolCallId,
611
+ name,
612
+ output: `Error: File not found for editing: ${args.file_path}`,
613
+ error: true,
614
+ };
615
+ }
616
+ const targetContent = args.target_content;
617
+ const replacementContent = args.replacement_content;
618
+ const allowMultiple = !!args.allow_multiple;
619
+ const fileContent = fs.readFileSync(resolvedPath, 'utf-8');
620
+ if (!fileContent.includes(targetContent)) {
621
+ return {
622
+ tool_call_id: toolCallId,
623
+ name,
624
+ output: `Error: target_content not found in ${args.file_path}. Please inspect the file with read_file first to ensure exact character and whitespace match.`,
625
+ error: true,
626
+ };
627
+ }
628
+ const count = fileContent.split(targetContent).length - 1;
629
+ if (count > 1 && !allowMultiple) {
630
+ return {
631
+ tool_call_id: toolCallId,
632
+ name,
633
+ output: `Error: target_content appears ${count} times in ${args.file_path}. Provide more surrounding context to match a unique block or set allow_multiple: true.`,
634
+ error: true,
635
+ };
636
+ }
637
+ const newContent = allowMultiple
638
+ ? fileContent.replaceAll(targetContent, replacementContent)
639
+ : fileContent.replace(targetContent, replacementContent);
640
+ fs.writeFileSync(resolvedPath, newContent, 'utf-8');
641
+ return {
642
+ tool_call_id: toolCallId,
643
+ name,
644
+ output: `Successfully edited ${args.file_path} (replaced ${count} occurrence${count > 1 ? 's' : ''}).`,
645
+ };
646
+ }
647
+ case 'create_directory': {
648
+ const targetPath = path.resolve(this.workingDir, args.dir_path);
649
+ fs.mkdirSync(targetPath, { recursive: true });
650
+ return {
651
+ tool_call_id: toolCallId,
652
+ name,
653
+ output: `Successfully created directory: ${args.dir_path}`,
654
+ };
655
+ }
656
+ case 'delete_file': {
657
+ const targetPath = path.resolve(this.workingDir, args.file_path);
658
+ if (!fs.existsSync(targetPath)) {
659
+ return {
660
+ tool_call_id: toolCallId,
661
+ name,
662
+ output: `Error: File or directory not found to delete: ${args.file_path}`,
663
+ error: true,
664
+ };
665
+ }
666
+ const stat = fs.statSync(targetPath);
667
+ if (stat.isDirectory()) {
668
+ fs.rmSync(targetPath, { recursive: true, force: true });
669
+ return {
670
+ tool_call_id: toolCallId,
671
+ name,
672
+ output: `Successfully deleted directory: ${args.file_path}`,
673
+ };
674
+ }
675
+ else {
676
+ fs.unlinkSync(targetPath);
677
+ return {
678
+ tool_call_id: toolCallId,
679
+ name,
680
+ output: `Successfully deleted file: ${args.file_path}`,
681
+ };
682
+ }
683
+ }
684
+ case 'find_files': {
685
+ const rootDir = path.resolve(this.workingDir, args.dir_path || '.');
686
+ if (!fs.existsSync(rootDir)) {
687
+ return {
688
+ tool_call_id: toolCallId,
689
+ name,
690
+ output: `Error: Directory not found: ${args.dir_path}`,
691
+ error: true,
692
+ };
693
+ }
694
+ const pattern = args.pattern.toLowerCase();
695
+ const maxResults = args.max_results || 50;
696
+ const matches = [];
697
+ function scanDir(dir) {
698
+ if (matches.length >= maxResults)
699
+ return;
700
+ let entries = [];
701
+ try {
702
+ entries = fs.readdirSync(dir, { withFileTypes: true });
703
+ }
704
+ catch {
705
+ return;
706
+ }
707
+ for (const entry of entries) {
708
+ if (matches.length >= maxResults)
709
+ return;
710
+ if (entry.name.startsWith('.') ||
711
+ entry.name === 'node_modules' ||
712
+ entry.name === 'dist' ||
713
+ entry.name === 'build') {
714
+ continue;
715
+ }
716
+ const full = path.join(dir, entry.name);
717
+ const rel = path.relative(rootDir, full).replace(/\\/g, '/');
718
+ if (entry.isDirectory()) {
719
+ scanDir(full);
720
+ }
721
+ else {
722
+ if (rel.toLowerCase().includes(pattern) ||
723
+ entry.name.toLowerCase().includes(pattern) ||
724
+ (pattern.startsWith('*.') && entry.name.toLowerCase().endsWith(pattern.slice(1)))) {
725
+ matches.push(rel);
726
+ }
727
+ }
728
+ }
729
+ }
730
+ scanDir(rootDir);
731
+ return {
732
+ tool_call_id: toolCallId,
733
+ name,
734
+ output: matches.length > 0
735
+ ? `Found ${matches.length} matching file(s):\n${matches.join('\n')}`
736
+ : `No files found matching '${args.pattern}'`,
737
+ };
738
+ }
739
+ case 'grep_search': {
740
+ const rootDir = path.resolve(this.workingDir, args.dir_path || '.');
741
+ if (!fs.existsSync(rootDir)) {
742
+ return {
743
+ tool_call_id: toolCallId,
744
+ name,
745
+ output: `Error: Directory not found: ${args.dir_path}`,
746
+ error: true,
747
+ };
748
+ }
749
+ const isRegex = !!args.is_regex;
750
+ const caseSensitive = !!args.case_sensitive;
751
+ const maxResults = args.max_results || 50;
752
+ const matches = [];
753
+ let regex;
754
+ try {
755
+ regex = isRegex
756
+ ? new RegExp(args.query, caseSensitive ? 'g' : 'gi')
757
+ : new RegExp(args.query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), caseSensitive ? 'g' : 'gi');
758
+ }
759
+ catch (reErr) {
760
+ return {
761
+ tool_call_id: toolCallId,
762
+ name,
763
+ output: `Invalid regular expression: ${reErr.message}`,
764
+ error: true,
765
+ };
766
+ }
767
+ function searchFiles(dir) {
768
+ if (matches.length >= maxResults)
769
+ return;
770
+ let entries = [];
771
+ try {
772
+ entries = fs.readdirSync(dir, { withFileTypes: true });
773
+ }
774
+ catch {
775
+ return;
776
+ }
777
+ for (const entry of entries) {
778
+ if (matches.length >= maxResults)
779
+ return;
780
+ if (entry.name.startsWith('.') ||
781
+ entry.name === 'node_modules' ||
782
+ entry.name === 'dist' ||
783
+ entry.name === 'build') {
784
+ continue;
785
+ }
786
+ const full = path.join(dir, entry.name);
787
+ if (entry.isDirectory()) {
788
+ searchFiles(full);
789
+ }
790
+ else {
791
+ try {
792
+ const content = fs.readFileSync(full, 'utf-8');
793
+ const lines = content.split('\n');
794
+ const rel = path.relative(process.cwd(), full).replace(/\\/g, '/');
795
+ for (let i = 0; i < lines.length; i++) {
796
+ if (matches.length >= maxResults)
797
+ return;
798
+ if (regex.test(lines[i])) {
799
+ matches.push(`${rel}:${i + 1}: ${lines[i].trim()}`);
800
+ }
801
+ regex.lastIndex = 0;
802
+ }
803
+ }
804
+ catch { }
805
+ }
806
+ }
807
+ }
808
+ searchFiles(rootDir);
809
+ return {
810
+ tool_call_id: toolCallId,
811
+ name,
812
+ output: matches.length > 0
813
+ ? `Found ${matches.length} match(es):\n${matches.join('\n')}`
814
+ : `No matches found for '${args.query}'`,
815
+ };
816
+ }
817
+ case 'file_info': {
818
+ let resolvedPath = args.file_path;
819
+ if (resolvedPath.startsWith('~')) {
820
+ resolvedPath = path.join(os.homedir(), resolvedPath.slice(1));
821
+ }
822
+ else {
823
+ resolvedPath = path.resolve(this.workingDir, resolvedPath);
824
+ }
825
+ if (!fs.existsSync(resolvedPath)) {
826
+ return {
827
+ tool_call_id: toolCallId,
828
+ name,
829
+ output: `Error: File not found: ${args.file_path}`,
830
+ error: true,
831
+ };
832
+ }
833
+ const stat = fs.statSync(resolvedPath);
834
+ const isDir = stat.isDirectory();
835
+ let lineCount = 0;
836
+ if (!isDir) {
837
+ try {
838
+ const content = fs.readFileSync(resolvedPath, 'utf-8');
839
+ lineCount = content.split('\n').length;
840
+ }
841
+ catch { }
842
+ }
843
+ const info = [
844
+ `Path: ${args.file_path}`,
845
+ `Type: ${isDir ? 'Directory' : 'File'}`,
846
+ `Size: ${(stat.size / 1024).toFixed(2)} KB (${stat.size} bytes)`,
847
+ `Lines: ${isDir ? 'N/A' : lineCount}`,
848
+ `Created: ${stat.birthtime.toLocaleString()}`,
849
+ `Modified: ${stat.mtime.toLocaleString()}`,
850
+ ];
851
+ return {
852
+ tool_call_id: toolCallId,
853
+ name,
854
+ output: info.join('\n'),
855
+ };
856
+ }
857
+ case 'git_diff': {
858
+ const stagedFlag = args.staged ? '--staged' : '';
859
+ const fileTarget = args.file_path ? ` -- "${args.file_path}"` : '';
860
+ const cmd = `git diff ${stagedFlag}${fileTarget}`;
861
+ try {
862
+ const { stdout, stderr } = await execPromise(cmd, {
863
+ cwd: this.workingDir,
864
+ timeout: 15000,
865
+ });
866
+ const output = (stdout || '') + (stderr ? `\n[STDERR]: ${stderr}` : '');
867
+ return {
868
+ tool_call_id: toolCallId,
869
+ name,
870
+ output: output.trim() || '(no git diff output - working tree clean)',
871
+ };
872
+ }
873
+ catch (gitErr) {
874
+ return {
875
+ tool_call_id: toolCallId,
876
+ name,
877
+ output: `Error running git diff: ${gitErr.message}`,
878
+ error: true,
879
+ };
880
+ }
881
+ }
461
882
  case 'list_dir': {
462
883
  const targetPath = path.resolve(this.workingDir, args.dir_path || '.');
463
884
  if (!fs.existsSync(targetPath)) {