astraagent 2.25.6
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/.env.template +22 -0
- package/LICENSE +21 -0
- package/README.md +333 -0
- package/astra/__init__.py +15 -0
- package/astra/__pycache__/__init__.cpython-314.pyc +0 -0
- package/astra/__pycache__/chat.cpython-314.pyc +0 -0
- package/astra/__pycache__/cli.cpython-314.pyc +0 -0
- package/astra/__pycache__/prompts.cpython-314.pyc +0 -0
- package/astra/__pycache__/updater.cpython-314.pyc +0 -0
- package/astra/chat.py +763 -0
- package/astra/cli.py +913 -0
- package/astra/core/__init__.py +8 -0
- package/astra/core/__pycache__/__init__.cpython-314.pyc +0 -0
- package/astra/core/__pycache__/agent.cpython-314.pyc +0 -0
- package/astra/core/__pycache__/config.cpython-314.pyc +0 -0
- package/astra/core/__pycache__/memory.cpython-314.pyc +0 -0
- package/astra/core/__pycache__/reasoning.cpython-314.pyc +0 -0
- package/astra/core/__pycache__/state.cpython-314.pyc +0 -0
- package/astra/core/agent.py +515 -0
- package/astra/core/config.py +247 -0
- package/astra/core/memory.py +782 -0
- package/astra/core/reasoning.py +423 -0
- package/astra/core/state.py +366 -0
- package/astra/core/voice.py +144 -0
- package/astra/llm/__init__.py +32 -0
- package/astra/llm/__pycache__/__init__.cpython-314.pyc +0 -0
- package/astra/llm/__pycache__/providers.cpython-314.pyc +0 -0
- package/astra/llm/providers.py +530 -0
- package/astra/planning/__init__.py +117 -0
- package/astra/prompts.py +289 -0
- package/astra/reflection/__init__.py +181 -0
- package/astra/search.py +469 -0
- package/astra/tasks.py +466 -0
- package/astra/tools/__init__.py +17 -0
- package/astra/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/advanced.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/base.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/browser.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/file.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/git.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/memory_tool.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/python.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/shell.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/web.cpython-314.pyc +0 -0
- package/astra/tools/__pycache__/windows.cpython-314.pyc +0 -0
- package/astra/tools/advanced.py +251 -0
- package/astra/tools/base.py +344 -0
- package/astra/tools/browser.py +93 -0
- package/astra/tools/file.py +476 -0
- package/astra/tools/git.py +74 -0
- package/astra/tools/memory_tool.py +89 -0
- package/astra/tools/python.py +238 -0
- package/astra/tools/shell.py +183 -0
- package/astra/tools/web.py +804 -0
- package/astra/tools/windows.py +542 -0
- package/astra/updater.py +450 -0
- package/astra/utils/__init__.py +230 -0
- package/bin/astraagent.js +73 -0
- package/bin/postinstall.js +25 -0
- package/config.json.template +52 -0
- package/main.py +16 -0
- package/package.json +51 -0
- package/pyproject.toml +72 -0
package/astra/tasks.py
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
"""Task Execution System - Perform various system tasks from chat."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import shutil
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, List, Optional, Any
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TaskExecutor:
|
|
13
|
+
"""Execute various system and file tasks."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def open_file(filepath: str) -> Dict[str, str]:
|
|
17
|
+
"""Open a file with default application.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
filepath: Path to file
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Status dict
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
filepath = os.path.abspath(filepath)
|
|
27
|
+
if not os.path.exists(filepath):
|
|
28
|
+
return {'success': False, 'message': f'File not found: {filepath}'}
|
|
29
|
+
|
|
30
|
+
if os.name == 'nt': # Windows
|
|
31
|
+
os.startfile(filepath)
|
|
32
|
+
elif os.name == 'posix': # Linux/Mac
|
|
33
|
+
import subprocess
|
|
34
|
+
subprocess.Popen(['xdg-open' if os.uname()[0] == 'Linux' else 'open', filepath])
|
|
35
|
+
|
|
36
|
+
return {'success': True, 'message': f'Opening: {filepath}'}
|
|
37
|
+
except Exception as e:
|
|
38
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def open_folder(folder_path: str) -> Dict[str, str]:
|
|
42
|
+
"""Open folder in file explorer.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
folder_path: Path to folder
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Status dict
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
folder_path = os.path.abspath(folder_path)
|
|
52
|
+
if not os.path.isdir(folder_path):
|
|
53
|
+
return {'success': False, 'message': f'Folder not found: {folder_path}'}
|
|
54
|
+
|
|
55
|
+
if os.name == 'nt': # Windows
|
|
56
|
+
os.startfile(folder_path)
|
|
57
|
+
elif os.name == 'posix': # Linux/Mac
|
|
58
|
+
subprocess.Popen(['xdg-open' if os.uname()[0] == 'Linux' else 'open', folder_path])
|
|
59
|
+
|
|
60
|
+
return {'success': True, 'message': f'Opening folder: {folder_path}'}
|
|
61
|
+
except Exception as e:
|
|
62
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def copy_file(source: str, dest: str) -> Dict[str, str]:
|
|
66
|
+
"""Copy file from source to destination.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
source: Source file path
|
|
70
|
+
dest: Destination file path
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Status dict
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
source = os.path.abspath(source)
|
|
77
|
+
dest = os.path.abspath(dest)
|
|
78
|
+
|
|
79
|
+
if not os.path.exists(source):
|
|
80
|
+
return {'success': False, 'message': f'Source file not found: {source}'}
|
|
81
|
+
|
|
82
|
+
# Create destination directory if needed
|
|
83
|
+
dest_dir = os.path.dirname(dest)
|
|
84
|
+
if dest_dir and not os.path.exists(dest_dir):
|
|
85
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
shutil.copy2(source, dest)
|
|
88
|
+
return {'success': True, 'message': f'Copied: {source} ā {dest}'}
|
|
89
|
+
except Exception as e:
|
|
90
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def move_file(source: str, dest: str) -> Dict[str, str]:
|
|
94
|
+
"""Move file from source to destination.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
source: Source file path
|
|
98
|
+
dest: Destination file path
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Status dict
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
source = os.path.abspath(source)
|
|
105
|
+
dest = os.path.abspath(dest)
|
|
106
|
+
|
|
107
|
+
if not os.path.exists(source):
|
|
108
|
+
return {'success': False, 'message': f'File not found: {source}'}
|
|
109
|
+
|
|
110
|
+
# Create destination directory if needed
|
|
111
|
+
dest_dir = os.path.dirname(dest)
|
|
112
|
+
if dest_dir and not os.path.exists(dest_dir):
|
|
113
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
114
|
+
|
|
115
|
+
shutil.move(source, dest)
|
|
116
|
+
return {'success': True, 'message': f'Moved: {source} ā {dest}'}
|
|
117
|
+
except Exception as e:
|
|
118
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def delete_file(filepath: str, confirm: bool = True) -> Dict[str, str]:
|
|
122
|
+
"""Delete a file.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
filepath: Path to file
|
|
126
|
+
confirm: Whether to require confirmation
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Status dict
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
filepath = os.path.abspath(filepath)
|
|
133
|
+
|
|
134
|
+
if not os.path.exists(filepath):
|
|
135
|
+
return {'success': False, 'message': f'File not found: {filepath}'}
|
|
136
|
+
|
|
137
|
+
if not os.path.isfile(filepath):
|
|
138
|
+
return {'success': False, 'message': f'Not a file: {filepath}'}
|
|
139
|
+
|
|
140
|
+
os.remove(filepath)
|
|
141
|
+
return {'success': True, 'message': f'Deleted: {filepath}'}
|
|
142
|
+
except Exception as e:
|
|
143
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def create_folder(folder_path: str) -> Dict[str, str]:
|
|
147
|
+
"""Create a new folder.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
folder_path: Path to create
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Status dict
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
folder_path = os.path.abspath(folder_path)
|
|
157
|
+
|
|
158
|
+
if os.path.exists(folder_path):
|
|
159
|
+
return {'success': False, 'message': f'Folder already exists: {folder_path}'}
|
|
160
|
+
|
|
161
|
+
os.makedirs(folder_path, exist_ok=True)
|
|
162
|
+
return {'success': True, 'message': f'Created folder: {folder_path}'}
|
|
163
|
+
except Exception as e:
|
|
164
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def delete_folder(folder_path: str, recursive: bool = False) -> Dict[str, str]:
|
|
168
|
+
"""Delete a folder.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
folder_path: Path to folder
|
|
172
|
+
recursive: Whether to delete recursively (all contents)
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Status dict
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
folder_path = os.path.abspath(folder_path)
|
|
179
|
+
|
|
180
|
+
if not os.path.isdir(folder_path):
|
|
181
|
+
return {'success': False, 'message': f'Folder not found: {folder_path}'}
|
|
182
|
+
|
|
183
|
+
if recursive:
|
|
184
|
+
shutil.rmtree(folder_path)
|
|
185
|
+
else:
|
|
186
|
+
os.rmdir(folder_path)
|
|
187
|
+
|
|
188
|
+
return {'success': True, 'message': f'Deleted folder: {folder_path}'}
|
|
189
|
+
except Exception as e:
|
|
190
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
191
|
+
|
|
192
|
+
@staticmethod
|
|
193
|
+
def get_file_info(filepath: str) -> Dict[str, Any]:
|
|
194
|
+
"""Get detailed information about a file.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
filepath: Path to file
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Details dict
|
|
201
|
+
"""
|
|
202
|
+
try:
|
|
203
|
+
filepath = os.path.abspath(filepath)
|
|
204
|
+
|
|
205
|
+
if not os.path.exists(filepath):
|
|
206
|
+
return {'error': f'File not found: {filepath}'}
|
|
207
|
+
|
|
208
|
+
stat = os.stat(filepath)
|
|
209
|
+
path_obj = Path(filepath)
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
'path': filepath,
|
|
213
|
+
'name': os.path.basename(filepath),
|
|
214
|
+
'exists': True,
|
|
215
|
+
'is_file': os.path.isfile(filepath),
|
|
216
|
+
'is_dir': os.path.isdir(filepath),
|
|
217
|
+
'size': stat.st_size,
|
|
218
|
+
'size_mb': round(stat.st_size / 1024 / 1024, 2),
|
|
219
|
+
'created': datetime.fromtimestamp(stat.st_ctime).isoformat(),
|
|
220
|
+
'modified': datetime.fromtimestamp(stat.st_mtime).isoformat(),
|
|
221
|
+
'extension': path_obj.suffix,
|
|
222
|
+
'parent': str(path_obj.parent)
|
|
223
|
+
}
|
|
224
|
+
except Exception as e:
|
|
225
|
+
return {'error': str(e)}
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def rename_file(filepath: str, new_name: str) -> Dict[str, str]:
|
|
229
|
+
"""Rename a file.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
filepath: Path to file
|
|
233
|
+
new_name: New filename
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Status dict
|
|
237
|
+
"""
|
|
238
|
+
try:
|
|
239
|
+
filepath = os.path.abspath(filepath)
|
|
240
|
+
|
|
241
|
+
if not os.path.exists(filepath):
|
|
242
|
+
return {'success': False, 'message': f'File not found: {filepath}'}
|
|
243
|
+
|
|
244
|
+
directory = os.path.dirname(filepath)
|
|
245
|
+
new_path = os.path.join(directory, new_name)
|
|
246
|
+
|
|
247
|
+
if os.path.exists(new_path):
|
|
248
|
+
return {'success': False, 'message': f'File already exists: {new_path}'}
|
|
249
|
+
|
|
250
|
+
os.rename(filepath, new_path)
|
|
251
|
+
return {'success': True, 'message': f'Renamed: {filepath} ā {new_path}'}
|
|
252
|
+
except Exception as e:
|
|
253
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def list_directory(dirpath: str, show_hidden: bool = False) -> Dict[str, Any]:
|
|
257
|
+
"""List contents of a directory.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
dirpath: Path to directory
|
|
261
|
+
show_hidden: Whether to show hidden files
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Contents dict
|
|
265
|
+
"""
|
|
266
|
+
try:
|
|
267
|
+
dirpath = os.path.abspath(dirpath)
|
|
268
|
+
|
|
269
|
+
if not os.path.isdir(dirpath):
|
|
270
|
+
return {'error': f'Not a directory: {dirpath}'}
|
|
271
|
+
|
|
272
|
+
dirs = []
|
|
273
|
+
files = []
|
|
274
|
+
|
|
275
|
+
for item in sorted(os.listdir(dirpath)):
|
|
276
|
+
if not show_hidden and item.startswith('.'):
|
|
277
|
+
continue
|
|
278
|
+
|
|
279
|
+
item_path = os.path.join(dirpath, item)
|
|
280
|
+
|
|
281
|
+
if os.path.isdir(item_path):
|
|
282
|
+
dirs.append({
|
|
283
|
+
'name': item,
|
|
284
|
+
'path': item_path,
|
|
285
|
+
'type': 'directory'
|
|
286
|
+
})
|
|
287
|
+
else:
|
|
288
|
+
try:
|
|
289
|
+
size = os.path.getsize(item_path)
|
|
290
|
+
files.append({
|
|
291
|
+
'name': item,
|
|
292
|
+
'path': item_path,
|
|
293
|
+
'type': 'file',
|
|
294
|
+
'size': size,
|
|
295
|
+
'size_kb': round(size / 1024, 2)
|
|
296
|
+
})
|
|
297
|
+
except:
|
|
298
|
+
pass
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
'path': dirpath,
|
|
302
|
+
'directories': dirs,
|
|
303
|
+
'files': files,
|
|
304
|
+
'total_items': len(dirs) + len(files)
|
|
305
|
+
}
|
|
306
|
+
except Exception as e:
|
|
307
|
+
return {'error': str(e)}
|
|
308
|
+
|
|
309
|
+
@staticmethod
|
|
310
|
+
def search_and_replace_in_file(filepath: str, search_text: str,
|
|
311
|
+
replace_text: str, backup: bool = True) -> Dict[str, str]:
|
|
312
|
+
"""Search and replace text in a file.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
filepath: Path to file
|
|
316
|
+
search_text: Text to find
|
|
317
|
+
replace_text: Replacement text
|
|
318
|
+
backup: Whether to create backup
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Status dict
|
|
322
|
+
"""
|
|
323
|
+
try:
|
|
324
|
+
filepath = os.path.abspath(filepath)
|
|
325
|
+
|
|
326
|
+
if not os.path.exists(filepath):
|
|
327
|
+
return {'success': False, 'message': f'File not found: {filepath}'}
|
|
328
|
+
|
|
329
|
+
# Read file
|
|
330
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
331
|
+
content = f.read()
|
|
332
|
+
|
|
333
|
+
# Count replacements
|
|
334
|
+
count = content.count(search_text)
|
|
335
|
+
|
|
336
|
+
if count == 0:
|
|
337
|
+
return {'success': False, 'message': f'Text not found: {search_text}'}
|
|
338
|
+
|
|
339
|
+
# Backup if requested
|
|
340
|
+
if backup:
|
|
341
|
+
backup_path = filepath + '.backup'
|
|
342
|
+
shutil.copy2(filepath, backup_path)
|
|
343
|
+
|
|
344
|
+
# Replace
|
|
345
|
+
new_content = content.replace(search_text, replace_text)
|
|
346
|
+
|
|
347
|
+
# Write back
|
|
348
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
349
|
+
f.write(new_content)
|
|
350
|
+
|
|
351
|
+
msg = f'Replaced {count} occurrence{"s" if count != 1 else ""} in {filepath}'
|
|
352
|
+
if backup:
|
|
353
|
+
msg += f'\nBackup created: {backup_path}'
|
|
354
|
+
|
|
355
|
+
return {'success': True, 'message': msg}
|
|
356
|
+
except Exception as e:
|
|
357
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
358
|
+
|
|
359
|
+
@staticmethod
|
|
360
|
+
def create_file(filepath: str, content: str = "") -> Dict[str, str]:
|
|
361
|
+
"""Create a new file with optional content.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
filepath: Path to create
|
|
365
|
+
content: Initial content
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Status dict
|
|
369
|
+
"""
|
|
370
|
+
try:
|
|
371
|
+
filepath = os.path.abspath(filepath)
|
|
372
|
+
|
|
373
|
+
if os.path.exists(filepath):
|
|
374
|
+
return {'success': False, 'message': f'File already exists: {filepath}'}
|
|
375
|
+
|
|
376
|
+
# Create parent directories if needed
|
|
377
|
+
parent_dir = os.path.dirname(filepath)
|
|
378
|
+
if parent_dir and not os.path.exists(parent_dir):
|
|
379
|
+
os.makedirs(parent_dir, exist_ok=True)
|
|
380
|
+
|
|
381
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
382
|
+
f.write(content)
|
|
383
|
+
|
|
384
|
+
return {'success': True, 'message': f'Created file: {filepath}'}
|
|
385
|
+
except Exception as e:
|
|
386
|
+
return {'success': False, 'message': f'Error: {str(e)}'}
|
|
387
|
+
|
|
388
|
+
@staticmethod
|
|
389
|
+
def get_disk_usage(path: str = "/") -> Dict[str, Any]:
|
|
390
|
+
"""Get disk usage information.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
path: Path to check (root by default)
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Usage dict
|
|
397
|
+
"""
|
|
398
|
+
try:
|
|
399
|
+
path = os.path.abspath(path)
|
|
400
|
+
|
|
401
|
+
if os.name == 'nt': # Windows
|
|
402
|
+
import ctypes
|
|
403
|
+
free_bytes = ctypes.c_ulonglong(0)
|
|
404
|
+
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(path),
|
|
405
|
+
free_bytes, None, None)
|
|
406
|
+
free = free_bytes.value
|
|
407
|
+
|
|
408
|
+
# Get total (approximate)
|
|
409
|
+
stat = os.statvfs(os.path.splitdrive(path)[0] + '\\') if hasattr(os, 'statvfs') else None
|
|
410
|
+
if stat:
|
|
411
|
+
total = stat.f_blocks * stat.f_frsize
|
|
412
|
+
else:
|
|
413
|
+
total = None
|
|
414
|
+
else: # Unix/Linux
|
|
415
|
+
stat = os.statvfs(path)
|
|
416
|
+
free = stat.f_bavail * stat.f_frsize
|
|
417
|
+
total = stat.f_blocks * stat.f_frsize
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
'path': path,
|
|
421
|
+
'free_gb': round(free / 1024 / 1024 / 1024, 2) if free else None,
|
|
422
|
+
'free_bytes': free,
|
|
423
|
+
'total_gb': round(total / 1024 / 1024 / 1024, 2) if total else None,
|
|
424
|
+
'total_bytes': total
|
|
425
|
+
}
|
|
426
|
+
except Exception as e:
|
|
427
|
+
return {'error': str(e)}
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def format_task_result(result: Dict) -> str:
|
|
431
|
+
"""Format task result for display."""
|
|
432
|
+
if isinstance(result, dict):
|
|
433
|
+
if 'error' in result:
|
|
434
|
+
return f"ā Error: {result['error']}"
|
|
435
|
+
|
|
436
|
+
if 'success' in result:
|
|
437
|
+
status = "ā
" if result['success'] else "ā"
|
|
438
|
+
return f"{status} {result.get('message', 'Task completed')}"
|
|
439
|
+
|
|
440
|
+
if 'path' in result and 'is_file' in result:
|
|
441
|
+
# File info
|
|
442
|
+
output = f"\nš File Info: {result['name']}\n"
|
|
443
|
+
output += f" Path: {result['path']}\n"
|
|
444
|
+
output += f" Size: {result['size_mb']} MB ({result['size']} bytes)\n"
|
|
445
|
+
output += f" Modified: {result['modified']}\n"
|
|
446
|
+
return output
|
|
447
|
+
|
|
448
|
+
if 'directories' in result and 'files' in result:
|
|
449
|
+
# Directory listing
|
|
450
|
+
output = f"\nš Directory: {result['path']}\n"
|
|
451
|
+
output += f" Folders: {len(result['directories'])}\n"
|
|
452
|
+
output += f" Files: {len(result['files'])}\n"
|
|
453
|
+
|
|
454
|
+
if result['directories']:
|
|
455
|
+
output += "\n Folders:\n"
|
|
456
|
+
for d in result['directories'][:10]:
|
|
457
|
+
output += f" š {d['name']}\n"
|
|
458
|
+
|
|
459
|
+
if result['files']:
|
|
460
|
+
output += "\n Files:\n"
|
|
461
|
+
for f in result['files'][:10]:
|
|
462
|
+
output += f" š {f['name']} ({f['size_kb']} KB)\n"
|
|
463
|
+
|
|
464
|
+
return output
|
|
465
|
+
|
|
466
|
+
return str(result)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Tools module for AstraAgent."""
|
|
2
|
+
|
|
3
|
+
from astra.tools.base import Tool, ToolResult, ToolRegistry
|
|
4
|
+
from astra.tools.shell import ShellTool
|
|
5
|
+
from astra.tools.file import FileReadTool, FileWriteTool, ListDirTool
|
|
6
|
+
from astra.tools.python import PythonTool
|
|
7
|
+
from astra.tools.web import WebSearchTool, WebFetchTool
|
|
8
|
+
from astra.tools.browser import BrowserTool
|
|
9
|
+
from astra.tools.git import GitTool
|
|
10
|
+
from astra.tools.memory_tool import MemoryTool
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Tool", "ToolResult", "ToolRegistry",
|
|
14
|
+
"ShellTool", "FileReadTool", "FileWriteTool", "ListDirTool",
|
|
15
|
+
"PythonTool", "WebSearchTool", "WebFetchTool", "BrowserTool",
|
|
16
|
+
"GitTool", "MemoryTool"
|
|
17
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|